summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/usb/controller/ehci.c40
-rw-r--r--sys/dev/usb/controller/ehci.h2
-rw-r--r--sys/dev/usb/controller/ehci_pci.c13
3 files changed, 51 insertions, 4 deletions
diff --git a/sys/dev/usb/controller/ehci.c b/sys/dev/usb/controller/ehci.c
index 42b78c3..28be7ab 100644
--- a/sys/dev/usb/controller/ehci.c
+++ b/sys/dev/usb/controller/ehci.c
@@ -113,10 +113,12 @@ extern struct usb_pipe_methods ehci_device_intr_methods;
extern struct usb_pipe_methods ehci_device_isoc_fs_methods;
extern struct usb_pipe_methods ehci_device_isoc_hs_methods;
-static void ehci_do_poll(struct usb_bus *bus);
-static void ehci_device_done(struct usb_xfer *xfer, usb_error_t error);
-static uint8_t ehci_check_transfer(struct usb_xfer *xfer);
-static void ehci_timeout(void *arg);
+static void ehci_do_poll(struct usb_bus *);
+static void ehci_device_done(struct usb_xfer *, usb_error_t);
+static uint8_t ehci_check_transfer(struct usb_xfer *);
+static void ehci_timeout(void *);
+static void ehci_poll_timeout(void *);
+
static void ehci_root_intr(ehci_softc_t *sc);
struct ehci_std_temp {
@@ -243,6 +245,7 @@ ehci_init(ehci_softc_t *sc)
DPRINTF("start\n");
usb_callout_init_mtx(&sc->sc_tmo_pcd, &sc->sc_bus.bus_mtx, 0);
+ usb_callout_init_mtx(&sc->sc_tmo_poll, &sc->sc_bus.bus_mtx, 0);
#if USB_DEBUG
if (ehcidebug > 2) {
@@ -520,6 +523,7 @@ ehci_detach(ehci_softc_t *sc)
USB_BUS_LOCK(&sc->sc_bus);
usb_callout_stop(&sc->sc_tmo_pcd);
+ usb_callout_stop(&sc->sc_tmo_poll);
EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
USB_BUS_UNLOCK(&sc->sc_bus);
@@ -532,6 +536,7 @@ ehci_detach(ehci_softc_t *sc)
usb_pause_mtx(NULL, hz / 20);
usb_callout_drain(&sc->sc_tmo_pcd);
+ usb_callout_drain(&sc->sc_tmo_poll);
}
void
@@ -1472,6 +1477,28 @@ repeat:
}
}
+/*
+ * Some EHCI chips from VIA / ATI 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 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.
+ */
+static void
+ehci_poll_timeout(void *arg)
+{
+ ehci_softc_t *sc = arg;
+
+ DPRINTFN(3, "\n");
+ ehci_interrupt_poll(sc);
+}
+
/*------------------------------------------------------------------------*
* ehci_interrupt - EHCI interrupt handler
*
@@ -1539,6 +1566,11 @@ ehci_interrupt(ehci_softc_t *sc)
/* poll all the USB transfers */
ehci_interrupt_poll(sc);
+ if (sc->sc_flags & EHCI_SCFLG_LOSTINTRBUG) {
+ usb_callout_reset(&sc->sc_tmo_poll, hz / 128,
+ (void *)&ehci_poll_timeout, sc);
+ }
+
done:
USB_BUS_UNLOCK(&sc->sc_bus);
}
diff --git a/sys/dev/usb/controller/ehci.h b/sys/dev/usb/controller/ehci.h
index 32c0835..a3d8406 100644
--- a/sys/dev/usb/controller/ehci.h
+++ b/sys/dev/usb/controller/ehci.h
@@ -321,6 +321,7 @@ typedef struct ehci_softc {
struct ehci_hw_softc sc_hw;
struct usb_bus sc_bus; /* base device */
struct usb_callout sc_tmo_pcd;
+ struct usb_callout sc_tmo_poll;
union ehci_hub_desc sc_hub_desc;
struct usb_device *sc_devices[EHCI_MAX_DEVICES];
@@ -348,6 +349,7 @@ typedef struct ehci_softc {
#define EHCI_SCFLG_BIGEDESC 0x0008 /* big-endian byte order descriptors */
#define EHCI_SCFLG_BIGEMMIO 0x0010 /* big-endian byte order MMIO */
#define EHCI_SCFLG_TT 0x0020 /* transaction translator present */
+#define EHCI_SCFLG_LOSTINTRBUG 0x0040 /* workaround for VIA / ATI chipsets */
uint8_t sc_offs; /* offset to operational registers */
uint8_t sc_doorbell_disable; /* set on doorbell failure */
diff --git a/sys/dev/usb/controller/ehci_pci.c b/sys/dev/usb/controller/ehci_pci.c
index d1440c1..ec6f3f7 100644
--- a/sys/dev/usb/controller/ehci_pci.c
+++ b/sys/dev/usb/controller/ehci_pci.c
@@ -439,6 +439,19 @@ ehci_pci_attach(device_t self)
break;
}
+ /* Dropped interrupts workaround */
+ switch (pci_get_vendor(self)) {
+ case PCI_EHCI_VENDORID_ATI:
+ case PCI_EHCI_VENDORID_VIA:
+ sc->sc_flags |= EHCI_SCFLG_LOSTINTRBUG;
+ if (bootverbose)
+ device_printf(self,
+ "Dropped interrupts workaround enabled\n");
+ break;
+ default:
+ break;
+ }
+
err = ehci_init(sc);
if (!err) {
err = device_probe_and_attach(sc->sc_bus.bdev);
OpenPOWER on IntegriCloud