From: David Brownell <[EMAIL PROTECTED]> Date: Mon, 08 Oct 2007 21:36:43 -0700
> Don't need this "limit_1" timeout; "reset_done" handles all > the timeout needed there. The regs->fmnumber is essentially > a millisecond counter. If the hardware hangs and the register stops incrementing, the entire kernel will hang. That is unacceptable. We do need it. > > > + int limit_2; > > + > > /* spin until any current reset finishes */ > > - for (;;) { > > + limit_2 = PORT_RESET_MSEC * 2; > > This is the loop that didn't terminate for you, right? > PORT_RESET_HW_MSEC is the ceiling you should use here, > not PORT_RESET_MSEC. Ok, fixed. > What values do you see for "portstat"? 0x111 > I suspect there will be some flag set which would allow a more > immediate exit from that loop. RH_PS_CCS might clear, for example. Absolutely nothing clears in the register from it's initial value. Here is the patch with the limit_2 initial value fixed. I kept loop_1 in there, it is necessary. No kernel code should hang in an endless loop because of malfunctioning hardware. Signed-off-by: David S. Miller <[EMAIL PROTECTED]> diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index bb9cc59..9149593 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -563,14 +563,19 @@ static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port) u32 temp; u16 now = ohci_readl(ohci, &ohci->regs->fmnumber); u16 reset_done = now + PORT_RESET_MSEC; + int limit_1; /* 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 { + limit_1 = 100; + while (--limit_1 >= 0) { + 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) @@ -579,6 +584,10 @@ static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port) break; udelay (500); } + if (limit_2 < 0) { + ohci_warn(ohci, "Root port inner-loop reset timeout, " + "portstat[%08x]\n", temp); + } if (!(temp & RH_PS_CCS)) break; @@ -589,7 +598,14 @@ static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port) 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)); + if (!tick_before(now, reset_done)) + break; + } + if (limit_1 < 0) { + ohci_warn(ohci, "Root port outer-loop reset timeout, " + "now[%04x] reset_done[%04x]\n", + now, reset_done); + } /* caller synchronizes using PRSC */ return 0; - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/