summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/xhci-ring.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-rw-r--r--drivers/usb/host/xhci-ring.c118
1 files changed, 50 insertions, 68 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 48028bab57..fd0cde3d15 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -308,7 +308,7 @@ static unsigned int xhci_num_trbs_free(struct xhci_hcd *xhci, struct xhci_ring *
free += last_on_seg - enq;
enq_seg = enq_seg->next;
enq = enq_seg->trbs;
- } while (i++ <= ring->num_segs);
+ } while (i++ < ring->num_segs);
return free;
}
@@ -351,10 +351,8 @@ static unsigned int xhci_ring_expansion_needed(struct xhci_hcd *xhci, struct xhc
while (new_segs > 0) {
seg = seg->next;
if (seg == ring->deq_seg) {
- xhci_dbg(xhci, "Ring expansion by %d segments needed\n",
- new_segs);
- xhci_dbg(xhci, "Adding %d trbs moves enq %d trbs into deq seg\n",
- num_trbs, trbs_past_seg % TRBS_PER_SEGMENT);
+ xhci_dbg(xhci, "Adding %d trbs requires expanding ring by %d segments\n",
+ num_trbs, new_segs);
return new_segs;
}
new_segs--;
@@ -1026,8 +1024,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
td->urb->stream_id);
hw_deq &= ~0xf;
- if (td->cancel_status == TD_HALTED ||
- trb_in_td(xhci, td->start_seg, td->first_trb, td->last_trb, hw_deq, false)) {
+ if (td->cancel_status == TD_HALTED || trb_in_td(xhci, td, hw_deq, false)) {
switch (td->cancel_status) {
case TD_CLEARED: /* TD is already no-op */
case TD_CLEARING_CACHE: /* set TR deq command already queued */
@@ -1104,8 +1101,7 @@ static struct xhci_td *find_halted_td(struct xhci_virt_ep *ep)
hw_deq = xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0);
hw_deq &= ~0xf;
td = list_first_entry(&ep->ring->td_list, struct xhci_td, td_list);
- if (trb_in_td(ep->xhci, td->start_seg, td->first_trb,
- td->last_trb, hw_deq, false))
+ if (trb_in_td(ep->xhci, td, hw_deq, false))
return td;
}
return NULL;
@@ -2083,25 +2079,19 @@ cleanup:
}
/*
- * This TD is defined by the TRBs starting at start_trb in start_seg and ending
- * at end_trb, which may be in another segment. If the suspect DMA address is a
- * TRB in this TD, this function returns that TRB's segment. Otherwise it
- * returns 0.
+ * If the suspect DMA address is a TRB in this TD, this function returns that
+ * TRB's segment. Otherwise it returns 0.
*/
-struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
- struct xhci_segment *start_seg,
- union xhci_trb *start_trb,
- union xhci_trb *end_trb,
- dma_addr_t suspect_dma,
- bool debug)
+struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td, dma_addr_t suspect_dma,
+ bool debug)
{
dma_addr_t start_dma;
dma_addr_t end_seg_dma;
dma_addr_t end_trb_dma;
struct xhci_segment *cur_seg;
- start_dma = xhci_trb_virt_to_dma(start_seg, start_trb);
- cur_seg = start_seg;
+ start_dma = xhci_trb_virt_to_dma(td->start_seg, td->first_trb);
+ cur_seg = td->start_seg;
do {
if (start_dma == 0)
@@ -2110,7 +2100,7 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
end_seg_dma = xhci_trb_virt_to_dma(cur_seg,
&cur_seg->trbs[TRBS_PER_SEGMENT - 1]);
/* If the end TRB isn't in this segment, this is set to 0 */
- end_trb_dma = xhci_trb_virt_to_dma(cur_seg, end_trb);
+ end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->last_trb);
if (debug)
xhci_warn(xhci,
@@ -2144,7 +2134,7 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci,
}
cur_seg = cur_seg->next;
start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]);
- } while (cur_seg != start_seg);
+ } while (cur_seg != td->start_seg);
return NULL;
}
@@ -2620,7 +2610,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
struct xhci_ep_ctx *ep_ctx;
u32 trb_comp_code;
int td_num = 0;
- bool handling_skipped_tds = false;
slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
@@ -2757,19 +2746,19 @@ static int handle_tx_event(struct xhci_hcd *xhci,
*/
xhci_dbg(xhci, "underrun event on endpoint\n");
if (!list_empty(&ep_ring->td_list))
- xhci_dbg(xhci, "Underrun Event for slot %d ep %d "
- "still with TDs queued?\n",
- TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
- ep_index);
- goto cleanup;
+ xhci_dbg(xhci, "Underrun Event for slot %u ep %d still with TDs queued?\n",
+ slot_id, ep_index);
+ if (ep->skip)
+ break;
+ return 0;
case COMP_RING_OVERRUN:
xhci_dbg(xhci, "overrun event on endpoint\n");
if (!list_empty(&ep_ring->td_list))
- xhci_dbg(xhci, "Overrun Event for slot %d ep %d "
- "still with TDs queued?\n",
- TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
- ep_index);
- goto cleanup;
+ xhci_dbg(xhci, "Overrun Event for slot %u ep %d still with TDs queued?\n",
+ slot_id, ep_index);
+ if (ep->skip)
+ break;
+ return 0;
case COMP_MISSED_SERVICE_ERROR:
/*
* When encounter missed service error, one or more isoc tds
@@ -2781,13 +2770,13 @@ static int handle_tx_event(struct xhci_hcd *xhci,
xhci_dbg(xhci,
"Miss service interval error for slot %u ep %u, set skip flag\n",
slot_id, ep_index);
- goto cleanup;
+ return 0;
case COMP_NO_PING_RESPONSE_ERROR:
ep->skip = true;
xhci_dbg(xhci,
"No Ping response error for slot %u ep %u, Skip one Isoc TD\n",
slot_id, ep_index);
- goto cleanup;
+ return 0;
case COMP_INCOMPATIBLE_DEVICE_ERROR:
/* needs disable slot command to recover */
@@ -2804,7 +2793,9 @@ static int handle_tx_event(struct xhci_hcd *xhci,
xhci_warn(xhci,
"ERROR Unknown event condition %u for slot %u ep %u , HC probably busted\n",
trb_comp_code, slot_id, ep_index);
- goto cleanup;
+ if (ep->skip)
+ break;
+ return 0;
}
do {
@@ -2823,9 +2814,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if (!(trb_comp_code == COMP_STOPPED ||
trb_comp_code == COMP_STOPPED_LENGTH_INVALID ||
ep_ring->last_td_was_short)) {
- xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n",
- TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
- ep_index);
+ xhci_warn(xhci, "WARN Event TRB for slot %u ep %d with no TDs queued?\n",
+ slot_id, ep_index);
}
if (ep->skip) {
ep->skip = false;
@@ -2838,7 +2828,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
xhci_handle_halted_endpoint(xhci, ep, NULL,
EP_HARD_RESET);
}
- goto cleanup;
+ return 0;
}
/* We've skipped all the TDs on the ep ring when ep->skip set */
@@ -2846,7 +2836,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
ep->skip = false;
xhci_dbg(xhci, "All tds on the ep_ring skipped. Clear skip flag for slot %u ep %u.\n",
slot_id, ep_index);
- goto cleanup;
+ return 0;
}
td = list_first_entry(&ep_ring->td_list, struct xhci_td,
@@ -2855,8 +2845,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
td_num--;
/* Is this a TRB in the currently executing TD? */
- ep_seg = trb_in_td(xhci, td->start_seg, td->first_trb,
- td->last_trb, ep_trb_dma, false);
+ ep_seg = trb_in_td(xhci, td, ep_trb_dma, false);
/*
* Skip the Force Stopped Event. The event_trb(event_dma) of FSE
@@ -2868,14 +2857,14 @@ static int handle_tx_event(struct xhci_hcd *xhci,
*/
if (!ep_seg && (trb_comp_code == COMP_STOPPED ||
trb_comp_code == COMP_STOPPED_LENGTH_INVALID)) {
- goto cleanup;
+ continue;
}
if (!ep_seg) {
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
skip_isoc_td(xhci, td, ep, status);
- goto cleanup;
+ continue;
}
/*
@@ -2885,7 +2874,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) &&
ep_ring->last_td_was_short) {
ep_ring->last_td_was_short = false;
- goto cleanup;
+ return 0;
}
/*
@@ -2903,8 +2892,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
!list_is_last(&td->td_list, &ep_ring->td_list)) {
struct xhci_td *td_next = list_next_entry(td, td_list);
- ep_seg = trb_in_td(xhci, td_next->start_seg, td_next->first_trb,
- td_next->last_trb, ep_trb_dma, false);
+ ep_seg = trb_in_td(xhci, td_next, ep_trb_dma, false);
if (ep_seg) {
/* give back previous TD, start handling new */
xhci_dbg(xhci, "Missing TD completion event after mid TD error\n");
@@ -2923,8 +2911,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
"part of current TD ep_index %d "
"comp_code %u\n", ep_index,
trb_comp_code);
- trb_in_td(xhci, td->start_seg, td->first_trb,
- td->last_trb, ep_trb_dma, true);
+ trb_in_td(xhci, td, ep_trb_dma, true);
+
return -ESHUTDOWN;
}
}
@@ -2960,30 +2948,24 @@ static int handle_tx_event(struct xhci_hcd *xhci,
trb_comp_code))
xhci_handle_halted_endpoint(xhci, ep, td,
EP_HARD_RESET);
- goto cleanup;
- }
-
- td->status = status;
-
- /* update the urb's actual_length and give back to the core */
- if (usb_endpoint_xfer_control(&td->urb->ep->desc))
- process_ctrl_td(xhci, ep, ep_ring, td, ep_trb, event);
- else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
- process_isoc_td(xhci, ep, ep_ring, td, ep_trb, event);
- else
- process_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event);
-cleanup:
- handling_skipped_tds = ep->skip &&
- trb_comp_code != COMP_MISSED_SERVICE_ERROR &&
- trb_comp_code != COMP_NO_PING_RESPONSE_ERROR;
+ } else {
+ td->status = status;
+ /* update the urb's actual_length and give back to the core */
+ if (usb_endpoint_xfer_control(&td->urb->ep->desc))
+ process_ctrl_td(xhci, ep, ep_ring, td, ep_trb, event);
+ else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
+ process_isoc_td(xhci, ep, ep_ring, td, ep_trb, event);
+ else
+ process_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event);
+ }
/*
* If ep->skip is set, it means there are missed tds on the
* endpoint ring need to take care of.
* Process them as short transfer until reach the td pointed by
* the event.
*/
- } while (handling_skipped_tds);
+ } while (ep->skip);
return 0;