/* * Copyright (c) 1998-2002 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-2002 Apple Computer, Inc. All rights reserved. * * DRI: Dave Radcliffe * */ #include __BEGIN_DECLS #include #include /* Map memory map IO space */ #include extern vm_offset_t ml_io_map(vm_offset_t phys_addr, vm_size_t size); __END_DECLS #include #include #include "MacRISC2.h" #include static unsigned long macRISC2Speed[] = { 0, 1 }; #include #include "IOPMSlotsMacRISC2.h" #include "IOPMUSBMacRISC2.h" #include #include extern char *gIOMacRISC2PMTree; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define super ApplePlatformExpert OSDefineMetaClassAndStructors(MacRISC2PE, ApplePlatformExpert); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ bool MacRISC2PE::start(IOService *provider) { long machineType; OSData *tmpData; IORegistryEntry *uniNRegEntry; IORegistryEntry *powerMgtEntry; UInt32 *primInfo; UInt32 uniNArbCtrl, uniNBaseAddressTemp, uniNMPCIMemTimeout; UInt32 stepType; OSDictionary *propTable; OSCollectionIterator *propIter; OSString *propKey; OSData *propVal; SInt32 numFuncs, retval; setChipSetType(kChipSetTypeCore2001); // Set the machine type. provider_name = provider->getName(); machineType = kMacRISC2TypeUnknown; doPlatformPowerMonitor = false; if (provider_name != NULL) { if (0 == strncmp(provider_name, "PowerMac", strlen("PowerMac"))) machineType = kMacRISC2TypePowerMac; else if (0 == strncmp(provider_name, "RackMac", strlen("RackMac"))) machineType = kMacRISC2TypePowerMac; else if (0 == strncmp(provider_name, "PowerBook", strlen("PowerBook"))) machineType = kMacRISC2TypePowerBook; else if (0 == strncmp(provider_name, "iBook", strlen("iBook"))) machineType = kMacRISC2TypePowerBook; else // kMacRISC2TypeUnknown IOLog ("AppleMacRISC2PE - warning: unknown machineType\n"); } isPortable = (machineType == kMacRISC2TypePowerBook); setMachineType(machineType); // Get the bus speed from the Device Tree. tmpData = OSDynamicCast(OSData, provider->getProperty("clock-frequency")); if (tmpData == 0) return false; macRISC2Speed[0] = *(unsigned long *)tmpData->getBytesNoCopy(); // Get a memory mapping for Uni-N's registers. uniNRegEntry = provider->childFromPath("uni-n", gIODTPlane); if (uniNRegEntry == 0) return false; tmpData = OSDynamicCast(OSData, uniNRegEntry->getProperty("reg")); if (tmpData == 0) return false; uniNBaseAddressTemp = ((unsigned long *)tmpData->getBytesNoCopy())[0]; uniNBaseAddress = (unsigned long *)ml_io_map(uniNBaseAddressTemp, 0x3200); if (uniNBaseAddress == 0) return false; // Set QAckDelay depending on the version of Uni-N. uniNVersion = readUniNReg(kUniNVersion); if (uniNVersion < kUniNVersion150) { uniNArbCtrl = readUniNReg(kUniNArbCtrl); uniNArbCtrl &= ~kUniNArbCtrlQAckDelayMask; if (uniNVersion < kUniNVersion107) { uniNArbCtrl |= kUniNArbCtrlQAckDelay105 << kUniNArbCtrlQAckDelayShift; } else { uniNArbCtrl |= kUniNArbCtrlQAckDelay << kUniNArbCtrlQAckDelayShift; } writeUniNReg(kUniNArbCtrl, uniNArbCtrl); } // Set Max/PCI Memory Timeout for appropriate Uni-N. if ( ((uniNVersion >= kUniNVersion150) && (uniNVersion <= kUniNVersion200)) || (uniNVersion == kUniNVersionPangea) ) { uniNMPCIMemTimeout = readUniNReg(kUniNMPCIMemTimeout); uniNMPCIMemTimeout &= ~kUniNMPCIMemTimeoutMask; uniNMPCIMemTimeout |= kUniNMPCIMemGrantTime; writeUniNReg(kUniNMPCIMemTimeout, uniNMPCIMemTimeout); } // Parse platform function commands for uni-n numFuncs = 0; if ( ((propTable = uniNRegEntry->dictionaryWithProperties()) == 0) || ((propIter = OSCollectionIterator::withCollection(propTable)) == 0) ) { if (propTable) propTable->release(); return(false); } while ((propKey = OSDynamicCast(OSString, propIter->getNextObject())) != 0) { if (strncmp(kFunctionProvidedPrefix, propKey->getCStringNoCopy(), strlen(kFunctionProvidedPrefix)) == 0) { propVal = OSDynamicCast(OSData, propTable->getObject(propKey)); if ((retval = parseProvidedFunction(propKey, propVal)) < 0) { // something went wrong, free resources and bail out releaseResources(); numFuncs = 0; break; } else numFuncs += retval; } } if (propTable) { propTable->release(); propTable = 0; } if (propIter) { propIter->release(); propIter = 0; } // publish on-demand and services in IOResources if (fOnDemand) publishStrings(fOnDemand); // Creates the nubs for the children of uni-n IOService *uniNServiceEntry = OSDynamicCast(IOService, uniNRegEntry); if (uniNServiceEntry != NULL) createNubs(this, uniNRegEntry->getChildIterator( gIODTPlane )); // Get PM features and private features powerMgtEntry = retrievePowerMgtEntry (); if (powerMgtEntry == 0) { kprintf ("didn't find power mgt node\n"); return false; } tmpData = OSDynamicCast(OSData, powerMgtEntry->getProperty ("prim-info")); if (tmpData != 0) { primInfo = (unsigned long *)tmpData->getBytesNoCopy(); if (primInfo != 0) { _pePMFeatures = primInfo[3]; _pePrivPMFeatures = primInfo[4]; _peNumBatteriesSupported = ((primInfo[6]>>16) & 0x000000FF); kprintf ("Public PM Features: %0x.\n",_pePMFeatures); kprintf ("Privat PM Features: %0x.\n",_pePrivPMFeatures); kprintf ("Num Internal Batteries Supported: %0x.\n", _peNumBatteriesSupported); } } // This is to make sure that is PMRegisterDevice reentrant mutex = IOLockAlloc(); if (mutex == NULL) return false; else IOLockInit( mutex ); // Set up processorSpeedChangeFlags depending on platform processorSpeedChangeFlags = kNoSpeedChange; stepType = 0; if (machineType == kMacRISC2TypePowerBook) { OSIterator *childIterator; IORegistryEntry *cpuEntry, *powerPCEntry; OSData *cpuSpeedData, *stepTypeData; // locate the first PowerPC,xx cpu node so we can get clock properties cpuEntry = provider->childFromPath("cpus", gIODTPlane); if ((childIterator = cpuEntry->getChildIterator (gIODTPlane)) != NULL) { while ((powerPCEntry = (IORegistryEntry *)(childIterator->getNextObject ())) != NULL) { if (!strncmp ("PowerPC", powerPCEntry->getName(gIODTPlane), strlen ("PowerPC"))) { // Look for dynamic power step feature stepTypeData = OSDynamicCast( OSData, powerPCEntry->getProperty( "dynamic-power-step" )); if (stepTypeData) processorSpeedChangeFlags = kProcessorBasedSpeedChange | kProcessorFast | kL3CacheEnabled | kL2CacheEnabled; else { // Look for forced-reduced-speed case stepTypeData = OSDynamicCast( OSData, powerPCEntry->getProperty( "force-reduced-speed" )); cpuSpeedData = OSDynamicCast( OSData, powerPCEntry->getProperty( "max-clock-frequency" )); if (stepTypeData && cpuSpeedData) { UInt32 newCPUSpeed, newNum; doPlatformPowerMonitor = true; // At minimum disable L3 cache // Note that caches are enabled at this point, but the processor may not be at full speed. processorSpeedChangeFlags = kDisableL3SpeedChange | kL3CacheEnabled | kL2CacheEnabled; if (stepTypeData->getLength() > 0) stepType = *(UInt32 *) stepTypeData->getBytesNoCopy(); newCPUSpeed = *(UInt32 *) cpuSpeedData->getBytesNoCopy(); if (newCPUSpeed != gPEClockFrequencyInfo.cpu_clock_rate_hz) { // If max cpu speed is greater than what OF reported to us // then enable PMU speed change in addition to L3 speed change if ((_pePrivPMFeatures & (1 << 17)) != 0) processorSpeedChangeFlags |= kPMUBasedSpeedChange; processorSpeedChangeFlags |= kEnvironmentalSpeedChange; // Also fix up internal clock rates newNum = newCPUSpeed / (gPEClockFrequencyInfo.cpu_clock_rate_hz / gPEClockFrequencyInfo.bus_to_cpu_rate_num); gPEClockFrequencyInfo.bus_to_cpu_rate_num = newNum; // Set new numerator gPEClockFrequencyInfo.cpu_clock_rate_hz = newCPUSpeed; // Set new speed } } else // All other notebooks if ((_pePrivPMFeatures & (1 << 17)) != 0) processorSpeedChangeFlags = kPMUBasedSpeedChange | kProcessorFast | kL3CacheEnabled | kL2CacheEnabled; } break; } } childIterator->release(); } } // Init power monitor states. This should be driven by data in the device-tree if (doPlatformPowerMonitor) { powerMonWeakCharger.bitsSet = kIOPMACInstalled | kIOPMACnoChargeCapability; powerMonWeakCharger.bitsClear = 0; powerMonWeakCharger.bitsMask = powerMonWeakCharger.bitsSet | powerMonWeakCharger.bitsClear; powerMonBatteryWarning.bitsSet = kIOPMRawLowBattery; powerMonBatteryWarning.bitsClear = 0; powerMonBatteryWarning.bitsMask = powerMonBatteryWarning.bitsSet | powerMonBatteryWarning.bitsClear; powerMonBatteryDepleted.bitsSet = kIOPMBatteryDepleted; powerMonBatteryDepleted.bitsClear = 0; powerMonBatteryDepleted.bitsMask = powerMonBatteryDepleted.bitsSet | powerMonBatteryDepleted.bitsClear; powerMonBatteryNotInstalled.bitsSet = 0; powerMonBatteryNotInstalled.bitsClear = kIOPMBatteryInstalled; powerMonBatteryNotInstalled.bitsMask = powerMonBatteryNotInstalled.bitsSet | powerMonBatteryNotInstalled.bitsClear; if ((stepType & 1) == 0) { powerMonClamshellClosed.bitsSet = kIOPMClosedClamshell; powerMonClamshellClosed.bitsClear = 0; powerMonClamshellClosed.bitsMask = powerMonClamshellClosed.bitsSet | powerMonClamshellClosed.bitsClear; } else { // Don't do anything on clamshell closed powerMonClamshellClosed.bitsMask = powerMonClamshellClosed.bitsSet = 0xFFFFFFFF; powerMonClamshellClosed.bitsClear = 0; } powerMonForceLowPower.bitsSet = kIOPMForceLowSpeed; powerMonForceLowPower.bitsClear = 0; powerMonForceLowPower.bitsMask = powerMonForceLowPower.bitsSet | powerMonForceLowPower.bitsClear; } else { // Assume no power monitoring powerMonWeakCharger.bitsMask = powerMonWeakCharger.bitsSet = 0xFFFFFFFF; powerMonWeakCharger.bitsClear = 0; powerMonBatteryWarning.bitsMask = powerMonBatteryWarning.bitsSet = 0xFFFFFFFF; powerMonBatteryWarning.bitsClear = 0; powerMonBatteryDepleted.bitsMask = powerMonBatteryDepleted.bitsSet = 0xFFFFFFFF; powerMonBatteryDepleted.bitsClear = 0; powerMonBatteryNotInstalled.bitsMask = powerMonBatteryNotInstalled.bitsSet = 0xFFFFFFFF; powerMonBatteryNotInstalled.bitsClear = 0; powerMonClamshellClosed.bitsMask = powerMonClamshellClosed.bitsSet = 0xFFFFFFFF; powerMonClamshellClosed.bitsClear = 0; powerMonForceLowPower.bitsMask = 0xFFFFFFFF; powerMonForceLowPower.bitsSet = powerMonForceLowPower.bitsClear = 0; // Assume we never set low power } return super::start(provider); } IORegistryEntry * MacRISC2PE::retrievePowerMgtEntry (void) { IORegistryEntry * theEntry = 0; IORegistryEntry * anObj = 0; IORegistryIterator * iter; OSString * powerMgtNodeName; iter = IORegistryIterator::iterateOver (IORegistryEntry::getPlane(kIODeviceTreePlane), kIORegistryIterateRecursively); if (iter) { powerMgtNodeName = OSString::withCString("power-mgt"); anObj = iter->getNextObject (); while (anObj) { if (anObj->compareName(powerMgtNodeName)) { theEntry = anObj; break; } anObj = iter->getNextObject(); } powerMgtNodeName->release(); iter->release (); } return theEntry; } bool MacRISC2PE::platformAdjustService(IOService *service) { const OSSymbol *tmpSymbol, *keySymbol; bool result; if (IODTMatchNubWithKeys(service, "open-pic")) { keySymbol = OSSymbol::withCStringNoCopy("InterruptControllerName"); tmpSymbol = IODTInterruptControllerName(service); result = service->setProperty(keySymbol, tmpSymbol); return true; } if (!strcmp(service->getName(), "programmer-switch")) { // Set property to tell AppleNMI to mask/unmask NMI @ sleep/wake service->setProperty("mask_NMI", service); return true; } if (!strcmp(service->getName(), "pmu")) { // Change the interrupt mapping for pmu source 4. OSArray *tmpArray; OSCollectionIterator *extIntList; IORegistryEntry *extInt; OSObject *extIntControllerName; OSObject *extIntControllerData; // Set the no-nvram property. service->setProperty("no-nvram", service); // Find the new interrupt information. extIntList = IODTFindMatchingEntries(getProvider(), kIODTRecursive, "'extint-gpio1'"); extInt = (IORegistryEntry *)extIntList->getNextObject(); tmpArray = (OSArray *)extInt->getProperty(gIOInterruptControllersKey); extIntControllerName = tmpArray->getObject(0); tmpArray = (OSArray *)extInt->getProperty(gIOInterruptSpecifiersKey); extIntControllerData = tmpArray->getObject(0); // Replace the interrupt infomation for pmu source 4. tmpArray = (OSArray *)service->getProperty(gIOInterruptControllersKey); tmpArray->replaceObject(4, extIntControllerName); tmpArray = (OSArray *)service->getProperty(gIOInterruptSpecifiersKey); tmpArray->replaceObject(4, extIntControllerData); extIntList->release(); return true; } if (!strcmp(service->getName(), "via-pmu")) { service->setProperty("BusSpeedCorrect", this); return true; } /* * For uni-n pci version 1.5 which services mac-io, tell pci driver to disable read data gating * -- Note that previous versions of this driver defined version 1.5 as 0x0010. This is not * correct and we now use 0x0011 for uni-n 1.5. */ if ((uniNVersion == kUniNVersion150) && IODTMatchNubWithKeys(service, "('pci', 'uni-north')") && (service->childFromPath("mac-io", gIODTPlane) != NULL)) { service->setProperty ("DisableRDG", true); return true; } /* * For Firewire on uni-n Pangea & uni-n 1.5 through 2.0, adjust the cache line size and timer latency */ if (((uniNVersion >= kUniNVersion150) && (uniNVersion <= kUniNVersion200) || (uniNVersion == kUniNVersionPangea)) && (!strcmp(service->getName(gIODTPlane), "firewire")) && IODTMatchNubWithKeys(service->getParentEntry(gIODTPlane), "('pci', 'uni-north')")) { char data; data = 0x08; // 32 byte cache line service->setProperty (kIOPCICacheLineSize, &data, 1); data = 0x40; // latency timer to 40 service->setProperty (kIOPCITimerLatency, &data, 1); return true; } /* * For Ethernet on uni-n 2.0, adjust the cache line size and timer latency */ if ((uniNVersion == kUniNVersion200) && IODTMatchNubWithKeys(service, "('ethernet', 'gmac')") && IODTMatchNubWithKeys(service->getParentEntry(gIODTPlane), "('pci', 'uni-north')")) { char data; long cacheSize; OSData *cacheData; data = 0x08; // assume default of 32 byte cache line cacheData = OSDynamicCast( OSData, service->getProperty( "cache-line-size" ) ); if (cacheData) { cacheSize = *(long *)cacheData->getBytesNoCopy(); data = (cacheSize >> 2); } service->setProperty (kIOPCICacheLineSize, &data, 1); data = 0x20; // latency timer to 20 service->setProperty (kIOPCITimerLatency, &data, 1); return true; } // For usb@18 on PowerBook4,x machines add an "AAPL,SuspendablePorts" property if it doesn't exist if (!strcmp(service->getName(), "usb") && (0 == strncmp(provider_name, "PowerBook4,", strlen("PowerBook4,"))) && IODTMatchNubWithKeys(service->getParentEntry(gIODTPlane), "('pci', 'uni-north')")) { OSData *regProp; IOPCIAddressSpace *pciAddress; UInt32 ports; if( (regProp = (OSData *) service->getProperty("reg"))) { pciAddress = (IOPCIAddressSpace *) regProp->getBytesNoCopy(); // Only for usb@18 if (pciAddress->s.deviceNum == 0x18) { // If property doesn't exist, create it if(!((OSData *) service->getProperty(kAAPLSuspendablePorts))) { ports = 4; service->setProperty (kAAPLSuspendablePorts, &ports, sizeof(UInt32)); } return true; } } } return true; } IOReturn MacRISC2PE::callPlatformFunction(const OSSymbol *functionName, bool waitForFunction, void *param1, void *param2, void *param3, void *param4) { OSData *func; if (functionName == gGetDefaultBusSpeedsKey) { getDefaultBusSpeeds((long *)param1, (unsigned long **)param2); return kIOReturnSuccess; } if (functionName->isEqualTo("EnableUniNEthernetClock")) { enableUniNEthernetClock((bool)param1, (IOService *)param2); return kIOReturnSuccess; } if (functionName->isEqualTo("EnableFireWireClock")) { enableUniNFireWireClock((bool)param1, (IOService *)param2); return kIOReturnSuccess; } if (functionName->isEqualTo("EnableFireWireCablePower")) { enableUniNFireWireCablePower((bool)param1); return kIOReturnSuccess; } if (functionName->isEqualTo("AccessUniN15PerformanceRegister")) { return accessUniN15PerformanceRegister((bool)param1, (long)param2, (unsigned long *)param3); } if (functionName->isEqualTo("PlatformIsPortable")) { *(bool *) param1 = isPortable; return kIOReturnSuccess; } if (functionName->isEqualTo("PlatformPowerMonitor")) { return platformPowerMonitor ((UInt32 *) param1); } // Is this an on-demand platform function? if (fOnDemand && (func = OSDynamicCast(OSData, fOnDemand->getObject(functionName))) != 0) { if (performFunction(func, param1, param2, param3, param4)) return kIOReturnSuccess; else return kIOReturnError; } return super::callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4); } unsigned long MacRISC2PE::readUniNReg(unsigned long offset) { return uniNBaseAddress[offset / 4]; } void MacRISC2PE::writeUniNReg(unsigned long offset, unsigned long data) { uniNBaseAddress[offset / 4] = data; eieio(); } void MacRISC2PE::getDefaultBusSpeeds(long *numSpeeds, unsigned long **speedList) { if ((numSpeeds == 0) || (speedList == 0)) return; *numSpeeds = 1; *speedList = macRISC2Speed; } void MacRISC2PE::enableUniNEthernetClock(bool enable, IOService *nub) { unsigned long regTemp; if (enable) configureUniNPCIDevice(nub); if (mutex != NULL) IOLockLock(mutex); regTemp = readUniNReg(kUniNClockControl); if (enable) { regTemp |= kUniNEthernetClockEnable; } else { regTemp &= ~kUniNEthernetClockEnable; } writeUniNReg(kUniNClockControl, regTemp); if (mutex != NULL) IOLockUnlock(mutex); } void MacRISC2PE::enableUniNFireWireClock(bool enable, IOService *nub) { unsigned long regTemp; if (enable) configureUniNPCIDevice(nub); //IOLog("FWClock, enable = %d kFW = %x\n", enable, kUniNFirewireClockEnable); if (mutex != NULL) IOLockLock(mutex); regTemp = readUniNReg(kUniNClockControl); if (enable) { regTemp |= kUniNFirewireClockEnable; } else { regTemp &= ~kUniNFirewireClockEnable; } writeUniNReg(kUniNClockControl, regTemp); if (mutex != NULL) IOLockUnlock(mutex); } void MacRISC2PE::enableUniNFireWireCablePower(bool enable) { // Turn off cable power supply on mid/merc/pismo(on pismo only, this kills the phy) if(getMachineType() == kMacRISC2TypePowerBook) { IOService *keyLargo; keyLargo = waitForService(serviceMatching("KeyLargo")); if(keyLargo) { UInt32 gpioOffset = 0x73; keyLargo->callPlatformFunction(OSSymbol::withCString("keyLargo_writeRegUInt8"), true, (void *)&gpioOffset, (void *)(enable ? 0:4), 0, 0); } } } void MacRISC2PE::configureUniNPCIDevice (IOService *nub) { OSData *cacheData, *latencyData; IOPCIDevice *provider; if (nub) provider = OSDynamicCast(IOPCIDevice, nub); else provider = NULL; if (provider) { cacheData = (OSData *)provider->getProperty (kIOPCICacheLineSize); latencyData = (OSData *)provider->getProperty (kIOPCITimerLatency); if (cacheData || latencyData) { UInt32 configData; configData = provider->configRead32 (kIOPCIConfigCacheLineSize); if (cacheData) configData = (configData & 0xFFFFFF00) | *(char *) cacheData->getBytesNoCopy(); if (latencyData) configData = (configData & 0xFFFF00FF) | ((*(char *) latencyData->getBytesNoCopy()) << 8); provider->configWrite32 (kIOPCIConfigCacheLineSize, configData); } } return; } enum { kMCMonitorModeControl = 0, kMCCommand, kMCPerformanceMonitor0, kMCPerformanceMonitor1, kMCPerformanceMonitor2, kMCPerformanceMonitor3 }; IOReturn MacRISC2PE::accessUniN15PerformanceRegister(bool write, long regNumber, unsigned long *data) { unsigned long offset; if (uniNVersion < kUniNVersion150) return kIOReturnUnsupported; switch (regNumber) { case kMCMonitorModeControl : offset = kUniNMMCR; break; case kMCCommand : offset = kUniNMCMDR; break; case kMCPerformanceMonitor0 : offset = kUniNMPMC1; break; case kMCPerformanceMonitor1 : offset = kUniNMPMC2; break; case kMCPerformanceMonitor2 : offset = kUniNMPMC3; break; case kMCPerformanceMonitor3 : offset = kUniNMPMC4; break; default : return kIOReturnBadArgument; } if (data == 0) return kIOReturnBadArgument; if (write) { writeUniNReg(offset, *data); } else { *data = readUniNReg(offset); } return kIOReturnSuccess; } //********************************************************************************* // platformPowerMonitor // // A call platform function call called by the ApplePMU driver. ApplePMU call us // with a set of power flags. We examine those flags and modify the state // according to the characteristics of the platform. // // If necessary, we force an immediate change in the power state //********************************************************************************* IOReturn MacRISC2PE::platformPowerMonitor(UInt32 *powerFlags) { IOReturn result; if (doPlatformPowerMonitor) { // First check primary power conditions if (((*powerFlags & powerMonWeakCharger.bitsMask) == (powerMonWeakCharger.bitsMask & powerMonWeakCharger.bitsSet & ~powerMonWeakCharger.bitsClear)) || ((*powerFlags & powerMonBatteryWarning.bitsMask) == (powerMonBatteryWarning.bitsMask & powerMonBatteryWarning.bitsSet & ~powerMonBatteryWarning.bitsClear)) || ((*powerFlags & powerMonBatteryDepleted.bitsMask) == (powerMonBatteryDepleted.bitsMask & powerMonBatteryDepleted.bitsSet & ~powerMonBatteryDepleted.bitsClear)) || ((*powerFlags & powerMonBatteryNotInstalled.bitsMask) == (powerMonBatteryNotInstalled.bitsMask & powerMonBatteryNotInstalled.bitsSet & ~powerMonBatteryNotInstalled.bitsClear))) { /* * For these primary power conditions we signal the power manager to force low power state * This includes both reduced processor speed and disabled L3 cache. */ *powerFlags |= (powerMonForceLowPower.bitsMask & powerMonForceLowPower.bitsSet); /* * If we previously speed changed due to a closed clamshell and the L3 cache is still enabled * we must call through to get the L3 cache disabled as well */ if (processorSpeedChangeFlags & kL3CacheEnabled) { if (!macRISC2CPU) macRISC2CPU = waitForService (serviceMatching("MacRISC2CPU")); if (macRISC2CPU) { processorSpeedChangeFlags &= ~kClamshellClosedSpeedChange; macRISC2CPU->setAggressiveness (kPMSetProcessorSpeed, 1); // Force slow now so cache state is right } } } else if ((*powerFlags & powerMonClamshellClosed.bitsMask) == (powerMonClamshellClosed.bitsMask & powerMonClamshellClosed.bitsSet & ~powerMonClamshellClosed.bitsClear)) { /* * clamShell closed with no other power conditions is a special case -- * leave L3 cache enabled */ *powerFlags |= (powerMonForceLowPower.bitsMask & powerMonForceLowPower.bitsSet); if (!(processorSpeedChangeFlags & kL3CacheEnabled)) { if (!macRISC2CPU) macRISC2CPU = waitForService (serviceMatching("MacRISC2CPU")); if (macRISC2CPU) { if (processorSpeedChangeFlags & kPMUBasedSpeedChange) { // Only want setAggressiveness to enable cache processorSpeedChangeFlags &= ~kPMUBasedSpeedChange; macRISC2CPU->setAggressiveness (kPMSetProcessorSpeed, 0); // Force fast now so cache state is right processorSpeedChangeFlags |= kPMUBasedSpeedChange; } } } processorSpeedChangeFlags |= kClamshellClosedSpeedChange; // Show clamshell state } else { /* * No low power conditions exist, clear all flags */ *powerFlags &= ~(powerMonForceLowPower.bitsMask & powerMonForceLowPower.bitsClear); processorSpeedChangeFlags &= ~kClamshellClosedSpeedChange; } result = kIOReturnSuccess; } else result = kIOReturnUnsupported; // Not supported on this platform return result; } //********************************************************************************* // PMInstantiatePowerDomains // // This overrides the vanilla implementation in IOPlatformExpert. It instantiates // a root domain with two children, one for the USB bus (to handle the USB idle // power budget), and one for the expansions slots on the PCI bus (to handle // the idle PCI power budget) //********************************************************************************* void MacRISC2PE::PMInstantiatePowerDomains ( void ) { OSString * errorStr = new OSString; OSObject * obj; IOPMUSBMacRISC2 * usbMacRISC2; obj = OSUnserializeXML (gIOMacRISC2PMTree, &errorStr); if( 0 == (thePowerTree = ( OSArray * ) obj) ) { kprintf ("error parsing power tree: %s", errorStr->getCStringNoCopy()); } getProvider()->setProperty ("powertreedesc", thePowerTree); #if CREATE_PLEXUS plexus = new IOPMPagingPlexus; if ( plexus ) { plexus->init(); plexus->attach(this); plexus->start(this); } #endif root = IOPMrootDomain::construct(); root->attach(this); root->start(this); if ( plexus ) { root->addPowerChild(plexus); } root->setSleepSupported(kRootDomainSleepSupported); if (NULL == root) { kprintf ("PMInstantiatePowerDomains - null ROOT\n"); return; } PMRegisterDevice (NULL, root); usbMacRISC2 = new IOPMUSBMacRISC2; if (usbMacRISC2) { usbMacRISC2->init (); usbMacRISC2->attach (this); usbMacRISC2->start (this); PMRegisterDevice (root, usbMacRISC2); if ( plexus ) { plexus->addPowerChild (usbMacRISC2); } } slotsMacRISC2 = new IOPMSlotsMacRISC2; if (slotsMacRISC2) { slotsMacRISC2->init (); slotsMacRISC2->attach (this); slotsMacRISC2->start (this); PMRegisterDevice (root, slotsMacRISC2); if ( plexus ) { plexus->addPowerChild (slotsMacRISC2); } } if (processorSpeedChangeFlags != kNoSpeedChange) { // Any system that support Speed change supports Reduce Processor Speed. root->publishFeature("Reduce Processor Speed"); // Enable Dynamic Power Step for low latency systems. if (processorSpeedChangeFlags & kProcessorBasedSpeedChange) { root->publishFeature("Dynamic Power Step"); } } return; } //********************************************************************************* // PMRegisterDevice // // This overrides the vanilla implementation in IOPlatformExpert. We try to // put a device into the right position within the power domain hierarchy. //********************************************************************************* extern const IORegistryPlane * gIOPowerPlane; void MacRISC2PE::PMRegisterDevice(IOService * theNub, IOService * theDevice) { bool nodeFound = false; IOReturn err = -1; OSData * propertyPtr = 0; const char * theProperty; // Starts the protected area, we are trying to protect numInstancesRegistered if (mutex != NULL) IOLockLock(mutex); // reset our tracking variables before we check the XML-derived tree multipleParentKeyValue = NULL; numInstancesRegistered = 0; // try to find a home for this registrant in our XML-derived tree nodeFound = CheckSubTree (thePowerTree, theNub, theDevice, NULL); if (0 == numInstancesRegistered) { // make sure the provider is within the Power Plane...if not, // back up the hierarchy until we find a grandfather or great // grandfather, etc., that is in the Power Plane. while( theNub && (!theNub->inPlane(gIOPowerPlane))) theNub = theNub->getProvider(); } // Ends the protected area, we are trying to protect numInstancesRegistered if (mutex != NULL) IOLockUnlock(mutex); // try to register with the given (or reassigned in the case above) provider. if ( NULL != theNub ) err = theNub->addPowerChild (theDevice); // failing that then register with root (but only if we didn't register in the // XML-derived tree and only if the device we're registering is not the root). if ((err != IOPMNoErr) && (0 == numInstancesRegistered) && (theDevice != root)) { root->addPowerChild (theDevice); if ( plexus ) { plexus->addPowerChild (theDevice); } } // in addition, if it's in a PCI slot, give it to the Aux Power Supply driver propertyPtr = OSDynamicCast(OSData,theDevice->getProperty("AAPL,slot-name")); if ( propertyPtr ) { theProperty = (const char *) propertyPtr->getBytesNoCopy(); if ( strncmp("SLOT-",theProperty,5) == 0 ) { slotsMacRISC2->addPowerChild (theDevice); } } } // -- doPlatformFunction support void MacRISC2PE::publishStrings(OSCollection *strings) { OSCollectionIterator *strIter; OSSymbol *key; if (!strings) return; strIter = OSCollectionIterator::withCollection(strings); if (strIter){ while ((key = OSDynamicCast(OSSymbol, strIter->getNextObject())) != 0) publishResource(key, this); strIter->release(); } } void MacRISC2PE::releaseResources(void) { UInt32 i; if (fOnDemand) { fOnDemand->release(); fOnDemand = 0; } for (i=0; irelease(); fFuncList[i] = 0; } } return; } SInt32 MacRISC2PE::parseProvidedFunction(OSString *key, OSData *value) { SInt32 numFuncs; UInt32 wordsLeft, phandle, flags, cmdCount, cmdLen, maskLen, valueLen; UInt32 *quadlet; OSData *cmd; const OSSymbol *aKey; char funcNameWithPrefix[160], funcName[128]; const char *myFunc; bool processingCmdList; processingCmdList = false; numFuncs = 0; cmd = 0; cmdCount = 0; wordsLeft = value->getLength() / sizeof(UInt32); quadlet = (UInt32 *)value->getBytesNoCopy(); if (quadlet == 0) return(-1); while (wordsLeft >= 3) { // need at least (phandle,flags,cmd) -> 3 words if (processingCmdList) // if cmdCount is zero, done processing list processingCmdList = (cmdCount != 0); else { numFuncs++; phandle = *quadlet++; flags = *quadlet++; wordsLeft -= 2; } // If no flags are set this is a useless command if (!flags) return(-1); // Examine the command - not all commands are supported switch (*quadlet) { case kCommandCommandList: DLOG ("parseProvidedFunction got command kCommandCommandList,"); processingCmdList = true; cmd = OSData::withBytes ((void *)&phandle, sizeof(UInt32)); cmdLen = 1 + kCommandCommandListLength; cmd->appendBytes((void *)quadlet, cmdLen * sizeof(UInt32)); quadlet++; cmdCount = *quadlet++; wordsLeft -= cmdLen; DLOG (" cmdCount = %ld\n", cmdCount); break; case kCommandWriteReg32: DLOG ("parseProvidedFunction got command kCommandWriteReg32\n"); if (wordsLeft < kCommandWriteReg32Length) return(-1); if (processingCmdList) cmdCount--; else cmd = OSData::withBytes ((void *)&phandle, sizeof(UInt32)); cmdLen = 1 + kCommandWriteReg32Length; cmd->appendBytes((void *)quadlet, cmdLen * sizeof(UInt32)); quadlet += cmdLen; wordsLeft -= cmdLen; break; case kCommandConfigRead: DLOG ("parseProvidedFunction got command kCommandConfigRead\n"); if (wordsLeft < kCommandConfigReadLength) return(-1); if (processingCmdList) cmdCount--; else cmd = OSData::withBytes ((void *)&phandle, sizeof(UInt32)); cmdLen = 1 + kCommandConfigReadLength; cmd->appendBytes((void *)quadlet, cmdLen * sizeof(UInt32)); quadlet += cmdLen; wordsLeft -= cmdLen; break; case kCommandConfigRMW: DLOG ("parseProvidedFunction got command kCommandConfigRMW\n"); if (wordsLeft < kCommandConfigRMWLength) return(-1); if (processingCmdList) cmdCount--; else cmd = OSData::withBytes ((void *)&phandle, sizeof(UInt32)); // Calculate correct length of command, taking into account mask and data length maskLen = quadlet[2]; // get mask array length valueLen = quadlet[3]; // get data array length cmdLen = 1 + kCommandConfigRMWLength + (maskLen / sizeof(UInt32)) + (valueLen / sizeof(UInt32)); cmd->appendBytes((void *)quadlet, cmdLen * sizeof (UInt32)); quadlet += cmdLen; wordsLeft -= cmdLen; break; default: // unsupported command DLOG("MacRISC2PE got unsupported command\n" \ "\tphandle = %08lx\n" \ "\t flags = %08lx\n" \ "\tcommand = %08lx\n", phandle, flags, *quadlet); return(-1); } if (!cmd) return(-1); /* * If we're not processing a commandList or there aren't enought wordsLeft * for a full command, then assume we have a complete command and process it. */ if (!processingCmdList || (wordsLeft < 3)) { // Examine flags, add command to appropriate execution list(s) if (flags & kFlagOnInit) ADD_OBJ_TO_SET(cmd, fFuncList[kListOnInit]); if (flags & kFlagOnTerm) ADD_OBJ_TO_SET(cmd, fFuncList[kListOnTerm]); // sleep and wake to be added in later release //if (flags & kFlagOnSleep) ADD_OBJ_TO_SET(cmd, fFuncList[kListOnSleep], -1); //if (flags & kFlagOnWake) ADD_OBJ_TO_SET(cmd, fFuncList[kListOnWake], -1); // On-demand functions need special attention if (flags & kFlagOnDemand) { // Build an OSString to represent this function myFunc = key->getCStringNoCopy() + strlen(kFunctionProvidedPrefix); sprintf(funcName, "%s-%08lx", myFunc, phandle); sprintf(funcNameWithPrefix, "%s%s", kFunctionRequiredPrefix, funcName); if ((aKey = OSSymbol::withCString(funcNameWithPrefix)) == 0) return(-1); DLOG ("parseProvidedFunction registering demand function '%s\n", funcNameWithPrefix); // register for on-demand execution if (fOnDemand) { if (!fOnDemand->setObject(aKey,cmd)) return(-1); } else if ((fOnDemand = OSDictionary::withObjects( &(const OSObject *)cmd, &aKey, 1, 1)) == 0) return(-1); } cmd->release(); } } return(numFuncs); } bool MacRISC2PE::performFunctionList(const OSSet *funcList) { bool ret; OSCollectionIterator *iter; const OSData *aFunc; // Sanity check if (funcList == 0) return(false); iter = OSCollectionIterator::withCollection(funcList); if (iter == 0) return(false); ret = true; // Attempt every function in the list. If any of them fail, return false. while((aFunc = OSDynamicCast(OSData, iter->getNextObject())) != 0) if (!performFunction(aFunc, (void *)1, (void *)0, (void *)0, (void *)0)) ret = false; iter->release(); return(ret); } bool MacRISC2PE::performFunction(const OSData *func, void *param1 = 0, void *param2 = 0, void *param3 = 0, void *param4 = 0) { UInt32 *quadlet, pHandle, offset, data, value, valueLen, mask, maskLen, writeLen, wordsLeft, cmdCount; SInt32 lastCmd; bool processingCmdList; IOPCIDevice *nub; if (func == 0) return(false); processingCmdList = false; cmdCount = 0; wordsLeft = func->getLength() / sizeof(UInt32); data = 0; nub = NULL; lastCmd = -1; quadlet = (UInt32 *)func->getBytesNoCopy(); if (quadlet == 0) return(-1); while (wordsLeft >= 2) { // need at least (phandle,cmd) -> 2 words if (processingCmdList) // if cmdCount is zero, done processing list processingCmdList = (cmdCount != 0); else { pHandle = *quadlet++; wordsLeft--; } // Examine the command - not all commands are supported switch (*quadlet) { case kCommandCommandList: DLOG ("MacRISC2PE::performFunction got command kCommandCommandList,"); processingCmdList = true; quadlet++; cmdCount = *quadlet++; wordsLeft -= (1 + kCommandCommandListLength); DLOG (" cmdCount = %ld\n", cmdCount); break; case kCommandWriteReg32: DLOG ("MacRISC2PE::performFunction command kCommandWriteReg32\n"); if (processingCmdList) cmdCount--; quadlet++; offset = *quadlet++; value = *quadlet++; mask = *quadlet++; wordsLeft -= (1 + kCommandWriteReg32Length); // If mask isn't all ones, read data and mask it if (mask != 0xFFFFFFFF) { data = readUniNReg (offset); data &= mask; data |= value; } else // just write the data data = value; DLOG ("MacRISC2PE::performFunction - writing data 0x%lx to Uni-N register offset 0x%lx\n", data, offset); // write the result to the Uni-N register writeUniNReg(offset, data); break; // Currently only handle config reads of 4 bytes or less case kCommandConfigRead: DLOG ("MacRISC2PE::performFunction command kCommandConfigRead\n"); if (processingCmdList) cmdCount--; quadlet++; offset = *quadlet++; valueLen = *quadlet++; wordsLeft -= (1 + kCommandConfigReadLength); if (valueLen != 4) { IOLog ("MacRISC2PE::performFunction config reads cannot handle anything other than 4 bytes, found length %ld\n", valueLen); return false; } if (!nub) { if (!pHandle) { IOLog ("MacRISC2PE::performFunction config read requires pHandle to locate nub\n"); return false; } nub = findNubForPHandle (pHandle); if (!nub) { IOLog ("MacRISC2PE::performFunction config read cannot find nub for pHandle 0x%08lx\n", pHandle); return false; } } // NOTE - code below assumes read of 4 bytes, i.e., valueLen == 4!! data = nub->configRead32 (offset); DLOG ("MacRISC2PE::performFunction config read 0x%lx at offset 0x%lx\n", data, offset); if (param1) *(UInt32 *)param1 = data; lastCmd = kCommandConfigRead; break; // Currently only handle config reads/writes of 4 bytes case kCommandConfigRMW: DLOG ("MacRISC2PE::performFunction command kCommandConfigRMW\n"); if (processingCmdList) cmdCount--; // data must have been read above if (lastCmd != kCommandConfigRead) { IOLog ("MacRISC2PE::performFunction - config modify/write requires prior read\n"); return false; } quadlet++; offset = *quadlet++; maskLen = *quadlet++; valueLen = *quadlet++; writeLen = *quadlet++; wordsLeft -= (1 + kCommandConfigRMWLength + (maskLen / sizeof(UInt32)) + (valueLen / sizeof(UInt32))); if (writeLen != 4) { IOLog ("MacRISC2PE::performFunction config read/modify/write cannot handle anything other than 4 bytes, found length %ld\n", writeLen); return false; } // NOTE - code below makes implicit assumption that mask and value are each 4 bytes!! mask = *quadlet++; value = *quadlet++; DLOG("\nMacRISC2PE::performFunction config read/modify/write offset = %08lx maskLen = %08lx dataLen = %08lx writeLen = %08lx, mask = %08lx, data = %08lx\n", offset, maskLen, valueLen, writeLen, mask, value); if (!nub) { if (!pHandle) { IOLog ("MacRISC2PE::performFunction config read/modify/write requires pHandle to locate nub\n"); return false; } nub = findNubForPHandle (pHandle); if (!nub) { IOLog ("MacRISC2PE::performFunction config read/modify/write cannot find nub for pHandle 0x%08lx\n", pHandle); return false; } } // data must have been previously read data &= mask; data |= value; DLOG ("MacRISC2PE::performFunction config write data 0x%lx to offset 0x%lx\n", data, offset); nub->configWrite32 (offset, data); break; default: IOLog("MacRISC2PE::performFunction got unsupported command %08lx\n", *quadlet); return(false); } } return(true); } IOPCIDevice* MacRISC2PE::findNubForPHandle( UInt32 pHandleValue ) { IORegistryIterator* iterator; IORegistryEntry* matchingEntry = NULL; iterator = IORegistryIterator::iterateOver( gIODTPlane, kIORegistryIterateRecursively ); if ( iterator == NULL ) return( NULL ); while ( ( matchingEntry = iterator->getNextObject() ) != NULL ) { OSData* property; if ( ( property = OSDynamicCast( OSData, matchingEntry->getProperty( "AAPL,phandle" ) ) ) != NULL ) if ( pHandleValue == *( ( UInt32 * ) property->getBytesNoCopy() ) ) break; } iterator->release(); DLOG ("MacRISC2PE::findNubForPHandle - found matchingEntry '%s'\n", matchingEntry->getName()); return( OSDynamicCast (IOPCIDevice, matchingEntry)); }