EHCI suspend/resume patch

From: Ian Dowse <iedowse_at_maths.tcd.ie>
Date: Fri, 22 Oct 2004 02:03:15 +0100
Hi,

David Gwynne from OpenBSD sent me some patches to make USB EHCI
host controllers handle suspending and resuming better. These
have mostly been committed to OpenBSD already.

Has anyone been having trouble with EHCI devices not recovering
after a suspend/resume? If so, could you try the patch below and
see if it helps? I've only checked that this compiles so far, and
I could have easily messed up applying David's patches.

Ian


Index: ehci.c
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ehci.c,v
retrieving revision 1.14
diff -u -r1.14 ehci.c
--- ehci.c	2 Aug 2004 15:37:34 -0000	1.14
+++ ehci.c	22 Oct 2004 00:54:28 -0000
_at__at_ -990,8 +990,8 _at__at_
 ehci_power(int why, void *v)
 {
 	ehci_softc_t *sc = v;
-	//u_int32_t ctl;
-	int s;
+	u_int32_t cmd, hcr;
+	int s, i;
 
 #ifdef EHCI_DEBUG
 	DPRINTF(("ehci_power: sc=%p, why=%d\n", sc, why));
_at__at_ -1005,46 +1005,101 _at__at_
 	case PWR_STANDBY:
 #endif
 		sc->sc_bus.use_polling++;
-#if 0
-OOO
-		ctl = OREAD4(sc, EHCI_CONTROL) & ~EHCI_HCFS_MASK;
-		if (sc->sc_control == 0) {
-			/*
-			 * Preserve register values, in case that APM BIOS
-			 * does not recover them.
-			 */
-			sc->sc_control = ctl;
-			sc->sc_intre = OREAD4(sc, EHCI_INTERRUPT_ENABLE);
+
+		for (i = 1; i <= sc->sc_noport; i++) {
+			cmd = EOREAD4(sc, EHCI_PORTSC(i));
+			if ((cmd & EHCI_PS_PO) == 0 &&
+			    (cmd & EHCI_PS_PE) == EHCI_PS_PE)
+				EOWRITE4(sc, EHCI_PORTSC(i),
+				    cmd | EHCI_PS_SUSP);
+		}
+
+		sc->sc_cmd = EOREAD4(sc, EHCI_USBCMD);
+
+		cmd = sc->sc_cmd & ~(EHCI_CMD_ASE | EHCI_CMD_PSE);
+		EOWRITE4(sc, EHCI_USBCMD, cmd);
+
+		for (i = 0; i < 100; i++) {
+			hcr = EOREAD4(sc, EHCI_USBSTS) &
+			    (EHCI_STS_ASS | EHCI_STS_PSS);
+			if (hcr == 0)
+				break;
+
+			usb_delay_ms(&sc->sc_bus, 1);
 		}
-		ctl |= EHCI_HCFS_SUSPEND;
-		OWRITE4(sc, EHCI_CONTROL, ctl);
-#endif
-		usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT);
+		if (hcr != 0) {
+			printf("%s: reset timeout\n",
+			    USBDEVNAME(sc->sc_bus.bdev));
+		}
+
+		cmd &= ~EHCI_CMD_RS;
+		EOWRITE4(sc, EHCI_USBCMD, cmd);
+
+		for (i = 0; i < 100; i++) {
+			hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
+			if (hcr == EHCI_STS_HCH)
+				break;
+
+			usb_delay_ms(&sc->sc_bus, 1);
+		}
+		if (hcr != EHCI_STS_HCH) {
+			printf("%s: config timeout\n",
+			    USBDEVNAME(sc->sc_bus.bdev));
+		}
+
 		sc->sc_bus.use_polling--;
 		break;
+
 	case PWR_RESUME:
 		sc->sc_bus.use_polling++;
-#if 0
-OOO
-		/* Some broken BIOSes do not recover these values */
-		OWRITE4(sc, EHCI_HCCA, DMAADDR(&sc->sc_hccadma, 0));
-		OWRITE4(sc, EHCI_CONTROL_HEAD_ED, sc->sc_ctrl_head->physaddr);
-		OWRITE4(sc, EHCI_BULK_HEAD_ED, sc->sc_bulk_head->physaddr);
-		if (sc->sc_intre)
-			OWRITE4(sc, EHCI_INTERRUPT_ENABLE,
-				sc->sc_intre & (EHCI_ALL_INTRS | EHCI_MIE));
-		if (sc->sc_control)
-			ctl = sc->sc_control;
-		else
-			ctl = OREAD4(sc, EHCI_CONTROL);
-		ctl |= EHCI_HCFS_RESUME;
-		OWRITE4(sc, EHCI_CONTROL, ctl);
-		usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY);
-		ctl = (ctl & ~EHCI_HCFS_MASK) | EHCI_HCFS_OPERATIONAL;
-		OWRITE4(sc, EHCI_CONTROL, ctl);
-		usb_delay_ms(&sc->sc_bus, USB_RESUME_RECOVERY);
-		sc->sc_control = sc->sc_intre = 0;
-#endif
+
+		/* restore things in case the bios sucks */
+		EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0);
+		EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0));
+		EOWRITE4(sc, EHCI_ASYNCLISTADDR,
+		    sc->sc_async_head->physaddr | EHCI_LINK_QH);
+		EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+
+		hcr = 0;
+		for (i = 1; i <= sc->sc_noport; i++) {
+			cmd = EOREAD4(sc, EHCI_PORTSC(i));
+			if ((cmd & EHCI_PS_PO) == 0 &&
+			    (cmd & EHCI_PS_SUSP) == EHCI_PS_SUSP) {
+				EOWRITE4(sc, EHCI_PORTSC(i),
+				    cmd | EHCI_PS_FPR);
+				hcr = 1;
+			}
+		}
+
+		if (hcr) {
+			usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT);
+
+			for (i = 1; i <= sc->sc_noport; i++) {
+				cmd = EOREAD4(sc, EHCI_PORTSC(i));
+				if ((cmd & EHCI_PS_PO) == 0 &&
+				    (cmd & EHCI_PS_SUSP) == EHCI_PS_SUSP)
+					EOWRITE4(sc, EHCI_PORTSC(i),
+					    cmd & ~EHCI_PS_FPR);
+			}
+		}
+
+		EOWRITE4(sc, EHCI_USBCMD, sc->sc_cmd);
+
+		for (i = 0; i < 100; i++) {
+			hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
+			if (hcr != EHCI_STS_HCH)
+				break;
+
+			usb_delay_ms(&sc->sc_bus, 1);
+		}
+
+		if (hcr == EHCI_STS_HCH) {
+			printf("%s: config timeout\n",
+			    USBDEVNAME(sc->sc_bus.bdev));
+		}
+
+		usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT);
+
 		sc->sc_bus.use_polling--;
 		break;
 #if defined(__NetBSD__) || defined(__OpenBSD__)
Index: ehcivar.h
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sys/dev/usb/ehcivar.h,v
retrieving revision 1.4
diff -u -r1.4 ehcivar.h
--- ehcivar.h	2 Aug 2004 15:37:34 -0000	1.4
+++ ehcivar.h	22 Oct 2004 00:54:29 -0000
_at__at_ -106,6 +106,7 _at__at_
 	char sc_vendor[16];		/* vendor string for root hub */
 	int sc_id_vendor;		/* vendor ID for root hub */
 
+	u_int32_t sc_cmd;		/* shadow of cmd reg during suspend */
 #if defined(__NetBSD__) || defined(__OpenBSD__)
 	void *sc_powerhook;		/* cookie from power hook */
 	void *sc_shutdownhook;		/* cookie from shutdown hook */
Received on Fri Oct 22 2004 - 00:03:51 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:38:19 UTC