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;