summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHemant Kumar <hemantk@codeaurora.org>2016-10-31 19:22:47 -0700
committerHemant Kumar <hemantk@codeaurora.org>2016-11-03 16:53:45 -0700
commit2a1d099062cf4b4987913b0dafc368f2309b834c (patch)
treeaaf9488c96a7d728fe0ad51ed4f4d5ff3d0faacb
parent758693b4a6d94a0724081578d24f6ba1cc449255 (diff)
usb: xhci: Clear event handler busy flag upon event ring cleanup
There is a possibility of unacknowledged pending interrupt at the time of secondary event ring cleanup. As a result xHC does not generate any interrupt further because event handler busy flag is set. Hence disable interrupter to prevent any further interrupt, acknowledge any pending interrupt and clear event handler busy flag. This allows xHC to generate interrupt after setting up new event ring. Change-Id: I1b1791eacbf7afffd101d56d0ae06fe237b8c076 Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
-rw-r--r--drivers/usb/host/xhci-mem.c30
1 files changed, 28 insertions, 2 deletions
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index ac298e632d73..29dc6ab252b1 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1789,6 +1789,8 @@ void xhci_free_command(struct xhci_hcd *xhci,
int xhci_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned intr_num)
{
int size;
+ u32 iman_reg;
+ u64 erdp_reg;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct device *dev = xhci_to_hcd(xhci)->self.controller;
@@ -1800,14 +1802,38 @@ int xhci_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned intr_num)
size =
sizeof(struct xhci_erst_entry)*(xhci->sec_erst[intr_num].num_entries);
- if (xhci->sec_erst[intr_num].entries)
+ if (xhci->sec_erst[intr_num].entries) {
+ /*
+ * disable irq, ack pending interrupt and clear EHB for xHC to
+ * generate interrupt again when new event ring is setup
+ */
+ iman_reg =
+ readl_relaxed(&xhci->sec_ir_set[intr_num]->irq_pending);
+ iman_reg &= ~IMAN_IE;
+ writel_relaxed(iman_reg,
+ &xhci->sec_ir_set[intr_num]->irq_pending);
+ iman_reg =
+ readl_relaxed(&xhci->sec_ir_set[intr_num]->irq_pending);
+ if (iman_reg & IMAN_IP)
+ writel_relaxed(iman_reg,
+ &xhci->sec_ir_set[intr_num]->irq_pending);
+ /* make sure IP gets cleared before clearing EHB */
+ mb();
+
+ erdp_reg = xhci_read_64(xhci,
+ &xhci->sec_ir_set[intr_num]->erst_dequeue);
+ xhci_write_64(xhci, erdp_reg | ERST_EHB,
+ &xhci->sec_ir_set[intr_num]->erst_dequeue);
+
dma_free_coherent(dev, size, xhci->sec_erst[intr_num].entries,
xhci->sec_erst[intr_num].erst_dma_addr);
- xhci->sec_erst[intr_num].entries = NULL;
+ xhci->sec_erst[intr_num].entries = NULL;
+ }
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed SEC ERST#%d",
intr_num);
if (xhci->sec_event_ring[intr_num])
xhci_ring_free(xhci, xhci->sec_event_ring[intr_num]);
+
xhci->sec_event_ring[intr_num] = NULL;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Freed sec event ring");