usb: xhci-msm-hsic: Reset transfer ring upon bus suspend

HW TR dequeue pointer gets stuck to a No-Op TRB while aborting
ongoing transfer during function suspend. This is triggering
frequent ring expansion and eventually system goes out of
dma memory pool. Workaround this issue by resetting HW TR dequeue
pointer to the trb of first segment of the transfer ring. Also,
reset SW enqueue and dequeue pointers to the same trb.

CRs-Fixed: 556954
Change-Id: I36d3a738953b13b7d54fff3275395c03ab00a61c
Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
This commit is contained in:
Hemant Kumar 2013-11-19 16:49:50 -08:00
parent 702027a1b4
commit 0a84e4a6f6
5 changed files with 94 additions and 1 deletions

View File

@ -259,6 +259,83 @@ int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
return slot_id;
}
/*
* For ep 0 to 30 it resets sw enq and deq pointers to the first seg trb of the
* ring and issues set tr deq cmd to reset hw deq pointers to the first seg trb
* of the ring. And wait for the last command to complete.
*/
static int xhci_reset_xfer_ring(struct xhci_hcd *xhci, int slot_id)
{
struct xhci_virt_device *virt_dev;
struct xhci_virt_ep *ep;
struct xhci_dequeue_state deq_state;
struct xhci_ep_ctx *ep_ctx;
struct xhci_command *cmd;
unsigned long flags;
unsigned int ep_state;
int i;
int timeleft;
int ret;
virt_dev = xhci->devs[slot_id];
cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO);
if (!cmd) {
xhci_dbg(xhci, "Couldn't allocate command structure.\n");
return -ENOMEM;
}
spin_lock_irqsave(&xhci->lock, flags);
for (i = LAST_EP_INDEX; i >= 0; i--) {
ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->out_ctx, i);
ep_state = ep_ctx->ep_info & cpu_to_le32(EP_STATE_MASK);
ep = &virt_dev->eps[i];
if (ep->ring && ep->ring->dequeue
&& ep_state == cpu_to_le32(EP_STATE_STOPPED)
&& ep->ring->enqueue != ep->ring->dequeue) {
ep->stopped_td = NULL;
ep->stopped_trb = NULL;
xhci_reinit_cached_ring(xhci, ep->ring, 1,
ep->ring->type);
memset(&deq_state, 0, sizeof(deq_state));
deq_state.new_deq_ptr = ep->ring->first_seg->trbs;
deq_state.new_deq_seg = ep->ring->first_seg;
deq_state.new_cycle_state = 0x1;
cmd->command_trb =
xhci_find_next_enqueue(xhci->cmd_ring);
xhci_queue_new_dequeue_state(xhci,
slot_id, i, ep->ring->stream_id,
&deq_state);
}
}
if (!cmd->command_trb) {
spin_unlock_irqrestore(&xhci->lock, flags);
goto command_cleanup;
}
list_add_tail(&cmd->cmd_list, &virt_dev->cmd_list);
xhci_ring_cmd_db(xhci);
spin_unlock_irqrestore(&xhci->lock, flags);
/* Wait for last set tr deq command to finish */
timeleft = wait_for_completion_interruptible_timeout(
cmd->completion,
XHCI_CMD_DEFAULT_TIMEOUT);
if (timeleft <= 0) {
xhci_warn(xhci, "%s while waiting for set tr deq command\n",
timeleft == 0 ? "Timeout" : "Signal");
spin_lock_irqsave(&xhci->lock, flags);
if (cmd->cmd_list.next != LIST_POISON1)
list_del(&cmd->cmd_list);
spin_unlock_irqrestore(&xhci->lock, flags);
ret = -ETIME;
}
command_cleanup:
xhci_free_command(xhci, cmd);
return ret;
}
/*
* Stop device
* It issues stop endpoint command for EP 0 to 30. And wait the last command
@ -1257,6 +1334,8 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
port_index + 1);
if (slot_id) {
spin_unlock_irqrestore(&xhci->lock, flags);
if (xhci->quirks & XHCI_TR_DEQ_RESET_QUIRK)
xhci_reset_xfer_ring(xhci, slot_id);
xhci_stop_device(xhci, slot_id, 1);
spin_lock_irqsave(&xhci->lock, flags);
}

View File

@ -294,7 +294,7 @@ void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci,
/* Zero an endpoint ring (except for link TRBs) and move the enqueue and dequeue
* pointers to the beginning of the ring.
*/
static void xhci_reinit_cached_ring(struct xhci_hcd *xhci,
void xhci_reinit_cached_ring(struct xhci_hcd *xhci,
struct xhci_ring *ring, unsigned int cycle_state,
enum xhci_ring_type type)
{

View File

@ -466,6 +466,13 @@ static void mxhci_hsic_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
if (mxhci->wakeup_irq)
xhci->quirks |= XHCI_NO_SELECTIVE_SUSPEND;
/*
* Observing hw tr deq pointer getting stuck to a noop trb
* when aborting transfer during suspend. Reset tr deq pointer
* to start of the first seg of the xfer ring.
*/
xhci->quirks |= XHCI_TR_DEQ_RESET_QUIRK;
if (!pdata)
return;
if (pdata->vendor == SYNOPSIS_DWC3_VENDOR &&

View File

@ -1146,6 +1146,9 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING;
dev->eps[ep_index].queued_deq_seg = NULL;
dev->eps[ep_index].queued_deq_ptr = NULL;
handle_cmd_in_cmd_wait_list(xhci, dev, event);
/* Restart any rings with pending URBs */
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
}

View File

@ -1553,6 +1553,7 @@ struct xhci_hcd {
*/
#define XHCI_TR_DEQ_ERR_QUIRK (1 << 19)
#define XHCI_NO_SELECTIVE_SUSPEND (1 << 20)
#define XHCI_TR_DEQ_RESET_QUIRK (1 << 21)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@ -1730,6 +1731,9 @@ void xhci_slot_copy(struct xhci_hcd *xhci,
int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev,
struct usb_device *udev, struct usb_host_endpoint *ep,
gfp_t mem_flags);
void xhci_reinit_cached_ring(struct xhci_hcd *xhci,
struct xhci_ring *ring, unsigned int cycle_state,
enum xhci_ring_type type);
void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring);
int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
unsigned int num_trbs, gfp_t flags);