summaryrefslogtreecommitdiff
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-12-20 14:04:31 -0800
committerGerrit - the friendly Code Review server <code-review@localhost>2016-12-20 14:04:30 -0800
commit358f9b3361ae7a8e996515f2e36ac258480cf8d2 (patch)
treedc50c9a2e3d713a8071a206acf0eb44e95125834 /drivers/usb/host
parent8a0ab369eb3c8a36657d1cfea387c736ad7fe36c (diff)
parent13931636c5ce6c6b748a3338593ea8531898a87f (diff)
Merge "usb: xhci: Acknowledge pending events in secondary event ring"
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/xhci-mem.c100
-rw-r--r--drivers/usb/host/xhci-plat.c4
2 files changed, 78 insertions, 26 deletions
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 15ef99f6e52d..373e9d039457 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1789,11 +1789,84 @@ void xhci_free_command(struct xhci_hcd *xhci,
kfree(command);
}
+void xhci_handle_sec_intr_events(struct xhci_hcd *xhci, int intr_num)
+{
+ union xhci_trb *erdp_trb, *current_trb;
+ struct xhci_segment *seg;
+ u64 erdp_reg;
+ u32 iman_reg;
+ dma_addr_t deq;
+ unsigned long segment_offset;
+
+ /* disable irq, ack pending interrupt and ack all pending events */
+
+ 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);
+
+ /* last acked event trb is in erdp reg */
+ erdp_reg =
+ xhci_read_64(xhci, &xhci->sec_ir_set[intr_num]->erst_dequeue);
+ deq = (dma_addr_t)(erdp_reg & ~ERST_PTR_MASK);
+ if (!deq) {
+ pr_debug("%s: event ring handling not required\n", __func__);
+ return;
+ }
+
+ seg = xhci->sec_event_ring[intr_num]->first_seg;
+ segment_offset = deq - seg->dma;
+
+ /* find out virtual address of the last acked event trb */
+ erdp_trb = current_trb = &seg->trbs[0] +
+ (segment_offset/sizeof(*current_trb));
+
+ /* read cycle state of the last acked trb to find out CCS */
+ xhci->sec_event_ring[intr_num]->cycle_state =
+ (current_trb->event_cmd.flags & TRB_CYCLE);
+
+ while (1) {
+ /* last trb of the event ring: toggle cycle state */
+ if (current_trb == &seg->trbs[TRBS_PER_SEGMENT - 1]) {
+ xhci->sec_event_ring[intr_num]->cycle_state ^= 1;
+ current_trb = &seg->trbs[0];
+ } else {
+ current_trb++;
+ }
+
+ /* cycle state transition */
+ if ((le32_to_cpu(current_trb->event_cmd.flags) & TRB_CYCLE) !=
+ xhci->sec_event_ring[intr_num]->cycle_state)
+ break;
+ }
+
+ if (erdp_trb != current_trb) {
+ deq =
+ xhci_trb_virt_to_dma(xhci->sec_event_ring[intr_num]->deq_seg,
+ current_trb);
+ if (deq == 0)
+ xhci_warn(xhci,
+ "WARN ivalid SW event ring dequeue ptr.\n");
+ /* Update HC event ring dequeue pointer */
+ erdp_reg &= ERST_PTR_MASK;
+ erdp_reg |= ((u64) deq & (u64) ~ERST_PTR_MASK);
+ }
+
+ /* Clear the event handler busy flag (RW1C); event ring is empty. */
+ erdp_reg |= ERST_EHB;
+ xhci_write_64(xhci, erdp_reg,
+ &xhci->sec_ir_set[intr_num]->erst_dequeue);
+}
+
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;
@@ -1806,28 +1879,7 @@ 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) {
- /*
- * 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);
-
+ xhci_handle_sec_intr_events(xhci, intr_num);
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;
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index fcb9b4b822aa..1221a80e0bdc 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -336,7 +336,7 @@ static int xhci_plat_runtime_suspend(struct device *dev)
dev_dbg(dev, "xhci-plat runtime suspend\n");
- return 0;
+ return xhci_suspend(xhci, true);
}
static int xhci_plat_runtime_resume(struct device *dev)
@@ -350,7 +350,7 @@ static int xhci_plat_runtime_resume(struct device *dev)
dev_dbg(dev, "xhci-plat runtime resume\n");
- ret = 0;
+ ret = xhci_resume(xhci, false);
pm_runtime_mark_last_busy(dev);
return ret;