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>
33 #include <IOKit/pci/IOPCIBridge.h>
35 #include "GossamerCPU.h"
39 unsigned int ml_throttle(unsigned int step
);
41 void machine_idle(void);
44 // Uncomment the following define to get verbose logs on the sleep/wake cycles
45 //#define VERBOSE_LOGS_ON
48 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
52 OSDefineMetaClassAndStructors(GossamerCPU
, IOCPU
);
54 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
56 UInt32
GossamerCPU::restartAddress
= 0x100;
58 IOService
*GossamerCPU::findIOInterface(char *name
)
65 // find the dictionary of the Heathrow matches.
66 dict
= serviceMatching(name
);
68 #ifdef VERBOSE_LOGS_ON
69 kprintf("GossamerCPU::findIOInterface faild to get a matching dictionary for %s\n", name
);
70 #endif // VERBOSE_LOGS_ON
74 service
= waitForService(dict
, NULL
);
75 if (service
== NULL
) {
76 #ifdef VERBOSE_LOGS_ON
77 kprintf("GossamerCPU::findIOInterface failed to get a matching service for %s\n", name
);
78 #endif// VERBOSE_LOGS_ON
85 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
87 bool GossamerCPU::start(IOService
*provider
)
90 ml_processor_info_t processor_info
;
91 bool success
= super::start(provider
);
92 GossamerPE
*gossamerBoard
;
97 // callPlatformFunction symbols
98 heathrow_sleepState
= OSSymbol::withCString("heathrow_sleepState");
99 heathrow_set_light
= OSSymbol::withCString("heathrow_set_light");
100 cuda_check_any_interrupt
= OSSymbol::withCString("cuda_check_any_interrupt");
101 usb_remote_wakeup
= OSSymbol::withCString("usb_remote_wakeup");
103 #ifdef VERBOSE_LOGS_ON
104 kprintf("GossamerCPU::start start\n");
105 #endif // VERBOSE_LOGS_ON
108 gossamerBoard
= OSDynamicCast(GossamerPE
, getPlatform());
109 if (gossamerBoard
== 0) {
110 #ifdef VERBOSE_LOGS_ON
111 kprintf("GossamerCPU::start this is not a GossamerPE\n");
112 #endif // VERBOSE_LOGS_ON
116 cpuIC
= new IOCPUInterruptController
;
120 if (cpuIC
->initCPUInterruptController(1) != kIOReturnSuccess
) return false;
123 cpuIC
->registerCPUInterruptController();
125 processor_info
.cpu_id
= (cpu_id_t
)this;
126 processor_info
.boot_cpu
= true;
127 processor_info
.start_paddr
= restartAddress
;
128 processor_info
.l2cr_value
= mfl2cr() & 0x7FFFFFFF; // cache-disabled value
129 processor_info
.supports_nap
= false; // doze, do not nap
130 processor_info
.time_base_enable
= 0;
132 // Register this CPU with mach.
133 result
= ml_processor_register(&processor_info
, &machProcessor
,
135 if (result
== KERN_FAILURE
)
138 setCPUState(kIOCPUStateUninitalized
);
140 processor_start(machProcessor
);
142 #ifdef VERBOSE_LOGS_ON
143 kprintf("GossamerCPU::start end %d \n", success
);
144 #endif // VERBOSE_LOGS_ON
151 void GossamerCPU::ipiHandler(void *refCon
, void *nub
, int source
)
153 // Call mach IPI handler for this CPU.
158 void GossamerCPU::initCPU(bool boot
)
160 #ifdef VERBOSE_LOGS_ON
161 kprintf("GossamerCPU::initCPU start\n");
162 #endif // VERBOSE_LOGS_ON
164 if (grackle
!= NULL
) {
165 IOPCIAddressSpace grackleSpace
;
166 UInt32 grackleMemConfiguration
;
168 #ifdef VERBOSE_LOGS_ON
169 kprintf("GossamerCPU::initCPU AppleGracklePCI sets the ram in autorefresh off\n");
170 #endif // VERBOSE_LOGS_ON
172 grackleSpace
.bits
= 0x80000000;
173 grackleMemConfiguration
= grackle
->configRead32(grackleSpace
, 0x70);
175 #ifdef VERBOSE_LOGS_ON
176 kprintf("GossamerCPU::initCPU AppleGracklePCI current power managment mode :0x%08lx\n", grackleMemConfiguration
);
177 #endif // VERBOSE_LOGS_ON
179 // Disables NAP and PM
180 grackleMemConfiguration
&= ~(0x90);
181 #ifdef VERBOSE_LOGS_ON
182 kprintf("GossamerCPU::initCPU AppleGracklePCI new power managment mode :0x%08lx\n", grackleMemConfiguration
);
183 #endif // VERBOSE_LOGS_ON
185 grackle
->configWrite32(grackleSpace
, 0x70, grackleMemConfiguration
);
190 kprintf("GossamerCPU::initCPU not found AppleGracklePCI\n");
192 if (heathrow
!= NULL
) {
193 // we are waking up from sleep so:
194 heathrow
->callPlatformFunction(heathrow_sleepState
, false, (void *)false, 0, 0, 0);
198 kprintf("GossamerCPU::initCPU not found Heathrow\n");
201 The following code is commented because the only Gossamer machine with a pci 2 pci Bridge
202 is the BWG3 and in that machine we do not remove power from the bridge. I am however leaving
203 this code here as reference (and to make clear that it is not running for a reason)
204 // Restore the PCI-PCI Bridge.
205 if (pci2pciBridge != NULL)
206 pci2pciBridge->restoreBridgeState(); */
208 // Restore time base after wake (since CPU's TBR was set to zero during sleep)
212 // Init the interrupts.
214 cpuIC
->enableCPUInterrupt(this);
216 setCPUState(kIOCPUStateRunning
);
218 gossamerPE
= OSDynamicCast(GossamerPE
, getPlatform());
220 //Initially Gossamers with Cuda are not in sleep mode
221 gossamerPE
->setProperty("GossamerCudaSleeping", false);
224 #ifdef VERBOSE_LOGS_ON
225 kprintf("GossamerCPU::initCPU end\n");
226 #endif VERBOSE_LOGS_ON
229 //extern "C" void _gossamer_cpu_wake(void);
230 extern UInt32 ResetHandler
;
232 #ifdef VERBOSE_LOGS_ON
233 // The following function exist only to check that the wake vector is placed correctly.
237 __asm__
volatile("_gossamer_cpu_wake:");
238 //kprintf("_gossamer_cpu_wake going to 0x100\n");
239 __asm__
volatile(" ba 0x100");
241 #endif // VERBOSE_LOGS_ON
243 // flushes the cash for a word at the given address.
244 #define cFlush(addr) __asm__ volatile("dcbf 0, %0" : : "r" (addr))
247 void gossamer_cpu_wake(void);
248 extern void cacheInit(void);
249 extern void cacheDisable(void);
252 void GossamerCPU::quiesceCPU(void)
254 UInt32 larsCode
= (((UInt32
)'L') << 24) | (((UInt32
)'a') << 16) | (((UInt32
)'r') << 8) | (((UInt32
)'s') << 0);
255 UInt32 restartReferencePhi
= pmap_extract(kernel_pmap
,(vm_address_t
)&restartAddress
);
257 // disables the interrupts (they should be already disabled, but one more tiem won't hurt):
258 ml_set_interrupts_enabled(FALSE
);
260 #ifdef VERBOSE_LOGS_ON
261 kprintf("GossamerCPU::quiesceCPU BEFORE 0x%08lx 0x%08lx start\n", 0x00000000, ml_phys_read(0x00000000));
262 kprintf("GossamerCPU::quiesceCPU BEFORE 0x%08lx 0x%08lx start\n", 0x00000004, ml_phys_read(0x00000004));
264 // Set the wake vector to point to the my checkpoint vector
265 ml_phys_write(restartReferencePhi
, gossamer_cpu_wake
); //restartAddress = gossamer_cpu_wake;
268 // Set the wake vector to point to the reset vector
269 ml_phys_write(restartReferencePhi
, 0x100); //restartAddress = 0x100;
271 #endif // VERBOSE_LOGS_ON
273 ml_phys_write(0x00000000, restartReferencePhi
);
276 // Set the wake vector to point to the reset vector
277 ml_phys_write(0x00000004, larsCode
);
280 // and flushes the data cache:
281 flush_dcache(restartReferencePhi
, 4, true);
282 flush_dcache(0x00000000, 8, true);
284 // Also makes sure that the reset hander is correctly flushed:
285 flush_dcache(&ResetHandler
, 12, true);
287 __asm__
volatile("sync");
288 __asm__
volatile("isync");
290 #ifdef VERBOSE_LOGS_ON
291 kprintf("GossamerCPU::quiesceCPU AFTER 0x%08lx 0x%08lx start\n", 0x00000000, ml_phys_read(0x00000000));
292 kprintf("GossamerCPU::quiesceCPU AFTER 0x%08lx 0x%08lx start\n", ml_phys_read(0x00000000), ml_phys_read(ml_phys_read(0x00000000)));
293 kprintf("GossamerCPU::quiesceCPU AFTER 0x%08lx 0x%08lx start\n", 0x00000004, ml_phys_read(0x00000004));
296 // Send PMU command to shutdown system before io is turned off
298 pmu
->callPlatformFunction("sleepNow", false, 0, 0, 0, 0);
300 kprintf("GossamerCPU::quiesceCPU can't find ApplePMU\n");
302 if (heathrow
!= NULL
) {
303 heathrow
->callPlatformFunction(heathrow_sleepState
, false, (void *)true, 0, 0, 0);
306 kprintf("GossamerCPU::quiesceCPU not found Heathrow\n");
308 if (grackle
!= NULL
) {
309 IOPCIAddressSpace grackleSpace
;
310 UInt32 grackleProcConfiguration
, grackleMemConfiguration
;
312 #ifdef VERBOSE_LOGS_ON
313 kprintf("GossamerCPU::quiesceCPU AppleGracklePCI sets the ram in autorefresh\n");
315 grackleSpace
.bits
= 0x80000000;
316 grackleProcConfiguration
= grackle
->configRead32(grackleSpace
, 0xA8);
317 kprintf("GossamerCPU::quiesceCPU AppleGracklePCI current processorinterface conf :0x%08lx\n", grackleProcConfiguration
);
318 #endif // VERBOSE_LOGS_ON
320 grackleSpace
.bits
= 0x80000000;
321 grackleMemConfiguration
= grackle
->configRead32(grackleSpace
, 0x70);
322 #ifdef VERBOSE_LOGS_ON
323 kprintf("GossamerCPU::quiesceCPU AppleGracklePCI current power managment mode :0x%08lx\n", grackleMemConfiguration
);
324 #endif // VERBOSE_LOGS_ON
326 // Enables NAP and PM
327 grackleMemConfiguration
|= 0x90;
328 #ifdef VERBOSE_LOGS_ON
329 kprintf("GossamerCPU::quiesceCPU AppleGracklePCI new power managment mode :0x%08lx\n", grackleMemConfiguration
);
330 #endif // VERBOSE_LOGS_ON
332 grackle
->configWrite32(grackleSpace
, 0x70, grackleMemConfiguration
);
335 kprintf("GossamerCPU::quiesceCPU not found AppleGracklePCI\n");
337 // Save time base before sleep since CPU's TBR will be set to zero at wake.
340 // These make all the difference between a succesful wake and a crash,
341 // however it is still unclear why this happens. I'll leave to B.A. to
346 #ifdef VERBOSE_LOGS_ON
347 kprintf("GossamerCPU::quiesceCPU calling ml_ppc_sleep\n");
348 #endif // VERBOSE_LOGS_ON
350 // Now we loop here waiting for the PMU to kick in and sleep the machine.
351 // We do NOT call ml_ppc_sleep because while ml_ppc_sleep works greate for Core99
352 // it has some problems with Gossamer CPUS. Also the code in ml_ppc_sleep to
353 // clear the interrupts (and so keep the processor in its sleep state) is needed
354 // by the Core99 platform (otherwise the machine does not sleep), but it is totally
355 // useless for Gossamer CPUs since whatever is the state of the CPU the pmu
356 // will put the whole system to sleep.
364 const OSSymbol
*GossamerCPU::getCPUName(void)
366 return OSSymbol::withCStringNoCopy("Primary0");
369 kern_return_t
GossamerCPU::startCPU(vm_offset_t
/*start_paddr*/,
370 vm_offset_t
/*arg_paddr*/)
375 void GossamerCPU::haltCPU(void)
380 grackle
= OSDynamicCast(AppleGracklePCI
, findIOInterface("AppleGracklePCI"));
382 kprintf("GossamerCPU::haltCPU missing grackle\n");
384 pci2pciBridge
= NULL
;
386 // Finds heathrow and pmu because we need them in quienceCPU. We can
387 // not put the "findIOInterface" code there because it may block and
388 // quienceCPU runs in interrupt context.
389 heathrow
= OSDynamicCast(IOService
, findIOInterface("Heathrow"));
390 //Actually, pmu find is moved below because it hangs when beige G3 go to sleep
393 The following code is commented because the only Gossamer machine with a pci 2 pci Bridge
394 is the BWG3 and in that machine we do not remove power from the bridge. I am however leaving
395 this code here as reference (and to make clear that it is not running for a reason)
396 IORegistryEntry *pci2pciBridgeEntry = fromPath("/pci@80000000/@d", gIODTPlane);
397 IOService *pci2pciBridgeNub = OSDynamicCast(IOService, pci2pciBridgeEntry);
398 if (pci2pciBridgeNub != NULL) {
399 pci2pciBridge = OSDynamicCast(IOPCI2PCIBridge, pci2pciBridgeNub->getClient());
402 if (pci2pciBridge != NULL)
403 pci2pciBridge->saveBridgeState();
405 #ifdef VERBOSE_LOGS_ON
406 kprintf("GossamerCPU::haltCPU Here!\n");
407 #endif // VERBOSE_LOGS_ON
409 gossamerPE
= OSDynamicCast(GossamerPE
, getPlatform());
410 if (gossamerPE
== 0 )
412 processor_exit(machProcessor
);
415 machine_type
= gossamerPE
->getMachineType();
417 //Isolate only those Gossamers that have a Cuda, not PG&E
418 if ((machine_type
!= kGossamerType101
) && (machine_type
!= kGossamerTypeWallstreet
))
421 IOService
*cudaDriver
;
422 IOService
*usbOHCIDriver
;
427 cudaDriver
= waitForService(serviceMatching("AppleCuda"), &t
);
428 usbOHCIDriver
= waitForService(serviceMatching("AppleUSBOHCI"), &t
);
430 if ((heathrow
!= NULL
) && (machine_type
== kGossamerTypeYosemite
))
432 heathrow
->callPlatformFunction(heathrow_set_light
, false, (void *)false, 0, 0, 0);
435 gossamerPE
->setProperty("GossamerCudaSleeping", true);
436 ml_throttle(254); //throttle cpu speed as much as possible
438 while (true) //sit here in a loop, pretending to be asleep
440 machine_idle(); //Max power savings for G3 CPU, needs interrupts enabled.
441 // It will return when any interrupt occurs
442 if (cudaDriver
!= NULL
)
445 cudaDriver
->callPlatformFunction(cuda_check_any_interrupt
, false, (void *)&anyint
, 0, 0, 0);
452 if (usbOHCIDriver
!= NULL
)
455 usbOHCIDriver
->callPlatformFunction(usb_remote_wakeup
, false, (void *)&anyint
, 0, 0, 0);
461 IOSleep(5); //allows USB thread to run since no more thread scheduling. 1 ms
462 // is enough for slow Yosemite, 5 is needed for iMacs.
465 ml_throttle(0); //remove throttle from CPU speed
467 gossamerPE
->setProperty("GossamerCudaSleeping", false);
469 if ((heathrow
!= NULL
) && (machine_type
== kGossamerTypeYosemite
))
471 heathrow
->callPlatformFunction(heathrow_set_light
, false, (void *)true, 0, 0, 0);
477 pmu
= OSDynamicCast(IOService
, findIOInterface("ApplePMU"));
478 processor_exit(machProcessor
);
482 void GossamerCPU::saveTimeBase(bool save
)
484 if(save
) { // Save time base.
489 } while (tbHigh
!= tbHigh2
);
490 } else { // Restore time base