diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 36 | ||||
-rw-r--r-- | drivers/usb/host/ohci.h | 1 |
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 @@ static const char hcd_name [] = "ohci_hcd"; #define STATECHANGE_DELAY msecs_to_jiffies(300) -#define IO_WATCHDOG_DELAY msecs_to_jiffies(250) +#define IO_WATCHDOG_DELAY msecs_to_jiffies(250) #include "ohci.h" #include "pci-quirks.h" @@ -230,9 +230,11 @@ static int ohci_urb_enqueue ( /* Start up the I/O watchdog timer, if it's not running */ if (!timer_pending(&ohci->io_watchdog) && - list_empty(&ohci->eds_in_use)) + list_empty(&ohci->eds_in_use)) { + ohci->prev_frame_no = ohci_frame_no(ohci); mod_timer(&ohci->io_watchdog, jiffies + IO_WATCHDOG_DELAY); + } list_add(&ed->in_use_list, &ohci->eds_in_use); if (ed->type == PIPE_ISOCHRONOUS) { @@ -727,6 +729,7 @@ static void io_watchdog_func(unsigned long _ohci) u32 head; struct ed *ed; struct td *td, *td_start, *td_next; + unsigned frame_no; unsigned long flags; spin_lock_irqsave(&ohci->lock, flags); @@ -742,6 +745,7 @@ static void io_watchdog_func(unsigned long _ohci) if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) { if (ohci->prev_donehead) { ohci_err(ohci, "HcDoneHead not written back; disabled\n"); + died: usb_hc_died(ohci_to_hcd(ohci)); ohci_dump(ohci); ohci_shutdown(ohci_to_hcd(ohci)); @@ -802,7 +806,35 @@ static void io_watchdog_func(unsigned long _ohci) ohci_work(ohci); if (ohci->rh_state == OHCI_RH_RUNNING) { + + /* + * Sometimes a controller just stops working. We can tell + * by checking that the frame counter has advanced since + * the last time we ran. + * + * But be careful: Some controllers violate the spec by + * stopping their frame counter when no ports are active. + */ + frame_no = ohci_frame_no(ohci); + if (frame_no == ohci->prev_frame_no) { + int active_cnt = 0; + int i; + unsigned tmp; + + for (i = 0; i < ohci->num_ports; ++i) { + tmp = roothub_portstatus(ohci, i); + /* Enabled and not suspended? */ + if ((tmp & RH_PS_PES) && !(tmp & RH_PS_PSS)) + ++active_cnt; + } + + if (active_cnt > 0) { + ohci_err(ohci, "frame counter not updating; disabled\n"); + goto died; + } + } if (!list_empty(&ohci->eds_in_use)) { + ohci->prev_frame_no = frame_no; ohci->prev_wdh_cnt = ohci->wdh_cnt; ohci->prev_donehead = ohci_readl(ohci, &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 { // there are also chip quirks/bugs in init logic + unsigned prev_frame_no; unsigned wdh_cnt, prev_wdh_cnt; u32 prev_donehead; struct timer_list io_watchdog; |