AppleUSBUHCI_PwrMgmt.cpp [plain text]
#include <IOKit/usb/USB.h>
#include <IOKit/usb/IOUSBLog.h>
#include <IOKit/usb/IOUSBRootHubDevice.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#include <IOKit/IORegistryEntry.h>
#include <IOKit/IOHibernatePrivate.h>
#include <IOKit/acpi/IOACPIPlatformDevice.h>
#include <libkern/libkern.h>
#ifndef kACPIDevicePathKey
#define kACPIDevicePathKey "acpi-path"
#endif
#include "AppleUSBUHCI.h"
#define super IOUSBControllerV2
#pragma mark Global variables
class AppleUHCIPwrMgmtGlobals
{
public:
AppleUHCIPwrMgmtGlobals();
~AppleUHCIPwrMgmtGlobals();
inline bool isValid( void ) const;
};
#define kUSBRemoteWakeupKey "usb_remote_wakeup"
static const OSSymbol * gUSBRemoteWakeupKey;
static AppleUHCIPwrMgmtGlobals gAppleUHCIPwrMgmtGlobals;
AppleUHCIPwrMgmtGlobals::AppleUHCIPwrMgmtGlobals()
{
gUSBRemoteWakeupKey = OSSymbol::withCStringNoCopy( kUSBRemoteWakeupKey );
}
AppleUHCIPwrMgmtGlobals::~AppleUHCIPwrMgmtGlobals()
{
if (gUSBRemoteWakeupKey) gUSBRemoteWakeupKey->release();
}
bool AppleUHCIPwrMgmtGlobals::isValid( void ) const
{
return (gUSBRemoteWakeupKey);
}
#pragma mark Public power management interface
#define kUHCI_NUM_POWER_STATES 2
static IOPMPowerState powerStates[kUHCI_NUM_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 }
};
void
AppleUSBUHCI::initForPM (IOPCIDevice *provider)
{
USBLog(3, "AppleUSBUHCI[%p]::initForPM %p", this, provider);
if (provider->getProperty("built-in") && (_errataBits & kErrataICH6PowerSequencing))
{
setProperty("Card Type","Built-in");
_unloadUIMAcrossSleep = false;
}
else
{
setProperty("Card Type","PCI");
_unloadUIMAcrossSleep = true;
}
if (_errataBits & kErrataICH6PowerSequencing)
_powerDownNotifier = registerPrioritySleepWakeInterest(PowerDownHandler, this, 0);
if ((_ExpressCardPort = ExpressCardPort(provider)))
{
provider->callPlatformFunction(
"RegisterDebugDriver",
false,
provider,
(void *) this,
(void *) NULL,
(void *) NULL );
}
_badExpressCardAttached = false;
registerPowerDriver(this, powerStates, kUHCI_NUM_POWER_STATES);
changePowerStateTo(kUHCIPowerLevelRunning);
}
unsigned long
AppleUSBUHCI::maxCapabilityForDomainState ( IOPMPowerFlags domainState )
{
if ( (domainState & IOPMPowerOn) || (domainState & kIOPMDoze) )
{
return kUHCIPowerLevelRunning;
} else
{
return kUHCIPowerLevelSuspend;
}
}
unsigned long
AppleUSBUHCI::initialPowerStateForDomainState ( IOPMPowerFlags domainState )
{
return kUHCIPowerLevelRunning;
}
IOReturn
AppleUSBUHCI::setPowerState( unsigned long powerStateOrdinal, IOService* whatDevice )
{
IOReturn result;
static uint32_t * pHibernateState;
USBLog(2, "AppleUSBUHCI[%p]::setPowerState(%d, %p) _saveInterrupts[%p]", this, (int)powerStateOrdinal, whatDevice, (void*)_saveInterrupts);
if (_uhciBusState != kUHCIBusStateSuspended)
{
USBLog(6, "AppleUSBUHCI[%p]::setPowerState calling _workLoop->CloseGate()", this);
_workLoop->CloseGate();
} else
{
USBLog(6, "AppleUSBUHCI[%p]::setPowerState calling _workLoop->wake(%p)", this, (void*)_uhciBusState);
result = _workLoop->wake(&_uhciBusState);
if (result != kIOReturnSuccess)
{
USBError(1, "AppleUSBUHCI[%p]::setPowerState - Can't wake workloop, error %p", this, (void*)result);
}
}
switch (powerStateOrdinal)
{
case kUHCIPowerLevelRunning:
USBLog(2, "AppleUSBUHCI[%p]::setPowerState RUN - changing to running state", this);
if (_idleSuspend)
{
USBLog(2, "AppleUSBUHCI[%p]::setPowerState RUN- from idle suspend", this);
Run(true);
_uhciBusState = kUHCIBusStateRunning;
_idleSuspend = false;
break;
}
if (_uimInitialized && pHibernateState && *pHibernateState && !_wakingFromHibernation)
{
USBLog(1, "AppleUSBUHCI[%p]::setPowerState RUN - pHibernateState[%d] INTR[%p] _uimInitialized[%s] _uhciAvailable[%s] _uhciBusState[%d]", this, *pHibernateState, (void*)ioRead16(kUHCI_INTR), _uimInitialized ? "true" : "false", _uhciAvailable ? "true" : "false", _uhciBusState);
_wakingFromHibernation = true; Run(false);
_uhciBusState = kUHCIBusStateOff;
_uhciAvailable = false;
if ( _rootHubDevice )
{
USBLog(2, "AppleUSBUHCI[%p]::setPowerState - Terminating root hub in setPowerState()", this);
_rootHubDevice->terminate(kIOServiceRequired | kIOServiceSynchronous);
_rootHubDevice->detachAll(gIOUSBPlane);
_rootHubDevice->release();
_rootHubDevice = NULL;
USBLog(2, "AppleUSBUHCI[%p]::setPowerState - Terminated root hub in setPowerState()", this);
}
IOSleep(50); USBLog(2,"AppleUSBUHCI[%p]::setPowerState - spawning root hub creation thread", this);
thread_call_enter(_rootHubCreationThread);
break;
}
if (_uhciBusState == kUHCIBusStateSuspended)
{
if (!isInactive())
{
_idleSuspend = false;
if (_rootHubDevice == NULL)
{
USBLog(2,"AppleUSBUHCI[%p]::setPowerState - spawning root hub creation thread", this);
thread_call_enter(_rootHubCreationThread);
}
else
{
UIMInitializeForPowerUp();
}
}
}
USBLog(2, "AppleUSBUHCI[%p]::setPowerState RUN - resuming controller", this);
ResumeController();
USBLog(2, "AppleUSBUHCI[%p]::setPowerState RUN - enabling interrupt", this);
EnableUSBInterrupt(true);
_remoteWakeupOccurred = true;
USBLog(2, "AppleUSBUHCI[%p]::setPowerState RUN - changing _uhciBusState to kUHCIBusStateRunning", this);
_uhciBusState = kUHCIBusStateRunning;
break;
case kUHCIPowerLevelSuspend:
USBLog(5, "%s[%p]::setPowerState SUSPEND changing to suspended state", getName(), this);
if (_unloadUIMAcrossSleep)
{
USBLog(2, "AppleUSBUHCI[%p]::setPowerState SUSPEND - Unloading UIM before going to sleep", this);
if ( _rootHubDevice )
{
_rootHubDevice->terminate(kIOServiceRequired | kIOServiceSynchronous);
_rootHubDevice->detachAll(gIOUSBPlane);
_rootHubDevice->release();
_rootHubDevice = NULL;
}
}
USBLog(5, "AppleUSBUHCI[%p]::setPowerState SUSPEND - disabling interrupt", this);
EnableUSBInterrupt(false);
SuspendController();
UIMFinalizeForPowerDown();
if ( !pHibernateState )
{
OSData * data = OSDynamicCast(OSData, (IOService::getPMRootDomain())->getProperty(kIOHibernateStateKey));
if (data)
{
pHibernateState = (uint32_t *) data->getBytesNoCopy();
}
}
if (pHibernateState && *pHibernateState)
{
USBLog(2, "AppleUSBUHCI[%p]::setPowerState SUSPEND - pHibernateState[%d]", this, *pHibernateState);
}
_remoteWakeupOccurred = false;
USBLog(5, "AppleUSBUHCI[%p]::setPowerState SUSPEND - changing _uhciBusState to kUHCIBusStateSuspended", this);
_uhciBusState = kUHCIBusStateSuspended;
_idleSuspend = false;
break;
case kUHCIPowerLevelIdleSuspend:
USBLog(2, "AppleUSBUHCI[%p]::setPowerState IDLE SUSPEND- changing to idle suspended state (really stopped)", this);
Run(false);
USBLog(2, "AppleUSBUHCI[%p]::setPowerState IDLE SUSPEND- changing _uhciBusState to kUHCIBusStateOff", this);
_uhciBusState = kUHCIBusStateOff;
_idleSuspend = true;
break;
default:
USBLog(1, "AppleUSBUHCI[%p]::setPowerState - unknown power state %d", this, (int)powerStateOrdinal);
break;
}
if (_uhciBusState == kUHCIBusStateSuspended)
{
USBLog(2, "AppleUSBUHCI[%p]::setPowerState calling _workLoop->sleep(%p)", this, (void*)_uhciBusState);
result = _workLoop->sleep(&_uhciBusState);
if (result!= kIOReturnSuccess)
{
USBError(1, "AppleUSBUHCI[%p]::setPowerState - Can't sleep workloop, error 0x%x", this, result);
}
} else
{
USBLog(2, "AppleUSBUHCI[%p]::setPowerState calling _workLoop->OpenGate()", this);
_workLoop->OpenGate();
}
USBLog(2, "AppleUSBUHCI[%p]::setPowerState(%d, %p) DONE - _saveInterrupts[%p]", this, (int)powerStateOrdinal, whatDevice, (void*)_saveInterrupts);
return IOPMAckImplied;
}
IOReturn
AppleUSBUHCI::callPlatformFunction(const OSSymbol *functionName,
bool waitForFunction,
void *param1, void *param2,
void *param3, void *param4)
{
USBLog(3, "%s[%p]::callPlatformFunction(%s)", getName(), this, functionName->getCStringNoCopy());
if (!strcmp(functionName->getCStringNoCopy(), "SetDebugDriverPowerState"))
{
if (param1)
{
}
else
{
if ((_badExpressCardAttached) && (_ExpressCardPort > 0) && (_errataBits & kErrataSupportsPortResumeEnable))
{
_device->configWrite8(kUHCI_PCI_RES, 0x03 & ~(1 << (_ExpressCardPort-1))); }
}
}
if (functionName == gUSBRemoteWakeupKey)
{
bool *wake = (bool *)param1;
if (_remoteWakeupOccurred)
{
*wake = true;
} else
{
*wake = false;
}
return kIOReturnSuccess;
}
return super::callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4);
}
#pragma mark Internal methods
void
AppleUSBUHCI::ResumeController(void)
{
UInt16 cmd;
int i;
USBLog(5, "AppleUSBUHCI[%p]::ResumeController", this);
USBLog(5, "AppleUSBUHCI[%p]::ResumeController cmd state %x, status %x", this, ioRead16(kUHCI_CMD), ioRead16(kUHCI_STS));
cmd = ioRead16(kUHCI_CMD);
if (cmd & kUHCI_CMD_RS)
{
USBLog(3, "AppleUSBUHCI[%p]::ResumeController - already running - returning", this);
return;
}
for (i=0;i < kUHCI_NVFRAMES; i++)
{
_frameList[i] |= HostToUSBLong(kUHCI_FRAME_T);
}
if (cmd & kUHCI_CMD_EGSM)
{
USBLog(5, "AppleUSBUHCI[%p]::ResumeController controller is globally suspended - forcing resume", this);
cmd |= kUHCI_CMD_FGR;
ioWrite16(kUHCI_CMD, cmd);
cmd = ioRead16(kUHCI_CMD);
USBLog(5, "AppleUSBUHCI[%p]::ResumeController after EGSM->FGR, state is[%p]", this, (void*)cmd);
}
if (cmd & kUHCI_CMD_FGR)
{
IOSleep(20);
cmd &= ~kUHCI_CMD_FGR;
cmd &= ~kUHCI_CMD_EGSM;
ioWrite16(kUHCI_CMD, cmd);
}
USBLog(5, "AppleUSBUHCI[%p]::ResumeController starting controller", this);
Run(true);
IOSleep(10);
for (i=0;i < kUHCI_NVFRAMES; i++)
{
_frameList[i] &= ~HostToUSBLong(kUHCI_FRAME_T);
}
USBLog(5, "AppleUSBUHCI[%p]::ResumeController resume done, cmd %x, status %x ports[%p, %p]", this, ioRead16(kUHCI_CMD), ioRead16(kUHCI_STS),(void*)ReadPortStatus(0), (void*)ReadPortStatus(1));
}
void
AppleUSBUHCI::SuspendController(void)
{
UInt16 cmd, value;
int i;
USBLog(5, "%s[%p]::SuspendController", getName(), this);
USBLog(5, "%s[%p]: cmd state %x, status %x", getName(), this, ioRead16(kUHCI_CMD), ioRead16(kUHCI_STS));
Run(false);
for (i=0; i< 2; i++)
{
value = ReadPortStatus(i) & kUHCI_PORTSC_MASK;
if (value & kUHCI_PORTSC_PED)
{
if (value & kUHCI_PORTSC_SUSPEND)
{
USBLog(5, "AppleUSBUHCI[%p]::SuspendController - port[%d] is suspended [%p]", this, i, (void*)value);
}
else
{
USBLog(5, "AppleUSBUHCI[%p]::SuspendController - port[%d] is enabled but not suspended [%p]", this, i, (void*)value);
}
}
else
{
USBLog(5, "AppleUSBUHCI[%p]::SuspendController - port[%d] is not enabled [%p]", this, i, (void*)value);
}
if ((_errataBits & kErrataUHCISupportsOvercurrent) && (value & kUHCI_PORTSC_OCI)) {
USBLog(1, "AppleUSBUHCI[%p]::SuspendController - port[%d] had the overcurrent bit set. Clearing it", this, i);
WritePortStatus(i, kUHCI_PORTSC_OCI); }
}
cmd = ioRead16(kUHCI_CMD) & ~kUHCI_CMD_FGR;
cmd |= kUHCI_CMD_EGSM;
ioWrite16(kUHCI_CMD, cmd);
_uhciBusState = kUHCIBusStateSuspended;
IOSleep(3);
USBLog(5, "%s[%p]: suspend done, cmd %x, status %x", getName(), this, ioRead16(kUHCI_CMD), ioRead16(kUHCI_STS));
}
IOReturn
AppleUSBUHCI::PowerDownHandler(void *target, void *refCon, UInt32 messageType, IOService *service, void *messageArgument, vm_size_t argSize )
{
AppleUSBUHCI * me = OSDynamicCast(AppleUSBUHCI, (OSObject *)target);
if (!me)
return kIOReturnUnsupported;
USBLog(5, "AppleUSBUHCI[%p]::PowerDownHandler %p %p", me, (void*)messageType, messageArgument);
switch (messageType)
{
case kIOMessageSystemWillRestart:
case kIOMessageSystemWillPowerOff:
if (me->_uhciBusState == kUHCIBusStateRunning)
{
if ( me->_rootHubDevice )
{
USBLog(3, "AppleUSBUHCI[%p]::PowerDownHandler - terminating root hub", me);
me->_rootHubDevice->terminate(kIOServiceRequired | kIOServiceSynchronous);
me->_rootHubDevice->detachAll(gIOUSBPlane);
me->_rootHubDevice->release();
me->_rootHubDevice = NULL;
}
me->_watchdogUSBTimer->cancelTimeout();
me->EnableUSBInterrupt(false);
me->Run(false);
me->Command(kUHCI_CMD_GRESET);
me->_uhciBusState = kUHCIBusStateOff;
}
me->UIMFinalizeForPowerDown();
break;
default:
break;
}
return kIOReturnSuccess;
}
static IOACPIPlatformDevice * CopyACPIDevice( IORegistryEntry * device )
{
IOACPIPlatformDevice * acpiDevice = 0;
OSString * acpiPath;
if (device)
{
acpiPath = (OSString *) device->copyProperty(kACPIDevicePathKey);
if (acpiPath && !OSDynamicCast(OSString, acpiPath))
{
acpiPath->release();
acpiPath = 0;
}
if (acpiPath)
{
IORegistryEntry * entry;
entry = IORegistryEntry::fromPath(acpiPath->getCStringNoCopy());
acpiPath->release();
if (entry && entry->metaCast("IOACPIPlatformDevice"))
acpiDevice = (IOACPIPlatformDevice *) entry;
else if (entry)
entry->release();
}
}
return (acpiDevice);
}
static bool HasExpressCardUSB( IORegistryEntry * acpiDevice, UInt32 * portnum )
{
const IORegistryPlane * acpiPlane;
bool match = false;
IORegistryIterator * iter;
IORegistryEntry * entry;
do {
acpiPlane = acpiDevice->getPlane( "IOACPIPlane" );
if (!acpiPlane)
break;
iter = IORegistryIterator::iterateOver(
acpiDevice,
acpiPlane,
kIORegistryIterateRecursively);
if (iter)
{
while (!match && (entry = iter->getNextObject()))
{
if ((entry->getChildEntry(acpiPlane) == 0) &&
entry->metaCast("IOACPIPlatformDevice"))
{
IOACPIPlatformDevice * port;
port = (IOACPIPlatformDevice *) entry;
if (port->validateObject( "_EJD" ) == kIOReturnSuccess)
{
if (portnum)
*portnum = strtoul(port->getLocation(), NULL, 10);
match = true;
}
}
}
iter->release();
}
}
while (false);
return match;
}
UInt32 AppleUSBUHCI::ExpressCardPort( IOService * provider )
{
IOACPIPlatformDevice * acpiDevice;
UInt32 portNum = 0;
bool isPCIeUSB;
acpiDevice = CopyACPIDevice( provider );
if (acpiDevice)
{
isPCIeUSB = HasExpressCardUSB( acpiDevice, &portNum );
acpiDevice->release();
}
return(portNum);
}