2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 * Copyright (c) 1999-2000 Apple Computer, Inc. All rights reserved.
28 #include <ppc/proc_reg.h>
31 #include <IOKit/IODeviceTreeSupport.h>
32 #include <IOKit/IOPlatformExpert.h>
34 #include "GossamerCPU.h"
38 unsigned int ml_throttle(unsigned int step
);
40 void machine_idle(void);
43 // Uncomment the following define to get verbose logs on the sleep/wake cycles
44 //#define VERBOSE_LOGS_ON
47 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
51 OSDefineMetaClassAndStructors(GossamerCPU
, IOCPU
);
53 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
55 UInt32
GossamerCPU::restartAddress
= 0x100;
57 IOService
*GossamerCPU::findIOInterface(char *name
)
64 // find the dictionary of the Heathrow matches.
65 dict
= serviceMatching(name
);
67 #ifdef VERBOSE_LOGS_ON
68 kprintf("GossamerCPU::findIOInterface faild to get a matching dictionary for %s\n", name
);
69 #endif // VERBOSE_LOGS_ON
73 service
= waitForService(dict
, NULL
);
74 if (service
== NULL
) {
75 #ifdef VERBOSE_LOGS_ON
76 kprintf("GossamerCPU::findIOInterface failed to get a matching service for %s\n", name
);
77 #endif// VERBOSE_LOGS_ON
84 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
86 bool GossamerCPU::start(IOService
*provider
)
89 ml_processor_info_t processor_info
;
90 bool success
= super::start(provider
);
91 GossamerPE
*gossamerBoard
;
96 // callPlatformFunction symbols
97 heathrow_sleepState
= OSSymbol::withCString("heathrow_sleepState");
98 heathrow_set_light
= OSSymbol::withCString("heathrow_set_light");
99 cuda_check_any_interrupt
= OSSymbol::withCString("cuda_check_any_interrupt");
100 usb_remote_wakeup
= OSSymbol::withCString("usb_remote_wakeup");
102 #ifdef VERBOSE_LOGS_ON
103 kprintf("GossamerCPU::start start\n");
104 #endif // VERBOSE_LOGS_ON
107 gossamerBoard
= OSDynamicCast(GossamerPE
, getPlatform());
108 if (gossamerBoard
== 0) {
109 #ifdef VERBOSE_LOGS_ON
110 kprintf("GossamerCPU::start this is not a GossamerPE\n");
111 #endif // VERBOSE_LOGS_ON
115 cpuIC
= new IOCPUInterruptController
;
119 if (cpuIC
->initCPUInterruptController(1) != kIOReturnSuccess
) return false;
122 cpuIC
->registerCPUInterruptController();
124 processor_info
.cpu_id
= (cpu_id_t
)this;
125 processor_info
.boot_cpu
= true;
126 processor_info
.start_paddr
= restartAddress
;
127 processor_info
.l2cr_value
= mfl2cr() & 0x7FFFFFFF; // cache-disabled value
128 processor_info
.supports_nap
= false; // doze, do not nap
129 processor_info
.time_base_enable
= 0;
131 // Register this CPU with mach.
132 result
= ml_processor_register(&processor_info
, &machProcessor
,
134 if (result
== KERN_FAILURE
)
137 setCPUState(kIOCPUStateUninitalized
);
139 processor_start(machProcessor
);
141 #ifdef VERBOSE_LOGS_ON
142 kprintf("GossamerCPU::start end %d \n", success
);
143 #endif // VERBOSE_LOGS_ON
150 void GossamerCPU::ipiHandler(void *refCon
, void *nub
, int source
)
152 // Call mach IPI handler for this CPU.
157 void GossamerCPU::initCPU(bool boot
)
159 #ifdef VERBOSE_LOGS_ON
160 kprintf("GossamerCPU::initCPU start\n");
161 #endif // VERBOSE_LOGS_ON
163 if (grackle
!= NULL
) {
164 IOPCIAddressSpace grackleSpace
;
165 UInt32 grackleMemConfiguration
;
167 #ifdef VERBOSE_LOGS_ON
168 kprintf("GossamerCPU::initCPU AppleGracklePCI sets the ram in autorefresh off\n");
169 #endif // VERBOSE_LOGS_ON
171 grackleSpace
.bits
= 0x80000000;
172 grackleMemConfiguration
= grackle
->configRead32(grackleSpace
, 0x70);
174 #ifdef VERBOSE_LOGS_ON
175 kprintf("GossamerCPU::initCPU AppleGracklePCI current power managment mode :0x%08lx\n", grackleMemConfiguration
);
176 #endif // VERBOSE_LOGS_ON
178 // Disables NAP and PM
179 grackleMemConfiguration
&= ~(0x90);
180 #ifdef VERBOSE_LOGS_ON
181 kprintf("GossamerCPU::initCPU AppleGracklePCI new power managment mode :0x%08lx\n", grackleMemConfiguration
);
182 #endif // VERBOSE_LOGS_ON
184 grackle
->configWrite32(grackleSpace
, 0x70, grackleMemConfiguration
);
189 kprintf("GossamerCPU::initCPU not found AppleGracklePCI\n");
191 if (heathrow
!= NULL
) {
192 // we are waking up from sleep so:
193 heathrow
->callPlatformFunction(heathrow_sleepState
, false, (void *)false, 0, 0, 0);
197 kprintf("GossamerCPU::initCPU not found Heathrow\n");
200 The following code is commented because the only Gossamer machine with a pci 2 pci Bridge
201 is the BWG3 and in that machine we do not remove power from the bridge. I am however leaving
202 this code here as reference (and to make clear that it is not running for a reason)
203 // Restore the PCI-PCI Bridge.
204 if (pci2pciBridge != NULL)
205 pci2pciBridge->restoreBridgeState(); */
207 // Restore time base after wake (since CPU's TBR was set to zero during sleep)
211 // Init the interrupts.
213 cpuIC
->enableCPUInterrupt(this);
215 setCPUState(kIOCPUStateRunning
);
217 gossamerPE
= OSDynamicCast(GossamerPE
, getPlatform());
219 //Initially Gossamers with Cuda are not in sleep mode
220 gossamerPE
->setProperty("GossamerCudaSleeping", false);
223 #ifdef VERBOSE_LOGS_ON
224 kprintf("GossamerCPU::initCPU end\n");
225 #endif VERBOSE_LOGS_ON
228 //extern "C" void _gossamer_cpu_wake(void);
229 extern UInt32 ResetHandler
;
231 #ifdef VERBOSE_LOGS_ON
232 // The following function exist only to check that the wake vector is placed correctly.
236 __asm__
volatile("_gossamer_cpu_wake:");
237 //kprintf("_gossamer_cpu_wake going to 0x100\n");
238 __asm__
volatile(" ba 0x100");
240 #endif // VERBOSE_LOGS_ON
242 // flushes the cash for a word at the given address.
243 #define cFlush(addr) __asm__ volatile("dcbf 0, %0" : : "r" (addr))
246 void gossamer_cpu_wake(void);
247 extern void cacheInit(void);
248 extern void cacheDisable(void);
251 void GossamerCPU::quiesceCPU(void)
253 UInt32 larsCode
= (((UInt32
)'L') << 24) | (((UInt32
)'a') << 16) | (((UInt32
)'r') << 8) | (((UInt32
)'s') << 0);
254 UInt32 restartReferencePhi
= pmap_extract(kernel_pmap
,(vm_address_t
)&restartAddress
);
256 // disables the interrupts (they should be already disabled, but one more tiem won't hurt):
257 ml_set_interrupts_enabled(FALSE
);
259 #ifdef VERBOSE_LOGS_ON
260 kprintf("GossamerCPU::quiesceCPU BEFORE 0x%08lx 0x%08lx start\n", 0x00000000, ml_phys_read(0x00000000));
261 kprintf("GossamerCPU::quiesceCPU BEFORE 0x%08lx 0x%08lx start\n", 0x00000004, ml_phys_read(0x00000004));
263 // Set the wake vector to point to the my checkpoint vector
264 ml_phys_write(restartReferencePhi
, gossamer_cpu_wake
); //restartAddress = gossamer_cpu_wake;
267 // Set the wake vector to point to the reset vector
268 ml_phys_write(restartReferencePhi
, 0x100); //restartAddress = 0x100;
270 #endif // VERBOSE_LOGS_ON
272 ml_phys_write(0x00000000, restartReferencePhi
);
275 // Set the wake vector to point to the reset vector
276 ml_phys_write(0x00000004, larsCode
);
279 // and flushes the data cache:
280 flush_dcache(restartReferencePhi
, 4, true);
281 flush_dcache(0x00000000, 8, true);
283 // Also makes sure that the reset hander is correctly flushed:
284 flush_dcache(&ResetHandler
, 12, true);
286 __asm__
volatile("sync");
287 __asm__
volatile("isync");
289 #ifdef VERBOSE_LOGS_ON
290 kprintf("GossamerCPU::quiesceCPU AFTER 0x%08lx 0x%08lx start\n", 0x00000000, ml_phys_read(0x00000000));
291 kprintf("GossamerCPU::quiesceCPU AFTER 0x%08lx 0x%08lx start\n", ml_phys_read(0x00000000), ml_phys_read(ml_phys_read(0x00000000)));
292 kprintf("GossamerCPU::quiesceCPU AFTER 0x%08lx 0x%08lx start\n", 0x00000004, ml_phys_read(0x00000004));
295 // Send PMU command to shutdown system before io is turned off
297 pmu
->callPlatformFunction("sleepNow", false, 0, 0, 0, 0);
299 kprintf("GossamerCPU::quiesceCPU can't find ApplePMU\n");
301 if (heathrow
!= NULL
) {
302 heathrow
->callPlatformFunction(heathrow_sleepState
, false, (void *)true, 0, 0, 0);
305 kprintf("GossamerCPU::quiesceCPU not found Heathrow\n");
307 if (grackle
!= NULL
) {
308 IOPCIAddressSpace grackleSpace
;
309 UInt32 grackleProcConfiguration
, grackleMemConfiguration
;
311 #ifdef VERBOSE_LOGS_ON
312 kprintf("GossamerCPU::quiesceCPU AppleGracklePCI sets the ram in autorefresh\n");
314 grackleSpace
.bits
= 0x80000000;
315 grackleProcConfiguration
= grackle
->configRead32(grackleSpace
, 0xA8);
316 kprintf("GossamerCPU::quiesceCPU AppleGracklePCI current processorinterface conf :0x%08lx\n", grackleProcConfiguration
);
317 #endif // VERBOSE_LOGS_ON
319 grackleSpace
.bits
= 0x80000000;
320 grackleMemConfiguration
= grackle
->configRead32(grackleSpace
, 0x70);
321 #ifdef VERBOSE_LOGS_ON
322 kprintf("GossamerCPU::quiesceCPU AppleGracklePCI current power managment mode :0x%08lx\n", grackleMemConfiguration
);
323 #endif // VERBOSE_LOGS_ON
325 // Enables NAP and PM
326 grackleMemConfiguration
|= 0x90;
327 #ifdef VERBOSE_LOGS_ON
328 kprintf("GossamerCPU::quiesceCPU AppleGracklePCI new power managment mode :0x%08lx\n", grackleMemConfiguration
);
329 #endif // VERBOSE_LOGS_ON
331 grackle
->configWrite32(grackleSpace
, 0x70, grackleMemConfiguration
);
334 kprintf("GossamerCPU::quiesceCPU not found AppleGracklePCI\n");
336 // Save time base before sleep since CPU's TBR will be set to zero at wake.
339 // These make all the difference between a succesful wake and a crash,
340 // however it is still unclear why this happens. I'll leave to B.A. to
345 #ifdef VERBOSE_LOGS_ON
346 kprintf("GossamerCPU::quiesceCPU calling ml_ppc_sleep\n");
347 #endif // VERBOSE_LOGS_ON
349 // Now we loop here waiting for the PMU to kick in and sleep the machine.
350 // We do NOT call ml_ppc_sleep because while ml_ppc_sleep works greate for Core99
351 // it has some problems with Gossamer CPUS. Also the code in ml_ppc_sleep to
352 // clear the interrupts (and so keep the processor in its sleep state) is needed
353 // by the Core99 platform (otherwise the machine does not sleep), but it is totally
354 // useless for Gossamer CPUs since whatever is the state of the CPU the pmu
355 // will put the whole system to sleep.
363 const OSSymbol
*GossamerCPU::getCPUName(void)
365 return OSSymbol::withCStringNoCopy("Primary0");
368 kern_return_t
GossamerCPU::startCPU(vm_offset_t
/*start_paddr*/,
369 vm_offset_t
/*arg_paddr*/)
374 void GossamerCPU::haltCPU(void)
379 grackle
= (IOPCIBridge
*)findIOInterface("AppleGracklePCI")->metaCast("IOPCIBridge");
381 kprintf("GossamerCPU::haltCPU missing grackle\n");
383 pci2pciBridge
= NULL
;
385 // Finds heathrow and pmu because we need them in quienceCPU. We can
386 // not put the "findIOInterface" code there because it may block and
387 // quienceCPU runs in interrupt context.
388 heathrow
= OSDynamicCast(IOService
, findIOInterface("Heathrow"));
389 //Actually, pmu find is moved below because it hangs when beige G3 go to sleep
392 The following code is commented because the only Gossamer machine with a pci 2 pci Bridge
393 is the BWG3 and in that machine we do not remove power from the bridge. I am however leaving
394 this code here as reference (and to make clear that it is not running for a reason)
395 IORegistryEntry *pci2pciBridgeEntry = fromPath("/pci@80000000/@d", gIODTPlane);
396 IOService *pci2pciBridgeNub = OSDynamicCast(IOService, pci2pciBridgeEntry);
397 if (pci2pciBridgeNub != NULL) {
398 pci2pciBridge = OSDynamicCast(IOPCI2PCIBridge, pci2pciBridgeNub->getClient());
401 if (pci2pciBridge != NULL)
402 pci2pciBridge->saveBridgeState();
404 #ifdef VERBOSE_LOGS_ON
405 kprintf("GossamerCPU::haltCPU Here!\n");
406 #endif // VERBOSE_LOGS_ON
408 gossamerPE
= OSDynamicCast(GossamerPE
, getPlatform());
409 if (gossamerPE
== 0 )
411 processor_exit(machProcessor
);
414 machine_type
= gossamerPE
->getMachineType();
416 //Isolate only those Gossamers that have a Cuda, not PG&E
417 if ((machine_type
!= kGossamerType101
) && (machine_type
!= kGossamerTypeWallstreet
))
420 IOService
*cudaDriver
;
421 IOService
*usbOHCIDriver
;
426 cudaDriver
= waitForService(serviceMatching("AppleCuda"), &t
);
427 usbOHCIDriver
= waitForService(serviceMatching("AppleUSBOHCI"), &t
);
429 if ((heathrow
!= NULL
) && (machine_type
== kGossamerTypeYosemite
))
431 heathrow
->callPlatformFunction(heathrow_set_light
, false, (void *)false, 0, 0, 0);
434 gossamerPE
->setProperty("GossamerCudaSleeping", true);
435 ml_throttle(254); //throttle cpu speed as much as possible
437 while (true) //sit here in a loop, pretending to be asleep
439 machine_idle(); //Max power savings for G3 CPU, needs interrupts enabled.
440 // It will return when any interrupt occurs
441 if (cudaDriver
!= NULL
)
444 cudaDriver
->callPlatformFunction(cuda_check_any_interrupt
, false, (void *)&anyint
, 0, 0, 0);
451 if (usbOHCIDriver
!= NULL
)
454 usbOHCIDriver
->callPlatformFunction(usb_remote_wakeup
, false, (void *)&anyint
, 0, 0, 0);
460 IOSleep(7); //allows USB thread to run since no more thread scheduling. 1 ms
461 // is enough for slow Yosemite, 7 is needed for iMacs.
464 ml_throttle(0); //remove throttle from CPU speed
466 gossamerPE
->setProperty("GossamerCudaSleeping", false);
468 if ((heathrow
!= NULL
) && (machine_type
== kGossamerTypeYosemite
))
470 heathrow
->callPlatformFunction(heathrow_set_light
, false, (void *)true, 0, 0, 0);
476 pmu
= OSDynamicCast(IOService
, findIOInterface("ApplePMU"));
477 processor_exit(machProcessor
);
481 void GossamerCPU::saveTimeBase(bool save
)
483 if(save
) { // Save time base.
488 } while (tbHigh
!= tbHigh2
);
489 } else { // Restore time base