From 094e3cd2841f9d603bbd5db176b675c6253e9153 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sat, 2 Feb 2008 02:42:52 -0800 Subject: [PATCH] --- yaml --- r: 93280 b: refs/heads/master c: e01e7fe3886715f083313da409c5850472455d06 h: refs/heads/master v: v3 --- [refs] | 2 +- trunk/drivers/usb/host/ohci-hub.c | 24 +++++++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/[refs] b/[refs] index 0a52faa1f57a..0f3c41da2c01 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 9776afc8b3dc487557f3f576002520f59be334e6 +refs/heads/master: e01e7fe3886715f083313da409c5850472455d06 diff --git a/trunk/drivers/usb/host/ohci-hub.c b/trunk/drivers/usb/host/ohci-hub.c index 48e4b11f4d3e..c638e6b33c43 100644 --- a/trunk/drivers/usb/host/ohci-hub.c +++ b/trunk/drivers/usb/host/ohci-hub.c @@ -564,14 +564,18 @@ 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 = 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 (struct ohci_hcd *ohci, unsigned port) 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 (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)); - /* 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; }