aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ohci-hub.c
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2008-02-02 05:42:52 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2008-04-25 00:16:34 -0400
commite01e7fe3886715f083313da409c5850472455d06 (patch)
tree1e7804fe4f5fe563335ee270218062d406128f42 /drivers/usb/host/ohci-hub.c
parent9776afc8b3dc487557f3f576002520f59be334e6 (diff)
USB: ohci: port reset paranoia timeout
This limits how long the OHCI port reset loop waits for the hardware to do its job, if the controller either (a) dies, or (b) can't finish the reset. Such limits are always a good idea. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/ohci-hub.c')
-rw-r--r--drivers/usb/host/ohci-hub.c24
1 files changed, 21 insertions, 3 deletions
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 48e4b11f4d3e..c638e6b33c43 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -564,14 +564,18 @@ static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port)
564 u32 temp; 564 u32 temp;
565 u16 now = ohci_readl(ohci, &ohci->regs->fmnumber); 565 u16 now = ohci_readl(ohci, &ohci->regs->fmnumber);
566 u16 reset_done = now + PORT_RESET_MSEC; 566 u16 reset_done = now + PORT_RESET_MSEC;
567 int limit_1 = DIV_ROUND_UP(PORT_RESET_MSEC, PORT_RESET_HW_MSEC);
567 568
568 /* build a "continuous enough" reset signal, with up to 569 /* build a "continuous enough" reset signal, with up to
569 * 3msec gap between pulses. scheduler HZ==100 must work; 570 * 3msec gap between pulses. scheduler HZ==100 must work;
570 * this might need to be deadline-scheduled. 571 * this might need to be deadline-scheduled.
571 */ 572 */
572 do { 573 do {
574 int limit_2;
575
573 /* spin until any current reset finishes */ 576 /* spin until any current reset finishes */
574 for (;;) { 577 limit_2 = PORT_RESET_HW_MSEC * 2;
578 while (--limit_2 >= 0) {
575 temp = ohci_readl (ohci, portstat); 579 temp = ohci_readl (ohci, portstat);
576 /* handle e.g. CardBus eject */ 580 /* handle e.g. CardBus eject */
577 if (temp == ~(u32)0) 581 if (temp == ~(u32)0)
@@ -581,6 +585,17 @@ static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port)
581 udelay (500); 585 udelay (500);
582 } 586 }
583 587
588 /* timeout (a hardware error) has been observed when
589 * EHCI sets CF while this driver is resetting a port;
590 * presumably other disconnect paths might do it too.
591 */
592 if (limit_2 < 0) {
593 ohci_dbg(ohci,
594 "port[%d] reset timeout, stat %08x\n",
595 port, temp);
596 break;
597 }
598
584 if (!(temp & RH_PS_CCS)) 599 if (!(temp & RH_PS_CCS))
585 break; 600 break;
586 if (temp & RH_PS_PRSC) 601 if (temp & RH_PS_PRSC)
@@ -590,8 +605,11 @@ static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port)
590 ohci_writel (ohci, RH_PS_PRS, portstat); 605 ohci_writel (ohci, RH_PS_PRS, portstat);
591 msleep(PORT_RESET_HW_MSEC); 606 msleep(PORT_RESET_HW_MSEC);
592 now = ohci_readl(ohci, &ohci->regs->fmnumber); 607 now = ohci_readl(ohci, &ohci->regs->fmnumber);
593 } while (tick_before(now, reset_done)); 608 } while (tick_before(now, reset_done) && --limit_1 >= 0);
594 /* caller synchronizes using PRSC */ 609
610 /* caller synchronizes using PRSC ... and handles PRS
611 * still being set when this returns.
612 */
595 613
596 return 0; 614 return 0;
597} 615}