Re: [patch 2.6.23] OHCI port reset loop limit
David Brownell <[email protected]>
| Newsgroups | gmane.linux.usb.devel |
|---|---|
| Message-ID | <[email protected]> |
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. In this case there are system startup paths which, on at least the NEC implementation, can prevent finishing that reset. It happens when a system loads EHCI after OHCI, so EHCI takes a port away from OHCI ... if it takes away a port which OHCI is resetting. (Note we have *ALWAYS* recommended EHCI be loaded first.) Problem reported by Dave Miller; this patch is based on one he provided. Signed-off-by: David Brownell <[email protected]> --- This is a resend of what I sent on 9-October, with two minor tweaks. The "limit_1" value is now sane, and the comment points out that a reset may still be happening when this returns. drivers/usb/host/ohci-hub.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) --- g26.orig/drivers/usb/host/ohci-hub.c 2007-11-06 21:52:51.000000000 -0800 +++ g26/drivers/usb/host/ohci-hub.c 2007-11-07 13:51:59.000000000 -0800 @@ -564,14 +564,18 @@ static inline int root_port_reset (struc u32 temp; u16 now = ohci_readl(ohci, &ohci->regs->fmnumber); u16 reset_done = now + PORT_RESET_MSEC; + int limit_1 = DIV_ROUND_UP(PORT_RESET_MSEC, PORT_RESET_HW_MSEC); /* build a "continuous enough" reset signal, with up to * 3msec gap between pulses. scheduler HZ==100 must work; * this might need to be deadline-scheduled. */ do { + int limit_2; + /* spin until any current reset finishes */ - for (;;) { + limit_2 = PORT_RESET_HW_MSEC * 2; + while (--limit_2 >= 0) { temp = ohci_readl (ohci, portstat); /* handle e.g. CardBus eject */ if (temp == ~(u32)0) @@ -581,6 +585,17 @@ static inline int root_port_reset (struc udelay (500); } + /* timeout (a hardware error) has been observed when + * EHCI sets CF while this driver is resetting a port; + * presumably other disconnect paths might do it too. + */ + if (limit_2 < 0) { + ohci_dbg(ohci, + "port[%d] reset timeout, stat %08x\n", + port, temp); + break; + } + if (!(temp & RH_PS_CCS)) break; if (temp & RH_PS_PRSC) @@ -590,8 +605,11 @@ static inline int root_port_reset (struc ohci_writel (ohci, RH_PS_PRS, portstat); msleep(PORT_RESET_HW_MSEC); now = ohci_readl(ohci, &ohci->regs->fmnumber); - } while (tick_before(now, reset_done)); - /* caller synchronizes using PRSC */ + } while (tick_before(now, reset_done) && --limit_1 >= 0); + + /* caller synchronizes using PRSC ... and handles PRS + * still being set when this returns. + */ return 0; } ------------------------------------------------------------------------- This SF.net email is sponsored by: Splunk Inc. Still grepping through log files to find problems? Stop. Now Search log events and configuration files using AJAX and a browser. Download your FREE copy of Splunk now >> http://get.splunk.com/ _______________________________________________ [email protected] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel