Commit 6402c796 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: EHCI: work around silicon bug in Intel's EHCI controllers

This patch (as1660) works around a hardware problem present in some
(if not all) Intel EHCI controllers.  After a QH has been unlinked
from the async schedule and the corresponding IAA interrupt has
occurred, the controller is not supposed access the QH and its qTDs.
There certainly shouldn't be any more DMA writes to those structures.
Nevertheless, Intel's controllers have been observed to perform a
final writeback to the QH's overlay region and to the most recent qTD.
For more information and a test program to determine whether this
problem is present in a particular controller, see

	http://marc.info/?l=linux-usb&m=135492071812265&w=2
	http://marc.info/?l=linux-usb&m=136182570800963&w=2

This patch works around the problem by always waiting for two IAA
cycles when unlinking an async QH.  The extra IAA delay gives the
controller time to perform its final writeback.

Surprisingly enough, the effects of this silicon bug have gone
undetected until quite recently.  More through luck than anything
else, it hasn't caused any apparent problems.  However, it does
interact badly with the path that follows this one, so it needs to be
addressed.

This is the first part of a fix for the regression reported at:

	https://bugs.launchpad.net/bugs/1088733Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Tested-by: default avatarStephen Thirlwall <sdt@dr.com>
CC: <stable@vger.kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ea5301aa
...@@ -748,11 +748,9 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) ...@@ -748,11 +748,9 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
/* guard against (alleged) silicon errata */ /* guard against (alleged) silicon errata */
if (cmd & CMD_IAAD) if (cmd & CMD_IAAD)
ehci_dbg(ehci, "IAA with IAAD still set?\n"); ehci_dbg(ehci, "IAA with IAAD still set?\n");
if (ehci->async_iaa) { if (ehci->async_iaa)
COUNT(ehci->stats.iaa); COUNT(ehci->stats.iaa);
end_unlink_async(ehci); end_unlink_async(ehci);
} else
ehci_dbg(ehci, "IAA with nothing unlinked?\n");
} }
/* remote wakeup [4.3.1] */ /* remote wakeup [4.3.1] */
......
...@@ -1170,7 +1170,7 @@ static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -1170,7 +1170,7 @@ static void single_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
struct ehci_qh *prev; struct ehci_qh *prev;
/* Add to the end of the list of QHs waiting for the next IAAD */ /* Add to the end of the list of QHs waiting for the next IAAD */
qh->qh_state = QH_STATE_UNLINK; qh->qh_state = QH_STATE_UNLINK_WAIT;
if (ehci->async_unlink) if (ehci->async_unlink)
ehci->async_unlink_last->unlink_next = qh; ehci->async_unlink_last->unlink_next = qh;
else else
...@@ -1213,9 +1213,19 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) ...@@ -1213,9 +1213,19 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested)
/* Do only the first waiting QH (nVidia bug?) */ /* Do only the first waiting QH (nVidia bug?) */
qh = ehci->async_unlink; qh = ehci->async_unlink;
ehci->async_iaa = qh;
ehci->async_unlink = qh->unlink_next; /*
qh->unlink_next = NULL; * Intel (?) bug: The HC can write back the overlay region
* even after the IAA interrupt occurs. In self-defense,
* always go through two IAA cycles for each QH.
*/
if (qh->qh_state == QH_STATE_UNLINK_WAIT) {
qh->qh_state = QH_STATE_UNLINK;
} else {
ehci->async_iaa = qh;
ehci->async_unlink = qh->unlink_next;
qh->unlink_next = NULL;
}
/* Make sure the unlinks are all visible to the hardware */ /* Make sure the unlinks are all visible to the hardware */
wmb(); wmb();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment