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>
#include "AppleUSBUHCI.h"
#include "USBTracepoints.h"
#define super IOUSBControllerV3
#define _controllerCanSleep _expansionData->_controllerCanSleep
#ifndef kACPIDevicePathKey
#define kACPIDevicePathKey "acpi-path"
#endif
#if UHCI_USE_KPRINTF
#define UHCIPWRMGMT_USE_KPRINTF UHCI_USE_KPRINTF
#else
#define UHCIPWRMGMT_USE_KPRINTF 0
#endif
#if UHCIPWRMGMT_USE_KPRINTF
#undef USBLog
#undef USBError
void kprintf(const char *format, ...) __attribute__((format(printf, 1, 2)));
#define USBLog( LEVEL, FORMAT, ARGS... ) if ((LEVEL) <= UHCIPWRMGMT_USE_KPRINTF) { kprintf( FORMAT "\n", ## ARGS ) ; }
#define USBError( LEVEL, FORMAT, ARGS... ) { kprintf( FORMAT "\n", ## ARGS ) ; }
#endif
#pragma mark Public power management interface
void
AppleUSBUHCI::CheckSleepCapability(void)
{
if (_device->getProperty("built-in") && (_errataBits & kErrataICH6PowerSequencing))
{
setProperty("Card Type","Built-in");
_controllerCanSleep = true;
}
else
{
setProperty("Card Type","PCI");
_controllerCanSleep = false;
}
if ((_ExpressCardPort = ExpressCardPort(_device)))
{
_device->callPlatformFunction(
"RegisterDebugDriver",
false,
_device,
(void *) this,
(void *) NULL,
(void *) NULL );
}
_badExpressCardAttached = false;
registerService();
}
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 (!strncmp(functionName->getCStringNoCopy(), "SetDebugDriverPowerState", 24))
{
if (param1)
{
}
else
{
if ((_badExpressCardAttached) && (_ExpressCardPort > 0) && (_errataBits & kErrataSupportsPortResumeEnable))
{
_device->configWrite8(kUHCI_PCI_RES, 0x03 & ~(1 << (_ExpressCardPort-1))); }
}
}
return super::callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4);
}
#pragma mark Internal methods
void
AppleUSBUHCI::ResumeController(void)
{
UInt16 cmd;
int i;
USBTrace( kUSBTUHCI, KTPUHCIResumeController , (uintptr_t)this, 0, 0, 0);
showRegisters(7, "+ResumeController");
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, cmd is[%p]", this, (void*)cmd);
}
if (cmd & kUHCI_CMD_FGR)
{
IOSleep(20);
cmd &= ~kUHCI_CMD_FGR;
cmd &= ~kUHCI_CMD_EGSM;
ioWrite16(kUHCI_CMD, cmd);
}
if ((cmd & (kUHCI_CMD_MAXP | kUHCI_CMD_CF)) != (kUHCI_CMD_MAXP | kUHCI_CMD_CF))
{
USBLog(5, "AppleUSBUHCI[%p]::ResumeController marking MAXP and CF", this);
cmd |= (kUHCI_CMD_MAXP | kUHCI_CMD_CF);
ioWrite16(kUHCI_CMD, cmd);
}
if (_framesPaddr != NULL)
{
USBLog(5, "AppleUSBUHCI[%p]::ResumeController setting FRBASEADDR[%p]", this, (void*)_framesPaddr);
ioWrite32(kUHCI_FRBASEADDR, _framesPaddr);
}
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(7, "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));
showRegisters(7, "-ResumeController");
}
void
AppleUSBUHCI::SuspendController(void)
{
UInt16 cmd, value;
int i;
USBTrace( kUSBTUHCI, kTPUHCISuspendController, (uintptr_t)this, 0, 0, 1 );
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);
USBTrace( kUSBTUHCI, kTPUHCISuspendController, (uintptr_t)this, i, 0, 2 );
WritePortStatus(i, kUHCI_PORTSC_OCI); }
}
cmd = ioRead16(kUHCI_CMD) & ~kUHCI_CMD_FGR;
cmd |= kUHCI_CMD_EGSM;
ioWrite16(kUHCI_CMD, cmd);
_myBusState = kUSBBusStateSuspended;
IOSleep(3);
USBLog(5, "%s[%p]: suspend done, cmd %x, status %x", getName(), this, ioRead16(kUHCI_CMD), ioRead16(kUHCI_STS));
}
IOReturn
AppleUSBUHCI::SaveControllerStateForSleep(void)
{
USBLog(5, "AppleUSBUHCI[%p]::SaveControllerStateForSleep cancelling rhTimer", this);
USBLog(5, "AppleUSBUHCI[%p]::SaveControllerStateForSleep SUSPEND - disabling interrupt", this);
SuspendController();
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::RestoreControllerStateFromSleep(void)
{
int i;
UInt16 value;
bool wakeMsg = false;
USBLog(5, "AppleUSBUHCI[%p]::RestoreControllerStateFromSleep RUN - resuming controller", this);
for (i=0; i< 2; i++)
{
value = ReadPortStatus(i);
if (value & kUHCI_PORTSC_CSC)
{
IOLog("USB (UHCI):Port %d on bus 0x%x connected or disconnected\n", (int)i+1, (uint32_t)_busNumber);
}
else if (value & kUHCI_PORTSC_RD)
{
IOLog("USB (UHCI):Port %d on bus 0x%x has remote wakeup from some device\n", (int)i+1, (uint32_t)_busNumber);
}
}
ResumeController();
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::ResetControllerState(void)
{
UInt32 value;
int i;
USBTrace( kUSBTUHCI, KTPUHCIResetControllerState, (uintptr_t)this, 0, 0, 0);
USBLog(5, "AppleUSBUHCI[%p]::+ResetControllerState", this);
Command(kUHCI_CMD_HCRESET);
for(i=0; (i < kUHCI_RESET_DELAY) && (ioRead16(kUHCI_CMD) & kUHCI_CMD_HCRESET); i++)
{
IOSleep(1);
}
if (i >= kUHCI_RESET_DELAY)
{
USBError(1, "AppleUSBUHCI[%p]::ResetControllerStatecontroller - reset failed", this);
return kIOReturnTimeout;
}
USBLog(5, "AppleUSBUHCI[%p]::ResetControllerStatecontroller - reset done after %d spins", this, i);
if (_framesPaddr != NULL)
{
ioWrite32(kUHCI_FRBASEADDR, _framesPaddr);
}
for (i = 0; i < kUHCI_NUM_PORTS; i++)
{
_lastPortStatus[i] = 0;
}
Command(kUHCI_CMD_MAXP | kUHCI_CMD_CF);
USBLog(5, "AppleUSBUHCI[%p]::-ResetControllerState", this);
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::RestartControllerFromReset(void)
{
USBTrace( kUSBTUHCI, KTPUHCIRestartControllerFromReset, (uintptr_t)this, 0, 0, 0);
USBLog(5, "AppleUSBUHCI[%p]::RestartControllerFromReset - _myBusState(%d) CMD(%p) STS(%p) FRBASEADDR(%p) IOPCIConfigCommand(%p)", this, (int)_myBusState, (void*)ioRead16(kUHCI_CMD), (void*)ioRead16(kUHCI_STS), (void*)ioRead32(kUHCI_FRBASEADDR), (void*)_device->configRead16(kIOPCIConfigCommand));
Run(true);
_saveInterrupts = kUHCI_INTR_TIE | kUHCI_INTR_RIE | kUHCI_INTR_IOCE | kUHCI_INTR_SPIE;
USBLog(5, "AppleUSBUHCI[%p]::RestartControllerFromReset - I set _saveInterrupts to (%p)", this, (void*)_saveInterrupts);
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::EnableInterruptsFromController(bool enable)
{
USBTrace( kUSBTUHCI, KTPUHCIEnableInterrupts, (uintptr_t)this, enable, 0, 0);
if (enable)
{
USBLog(5, "AppleUSBUHCI[%p]::EnableInterruptsFromController - enabling interrupts, USBIntr(%p) _savedUSBIntr(%p)", this, (void*)ioRead16(kUHCI_INTR), (void*)_saveInterrupts);
ioWrite16(kUHCI_INTR, _saveInterrupts);
_saveInterrupts = 0;
EnableUSBInterrupt(true);
}
else
{
_saveInterrupts = ioRead16(kUHCI_INTR);
ioWrite16(kUHCI_INTR, 0);
EnableUSBInterrupt(false);
USBLog(5, "AppleUSBUHCI[%p]::EnableInterruptsFromController - interrupts disabled, _saveInterrupts(%p)", this, (void*)_saveInterrupts);
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::DozeController(void)
{
UInt16 cmd;
USBTrace( kUSBTUHCI, KTPUHCIDozeController, (uintptr_t)this, 0, 0, 0);
USBLog(6, "AppleUSBUHCI[%p]::DozeController", this);
showRegisters(7, "+DozeController - stopping controller");
Run(false);
USBLog(6, "AppleUSBUHCI[%p]::DozeController Globally suspending", this);
cmd = ioRead16(kUHCI_CMD) & ~kUHCI_CMD_FGR;
cmd |= kUHCI_CMD_EGSM;
ioWrite16(kUHCI_CMD, cmd);
_myBusState = kUSBBusStateSuspended;
IOSleep(3);
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::WakeControllerFromDoze(void)
{
UInt16 cmd;
int i;
bool portHasRD[kUHCI_NUM_PORTS];
UInt16 status;
USBTrace( kUSBTUHCI, KTPUHCIWakeFromDoze, (uintptr_t)this, 0, 0, 0);
for (i=0; i<kUHCI_NUM_PORTS; i++)
{
status = ReadPortStatus(i);
if (status & kUHCI_PORTSC_RD)
{
USBLog(6, "AppleUSBUHCI[%p]::WakeControllerFromDoze controller port %d has kUHCI_PORTSC_RD set", this, i+1);
portHasRD[i] = true;
}
else
{
portHasRD[i] = false;
}
}
cmd = ioRead16(kUHCI_CMD);
if (cmd & kUHCI_CMD_EGSM)
{
USBLog(6, "AppleUSBUHCI[%p]::WakeControllerFromDoze controller is globally suspended - forcing resume", this);
cmd |= kUHCI_CMD_FGR;
ioWrite16(kUHCI_CMD, cmd);
cmd = ioRead16(kUHCI_CMD);
USBLog(6, "AppleUSBUHCI[%p]::WakeControllerFromDoze after EGSM->FGR, cmd is[%p], sleeping 20ms", this, (void*)cmd);
IOSleep(20);
cmd &= ~kUHCI_CMD_FGR;
cmd &= ~kUHCI_CMD_EGSM;
ioWrite16(kUHCI_CMD, cmd);
for (i=0; i<kUHCI_NUM_PORTS; i++)
{
if (portHasRD[i] )
{
status = ReadPortStatus(i) & kUHCI_PORTSC_MASK;
status &= ~(kUHCI_PORTSC_RD | kUHCI_PORTSC_SUSPEND);
USBLog(6, "AppleUSBUHCI[%p]::WakeControllerFromDoze de-asserting resume signal for port %d by writing (%p)", this, i+1, (void*)status);
WritePortStatus(i, status);
IOSync();
IOSleep(2); }
}
}
USBLog(6, "AppleUSBUHCI[%p]::WakeControllerFromDoze calling Run(true)", this);
Run(true);
_myBusState = kUSBBusStateRunning;
showRegisters(7, "-WakeControllerFromDoze");
return kIOReturnSuccess;
}
IOReturn
AppleUSBUHCI::powerStateWillChangeTo ( IOPMPowerFlags capabilities, unsigned long newState, IOService* whichDevice)
{
USBTrace( kUSBTUHCI, KTPUHCIPowerState, (uintptr_t)this, newState, 0, 1);
USBLog(5, "AppleUSBUHCI[%p]::powerStateWillChangeTo new state (%d)", this, (int)newState);
showRegisters(7, "powerStateWillChangeTo");
return super::powerStateWillChangeTo(capabilities, newState, whichDevice);
}
IOReturn
AppleUSBUHCI::powerStateDidChangeTo ( IOPMPowerFlags capabilities, unsigned long newState, IOService* whichDevice)
{
USBTrace( kUSBTUHCI, KTPUHCIPowerState, (uintptr_t)this, newState, 0, 2);
USBLog(5, "AppleUSBUHCI[%p]::powerStateDidChangeTo new state (%d)", this, (int)newState);
showRegisters(7, "powerStateDidChangeTo");
return super::powerStateDidChangeTo(capabilities, newState, whichDevice);
}
void
AppleUSBUHCI::powerChangeDone ( unsigned long fromState)
{
unsigned long newState = getPowerState();
USBTrace( kUSBTUHCI, KTPUHCIPowerState, (uintptr_t)this, fromState, newState, 3);
USBLog((fromState == newState) ? 7 : 5, "AppleUSBUHCI[%p]::powerChangeDone from state (%d) to state (%d) _controllerAvailable(%s)", this, (int)fromState, (int)newState, _controllerAvailable ? "true" : "false");
if (_controllerAvailable)
showRegisters(7, "powerChangeDone");
super::powerChangeDone(fromState);
}
#pragma mark ¥¥¥¥ Utility functions ¥¥¥¥
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;
USBLog(3, "AppleUSBUHCI for acpiDevice: %p: HasExpressCardUSB: portNum: %d", acpiDevice, (uint32_t)*portnum);
}
}
}
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);
}