summaryrefslogtreecommitdiff
path: root/sys/dev/usb/xhci.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb/xhci.c')
-rw-r--r--sys/dev/usb/xhci.c150
1 files changed, 78 insertions, 72 deletions
diff --git a/sys/dev/usb/xhci.c b/sys/dev/usb/xhci.c
index 7a6ff253a9c..efa46ad674a 100644
--- a/sys/dev/usb/xhci.c
+++ b/sys/dev/usb/xhci.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: xhci.c,v 1.91 2019/02/21 22:44:44 patrick Exp $ */
+/* $OpenBSD: xhci.c,v 1.92 2019/02/27 05:22:37 patrick Exp $ */
/*
* Copyright (c) 2014-2015 Martin Pieuchot
@@ -91,8 +91,7 @@ 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 *,
- int);
+struct xhci_trb *xhci_ring_produce(struct xhci_softc *, struct xhci_ring *);
struct xhci_trb *xhci_xfer_get_trb(struct xhci_softc *, struct usbd_xfer*,
uint8_t *, int);
@@ -1585,9 +1584,10 @@ 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, int last)
+xhci_ring_produce(struct xhci_softc *sc, struct xhci_ring *ring)
{
struct xhci_trb *trb = &ring->trbs[ring->index];
+ int oidx;
KASSERT(ring->index < ring->ntrb);
@@ -1595,7 +1595,7 @@ xhci_ring_produce(struct xhci_softc *sc, struct xhci_ring *ring, int last)
sizeof(struct xhci_trb), BUS_DMASYNC_POSTREAD |
BUS_DMASYNC_POSTWRITE);
- ring->index++;
+ oidx = ring->index++;
/* Toggle cycle state of the link TRB and skip it. */
if (ring->index == (ring->ntrb - 1)) {
@@ -1605,14 +1605,18 @@ xhci_ring_produce(struct xhci_softc *sc, struct xhci_ring *ring, int last)
sizeof(struct xhci_trb), BUS_DMASYNC_POSTREAD |
BUS_DMASYNC_POSTWRITE);
- lnk->trb_flags ^= htole32(XHCI_TRB_CYCLE);
lnk->trb_flags &= htole32(~XHCI_TRB_CHAIN);
- if (!last)
+ if (letoh32(ring->trbs[oidx].trb_flags) & XHCI_TRB_CHAIN)
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);
+ lnk->trb_flags ^= htole32(XHCI_TRB_CYCLE);
+
+ bus_dmamap_sync(ring->dma.tag, ring->dma.map, TRBOFF(ring, lnk),
+ sizeof(struct xhci_trb), BUS_DMASYNC_PREWRITE);
+
ring->index = 0;
ring->toggle ^= 1;
}
@@ -1637,7 +1641,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, last));
+ return (xhci_ring_produce(sc, &xp->ring));
}
int
@@ -1650,7 +1654,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, 1);
+ trb = xhci_ring_produce(sc, &sc->sc_cmd_ring);
if (trb == NULL)
return (EAGAIN);
trb->trb_paddr = trb0->trb_paddr;
@@ -2615,7 +2619,7 @@ xhci_device_ctrl_start(struct usbd_xfer *xfer)
struct xhci_pipe *xp = (struct xhci_pipe *)xfer->pipe;
struct xhci_trb *trb0, *trb;
uint32_t flags, len = UGETW(xfer->request.wLength);
- uint8_t toggle0, toggle;
+ uint8_t toggle;
int s;
KASSERT(xfer->rqflags & URQ_REQUEST);
@@ -2626,8 +2630,23 @@ xhci_device_ctrl_start(struct usbd_xfer *xfer)
if (xp->free_trbs < 3)
return (USBD_NOMEM);
- /* We'll do the setup TRB once we're finished with the other stages. */
- trb0 = xhci_xfer_get_trb(sc, xfer, &toggle0, 0);
+ /* We'll toggle the setup TRB once we're finished with the stages. */
+ trb0 = xhci_xfer_get_trb(sc, xfer, &toggle, 0);
+
+ flags = XHCI_TRB_TYPE_SETUP | XHCI_TRB_IDT | (toggle ^ 1);
+ if (len != 0) {
+ if (usbd_xfer_isread(xfer))
+ flags |= XHCI_TRB_TRT_IN;
+ else
+ flags |= XHCI_TRB_TRT_OUT;
+ }
+
+ memcpy(&trb0->trb_paddr, &xfer->request, sizeof(trb0->trb_paddr));
+ trb0->trb_status = htole32(XHCI_TRB_INTR(0) | XHCI_TRB_LEN(8));
+ trb0->trb_flags = htole32(flags);
+ bus_dmamap_sync(xp->ring.dma.tag, xp->ring.dma.map,
+ TRBOFF(&xp->ring, trb0), sizeof(struct xhci_trb),
+ BUS_DMASYNC_PREWRITE);
/* Data TRB */
if (len != 0) {
@@ -2665,21 +2684,7 @@ xhci_device_ctrl_start(struct usbd_xfer *xfer)
BUS_DMASYNC_PREWRITE);
/* Setup TRB */
- flags = XHCI_TRB_TYPE_SETUP | XHCI_TRB_IDT | toggle0;
- if (len != 0) {
- if (usbd_xfer_isread(xfer))
- flags |= XHCI_TRB_TRT_IN;
- else
- flags |= XHCI_TRB_TRT_OUT;
- }
-
- memcpy(&trb0->trb_paddr, &xfer->request, sizeof(trb0->trb_paddr));
- trb0->trb_status = htole32(XHCI_TRB_INTR(0) | XHCI_TRB_LEN(8));
- bus_dmamap_sync(xp->ring.dma.tag, xp->ring.dma.map,
- TRBOFF(&xp->ring, trb0), sizeof(struct xhci_trb),
- BUS_DMASYNC_PREWRITE);
-
- trb0->trb_flags = htole32(flags);
+ trb0->trb_flags ^= htole32(XHCI_TRB_CYCLE);
bus_dmamap_sync(xp->ring.dma.tag, xp->ring.dma.map,
TRBOFF(&xp->ring, trb0), sizeof(struct xhci_trb),
BUS_DMASYNC_PREWRITE);
@@ -2723,9 +2728,9 @@ xhci_device_generic_start(struct usbd_xfer *xfer)
struct xhci_pipe *xp = (struct xhci_pipe *)xfer->pipe;
struct xhci_trb *trb0, *trb;
uint32_t len, remain, flags;
- uint32_t len0, mps = UGETW(xfer->pipe->endpoint->edesc->wMaxPacketSize);
+ uint32_t mps = UGETW(xfer->pipe->endpoint->edesc->wMaxPacketSize);
uint64_t paddr = DMAADDR(&xfer->dmabuf, 0);
- uint8_t toggle0, toggle;
+ uint8_t toggle;
int s, i, ntrb;
KASSERT(!(xfer->rqflags & URQ_REQUEST));
@@ -2737,11 +2742,11 @@ xhci_device_generic_start(struct usbd_xfer *xfer)
ntrb = howmany(xfer->length, XHCI_TRB_MAXSIZE);
/* If the buffer crosses a 64k boundary, we need one more. */
- len0 = XHCI_TRB_MAXSIZE - (paddr & (XHCI_TRB_MAXSIZE - 1));
- if (len0 < xfer->length)
+ len = XHCI_TRB_MAXSIZE - (paddr & (XHCI_TRB_MAXSIZE - 1));
+ if (len < xfer->length)
ntrb++;
else
- len0 = xfer->length;
+ len = xfer->length;
/* If we need to append a zero length packet, we need one more. */
if ((xfer->flags & USBD_FORCE_SHORT_XFER || xfer->length == 0) &&
@@ -2751,11 +2756,25 @@ xhci_device_generic_start(struct usbd_xfer *xfer)
if (xp->free_trbs < ntrb)
return (USBD_NOMEM);
- /* We'll do the first TRB once we're finished with the chain. */
- trb0 = xhci_xfer_get_trb(sc, xfer, &toggle0, (ntrb == 1));
+ /* We'll toggle the first TRB once we're finished with the chain. */
+ trb0 = xhci_xfer_get_trb(sc, xfer, &toggle, (ntrb == 1));
+ flags = XHCI_TRB_TYPE_NORMAL | (toggle ^ 1);
+ if (usbd_xfer_isread(xfer))
+ flags |= XHCI_TRB_ISP;
+ flags |= (ntrb == 1) ? XHCI_TRB_IOC : XHCI_TRB_CHAIN;
- remain = xfer->length - len0;
- paddr += len0;
+ trb0->trb_paddr = htole64(DMAADDR(&xfer->dmabuf, 0));
+ trb0->trb_status = htole32(
+ XHCI_TRB_INTR(0) | XHCI_TRB_LEN(len) |
+ xhci_xfer_tdsize(xfer, xfer->length, len)
+ );
+ trb0->trb_flags = htole32(flags);
+ bus_dmamap_sync(xp->ring.dma.tag, xp->ring.dma.map,
+ TRBOFF(&xp->ring, trb0), sizeof(struct xhci_trb),
+ BUS_DMASYNC_PREWRITE);
+
+ remain = xfer->length - len;
+ paddr += len;
/* Chain more TRBs if needed. */
for (i = ntrb - 1; i > 0; i--) {
@@ -2784,21 +2803,7 @@ xhci_device_generic_start(struct usbd_xfer *xfer)
}
/* First TRB. */
- flags = XHCI_TRB_TYPE_NORMAL | toggle0;
- if (usbd_xfer_isread(xfer))
- flags |= XHCI_TRB_ISP;
- flags |= (ntrb == 1) ? XHCI_TRB_IOC : XHCI_TRB_CHAIN;
-
- trb0->trb_paddr = htole64(DMAADDR(&xfer->dmabuf, 0));
- trb0->trb_status = htole32(
- XHCI_TRB_INTR(0) | XHCI_TRB_LEN(len0) |
- xhci_xfer_tdsize(xfer, xfer->length, len0)
- );
- bus_dmamap_sync(xp->ring.dma.tag, xp->ring.dma.map,
- TRBOFF(&xp->ring, trb0), sizeof(struct xhci_trb),
- BUS_DMASYNC_PREWRITE);
-
- trb0->trb_flags = htole32(flags);
+ trb0->trb_flags ^= htole32(XHCI_TRB_CYCLE);
bus_dmamap_sync(xp->ring.dma.tag, xp->ring.dma.map,
TRBOFF(&xp->ring, trb0), sizeof(struct xhci_trb),
BUS_DMASYNC_PREWRITE);
@@ -2857,8 +2862,8 @@ xhci_device_isoc_start(struct usbd_xfer *xfer)
uint32_t len, remain, flags;
uint64_t paddr = DMAADDR(&xfer->dmabuf, 0);
uint32_t len0, tbc, tlbpc;
- uint8_t toggle0, toggle;
int s, i, ntrb = xfer->nframes;
+ uint8_t toggle;
KASSERT(!(xfer->rqflags & URQ_REQUEST));
@@ -2883,8 +2888,26 @@ xhci_device_isoc_start(struct usbd_xfer *xfer)
len0 = xfer->frlengths[0];
- /* We'll do the first TRB once we're finished with the chain. */
- trb0 = xhci_xfer_get_trb(sc, xfer, &toggle0, (ntrb == 1));
+ /* We'll toggle the first TRB once we're finished with the chain. */
+ trb0 = xhci_xfer_get_trb(sc, xfer, &toggle, (ntrb == 1));
+
+ flags = XHCI_TRB_TYPE_ISOCH | XHCI_TRB_SIA | (toggle ^ 1);
+ if (usbd_xfer_isread(xfer))
+ flags |= XHCI_TRB_ISP;
+ flags |= (ntrb == 1) ? XHCI_TRB_IOC : XHCI_TRB_CHAIN;
+
+ tbc = xhci_xfer_tbc(xfer, len0, &tlbpc);
+ flags |= XHCI_TRB_ISOC_TBC(tbc) | XHCI_TRB_ISOC_TLBPC(tlbpc);
+
+ trb0->trb_paddr = htole64(DMAADDR(&xfer->dmabuf, 0));
+ trb0->trb_status = htole32(
+ XHCI_TRB_INTR(0) | XHCI_TRB_LEN(len0) |
+ xhci_xfer_tdsize(xfer, xfer->length, len0)
+ );
+ trb0->trb_flags = htole32(flags);
+ bus_dmamap_sync(xp->ring.dma.tag, xp->ring.dma.map,
+ TRBOFF(&xp->ring, trb0), sizeof(struct xhci_trb),
+ BUS_DMASYNC_PREWRITE);
remain = xfer->length - len0;
paddr += len0;
@@ -2916,24 +2939,7 @@ xhci_device_isoc_start(struct usbd_xfer *xfer)
}
/* First TRB. */
- flags = XHCI_TRB_TYPE_ISOCH | XHCI_TRB_SIA | toggle0;
- if (usbd_xfer_isread(xfer))
- flags |= XHCI_TRB_ISP;
- flags |= (ntrb == 1) ? XHCI_TRB_IOC : XHCI_TRB_CHAIN;
-
- tbc = xhci_xfer_tbc(xfer, len0, &tlbpc);
- flags |= XHCI_TRB_ISOC_TBC(tbc) | XHCI_TRB_ISOC_TLBPC(tlbpc);
-
- trb0->trb_paddr = htole64(DMAADDR(&xfer->dmabuf, 0));
- trb0->trb_status = htole32(
- XHCI_TRB_INTR(0) | XHCI_TRB_LEN(len0) |
- xhci_xfer_tdsize(xfer, xfer->length, len0)
- );
- bus_dmamap_sync(xp->ring.dma.tag, xp->ring.dma.map,
- TRBOFF(&xp->ring, trb0), sizeof(struct xhci_trb),
- BUS_DMASYNC_PREWRITE);
-
- trb0->trb_flags = htole32(flags);
+ trb0->trb_flags ^= htole32(XHCI_TRB_CYCLE);
bus_dmamap_sync(xp->ring.dma.tag, xp->ring.dma.map,
TRBOFF(&xp->ring, trb0), sizeof(struct xhci_trb),
BUS_DMASYNC_PREWRITE);