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 kAppleCurrentAvailable "AAPL,current-available"
#define DEBUG_PCI_PWR_MGMT 1
#define _controllerCanSleep _expansionData->_controllerCanSleep
void
AppleUSBOHCI::CheckSleepCapability(void)
{
_controllerCanSleep = true;
_onCardBus = (0 != _device->metaCast("IOCardBusDevice"));
if ( !_device->getProperty("AAPL,clock-id") && !_onCardBus && !((getPlatform()->getChipSetType() == kChipSetTypeGossamer) && getPlatform()->getMachineType() == kGossamerTypeYosemite) )
{
if (_device->hasPCIPowerManagement() && (_device->enablePCIPowerManagement() == kIOReturnSuccess))
{
_hasPCIPwrMgmt = true;
setProperty("Card Type","Built-in");
}
else
{
USBError(1, "AppleUSBOHCI[%p]::CheckSleepCapability - OHCI 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()
{
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();
}
hcControl = kOHCIFunctionalState_Suspend << kOHCIHcControl_HCFSPhase;
if (_hasPCIPwrMgmt)
hcControl |= kOHCIHcControl_RWC | kOHCIHcControl_RWE;
_pOHCIRegisters->hcControl = HostToUSBLong(hcControl);
IOSleep(3);
}
void
AppleUSBOHCI::ResumeUSBBus()
{
switch ((USBToHostLong(_pOHCIRegisters->hcControl) & kOHCIHcControl_HCFS) >> kOHCIHcControl_HCFSPhase )
{
case kOHCIFunctionalState_Suspend:
USBLog(2, "AppleUSBOHCI[%p]:: Resuming bus from Suspend state", this);
_pOHCIRegisters->hcControl = HostToUSBLong(kOHCIFunctionalState_Resume << kOHCIHcControl_HCFSPhase);
case kOHCIFunctionalState_Resume:
if(_errataBits & kErrataLucentSuspendResume)
{
USBLog(2, "AppleUSBOHCI[%p]:: Delaying 35 milliseconds in resume state", this);
IOSleep(35);
}
else
{
USBLog(2, "AppleUSBOHCI[%p]:: Delaying 20 milliseconds in resume state", this);
IOSleep(20);
}
case kOHCIFunctionalState_Reset:
USBLog(2, "AppleUSBOHCI[%p]: Changing bus to operational", this);
_pOHCIRegisters->hcControl = HostToUSBLong(kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase);
IOSleep(3); _pOHCIRegisters->hcControl = HostToUSBLong((kOHCIFunctionalState_Operational << kOHCIHcControl_HCFSPhase)
| kOHCIHcControl_CLE | (_OptiOn ? kOHCIHcControl_Zero : kOHCIHcControl_BLE)
| kOHCIHcControl_PLE | kOHCIHcControl_IE);
break;
default:
USBLog(2, "AppleUSBOHCI[%p]: Bus already operational", this);
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]::setPowerState - suspending the bus", this);
_remote_wakeup_occurred = false;
SuspendUSBBus();
USBLog(2, "AppleUSBOHCI[%p]::setPowerState - 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);
if (USBToHostLong(_pOHCIRegisters->hcInterruptStatus) & kOHCIHcInterrupt_RD)
{
IOLog("USB caused wake event (OHCI)\n");
}
_remote_wakeup_occurred = true;
ResumeUSBBus();
_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(2);
_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 hcDoneHead;
USBLog(3, "AppleUSBOHCI[%p]::RestartControllerFromReset - Re-loading UIM if necessary (%d)", this, _uimInitialized );
hcDoneHead = USBToHostLong(_pOHCIRegisters->hcDoneHead);
if ( hcDoneHead != NULL )
{
USBError(1,"AppleUSBOHCI[%p]::RestartControllerFromReset Non-NULL hcDoneHead: 0x%lx", this, hcDoneHead );
_pOHCIRegisters->hcCommandStatus = USBToHostLong(kOHCIHcCommandStatus_HCR); IOSleep(3);
}
_pOHCIRegisters->hcControlCurrentED = 0;
_pOHCIRegisters->hcControlHeadED = HostToUSBLong ((UInt32) _pControlHead->pPhysical);
_pOHCIRegisters->hcBulkHeadED = HostToUSBLong ((UInt32) _pBulkHead->pPhysical);
IOSync();
OSWriteLittleInt32(&_pOHCIRegisters->hcHCCA, 0, _hccaPhysAddr);
IOSync();
_pOHCIRegisters->hcInterruptStatus = USBToHostLong(kOHCIHcInterrupt_WDH);
IOSync();
UInt32 hcFSMPS; UInt32 hcFI; UInt32 hcPS;
hcFI = USBToHostLong(_pOHCIRegisters->hcFmInterval) & kOHCIHcFmInterval_FI;
hcFSMPS = ((((hcFI-kOHCIMax_OverHead) * 6)/7) << kOHCIHcFmInterval_FSMPSPhase);
hcPS = (hcFI * 9) / 10; _pOHCIRegisters->hcFmInterval = HostToUSBLong(hcFI | hcFSMPS);
_pOHCIRegisters->hcPeriodicStart = HostToUSBLong(hcPS);
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue = 0, count = 0;
newValue = USBToHostLong(_pOHCIRegisters->hcFmInterval);
while ((count++ < 10) && (newValue != (hcFI | hcFSMPS)))
{
USBError(1, "OHCI driver: RestartControllerFromReset - hcFmInterval not sticking. Retrying.");
_pOHCIRegisters->hcFmInterval = HostToUSBLong(hcFI | hcFSMPS);
IOSync();
newValue = USBToHostLong(_pOHCIRegisters->hcFmInterval);
}
count = 0; newValue = USBToHostLong(_pOHCIRegisters->hcPeriodicStart);
while ((count++ < 10) && (newValue != hcPS))
{
USBError(1, "OHCI driver: RestartControllerFromReset - hcPeriodicStart not sticking. Retrying.");
_pOHCIRegisters->hcPeriodicStart = HostToUSBLong(hcPS);
IOSync();
newValue = USBToHostLong(_pOHCIRegisters->hcPeriodicStart);
}
}
if (_errataBits & kErrataDisableOvercurrent)
_pOHCIRegisters->hcRhDescriptorA |= HostToUSBLong(kOHCIHcRhDescriptorA_NOCP);
_pOHCIRegisters->hcRhStatus = HostToUSBLong(kOHCIHcRhStatus_OCIC | kOHCIHcRhStatus_DRWE);
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue = 0, 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 | 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();
return kIOReturnSuccess;
}
IOReturn
AppleUSBOHCI::WakeControllerFromDoze(void)
{
ResumeUSBBus();
_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);
}