Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 13 Aug 2009 07:21:24 +0000 (UTC)
From:      Nick Hibma <n_hibma@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org
Subject:   svn commit: r196167 - stable/7/sys/dev/usb
Message-ID:  <200908130721.n7D7LOWd090861@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: n_hibma
Date: Thu Aug 13 07:21:24 2009
New Revision: 196167
URL: http://svn.freebsd.org/changeset/base/196167

Log:
  Restart the controller if it has halted. Restarting it makes the USB
  tree functional again (without reconnecting the devices, mind you).
  
  On my laptop there is probably a short somewhere on the motherboard and
  once in a while the host controller halts.

Modified:
  stable/7/sys/dev/usb/uhci.c

Modified: stable/7/sys/dev/usb/uhci.c
==============================================================================
--- stable/7/sys/dev/usb/uhci.c	Thu Aug 13 07:19:43 2009	(r196166)
+++ stable/7/sys/dev/usb/uhci.c	Thu Aug 13 07:21:24 2009	(r196167)
@@ -1205,20 +1205,55 @@ uhci_intr1(uhci_softc_t *sc)
 		       device_get_nameunit(sc->sc_bus.bdev));
 	}
 	if (status & UHCI_STS_HCH) {
-		/* no acknowledge needed */
 		if (!sc->sc_dying) {
+			ack |= UHCI_STS_HCH;
 			printf("%s: host controller halted\n",
-			    device_get_nameunit(sc->sc_bus.bdev));
-#ifdef USB_DEBUG
-			uhci_dump_all(sc);
-#endif
+			       device_get_nameunit(sc->sc_bus.bdev));
 		}
-		sc->sc_dying = 1;
 	}
 
 	if (!ack)
 		return (0);	/* nothing to acknowledge */
-	UWRITE2(sc, UHCI_STS, ack); /* acknowledge the ints */
+
+	UWRITE2(sc, UHCI_STS, ack & ~UHCI_STS_HCH); /* acknowledge the ints */
+
+	if (ack & UHCI_STS_HCH) {
+		/* Restart the controller, by Manuel Bouyer */
+		sc->sc_saved_frnum = UREAD2(sc, UHCI_FRNUM);
+		sc->sc_saved_sof = UREAD1(sc, UHCI_SOF);
+
+		sc->sc_bus.use_polling++;
+		uhci_run(sc, 0); /* stop the controller */
+		UWRITE2(sc, UHCI_INTR, 0); /* disable intrs */
+
+		uhci_globalreset(sc);
+		uhci_reset(sc);
+
+		/* restore saved state */
+		UWRITE4(sc, UHCI_FLBASEADDR, DMAADDR(&sc->sc_dma, 0));
+		UWRITE2(sc, UHCI_FRNUM, sc->sc_saved_frnum);
+		UWRITE1(sc, UHCI_SOF, sc->sc_saved_sof);
+
+		UWRITE2(sc, UHCI_INTR, UHCI_INTR_TOCRCIE | UHCI_INTR_RIE |
+			UHCI_INTR_IOCE | UHCI_INTR_SPIE); /* re-enable intrs */
+		UHCICMD(sc, UHCI_CMD_MAXP);
+
+		uhci_run(sc, 1); /* and start traffic again */
+		sc->sc_bus.use_polling--;
+
+		if (UREAD2(sc, UHCI_STS) & UHCI_STS_HCH) {
+			printf("%s: host controller couldn't be restarted\n",
+			       device_get_nameunit(sc->sc_bus.bdev));
+#ifdef USB_DEBUG
+			uhci_dump_all(sc);
+#endif
+			sc->sc_dying = 1;
+			return (0);
+		}
+
+		printf("%s: host controller restarted\n",
+		       device_get_nameunit(sc->sc_bus.bdev));
+	}
 
 	sc->sc_bus.no_intrs++;
 	usb_schedsoftintr(&sc->sc_bus);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200908130721.n7D7LOWd090861>