summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/xhci-mem.c2
-rw-r--r--drivers/usb/host/xhci-ring.c43
2 files changed, 33 insertions, 12 deletions
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 7a21ac81f..af8d4b74c 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -2301,7 +2301,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags)
if (!segs)
segs = ERST_DEFAULT_SEGS;
- max_segs = FIELD_GET(HCS_ERST_MAX, xhci->hcs_params2) << 2;
+ max_segs = BIT(FIELD_GET(HCS_ERST_MAX, xhci->hcs_params2));
segs = min(segs, max_segs);
ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev));
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 97a1b53c1..ec278a9f9 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -824,21 +824,18 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
usb_hcd_giveback_urb(hcd, urb, status);
}
-static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci,
- struct xhci_ring *ring, struct xhci_td *td)
+static void xhci_unmap_one_bounce_buffer(struct xhci_hcd *xhci,
+ struct xhci_ring *ring, struct xhci_td *td,
+ struct xhci_segment *seg)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
- struct xhci_segment *seg = td->bounce_seg;
struct urb *urb = td->urb;
size_t len;
- if (!ring || !seg || !urb)
- return;
-
if (usb_urb_dir_out(urb)) {
dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len,
DMA_TO_DEVICE);
- return;
+ goto done;
}
dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len,
@@ -854,10 +851,29 @@ static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci,
memcpy(urb->transfer_buffer + seg->bounce_offs, seg->bounce_buf,
seg->bounce_len);
}
+done:
seg->bounce_len = 0;
seg->bounce_offs = 0;
}
+static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci,
+ struct xhci_ring *ring, struct xhci_td *td)
+{
+ struct xhci_segment *seg;
+ int i = 0;
+
+ if (!td->bounce_seg || !ring || !td->urb)
+ return;
+
+ /* td->bounce_seg is the last one bounced, unmap them all */
+ for (seg = td->start_seg; i++ < ring->num_segs; seg = seg->next) {
+ if (seg->bounce_len)
+ xhci_unmap_one_bounce_buffer(xhci, ring, td, seg);
+ if (seg == td->bounce_seg)
+ break;
+ }
+}
+
static void xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td,
struct xhci_ring *ep_ring, int status)
{
@@ -3685,7 +3701,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
&trb_buff_len,
ring->enq_seg)) {
send_addr = ring->enq_seg->bounce_dma;
- /* assuming TD won't span 2 segs */
+ /* TD bounced at least, and last on this seg */
td->bounce_seg = ring->enq_seg;
}
}
@@ -4312,11 +4328,16 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
check_interval(urb, ep_ctx);
/*
- * Check if this starts the isoc data flow. Relies on hw setting ep ctx
- * state after doorbell ring. Consider adding list_empty(td_list) check
+ * Schedule the URB discontiguously if all previous URBs have completed.
+ * XXX core can't tell if completions are pending but not running yet.
*/
- if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_RUNNING)
+ if (list_empty(&ep_ring->td_list) &&
+ !hcd_periodic_completion_in_progress(xhci_to_hcd(xhci), urb->ep)) {
+ if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING)
+ xhci_dbg(xhci, "Unexpected running ring at isoc stream start, uframe: %d\n",
+ xep->next_uframe);
xep->next_uframe = -1;
+ }
return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index);
}