diff options
-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 @@ | |||
72 | static const char hcd_name [] = "ohci_hcd"; | 72 | static 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; |