Merge "usb: xhci: Acknowledge pending events in secondary event ring"
This commit is contained in:
commit
358f9b3361
2 changed files with 78 additions and 26 deletions
|
@ -1789,11 +1789,84 @@ void xhci_free_command(struct xhci_hcd *xhci,
|
||||||
kfree(command);
|
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 xhci_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned intr_num)
|
||||||
{
|
{
|
||||||
int size;
|
int size;
|
||||||
u32 iman_reg;
|
|
||||||
u64 erdp_reg;
|
|
||||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||||
struct device *dev = xhci_to_hcd(xhci)->self.controller;
|
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 =
|
size =
|
||||||
sizeof(struct xhci_erst_entry)*(xhci->sec_erst[intr_num].num_entries);
|
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) {
|
||||||
/*
|
xhci_handle_sec_intr_events(xhci, intr_num);
|
||||||
* 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,
|
dma_free_coherent(dev, size, xhci->sec_erst[intr_num].entries,
|
||||||
xhci->sec_erst[intr_num].erst_dma_addr);
|
xhci->sec_erst[intr_num].erst_dma_addr);
|
||||||
xhci->sec_erst[intr_num].entries = NULL;
|
xhci->sec_erst[intr_num].entries = NULL;
|
||||||
|
|
|
@ -336,7 +336,7 @@ static int xhci_plat_runtime_suspend(struct device *dev)
|
||||||
|
|
||||||
dev_dbg(dev, "xhci-plat runtime suspend\n");
|
dev_dbg(dev, "xhci-plat runtime suspend\n");
|
||||||
|
|
||||||
return 0;
|
return xhci_suspend(xhci, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xhci_plat_runtime_resume(struct device *dev)
|
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");
|
dev_dbg(dev, "xhci-plat runtime resume\n");
|
||||||
|
|
||||||
ret = 0;
|
ret = xhci_resume(xhci, false);
|
||||||
pm_runtime_mark_last_busy(dev);
|
pm_runtime_mark_last_busy(dev);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Add table
Reference in a new issue