USB: ehci: add workaround for Synopsys HC bug
authorGabor Juhos <juhosg@openwrt.org>
Wed, 13 Apr 2011 08:54:23 +0000 (10:54 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 13 Apr 2011 23:58:18 +0000 (16:58 -0700)
A Synopsys USB core used in various SoCs has a bug which might cause
that the host controller not issuing ping.

When software uses the Doorbell mechanism to remove queue heads, the
host controller still has references to the removed queue head even
after indicating an Interrupt on Async Advance. This happens if the last
executed queue head's Next Link queue head is removed.

Consequences of the defect:
The Host controller fetches the removed queue head, using memory that
would otherwise be deallocated.This results in incorrect transactions on
both the USB and system memory. This may result in undefined behavior.

Workarounds:

1) If no queue head is active (no Status field's Active bit is set)
after removing the queue heads, the software can write one of the valid
queue head addresses to the ASYNCLISTADDR register and deallocate the
removed queue head's memory after 2 microframes.

If one or more of the queue heads is active (the Active bit is set in
the Status field) after removing the queue heads, the software can delay
memory deallocation after time X, where X is the time required for the
Host Controller to go through all the queue heads once. X varies with
the number of queue heads and the time required to process periodic
transactions: if more periodic transactions must be performed, the Host
Controller has less time to process asynchronous transaction processing.

2) Do not use the Doorbell mechanism to remove the queue heads. Disable
the Asynchronous Schedule Enable bit instead.

The bug has been discussed on the linux-usb-devel mailing-list
four years ago, the original thread can be found here:
http://www.mail-archive.com/linux-usb-devel@lists.sourceforge.net/msg45345.html

This patch implements the first workaround as suggested by David Brownell.

The built-in USB host controller of the Atheros AR7130/AR7141/AR7161 SoCs
requires this to work properly.

Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/ehci-ath79.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci.h

index 74325b87bd77cbee24f57b42de64d3c78a1066ec..7ea23b50f5d8f2a75a06d5bada68baac1449945c 100644 (file)
@@ -54,6 +54,8 @@ static int ehci_ath79_init(struct usb_hcd *hcd)
 
        switch (id->driver_data) {
        case EHCI_ATH79_IP_V1:
+               ehci->has_synopsys_hc_bug = 1;
+
                ehci->caps = hcd->regs;
                ehci->regs = hcd->regs +
                        HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
index 98ded66e8d3fc5ccfbe4bfd9170c51e99cf3b752..6582aeab623797e019f84aa95c44b77a84d2d881 100644 (file)
@@ -1183,6 +1183,10 @@ static void end_unlink_async (struct ehci_hcd *ehci)
                ehci->reclaim = NULL;
                start_unlink_async (ehci, next);
        }
+
+       if (ehci->has_synopsys_hc_bug)
+               ehci_writel(ehci, (u32) ehci->async->qh_dma,
+                           &ehci->regs->async_next);
 }
 
 /* makes sure the async qh will become idle */
index 333ddc1569198631879c6ad22696361e322f38fe..168f1a88c4d0ec3d8245ba620af05800e638f364 100644 (file)
@@ -134,6 +134,7 @@ struct ehci_hcd {                   /* one per controller */
        unsigned                amd_pll_fix:1;
        unsigned                fs_i_thresh:1;  /* Intel iso scheduling */
        unsigned                use_dummy_qh:1; /* AMD Frame List table quirk*/
+       unsigned                has_synopsys_hc_bug:1; /* Synopsys HC */
 
        /* required for usb32 quirk */
        #define OHCI_CTRL_HCFS          (3 << 6)