If you have a machine with UHCI and EHCI that comes up with a "uhciN: host controller halted" message during boot, leaving you without working USB (or a hung machine), you might want to give this patch a go. It seems that SMM on some machines resets the UHCI part of the chip when it performs legacy handover of the EHCI part, resulting in us seeing the UHCI part halt unexpectedly. This patch makes us reset/restart the chip the first time (actually, few times) that we see the host controller halt, which seems to work around the problem. Chris Pascoe 2007/03/11 Index: sys/dev/usb/uhci.c =================================================================== RCS file: /cvs/src/sys/dev/usb/uhci.c,v retrieving revision 1.48 diff -u -p -r1.48 uhci.c --- sys/dev/usb/uhci.c 23 Jun 2006 06:27:11 -0000 1.48 +++ sys/dev/usb/uhci.c 11 Mar 2007 02:19:51 -0000 @@ -108,6 +108,8 @@ int uhcinoloop = 0; #define DPRINTFN(n,x) #endif +#define MAX_HCH_RESTARTS 5 + /* * The UHCI controller is little endian, so on big endian machines * the data stored in memory needs to be swapped. @@ -409,10 +411,16 @@ uhci_init(uhci_softc_t *sc) uhci_dumpregs(sc); #endif + /* Save SOF over HC reset. */ + sc->sc_saved_sof = UREAD1(sc, UHCI_SOF); + UWRITE2(sc, UHCI_INTR, 0); /* disable interrupts */ uhci_globalreset(sc); /* reset the controller */ uhci_reset(sc); + /* Restore saved SOF. */ + UWRITE1(sc, UHCI_SOF, sc->sc_saved_sof); + /* Allocate and initialize real frame array. */ err = usb_allocmem(&sc->sc_bus, UHCI_FRAMELIST_COUNT * sizeof(uhci_physaddr_t), @@ -682,6 +690,8 @@ uhci_shutdown(void *v) uhci_softc_t *sc = v; DPRINTF(("uhci_shutdown: stopping the HC\n")); + usb_uncallout(sc->sc_poll_handle, uhci_poll_hub, xfer); + sc->sc_dying = 1; uhci_run(sc, 0); /* stop the controller */ } @@ -720,7 +730,6 @@ uhci_power(int why, void *v) /* save some state if BIOS doesn't */ sc->sc_saved_frnum = UREAD2(sc, UHCI_FRNUM); - sc->sc_saved_sof = UREAD1(sc, UHCI_SOF); UWRITE2(sc, UHCI_INTR, 0); /* disable intrs */ @@ -974,6 +983,26 @@ uhci_poll_hub(void *addr) DPRINTFN(20, ("uhci_poll_hub\n")); + /* Try to restart a halted controller by faking a suspend/resume. */ + if ((sc->sc_hch_seen || (UREAD2(sc, UHCI_STS) & UHCI_STS_HCH)) && + sc->sc_hch_restarts < MAX_HCH_RESTARTS && !sc->sc_dying && + sc->sc_suspend == PWR_RESUME) { + printf("%s: attempting to restart halted controller " + "(status 0x%04x)\n", USBDEVNAME(sc->sc_bus.bdev), + UREAD2(sc, UHCI_STS)); + + s = splhardusb(); + sc->sc_hch_restarts++; + sc->sc_bus.use_polling++; + uhci_power(PWR_STANDBY, sc); + uhci_power(PWR_RESUME, sc); + uhci_portreset(sc, 1); + uhci_portreset(sc, 2); + sc->sc_bus.use_polling--; + sc->sc_hch_seen = 0; + splx(s); + } + usb_callout(sc->sc_poll_handle, sc->sc_ival, uhci_poll_hub, xfer); p = KERNADDR(&xfer->dmabuf, 0); @@ -1236,8 +1265,29 @@ uhci_intr1(uhci_softc_t *sc) if (status & UHCI_STS_HCH) { /* no acknowledge needed */ if (!sc->sc_dying) { - printf("%s: host controller halted\n", - USBDEVNAME(sc->sc_bus.bdev)); + if (sc->sc_hch_seen == 1 || + sc->sc_hch_restarts == MAX_HCH_RESTARTS) + printf("%s: host controller halted", + USBDEVNAME(sc->sc_bus.bdev)); + /* + * The chip could have been halted by SMM when its + * companion controller attached, so take note of this. + * The root hub polling timeout will restart the chip. + */ + if (sc->sc_hch_restarts < MAX_HCH_RESTARTS) { + if (sc->sc_hch_seen == 0) + sc->sc_hch_seen = 1; + else if (sc->sc_hch_seen == 1) { + printf(" (status 0x%04x), will attempt " + "restart\n", status); + sc->sc_hch_seen = 2; + } +#ifdef UHCI_DEBUG + uhci_dumpregs(sc); +#endif + return (1); + } + printf("\n"); #ifdef UHCI_DEBUG uhci_dump_all(sc); #endif Index: sys/dev/usb/uhcivar.h =================================================================== RCS file: /cvs/src/sys/dev/usb/uhcivar.h,v retrieving revision 1.15 diff -u -p -r1.15 uhcivar.h --- sys/dev/usb/uhcivar.h 8 Jul 2003 13:19:09 -0000 1.15 +++ sys/dev/usb/uhcivar.h 11 Mar 2007 02:19:51 -0000 @@ -170,6 +170,9 @@ typedef struct uhci_softc { char sc_suspend; char sc_dying; + char sc_hch_restarts; + char sc_hch_seen; + LIST_HEAD(, uhci_intr_info) sc_intrhead; /* Info for the root hub interrupt "pipe". */