You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
74 lines
2.6 KiB
74 lines
2.6 KiB
From 38140af8d8644b2ff8dd15fd4c2b60dd2812e96e Mon Sep 17 00:00:00 2001
|
|
|
|
From: Mathias Nyman <mathias.nyman@linux.intel.com>
|
|
|
|
Subject: [PATCH 3/4] xhci: Find out where an endpoint or stream stopped from
|
|
its context.
|
|
|
|
When xHC is asked to stop an endpoint it will save the position it
|
|
stopped on in the endpoint or stream context dequeue field.
|
|
|
|
xhci driver needs to know on what TRB the ring stopped when we
|
|
want to cancel a tranfer because xHC hardware may cache TRBs and we
|
|
can't write over the stopped TRB with a no-op TRB.
|
|
we need to as xHC to hop over it and flush caches.
|
|
|
|
xhci driver used to get the stopped position from a "stopped" transfer
|
|
event got before the stop endpoint command completed, but if the ring
|
|
is already stopped, or in a halted or error state this event is missing.
|
|
|
|
Get the stopped position from the endpoint or stream context instead
|
|
|
|
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
|
|
---
|
|
drivers/usb/host/xhci-ring.c | 19 +++++++++++++++----
|
|
1 file changed, 15 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
|
|
index cfe9072..a7ad61a 100644
|
|
--- a/drivers/usb/host/xhci-ring.c
|
|
+++ b/drivers/usb/host/xhci-ring.c
|
|
@@ -733,7 +733,9 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
|
|
struct list_head *entry;
|
|
struct xhci_td *cur_td = NULL;
|
|
struct xhci_td *last_unlinked_td;
|
|
+ struct xhci_virt_device *vdev;
|
|
|
|
+ u64 hw_deq;
|
|
struct xhci_dequeue_state deq_state;
|
|
|
|
if (unlikely(TRB_TO_SUSPEND_PORT(le32_to_cpu(trb->generic.field[3])))) {
|
|
@@ -746,6 +748,8 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
|
|
|
|
memset(&deq_state, 0, sizeof(deq_state));
|
|
ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
|
|
+
|
|
+ vdev = xhci->devs[slot_id];
|
|
ep = &xhci->devs[slot_id]->eps[ep_index];
|
|
|
|
if (list_empty(&ep->cancelled_td_list)) {
|
|
@@ -789,12 +793,19 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
|
|
* If we stopped on the TD we need to cancel, then we have to
|
|
* move the xHC endpoint ring dequeue pointer past this TD.
|
|
*/
|
|
- if (cur_td == ep->stopped_td)
|
|
+ hw_deq = xhci_get_hw_deq(xhci, vdev, ep_index,
|
|
+ cur_td->urb->stream_id);
|
|
+ hw_deq &= ~0xf;
|
|
+
|
|
+ if (trb_in_td(xhci, cur_td->start_seg, cur_td->first_trb,
|
|
+ cur_td->last_trb, hw_deq, false)) {
|
|
xhci_find_new_dequeue_state(xhci, slot_id, ep_index,
|
|
- cur_td->urb->stream_id,
|
|
- cur_td, &deq_state);
|
|
- else
|
|
+ cur_td->urb->stream_id,
|
|
+ cur_td, &deq_state);
|
|
+ } else {
|
|
td_to_noop(xhci, ep_ring, cur_td, false);
|
|
+ }
|
|
+
|
|
remove_finished_td:
|
|
/*
|
|
* The event handler won't see a completion for this TD anymore,
|