summaryrefslogtreecommitdiff
path: root/sys/dev/usb
diff options
context:
space:
mode:
authorChristopher Pascoe <pascoe@cvs.openbsd.org>2005-03-07 11:12:05 +0000
committerChristopher Pascoe <pascoe@cvs.openbsd.org>2005-03-07 11:12:05 +0000
commite53a0cb5d419926b8555ed36af032df7aa3a6d6a (patch)
tree768a7b4a876baee437e105f82fd21a79e3a666eb /sys/dev/usb
parentfd9399343260e05d83411f4cc5afd7a8fc405a8c (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@
Diffstat (limited to 'sys/dev/usb')
-rw-r--r--sys/dev/usb/ehci.c34
-rw-r--r--sys/dev/usb/ehcivar.h5
2 files changed, 37 insertions, 2 deletions
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 */