dde_linux: backport update event ring for usb_host

This commit contains a backport of commit [1] that deals with updating
the event ring dequeue pointer more often to prevent unnecessary
'Event Ring Full' errors.

  [1] 'usb: host: xhci: update event ring dequeue pointer on purpose'
      (dc0ffbea5729a3abafa577ebfce87f18b79e294b)

Fixes #4296.
This commit is contained in:
Josef Söntgen 2021-10-11 13:57:36 +02:00 committed by Norman Feske
parent 1d1379430a
commit 8679f32d0b
3 changed files with 111 additions and 5 deletions

View File

@ -0,0 +1,105 @@
This is a backport of commit dc0ffbea5729a3abafa577ebfce87f18b79e294b:
Subject: [PATCH] usb: host: xhci: update event ring dequeue pointer on purpose
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>
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2717,6 +2717,42 @@
}
/*
+ * 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.
@@ -2727,9 +2763,9 @@
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 */
@@ -2783,24 +2819,14 @@
/* 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:

View File

@ -1 +1 @@
017ed559d35da47f9df49d5ac1c3c7ef26b0491e
1c5d7ab1a7c3fb9cb594123c46ee861f782c5843

View File

@ -180,10 +180,11 @@ PATCH_OPT(patches/wpa_supplicant.patch) := -p1 -d ${DIR(wpa_supplicant)}
# USB HOST
USB_HOST_OPT = -p1 -d$(SRC_DIR_USB_HOST)
PATCH_OPT(patches/usb_host_mem.patch) := $(USB_HOST_OPT)
PATCH_OPT(patches/usb_host_omap.patch) := $(USB_HOST_OPT)
PATCH_OPT(patches/usb_host_dwc_otg.patch) := $(USB_HOST_OPT)
PATCH_OPT(patches/usb_host_isoc_bei.patch):= $(USB_HOST_OPT)
PATCH_OPT(patches/usb_host_mem.patch) := $(USB_HOST_OPT)
PATCH_OPT(patches/usb_host_omap.patch) := $(USB_HOST_OPT)
PATCH_OPT(patches/usb_host_dwc_otg.patch) := $(USB_HOST_OPT)
PATCH_OPT(patches/usb_host_isoc_bei.patch) := $(USB_HOST_OPT)
PATCH_OPT(patches/usb_host_update_event_ring.patch) := $(USB_HOST_OPT)
# USB HID
USB_HID_OPT = -p1 -d$(SRC_DIR_USB_HID)