AppleUSBOHCI_PwrMgmt.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOService.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/pwr_mgt/RootDomain.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
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 ( !provider->getProperty("AAPL,clock-id") && !_onCardBus && !((getPlatform()->getChipSetType() == kChipSetTypeGossamer) && getPlatform()->getMachineType() == kGossamerTypeYosemite) )
{
if (provider->hasPCIPowerManagement() && (provider->enablePCIPowerManagement() == kIOReturnSuccess))
{
_hasPCIPwrMgmt = true;
setProperty("Card Type","Built-in");
}
else
{
USBError(1, "AppleUSBOHCI[%p]::start OHCI controller will be unloaded across sleep",this);
_unloadUIMAcrossSleep = true;
setProperty("Card Type","PCI");
}
}
else
{
setProperty("Card Type","Built-in");
}
if ( _onCardBus )
{
setProperty("Card Type","CardBus");
_unloadUIMAcrossSleep = true;
}
_usb_remote_wakeup = OSSymbol::withCString("usb_remote_wakeup");
registerService();
if ( provider->getProperty("AAPL,clock-id"))
{
USBLog(2, "AppleUSBOHCI[%p]:: registering controlling driver with clock", this);
registerPowerDriver(this,ourPowerStatesKL,number_of_power_states);
}
else
{
USBLog(2, "AppleUSBOHCI[%p]:: registering controlling driver without clock", 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;
#ifndef kIOHibernateStateKey
#define kIOHibernateStateKey "IOHibernateState"
#endif
static uint32_t * pHibernateState;
USBLog(4,"AppleUSBOHCI[%p]::setPowerState (%ld) bus %ld", this, powerStateOrdinal, _busNumber );
IOSleep(5);
if (_ohciBusState != kOHCIBusStateSuspended)
{
_workLoop->CloseGate();
}
else
{
sleepRes = _workLoop->wake(&_ohciBusState);
if(sleepRes != kIOReturnSuccess)
{
USBError(1, "AppleUSBOHCI[%p]::setPowerState - Can't wake workloop, error 0x%x", this, sleepRes);
}
else
{
USBLog(5, "AppleUSBOHCI[%p]::setPowerState - workLoop successfully awakened", this);
}
}
if ( powerStateOrdinal == kOHCISetPowerLevelSuspend )
{
if ( !pHibernateState )
{
OSData * data = OSDynamicCast(OSData, (IOService::getPMRootDomain())->getProperty(kIOHibernateStateKey));
if (data)
pHibernateState = (uint32_t *) data->getBytesNoCopy();
}
if ( _unloadUIMAcrossSleep )
{
USBLog(3,"AppleUSBOHCI[%p]::setPowerState - Unloading UIM for bus %ld before going to sleep",this, _busNumber );
if ( _rootHubDevice )
{
USBLog(2, "AppleUSBOHCI[%p] Terminating root hub in setPowerState()", this);
_rootHubDevice->terminate(kIOServiceRequired | kIOServiceSynchronous);
_rootHubDevice->detachAll(gIOUSBPlane);
_rootHubDevice->release();
_rootHubDevice = NULL;
USBLog(2, "AppleUSBOHCI[%p]::setPowerState - Terminated root hub in setPowerState()", this);
}
SuspendUSBBus();
UIMFinalizeForPowerDown();
_ohciAvailable = false; }
else
{
USBLog(2, "AppleUSBOHCI[%p]::setPowerState - suspending the bus", this);
_remote_wakeup_occurred = false;
SuspendUSBBus();
USBLog(2, "AppleUSBOHCI[%p]::setPowerState - The bus is now suspended", this);
}
_ohciBusState = kOHCIBusStateSuspended;
_idleSuspend = false;
if (_hasPCIPwrMgmt)
{
_pOHCIRegisters->hcInterruptDisable = HostToUSBLong (kOHCIHcInterrupt_MIE); _ohciAvailable = false; }
}
if ( powerStateOrdinal == kOHCISetPowerLevelIdleSuspend )
{
USBLog(2, "AppleUSBOHCI[%p]::setPowerState - Suspending the bus due to inactivity", this);
_idleSuspend = true;
SuspendUSBBus();
USBLog(2, "AppleUSBOHCI[%p]::setPowerState - The bus is now suspended due to inactivity", this);
}
if ( powerStateOrdinal == kOHCISetPowerLevelRunning )
{
if ( !_uimInitialized )
{
if ( isInactive() || (_onCardBus && _pcCardEjected) )
{
_ohciBusState = kOHCIBusStateRunning;
USBLog(3,"AppleUSBOHCI[%p]::setPowerState - isInactive (or pccardEjected) while setPowerState (%d,%d)",this, isInactive(), _pcCardEjected);
}
else
{
IOReturn err = kIOReturnSuccess;
USBLog(5, "AppleUSBOHCI[%p]::setPowerState - Re-loading UIM if necessary (%d)", this, _uimInitialized );
UIMInitializeForPowerUp();
_ohciAvailable = true; _ohciBusState = kOHCIBusStateRunning;
if ( _rootHubDevice == NULL )
{
err = CreateRootHubDevice( _device, &_rootHubDevice );
if ( err != kIOReturnSuccess )
{
USBError(1,"AppleUSBOHCI[%p]::setPowerState - Could not create root hub device upon wakeup (%x)!", this, err);
}
else
{
_rootHubDevice->registerService(kIOServiceRequired | kIOServiceSynchronous);
}
}
}
}
else
{
USBLog(2, "AppleUSBOHCI[%p]::setPowerState - powering on USB", this);
if (USBToHostLong(_pOHCIRegisters->hcInterruptStatus) & kOHCIHcInterrupt_RD)
{
IOLog("USB caused wake event (OHCI)\n");
}
_remote_wakeup_occurred = true;
if (_hasPCIPwrMgmt)
{
_ohciAvailable = true; _pOHCIRegisters->hcInterruptEnable = HostToUSBLong (kOHCIHcInterrupt_MIE); USBLog(4, "AppleUSBOHCI[%p]::setPowerState - after reenabling interrupts, hcInterruptEnable = 0x%lx", this, (UInt32) USBToHostLong(_pOHCIRegisters->hcInterruptEnable));
}
ResumeUSBBus();
_ohciBusState = kOHCIBusStateRunning;
if ( _uimInitialized && pHibernateState && *pHibernateState )
{
USBLog(3,"AppleUSBOHCI[%p]::setPowerState - Unloading UIM for bus %ld after hibernate", this, _busNumber );
if ( _rootHubDevice )
{
USBLog(2, "AppleUSBOHCI[%p]::setPowerState - Terminating root hub in setPowerState()", this);
_rootHubDevice->terminate(kIOServiceRequired | kIOServiceSynchronous);
_rootHubDevice->detachAll(gIOUSBPlane);
_rootHubDevice->release();
_rootHubDevice = NULL;
USBLog(2, "AppleUSBOHCI[%p]::setPowerState - Terminated root hub in setPowerState()", this);
}
SuspendUSBBus();
UIMFinalizeForPowerDown();
_ohciAvailable = false;
IOSleep(50); USBLog(3,"AppleUSBOHCI[%p]::setPowerState - Would create root hub here, postponing it!", this);
thread_call_enter(_rootHubCreationThread);
}
}
LastRootHubPortStatusChanged(true);
_idleSuspend = false;
}
if (_ohciBusState == kOHCIBusStateSuspended)
{
sleepRes = _workLoop->sleep(&_ohciBusState);
if(sleepRes != kIOReturnSuccess)
{
USBError(1, "AppleUSBOHCI[%p]::setPowerState - Can't sleep workloop, error 0x%x", this, sleepRes);
}
else
{
USBLog(5, "AppleUSBOHCI[%p]::setPowerState - workLoop successfully slept", this);
}
}
else
{
_workLoop->OpenGate();
}
USBLog(4,"AppleUSBOHCI[%p]::setPowerState done", 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);
if ( _writeDoneHeadInterrupt )
{
USBError(1,"AppleUSBOHCI[%p] Processing WDH before suspending", this);
PollInterrupts();
}
_pOHCIRegisters->hcInterruptEnable = _pOHCIRegisters->hcInterruptEnable | HostToUSBLong(kOHCIHcInterrupt_RD);
hcControl = kOHCIFunctionalState_Suspend << kOHCIHcControl_HCFSPhase;
if (_hasPCIPwrMgmt)
hcControl |= kOHCIHcControl_RWC | kOHCIHcControl_RWE;
_pOHCIRegisters->hcControl = HostToUSBLong(hcControl);
IOSleep(3);
}
void
AppleUSBOHCI::ResumeUSBBus()
{
switch ((USBToHostLong(_pOHCIRegisters->hcControl) & kOHCIHcControl_HCFS) >> kOHCIHcControl_HCFSPhase )
{
case kOHCIFunctionalState_Suspend:
USBLog(2, "AppleUSBOHCI[%p]:: Resuming bus from Suspend state", this);
_pOHCIRegisters->hcControl = HostToUSBLong(kOHCIFunctionalState_Resume << kOHCIHcControl_HCFSPhase);
case kOHCIFunctionalState_Resume:
if(_errataBits & kErrataLucentSuspendResume)
{
USBLog(2, "AppleUSBOHCI[%p]:: Delaying 35 milliseconds in resume state", this);
IOSleep(35);
}
else
{
USBLog(2, "AppleUSBOHCI[%p]:: Delaying 20 milliseconds in resume state", this);
IOSleep(20);
}
case kOHCIFunctionalState_Reset:
USBLog(2, "AppleUSBOHCI[%p]: Changing bus to operational", 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, "AppleUSBOHCI[%p]: Bus already operational", this);
break;
}
}