diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 50 |
1 files changed, 37 insertions, 13 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 3b962ba..7dfd177 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1184,7 +1184,6 @@ static void handle_port_status(struct xhci_hcd *xhci, /* Update event ring dequeue pointer before dropping the lock */ inc_deq(xhci, xhci->event_ring, true); - xhci_set_hc_event_deq(xhci); spin_unlock(&xhci->lock); /* Pass this up to the core */ @@ -1924,7 +1923,6 @@ cleanup: */ if (trb_comp_code == COMP_MISSED_INT || !ep->skip) { inc_deq(xhci, xhci->event_ring, true); - xhci_set_hc_event_deq(xhci); } if (ret) { @@ -2022,11 +2020,10 @@ static void xhci_handle_event(struct xhci_hcd *xhci) return; } - if (update_ptrs) { - /* Update SW and HC event ring dequeue pointer */ + if (update_ptrs) + /* Update SW event ring dequeue pointer */ inc_deq(xhci, xhci->event_ring, true); - xhci_set_hc_event_deq(xhci); - } + /* Are there more items on the event ring? */ xhci_handle_event(xhci); } @@ -2042,6 +2039,8 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) u32 status, irq_pending; union xhci_trb *trb; u64 temp_64; + union xhci_trb *event_ring_deq; + dma_addr_t deq; spin_lock(&xhci->lock); trb = xhci->event_ring->dequeue; @@ -2090,18 +2089,43 @@ hw_died: irq_pending |= 0x3; xhci_writel(xhci, irq_pending, &xhci->ir_set->irq_pending); - if (xhci->xhc_state & XHCI_STATE_DYING) + if (xhci->xhc_state & XHCI_STATE_DYING) { xhci_dbg(xhci, "xHCI dying, ignoring interrupt. " "Shouldn't IRQs be disabled?\n"); - else - /* FIXME this should be a delayed service routine - * that clears the EHB. + /* Clear the event handler busy flag (RW1C); + * the event ring should be empty. */ - xhci_handle_event(xhci); + temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + xhci_write_64(xhci, temp_64 | ERST_EHB, + &xhci->ir_set->erst_dequeue); + spin_unlock(&xhci->lock); + + return IRQ_HANDLED; + } + + event_ring_deq = xhci->event_ring->dequeue; + /* FIXME this should be a delayed service routine + * that clears the EHB. + */ + xhci_handle_event(xhci); - /* Clear the event handler busy flag (RW1C); event ring is empty. */ temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - xhci_write_64(xhci, temp_64 | ERST_EHB, &xhci->ir_set->erst_dequeue); + /* If necessary, update the HW's version of the event ring deq ptr. */ + if (event_ring_deq != xhci->event_ring->dequeue) { + deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, + xhci->event_ring->dequeue); + if (deq == 0) + xhci_warn(xhci, "WARN something wrong with SW event " + "ring dequeue ptr.\n"); + /* Update HC event ring dequeue pointer */ + temp_64 &= ERST_PTR_MASK; + temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK); + } + + /* Clear the event handler busy flag (RW1C); event ring is empty. */ + temp_64 |= ERST_EHB; + xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); + spin_unlock(&xhci->lock); return IRQ_HANDLED; |