summaryrefslogtreecommitdiff
path: root/drivers/usb/host/xhci-ring.c
diff options
context:
space:
mode:
authorAndiry Xu <andiry.xu@amd.com>2012-03-05 17:49:37 +0800
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2012-03-13 09:30:24 -0700
commit8dfec6140fc617b932cf9a09ba46d0ee3f3a7d87 (patch)
tree0929c51b289a54c7ac11e6919ff1f9ae737535ff /drivers/usb/host/xhci-ring.c
parent186a7ef13a8fa3bc7cca1ccd33bd469b931e46de (diff)
xHCI: dynamic ring expansion
If room_on_ring() check fails, try to expand the ring and check again. When expand a ring, use a cached ring or allocate new segments, link the original ring and the new ring or segments, update the original ring's segment numbers and the last segment pointer. Signed-off-by: Andiry Xu <andiry.xu@amd.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Tested-by: Paul Zimmerman <Paul.Zimmerman@synopsys.com>
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-rw-r--r--drivers/usb/host/xhci-ring.c33
1 files changed, 28 insertions, 5 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index f9b6fa364f22..4194f348e3a7 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2490,6 +2490,8 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
u32 ep_state, unsigned int num_trbs, gfp_t mem_flags)
{
+ unsigned int num_trbs_needed;
+
/* Make sure the endpoint has been added to xHC schedule */
switch (ep_state) {
case EP_STATE_DISABLED:
@@ -2517,11 +2519,32 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
*/
return -EINVAL;
}
- if (!room_on_ring(xhci, ep_ring, num_trbs)) {
- /* FIXME allocate more room */
- xhci_err(xhci, "ERROR no room on ep ring\n");
- return -ENOMEM;
- }
+
+ while (1) {
+ if (room_on_ring(xhci, ep_ring, num_trbs))
+ break;
+
+ if (ep_ring == xhci->cmd_ring) {
+ xhci_err(xhci, "Do not support expand command ring\n");
+ return -ENOMEM;
+ }
+
+ if (ep_ring->enq_seg == ep_ring->deq_seg &&
+ ep_ring->dequeue > ep_ring->enqueue) {
+ xhci_err(xhci, "Can not expand the ring while dequeue "
+ "pointer has not passed the link TRB\n");
+ return -ENOMEM;
+ }
+
+ xhci_dbg(xhci, "ERROR no room on ep ring, "
+ "try ring expansion\n");
+ num_trbs_needed = num_trbs - ep_ring->num_trbs_free;
+ if (xhci_ring_expansion(xhci, ep_ring, num_trbs_needed,
+ mem_flags)) {
+ xhci_err(xhci, "Ring expansion failed\n");
+ return -ENOMEM;
+ }
+ };
if (enqueue_is_link_trb(ep_ring)) {
struct xhci_ring *ring = ep_ring;