summaryrefslogtreecommitdiff
path: root/drivers/usb/host/xhci-ring.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-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)