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