aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorGabor Juhos <juhosg@openwrt.org>2011-04-13 04:54:23 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-04-13 19:58:18 -0400
commit2f7ac6c199978d0a0e407a12534201aa675a6482 (patch)
tree1423b565f0c7fdff0ddaed5c343bcc96e9953cd2 /drivers
parent502fa84195f47a79d7220470ebaa85a773659755 (diff)
USB: ehci: add workaround for Synopsys HC bug
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/host/ehci-ath79.c2
-rw-r--r--drivers/usb/host/ehci-q.c4
-rw-r--r--drivers/usb/host/ehci.h1
3 files changed, 7 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-ath79.c b/drivers/usb/host/ehci-ath79.c
index 74325b87bd77..7ea23b50f5d8 100644
--- a/drivers/usb/host/ehci-ath79.c
+++ b/drivers/usb/host/ehci-ath79.c
@@ -54,6 +54,8 @@ static int ehci_ath79_init(struct usb_hcd *hcd)
54 54
55 switch (id->driver_data) { 55 switch (id->driver_data) {
56 case EHCI_ATH79_IP_V1: 56 case EHCI_ATH79_IP_V1:
57 ehci->has_synopsys_hc_bug = 1;
58
57 ehci->caps = hcd->regs; 59 ehci->caps = hcd->regs;
58 ehci->regs = hcd->regs + 60 ehci->regs = hcd->regs +
59 HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); 61 HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 98ded66e8d3f..6582aeab6237 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1183,6 +1183,10 @@ static void end_unlink_async (struct ehci_hcd *ehci)
1183 ehci->reclaim = NULL; 1183 ehci->reclaim = NULL;
1184 start_unlink_async (ehci, next); 1184 start_unlink_async (ehci, next);
1185 } 1185 }
1186
1187 if (ehci->has_synopsys_hc_bug)
1188 ehci_writel(ehci, (u32) ehci->async->qh_dma,
1189 &ehci->regs->async_next);
1186} 1190}
1187 1191
1188/* makes sure the async qh will become idle */ 1192/* makes sure the async qh will become idle */
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 333ddc156919..168f1a88c4d0 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -134,6 +134,7 @@ struct ehci_hcd { /* one per controller */
134 unsigned amd_pll_fix:1; 134 unsigned amd_pll_fix:1;
135 unsigned fs_i_thresh:1; /* Intel iso scheduling */ 135 unsigned fs_i_thresh:1; /* Intel iso scheduling */
136 unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/ 136 unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
137 unsigned has_synopsys_hc_bug:1; /* Synopsys HC */
137 138
138 /* required for usb32 quirk */ 139 /* required for usb32 quirk */
139 #define OHCI_CTRL_HCFS (3 << 6) 140 #define OHCI_CTRL_HCFS (3 << 6)