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/IOMessage.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("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);
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;
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) {
if (!isInactive()) {
EnableUSBInterrupt(false);
UIMInitializeForPowerUp();
EnableUSBInterrupt(true);
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();
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);
}
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(4, "UHCI: %p: PowerDownHandler %x %x", me, messageType, messageArgument);
switch (messageType)
{
case kIOMessageSystemWillRestart:
case kIOMessageSystemWillPowerOff:
if (me->_powerLevel == kUHCIPowerLevelRunning) {
if ( me->_rootHubDevice )
{
me->_rootHubDevice->terminate(kIOServiceRequired | kIOServiceSynchronous);
me->_rootHubDevice->detachAll(gIOUSBPlane);
me->_rootHubDevice->release();
me->_rootHubDevice = NULL;
}
me->SuspendController();
me->_powerLevel = kUHCIPowerLevelSuspend;
}
if (me->_powerLevel != kUHCIPowerLevelSuspend) {
me->UIMFinalizeForPowerDown();
}
break;
default:
break;
}
return kIOReturnSuccess;
}