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