diff options
author | Christopher Pascoe <pascoe@cvs.openbsd.org> | 2005-03-07 11:12:05 +0000 |
---|---|---|
committer | Christopher Pascoe <pascoe@cvs.openbsd.org> | 2005-03-07 11:12:05 +0000 |
commit | e53a0cb5d419926b8555ed36af032df7aa3a6d6a (patch) | |
tree | 768a7b4a876baee437e105f82fd21a79e3a666eb | |
parent | fd9399343260e05d83411f4cc5afd7a8fc405a8c (diff) |
Add a workaround for VIA EHCI controllers which, under load, signal qTD
completion before they have performed writeback from the overlay qTD.
This condition would exhibit itself as a umass stall that never recovers.
ok dlg@
-rw-r--r-- | sys/dev/pci/ehci_pci.c | 7 | ||||
-rw-r--r-- | sys/dev/usb/ehci.c | 34 | ||||
-rw-r--r-- | sys/dev/usb/ehcivar.h | 5 |
3 files changed, 43 insertions, 3 deletions
diff --git a/sys/dev/pci/ehci_pci.c b/sys/dev/pci/ehci_pci.c index 398f6f0b27d..eaf5df4ea19 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.4 2004/12/29 01:52:27 dlg Exp $ */ +/* $OpenBSD: ehci_pci.c,v 1.5 2005/03/07 11:12:04 pascoe Exp $ */ /* $NetBSD: ehci_pci.c,v 1.15 2004/04/23 21:13:06 itojun Exp $ */ /* @@ -51,6 +51,7 @@ __KERNEL_RCSID(0, "$NetBSD: ehci_pci.c,v 1.15 2004/04/23 21:13:06 itojun Exp $") #include <machine/bus.h> +#include <dev/pci/pcidevs.h> #include <dev/pci/pcivar.h> #include <dev/pci/usb_pci.h> @@ -190,6 +191,10 @@ ehci_pci_attach(struct device *parent, struct device *self, void *aux) snprintf(sc->sc.sc_vendor, sizeof(sc->sc.sc_vendor), "vendor 0x%04x", PCI_VENDOR(pa->pa_id)); + /* Enable workaround for dropped interrupts as required */ + if (sc->sc.sc_id_vendor == PCI_VENDOR_VIATECH) + sc->sc.sc_flags |= EHCIF_DROPPED_INTR_WORKAROUND; + /* * 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/ehci.c b/sys/dev/usb/ehci.c index 82159709010..dad2e61ca11 100644 --- a/sys/dev/usb/ehci.c +++ b/sys/dev/usb/ehci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ehci.c,v 1.37 2005/03/06 05:18:34 pascoe Exp $ */ +/* $OpenBSD: ehci.c,v 1.38 2005/03/07 11:12:04 pascoe Exp $ */ /* $NetBSD: ehci.c,v 1.66 2004/06/30 03:11:56 mycroft Exp $ */ /* @@ -155,6 +155,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 *); @@ -487,6 +488,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); @@ -687,6 +689,12 @@ ehci_softintr(void *v) ehci_check_intr(sc, ex); } + /* Schedule a callout to catch any dropped transactions. */ + if ((sc->sc_flags & EHCIF_DROPPED_INTR_WORKAROUND) && + !LIST_EMPTY(&sc->sc_intrhead)) + usb_callout(sc->sc_tmo_intrlist, hz, + ehci_intrlist_timeout, sc); + #ifdef USB_USE_SOFTINTR if (sc->sc_softwake) { sc->sc_softwake = 0; @@ -926,6 +934,7 @@ ehci_detach(struct ehci_softc *sc, int flags) if (rv != 0) return (rv); + usb_uncallout(sc->sc_tmo_intrlist, ehci_intrlist_timeout, sc); usb_uncallout(sc->sc_tmo_pcd, ehci_pcd_enable, sc); if (sc->sc_powerhook != NULL) @@ -2511,6 +2520,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(); + + printf("ehci_intrlist_timeout\n"); + usb_schedsoftintr(&sc->sc_bus); + + splx(s); +} + /************************/ Static usbd_status diff --git a/sys/dev/usb/ehcivar.h b/sys/dev/usb/ehcivar.h index bdfde1445b0..8e79b5fd3e4 100644 --- a/sys/dev/usb/ehcivar.h +++ b/sys/dev/usb/ehcivar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ehcivar.h,v 1.7 2004/12/29 01:52:27 dlg Exp $ */ +/* $OpenBSD: ehcivar.h,v 1.8 2005/03/07 11:12:04 pascoe Exp $ */ /* $NetBSD: ehcivar.h,v 1.12 2001/12/31 12:16:57 augustss Exp $ */ /* @@ -92,6 +92,8 @@ typedef struct ehci_softc { bus_space_handle_t ioh; bus_size_t sc_size; u_int sc_offs; /* offset to operational regs */ + int sc_flags; /* misc flags */ +#define EHCIF_DROPPED_INTR_WORKAROUND 0x01 char sc_vendor[16]; /* vendor string for root hub */ int sc_id_vendor; /* vendor ID for root hub */ @@ -131,6 +133,7 @@ typedef struct ehci_softc { struct lock sc_doorbell_lock; usb_callout_t sc_tmo_pcd; + usb_callout_t sc_tmo_intrlist; device_ptr_t sc_child; /* /dev/usb# device */ |