CFT - patch to fix ehci hang on shutdown

Don Lewis truckman at FreeBSD.org
Sun Aug 5 00:06:36 UTC 2007


I've got an AMD 64 X2 machine that uses the recent NVIDIA GeForce 7050 /
nForce 630a chipset that hangs on shutdown.  It hangs with both UP and
SMP kernels, and with both FreeBSD 6.2-STABLE and 7.0-CURRENT.  The
problem appears to be that a delay is needed between the the call

	EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
	
in ehci_shutdown(), and the call

	cparams = EREAD4(sc, EHCI_HCCPARAMS);
	
in ehci_pci_givecontroller().


There are three instances of a code sequence that does a controller
reset in ehci.c, one of which, in ehci_init(), inserts some delays and
periodically polls the controller to look for the completion of the
reset.  It seems to make sense to encapsulate the latter version of the
sequence in a separate function, and then call that function from
ehci_reset(), ehci_detach(), and ehci_shutdown().  I implemented this in
the attached patch, and it fixes shutdown problem for me on both
7.0-CURRENT and 6.2-STABLE (with some minor tweaks for the latter).

I've tested this patch on this system with both i386 and amd64 kernels,
and I also tested it on my Pentium-M laptop.  The shutdown problems are
gone and everything else looks normal, but I don't have any USB 2.0
peripherals to do further testing.  I'd appreciate any testing that can
be done in the next serveral days before I ask re@ for approval to
commit this to -CURRENT.

Index: sys/dev/usb/ehci.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/usb/ehci.c,v
retrieving revision 1.55
diff -u -r1.55 ehci.c
--- sys/dev/usb/ehci.c	20 Jun 2007 05:10:52 -0000	1.55
+++ sys/dev/usb/ehci.c	4 Aug 2007 21:05:46 -0000
@@ -311,6 +311,25 @@
 	ehci_device_isoc_done,
 };
 
+static usbd_status
+ehci_hcreset(ehci_softc_t *sc)
+{
+	u_int32_t hcr;
+	u_int i;
+
+	EOWRITE4(sc, EHCI_USBCMD, 0);	/* Halt controller */
+	usb_delay_ms(&sc->sc_bus, 1);
+	EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
+	for (i = 0; i < 100; i++) {
+		usb_delay_ms(&sc->sc_bus, 1);
+		hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET;
+		if (!hcr)
+			return (USBD_NORMAL_COMPLETION);
+	}
+	printf("%s: reset timeout\n", device_get_nameunit(sc->sc_bus.bdev));
+	return (USBD_IOERROR);
+}
+
 usbd_status
 ehci_init(ehci_softc_t *sc)
 {
@@ -365,20 +384,9 @@
 
 	/* Reset the controller */
 	DPRINTF(("%s: resetting\n", device_get_nameunit(sc->sc_bus.bdev)));
-	EOWRITE4(sc, EHCI_USBCMD, 0);	/* Halt controller */
-	usb_delay_ms(&sc->sc_bus, 1);
-	EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
-	for (i = 0; i < 100; i++) {
-		usb_delay_ms(&sc->sc_bus, 1);
-		hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET;
-		if (!hcr)
-			break;
-	}
-	if (hcr) {
-		printf("%s: reset timeout\n",
-		    device_get_nameunit(sc->sc_bus.bdev));
-		return (USBD_IOERROR);
-	}
+	err = ehci_hcreset(sc);
+	if (err != USBD_NORMAL_COMPLETION)
+		return (err);
 
 	/* frame list size at default, read back what we got and use that */
 	switch (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD))) {
@@ -927,8 +935,7 @@
 	sc->sc_dying = 1;
 
 	EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
-	EOWRITE4(sc, EHCI_USBCMD, 0);
-	EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
+	(void) ehci_hcreset(sc);
 	callout_stop(&sc->sc_tmo_intrlist);
 	callout_stop(&sc->sc_tmo_pcd);
 
@@ -1090,8 +1097,7 @@
 	ehci_softc_t *sc = v;
 
 	DPRINTF(("ehci_shutdown: stopping the HC\n"));
-	EOWRITE4(sc, EHCI_USBCMD, 0);	/* Halt controller */
-	EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
+	(void) ehci_hcreset(sc);
 }
 
 usbd_status




More information about the freebsd-current mailing list