diff options
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 73 |
1 files changed, 39 insertions, 34 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index b7517c3c8059..4d664ba53fe9 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1399,6 +1399,20 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, struct xhci_stream_ctx *ctx = &ep->stream_info->stream_ctx_array[stream_id]; deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK; + + /* + * Cadence xHCI controllers store some endpoint state + * information within Rsvd0 fields of Stream Endpoint + * context. This field is not cleared during Set TR + * Dequeue Pointer command which causes XDMA to skip + * over transfer ring and leads to data loss on stream + * pipe. + * To fix this issue driver must clear Rsvd0 field. + */ + if (xhci->quirks & XHCI_CDNS_SCTX_QUIRK) { + ctx->reserved[0] = 0; + ctx->reserved[1] = 0; + } } else { deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK; } @@ -2521,9 +2535,6 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->status = 0; break; case COMP_SHORT_PACKET: - xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n", - td->urb->ep->desc.bEndpointAddress, - requested, remaining); td->status = 0; break; case COMP_STOPPED_SHORT_PACKET: @@ -2764,35 +2775,25 @@ static int handle_tx_event(struct xhci_hcd *xhci, return 0; } - do { - /* This TRB should be in the TD at the head of this ring's - * TD list. + if (list_empty(&ep_ring->td_list)) { + /* + * Don't print wanings if ring is empty due to a stopped endpoint generating an + * extra completion event if the device was suspended. Or, a event for the last TRB + * of a short TD we already got a short event for. The short TD is already removed + * from the TD list. */ - if (list_empty(&ep_ring->td_list)) { - /* - * Don't print wanings if it's due to a stopped endpoint - * generating an extra completion event if the device - * was suspended. Or, a event for the last TRB of a - * short TD we already got a short event for. - * The short TD is already removed from the TD list. - */ - - 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 %u ep %d with no TDs queued?\n", - slot_id, ep_index); - } - if (ep->skip) { - ep->skip = false; - xhci_dbg(xhci, "td_list is empty while skip flag set. Clear skip flag for slot %u ep %u.\n", - slot_id, ep_index); - } - - td = NULL; - goto check_endpoint_halted; + if (trb_comp_code != COMP_STOPPED && + trb_comp_code != COMP_STOPPED_LENGTH_INVALID && + !ep_ring->last_td_was_short) { + xhci_warn(xhci, "Event TRB for slot %u ep %u with no TDs queued\n", + slot_id, ep_index); } + ep->skip = false; + goto check_endpoint_halted; + } + + do { td = list_first_entry(&ep_ring->td_list, struct xhci_td, td_list); @@ -2803,7 +2804,14 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) { skip_isoc_td(xhci, td, ep, status); - continue; + if (!list_empty(&ep_ring->td_list)) + continue; + + xhci_dbg(xhci, "All TDs skipped for slot %u ep %u. Clear skip flag.\n", + slot_id, ep_index); + ep->skip = false; + td = NULL; + goto check_endpoint_halted; } /* @@ -2910,6 +2918,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, process_isoc_td(xhci, ep, ep_ring, td, ep_trb, event); else process_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event); + return 0; check_endpoint_halted: if (xhci_halted_host_endpoint(ep_ctx, trb_comp_code)) @@ -3940,10 +3949,6 @@ static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci, start_frame_id = (start_frame_id >> 3) & 0x7ff; end_frame_id = (end_frame_id >> 3) & 0x7ff; - xhci_dbg(xhci, "%s: index %d, reg 0x%x start_frame_id 0x%x, end_frame_id 0x%x, start_frame 0x%x\n", - __func__, index, readl(&xhci->run_regs->microframe_index), - start_frame_id, end_frame_id, start_frame); - if (start_frame_id < end_frame_id) { if (start_frame > end_frame_id || start_frame < start_frame_id) |