AppleUSBUHCI_PwrMgmt.cpp [plain text]
#include <IOKit/usb/USB.h>
#include <IOKit/usb/IOUSBLog.h>
#include <IOKit/usb/IOUSBRootHubDevice.h>
#include "AppleUSBUHCI.h"
#define super IOUSBController
#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, "%s[%p]::initForPM %p", getName(), this, provider);
if (provider->getProperty("AAPL,clock-id")) {
USBLog(3, "%s[%p]: using Key Largo flags", getName(), this);
powerStates[kUHCIPowerLevelRunning].inputPowerRequirement |= IOPMClockNormal;
}
setProperty("Card Type","PCI");
_unloadUIMAcrossSleep = true;
registerPowerDriver(this, powerStates, kUHCI_NUM_POWER_STATES);
changePowerStateTo(kUHCIPowerLevelRunning);
}
unsigned long
AppleUSBUHCI::maxCapabilityForDomainState ( IOPMPowerFlags domainState )
{
if ( getProvider()->getProperty("AAPL,clock-id") ) {
if (((domainState & IOPMPowerOn) && (domainState & IOPMClockNormal)) ||
(domainState & kIOPMDoze) && (domainState & IOPMClockNormal)) {
return kUHCIPowerLevelRunning;
} else {
return kUHCIPowerLevelSuspend;
}
} else {
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;
USBLog(3, "%s[%p]::setPowerState(%d, %p)", getName(), this, powerStateOrdinal, whatDevice);
if (_powerLevel != kUHCIPowerLevelSuspend) {
_workLoop->CloseGate();
} else {
result = _workLoop->wake(&_powerLevel);
if (result != kIOReturnSuccess) {
USBError(1, "%s[%p] setPowerState - Can't wake workloop, error 0x%x", getName(), this, result);
}
}
switch (powerStateOrdinal) {
case kUHCIPowerLevelRunning:
USBLog(3, "%s[%p]: changing to running state", getName(), this);
if (_powerLevel == kUHCIPowerLevelSuspend && _unloadUIMAcrossSleep) {
if (!isInactive()) {
UIMInitializeForPowerUp();
if (_rootHubDevice == NULL) {
result = CreateRootHubDevice(_device, &_rootHubDevice);
if (result != kIOReturnSuccess) {
USBError(1,"%s[%p] Could not create root hub device on wakeup (%x)!",getName(), this, result);
} else {
_rootHubDevice->registerService(kIOServiceRequired | kIOServiceSynchronous);
}
}
}
}
ResumeController();
_remoteWakeupOccurred = true;
_powerLevel = kUHCIPowerLevelRunning;
break;
case kUHCIPowerLevelSuspend:
USBLog(3, "%s[%p]: changing to suspended state", getName(), this);
if (_unloadUIMAcrossSleep) {
USBLog(3, "%s[%p] Unloading UIM before going to sleep", getName(), this);
if ( _rootHubDevice )
{
_rootHubDevice->terminate(kIOServiceRequired | kIOServiceSynchronous);
_rootHubDevice->detachAll(gIOUSBPlane);
_rootHubDevice->release();
_rootHubDevice = NULL;
}
}
SuspendController();
if (_unloadUIMAcrossSleep) {
UIMFinalizeForPowerDown();
}
_remoteWakeupOccurred = false;
_powerLevel = kUHCIPowerLevelSuspend;
break;
case kUHCIPowerLevelIdleSuspend:
USBLog(3, "%s[%p]: changing to idle suspended state", getName(), this);
SuspendController();
_powerLevel = kUHCIPowerLevelIdleSuspend;
break;
default:
USBLog(3, "%s[%p]: unknown power state %d", getName(), this, powerStateOrdinal);
break;
}
if (_powerLevel == kUHCIPowerLevelSuspend) {
result = _workLoop->sleep(&_powerLevel);
if (result!= kIOReturnSuccess) {
USBError(1, "%s[%p] setPowerState - Can't sleep workloop, error 0x%x", getName(), this, result);
}
} else {
_workLoop->OpenGate();
}
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 (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(3, "%s[%p]::ResumeController", getName(), this);
USBLog(3, "%s[%p]: cmd state %x, status %x", getName(), this,
ioRead16(kUHCI_CMD), ioRead16(kUHCI_STS));
for (i=0; i<10; i++) {
cmd = ioRead16(kUHCI_CMD);
if (cmd & kUHCI_CMD_EGSM) {
USBLog(5, "%s[%p]: taking controller out of global suspend mode",
getName(), this);
cmd &= ~kUHCI_CMD_EGSM;
cmd |= kUHCI_CMD_FGR;
ioWrite16(kUHCI_CMD, cmd);
IODelay(20);
continue;
}
if (cmd & kUHCI_CMD_FGR) {
USBLog(3, "%s[%p]: taking controller out of force resume",
getName(), this);
IOSleep(20);
cmd &= ~kUHCI_CMD_FGR;
ioWrite16(kUHCI_CMD, cmd);
IOSleep(3);
continue;
}
if ((cmd & kUHCI_CMD_RS) == 0) {
USBLog(3, "%s[%p]: starting controller",
getName(), this);
Run(true);
}
break;
}
USBLog(3, "%s[%p]: resume done, cmd %x, status %x", getName(), this,
ioRead16(kUHCI_CMD), ioRead16(kUHCI_STS));
}
void
AppleUSBUHCI::SuspendController(void)
{
UInt16 cmd;
USBLog(3, "%s[%p]::SuspendController", getName(), this);
USBLog(3, "%s[%p]: cmd state %x, status %x", getName(), this,
ioRead16(kUHCI_CMD), ioRead16(kUHCI_STS));
Run(false);
cmd = ioRead16(kUHCI_CMD) & ~kUHCI_CMD_FGR;
cmd |= kUHCI_CMD_EGSM;
ioWrite16(kUHCI_CMD, cmd);
IOSleep(3);
USBLog(3, "%s[%p]: suspend done, cmd %x, status %x", getName(), this,
ioRead16(kUHCI_CMD), ioRead16(kUHCI_STS));
}
void
AppleUSBUHCI::StopController(void)
{
Run(false);
}
void
AppleUSBUHCI::RestartController(void)
{
Run(true);
}