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>
This commit is contained in:
Mathias Nyman 2018-09-20 19:13:37 +03:00 committed by Greg Kroah-Hartman
parent c94d41e9dd
commit f8f80be501
2 changed files with 21 additions and 0 deletions

View File

@ -1155,6 +1155,10 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
/* Clear our internal halted state */ /* Clear our internal halted state */
xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED; 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, static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id,
@ -2132,10 +2136,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, union xhci_trb *ep_trb, struct xhci_transfer_event *event,
struct xhci_virt_ep *ep, int *status) struct xhci_virt_ep *ep, int *status)
{ {
struct xhci_slot_ctx *slot_ctx;
struct xhci_ring *ep_ring; struct xhci_ring *ep_ring;
u32 trb_comp_code; u32 trb_comp_code;
u32 remaining, requested, ep_trb_len; 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)); 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)); trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
@ -2144,6 +2154,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
switch (trb_comp_code) { switch (trb_comp_code) {
case COMP_SUCCESS: case COMP_SUCCESS:
ep_ring->err_count = 0;
/* handle success with untransferred data as short packet */ /* handle success with untransferred data as short packet */
if (ep_trb != td->last_trb || remaining) { if (ep_trb != td->last_trb || remaining) {
xhci_warn(xhci, "WARN Successful completion on short TX\n"); xhci_warn(xhci, "WARN Successful completion on short TX\n");
@ -2167,6 +2178,14 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
ep_trb_len = 0; ep_trb_len = 0;
remaining = 0; remaining = 0;
break; 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: default:
/* do nothing */ /* do nothing */
break; break;

View File

@ -1496,6 +1496,7 @@ static inline const char *xhci_trb_type_string(u8 type)
/* How much data is left before the 64KB boundary? */ /* How much data is left before the 64KB boundary? */
#define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr) (TRB_MAX_BUFF_SIZE - \ #define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr) (TRB_MAX_BUFF_SIZE - \
(addr & (TRB_MAX_BUFF_SIZE - 1))) (addr & (TRB_MAX_BUFF_SIZE - 1)))
#define MAX_SOFT_RETRY 3
struct xhci_segment { struct xhci_segment {
union xhci_trb *trbs; union xhci_trb *trbs;
@ -1583,6 +1584,7 @@ struct xhci_ring {
* if we own the TRB (if we are the consumer). See section 4.9.1. * if we own the TRB (if we are the consumer). See section 4.9.1.
*/ */
u32 cycle_state; u32 cycle_state;
unsigned int err_count;
unsigned int stream_id; unsigned int stream_id;
unsigned int num_segs; unsigned int num_segs;
unsigned int num_trbs_free; unsigned int num_trbs_free;