]> git.saurik.com Git - apple/xnu.git/blob - iokit/Drivers/platform/drvAppleGossamerPE/GossamerCPU.cpp
xnu-201.42.3.tar.gz
[apple/xnu.git] / iokit / Drivers / platform / drvAppleGossamerPE / GossamerCPU.cpp
1 /*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * Copyright (c) 1999-2000 Apple Computer, Inc. All rights reserved.
24 *
25 */
26
27 extern "C" {
28 #include <ppc/proc_reg.h>
29 }
30
31 #include <IOKit/IODeviceTreeSupport.h>
32 #include <IOKit/IOPlatformExpert.h>
33
34 #include "GossamerCPU.h"
35 #include "Gossamer.h"
36
37 extern "C" {
38 unsigned int ml_throttle(unsigned int step);
39 int kdp_getc(void);
40 void machine_idle(void);
41 }
42
43 // Uncomment the following define to get verbose logs on the sleep/wake cycles
44 //#define VERBOSE_LOGS_ON
45
46
47 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
48
49 #define super IOCPU
50
51 OSDefineMetaClassAndStructors(GossamerCPU, IOCPU);
52
53 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
54
55 UInt32 GossamerCPU::restartAddress = 0x100;
56
57 IOService *GossamerCPU::findIOInterface(char *name)
58 {
59 OSDictionary *dict;
60 IOService *service;
61
62 heathrow = NULL;
63
64 // find the dictionary of the Heathrow matches.
65 dict = serviceMatching(name);
66 if (dict == NULL) {
67 #ifdef VERBOSE_LOGS_ON
68 kprintf("GossamerCPU::findIOInterface faild to get a matching dictionary for %s\n", name);
69 #endif // VERBOSE_LOGS_ON
70 return NULL;
71 }
72
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
78 return NULL;
79 }
80
81 return (service);
82 }
83
84 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
85
86 bool GossamerCPU::start(IOService *provider)
87 {
88 kern_return_t result;
89 ml_processor_info_t processor_info;
90 bool success = super::start(provider);
91 GossamerPE *gossamerBoard;
92
93 if (!success)
94 return false;
95
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");
101
102 #ifdef VERBOSE_LOGS_ON
103 kprintf("GossamerCPU::start start\n");
104 #endif // VERBOSE_LOGS_ON
105
106 // Checks the board:
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
112 return false;
113 }
114
115 cpuIC = new IOCPUInterruptController;
116 if (cpuIC == 0)
117 return false;
118
119 if (cpuIC->initCPUInterruptController(1) != kIOReturnSuccess) return false;
120 cpuIC->attach(this);
121
122 cpuIC->registerCPUInterruptController();
123
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;
130
131 // Register this CPU with mach.
132 result = ml_processor_register(&processor_info, &machProcessor,
133 &ipi_handler);
134 if (result == KERN_FAILURE)
135 return false;
136
137 setCPUState(kIOCPUStateUninitalized);
138
139 processor_start(machProcessor);
140
141 #ifdef VERBOSE_LOGS_ON
142 kprintf("GossamerCPU::start end %d \n", success);
143 #endif // VERBOSE_LOGS_ON
144
145 registerService();
146
147 return success;
148 }
149
150 void GossamerCPU::ipiHandler(void *refCon, void *nub, int source)
151 {
152 // Call mach IPI handler for this CPU.
153 if (ipi_handler)
154 ipi_handler();
155 }
156
157 void GossamerCPU::initCPU(bool boot)
158 {
159 #ifdef VERBOSE_LOGS_ON
160 kprintf("GossamerCPU::initCPU start\n");
161 #endif // VERBOSE_LOGS_ON
162
163 if (grackle != NULL) {
164 IOPCIAddressSpace grackleSpace;
165 UInt32 grackleMemConfiguration;
166
167 #ifdef VERBOSE_LOGS_ON
168 kprintf("GossamerCPU::initCPU AppleGracklePCI sets the ram in autorefresh off\n");
169 #endif // VERBOSE_LOGS_ON
170
171 grackleSpace.bits = 0x80000000;
172 grackleMemConfiguration = grackle->configRead32(grackleSpace, 0x70);
173
174 #ifdef VERBOSE_LOGS_ON
175 kprintf("GossamerCPU::initCPU AppleGracklePCI current power managment mode :0x%08lx\n", grackleMemConfiguration);
176 #endif // VERBOSE_LOGS_ON
177
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
183
184 grackle->configWrite32(grackleSpace, 0x70, grackleMemConfiguration);
185
186 grackle = NULL;
187 }
188 else
189 kprintf("GossamerCPU::initCPU not found AppleGracklePCI\n");
190
191 if (heathrow != NULL) {
192 // we are waking up from sleep so:
193 heathrow->callPlatformFunction(heathrow_sleepState, false, (void *)false, 0, 0, 0);
194 heathrow = NULL;
195 }
196 else
197 kprintf("GossamerCPU::initCPU not found Heathrow\n");
198
199 /*
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(); */
206
207 // Restore time base after wake (since CPU's TBR was set to zero during sleep)
208 if(!boot)
209 saveTimeBase(false);
210
211 // Init the interrupts.
212 if (boot)
213 cpuIC->enableCPUInterrupt(this);
214
215 setCPUState(kIOCPUStateRunning);
216
217 gossamerPE = OSDynamicCast(GossamerPE, getPlatform());
218 if (gossamerPE ) {
219 //Initially Gossamers with Cuda are not in sleep mode
220 gossamerPE->setProperty("GossamerCudaSleeping", false);
221 }
222
223 #ifdef VERBOSE_LOGS_ON
224 kprintf("GossamerCPU::initCPU end\n");
225 #endif VERBOSE_LOGS_ON
226 }
227
228 //extern "C" void _gossamer_cpu_wake(void);
229 extern UInt32 ResetHandler;
230
231 #ifdef VERBOSE_LOGS_ON
232 // The following function exist only to check that the wake vector is placed correctly.
233 static void
234 cpu_foo_wake()
235 {
236 __asm__ volatile("_gossamer_cpu_wake:");
237 //kprintf("_gossamer_cpu_wake going to 0x100\n");
238 __asm__ volatile(" ba 0x100");
239 }
240 #endif // VERBOSE_LOGS_ON
241
242 // flushes the cash for a word at the given address.
243 #define cFlush(addr) __asm__ volatile("dcbf 0, %0" : : "r" (addr))
244
245 extern "C" {
246 void gossamer_cpu_wake(void);
247 extern void cacheInit(void);
248 extern void cacheDisable(void);
249 }
250
251 void GossamerCPU::quiesceCPU(void)
252 {
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);
255
256 // disables the interrupts (they should be already disabled, but one more tiem won't hurt):
257 ml_set_interrupts_enabled(FALSE);
258
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));
262
263 // Set the wake vector to point to the my checkpoint vector
264 ml_phys_write(restartReferencePhi, gossamer_cpu_wake); //restartAddress = gossamer_cpu_wake;
265 eieio();
266 #else
267 // Set the wake vector to point to the reset vector
268 ml_phys_write(restartReferencePhi, 0x100); //restartAddress = 0x100;
269 eieio();
270 #endif // VERBOSE_LOGS_ON
271
272 ml_phys_write(0x00000000, restartReferencePhi);
273 eieio();
274
275 // Set the wake vector to point to the reset vector
276 ml_phys_write(0x00000004, larsCode);
277 eieio();
278
279 // and flushes the data cache:
280 flush_dcache(restartReferencePhi, 4, true);
281 flush_dcache(0x00000000, 8, true);
282
283 // Also makes sure that the reset hander is correctly flushed:
284 flush_dcache(&ResetHandler, 12, true);
285
286 __asm__ volatile("sync");
287 __asm__ volatile("isync");
288
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));
293 #endif
294
295 // Send PMU command to shutdown system before io is turned off
296 if (pmu != 0)
297 pmu->callPlatformFunction("sleepNow", false, 0, 0, 0, 0);
298 else
299 kprintf("GossamerCPU::quiesceCPU can't find ApplePMU\n");
300
301 if (heathrow != NULL) {
302 heathrow->callPlatformFunction(heathrow_sleepState, false, (void *)true, 0, 0, 0);
303 }
304 else
305 kprintf("GossamerCPU::quiesceCPU not found Heathrow\n");
306
307 if (grackle != NULL) {
308 IOPCIAddressSpace grackleSpace;
309 UInt32 grackleProcConfiguration, grackleMemConfiguration;
310
311 #ifdef VERBOSE_LOGS_ON
312 kprintf("GossamerCPU::quiesceCPU AppleGracklePCI sets the ram in autorefresh\n");
313
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
318
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
324
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
330
331 grackle->configWrite32(grackleSpace, 0x70, grackleMemConfiguration);
332 }
333 else
334 kprintf("GossamerCPU::quiesceCPU not found AppleGracklePCI\n");
335
336 // Save time base before sleep since CPU's TBR will be set to zero at wake.
337 saveTimeBase(true);
338
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
341 // figure it out.
342 cacheInit();
343 cacheDisable();
344
345 #ifdef VERBOSE_LOGS_ON
346 kprintf("GossamerCPU::quiesceCPU calling ml_ppc_sleep\n");
347 #endif // VERBOSE_LOGS_ON
348
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.
356
357 while(true) {
358 }
359
360 //ml_ppc_sleep();
361 }
362
363 const OSSymbol *GossamerCPU::getCPUName(void)
364 {
365 return OSSymbol::withCStringNoCopy("Primary0");
366 }
367
368 kern_return_t GossamerCPU::startCPU(vm_offset_t /*start_paddr*/,
369 vm_offset_t /*arg_paddr*/)
370 {
371 return KERN_FAILURE;
372 }
373
374 void GossamerCPU::haltCPU(void)
375 {
376 long machine_type;
377 grackle = NULL;
378
379 grackle = (IOPCIBridge *)findIOInterface("AppleGracklePCI")->metaCast("IOPCIBridge");
380 if (grackle == NULL)
381 kprintf("GossamerCPU::haltCPU missing grackle\n");
382
383 pci2pciBridge = NULL;
384
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
390
391 /*
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());
399 }
400
401 if (pci2pciBridge != NULL)
402 pci2pciBridge->saveBridgeState();
403 */
404 #ifdef VERBOSE_LOGS_ON
405 kprintf("GossamerCPU::haltCPU Here!\n");
406 #endif // VERBOSE_LOGS_ON
407
408 gossamerPE = OSDynamicCast(GossamerPE, getPlatform());
409 if (gossamerPE == 0 )
410 {
411 processor_exit(machProcessor);
412 return;
413 }
414 machine_type = gossamerPE->getMachineType();
415
416 //Isolate only those Gossamers that have a Cuda, not PG&E
417 if ((machine_type != kGossamerType101) && (machine_type != kGossamerTypeWallstreet))
418 {
419 mach_timespec_t t;
420 IOService *cudaDriver;
421 IOService *usbOHCIDriver;
422 bool anyint = false;
423
424 t.tv_sec = 1;
425 t.tv_nsec = 0;
426 cudaDriver = waitForService(serviceMatching("AppleCuda"), &t);
427 usbOHCIDriver = waitForService(serviceMatching("AppleUSBOHCI"), &t);
428
429 if ((heathrow != NULL) && (machine_type == kGossamerTypeYosemite))
430 {
431 heathrow->callPlatformFunction(heathrow_set_light, false, (void *)false, 0, 0, 0);
432 }
433
434 gossamerPE->setProperty("GossamerCudaSleeping", true);
435 ml_throttle(254); //throttle cpu speed as much as possible
436
437 while (true) //sit here in a loop, pretending to be asleep
438 {
439 machine_idle(); //Max power savings for G3 CPU, needs interrupts enabled.
440 // It will return when any interrupt occurs
441 if (cudaDriver != NULL)
442 {
443 anyint = false;
444 cudaDriver->callPlatformFunction(cuda_check_any_interrupt, false, (void *)&anyint, 0, 0, 0);
445 if (anyint)
446 {
447 break;
448 }
449 }
450
451 if (usbOHCIDriver != NULL)
452 {
453 anyint = false;
454 usbOHCIDriver->callPlatformFunction(usb_remote_wakeup, false, (void *)&anyint, 0, 0, 0);
455 if (anyint)
456 {
457 break;
458 }
459 }
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.
462 }
463
464 ml_throttle(0); //remove throttle from CPU speed
465
466 gossamerPE->setProperty("GossamerCudaSleeping", false);
467
468 if ((heathrow != NULL) && (machine_type == kGossamerTypeYosemite))
469 {
470 heathrow->callPlatformFunction(heathrow_set_light, false, (void *)true, 0, 0, 0);
471 }
472
473 }
474 else
475 {
476 pmu = OSDynamicCast(IOService, findIOInterface("ApplePMU"));
477 processor_exit(machProcessor);
478 }
479 }
480
481 void GossamerCPU::saveTimeBase(bool save)
482 {
483 if(save) { // Save time base.
484 do {
485 tbHigh = mftbu();
486 tbLow = mftb();
487 tbHigh2 = mftbu();
488 } while (tbHigh != tbHigh2);
489 } else { // Restore time base
490 mttb(0);
491 mttbu(tbHigh);
492 mttb(tbLow);
493 }
494 }
495