/* * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1999-2000 Apple Computer, Inc. All rights reserved. * * DRI: Josh de Cesare * */ #include __BEGIN_DECLS #include __END_DECLS #include #include #include #include #include #include "Core99CPU.h" /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define super IOCPU OSDefineMetaClassAndStructors(Core99CPU, IOCPU); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static IOCPUInterruptController *gCPUIC; bool Core99CPU::start(IOService *provider) { kern_return_t result; IORegistryEntry *cpusRegEntry, *uniNRegEntry, *mpicRegEntry; OSIterator *cpusIterator; OSData *tmpData; IOService *service; const OSSymbol *interruptControllerName; OSData *interruptData; OSArray *tmpArray; UInt32 maxCPUs, uniNVersion, physCPU; ml_processor_info_t processor_info; core99PE = OSDynamicCast(Core99PE, getPlatform()); if (core99PE == 0) return false; // callPlatformFunction symbols mpic_getProvider = OSSymbol::withCString( "mpic_getProvider" ); mpic_getIPIVector = OSSymbol::withCString( "mpic_getIPIVector" ); mpic_setCurrentTaskPriority = OSSymbol::withCString( "mpic_setCurrentTaskPriority" ); mpic_setUpForSleep = OSSymbol::withCString( "mpic_setUpForSleep" ); mpic_dispatchIPI = OSSymbol::withCString( "mpic_dispatchIPI" ); keyLargo_restoreRegisterState = OSSymbol::withCString( "keyLargo_restoreRegisterState" ); keyLargo_syncTimeBase = OSSymbol::withCString( "keyLargo_syncTimeBase" ); keyLargo_saveRegisterState = OSSymbol::withCString( "keyLargo_saveRegisterState" ); keyLargo_turnOffIO = OSSymbol::withCString( "keyLargo_turnOffIO" ); keyLargo_writeRegUInt8 = OSSymbol::withCString( "keyLargo_writeRegUInt8" ); if (!super::start(provider)) return false; // Get the Uni-N Version. uniNRegEntry = fromPath("/uni-n", gIODTPlane); if (uniNRegEntry == 0) return false; tmpData = OSDynamicCast(OSData, uniNRegEntry->getProperty("device-rev")); if (tmpData == 0) return false; uniNVersion = *(long *)tmpData->getBytesNoCopy(); // Count the CPUs. numCPUs = 0; cpusRegEntry = fromPath("/cpus", gIODTPlane); if (cpusRegEntry == 0) return false; cpusIterator = cpusRegEntry->getChildIterator(gIODTPlane); while (cpusIterator->getNextObject()) numCPUs++; cpusIterator->release(); // Limit the number of CPUs to one if uniNVersion is 1.0.7 or less. if (uniNVersion < kUniNVersion107) numCPUs = 1; // Limit the number of CPUs by the cpu=# boot arg. if ( PE_parse_boot_arg("cpus", &maxCPUs) ) { if (numCPUs > maxCPUs) numCPUs = maxCPUs; } // Get the "flush-on-lock" property from the fisrt cpu node. flushOnLock = false; cpusRegEntry = fromPath("/cpus/@0", gIODTPlane); if (cpusRegEntry == 0) return false; if (cpusRegEntry->getProperty("flush-on-lock") != 0) flushOnLock = true; // Set flushOnLock when numCPUs is not one. if (numCPUs != 1) flushOnLock = true; // Get the physical CPU number from the "reg" property. tmpData = OSDynamicCast(OSData, provider->getProperty("reg")); if (tmpData == 0) return false; physCPU = *(long *)tmpData->getBytesNoCopy(); setCPUNumber(physCPU); // Find out if this is the boot CPU. bootCPU = false; tmpData = OSDynamicCast(OSData, provider->getProperty("state")); if (tmpData == 0) return false; if (!strcmp((char *)tmpData->getBytesNoCopy(), "running")) bootCPU = true; if (bootCPU) { gCPUIC = new IOCPUInterruptController; if (gCPUIC == 0) return false; if (gCPUIC->initCPUInterruptController(numCPUs) != kIOReturnSuccess) return false; gCPUIC->attach(this); gCPUIC->registerCPUInterruptController(); } // Get the l2cr value from the property list. tmpData = OSDynamicCast(OSData, provider->getProperty("l2cr")); if (tmpData != 0) { l2crValue = *(long *)tmpData->getBytesNoCopy() & 0x7FFFFFFF; } else { l2crValue = mfl2cr() & 0x7FFFFFFF; } // Wait for KeyLargo to show up. keyLargo = waitForService(serviceMatching("KeyLargo")); if (keyLargo == 0) return false; // Wait for MPIC to show up. mpic = waitForService(serviceMatching("AppleMPICInterruptController")); if (mpic == 0) return false; // Set the Interrupt Properties for this cpu. mpic->callPlatformFunction(mpic_getProvider, false, (void *)&mpicRegEntry, 0, 0, 0); interruptControllerName = IODTInterruptControllerName(mpicRegEntry); mpic->callPlatformFunction(mpic_getIPIVector, false, (void *)&physCPU, (void *)&interruptData, 0, 0); if ((interruptControllerName == 0) || (interruptData == 0)) return false; tmpArray = OSArray::withCapacity(1); tmpArray->setObject(interruptControllerName); cpuNub->setProperty(gIOInterruptControllersKey, tmpArray); tmpArray->release(); tmpArray = OSArray::withCapacity(1); tmpArray->setObject(interruptData); cpuNub->setProperty(gIOInterruptSpecifiersKey, tmpArray); tmpArray->release(); setCPUState(kIOCPUStateUninitalized); if (physCPU < numCPUs) { processor_info.cpu_id = (cpu_id_t)this; processor_info.boot_cpu = bootCPU; processor_info.start_paddr = 0x0100; processor_info.l2cr_value = l2crValue; processor_info.supports_nap = !flushOnLock; processor_info.time_base_enable = (time_base_enable_t)&Core99CPU::enableCPUTimeBase; // Register this CPU with mach. result = ml_processor_register(&processor_info, &machProcessor, &ipi_handler); if (result == KERN_FAILURE) return false; processor_start(machProcessor); } // Before to go to sleep we wish to disable the napping mode so that the PMU // will not shutdown the system while going to sleep: service = waitForService(serviceMatching("IOPMrootDomain")); IOPMrootDomain *pmRootDomain = OSDynamicCast(IOPMrootDomain, service); if (pmRootDomain != 0) { kprintf("Register Core99CPU %d to acknowledge power changes\n", getCPUNumber()); pmRootDomain->registerInterestedDriver(this); } registerService(); return true; } // This is called before to start the sleep process and after waking up before to // start the wake process. We wish to disble the CPU nap mode going down and // re-enable it before to go up: IOReturn Core99CPU::powerStateWillChangeTo ( IOPMPowerFlags theFlags, unsigned long, IOService*) { if ( ! (theFlags & IOPMPowerOn) ) { // Sleep sequence: kprintf("Core99CPU %d powerStateWillChangeTo to acknowledge power changes (DOWN) we set napping %d\n", getCPUNumber(), false); // Disable napping (the function returns the previous state) rememberNap = ml_enable_nap(getCPUNumber(), false); } else { // Wake sequence: kprintf("Core99CPU %d powerStateWillChangeTo to acknowledge power changes (UP) we set napping %d\n", getCPUNumber(), rememberNap); // Re-set the nap as it was before. ml_enable_nap(getCPUNumber(), rememberNap); } return IOPMAckImplied; } void Core99CPU::initCPU(bool boot) { if (!boot && bootCPU) { // Tell Uni-N to enter normal mode. core99PE->writeUniNReg(kUniNPowerMngmnt, kUniNNormal); // Set the running state for HWInit. core99PE->writeUniNReg(kUniNHWInitState, kUniNHWInitStateRunning); // Restore the PCI-PCI Bridge. if (decBridge) decBridge->restoreBridgeState(); keyLargo->callPlatformFunction(keyLargo_restoreRegisterState, false, 0, 0, 0, 0); if ((core99PE->getMachineType() == kCore99TypePowerMac3_1) || (core99PE->getMachineType() == kCore99TypePowerMac3_3)) { // Disables the interrupts for this CPU. kprintf("Core99CPU::initCPU %d -> mpic->setUpForSleep on", getCPUNumber()); mpic->callPlatformFunction(mpic_setUpForSleep, false, (void *)false, (void *)getCPUNumber(), 0, 0); } } kprintf("Core99CPU::initCPU %d Here!\n", getCPUNumber()); // Set time base. if (bootCPU) keyLargo->callPlatformFunction(keyLargo_syncTimeBase, false, 0, 0, 0, 0); if (boot) { gCPUIC->enableCPUInterrupt(this); // Register and enable IPIs. cpuNub->registerInterrupt(0, this, (IOInterruptAction)&Core99CPU::ipiHandler, 0); cpuNub->enableInterrupt(0); } else { long priority = 0; mpic->callPlatformFunction(mpic_setCurrentTaskPriority, false, (void *)&priority, 0, 0, 0); } setCPUState(kIOCPUStateRunning); } void Core99CPU::quiesceCPU(void) { if (bootCPU) { // Send PMU command to shutdown system before io is turned off if (pmu != 0) pmu->callPlatformFunction("sleepNow", false, 0, 0, 0, 0); else kprintf("Core99CPU::quiesceCPU can't find ApplePMU\n"); if ((core99PE->getMachineType() == kCore99TypePowerMac3_1) || (core99PE->getMachineType() == kCore99TypePowerMac3_3)) { // Disables the interrupts for this CPU. kprintf("Core99CPU::quiesceCPU %d -> mpic->setUpForSleep off", getCPUNumber()); mpic->callPlatformFunction(mpic_setUpForSleep, false, (void *)true, (void *)getCPUNumber(), 0, 0); } kprintf("Core99CPU::quiesceCPU %d -> keyLargo->saveRegisterState()\n", getCPUNumber()); // Save KeyLargo's register state. keyLargo->callPlatformFunction(keyLargo_saveRegisterState, false, 0, 0, 0, 0); kprintf("Core99CPU::quiesceCPU %d -> keyLargo->turnOffIO", getCPUNumber()); // Turn Off all KeyLargo I/O. keyLargo->callPlatformFunction(keyLargo_turnOffIO, false, (void *)false, 0, 0, 0); // Set the wake vector to point to the reset vector ml_phys_write(0x0080, 0x100); // Set the sleeping state for HWInit. core99PE->writeUniNReg(kUniNHWInitState, kUniNHWInitStateSleeping); // Tell Uni-N to enter sleep mode. core99PE->writeUniNReg(kUniNPowerMngmnt, kUniNSleep); } ml_ppc_sleep(); } kern_return_t Core99CPU::startCPU(vm_offset_t /*start_paddr*/, vm_offset_t /*arg_paddr*/) { long gpioOffset; switch (getCPUNumber()) { case 0 : gpioOffset = 0x5B; break; case 1 : gpioOffset = 0x5C; break; case 2 : gpioOffset = 0x67; break; case 3 : gpioOffset = 0x68; break; default : return KERN_FAILURE; } // Strobe the reset line for this CPU. keyLargo->callPlatformFunction(keyLargo_writeRegUInt8, false, (void *)&gpioOffset, (void *)4, 0, 0); keyLargo->callPlatformFunction(keyLargo_writeRegUInt8, false, (void *)&gpioOffset, (void *)5, 0, 0); return KERN_SUCCESS; } void Core99CPU::haltCPU(void) { IORegistryEntry *decBridgeEntry; IOService *decBridgeNub; setCPUState(kIOCPUStateStopped); if (bootCPU) { // Find the DEC Bridge if it is there. decBridge = 0; decBridgeEntry = fromPath("/pci@f2000000/@d", gIODTPlane); decBridgeNub = OSDynamicCast(IOService, decBridgeEntry); if (decBridgeNub != 0) { decBridge = OSDynamicCast(IOPCI2PCIBridge, decBridgeNub->getClient()); } } // Finds the PMU so in quience we can put the machine to sleep. // I can not put this call there because quience runs in interrupt // context and waitForService may block. pmu = waitForService(serviceMatching("ApplePMU")); kprintf("Core99CPU::haltCPU %d Here!\n", getCPUNumber()); processor_exit(machProcessor); } void Core99CPU::signalCPU(IOCPU *target) { UInt32 physCPU = getCPUNumber(); Core99CPU *targetCPU = OSDynamicCast(Core99CPU, target); if (targetCPU == 0) return; mpic->callPlatformFunction(mpic_dispatchIPI, false, (void *)&physCPU, (void *)(1 << targetCPU->getCPUNumber()), 0, 0); } void Core99CPU::enableCPUTimeBase(bool enable) { long gpioOffset; UInt8 value; gpioOffset = 0x73; value = enable ? 5 : 4; keyLargo->callPlatformFunction(keyLargo_writeRegUInt8, false, (void *)&gpioOffset, (void *)value, 0, 0); } void Core99CPU::ipiHandler(void *refCon, void *nub, int source) { // Call mach IPI handler for this CPU. if (ipi_handler) ipi_handler(); } const OSSymbol *Core99CPU::getCPUName(void) { char tmpStr[256]; sprintf(tmpStr, "Primary%ld", getCPUNumber()); return OSSymbol::withCString(tmpStr); }