Commit b008df60 authored by Andiry Xu's avatar Andiry Xu Committed by Sarah Sharp

xHCI: count free TRBs on transfer ring

In the past, the room_on_ring() check was implemented by walking all over
the ring, which is wasteful and complicated.

Count the number of free TRBs instead. The free TRBs number should be
updated when enqueue/dequeue pointer is updated, or upon the completion
of a set dequeue pointer command.
Signed-off-by: default avatarAndiry Xu <andiry.xu@amd.com>
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
Tested-by: default avatarPaul Zimmerman <Paul.Zimmerman@synopsys.com>
parent 3fe4fe08
...@@ -135,6 +135,12 @@ static void xhci_initialize_ring_info(struct xhci_ring *ring) ...@@ -135,6 +135,12 @@ static void xhci_initialize_ring_info(struct xhci_ring *ring)
/* Not necessary for new rings, but needed for re-initialized rings */ /* Not necessary for new rings, but needed for re-initialized rings */
ring->enq_updates = 0; ring->enq_updates = 0;
ring->deq_updates = 0; ring->deq_updates = 0;
/*
* Each segment has a link TRB, and leave an extra TRB for SW
* accounting purpose
*/
ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1;
} }
/** /**
......
...@@ -145,10 +145,17 @@ static void next_trb(struct xhci_hcd *xhci, ...@@ -145,10 +145,17 @@ static void next_trb(struct xhci_hcd *xhci,
*/ */
static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
{ {
union xhci_trb *next = ++(ring->dequeue); union xhci_trb *next;
unsigned long long addr; unsigned long long addr;
ring->deq_updates++; ring->deq_updates++;
/* If this is not event ring, there is one more usable TRB */
if (ring->type != TYPE_EVENT &&
!last_trb(xhci, ring, ring->deq_seg, ring->dequeue))
ring->num_trbs_free++;
next = ++(ring->dequeue);
/* Update the dequeue pointer further if that was a link TRB or we're at /* Update the dequeue pointer further if that was a link TRB or we're at
* the end of an event ring segment (which doesn't have link TRBS) * the end of an event ring segment (which doesn't have link TRBS)
*/ */
...@@ -189,6 +196,10 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, ...@@ -189,6 +196,10 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
unsigned long long addr; unsigned long long addr;
chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN; chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
/* If this is not event ring, there is one less usable TRB */
if (ring->type != TYPE_EVENT &&
!last_trb(xhci, ring, ring->enq_seg, ring->enqueue))
ring->num_trbs_free--;
next = ++(ring->enqueue); next = ++(ring->enqueue);
ring->enq_updates++; ring->enq_updates++;
...@@ -240,54 +251,14 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, ...@@ -240,54 +251,14 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
/* /*
* Check to see if there's room to enqueue num_trbs on the ring. See rules * Check to see if there's room to enqueue num_trbs on the ring. See rules
* above. * above.
* FIXME: this would be simpler and faster if we just kept track of the number
* of free TRBs in a ring.
*/ */
static int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring, static inline int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring,
unsigned int num_trbs) unsigned int num_trbs)
{ {
int i; if (ring->num_trbs_free >= num_trbs)
union xhci_trb *enq = ring->enqueue;
struct xhci_segment *enq_seg = ring->enq_seg;
struct xhci_segment *cur_seg;
unsigned int left_on_ring;
/* If we are currently pointing to a link TRB, advance the
* enqueue pointer before checking for space */
while (last_trb(xhci, ring, enq_seg, enq)) {
enq_seg = enq_seg->next;
enq = enq_seg->trbs;
}
/* Check if ring is empty */
if (enq == ring->dequeue) {
/* Can't use link trbs */
left_on_ring = TRBS_PER_SEGMENT - 1;
for (cur_seg = enq_seg->next; cur_seg != enq_seg;
cur_seg = cur_seg->next)
left_on_ring += TRBS_PER_SEGMENT - 1;
/* Always need one TRB free in the ring. */
left_on_ring -= 1;
if (num_trbs > left_on_ring) {
xhci_warn(xhci, "Not enough room on ring; "
"need %u TRBs, %u TRBs left\n",
num_trbs, left_on_ring);
return 0;
}
return 1; return 1;
}
/* Make sure there's an extra empty TRB available */ return 0;
for (i = 0; i <= num_trbs; ++i) {
if (enq == ring->dequeue)
return 0;
enq++;
while (last_trb(xhci, ring, enq_seg, enq)) {
enq_seg = enq_seg->next;
enq = enq_seg->trbs;
}
}
return 1;
} }
/* Ring the host controller doorbell after placing a command on the ring */ /* Ring the host controller doorbell after placing a command on the ring */
...@@ -893,6 +864,43 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg) ...@@ -893,6 +864,43 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
xhci_dbg(xhci, "xHCI host controller is dead.\n"); xhci_dbg(xhci, "xHCI host controller is dead.\n");
} }
static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
struct xhci_virt_device *dev,
struct xhci_ring *ep_ring,
unsigned int ep_index)
{
union xhci_trb *dequeue_temp;
int num_trbs_free_temp;
bool revert = false;
num_trbs_free_temp = ep_ring->num_trbs_free;
dequeue_temp = ep_ring->dequeue;
while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) {
/* We have more usable TRBs */
ep_ring->num_trbs_free++;
ep_ring->dequeue++;
if (last_trb(xhci, ep_ring, ep_ring->deq_seg,
ep_ring->dequeue)) {
if (ep_ring->dequeue ==
dev->eps[ep_index].queued_deq_ptr)
break;
ep_ring->deq_seg = ep_ring->deq_seg->next;
ep_ring->dequeue = ep_ring->deq_seg->trbs;
}
if (ep_ring->dequeue == dequeue_temp) {
revert = true;
break;
}
}
if (revert) {
xhci_dbg(xhci, "Unable to find new dequeue pointer\n");
ep_ring->num_trbs_free = num_trbs_free_temp;
}
}
/* /*
* When we get a completion for a Set Transfer Ring Dequeue Pointer command, * When we get a completion for a Set Transfer Ring Dequeue Pointer command,
* we need to clear the set deq pending flag in the endpoint ring state, so that * we need to clear the set deq pending flag in the endpoint ring state, so that
...@@ -974,8 +982,8 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, ...@@ -974,8 +982,8 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
/* Update the ring's dequeue segment and dequeue pointer /* Update the ring's dequeue segment and dequeue pointer
* to reflect the new position. * to reflect the new position.
*/ */
ep_ring->deq_seg = dev->eps[ep_index].queued_deq_seg; update_ring_for_set_deq_completion(xhci, dev,
ep_ring->dequeue = dev->eps[ep_index].queued_deq_ptr; ep_ring, ep_index);
} else { } else {
xhci_warn(xhci, "Mismatch between completed Set TR Deq " xhci_warn(xhci, "Mismatch between completed Set TR Deq "
"Ptr command & xHCI internal state.\n"); "Ptr command & xHCI internal state.\n");
...@@ -3407,6 +3415,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ...@@ -3407,6 +3415,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
ep_ring->enqueue = urb_priv->td[0]->first_trb; ep_ring->enqueue = urb_priv->td[0]->first_trb;
ep_ring->enq_seg = urb_priv->td[0]->start_seg; ep_ring->enq_seg = urb_priv->td[0]->start_seg;
ep_ring->cycle_state = start_cycle; ep_ring->cycle_state = start_cycle;
ep_ring->num_trbs_free = ep_ring->num_trbs_free_temp;
usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb); usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
return ret; return ret;
} }
...@@ -3479,6 +3488,8 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, ...@@ -3479,6 +3488,8 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
urb->dev->speed == USB_SPEED_FULL) urb->dev->speed == USB_SPEED_FULL)
urb->interval /= 8; urb->interval /= 8;
} }
ep_ring->num_trbs_free_temp = ep_ring->num_trbs_free;
return xhci_queue_isoc_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); return xhci_queue_isoc_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index);
} }
......
...@@ -729,6 +729,7 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci) ...@@ -729,6 +729,7 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci)
ring->enq_seg = ring->deq_seg; ring->enq_seg = ring->deq_seg;
ring->enqueue = ring->dequeue; ring->enqueue = ring->dequeue;
ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1;
/* /*
* Ring is now zeroed, so the HW should look for change of ownership * Ring is now zeroed, so the HW should look for change of ownership
* when the cycle bit is set to 1. * when the cycle bit is set to 1.
......
...@@ -1278,6 +1278,8 @@ struct xhci_ring { ...@@ -1278,6 +1278,8 @@ struct xhci_ring {
u32 cycle_state; u32 cycle_state;
unsigned int stream_id; unsigned int stream_id;
unsigned int num_segs; unsigned int num_segs;
unsigned int num_trbs_free;
unsigned int num_trbs_free_temp;
enum xhci_ring_type type; enum xhci_ring_type type;
bool last_td_was_short; bool last_td_was_short;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment