diff options
-rw-r--r-- | sys/dev/usb/ehci.c | 32 | ||||
-rw-r--r-- | sys/dev/usb/ehci_pci.c | 4 | ||||
-rw-r--r-- | sys/dev/usb/ehcivar.h | 2 |
3 files changed, 38 insertions, 0 deletions
diff --git a/sys/dev/usb/ehci.c b/sys/dev/usb/ehci.c index eeb47a4..5324c5e0 100644 --- a/sys/dev/usb/ehci.c +++ b/sys/dev/usb/ehci.c @@ -153,6 +153,7 @@ Static void ehci_check_intr(ehci_softc_t *, struct ehci_xfer *); Static void ehci_idone(struct ehci_xfer *); Static void ehci_timeout(void *); Static void ehci_timeout_task(void *); +Static void ehci_intrlist_timeout(void *); Static usbd_status ehci_allocm(struct usbd_bus *, usb_dma_t *, u_int32_t); Static void ehci_freem(struct usbd_bus *, usb_dma_t *); @@ -489,6 +490,7 @@ ehci_init(ehci_softc_t *sc) EOWRITE4(sc, EHCI_ASYNCLISTADDR, sqh->physaddr | EHCI_LINK_QH); usb_callout_init(sc->sc_tmo_pcd); + usb_callout_init(sc->sc_tmo_intrlist); lockinit(&sc->sc_doorbell_lock, PZERO, "ehcidb", 0, 0); @@ -692,6 +694,12 @@ ehci_softintr(void *v) ehci_check_intr(sc, ex); } + /* Schedule a callout to catch any dropped transactions. */ + if ((sc->sc_flags & EHCI_SCFLG_LOSTINTRBUG) && + !LIST_EMPTY(&sc->sc_intrhead)) + usb_callout(sc->sc_tmo_intrlist, hz / 5, ehci_intrlist_timeout, + sc); + #ifdef USB_USE_SOFTINTR if (sc->sc_softwake) { sc->sc_softwake = 0; @@ -942,6 +950,7 @@ ehci_detach(struct ehci_softc *sc, int flags) EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); EOWRITE4(sc, EHCI_USBCMD, 0); EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); + usb_uncallout(sc->sc_tmo_intrlist, ehci_intrlist_timeout, sc); usb_uncallout(sc->sc_tmo_pcd, ehci_pcd_enable, sc); #if defined(__NetBSD__) || defined(__OpenBSD__) @@ -2700,6 +2709,29 @@ ehci_timeout_task(void *addr) splx(s); } +/* + * Some EHCI chips from VIA seem to trigger interrupts before writing back the + * qTD status, or miss signalling occasionally under heavy load. If the host + * machine is too fast, we we can miss transaction completion - when we scan + * the active list the transaction still seems to be active. This generally + * exhibits itself as a umass stall that never recovers. + * + * We work around this behaviour by setting up this callback after any softintr + * that completes with transactions still pending, giving us another chance to + * check for completion after the writeback has taken place. + */ +void +ehci_intrlist_timeout(void *arg) +{ + ehci_softc_t *sc = arg; + int s = splusb(); + + DPRINTFN(3, ("ehci_intrlist_timeout\n")); + usb_schedsoftintr(&sc->sc_bus); + + splx(s); +} + /************************/ Static usbd_status diff --git a/sys/dev/usb/ehci_pci.c b/sys/dev/usb/ehci_pci.c index 9918efc..b03d94c 100644 --- a/sys/dev/usb/ehci_pci.c +++ b/sys/dev/usb/ehci_pci.c @@ -373,6 +373,10 @@ ehci_pci_attach(device_t self) return ENXIO; } + /* Enable workaround for dropped interrupts as required */ + if (pci_get_vendor(self) == PCI_EHCI_VENDORID_VIA) + sc->sc_flags |= EHCI_SCFLG_LOSTINTRBUG; + /* * Find companion controllers. According to the spec they always * have lower function numbers so they should be enumerated already. diff --git a/sys/dev/usb/ehcivar.h b/sys/dev/usb/ehcivar.h index f7a2029..e1b852d 100644 --- a/sys/dev/usb/ehcivar.h +++ b/sys/dev/usb/ehcivar.h @@ -94,6 +94,7 @@ struct ehci_soft_islot { #define EHCI_COMPANION_MAX 8 #define EHCI_SCFLG_DONEINIT 0x0001 /* ehci_init() has been called. */ +#define EHCI_SCFLG_LOSTINTRBUG 0x0002 /* workaround for VIA chipsets */ typedef struct ehci_softc { struct usbd_bus sc_bus; /* base device */ @@ -153,6 +154,7 @@ typedef struct ehci_softc { struct lock sc_doorbell_lock; usb_callout_t sc_tmo_pcd; + usb_callout_t sc_tmo_intrlist; #if defined(__NetBSD__) || defined(__OpenBSD__) device_ptr_t sc_child; /* /dev/usb# device */ |