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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.