//================================================================================================ // // EnableInterruptsFromController // //================================================================================================ // 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; }
//================================================================================================ // // SuspendController // //================================================================================================ // void AppleUSBUHCI::SuspendController(void) { UInt16 cmd, value; int i; USBTrace( kUSBTUHCI, kTPUHCISuspendController, (uintptr_t)this, 0, 0, 1 ); USBLog(5, "AppleUSBUHCI[%p]::SuspendController", this); USBLog(5, "AppleUSBUHCI[%p]: cmd state %x, status %x", this, ioRead16(kUHCI_CMD), ioRead16(kUHCI_STS)); // Stop the controller 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); } // only do this for controllers with overcurrent additions. if ((_ERRATA64BITS & kErrataUHCISupportsOvercurrent) && (value & kUHCI_PORTSC_OCI)) // Is the latched Overcurrent set? { // if so, clear it or we won't suspend. 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); // clear overcurrent indicator } } // Put the controller in Global Suspend cmd = ioRead16(kUHCI_CMD) & ~kUHCI_CMD_FGR; cmd |= kUHCI_CMD_EGSM; ioWrite16(kUHCI_CMD, cmd); _myBusState = kUSBBusStateSuspended; IOSleep(3); USBLog(5, "AppleUSBUHCI[%p]: suspend done, cmd %x, status %x", this, ioRead16(kUHCI_CMD), ioRead16(kUHCI_STS)); }
//================================================================================================ // // DozeController // //================================================================================================ // IOReturn AppleUSBUHCI::DozeController(void) { UInt16 cmd; int i; bool portsBeingResumed = false; USBTrace( kUSBTUHCI, KTPUHCIDozeController, (uintptr_t)this, 0, 0, 0); USBLog(6, "AppleUSBUHCI[%p]::DozeController", this); for (i=0; i<kUHCI_NUM_PORTS; i++) { if (_rhPortBeingResumed[i]) { USBLog(1, "AppleUSBUHCI[%p]::DozeController - port (%d) is being resumed. not stopping the controller", this, i+1); portsBeingResumed = true; } } if (!portsBeingResumed) { showRegisters(7, "+DozeController - stopping controller"); Run(false); // In order to get a Resume Detected interrupt, the controller needs to be in Global suspend mode, so we will do that even when "dozing". USBLog(6, "AppleUSBUHCI[%p]::DozeController Globally suspending", this); // Put the controller in Global Suspend cmd = ioRead16(kUHCI_CMD) & ~kUHCI_CMD_FGR; cmd |= kUHCI_CMD_EGSM; ioWrite16(kUHCI_CMD, cmd); _myBusState = kUSBBusStateSuspended; IOSleep(3); } return kIOReturnSuccess; }
//================================================================================================ // // WakeControllerFromDoze // //================================================================================================ // IOReturn AppleUSBUHCI::WakeControllerFromDoze(void) { UInt16 cmd; int i; bool portHasRD[kUHCI_NUM_PORTS]; UInt16 status; USBTrace( kUSBTUHCI, KTPUHCIWakeFromDoze, (uintptr_t)this, 0, 0, 0); // First, see if we have any ports that have the RD bit set. If they do, then we can go ahead and clear it after we waited the 20ms for the // Global resume 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; } } // If we are in Global Suspend mode, we need to resume the controller. We will wait 20ms with the gate held. However, since we only // get into this mode if all devices are suspended, then delaying while holding the wl will not prevent any completions from happening, since // there aren't any. 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); // Clear any RD bits in the port if they were set, now that we have waited 20ms 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); // allow it to kick in } } } USBLog(6, "AppleUSBUHCI[%p]::WakeControllerFromDoze calling Run(true)", this); Run(true); _myBusState = kUSBBusStateRunning; showRegisters(7, "-WakeControllerFromDoze"); return kIOReturnSuccess; }
//================================================================================================ // // ResumeController // //================================================================================================ // 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; } // I need to save the existing frame list before I turn on processing so I can send SOF only for 10ms after we turn the controller on 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) { // this could either be because the remote wwakeup caused this state or because we did above // need to wait 20ms 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); } // restore the frame list register 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); // wait 10 ms for the device to recover IOSleep(10); // restore the list 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"); }
// Called at hardware interrupt time bool AppleUSBUHCI::FilterInterrupt(void) { UInt16 activeInterrupts; Boolean needSignal = false; UInt64 currentFrame; uint64_t timeStamp; // we leave all interrupts enabled, so see which ones are active activeInterrupts = ioRead16(kUHCI_STS) & kUHCI_STS_INTR_MASK; if (activeInterrupts != 0) { USBTrace( kUSBTUHCIInterrupts, kTPUHCIInterruptsFilterInterrupt , (uintptr_t)this, activeInterrupts, 0, 3 ); if (activeInterrupts & kUHCI_STS_HCPE) { // Host Controller Process Error - usually a bad data structure on the list _hostControllerProcessInterrupt = kUHCI_STS_HCPE; ioWrite16(kUHCI_STS, kUHCI_STS_HCPE); needSignal = true; //USBLog(1, "AppleUSBUHCI[%p]::FilterInterrupt - HCPE error - legacy reg = %p", this, (void*)_device->configRead16(kUHCI_PCI_LEGKEY)); USBTrace( kUSBTUHCIInterrupts, kTPUHCIInterruptsFilterInterrupt, (uintptr_t)this, _hostControllerProcessInterrupt, _device->configRead16(kUHCI_PCI_LEGKEY), 1 ); } if (activeInterrupts & kUHCI_STS_HSE) { // Host System Error - usually a PCI issue _hostSystemErrorInterrupt = kUHCI_STS_HSE; ioWrite16(kUHCI_STS, kUHCI_STS_HSE); needSignal = true; //USBLog(1, "AppleUSBUHCI[%p]::FilterInterrupt - HSE error - legacy reg = %p", this, (void*)_device->configRead16(kUHCI_PCI_LEGKEY)); USBTrace( kUSBTUHCIInterrupts, kTPUHCIInterruptsFilterInterrupt, (uintptr_t)this, _hostSystemErrorInterrupt, _device->configRead16(kUHCI_PCI_LEGKEY), 2 ); } if (activeInterrupts & kUHCI_STS_RD) { // Resume Detect - remote wakeup _resumeDetectInterrupt = kUHCI_STS_RD; ioWrite16(kUHCI_STS, kUHCI_STS_RD); needSignal = true; } if (activeInterrupts & kUHCI_STS_EI) { // USB Error Interrupt - transaction error (CRC, timeout, etc) _usbErrorInterrupt = kUHCI_STS_EI; ioWrite16(kUHCI_STS, kUHCI_STS_EI); needSignal = true; } if (activeInterrupts & kUHCI_STS_INT) { // Normal IOC interrupt - we need to check out low latency Isoch as well timeStamp = mach_absolute_time(); ioWrite16(kUHCI_STS, kUHCI_STS_INT); needSignal = true; // This function will give us the current frame number and check for rollover at the same time // since we are calling from filterInterrupts we will not be preempted, it will also update the // cached copy of the frame_number_with_time GetFrameNumberInternal(); // we need to check the periodic list to see if there are any Isoch TDs which need to come off // and potentially have their frame lists updated (for Low Latency) we will place them in reverse // order on a "done queue" which will be looked at by the isoch scavanger // only do this if the periodic schedule is enabled if (!_inAbortIsochEP && (_outSlot < kUHCI_NVFRAMES)) { AppleUHCIIsochTransferDescriptor *cachedHead; UInt32 cachedProducer; UInt16 curSlot, testSlot, nextSlot; curSlot = (ReadFrameNumber() & kUHCI_NVFRAMES_MASK); cachedHead = (AppleUHCIIsochTransferDescriptor*)_savedDoneQueueHead; cachedProducer = _producerCount; testSlot = _outSlot; while (testSlot != curSlot) { IOUSBControllerListElement *thing, *nextThing; AppleUHCIIsochTransferDescriptor *isochTD; nextSlot = (testSlot+1) & kUHCI_NVFRAMES_MASK; thing = _logicalFrameList[testSlot]; while (thing != NULL) { nextThing = thing->_logicalNext; isochTD = OSDynamicCast(AppleUHCIIsochTransferDescriptor, thing); if (!isochTD) break; // only care about Isoch in this list - if we get here we are at the interrupt TDs // need to unlink this TD _logicalFrameList[testSlot] = nextThing; _frameList[testSlot] = HostToUSBLong(thing->GetPhysicalLink()); if (isochTD->_lowLatency) isochTD->frStatus = isochTD->UpdateFrameList(*(AbsoluteTime*)&timeStamp); // place this guy on the backward done queue // the reason that we do not use the _logicalNext link is that the done queue is not a null terminated list // and the element linked "last" in the list might not be a true link - trust me isochTD->_doneQueueLink = cachedHead; cachedHead = isochTD; cachedProducer++; if (isochTD->_pEndpoint) { isochTD->_pEndpoint->onProducerQ++; OSDecrementAtomic( &(isochTD->_pEndpoint->scheduledTDs)); } thing = nextThing; } testSlot = nextSlot; _outSlot = testSlot; } IOSimpleLockLock( _wdhLock ); _savedDoneQueueHead = cachedHead; // updates the shadow head _producerCount = cachedProducer; // Validates _producerCount; IOSimpleLockUnlock( _wdhLock ); } // 8394970: Make sure we set the flag AFTER we have incremented our producer count. _usbCompletionInterrupt = kUHCI_STS_INT; } } // We will return false from this filter routine, // but will indicate that there the action routine should be called // by calling _filterInterruptSource->signalInterrupt(). // This is needed because IOKit will disable interrupts for a level interrupt // after the filter interrupt is run, until the action interrupt is called. // We want to be able to have our filter interrupt routine called // before the action routine runs, if needed. That is what will enable // low latency isoch transfers to work, as when the // system is under heavy load, the action routine can be delayed for tens of ms. // if (needSignal) _interruptSource->signalInterrupt(); return false; }