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

Reply via email to