AppleUSBOHCI_PwrMgmt.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOService.h>
#include <IOKit/IOPlatformExpert.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/IOUSBLog.h>
#define super IOUSBControllerV3
enum {
kGossamerTypeGossamer = 1,
kGossamerTypeSilk,
kGossamerTypeWallstreet,
kGossamerTypeiMac,
kGossamerTypeYosemite,
kGossamerType101
};
#include "AppleUSBOHCI.h"
#define DEBUG_PCI_PWR_MGMT 1
#define _controllerCanSleep _expansionData->_controllerCanSleep
void
AppleUSBOHCI::CheckSleepCapability(void)
{
_controllerCanSleep = true;
_hasPCIPwrMgmt = false;
_onCardBus = (0 != _device->metaCast("IOCardBusDevice"));
if ( !_device->getProperty("AAPL,clock-id") && !_onCardBus && !((getPlatform()->getChipSetType() == kChipSetTypeGossamer) && getPlatform()->getMachineType() == kGossamerTypeYosemite) )
{
if (_device->getProperty("built-in"))
{
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");
}
if ( _onCardBus )
{
setProperty("Card Type","CardBus");
_controllerCanSleep = 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;
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] Processing WDH before suspending", this);
PollInterrupts();
}
if (goingToSleep && (_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);
}
void
AppleUSBOHCI::ResumeUSBBus(bool wakingFromSleep)
{
UInt32 newValue;
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
{
IOLog("USB (OHCI):Port %d on bus 0x%x connected or disconnected. portSC(%p)\n", (int)port+1, (uint32_t)_busNumber, (void*)portSC);
}
}
else if (portSC & kOHCIHcRhPortStatus_PSSC)
{
IOLog("USB (OHCI):Port %d on bus 0x%x has remote wakeup from some device\n", (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; }
}
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]: 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;
}
}
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)
{
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)
{
USBLog(2, "AppleUSBOHCI[%p]::RestoreControllerStateFromSleep - powering on USB", this);
_remote_wakeup_occurred = true;
InitializeOperationalRegisters(false);
ResumeUSBBus(true);
_myBusState = kUSBBusStateRunning;
LastRootHubPortStatusChanged(true);
return kIOReturnSuccess;
}
IOReturn
AppleUSBOHCI::ResetControllerState(void)
{
_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;
USBLog(3, "AppleUSBOHCI[%p]::RestartControllerFromReset - Re-loading UIM if necessary (%d)", this, _uimInitialized );
InitializeOperationalRegisters(true);
_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)
{
if (enable)
{
USBLog(2, "AppleUSBOHCI[%p]::EnableInterruptsFromController - enabling interrupts", this);
_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)
{
SuspendUSBBus(false);
return kIOReturnSuccess;
}
IOReturn
AppleUSBOHCI::WakeControllerFromDoze(void)
{
ResumeUSBBus(false);
_myBusState = kUSBBusStateRunning;
return kIOReturnSuccess;
}
void
AppleUSBOHCI::powerChangeDone ( unsigned long fromState)
{
unsigned long newState = getPowerState();
USBLog((fromState == newState) ? 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);
}