diff options
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 48 |
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) |