#include <sys/cdefs.h>
__BEGIN_DECLS
#include <ppc/proc_reg.h>
__END_DECLS
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/IOCPU.h>
#include <IOKit/pci/IOPCIBridge.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#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;
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;
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();
numCPUs = 0;
cpusRegEntry = fromPath("/cpus", gIODTPlane);
if (cpusRegEntry == 0)
return false;
cpusIterator = cpusRegEntry->getChildIterator(gIODTPlane);
while (cpusIterator->getNextObject())
numCPUs++;
cpusIterator->release();
if (uniNVersion < kUniNVersion107)
numCPUs = 1;
if ( PE_parse_boot_arg("cpus", &maxCPUs) )
{
if (numCPUs > maxCPUs)
numCPUs = maxCPUs;
}
flushOnLock = false;
cpusRegEntry = fromPath("/cpus/@0", gIODTPlane);
if (cpusRegEntry == 0)
return false;
if (cpusRegEntry->getProperty("flush-on-lock") != 0)
flushOnLock = true;
if (numCPUs != 1)
flushOnLock = true;
tmpData = OSDynamicCast(OSData, provider->getProperty("reg"));
if (tmpData == 0)
return false;
physCPU = *(long *)tmpData->getBytesNoCopy();
setCPUNumber(physCPU);
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();
}
tmpData = OSDynamicCast(OSData, provider->getProperty("l2cr"));
if (tmpData != 0)
{
l2crValue = *(long *)tmpData->getBytesNoCopy() & 0x7FFFFFFF;
}
else
{
#if !defined(__i386__)
l2crValue = mfl2cr() & 0x7FFFFFFF;
#endif
}
keyLargo = waitForService(serviceMatching("KeyLargo"));
if (keyLargo == 0)
return false;
mpic = waitForService(serviceMatching("AppleMPICInterruptController"));
if (mpic == 0)
return false;
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 = OSMemberFunctionCast(time_base_enable_t, this, &Core99CPU::enableCPUTimeBase);
#if !defined(__i386__)
result = ml_processor_register(&processor_info, &machProcessor, &ipi_handler);
if (result == KERN_FAILURE)
return false;
#endif
processor_start(machProcessor);
}
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;
}
IOReturn Core99CPU::powerStateWillChangeTo ( IOPMPowerFlags theFlags, unsigned long, IOService*)
{
if ( ! (theFlags & IOPMPowerOn) )
{
kprintf("Core99CPU %d powerStateWillChangeTo to acknowledge power changes (DOWN) we set napping %d\n",
getCPUNumber(), false);
#if !defined(__i386__)
rememberNap = ml_enable_nap(getCPUNumber(), false);
#endif
}
else
{
kprintf("Core99CPU %d powerStateWillChangeTo to acknowledge power changes (UP) we set napping %d\n",
getCPUNumber(), rememberNap);
#if !defined(__i386__)
ml_enable_nap(getCPUNumber(), rememberNap);
#endif
}
return IOPMAckImplied;
}
void Core99CPU::initCPU(bool boot)
{
if (!boot && bootCPU)
{
core99PE->writeUniNReg(kUniNPowerMngmnt, kUniNNormal);
core99PE->writeUniNReg(kUniNHWInitState, kUniNHWInitStateRunning);
if (decBridge) decBridge->restoreBridgeState();
keyLargo->callPlatformFunction(keyLargo_restoreRegisterState, false, 0, 0, 0, 0);
if ((core99PE->getMachineType() == kCore99TypePowerMac3_1) ||
(core99PE->getMachineType() == kCore99TypePowerMac3_3))
{
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());
if (bootCPU)
keyLargo->callPlatformFunction(keyLargo_syncTimeBase, false, 0, 0, 0, 0);
if (boot)
{
gCPUIC->enableCPUInterrupt(this);
cpuNub->registerInterrupt(0, this, OSMemberFunctionCast(IOInterruptAction, this, &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)
{
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))
{
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());
keyLargo->callPlatformFunction(keyLargo_saveRegisterState, false, 0, 0, 0, 0);
kprintf("Core99CPU::quiesceCPU %d -> keyLargo->turnOffIO", getCPUNumber());
keyLargo->callPlatformFunction(keyLargo_turnOffIO, false, (void *)false, 0, 0, 0);
ml_phys_write(0x0080, 0x100);
core99PE->writeUniNReg(kUniNHWInitState, kUniNHWInitStateSleeping);
core99PE->writeUniNReg(kUniNPowerMngmnt, kUniNSleep);
}
#if !defined(__i386__)
ml_ppc_sleep();
#endif
}
kern_return_t Core99CPU::startCPU(vm_offset_t , vm_offset_t )
{
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;
}
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)
{
decBridge = 0;
decBridgeEntry = fromPath("/pci@f2000000/@d", gIODTPlane);
decBridgeNub = OSDynamicCast(IOService, decBridgeEntry);
if (decBridgeNub != 0)
{
decBridge = OSDynamicCast(IOPCI2PCIBridge, decBridgeNub->getClient());
}
}
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)
{
if (ipi_handler) ipi_handler();
}
const OSSymbol *Core99CPU::getCPUName(void)
{
char tmpStr[256];
sprintf(tmpStr, "Primary%ld", getCPUNumber());
return OSSymbol::withCString(tmpStr);
}