diff options
author | Patrick Wildt <patrick@cvs.openbsd.org> | 2019-02-21 22:44:45 +0000 |
---|---|---|
committer | Patrick Wildt <patrick@cvs.openbsd.org> | 2019-02-21 22:44:45 +0000 |
commit | fb3c4d3acc64391196c131bc90a0ba865dc9b341 (patch) | |
tree | dd4416687fce3256a340ed4ae71c379edbcfa6d1 /sys/dev | |
parent | 651b95692687f26f68a592a76b9db98af21ec9ed (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.c | 14 |
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; |