diff options
author | David Gwynne <dlg@cvs.openbsd.org> | 2004-12-29 01:52:28 +0000 |
---|---|---|
committer | David Gwynne <dlg@cvs.openbsd.org> | 2004-12-29 01:52:28 +0000 |
commit | e21f5a39ae887f8942ef4219e31f1ae7e5ade40c (patch) | |
tree | 0d92495fbe02d52ddc8ffd61fbb4247e4b95c341 /sys/dev/pci | |
parent | 72f515ed522e03c0608093ba6f814fbeebd7d9fc (diff) |
from freebsd: ehci.c 1.13, ehci_pci.c 1.13, ehcireg.h 1.5, ehcivar.h 1.3
log message:
Attempt to follow the correct procedure for synchronising with the
system BIOS to disable legacy device emulation as per the "EHCI
Extended Capability: Pre-OS to OS Handoff Synchronisation" section
of the EHCI spec. BIOSes that implement legacy emulation using SMIs
are supposed to disable the emulation when this procedure is performed.
tested on various archs by jsg@ and me
ok pascoe@, looks sane jsg@
Diffstat (limited to 'sys/dev/pci')
-rw-r--r-- | sys/dev/pci/ehci_pci.c | 76 |
1 files changed, 71 insertions, 5 deletions
diff --git a/sys/dev/pci/ehci_pci.c b/sys/dev/pci/ehci_pci.c index 82b1d85af24..398f6f0b27d 100644 --- a/sys/dev/pci/ehci_pci.c +++ b/sys/dev/pci/ehci_pci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ehci_pci.c,v 1.3 2004/05/30 01:25:17 tedu Exp $ */ +/* $OpenBSD: ehci_pci.c,v 1.4 2004/12/29 01:52:27 dlg Exp $ */ /* $NetBSD: ehci_pci.c,v 1.15 2004/04/23 21:13:06 itojun Exp $ */ /* @@ -69,10 +69,6 @@ extern int ehcidebug; #define DPRINTF(x) #endif -int ehci_pci_match(struct device *, void *, void *); -void ehci_pci_attach(struct device *, struct device *, void *); -int ehci_pci_detach(device_ptr_t, int); - struct ehci_pci_softc { ehci_softc_t sc; pci_chipset_tag_t sc_pc; @@ -80,6 +76,13 @@ struct ehci_pci_softc { void *sc_ih; /* interrupt vectoring */ }; +int ehci_pci_match(struct device *, void *, void *); +void ehci_pci_attach(struct device *, struct device *, void *); +int ehci_pci_detach(device_ptr_t, int); +void ehci_pci_givecontroller(struct ehci_pci_softc *); +void ehci_pci_takecontroller(struct ehci_pci_softc *); +void ehci_pci_shutdown(void *); + struct cfattach ehci_pci_ca = { sizeof(struct ehci_pci_softc), ehci_pci_match, ehci_pci_attach, ehci_pci_detach, ehci_activate @@ -203,6 +206,7 @@ ehci_pci_attach(struct device *parent, struct device *self, void *aux) } sc->sc.sc_ncomp = ncomp; + ehci_pci_takecontroller(sc); r = ehci_init(&sc->sc); if (r != USBD_NORMAL_COMPLETION) { printf("%s: init failed, error=%d\n", devname, r); @@ -210,6 +214,8 @@ ehci_pci_attach(struct device *parent, struct device *self, void *aux) return; } + sc->sc.sc_shutdownhook = shutdownhook_establish(ehci_pci_shutdown, sc); + /* Attach usb device. */ sc->sc.sc_child = config_found((void *)sc, &sc->sc.sc_bus, usbctlprint); @@ -234,3 +240,63 @@ ehci_pci_detach(device_ptr_t self, int flags) } return (0); } + +void +ehci_pci_givecontroller(struct ehci_pci_softc *sc) +{ + u_int32_t cparams, eec, legsup; + int eecp; + + cparams = EREAD4(&sc->sc, EHCI_HCCPARAMS); + for (eecp = EHCI_HCC_EECP(cparams); eecp != 0; + eecp = EHCI_EECP_NEXT(eec)) { + eec = pci_conf_read(sc->sc_pc, sc->sc_tag, eecp); + if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP) + continue; + legsup = eec; + pci_conf_write(sc->sc_pc, sc->sc_tag, eecp, + legsup & ~EHCI_LEGSUP_OSOWNED); + } +} + +void +ehci_pci_takecontroller(struct ehci_pci_softc *sc) +{ + u_int32_t cparams, eec, legsup; + int eecp, i; + + cparams = EREAD4(&sc->sc, EHCI_HCCPARAMS); + /* Synchronise with the BIOS if it owns the controller. */ + for (eecp = EHCI_HCC_EECP(cparams); eecp != 0; + eecp = EHCI_EECP_NEXT(eec)) { + eec = pci_conf_read(sc->sc_pc, sc->sc_tag, eecp); + if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP) + continue; + legsup = eec; + pci_conf_write(sc->sc_pc, sc->sc_tag, eecp, + legsup | EHCI_LEGSUP_OSOWNED); + if (legsup & EHCI_LEGSUP_BIOSOWNED) { + DPRINTF(("%s: waiting for BIOS to give up control\n", + USBDEVNAME(sc->sc.sc_bus.bdev))); + for (i = 0; i < 5000; i++) { + legsup = pci_conf_read(sc->sc_pc, sc->sc_tag, + eecp); + if ((legsup & EHCI_LEGSUP_BIOSOWNED) == 0) + break; + DELAY(1000); + } + if (legsup & EHCI_LEGSUP_BIOSOWNED) + printf("%s: timed out waiting for BIOS\n", + USBDEVNAME(sc->sc.sc_bus.bdev)); + } + } +} + +void +ehci_pci_shutdown(void *v) +{ + struct ehci_pci_softc *sc = (struct ehci_pci_softc *)v; + + ehci_shutdown(&sc->sc); + ehci_pci_givecontroller(sc); +} |