Date: Mon, 8 Dec 2003 21:37:45 -0500 From: Anish Mistry <mistry.7@osu.edu> To: freebsd-current@freebsd.org Subject: USB OHCI suspend and resume Message-ID: <200312082138.00259.mistry.7@osu.edu>
next in thread | raw e-mail | index | archive | help
--Boundary-00=_5VT1/msm8vnNWQM Content-Type: multipart/signed; charset="us-ascii"; protocol="application/pgp-signature"; micalg=pgp-sha1; boundary="Boundary-02=_IWT1/UUuys9G9pl"; name=" " Content-Transfer-Encoding: 7bit --Boundary-02=_IWT1/UUuys9G9pl Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Content-Description: signed data Content-Disposition: inline I've attached a patch that along with the 20031203 acpica import, allows=20 usb(ohci) suspend and resume to work without panicing my system. There is= =20 a very good chance it may cause/fix problems with usb(ohci) on your=20 system. Please report back any error or panic backtraces. style fixes to= =20 the patch coming near the end of the week. Thanks, =2D-=20 Anish Mistry --Boundary-02=_IWT1/UUuys9G9pl Content-Type: application/pgp-signature Content-Description: signature -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.3 (FreeBSD) iD8DBQA/1TWIxqA5ziudZT0RArYqAJ9cekAEC5FMd8rFM/E9X8fP7j6OqQCgznes 2sfP3AX7Rq5cQKacJhuQcgM= =rzh/ -----END PGP SIGNATURE----- --Boundary-02=_IWT1/UUuys9G9pl-- --Boundary-00=_5VT1/msm8vnNWQM Content-Type: text/x-diff; charset="us-ascii"; name="ohci-sr-20031208.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="ohci-sr-20031208.patch" diff -u sys/dev/usb.orig/ohci.c sys/dev/usb/ohci.c --- sys/dev/usb.orig/ohci.c Mon Dec 8 21:23:51 2003 +++ sys/dev/usb/ohci.c Mon Dec 8 21:25:58 2003 @@ -1010,7 +1010,7 @@ DPRINTF(("ohci_shutdown: stopping the HC\n")); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); } - +#endif /* * Handle suspend/resume. * @@ -1018,6 +1018,134 @@ * called from an intterupt context. This is all right since we * are almost suspended anyway. */ +usbd_status +ohci_resume(struct ohci_softc *sc) +{ + int s; + u_int32_t ctl, ival, fm, per, rev; + +#if defined(__OpenBSD__) + printf(","); +#else + printf("%s:", USBDEVNAME(sc->sc_bus.bdev)); +#endif + rev = OREAD4(sc, OHCI_REVISION); + printf(" OHCI version %d.%d%s\n", OHCI_REV_HI(rev), OHCI_REV_LO(rev), + OHCI_REV_LEGACY(rev) ? ", legacy support" : ""); + printf("ohci_resume: controller state: "); + switch(OREAD4(sc, OHCI_CONTROL) & OHCI_HCFS_MASK) { + case OHCI_HCFS_SUSPEND: + printf("SUSPEND"); + break; + case OHCI_HCFS_RESUME: + printf("RESUME"); + break; + case OHCI_HCFS_RESET: + printf("RESET"); + break; + case OHCI_HCFS_OPERATIONAL: + printf("OPERATIONAL"); + break; + } + printf("\n"); + s = splhardusb(); + /* The controller only responds to resume or reset writes at this point, so lets resume */ + /* We are only supposed to enter resume state from a suspend state. Should we check? */ + OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESUME); + usb_delay_ms(&sc->sc_bus, USB_RESUME_DELAY); + /* check if the controller has resumed */ + ctl = OREAD4(sc, OHCI_CONTROL); + if((ctl & OHCI_HCFS_RESUME) == OHCI_HCFS_RESUME) { + printf("ohci_resume: controller state: RESUME\n"); + } else { + /* panic or abort? */ + printf("ohci_resume: ??? controller did not resume!\n"); + printf("ohci_resume: OHCI_CONTROL: 0x%x\n",ctl); + } + +#ifdef USB_DEBUG + ohci_dumpregs(sc); +#endif + + /* reset or controller may not start */ + OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); + usb_delay_ms(&sc->sc_bus, USB_BUS_RESET_DELAY); + + /* spec says save frame interrupt value, reset, then restore */ + ival = OHCI_GET_IVAL(OREAD4(sc, OHCI_FM_INTERVAL)); + OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_HCR); /* Reset HC */ + usb_delay_ms(&sc->sc_bus, USB_BUS_RESET_DELAY); + + /* Some broken BIOSes do not recover these values */ + OWRITE4(sc, OHCI_HCCA, DMAADDR(&sc->sc_hccadma, 0)); + OWRITE4(sc, OHCI_CONTROL_HEAD_ED, sc->sc_ctrl_head->physaddr); + OWRITE4(sc, OHCI_BULK_HEAD_ED, sc->sc_bulk_head->physaddr); + /* disable all interrupts and then switch on all desired interrupts */ + OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); + OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_intre | OHCI_MIE ); + + fm = (OREAD4(sc, OHCI_FM_INTERVAL) & OHCI_FIT) ^ OHCI_FIT; + fm |= OHCI_FSMPS(ival) | ival; + OWRITE4(sc, OHCI_FM_INTERVAL, fm); + per = OHCI_PERIODIC(ival); + OWRITE4(sc, OHCI_PERIODIC_START, per); + + /* start controller */ + ctl = sc->sc_control; + OWRITE4(sc, OHCI_CONTROL, ctl); + usb_delay_ms(&sc->sc_bus, USB_RESUME_RECOVERY); + + /* power up ports */ + OWRITE4(sc, OHCI_RH_STATUS, OHCI_LPSC); + usb_delay_ms(&sc->sc_bus, OHCI_ENABLE_POWER_DELAY); + splx(s); +#ifdef USB_DEBUG + ohci_dumpregs(sc); +#endif + + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +ohci_suspend(struct ohci_softc *sc) +{ + u_int32_t ctl; + int s; + +#ifdef USB_DEBUG + ohci_dumpregs(sc); +#endif + + /* + * Preserve register values, in case that APM BIOS + * does not recover them. + */ + sc->sc_control = OREAD4(sc, OHCI_CONTROL); + sc->sc_intre = OREAD4(sc, OHCI_INTERRUPT_ENABLE); + s = splhardusb(); + /* disable interrupts */ + OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); + splx(s); + /* Reset to stop processing frames or the controller might not suspend */ + OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); + usb_delay_ms(&sc->sc_bus, USB_BUS_RESET_DELAY); + /* now suspend */ + ctl = OHCI_HCFS_SUSPEND; + OWRITE4(sc, OHCI_CONTROL, ctl); + usb_delay_ms(&sc->sc_bus, USB_RESUME_WAIT); + /* check if the controller is suspended */ + ctl = OREAD4(sc, OHCI_CONTROL); + if((ctl & OHCI_HCFS_SUSPEND) == OHCI_HCFS_SUSPEND) { + printf("ohci_suspend: controller state: SUSPEND.\n"); + } else { + /* panic or abort? */ + printf("ohci_suspend: ??? controller did no suspend!\n"); + printf("ohci_suspend: OHCI_CONTROL: 0x%x\n",ctl); + } + return (USBD_NORMAL_COMPLETION); +} + +#if defined(__NetBSD__) || defined(__OpenBSD__) void ohci_power(int why, void *v) { diff -u sys/dev/usb.orig/ohci_pci.c sys/dev/usb/ohci_pci.c --- sys/dev/usb.orig/ohci_pci.c Mon Dec 8 21:23:54 2003 +++ sys/dev/usb/ohci_pci.c Mon Dec 8 21:24:21 2003 @@ -304,11 +304,39 @@ return 0; } +/* implement suspend and resume */ +static int +ohci_pci_suspend(device_t self) +{ + int err; + ohci_softc_t *sc = device_get_softc(self); + err = bus_generic_suspend(self); + if (err) + return err; + ohci_suspend(sc); + return 0; +} + +static int +ohci_pci_resume(device_t self) +{ + ohci_softc_t *sc = device_get_softc(self); + pci_set_powerstate(self, PCI_POWERSTATE_D0); + if(ohci_resume(sc) != USBD_NORMAL_COMPLETION) { + return ENXIO; + } + bus_generic_resume(self); + return 0; +} + static device_method_t ohci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ohci_pci_probe), DEVMETHOD(device_attach, ohci_pci_attach), - DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_suspend, ohci_pci_suspend), + DEVMETHOD(device_resume, ohci_pci_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), diff -u sys/dev/usb.orig/ohcivar.h sys/dev/usb/ohcivar.h --- sys/dev/usb.orig/ohcivar.h Mon Dec 8 21:23:51 2003 +++ sys/dev/usb/ohcivar.h Mon Dec 8 21:24:21 2003 @@ -158,6 +158,8 @@ #define OXFER(xfer) ((struct ohci_xfer *)(xfer)) usbd_status ohci_init(ohci_softc_t *); +usbd_status ohci_suspend(ohci_softc_t *); +usbd_status ohci_resume(ohci_softc_t *); int ohci_intr(void *); #if defined(__NetBSD__) || defined(__OpenBSD__) int ohci_detach(ohci_softc_t *, int); --Boundary-00=_5VT1/msm8vnNWQM--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200312082138.00259.mistry.7>