From 11e40ec323edd0c00a1fb92e5027889d05c29bde Mon Sep 17 00:00:00 2001 From: Christopher Pascoe Date: Thu, 22 Mar 2007 05:53:37 +0000 Subject: Perform legacy emulation handover for USB1 companion controllers immediately but defer the remainder of their initialisation until after the other devices on their PCI bus have attached. This ensures that any USB2 controller has also completed its initialisation before we start to initialise the USB1 parts. This minimises the chance that a nasty SMM implementation will trash the USB1 controller's config when it performs legacy emulation handover of the USB2 part. This time without accidentally leaving the OHCI controller shutdown, and with some cosmetic fixes. ok dlg@, OHCI tests mglocker@ --- sys/dev/cardbus/ohci_cardbus.c | 12 +++++-- sys/dev/pci/ohci_pci.c | 33 +++++++++++++++++-- sys/dev/pci/uhci_pci.c | 29 +++++++++++++++-- sys/dev/usb/ohci.c | 72 +++++++++++++++++++++++++++--------------- sys/dev/usb/ohcivar.h | 4 ++- 5 files changed, 118 insertions(+), 32 deletions(-) (limited to 'sys/dev') diff --git a/sys/dev/cardbus/ohci_cardbus.c b/sys/dev/cardbus/ohci_cardbus.c index 5df15b2f135..857fea72a90 100644 --- a/sys/dev/cardbus/ohci_cardbus.c +++ b/sys/dev/cardbus/ohci_cardbus.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ohci_cardbus.c,v 1.8 2006/10/12 16:35:52 grange Exp $ */ +/* $OpenBSD: ohci_cardbus.c,v 1.9 2007/03/22 05:53:36 pascoe Exp $ */ /* $NetBSD: ohci_cardbus.c,v 1.19 2004/08/02 19:14:28 mycroft Exp $ */ /* @@ -159,7 +159,15 @@ ohci_cardbus_attach(struct device *parent, struct device *self, void *aux) else snprintf(sc->sc.sc_vendor, sizeof(sc->sc.sc_vendor), "vendor 0x%04x", CARDBUS_VENDOR(ca->ca_id)); - + + /* Display revision and perform legacy emulation handover. */ + if (ohci_checkrev(&sc->sc) != USBD_NORMAL_COMPLETION || + ohci_handover(&sc->sc) != USBD_NORMAL_COMPLETION) { + cardbus_intr_disestablish(sc->sc_cc, sc->sc_cf, sc->sc_ih); + sc->sc_ih = 0; + return; + } + r = ohci_init(&sc->sc); if (r != USBD_NORMAL_COMPLETION) { printf("%s: init failed, error=%d\n", devname, r); diff --git a/sys/dev/pci/ohci_pci.c b/sys/dev/pci/ohci_pci.c index 9b435a1b80f..0e0e716cf35 100644 --- a/sys/dev/pci/ohci_pci.c +++ b/sys/dev/pci/ohci_pci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ohci_pci.c,v 1.31 2007/03/18 20:14:51 mglocker Exp $ */ +/* $OpenBSD: ohci_pci.c,v 1.32 2007/03/22 05:53:36 pascoe Exp $ */ /* $NetBSD: ohci_pci.c,v 1.23 2002/10/02 16:51:47 thorpej Exp $ */ /* @@ -66,6 +66,7 @@ int ohci_pci_match(struct device *, void *, void *); void ohci_pci_attach(struct device *, struct device *, void *); +void ohci_pci_attach_deferred(struct device *); int ohci_pci_detach(struct device *, int); struct ohci_pci_softc { @@ -100,7 +101,6 @@ ohci_pci_attach(struct device *parent, struct device *self, void *aux) pci_chipset_tag_t pc = pa->pa_pc; char const *intrstr; pci_intr_handle_t ih; - usbd_status r; int s; const char *vendor; char *devname = sc->sc.sc_bus.bdev.dv_xname; @@ -158,6 +158,35 @@ ohci_pci_attach(struct device *parent, struct device *self, void *aux) else snprintf(sc->sc.sc_vendor, sizeof (sc->sc.sc_vendor), "vendor 0x%04x", PCI_VENDOR(pa->pa_id)); + + /* Display revision and perform legacy emulation handover. */ + if (ohci_checkrev(&sc->sc) != USBD_NORMAL_COMPLETION || + ohci_handover(&sc->sc) != USBD_NORMAL_COMPLETION) { + bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); + splx(s); + return; + } + + /* Ignore interrupts for now */ + sc->sc.sc_dying = 1; + + config_defer(self, ohci_pci_attach_deferred); + + splx(s); + + return; +} + +void +ohci_pci_attach_deferred(struct device *self) +{ + struct ohci_pci_softc *sc = (struct ohci_pci_softc *)self; + usbd_status r; + int s; + + s = splusb(); + + sc->sc.sc_dying = 0; r = ohci_init(&sc->sc); if (r != USBD_NORMAL_COMPLETION) { diff --git a/sys/dev/pci/uhci_pci.c b/sys/dev/pci/uhci_pci.c index 7cdbc61add9..859be8ef5c8 100644 --- a/sys/dev/pci/uhci_pci.c +++ b/sys/dev/pci/uhci_pci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uhci_pci.c,v 1.24 2007/03/18 20:14:51 mglocker Exp $ */ +/* $OpenBSD: uhci_pci.c,v 1.25 2007/03/22 05:53:36 pascoe Exp $ */ /* $NetBSD: uhci_pci.c,v 1.24 2002/10/02 16:51:58 thorpej Exp $ */ /* @@ -59,6 +59,7 @@ int uhci_pci_match(struct device *, void *, void *); void uhci_pci_attach(struct device *, struct device *, void *); +void uhci_pci_attach_deferred(struct device *); int uhci_pci_detach(struct device *, int); struct uhci_pci_softc { @@ -97,7 +98,6 @@ uhci_pci_attach(struct device *parent, struct device *self, void *aux) pci_intr_handle_t ih; const char *vendor; char *devname = sc->sc.sc_bus.bdev.dv_xname; - usbd_status r; int s; #if defined(__NetBSD__) @@ -172,7 +172,32 @@ uhci_pci_attach(struct device *parent, struct device *self, void *aux) else snprintf(sc->sc.sc_vendor, sizeof (sc->sc.sc_vendor), "vendor 0x%04x", PCI_VENDOR(pa->pa_id)); + + config_defer(self, uhci_pci_attach_deferred); + + /* Ignore interrupts for now */ + sc->sc.sc_dying = 1; + + splx(s); + + return; + +unmap_ret: + bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); + splx(s); +} + +void +uhci_pci_attach_deferred(struct device *self) +{ + struct uhci_pci_softc *sc = (struct uhci_pci_softc *)self; + char *devname = sc->sc.sc_bus.bdev.dv_xname; + usbd_status r; + int s; + + s = splhardusb(); + sc->sc.sc_dying = 0; r = uhci_init(&sc->sc); if (r != USBD_NORMAL_COMPLETION) { printf("%s: init failed, error=%d\n", devname, r); diff --git a/sys/dev/usb/ohci.c b/sys/dev/usb/ohci.c index 122a67ee846..20daeffb8d5 100644 --- a/sys/dev/usb/ohci.c +++ b/sys/dev/usb/ohci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ohci.c,v 1.75 2007/03/18 20:14:51 mglocker Exp $ */ +/* $OpenBSD: ohci.c,v 1.76 2007/03/22 05:53:36 pascoe Exp $ */ /* $NetBSD: ohci.c,v 1.139 2003/02/22 05:24:16 tsutsui Exp $ */ /* $FreeBSD: src/sys/dev/usb/ohci.c,v 1.22 1999/11/17 22:33:40 n_hibma Exp $ */ @@ -611,14 +611,10 @@ ohci_free_sitd(ohci_softc_t *sc, ohci_soft_itd_t *sitd) } usbd_status -ohci_init(ohci_softc_t *sc) +ohci_checkrev(ohci_softc_t *sc) { - ohci_soft_ed_t *sed, *psed; - usbd_status err; - int i; - u_int32_t s, ctl, rwc, ival, hcr, fm, per, rev, desca, descb; + u_int32_t rev; - DPRINTF(("ohci_init: start\n")); printf(","); rev = OREAD4(sc, OHCI_REVISION); printf(" version %d.%d%s\n", OHCI_REV_HI(rev), OHCI_REV_LO(rev), @@ -632,6 +628,48 @@ ohci_init(ohci_softc_t *sc) } sc->sc_bus.usbrev = USBREV_1_0; + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +ohci_handover(ohci_softc_t *sc) +{ + u_int32_t s, ctl; + int i; + + ctl = OREAD4(sc, OHCI_CONTROL); + if (ctl & OHCI_IR) { + /* SMM active, request change */ + DPRINTF(("ohci_handover: SMM active, request owner change\n")); + if ((sc->sc_intre & (OHCI_OC | OHCI_MIE)) == + (OHCI_OC | OHCI_MIE)) + OWRITE4(sc, OHCI_INTERRUPT_ENABLE, OHCI_MIE); + s = OREAD4(sc, OHCI_COMMAND_STATUS); + OWRITE4(sc, OHCI_COMMAND_STATUS, s | OHCI_OCR); + for (i = 0; i < 100 && (ctl & OHCI_IR); i++) { + usb_delay_ms(&sc->sc_bus, 1); + ctl = OREAD4(sc, OHCI_CONTROL); + } + OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_MIE); + if (ctl & OHCI_IR) { + printf("%s: SMM does not respond, will reset\n", + USBDEVNAME(sc->sc_bus.bdev)); + } + } + + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +ohci_init(ohci_softc_t *sc) +{ + ohci_soft_ed_t *sed, *psed; + usbd_status err; + int i; + u_int32_t ctl, rwc, ival, hcr, fm, per, desca, descb; + + DPRINTF(("ohci_init: start\n")); + for (i = 0; i < OHCI_HASH_SIZE; i++) LIST_INIT(&sc->sc_hash_tds[i]); for (i = 0; i < OHCI_HASH_SIZE; i++) @@ -720,24 +758,8 @@ ohci_init(ohci_softc_t *sc) /* Determine in what context we are running. */ if (ctl & OHCI_IR) { - /* SMM active, request change */ - DPRINTF(("ohci_init: SMM active, request owner change\n")); - if ((sc->sc_intre & (OHCI_OC | OHCI_MIE)) == - (OHCI_OC | OHCI_MIE)) - OWRITE4(sc, OHCI_INTERRUPT_ENABLE, OHCI_MIE); - s = OREAD4(sc, OHCI_COMMAND_STATUS); - OWRITE4(sc, OHCI_COMMAND_STATUS, s | OHCI_OCR); - for (i = 0; i < 100 && (ctl & OHCI_IR); i++) { - usb_delay_ms(&sc->sc_bus, 1); - ctl = OREAD4(sc, OHCI_CONTROL); - } - OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_MIE); - if (ctl & OHCI_IR) { - printf("%s: SMM does not respond, resetting\n", - USBDEVNAME(sc->sc_bus.bdev)); - OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET | rwc); - goto reset; - } + OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET | rwc); + goto reset; #if 0 /* Don't bother trying to reuse the BIOS init, we'll reset it anyway. */ } else if ((ctl & OHCI_HCFS_MASK) != OHCI_HCFS_RESET) { diff --git a/sys/dev/usb/ohcivar.h b/sys/dev/usb/ohcivar.h index 8750962a44e..fa9ce3e21c7 100644 --- a/sys/dev/usb/ohcivar.h +++ b/sys/dev/usb/ohcivar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ohcivar.h,v 1.21 2006/05/22 15:52:48 krw Exp $ */ +/* $OpenBSD: ohcivar.h,v 1.22 2007/03/22 05:53:36 pascoe Exp $ */ /* $NetBSD: ohcivar.h,v 1.32 2003/02/22 05:24:17 tsutsui Exp $ */ /* $FreeBSD: src/sys/dev/usb/ohcivar.h,v 1.13 1999/11/17 22:33:41 n_hibma Exp $ */ @@ -147,6 +147,8 @@ struct ohci_xfer { struct usb_task abort_task; }; +usbd_status ohci_checkrev(ohci_softc_t *); +usbd_status ohci_handover(ohci_softc_t *); usbd_status ohci_init(ohci_softc_t *); int ohci_intr(void *); int ohci_detach(ohci_softc_t *, int); -- cgit v1.2.3