UPSTREAM: xhci: Use soft retry to recover faster from transaction errors Use soft retry to recover from a USB Transaction Errors that are caused by temporary error conditions. The USB device is not aware that the xHC has halted the endpoint, and will be waiting for another retry A Soft Retry perform additional retries and recover from an error which has caused the xHC to halt an endpoint. Soft retry has some limitations: Soft Retry attempts shall not be performed on Isoch endpoints Soft Retry attempts shall not be performed if the device is behind a TT in a HS Hub Software shall limit the number of unsuccessful Soft Retry attempts to prevent an infinite loop. For more details on Soft retry see xhci specs 4.6.8.1 Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> (cherry picked from commit f8f80be501aa2f10669585c3e328fad079d8cb3a) Signed-off-by: Raghuram Hegde <raghuram.hegde@intel.com> Signed-off-by: Chethan T N <chethan.tumkur.narayan@intel.com> Signed-off-by: Benson Leung <bleung@chromium.org> BUG=b:123767576 TEST=Power up Chromebook and connect to a LE device. Connection shall succeed. Change-Id: Id79d10f6a49329a7c7aa5cfe48a7f271215e8b60 Reviewed-on: https://chromium-review.googlesource.com/1522767 Commit-Ready: Benson Leung <bleung@chromium.org> Tested-by: Benson Leung <bleung@chromium.org> Reviewed-by: Sean Paul <seanpaul@chromium.org> Reviewed-by: Chethan Tumkur Narayan <chethan.tumkur.narayan@intel.corp-partner.google.com> Reviewed-by: Todd Broch <tbroch@chromium.org> (cherry picked from commit dbace4e4145352938cabe88910ba5de00fe9b8e6) Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/1544369 Reviewed-by: Yoni Shavit <yshavit@chromium.org> Tested-by: Yoni Shavit <yshavit@chromium.org>
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 96fbe8d..6e0213f 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c
@@ -1169,6 +1169,10 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, /* Clear our internal halted state */ xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED; } + + /* if this was a soft reset, then restart */ + if ((le32_to_cpu(trb->generic.field[3])) & TRB_TSP) + ring_doorbell_for_active_rings(xhci, slot_id, ep_index); } static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id, @@ -2257,10 +2261,16 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, union xhci_trb *ep_trb, struct xhci_transfer_event *event, struct xhci_virt_ep *ep, int *status) { + struct xhci_slot_ctx *slot_ctx; struct xhci_ring *ep_ring; u32 trb_comp_code; u32 remaining, requested, ep_trb_len; + unsigned int slot_id; + int ep_index; + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); + slot_ctx = xhci_get_slot_ctx(xhci, xhci->devs[slot_id]->out_ctx); + ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1; ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); @@ -2269,6 +2279,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, switch (trb_comp_code) { case COMP_SUCCESS: + ep_ring->err_count = 0; /* handle success with untransferred data as short packet */ if (ep_trb != td->last_trb || remaining) { xhci_warn(xhci, "WARN Successful completion on short TX\n"); @@ -2292,6 +2303,14 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, ep_trb_len = 0; remaining = 0; break; + case COMP_USB_TRANSACTION_ERROR: + if ((ep_ring->err_count++ > MAX_SOFT_RETRY) || + le32_to_cpu(slot_ctx->tt_info) & TT_SLOT) + break; + *status = 0; + xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index, + ep_ring->stream_id, td, EP_SOFT_RESET); + return 0; default: /* do nothing */ break;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 78e3f9d..4baf0a5 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h
@@ -1502,6 +1502,7 @@ static inline const char *xhci_trb_type_string(u8 type) /* How much data is left before the 64KB boundary? */ #define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr) (TRB_MAX_BUFF_SIZE - \ (addr & (TRB_MAX_BUFF_SIZE - 1))) +#define MAX_SOFT_RETRY 3 struct xhci_segment { union xhci_trb *trbs; @@ -1589,6 +1590,7 @@ struct xhci_ring { * if we own the TRB (if we are the consumer). See section 4.9.1. */ u32 cycle_state; + unsigned int err_count; unsigned int stream_id; unsigned int num_segs; unsigned int num_trbs_free;