| From a28cb1de1c5138a44ec4e8c2a463c138f743a3ce Mon Sep 17 00:00:00 2001 |
| From: Peter Chen <peter.chen@nxp.com> |
| Date: Fri, 15 Nov 2019 18:50:00 +0200 |
| Subject: [PATCH] usb: host: xhci: update event ring dequeue pointer on purpose |
| |
| commit dc0ffbea5729a3abafa577ebfce87f18b79e294b upstream. |
| |
| On some situations, the software handles TRB events slower |
| than adding TRBs, then xhci_handle_event can't return zero |
| long time, the xHC will consider the event ring is full, |
| and trigger "Event Ring Full" error, but in fact, the software |
| has already finished lots of events, just no chance to |
| update ERDP (event ring dequeue pointer). |
| |
| In this commit, we force update ERDP if half of TRBS_PER_SEGMENT |
| events have handled to avoid "Event Ring Full" error. |
| |
| Signed-off-by: Peter Chen <peter.chen@nxp.com> |
| Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Link: https://lore.kernel.org/r/1573836603-10871-2-git-send-email-mathias.nyman@linux.intel.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c |
| index 2de4b2d16dd1..77a90d9c1c2d 100644 |
| --- a/drivers/usb/host/xhci-ring.c |
| +++ b/drivers/usb/host/xhci-ring.c |
| @@ -2716,6 +2716,42 @@ static int xhci_handle_event(struct xhci_hcd *xhci) |
| } |
| |
| /* |
| + * Update Event Ring Dequeue Pointer: |
| + * - When all events have finished |
| + * - To avoid "Event Ring Full Error" condition |
| + */ |
| +static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, |
| + union xhci_trb *event_ring_deq) |
| +{ |
| + u64 temp_64; |
| + dma_addr_t deq; |
| + |
| + temp_64 = xhci_read_64(xhci, &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"); |
| + /* |
| + * Per 4.9.4, Software writes to the ERDP register shall |
| + * always advance the Event Ring Dequeue Pointer value. |
| + */ |
| + if ((temp_64 & (u64) ~ERST_PTR_MASK) == |
| + ((u64) deq & (u64) ~ERST_PTR_MASK)) |
| + return; |
| + |
| + /* 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) */ |
| + temp_64 |= ERST_EHB; |
| + xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); |
| +} |
| + |
| +/* |
| * xHCI spec says we can get an interrupt, and if the HC has an error condition, |
| * we might get bad data out of the event ring. Section 4.10.2.7 has a list of |
| * indicators of an event TRB error, but we check the status *first* to be safe. |
| @@ -2726,9 +2762,9 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) |
| union xhci_trb *event_ring_deq; |
| irqreturn_t ret = IRQ_NONE; |
| unsigned long flags; |
| - dma_addr_t deq; |
| u64 temp_64; |
| u32 status; |
| + int event_loop = 0; |
| |
| spin_lock_irqsave(&xhci->lock, flags); |
| /* Check if the xHC generated the interrupt, or the irq is shared */ |
| @@ -2782,24 +2818,14 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) |
| /* FIXME this should be a delayed service routine |
| * that clears the EHB. |
| */ |
| - while (xhci_handle_event(xhci) > 0) {} |
| - |
| - temp_64 = xhci_read_64(xhci, &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); |
| + while (xhci_handle_event(xhci) > 0) { |
| + if (event_loop++ < TRBS_PER_SEGMENT / 2) |
| + continue; |
| + xhci_update_erst_dequeue(xhci, event_ring_deq); |
| + event_loop = 0; |
| } |
| |
| - /* 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); |
| + xhci_update_erst_dequeue(xhci, event_ring_deq); |
| ret = IRQ_HANDLED; |
| |
| out: |
| -- |
| 2.7.4 |
| |