summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYe Li <ye.li@nxp.com>2020-10-12 12:31:11 +0300
committerIgor Opaniuk <igor.opanyuk@toradex.com>2020-10-13 10:21:40 +0000
commit6b20f7e0126e3577b10369a33f1031ef9f5af830 (patch)
tree18b45990f5ff3007e8a1d07249d0fa08638e99d2
parentfa5dd193b38b8ebe6213c4f77c3b9196c9d4a402 (diff)
MLK-17348 xhci: Reset endpoint when endpoint is halted
When testing a poor USB disk "Transcend JetFlash XPGMC7W5 ", sometime we will get TRB TX error during getting string descriptors, then the usb process aborts due to the context state changed to halted. Actually when the endpoint context state is halted, we can send reset endpoint command to put it to stopped state, then set TR dequeue pointer for following doorbell ring to re-run it. This patch adds this error recovery mechanism. Signed-off-by: Ye Li <ye.li@nxp.com>
-rw-r--r--drivers/usb/host/xhci-ring.c48
1 files changed, 43 insertions, 5 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 3cd6c8a0dc..431e9a29c1 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -474,6 +474,29 @@ union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected)
BUG();
}
+static void reset_ep(struct usb_device *udev, int ep_index)
+{
+ struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
+ struct xhci_ring *ring = ctrl->devs[udev->slot_id]->eps[ep_index].ring;
+ union xhci_trb *event;
+
+ xhci_queue_command(ctrl, NULL, udev->slot_id, ep_index, TRB_RESET_EP);
+
+ event = xhci_wait_for_event(ctrl, TRB_COMPLETION);
+ BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags))
+ != udev->slot_id || GET_COMP_CODE(le32_to_cpu(
+ event->event_cmd.status)) != COMP_SUCCESS);
+ xhci_acknowledge_event(ctrl);
+
+ xhci_queue_command(ctrl, (void *)((uintptr_t)ring->enqueue |
+ ring->cycle_state), udev->slot_id, ep_index, TRB_SET_DEQ);
+ event = xhci_wait_for_event(ctrl, TRB_COMPLETION);
+ BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags))
+ != udev->slot_id || GET_COMP_CODE(le32_to_cpu(
+ event->event_cmd.status)) != COMP_SUCCESS);
+ xhci_acknowledge_event(ctrl);
+}
+
/*
* Stops transfer processing for an endpoint and throws away all unprocessed
* TRBs by setting the xHC's dequeue pointer to our enqueue pointer. The next
@@ -561,6 +584,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
int start_cycle;
u32 field = 0;
u32 length_field = 0;
+ u32 ep_state;
struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
int slot_id = udev->slot_id;
int ep_index;
@@ -618,12 +642,20 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
* prepare_trasfer() as there in 'Linux' since we are not
* maintaining multiple TDs/transfer at the same time.
*/
- ret = prepare_ring(ctrl, ring,
- le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK);
+ ep_state = le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK;
+ ret = prepare_ring(ctrl, ring, ep_state);
+
if (ret < 0)
return ret;
/*
+ * For halted EP, reset it to stopped state and set
+ * TR Dequeue Pointer
+ */
+ if (ep_state == EP_STATE_HALTED)
+ reset_ep(udev, ep_index);
+
+ /*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
* until we've finished creating all the other TRBs. The ring's cycle
* state may change as we enqueue the other TRBs, so save it too.
@@ -753,6 +785,7 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
int num_trbs;
u32 field;
u32 length_field;
+ u32 ep_state;
u64 buf_64 = 0;
struct xhci_generic_trb *start_trb;
struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
@@ -804,11 +837,16 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
* prepare_trasfer() as there in 'Linux' since we are not
* maintaining multiple TDs/transfer at the same time.
*/
- ret = prepare_ring(ctrl, ep_ring,
- le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK);
-
+ ep_state = le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK;
+ ret = prepare_ring(ctrl, ep_ring, ep_state);
if (ret < 0)
return ret;
+ /*
+ * For halted EP, reset it to stopped state and
+ * set TR Dequeue Pointer
+ */
+ if (ep_state == EP_STATE_HALTED)
+ reset_ep(udev, ep_index);
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)