From 3023eb079a414cbd2c3818a22512c084a35ff838 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Mon, 12 Oct 2020 12:31:11 +0300 Subject: 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 --- drivers/usb/host/xhci-ring.c | 48 +++++++++++++++++++++++++++++++++++++++----- 1 file 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,11 +642,19 @@ 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 @@ -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) -- cgit v1.2.3