From 221f8dfca89276d8aec54c6d07fbe20c281668f0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 26 Feb 2013 13:43:41 -0500 Subject: [PATCH] USB: EHCI: revert "remove ASS/PSS polling timeout" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch (as1649) reverts commit 55bcdce8a8228223ec4d17d8ded8134ed265d2c5 (USB: EHCI: remove ASS/PSS polling timeout). That commit was written under the assumption that some controllers may take a very long time to turn off their async and periodic schedules. It now appears that in fact the schedules do get turned off reasonably quickly, but some controllers occasionally leave the schedules' status bits turned on and consequently ehci-hcd can't tell that the schedules are off. VIA controllers in particular have this problem. ehci-hcd tells the hardware to turn off the async schedule, the schedule does get turned off, but the status bit remains on. Since the EHCI spec requires that the schedules not be re-enabled until the previous disable has taken effect, with an unlimited timeout the async schedule never gets turned back on. The resulting symptom is that the system is unable to communicate with USB devices. Signed-off-by: Alan Stern Reported-and-tested-by: Ronald Reported-and-tested-by: Paul Hartman Reported-and-tested-by: Dieter Nützel Reported-and-tested-by: Jean Delvare Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-timer.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index f904071d70df..20dbdcbe9b0f 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -113,15 +113,14 @@ static void ehci_poll_ASS(struct ehci_hcd *ehci) if (want != actual) { - /* Poll again later */ - ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true); - ++ehci->ASS_poll_count; - return; + /* Poll again later, but give up after about 20 ms */ + if (ehci->ASS_poll_count++ < 20) { + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true); + return; + } + ehci_dbg(ehci, "Waited too long for the async schedule status (%x/%x), giving up\n", + want, actual); } - - if (ehci->ASS_poll_count > 20) - ehci_dbg(ehci, "ASS poll count reached %d\n", - ehci->ASS_poll_count); ehci->ASS_poll_count = 0; /* The status is up-to-date; restart or stop the schedule as needed */ @@ -160,14 +159,14 @@ static void ehci_poll_PSS(struct ehci_hcd *ehci) if (want != actual) { - /* Poll again later */ - ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); - return; + /* Poll again later, but give up after about 20 ms */ + if (ehci->PSS_poll_count++ < 20) { + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); + return; + } + ehci_dbg(ehci, "Waited too long for the periodic schedule status (%x/%x), giving up\n", + want, actual); } - - if (ehci->PSS_poll_count > 20) - ehci_dbg(ehci, "PSS poll count reached %d\n", - ehci->PSS_poll_count); ehci->PSS_poll_count = 0; /* The status is up-to-date; restart or stop the schedule as needed */