AppleUSBOHCI_PwrMgmt.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOService.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/platform/ApplePlatformExpert.h>
#include <IOKit/usb/IOUSBRootHubDevice.h>
#include <IOKit/usb/IOUSBLog.h>
enum {
kGossamerTypeGossamer = 1,
kGossamerTypeSilk,
kGossamerTypeWallstreet,
kGossamerTypeiMac,
kGossamerTypeYosemite,
kGossamerType101
};
#include "AppleUSBOHCI.h"
#define number_of_power_states 2
#define kAppleCurrentAvailable "AAPL,current-available"
static IOPMPowerState ourPowerStates[number_of_power_states] = {
{
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{
1, IOPMDeviceUsable, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 }
};
static IOPMPowerState ourPowerStatesKL[number_of_power_states] = {
{
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{
1, IOPMDeviceUsable, IOPMPowerOn, IOPMPowerOn | IOPMClockNormal, 0, 0, 0, 0, 0, 0, 0, 0 }
};
#define DEBUG_PCI_PWR_MGMT 1
void AppleUSBOHCI::initForPM (IOPCIDevice *provider)
{
UInt8 pciPMCapOffset;
UInt16 pciPMCapReg;
UInt16 requiredSleepState = 0;
OSObject *anObject;
OSData *aString;
_onCardBus = (0 != provider->metaCast("IOCardBusDevice"));
if ( _onCardBus )
_unloadUIMAcrossSleep = true;
if ( !provider->getProperty("AAPL,clock-id") && !_onCardBus && !((getPlatform()->getChipSetType() == kChipSetTypeGossamer) && getPlatform()->getMachineType() == kGossamerTypeYosemite) )
{
if (hasPCIPwrMgmt() && (enablePCIPwrMgmt() == kIOReturnSuccess))
{
_hasPCIPwrMgmt = true;
}
else
{
USBLog(2, "%s[%p]::start OHCI controller will be unloaded across sleep",getName(),this);
_unloadUIMAcrossSleep = true;
}
}
_usb_remote_wakeup = OSSymbol::withCString("usb_remote_wakeup");
registerService();
if ( provider->getProperty("AAPL,clock-id"))
{
USBLog(2, "%s[%p]:: registering controlling driver with clock", getName(), this);
registerPowerDriver(this,ourPowerStatesKL,number_of_power_states);
}
else
{
USBLog(2, "%s[%p]:: registering controlling driver without clock", getName(), this);
registerPowerDriver(this,ourPowerStates,number_of_power_states);
}
changePowerStateTo(1);
}
unsigned long AppleUSBOHCI::maxCapabilityForDomainState ( IOPMPowerFlags domainState )
{
if ( getProvider()->getProperty("AAPL,clock-id")) {
if ( ((domainState & IOPMPowerOn) && (domainState & IOPMClockNormal) ) ||
(domainState & kIOPMDoze) && (domainState & IOPMClockNormal) ) {
return 1;
}
else {
return 0;
}
}
else { if ( (domainState & IOPMPowerOn) ||
(domainState & kIOPMDoze) ) {
return 1;
}
else {
return 0;
}
}
}
unsigned long AppleUSBOHCI::initialPowerStateForDomainState ( IOPMPowerFlags domainState )
{
return 1;
}
IOReturn
AppleUSBOHCI::setPowerState( unsigned long powerStateOrdinal, IOService* whatDevice )
{
IOReturn sleepRes;
USBLog(4,"%s[%p]::setPowerState (%ld) bus %d", getName(), this, powerStateOrdinal, _busNumber );
IOSleep(5);
if (_ohciBusState != kOHCIBusStateSuspended)
{
_workLoop->CloseGate();
}
else
{
sleepRes = _workLoop->wake(&_ohciBusState);
if(sleepRes != kIOReturnSuccess)
{
USBError(1, "%s[%p] setPowerState - Can't wake workloop, error 0x%x", getName(), this, sleepRes);
}
else
{
USBLog(5, "%s[%p :setPowerState - workLoop successfully awakened", getName(), this);
}
}
if ( powerStateOrdinal == kOHCISetPowerLevelSuspend )
{
if ( _unloadUIMAcrossSleep )
{
USBLog(3,"%s[%p] Unloading UIM for bus %d before going to sleep",getName(),this, _busNumber );
if ( _rootHubDevice )
{
_rootHubDevice->terminate(kIOServiceRequired | kIOServiceSynchronous);
_rootHubDevice->detachAll(gIOUSBPlane);
_rootHubDevice->release();
_rootHubDevice = NULL;
}
UIMFinalizeForPowerDown();
}
else
{
USBLog(2, "%s[%p] suspending the bus", getName(), this);
_remote_wakeup_occurred = false;
SuspendUSBBus();
USBLog(2, "%s[%p] The bus is now suspended", getName(), this);
}
_ohciBusState = kOHCIBusStateSuspended;
_idleSuspend = false;
if (_hasPCIPwrMgmt)
{
_pOHCIRegisters->hcInterruptDisable = HostToUSBLong (kOHCIHcInterrupt_MIE); _ohciAvailable = false; if (PMsleepEnabled && PMcontrolStatus && sleepControlBits)
{
#if DEBUG_PCI_PWR_MGMT
IOLog("%s[%p]::setPowerState(SLEEP) - setting PMCS to %x\n", getName(), this, sleepControlBits);
#endif
saveDeviceState( _device );
_device->configWrite16(PMcontrolStatus, sleepControlBits);
}
}
}
if ( powerStateOrdinal == kOHCISetPowerLevelIdleSuspend )
{
USBLog(2, "%s[%p] Suspending the bus due to inactivity", getName(), this);
_idleSuspend = true;
SuspendUSBBus();
USBLog(2, "%s[%p] The bus is now suspended due to inactivity", getName(), this);
}
if ( powerStateOrdinal == kOHCISetPowerLevelRunning )
{
if ( _unloadUIMAcrossSleep && !_idleSuspend )
{
if ( isInactive() || (_onCardBus && _pcCardEjected) )
{
_ohciBusState = kOHCIBusStateRunning;
USBLog(3,"%s[%p] isInactive (or pccardEjected) while setPowerState (%d,%d)",getName(),this, isInactive(), _pcCardEjected);
}
else
{
IOReturn err = kIOReturnSuccess;
USBLog(5, "%s[%p]: Re-loading UIM if necessary (%d)", getName(), this, _uimInitialized );
if ( !_uimInitialized )
UIMInitializeForPowerUp();
_ohciBusState = kOHCIBusStateRunning;
if ( _rootHubDevice == NULL )
{
err = CreateRootHubDevice( _device, &_rootHubDevice );
if ( err != kIOReturnSuccess )
{
USBError(1,"%s[%p] Could not create root hub device upon wakeup (%x)!",getName(), this, err);
}
else
{
_rootHubDevice->registerService(kIOServiceRequired | kIOServiceSynchronous);
}
}
}
}
else
{
USBLog(2, "%s[%p] setPowerState powering on USB", getName(), this);
_remote_wakeup_occurred = true;
if (_hasPCIPwrMgmt)
{
if (PMsleepEnabled && PMcontrolStatus && sleepControlBits)
{
if ((_device->configRead16(PMcontrolStatus) & kPCIPMCSPowerStateMask) != kPCIPMCSPowerStateD0)
{
#if DEBUG_PCI_PWR_MGMT
IOLog("%s[%p]::setPowerState(ON) - moving PMCS from %x to D0\n", getName(), this, _device->configRead16(PMcontrolStatus));
#endif
_device->configWrite16(PMcontrolStatus, kPCIPMCSPMEStatus | kPCIPMCSPowerStateD0);
}
else
{
#if DEBUG_PCI_PWR_MGMT
IOLog("%s[%p]::setPowerState(ON) - PMCS already at D0 (%x)\n", getName(), this, _device->configRead16(PMcontrolStatus));
#endif
_device->configWrite16(PMcontrolStatus, kPCIPMCSPMEStatus);
IOSleep(10); }
restoreDeviceState( _device );
_ohciAvailable = true; _pOHCIRegisters->hcInterruptEnable = HostToUSBLong (kOHCIHcInterrupt_MIE); USBLog(4, "%s[%p]::setPowerState - after reenabling interrupts, hcInterruptEnable = %p", getName(), this, USBToHostLong(_pOHCIRegisters->hcInterruptEnable));
}
}
ResumeUSBBus();
_ohciBusState = kOHCIBusStateRunning;
}
LastRootHubPortStatusChanged(true);
_idleSuspend = false;
}
if (_ohciBusState == kOHCIBusStateSuspended)
{
sleepRes = _workLoop->sleep(&_ohciBusState);
if(sleepRes != kIOReturnSuccess)
{
USBError(1, "%s[%p] setPowerState - Can't sleep workloop, error 0x%x", getName(), this, sleepRes);
}
else
{
USBLog(5, "%s[%p :setPowerState - workLoop successfully slept", getName(), this);
}
}
else
{
_workLoop->OpenGate();
}
USBLog(4,"%s[%p]::setPowerState done", getName(), this );
return IOPMAckImplied;
}
IOReturn
AppleUSBOHCI::callPlatformFunction(const OSSymbol *functionName,
bool waitForFunction,
void *param1, void *param2,
void *param3, void *param4)
{
if (functionName == _usb_remote_wakeup)
{
bool *wake;
wake = (bool *)param1;
if (_remote_wakeup_occurred)
{
*wake = true;
}
else
{
*wake = false;
}
return kIOReturnSuccess;
}
return kIOReturnBadArgument;
}
void
AppleUSBOHCI::SuspendUSBBus()
{
UInt32 something;
UInt32 hcControl;
hcControl = USBToHostLong(_pOHCIRegisters->hcControl);
hcControl &= ~(kOHCIHcControl_CLE | kOHCIHcControl_BLE | kOHCIHcControl_PLE | kOHCIHcControl_IE);
_pOHCIRegisters->hcControl = HostToUSBLong(hcControl);
IOSleep(1);
something = USBToHostLong(_pOHCIRegisters->hcInterruptStatus) & kOHCIHcInterrupt_WDH;
if (something)
{
USBError(1,"%s[%p] DANGER! WDH processing needs to get done before suspending", getName(), this);
}
_pOHCIRegisters->hcInterruptEnable = _pOHCIRegisters->hcInterruptEnable | HostToUSBLong(kOHCIHcInterrupt_RD);
hcControl = kOHCIFunctionalState_Suspend << kOHCIHcControl_HCFSPhase;
if (_hasPCIPwrMgmt)
hcControl |= kOHCIHcControl_RWC | kOHCIHcControl_RWE;
_pOHCIRegisters->hcControl = USBToHostLong(hcControl);
IOSleep(3);
}
void
AppleUSBOHCI::ResumeUSBBus()
{
switch ((USBToHostLong(_pOHCIRegisters->hcControl) & kOHCIHcControl_HCFS) >> kOHCIHcControl_HCFSPhase )
{
case kOHCIFunctionalState_Suspend:
USBLog(2, "%s[%p]:: Resuming bus from Suspend state", getName(), this);
_pOHCIRegisters->hcControl = HostToUSBLong(kOHCIFunctionalState_Resume << kOHCIHcControl_HCFSPhase);
case kOHCIFunctionalState_Resume:
if(_errataBits & kErrataLucentSuspendResume)
{
USBLog(2, "%s[%p]:: Delaying 35 milliseconds in resume state", getName(), this);
IOSleep(35);
}
else
{
USBLog(2, "%s[%p]:: Delaying 20 milliseconds in resume state", getName(), this);
IOSleep(20);
}
case kOHCIFunctionalState_Reset:
USBLog(2, "%s[%p]: Changing bus to operational", getName(), this);
_pOHCIRegisters->hcControl = HostToUSBLong(kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase);
IOSleep(3); _pOHCIRegisters->hcControl = HostToUSBLong((kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase)
| kOHCIHcControl_CLE | (_OptiOn ? kOHCIHcControl_Zero : kOHCIHcControl_BLE)
| kOHCIHcControl_PLE | kOHCIHcControl_IE);
break;
default:
USBLog(2, "%s[%p]: Bus already operational", getName(), this);
break;
}
}
bool AppleUSBOHCI::hasPCIPwrMgmt(IOOptionBits state)
{
UInt8 pciPMCapOffset;
UInt16 pciPMCapReg;
UInt16 requiredSleepState = 0;
OSObject *anObject;
OSData *aString;
sleepControlBits = 0; if (!PMcontrolStatus)
{
_device->findPCICapability(kIOPCIPowerManagementCapability, &pciPMCapOffset);
if (pciPMCapOffset > 0x3f) {
#if DEBUG_PCI_PWR_MGMT
IOLog("%s[%p]::hasPCIPwrMgmt found pciPMCapOffset %d\n", getName(), this, pciPMCapOffset);
#endif
PMcontrolStatus = pciPMCapOffset+4;
}
}
if (PMcontrolStatus)
{
pciPMCapReg = _device->configRead16(PMcontrolStatus-2);
#if DEBUG_PCI_PWR_MGMT
IOLog("%s[%p]::hasPCIPwrMgmt found pciPMCapReg %x\n", getName(), this, pciPMCapReg);
#endif
if (state)
{
requiredSleepState = state;
switch (state) {
case kPCIPMCPMESupportFromD3Cold:
case kPCIPMCPMESupportFromD3Hot:
sleepControlBits = (kPCIPMCSPMEStatus | kPCIPMCSPMEEnable | kPCIPMCSPowerStateD3);
break;
case kPCIPMCPMESupportFromD2:
sleepControlBits = (kPCIPMCSPMEStatus | kPCIPMCSPMEEnable | kPCIPMCSPowerStateD2);
break;
case kPCIPMCPMESupportFromD1:
sleepControlBits = (kPCIPMCSPMEStatus | kPCIPMCSPMEEnable | kPCIPMCSPowerStateD1);
break;
default:
requiredSleepState = 0;
}
}
else
{
anObject = _device->getProperty("sleep-power-state");
aString = OSDynamicCast(OSData, anObject);
if (aString)
{
#if DEBUG_PCI_PWR_MGMT
IOLog("%s[%p]::hasPCIPwrMgmt found sleep-power-state string %p\n", getName(), this, aString);
#endif
if (aString->isEqualTo("D3cold", 6))
{
requiredSleepState = kPCIPMCPMESupportFromD3Cold;
sleepControlBits = (kPCIPMCSPMEStatus | kPCIPMCSPMEEnable | kPCIPMCSPowerStateD3);
}
else if (aString->isEqualTo("D3Hot", 5))
{
requiredSleepState = kPCIPMCPMESupportFromD3Hot;
sleepControlBits = (kPCIPMCSPMEStatus | kPCIPMCSPMEEnable | kPCIPMCSPowerStateD3);
}
}
}
if (requiredSleepState)
{
#if DEBUG_PCI_PWR_MGMT
IOLog("%s[%p]::hasPCIPwrMgmt checking for requiredSleepState %x\n", getName(), this, requiredSleepState);
#endif
if ( pciPMCapReg & requiredSleepState )
{
PMcontrolStatus = pciPMCapOffset+4;
#if DEBUG_PCI_PWR_MGMT
IOLog("%s[%p]::hasPCIPwrMgmt - PMcontrolStatus is %d\n", getName(), this, PMcontrolStatus);
#endif
}
}
}
return sleepControlBits ? true : false;
}
IOReturn AppleUSBOHCI::enablePCIPwrMgmt(IOOptionBits state)
{
IOReturn ret = kIOReturnSuccess;
if (!PMcontrolStatus)
ret = kIOReturnBadArgument;
else
{
switch (state) {
case kPCIPMCSPowerStateD3:
case kPCIPMCSPowerStateD2:
case kPCIPMCSPowerStateD1:
sleepControlBits = state | kPCIPMCSPMEStatus | kPCIPMCSPMEEnable;
case 0xffffffff: if (!sleepControlBits)
{
#if DEBUG_PCI_PWR_MGMT
IOLog("%s[%p] - enablePCIPwrMgmt - no sleep control bits - not enabling", getName(), this);
#endif
ret = kIOReturnBadArgument;
}
else
{
#if DEBUG_PCI_PWR_MGMT
IOLog("%s[%p] - enablePCIPwrMgmt, enabling", getName(), this);
#endif
PMsleepEnabled = true;
}
break;
case kPCIPMCSPowerStateD0: sleepControlBits = 0;
PMsleepEnabled = false;
break;
default:
PMsleepEnabled = false;
ret = kIOReturnBadArgument;
break;
}
}
return ret;
}
enum { kSavedConfigSize = 64 };
enum { kSavedConfigs = 16 };
IOReturn AppleUSBOHCI::saveDeviceState( IOPCIDevice * device,
IOOptionBits options )
{
int i;
if( !savedConfig)
savedConfig = IONew( UInt32, kSavedConfigSize );
if( !savedConfig)
return( kIOReturnNotReady );
for( i = 1; i < kSavedConfigs; i++)
savedConfig[i] = device->configRead32( i * 4 );
return( kIOReturnSuccess );
}
IOReturn AppleUSBOHCI::restoreDeviceState( IOPCIDevice * device,
IOOptionBits options )
{
int i;
if( !savedConfig)
return( kIOReturnNotReady );
for( i = 2; i < kSavedConfigs; i++)
device->configWrite32( i * 4, savedConfig[ i ]);
device->configWrite32( kIOPCIConfigCommand, savedConfig[1]);
IODelete( savedConfig, UInt32, kSavedConfigSize);
savedConfig = NULL;
return( kIOReturnSuccess );
}