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
[-- Attachment #1 --]
[-- Attachment #2 --]
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);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200312082138.00259.mistry.7>
