aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2014-07-18 16:26:17 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-07-18 19:34:29 -0400
commit499b3803d3e2f062f73bf22372b38393369ffcbf (patch)
tree4f33ecaf596af157df8a4cdccbf1fd2ea5f6e3f0 /drivers/usb/host
parent81e38333513cec155c720432226dabe9f9f76a77 (diff)
USB: OHCI: add check for stopped frame counter
This patch adds an extra check to ohci-hcd's I/O watchdog routine. If the controller stops updating the frame counter, we will assume it is dead. But there has to be an exception: Some controllers stop the frame counter when no ports are connected. Check to make sure there is at least one active port before deciding the controller is dead. (This test may appear racy, but it isn't. Enabling a newly connected port takes several milliseconds, during which time the frame counter must advance.) Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Tested-by: Dennis New <dennisn@dennisn.linuxd.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/ohci-hcd.c36
-rw-r--r--drivers/usb/host/ohci.h1
2 files changed, 35 insertions, 2 deletions
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index aba8f19eae4d..46987735a2e3 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -72,7 +72,7 @@
72static const char hcd_name [] = "ohci_hcd"; 72static const char hcd_name [] = "ohci_hcd";
73 73
74#define STATECHANGE_DELAY msecs_to_jiffies(300) 74#define STATECHANGE_DELAY msecs_to_jiffies(300)
75#define IO_WATCHDOG_DELAY msecs_to_jiffies(250) 75#define IO_WATCHDOG_DELAY msecs_to_jiffies(250)
76 76
77#include "ohci.h" 77#include "ohci.h"
78#include "pci-quirks.h" 78#include "pci-quirks.h"
@@ -230,9 +230,11 @@ static int ohci_urb_enqueue (
230 230
231 /* Start up the I/O watchdog timer, if it's not running */ 231 /* Start up the I/O watchdog timer, if it's not running */
232 if (!timer_pending(&ohci->io_watchdog) && 232 if (!timer_pending(&ohci->io_watchdog) &&
233 list_empty(&ohci->eds_in_use)) 233 list_empty(&ohci->eds_in_use)) {
234 ohci->prev_frame_no = ohci_frame_no(ohci);
234 mod_timer(&ohci->io_watchdog, 235 mod_timer(&ohci->io_watchdog,
235 jiffies + IO_WATCHDOG_DELAY); 236 jiffies + IO_WATCHDOG_DELAY);
237 }
236 list_add(&ed->in_use_list, &ohci->eds_in_use); 238 list_add(&ed->in_use_list, &ohci->eds_in_use);
237 239
238 if (ed->type == PIPE_ISOCHRONOUS) { 240 if (ed->type == PIPE_ISOCHRONOUS) {
@@ -727,6 +729,7 @@ static void io_watchdog_func(unsigned long _ohci)
727 u32 head; 729 u32 head;
728 struct ed *ed; 730 struct ed *ed;
729 struct td *td, *td_start, *td_next; 731 struct td *td, *td_start, *td_next;
732 unsigned frame_no;
730 unsigned long flags; 733 unsigned long flags;
731 734
732 spin_lock_irqsave(&ohci->lock, flags); 735 spin_lock_irqsave(&ohci->lock, flags);
@@ -742,6 +745,7 @@ static void io_watchdog_func(unsigned long _ohci)
742 if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) { 745 if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) {
743 if (ohci->prev_donehead) { 746 if (ohci->prev_donehead) {
744 ohci_err(ohci, "HcDoneHead not written back; disabled\n"); 747 ohci_err(ohci, "HcDoneHead not written back; disabled\n");
748 died:
745 usb_hc_died(ohci_to_hcd(ohci)); 749 usb_hc_died(ohci_to_hcd(ohci));
746 ohci_dump(ohci); 750 ohci_dump(ohci);
747 ohci_shutdown(ohci_to_hcd(ohci)); 751 ohci_shutdown(ohci_to_hcd(ohci));
@@ -802,7 +806,35 @@ static void io_watchdog_func(unsigned long _ohci)
802 ohci_work(ohci); 806 ohci_work(ohci);
803 807
804 if (ohci->rh_state == OHCI_RH_RUNNING) { 808 if (ohci->rh_state == OHCI_RH_RUNNING) {
809
810 /*
811 * Sometimes a controller just stops working. We can tell
812 * by checking that the frame counter has advanced since
813 * the last time we ran.
814 *
815 * But be careful: Some controllers violate the spec by
816 * stopping their frame counter when no ports are active.
817 */
818 frame_no = ohci_frame_no(ohci);
819 if (frame_no == ohci->prev_frame_no) {
820 int active_cnt = 0;
821 int i;
822 unsigned tmp;
823
824 for (i = 0; i < ohci->num_ports; ++i) {
825 tmp = roothub_portstatus(ohci, i);
826 /* Enabled and not suspended? */
827 if ((tmp & RH_PS_PES) && !(tmp & RH_PS_PSS))
828 ++active_cnt;
829 }
830
831 if (active_cnt > 0) {
832 ohci_err(ohci, "frame counter not updating; disabled\n");
833 goto died;
834 }
835 }
805 if (!list_empty(&ohci->eds_in_use)) { 836 if (!list_empty(&ohci->eds_in_use)) {
837 ohci->prev_frame_no = frame_no;
806 ohci->prev_wdh_cnt = ohci->wdh_cnt; 838 ohci->prev_wdh_cnt = ohci->wdh_cnt;
807 ohci->prev_donehead = ohci_readl(ohci, 839 ohci->prev_donehead = ohci_readl(ohci,
808 &ohci->regs->donehead); 840 &ohci->regs->donehead);
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index 0548f5ca18e2..59f424567a8d 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -421,6 +421,7 @@ struct ohci_hcd {
421 421
422 // there are also chip quirks/bugs in init logic 422 // there are also chip quirks/bugs in init logic
423 423
424 unsigned prev_frame_no;
424 unsigned wdh_cnt, prev_wdh_cnt; 425 unsigned wdh_cnt, prev_wdh_cnt;
425 u32 prev_donehead; 426 u32 prev_donehead;
426 struct timer_list io_watchdog; 427 struct timer_list io_watchdog;