AppleUSBOHCI_PwrMgmt.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOService.h>
#include <IOKit/pwr_mgt/IOPM.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#include <IOKit/platform/ApplePlatformExpert.h>
#include <IOKit/usb/IOUSBRootHubDevice.h>
#include <IOKit/usb/IOUSBHubPolicyMaker.h>
#include <IOKit/usb/IOUSBLog.h>
#include "AppleUSBOHCI.h"
#include "USBTracepoints.h"
#define super IOUSBControllerV3
#define _controllerCanSleep _expansionData->_controllerCanSleep
enum {
kGossamerTypeGossamer = 1,
kGossamerTypeSilk,
kGossamerTypeWallstreet,
kGossamerTypeiMac,
kGossamerTypeYosemite,
kGossamerType101
};
#if OHCI_USE_KPRINTF
#define OHCIPWRMGMT_USE_KPRINTF OHCI_USE_KPRINTF
#else
#define OHCIPWRMGMT_USE_KPRINTF 0
#endif
#if OHCIPWRMGMT_USE_KPRINTF
#undef USBLog
#undef USBError
void kprintf(const char *format, ...) __attribute__((format(printf, 1, 2)));
#define USBLog( LEVEL, FORMAT, ARGS... ) if ((LEVEL) <= OHCIPWRMGMT_USE_KPRINTF) { kprintf( FORMAT "\n", ## ARGS ) ; }
#define USBError( LEVEL, FORMAT, ARGS... ) { kprintf( FORMAT "\n", ## ARGS ) ; }
#endif
void
AppleUSBOHCI::CheckSleepCapability(void)
{
_controllerCanSleep = true;
_hasPCIPwrMgmt = false;
if ( !_device->getProperty("AAPL,clock-id") && !((getPlatform()->getChipSetType() == kChipSetTypeGossamer) && getPlatform()->getMachineType() == kGossamerTypeYosemite) )
{
if (_device->getProperty("built-in"))
{
if (_errataBits & kErrataNECIncompleteWrite)
{
FixupNECControllerConfigRegisters();
}
if (_device->hasPCIPowerManagement(kPCIPMCPMESupportFromD3Cold) && (_device->enablePCIPowerManagement(kPCIPMCSPowerStateD3) == kIOReturnSuccess))
{
_hasPCIPwrMgmt = true;
setProperty("Card Type","Built-in");
}
}
else
{
if (_device->hasPCIPowerManagement() && (_device->enablePCIPowerManagement() == kIOReturnSuccess))
{
_hasPCIPwrMgmt = true;
setProperty("Card Type","Built-in");
}
}
if (!_hasPCIPwrMgmt)
{
USBError(1, "AppleUSBOHCI[%p]::CheckSleepCapability - controller will be unloaded across sleep",this);
_controllerCanSleep = false;
setProperty("Card Type","PCI");
}
}
else
{
setProperty("Card Type","Built-in");
}
_ExpressCardPort = ExpressCardPort(_device);
_badExpressCardAttached = false;
registerService();
}
IOReturn
AppleUSBOHCI::callPlatformFunction(const OSSymbol *functionName,
bool waitForFunction,
void *param1, void *param2,
void *param3, void *param4)
{
if (functionName == _usb_remote_wakeup)
{
bool *wake;
wake = (bool *)param1;
if (_remote_wakeup_occurred)
{
*wake = true;
}
else
{
*wake = false;
}
return kIOReturnSuccess;
}
return kIOReturnBadArgument;
}
void
AppleUSBOHCI::SuspendUSBBus(bool goingToSleep)
{
UInt32 something;
UInt32 hcControl;
USBTrace( kUSBTOHCI, KTPOHCISuspendUSBBus, (uintptr_t)this, goingToSleep, 0, 0);
USBLog(5,"AppleUSBOHCI[%p]::SuspendUSBBus goingToSleep = %s", this, goingToSleep ? "TRUE" : "FALSE");
hcControl = USBToHostLong(_pOHCIRegisters->hcControl);
hcControl &= ~(kOHCIHcControl_CLE | kOHCIHcControl_BLE | kOHCIHcControl_PLE | kOHCIHcControl_IE);
_pOHCIRegisters->hcControl = HostToUSBLong(hcControl);
IOSleep(1);
if ( _writeDoneHeadInterrupt )
{
USBError(1,"AppleUSBOHCI[%p]::SuspendUSBBus Processing WDH before suspending", this);
PollInterrupts();
}
if ( goingToSleep )
{
if (_errataBits & kErrataOHCINoGlobalSuspendOnSleep)
{
UInt32 port;
hcControl = kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase;
for (port=0; port < _rootHubNumPorts; port++)
{
_savedHcRhPortStatus[port] = USBToHostLong(_pOHCIRegisters->hcRhPortStatus[port]);
USBLog(5, "AppleUSBOHCI[%p]::SuspendUSBBus - port %d _savedHcRhPortStatus(%p)", this, (int)port+1, (void*)_savedHcRhPortStatus[port]);
}
}
else
{
hcControl = kOHCIFunctionalState_Suspend << kOHCIHcControl_HCFSPhase;
}
if (_hasPCIPwrMgmt)
hcControl |= kOHCIHcControl_RWC | kOHCIHcControl_RWE;
_pOHCIRegisters->hcControl = HostToUSBLong(hcControl);
IOSleep(3); }
else
{
UInt32 port;
for (port=0; port < _rootHubNumPorts; port++)
{
USBLog(7, "AppleUSBOHCI[%p]::SuspendUSBBus - hcRhPortStatus[%d] = %p", this, (int)port+1, (void*) USBToHostLong(_pOHCIRegisters->hcRhPortStatus[port]));
}
}
}
void
AppleUSBOHCI::ResumeUSBBus(bool wakingFromSleep)
{
UInt32 newValue;
USBTrace( kUSBTOHCI, KTPOHCIResumeUSBBus, (uintptr_t)this, wakingFromSleep, 0, 0);
USBLog(5,"AppleUSBOHCI[%p]::ResumeUSBBus wakingFromSleep = %s", this, wakingFromSleep ? "TRUE" : "FALSE" );
switch ((USBToHostLong(_pOHCIRegisters->hcControl) & kOHCIHcControl_HCFS) >> kOHCIHcControl_HCFSPhase )
{
case kOHCIFunctionalState_Suspend:
USBLog(2, "AppleUSBOHCI[%p]::ResumeUSBBus - Resuming bus from Suspend state", this);
_pOHCIRegisters->hcControl = HostToUSBLong(kOHCIFunctionalState_Resume << kOHCIHcControl_HCFSPhase);
case kOHCIFunctionalState_Resume:
if (_errataBits & kErrataLucentSuspendResume)
{
USBLog(2, "AppleUSBOHCI[%p]::ResumeUSBBus- Delaying 35 milliseconds in resume state", this);
IOSleep(35);
}
else
{
USBLog(2, "AppleUSBOHCI[%p]::ResumeUSBBus - Delaying 20 milliseconds in resume state", this);
IOSleep(20);
}
case kOHCIFunctionalState_Reset:
USBLog(2, "AppleUSBOHCI[%p]::ResumeUSBBus - Changing bus to operational", this);
_pOHCIRegisters->hcControl = HostToUSBLong(kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase);
IOSync();
IOSleep(3);
newValue = USBToHostLong(_pOHCIRegisters->hcRhStatus);
if (!(newValue & kOHCIHcRhStatus_DRWE))
{
_pOHCIRegisters->hcRhStatus = HostToUSBLong(kOHCIHcRhStatus_OCIC | kOHCIHcRhStatus_DRWE);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 count = 0;
newValue = USBToHostLong(_pOHCIRegisters->hcRhStatus); while ((count++ < 10) && !(newValue & kOHCIHcRhStatus_DRWE))
{
USBError(1, "OHCI driver::ResumeUSBBus - DRWE bit not sticking. Retrying.");
_pOHCIRegisters->hcRhStatus = HostToUSBLong(kOHCIHcRhStatus_OCIC | kOHCIHcRhStatus_DRWE);
IOSync();
newValue = USBToHostLong(_pOHCIRegisters->hcRhStatus);
}
}
}
_pOHCIRegisters->hcControl = HostToUSBLong((kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase)
| kOHCIHcControl_CLE | (_OptiOn ? kOHCIHcControl_Zero : kOHCIHcControl_BLE)
| kOHCIHcControl_PLE | kOHCIHcControl_IE);
IOSync();
break;
default:
USBLog(2, "AppleUSBOHCI[%p]::ResumeUSBBus Bus already operational - turning on the lists", this);
_pOHCIRegisters->hcControl = HostToUSBLong((kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase)
| kOHCIHcControl_CLE | (_OptiOn ? kOHCIHcControl_Zero : kOHCIHcControl_BLE)
| kOHCIHcControl_PLE | kOHCIHcControl_IE);
IOSync();
break;
}
if (wakingFromSleep)
{
UInt32 port, portSC;
IOSleep(1);
for (port=0; port < _rootHubNumPorts; port++)
{
UInt32 portSC = USBToHostLong(_pOHCIRegisters->hcRhPortStatus[port]);
if (portSC & kOHCIHcRhPortStatus_CSC)
{
if (portSC & kOHCIHcRhPortStatus_PES)
{
USBError(1, "USB (OHCI):Port %d on bus 0x%x has connect status change but is still enabled. setting clear port enable. hcRhPortStatus(%p)", (int)port+1, (uint32_t)_busNumber, (void*)portSC);
_pOHCIRegisters->hcRhPortStatus[port] = HostToUSBLong(kOHCIHcRhPortStatus_CCS); IOSleep(1);
portSC = USBToHostLong(_pOHCIRegisters->hcRhPortStatus[port]);
}
else
{
USBLog(5, "AppleUSBOHCI[%p]::ResumeUSBBus Port %d on bus 0x%x connected or disconnected. portSC(%p)", this, (int)port+1, (uint32_t)_busNumber, (void*)portSC);
}
}
else if (portSC & kOHCIHcRhPortStatus_PSSC)
{
if (_rootHubDevice && _rootHubDevice->GetPolicyMaker())
{
_rootHubDevice->GetPolicyMaker()->message(kIOUSBMessageRootHubWakeEvent, this, (void *)(uintptr_t) (port));
}
else
{
IOLog("USB (OHCI):Port %d on bus 0x%x has remote wakeup from some device\n", (int)port+1, (uint32_t)_busNumber);
}
USBLog(5, "AppleUSBOHCI[%p]::ResumeUSBBus Port %d on bus 0x%x has remote wakeup from some device", this, (int)port+1, (uint32_t)_busNumber);
}
else if ((_errataBits & kErrataOHCINoGlobalSuspendOnSleep) && (portSC & kOHCIHcRhPortStatus_CCS) && !(portSC & kOHCIHcRhPortStatus_PES) && (_savedHcRhPortStatus[port] & kOHCIHcRhPortStatus_PES)) {
USBError(1, "USB (OHCI):Port %d on bus 0x%x is connected but not enabled. trying to set port enable. hcRhPortStatus(%p) _savedHcRhPortStatus(%p)", (int)port+1, (uint32_t)_busNumber, (void*)portSC, (void*)_savedHcRhPortStatus[port]);
_pOHCIRegisters->hcRhPortStatus[port] = HostToUSBLong(kOHCIHcRhPortStatus_PES); IOSleep(1);
portSC = USBToHostLong(_pOHCIRegisters->hcRhPortStatus[port]);
USBLog(2, "AppleUSBOHCI[%p]::ResumeUSBBus - new hcRhPortStatus(%p)", this, (void*)portSC);
}
_savedHcRhPortStatus[port] = 0; }
}
}
IOReturn
AppleUSBOHCI::AllocatePowerStateArray(void)
{
IOReturn err;
err = super::AllocatePowerStateArray();
if (!err)
{
if ( _device->getProperty("AAPL,clock-id"))
{
_myPowerStates[kUSBPowerStateLowPower].inputPowerRequirement |= kIOPMClockNormal;
_myPowerStates[kUSBPowerStateOn].inputPowerRequirement |= kIOPMClockNormal;
}
}
return err;
}
IOReturn
AppleUSBOHCI::SaveControllerStateForSleep(void)
{
UInt8 pciPMCapOffset = 0;
UInt16 pmControlStatus = 0;
_device->findPCICapability(kIOPCIPowerManagementCapability, &pciPMCapOffset);
if (pciPMCapOffset > 0x3f) {
pmControlStatus = pciPMCapOffset + 4;
}
if (pmControlStatus)
{
UInt16 pmcsr = _device->configRead16(pmControlStatus);
USBLog(7, "AppleUSBOHCI[%p]::SaveControllerStateForSleep before PMCS for device (%p) is (%p)", this, _device, (void*)pmcsr);
if (pmcsr & kPCIPMCSPMEStatus)
{
_device->configWrite16(pmControlStatus, pmcsr);
IOSleep(2);
USBLog(2, "AppleUSBOHCI[%p]::SaveControllerStateForSleep after PMCS for device (%p) is (%p)", this, _device, (void*)_device->configRead16(pmControlStatus));
}
}
USBLog(2, "AppleUSBOHCI[%p]::SaveControllerStateForSleep - suspending the bus", this);
_remote_wakeup_occurred = false;
SuspendUSBBus(true);
USBLog(2, "AppleUSBOHCI[%p]::SaveControllerStateForSleep - The bus is now suspended", this);
_myBusState = kUSBBusStateSuspended;
if (_hasPCIPwrMgmt)
{
_pOHCIRegisters->hcInterruptDisable = HostToUSBLong (kOHCIHcInterrupt_MIE); }
return kIOReturnSuccess;
}
IOReturn
AppleUSBOHCI::RestoreControllerStateFromSleep(void)
{
UInt32 cmdSts = USBToHostLong(_pOHCIRegisters->hcCommandStatus);
if (cmdSts == kOHCIInvalidRegisterValue)
{
_controllerAvailable = false;
return kIOReturnSuccess;
}
USBLog(2, "AppleUSBOHCI[%p]::RestoreControllerStateFromSleep - powering on USB", this);
_remote_wakeup_occurred = true;
InitializeOperationalRegisters();
if (_errataBits & kErrataNECIncompleteWrite)
{
FixupNECControllerConfigRegisters();
}
ResumeUSBBus(true);
_myBusState = kUSBBusStateRunning;
LastRootHubPortStatusChanged(true);
return kIOReturnSuccess;
}
IOReturn
AppleUSBOHCI::ResetControllerState(void)
{
USBTrace( kUSBTOHCI, KTPOHCIResetControllerState, (uintptr_t)this, 0, 0, 0);
_pOHCIRegisters->hcInterruptDisable = HostToUSBLong(kOHCIHcInterrupt_MIE);
IOSync();
_pOHCIRegisters->hcControl = HostToUSBLong((kOHCIFunctionalState_Reset << kOHCIHcControl_HCFSPhase));
IOSync();
IOSleep(50);
_pOHCIRegisters->hcHCCA = 0;
_pOHCIRegisters->hcControlHeadED = 0;
_pOHCIRegisters->hcControlCurrentED = 0;
_pOHCIRegisters->hcBulkHeadED = 0;
_pOHCIRegisters->hcBulkCurrentED = 0;
IOSync();
OHCIRootHubPower(0 );
_pOHCIRegisters->hcCommandStatus = HostToUSBLong(kOHCIHcCommandStatus_HCR); IOSync();
IOSleep(1);
return kIOReturnSuccess;
}
IOReturn
AppleUSBOHCI::RestartControllerFromReset(void)
{
UInt32 newValue;
USBTrace( kUSBTOHCI, KTPOHCIRestartControllerFromReset, (uintptr_t)this, _uimInitialized, 0, 0);
USBLog(3, "AppleUSBOHCI[%p]::RestartControllerFromReset - Re-loading UIM if necessary (%d)", this, _uimInitialized );
InitializeOperationalRegisters();
_pOHCIRegisters->hcControl = HostToUSBLong(kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase);
IOSync();
IOSleep(3);
newValue = USBToHostLong(_pOHCIRegisters->hcRhStatus);
if (!(newValue & kOHCIHcRhStatus_DRWE))
{
_pOHCIRegisters->hcRhStatus = HostToUSBLong(kOHCIHcRhStatus_OCIC | kOHCIHcRhStatus_DRWE);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 count = 0;
newValue = USBToHostLong(_pOHCIRegisters->hcRhStatus); while ((count++ < 10) && !(newValue & kOHCIHcRhStatus_DRWE))
{
USBError(1, "OHCI driver::RestartControllerFromReset - DRWE bit not sticking. Retrying.");
_pOHCIRegisters->hcRhStatus = HostToUSBLong(kOHCIHcRhStatus_OCIC | kOHCIHcRhStatus_DRWE);
IOSync();
newValue = USBToHostLong(_pOHCIRegisters->hcRhStatus);
}
}
}
_pOHCIRegisters->hcControl = HostToUSBLong((kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase)
| kOHCIHcControl_CLE | (_OptiOn ? kOHCIHcControl_Zero : kOHCIHcControl_BLE)
| kOHCIHcControl_PLE | kOHCIHcControl_IE);
IOSync();
OHCIRootHubPower(1 );
_myBusState = kUSBBusStateRunning;
return kIOReturnSuccess;
}
IOReturn
AppleUSBOHCI::EnableInterruptsFromController(bool enable)
{
USBTrace( kUSBTOHCI, KTPOHCIEnableInterrupts, (uintptr_t)this, enable, 0, 0);
if (enable)
{
USBLog(2, "AppleUSBOHCI[%p]::EnableInterruptsFromController - enabling interrupts 0x%x", this, (kOHCIHcInterrupt_MIE | kOHCIDefaultInterrupts));
_pOHCIRegisters->hcInterruptEnable = HostToUSBLong (kOHCIHcInterrupt_MIE | kOHCIDefaultInterrupts);
IOSync();
}
else
{
_pOHCIRegisters->hcInterruptDisable = HostToUSBLong (kOHCIHcInterrupt_MIE); IOSync();
USBLog(2, "AppleUSBOHCI[%p]::EnableInterruptsFromController - interrupts disabled", this);
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBOHCI::DozeController(void)
{
USBTrace( kUSBTOHCI, KTPOHCIDozeController, (uintptr_t)this, 0, 0, 0);
if ( !(USBToHostLong(_pOHCIRegisters->hcInterruptEnable) & kOHCIHcInterrupt_RHSC) )
{
UInt32 interrupts;
USBLog(1, "AppleUSBOHCI[%p]::DozeController - RHSC is disabled. Enabling it", this);
interrupts = USBToHostLong(_pOHCIRegisters->hcInterruptEnable);
interrupts |= kOHCIHcInterrupt_RHSC;
_pOHCIRegisters->hcInterruptEnable = HostToUSBLong(interrupts);
IOSync();
}
SuspendUSBBus(false);
return kIOReturnSuccess;
}
IOReturn
AppleUSBOHCI::WakeControllerFromDoze(void)
{
USBTrace( kUSBTOHCI, KTPOHCIWakeControllerFromDoze, (uintptr_t)this, 0, 0, 0);
ResumeUSBBus(false);
if ( _rootHubStatuschangedInterruptReceived )
{
_rootHubStatuschangedInterruptReceived = false;
USBLog(6, "AppleUSBOHCI[%p]::WakeControllerFromDoze - we had received a RHSC interrupt, so waiting 21ms before proceeding", this);
IOSleep(21);
}
else
{
USBLog(6, "AppleUSBOHCI[%p]::WakeControllerFromDoze - not from a RHSC interrupt", this);
}
return kIOReturnSuccess;
}
void
AppleUSBOHCI::powerChangeDone ( unsigned long fromState)
{
unsigned long newState = getPowerState();
USBTrace( kUSBTOHCI, KTPOHCIPowerState, (uintptr_t)this, fromState, newState, 0);
USBLog((fromState == newState) || !_controllerAvailable ? 7 : 5, "AppleUSBOHCI[%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);
}