mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-21 15:45:02 +00:00
Work around a problem seen on VIA EHCI controllers where occasionally
an interrupt appears to occur before the transfer has been marked as completed. This caused umass transfers to get stuck, especially when writing large files. The workaround sets up a timer that rechecks for missed completed transfers if some operations are still pending. Other suggested workarounds, such as performing a PCI read immediately after acknowledging the interrupts, do not appear to help. Obtained from: OpenBSD
This commit is contained in:
parent
df3e5efa87
commit
afcb6f8261
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=154407
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user