summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorPatrick Wildt <patrick@cvs.openbsd.org>2019-02-21 22:44:45 +0000
committerPatrick Wildt <patrick@cvs.openbsd.org>2019-02-21 22:44:45 +0000
commitfb3c4d3acc64391196c131bc90a0ba865dc9b341 (patch)
treedd4416687fce3256a340ed4ae71c379edbcfa6d1 /sys/dev
parent651b95692687f26f68a592a76b9db98af21ec9ed (diff)
Transfers that span multiple TRBs which wrap around the ring and
thus have the Link TRB inbetween must have the Chain Bit set in the Link TRB. Otherwise xHCI controllers might think that the transfer ends at that point. Fixes an issue that was most prominently seen as Invalid CSW error when using umass0 on octeon and i.MX8M. Tested by visa@ ok mpi@
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/usb/xhci.c14
1 files changed, 9 insertions, 5 deletions
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c
index 1078acb20c1..7a6ff253a9c 100644
--- a/sys/dev/usb/xhci.c
+++ b/sys/dev/usb/xhci.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: xhci.c,v 1.90 2019/02/01 13:24:15 mpi Exp $ */
+/* $OpenBSD: xhci.c,v 1.91 2019/02/21 22:44:44 patrick Exp $ */
/*
* Copyright (c) 2014-2015 Martin Pieuchot
@@ -91,7 +91,8 @@ int xhci_ring_alloc(struct xhci_softc *, struct xhci_ring *, size_t,
void xhci_ring_free(struct xhci_softc *, struct xhci_ring *);
void xhci_ring_reset(struct xhci_softc *, struct xhci_ring *);
struct xhci_trb *xhci_ring_consume(struct xhci_softc *, struct xhci_ring *);
-struct xhci_trb *xhci_ring_produce(struct xhci_softc *, struct xhci_ring *);
+struct xhci_trb *xhci_ring_produce(struct xhci_softc *, struct xhci_ring *,
+ int);
struct xhci_trb *xhci_xfer_get_trb(struct xhci_softc *, struct usbd_xfer*,
uint8_t *, int);
@@ -1584,7 +1585,7 @@ xhci_ring_consume(struct xhci_softc *sc, struct xhci_ring *ring)
}
struct xhci_trb*
-xhci_ring_produce(struct xhci_softc *sc, struct xhci_ring *ring)
+xhci_ring_produce(struct xhci_softc *sc, struct xhci_ring *ring, int last)
{
struct xhci_trb *trb = &ring->trbs[ring->index];
@@ -1605,6 +1606,9 @@ xhci_ring_produce(struct xhci_softc *sc, struct xhci_ring *ring)
BUS_DMASYNC_POSTWRITE);
lnk->trb_flags ^= htole32(XHCI_TRB_CYCLE);
+ lnk->trb_flags &= htole32(~XHCI_TRB_CHAIN);
+ if (!last)
+ lnk->trb_flags |= htole32(XHCI_TRB_CHAIN);
bus_dmamap_sync(ring->dma.tag, ring->dma.map, TRBOFF(ring, lnk),
sizeof(struct xhci_trb), BUS_DMASYNC_PREWRITE);
@@ -1633,7 +1637,7 @@ xhci_xfer_get_trb(struct xhci_softc *sc, struct usbd_xfer *xfer,
xx->ntrb += 1;
*togglep = xp->ring.toggle;
- return (xhci_ring_produce(sc, &xp->ring));
+ return (xhci_ring_produce(sc, &xp->ring, last));
}
int
@@ -1646,7 +1650,7 @@ xhci_command_submit(struct xhci_softc *sc, struct xhci_trb *trb0, int timeout)
trb0->trb_flags |= htole32(sc->sc_cmd_ring.toggle);
- trb = xhci_ring_produce(sc, &sc->sc_cmd_ring);
+ trb = xhci_ring_produce(sc, &sc->sc_cmd_ring, 1);
if (trb == NULL)
return (EAGAIN);
trb->trb_paddr = trb0->trb_paddr;