AppleUSBEHCI_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/pwr_mgt/RootDomain.h>
#include <IOKit/IOMessage.h>
#include <IOKit/usb/IOUSBRootHubDevice.h>
#include <IOKit/usb/IOUSBLog.h>
#include "AppleUSBEHCI.h"
enum {
kGossamerTypeGossamer = 1,
kGossamerTypeSilk,
kGossamerTypeWallstreet,
kGossamerTypeiMac,
kGossamerTypeYosemite,
kGossamerType101
};
#include "AppleUSBEHCI.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}
};
void
AppleUSBEHCI::initForPM (IOPCIDevice *provider)
{
_onCardBus = (0 != provider->metaCast("IOCardBusDevice"));
if ( !provider->getProperty("AAPL,clock-id") && !((getPlatform()->getChipSetType() == kChipSetTypeGossamer) && getPlatform()->getMachineType() == kGossamerTypeYosemite) )
{
bool hasSupport = false;
if (_errataBits & kErrataICH6PowerSequencing)
hasSupport = provider->hasPCIPowerManagement(kPCIPMCPMESupportFromD3Cold);
else
hasSupport = provider->hasPCIPowerManagement();
if (hasSupport)
{
if (_errataBits & kErrataICH6PowerSequencing)
hasSupport = (provider->enablePCIPowerManagement(kPCIPMCPMESupportFromD3Cold) == kIOReturnSuccess);
else
hasSupport = (provider->enablePCIPowerManagement() == kIOReturnSuccess);
}
if (hasSupport)
{
_hasPCIPwrMgmt = true;
setProperty("Card Type","Built-in");
}
else
{
USBLog(1, "%s[%p]::start EHCI controller will be unloaded across sleep",getName(),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, "%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);
if (_errataBits & kErrataICH6PowerSequencing)
_powerDownNotifier = registerPrioritySleepWakeInterest(PowerDownHandler, this, 0);
}
unsigned long AppleUSBEHCI::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 AppleUSBEHCI::initialPowerStateForDomainState ( IOPMPowerFlags domainState )
{
return 1;
}
IOReturn
AppleUSBEHCI::setPowerState( unsigned long powerStateOrdinal, IOService* whatDevice )
{
IOReturn sleepRes;
#ifndef kIOHibernateStateKey
#define kIOHibernateStateKey "IOHibernateState"
#endif
static uint32_t * pHibernateState;
USBLog(4,"%s[%p]::setPowerState (%ld) bus %d", getName(), this, powerStateOrdinal, _busNumber );
IOSleep(5);
if (_ehciBusState != kEHCIBusStateSuspended)
{
_workLoop->CloseGate();
}
else
{
sleepRes = _workLoop->wake(&_ehciBusState);
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 == kEHCISetPowerLevelSuspend )
{
if ( !pHibernateState )
{
OSData * data = OSDynamicCast(OSData, (IOService::getPMRootDomain())->getProperty(kIOHibernateStateKey));
if (data)
{
pHibernateState = (uint32_t *) data->getBytesNoCopy();
}
}
if ( _unloadUIMAcrossSleep )
{
USBLog(3,"%s[%p] Unloading UIM for bus %d before going to sleep",getName(),this, (int)_busNumber );
if ( _rootHubDevice )
{
USBLog(2, "%s[%p] Terminating root hub in setPowerState()", getName(), this);
_rootHubDevice->terminate(kIOServiceRequired | kIOServiceSynchronous);
_rootHubDevice->detachAll(gIOUSBPlane);
_rootHubDevice->release();
_rootHubDevice = NULL;
USBLog(2, "%s[%p] Terminated root hub in setPowerState()", getName(), this);
}
SuspendUSBBus();
UIMFinalizeForPowerDown();
_ehciAvailable = false; }
else
{
USBLog(2, "%s[%p] suspending the bus", getName(), this);
_remote_wakeup_occurred = false;
if (_idleSuspend)
{
USBLog(5, "AppleUSBEHCI[%p]::setPowerState - in _idleSuspend - restarting USB before suspending", this);
RestartUSBBus();
}
USBLog(7, "AppleUSBEHCI[%p]::setPowerState - about to suspend bus - showing queue", this);
printAsyncQueue(7);
SuspendUSBBus();
USBLog(7, "AppleUSBEHCI[%p]::setPowerState The bus is now suspended - showing queue", this);
printAsyncQueue(7);
}
_ehciBusState = kEHCIBusStateSuspended;
_idleSuspend = false;
if (_hasPCIPwrMgmt)
{
_savedUSBIntr = _pEHCIRegisters->USBIntr; _pEHCIRegisters->USBIntr = 0; _ehciAvailable = false; }
}
if ( powerStateOrdinal == kEHCISetPowerLevelIdleSuspend )
{
USBLog(2, "%s[%p] halting the bus due to inactivity", getName(), this);
_idleSuspend = true;
StopUSBBus();
USBLog(2, "%s[%p] The bus is now halted due to inactivity", getName(), this);
}
if ( powerStateOrdinal == kEHCISetPowerLevelRunning )
{
if ( !_uimInitialized )
{
if ( isInactive() || (_onCardBus && _pcCardEjected) )
{
_ehciBusState = kEHCIBusStateRunning;
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 );
UIMInitializeForPowerUp();
_ehciBusState = kEHCIBusStateRunning;
_ehciAvailable = true;
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 if (_idleSuspend)
{
USBLog(2, "%s[%p]::setPowerState - in _idleSuspend - restarting USB", getName(), this);
RestartUSBBus();
}
else
{
USBLog(2, "%s[%p] setPowerState powering on USB", getName(), this);
if (USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCIPortChangeIntBit)
{
IOLog("USB caused wake event (EHCI)\n");
}
_remote_wakeup_occurred = true;
if (_hasPCIPwrMgmt)
{
_ehciAvailable = true; if (_savedUSBIntr)
_pEHCIRegisters->USBIntr = _savedUSBIntr; USBLog(4, "AppleUSBEHCI[%p]::setPowerState - after reenabling interrupts, USBIntr = %p", this, (void*)USBToHostLong(_pEHCIRegisters->USBIntr));
}
USBLog(7, "AppleUSBEHCI[%p]::setPowerState - about to resume bus - showing queue", this);
printAsyncQueue(7);
ResumeUSBBus();
USBLog(7, "AppleUSBEHCI[%p]::setPowerState - bus has been resumed - showing queue", this);
printAsyncQueue(7);
_ehciBusState = kEHCIBusStateRunning;
}
if ( _uimInitialized && pHibernateState && *pHibernateState )
{
USBLog(3,"%s[%p] Unloading UIM for bus %d after hibernate",getName(),this, (int)_busNumber );
if ( _rootHubDevice )
{
USBLog(2, "%s[%p] Terminating root hub in setPowerState()", getName(), this);
_rootHubDevice->terminate(kIOServiceRequired | kIOServiceSynchronous);
_rootHubDevice->detachAll(gIOUSBPlane);
_rootHubDevice->release();
_rootHubDevice = NULL;
USBLog(2, "%s[%p] Terminated root hub in setPowerState()", getName(), this);
}
SuspendUSBBus();
UIMFinalizeForPowerDown();
_ehciAvailable = false;
USBLog(3,"%s[%p] Would create root hub here, postponing it! (%d)",getName(), this, _needToCreateRootHub);
_needToCreateRootHub = TRUE;
}
LastRootHubPortStatusChanged(true);
_idleSuspend = false;
}
if (_ehciBusState == kEHCIBusStateSuspended)
{
sleepRes = _workLoop->sleep(&_ehciBusState);
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 AppleUSBEHCI::callPlatformFunction(const OSSymbol *functionName,
bool waitForFunction,
void *param1, void *param2,
void *param3, void *param4)
{
IOLog("EHCIUIM -- callPlatformFunction unimplimented \n");
if (functionName == usb_remote_wakeup)
{
bool *wake;
wake = (bool *)param1;
if (_remote_wakeup_occurred)
{
*wake = true;
}
else
{
*wake = false;
}
return kIOReturnSuccess;
}
return kIOReturnBadArgument;
}
void
AppleUSBEHCI::ResumeUSBBus()
{
UInt8 numPorts;
int i;
bool enabledports = false;
if (USBToHostLong(_pEHCIRegisters->ConfigFlag) != kEHCIPortRoutingBit)
{
USBLog(2, "%s[%p]::ResumeUSBBus - restoring ConfigFlag[from %p]", getName(), this, USBToHostLong(_pEHCIRegisters->ConfigFlag));
_pEHCIRegisters->ConfigFlag = HostToUSBLong(kEHCIPortRoutingBit);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue = 0, count = 0;
newValue = USBToHostLong(_pEHCIRegisters->ConfigFlag);
while ((count++ < 10) && (newValue != kEHCIPortRoutingBit))
{
USBError(1, "EHCI driver: ResumeUSBBus - ConfigFlag bit not sticking. Retrying.");
_pEHCIRegisters->ConfigFlag = HostToUSBLong(kEHCIPortRoutingBit);
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->ConfigFlag);
}
}
}
if (_savedPeriodicListBase && (_pEHCIRegisters->PeriodicListBase != _savedPeriodicListBase))
{
USBLog(2, "%s[%p]::ResumeUSBBus - restoring PeriodicListBase[from %p to %p]", getName(), this, USBToHostLong(_pEHCIRegisters->PeriodicListBase), USBToHostLong(_savedPeriodicListBase));
_pEHCIRegisters->PeriodicListBase = _savedPeriodicListBase;
IOSync();
}
if (_savedAsyncListAddr && (_pEHCIRegisters->AsyncListAddr != _savedAsyncListAddr))
{
USBLog(2, "%s[%p]::ResumeUSBBus - restoring AsyncListAddr[from %p to %p]", getName(), this, USBToHostLong(_pEHCIRegisters->AsyncListAddr), USBToHostLong(_savedAsyncListAddr));
_pEHCIRegisters->AsyncListAddr = _savedAsyncListAddr;
IOSync();
}
if (_is64bit)
_pEHCIRegisters->CTRLDSSegment = 0;
if (USBToHostLong(_savedUSBCMD) & kEHCICMDRunStop)
{
_pEHCIRegisters->USBCMD |= HostToUSBLong(kEHCICMDRunStop);
IOSync();
for (i=0; (i< 100) && (USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCIHCHaltedBit); i++)
IODelay(100);
if (i>1)
{
USBError(1, "AppleUSBEHCI[%p]::ResumeUSBBus - controller took (%d) turns to get going", this, i);
}
}
numPorts = USBToHostLong(_pEHCICapRegisters->HCSParams) & kEHCINumPortsMask;
USBLog(7, "%s[%p]::ResumeUSBBus - resuming %d ports", getName(), this, numPorts);
for (i=0; i < numPorts; i++)
{
UInt32 portStat;
portStat = USBToHostLong(_pEHCIRegisters->PortSC[i]);
if (portStat & kEHCIPortSC_Owner)
{
USBLog(7, "%s[%p]::ResumeUSBBus - port %d owned by OHCI", getName(), this, i);
}
else if (portStat & kEHCIPortSC_Enabled)
{
portStat |= kEHCIPortSC_Resume;
_pEHCIRegisters->PortSC[i] = HostToUSBLong(portStat);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue = 0, count = 0;
newValue = USBToHostLong(_pEHCIRegisters->PortSC[i]);
while ((count++ < 10) && !(newValue & kEHCIPortSC_Resume))
{
USBError(1, "EHCI driver: ResumeUSBBus - PortSC resume not sticking (on). Retrying.");
_pEHCIRegisters->PortSC[i] = HostToUSBLong(portStat);
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->PortSC[i]);
}
}
USBLog(7, "%s[%p]::ResumeUSBBus - port %d now resumed", getName(), this, i);
enabledports = true;
}
else
{
USBLog(7, "%s[%p]::ResumeUSBBus - port %d not enabled", getName(), this, i);
}
}
if (enabledports)
{
USBLog(7, "%s[%p]::ResumeUSBBus Delaying 20 milliseconds in resume state", getName(), this);
IODelay(20000);
for (i=0; i < numPorts; i++)
{
UInt32 portStat;
portStat = USBToHostLong(_pEHCIRegisters->PortSC[i]);
if (portStat & kEHCIPortSC_Owner)
{
USBLog(7, "%s[%p]::ResumeUSBBus - port %d owned by OHCI", getName(), this, i);
}
else if (portStat & kEHCIPortSC_Enabled)
{
portStat &= ~kEHCIPortSC_Resume;
_pEHCIRegisters->PortSC[i] = HostToUSBLong(portStat);
IOSync();
#if 0
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue = 0, count = 0;
IODelay(2000); newValue = USBToHostLong(_pEHCIRegisters->PortSC[i]);
while ((count++ < 10) && (newValue & kEHCIPortSC_Resume))
{
USBError(1, "EHCI driver: ResumeUSBBus - PortSC resume not sticking (off). Retrying.");
_pEHCIRegisters->PortSC[i] = HostToUSBLong(portStat);
IOSync();
IODelay(2000); newValue = USBToHostLong(_pEHCIRegisters->PortSC[i]);
}
}
#endif
USBLog(7, "%s[%p]::ResumeUSBBus - port %d finished resume sequence", getName(), this, i);
enabledports = true;
}
else
{
USBLog(7, "%s[%p]::ResumeUSBBus - port %d not enabled", getName(), this, i);
}
}
IODelay(10000); }
if (_savedUSBCMD)
{
USBLog(7, "%s[%p]::ResumeUSBBus - USBCMD is <%p> will be <%p>", getName(), this, (void*)_pEHCIRegisters->USBCMD, (void*)_savedUSBCMD);
_pEHCIRegisters->USBCMD = _savedUSBCMD;
}
}
void
AppleUSBEHCI::SuspendUSBBus()
{
UInt8 numPorts;
int i;
UInt32 usbcmd, usbsts;
_savedUSBCMD = _pEHCIRegisters->USBCMD;
USBLog(7, "%s[%p]::SuspendUSBBus - got _savedUSBCMD <%p>", getName(), this, (void*)_savedUSBCMD);
usbcmd = USBToHostLong(_savedUSBCMD);
if (usbcmd & kEHCICMDAsyncEnable)
{
for (i=0; (i < 100) && !(USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCISTSAsyncScheduleStatus); i++)
IODelay(100);
if (i)
{
USBError(1, "AppleUSBEHCI[%p]::SuspendUSBBus - Async Schedule should have been on but was off for %d loops", this, i);
}
usbcmd &= ~kEHCICMDAsyncEnable;
}
if (usbcmd & kEHCICMDPeriodicEnable)
{
for (i=0; (i < 100) && !(USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCISTSPeriodicScheduleStatus); i++)
IODelay(100);
if (i)
{
USBError(1, "AppleUSBEHCI[%p]::SuspendUSBBus - Periodic Schedule should have been on but was off for %d loops", this, i);
}
usbcmd &= ~kEHCICMDPeriodicEnable;
}
_pEHCIRegisters->USBCMD = HostToUSBLong(usbcmd); IOSync();
for (i=0; (i < 100) && (USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCISTSAsyncScheduleStatus); i++)
IODelay(1000);
if (i > 2)
{
USBError(1, "AppleUSBEHCI[%p]::SuspendUSBBus - Async Schedule took %d loops to turn off", this, i);
}
for (i=0; (i < 100) && (USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCISTSPeriodicScheduleStatus); i++)
IODelay(1000);
if (i > 2)
{
USBError(1, "AppleUSBEHCI[%p]::SuspendUSBBus - Periodic Schedule took %d loops to turn off", this, i);
}
_savedPeriodicListBase = _pEHCIRegisters->PeriodicListBase;
_savedAsyncListAddr = _pEHCIRegisters->AsyncListAddr;
GetNumberOfPorts( &numPorts );
USBLog(7, "%s[%p]::SuspendUSBBus - suspending %d ports", getName(), this, numPorts);
for (i=0; i < numPorts; i++)
{
UInt32 portStat;
portStat = USBToHostLong(_pEHCIRegisters->PortSC[i]);
if (portStat & kEHCIPortSC_Owner)
{
USBLog(7, "%s[%p]::SuspendUSBBus - port %d owned by OHCI", getName(), this, i);
}
else if (portStat & kEHCIPortSC_Enabled)
{
EHCIRootHubPortSuspend(i+1, true);
}
else
{
USBLog(7, "%s[%p]::SuspendUSBBus - port %d not enabled", getName(), this, i);
}
}
usbcmd &= ~kEHCICMDRunStop;
_pEHCIRegisters->USBCMD = HostToUSBLong(usbcmd);
IOSync();
_ehciBusState = kEHCIBusStateOff;
USBLog(7, "%s[%p]::SuspendUSBBus - ports suspended, HC stop set, waiting for halted", getName(), this);
do
{
usbsts = USBToHostLong(_pEHCIRegisters->USBSTS);
} while (!(usbsts & kEHCIHCHaltedBit));
USBLog(5, "%s[%p]::SuspendUSBBus - HC halted", getName(), this);
}
void
AppleUSBEHCI::StopUSBBus()
{
UInt32 usbcmd;
usbcmd = USBToHostLong(_pEHCIRegisters->USBCMD);
usbcmd &= ~kEHCICMDRunStop;
_pEHCIRegisters->USBCMD = HostToUSBLong(usbcmd);
_ehciBusState = kEHCIBusStateOff;
USBLog(5, "%s[%p]::StopUSBBus - HC halted", getName(), this);
}
void
AppleUSBEHCI::RestartUSBBus()
{
UInt32 usbcmd, usbsts;
do
{
usbsts = USBToHostLong(_pEHCIRegisters->USBSTS);
} while (!(usbsts & kEHCIHCHaltedBit));
usbcmd = USBToHostLong(_pEHCIRegisters->USBCMD);
usbcmd |= kEHCICMDRunStop;
_pEHCIRegisters->USBCMD = HostToUSBLong(usbcmd);
_ehciBusState = kEHCIBusStateRunning;
USBLog(5, "%s[%p]::RestartUSBBus - HC restarted", getName(), this);
}
IOReturn
AppleUSBEHCI::PowerDownHandler(void *target, void *refCon, UInt32 messageType, IOService *service,
void *messageArgument, vm_size_t argSize )
{
AppleUSBEHCI * me = OSDynamicCast(AppleUSBEHCI, (OSObject *)target);
if (!me || !(me->_errataBits & kErrataICH6PowerSequencing))
return kIOReturnUnsupported;
USBLog(2, "EHCI: %p: PowerDownHandler %p %p", me, (void*)messageType, messageArgument);
if (me->_ehciAvailable) {
switch (messageType)
{
case kIOMessageSystemWillRestart:
case kIOMessageSystemWillPowerOff:
if (me->_ehciBusState == kEHCIBusStateRunning) {
if ( me->_rootHubDevice ) {
me->_rootHubDevice->terminate(kIOServiceRequired | kIOServiceSynchronous);
me->_rootHubDevice->detachAll(gIOUSBPlane);
me->_rootHubDevice->release();
me->_rootHubDevice = NULL;
}
me->SuspendUSBBus();
}
if (me->_uimInitialized) {
me->UIMFinalizeForPowerDown();
}
break;
default:
break;
}
}
return kIOReturnSuccess;
}