diff options
Diffstat (limited to 'drivers/usb/host/ohci-hub.c')
-rw-r--r-- | drivers/usb/host/ohci-hub.c | 24 |
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 | } |