From owner-freebsd-current@FreeBSD.ORG Fri Oct 22 02:03:51 2004 Return-Path: Delivered-To: freebsd-current@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id A8F4F16A4CE for ; Fri, 22 Oct 2004 02:03:51 +0000 (GMT) Received: from salmon.maths.tcd.ie (salmon.maths.tcd.ie [134.226.81.11]) by mx1.FreeBSD.org (Postfix) with SMTP id CFC0E43D54 for ; Fri, 22 Oct 2004 02:03:50 +0000 (GMT) (envelope-from iedowse@maths.tcd.ie) Received: from walton.maths.tcd.ie by salmon.maths.tcd.ie with SMTP id ; 22 Oct 2004 02:03:41 +0100 (BST) To: current@freebsd.org Date: Fri, 22 Oct 2004 02:03:15 +0100 From: Ian Dowse Message-ID: <200410220203.aa82787@salmon.maths.tcd.ie> Subject: EHCI suspend/resume patch X-BeenThere: freebsd-current@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: Discussions about the use of FreeBSD-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 22 Oct 2004 02:03:51 -0000 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 @@ -990,8 +990,8 @@ 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)); @@ -1005,46 +1005,101 @@ 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 @@ -106,6 +106,7 @@ 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 */