diff --git a/repos/dde_linux/patches/usb_host_update_event_ring.patch b/repos/dde_linux/patches/usb_host_update_event_ring.patch new file mode 100644 index 0000000000..298d237fbc --- /dev/null +++ b/repos/dde_linux/patches/usb_host_update_event_ring.patch @@ -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 +Signed-off-by: Mathias Nyman +Link: https://lore.kernel.org/r/1573836603-10871-2-git-send-email-mathias.nyman@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +--- 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: diff --git a/repos/dde_linux/ports/dde_linux.hash b/repos/dde_linux/ports/dde_linux.hash index 3d95babc51..9c79d425fc 100644 --- a/repos/dde_linux/ports/dde_linux.hash +++ b/repos/dde_linux/ports/dde_linux.hash @@ -1 +1 @@ -017ed559d35da47f9df49d5ac1c3c7ef26b0491e +1c5d7ab1a7c3fb9cb594123c46ee861f782c5843 diff --git a/repos/dde_linux/ports/dde_linux.port b/repos/dde_linux/ports/dde_linux.port index 9fb7ab47ee..0a52ed8186 100644 --- a/repos/dde_linux/ports/dde_linux.port +++ b/repos/dde_linux/ports/dde_linux.port @@ -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)