I've been having some problems with the EHCI driver on my Thinkpad T40, running 2.6.9 with APM instead of ACPI. On resume after suspend-to-RAM the EHCI hardware keeps the interrupt line high, but the EHCI driver is not clearing the interrupt status register, causing Linux to eventually disable the interrupt altogether.
In particular, the following seems to be the cause: * When suspending, ehci_hub_suspend() sets hcd->state to USB_STATE_HALT. * On resume, ehci_hub_resume() first enables interrupts, and only much later does it set hcd->state to USB_STATE_RUNNING. * The interrupt handler wrapper usb_hcd_irq() will not call ehci_irq() if it's USB_STATE_HALT. * As a result, as soon as interrupts are enabled, the EHCI hardware can start delivering interrupts, and livelocks the system, not allowing ehci_hub_resume() to proceed and set state to USB_STATE_RUNNING. The attached patch for ehci-hub.c does the obvious thing: delay enabling interrupts until hcd->state is set to USB_STATE_RUNNING. The other attached patch for ehci-hcd.c adds STS_FLR to the set of interrupt status flags that we'll clear, just in case -- otherwise the driver might end up in a similar interrupt livelock scenario. -- kolya --- drivers/usb/host/ehci-hub.c 2004/11/03 04:57:16 1.1 +++ drivers/usb/host/ehci-hub.c 2004/11/03 05:04:04 @@ -88,6 +88,7 @@ struct usb_device *root = hcd_to_bus (&ehci->hcd)->root_hub; u32 temp; int i; + int intr_enable; if (!root->dev.power.power_state) return 0; @@ -95,8 +96,15 @@ return -EAGAIN; /* re-init operational registers in case we lost power */ + intr_enable = 0; if (readl (&ehci->regs->intr_enable) == 0) { - writel (INTR_MASK, &ehci->regs->intr_enable); + /* + * Delay actual enabling of interrupts until we set + * hcd->state to USB_STATE_RUNNING, otherwise usb_hcd_irq + * will not deliver interrupts to ehci_irq. + */ + intr_enable = 1; + writel (0, &ehci->regs->intr_enable); writel (0, &ehci->regs->segment); writel (ehci->periodic_dma, &ehci->regs->frame_list); writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next); @@ -141,6 +149,14 @@ root->dev.power.power_state = 0; ehci->next_statechange = jiffies + msecs_to_jiffies(5); ehci->hcd.state = USB_STATE_RUNNING; + + /* + * Now that state is USB_STATE_RUNNING, we can safely + * enable interrupts. + */ + if (intr_enable) + writel (INTR_MASK, &ehci->regs->intr_enable); + return 0; } --- drivers/usb/host/ehci-hcd.c 2004/11/03 02:27:02 1.1 +++ drivers/usb/host/ehci-hcd.c 2004/11/03 03:16:26 @@ -129,7 +129,7 @@ module_param (log2_irq_thresh, int, S_IRUGO); MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); -#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) +#define INTR_MASK (STS_IAA | STS_FATAL | STS_FLR | STS_PCD | STS_ERR | STS_INT) /*-------------------------------------------------------------------------*/ ------------------------------------------------------- This SF.Net email is sponsored by: Sybase ASE Linux Express Edition - download now for FREE LinuxWorld Reader's Choice Award Winner for best database on Linux. http://ads.osdn.com/?ad_id=5588&alloc_id=12065&op=click _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel