Merge "usb: xhci: Acknowledge pending events in secondary event ring"

This commit is contained in:
Linux Build Service Account 2016-12-20 14:04:31 -08:00 committed by Gerrit - the friendly Code Review server
commit 358f9b3361
2 changed files with 78 additions and 26 deletions

View file

@ -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;

View file

@ -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;