From b333180d8d55a1c0ccd7210c00490f675748b2da Mon Sep 17 00:00:00 2001 From: Hongyu Xie Date: Thu, 23 Jun 2022 14:19:42 +0300 Subject: [PATCH 01/16] xhci: Keep interrupt disabled in initialization until host is running. ANBZ: #26347 commit a808925075fb750804a60ff0710614466c396db4 upstream irq is disabled in xhci_quiesce(called by xhci_halt, with bit:2 cleared in USBCMD register), but xhci_run(called by usb_add_hcd) re-enable it. It's possible that you will receive thousands of interrupt requests after initialization for 2.0 roothub. And you will get a lot of warning like, "xHCI dying, ignoring interrupt. Shouldn't IRQs be disabled?". This amount of interrupt requests will cause the entire system to freeze. This problem was first found on a device with ASM2142 host controller on it. [tidy up old code while moving it, reword header -Mathias] Cc: stable@kernel.org Signed-off-by: Hongyu Xie Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20220623111945.1557702-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 485e6a47d346..854c7de94a8d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -606,8 +606,27 @@ static int xhci_init(struct usb_hcd *hcd) static int xhci_run_finished(struct xhci_hcd *xhci) { + unsigned long flags; + u32 temp; + + /* + * Enable interrupts before starting the host (xhci 4.2 and 5.5.2). + * Protect the short window before host is running with a lock + */ + spin_lock_irqsave(&xhci->lock, flags); + + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Enable interrupts"); + temp = readl(&xhci->op_regs->command); + temp |= (CMD_EIE); + writel(temp, &xhci->op_regs->command); + + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Enable primary interrupter"); + temp = readl(&xhci->ir_set->irq_pending); + writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending); + if (xhci_start(xhci)) { xhci_halt(xhci); + spin_unlock_irqrestore(&xhci->lock, flags); return -ENODEV; } xhci->shared_hcd->state = HC_STATE_RUNNING; @@ -618,6 +637,8 @@ static int xhci_run_finished(struct xhci_hcd *xhci) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished xhci_run for USB3 roothub"); + + spin_unlock_irqrestore(&xhci->lock, flags); return 0; } @@ -666,19 +687,6 @@ int xhci_run(struct usb_hcd *hcd) temp |= (xhci->imod_interval / 250) & ER_IRQ_INTERVAL_MASK; writel(temp, &xhci->ir_set->irq_control); - /* Set the HCD state before we enable the irqs */ - temp = readl(&xhci->op_regs->command); - temp |= (CMD_EIE); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Enable interrupts, cmd = 0x%x.", temp); - writel(temp, &xhci->op_regs->command); - - temp = readl(&xhci->ir_set->irq_pending); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Enabling event ring interrupter %p by writing 0x%x to irq_pending", - xhci->ir_set, (unsigned int) ER_IRQ_ENABLE(temp)); - writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending); - if (xhci->quirks & XHCI_NEC_HOST) { struct xhci_command *command; -- Gitee From f0006b9f3a9ff54dbde8dc7e01e6cfe21fe25925 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 29 Jan 2021 15:00:29 +0200 Subject: [PATCH 02/16] xhci: avoid DMA double fetch when reading event trb type. ANBZ: #26347 commit 0353810a047ef0101af01665f60597d7f5cc6b02 upstream Instead of re-reading, masking and endianness correcting the same trb several times to get the trb type from an event, just do it once and store it in a local variable. Also pass the trb_type directly to the vendor specific event handler, avoiding one more similar read. In addition to the security benefit this also cleans up the code and helps readability. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20210129130044.206855-13-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci-ring.c | 27 ++++++++++++--------------- drivers/usb/host/xhci.h | 2 +- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 583d90744bea..5d6b32b38678 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1607,11 +1607,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, } static void handle_vendor_event(struct xhci_hcd *xhci, - union xhci_trb *event) + union xhci_trb *event, u32 trb_type) { - u32 trb_type; - - trb_type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->generic.field[3])); xhci_dbg(xhci, "Vendor specific event TRB type = %u\n", trb_type); if (trb_type == TRB_NEC_CMD_COMP && (xhci->quirks & XHCI_NEC_HOST)) handle_cmd_completion(xhci, &event->event_cmd); @@ -2800,6 +2797,7 @@ static int xhci_handle_event(struct xhci_hcd *xhci) { union xhci_trb *event; int update_ptrs = 1; + u32 trb_type; int ret; /* Event ring hasn't been allocated yet. */ @@ -2821,31 +2819,30 @@ static int xhci_handle_event(struct xhci_hcd *xhci) * speculative reads of the event's flags/data below. */ rmb(); + trb_type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags)); /* FIXME: Handle more event types. */ - switch (le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) { - case TRB_TYPE(TRB_COMPLETION): + + switch (trb_type) { + case TRB_COMPLETION: handle_cmd_completion(xhci, &event->event_cmd); break; - case TRB_TYPE(TRB_PORT_STATUS): + case TRB_PORT_STATUS: handle_port_status(xhci, event); update_ptrs = 0; break; - case TRB_TYPE(TRB_TRANSFER): + case TRB_TRANSFER: ret = handle_tx_event(xhci, &event->trans_event); if (ret >= 0) update_ptrs = 0; break; - case TRB_TYPE(TRB_DEV_NOTE): + case TRB_DEV_NOTE: handle_device_notification(xhci, event); break; default: - if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >= - TRB_TYPE(48)) - handle_vendor_event(xhci, event); + if (trb_type >= TRB_VENDOR_DEFINED_LOW) + handle_vendor_event(xhci, event, trb_type); else - xhci_warn(xhci, "ERROR unknown event type %d\n", - TRB_FIELD_TO_TYPE( - le32_to_cpu(event->event_cmd.flags))); + xhci_warn(xhci, "ERROR unknown event type %d\n", trb_type); } /* Any of the above functions may drop and re-acquire the lock, so check * to make sure a watchdog timer didn't mark the host as non-responsive. diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 0b59fb9d4bac..26ccf1375700 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1418,7 +1418,7 @@ union xhci_trb { /* MFINDEX Wrap Event - microframe counter wrapped */ #define TRB_MFINDEX_WRAP 39 /* TRB IDs 40-47 reserved, 48-63 is vendor-defined */ - +#define TRB_VENDOR_DEFINED_LOW 48 /* Nec vendor-specific command completion event. */ #define TRB_NEC_CMD_COMP 48 /* Get NEC firmware revision. */ -- Gitee From b2940974a1681ac8727cd716e3424d09a8e8ff11 Mon Sep 17 00:00:00 2001 From: "Ahmed S. Darwish" Date: Mon, 19 Oct 2020 12:06:34 +0200 Subject: [PATCH 03/16] usb: xhci: Remove in_interrupt() checks ANBZ: #26347 commit 96eea5876eb01c35f6eda225f0cdc752a59b6615 upstream The usage of in_interrupt() in drivers is phased out for various reasons. xhci_set_hc_event_deq() has an !in_interrupt() check which is pointless because the function is only invoked from xhci_mem_init() which is clearly task context as it does GFP_KERNEL allocations. Remove it. xhci_urb_enqueue() prints a debug message if an URB is submitted after the underlying hardware was suspended. But that warning is only issued when in_interrupt() is true, which makes no sense. Simply return -ESHUTDOWN and be done with it. Signed-off-by: Ahmed S. Darwish Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Thomas Gleixner Cc: Mathias Nyman Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Link: https://lore.kernel.org/r/20201019101110.148631116@linutronix.de Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci-mem.c | 2 +- drivers/usb/host/xhci.c | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 5c55bf3299a3..5af2e1409070 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2111,7 +2111,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, xhci->event_ring->dequeue); - if (deq == 0 && !in_interrupt()) + if (!deq) xhci_warn(xhci, "WARN something wrong with SW event ring " "dequeue ptr.\n"); /* Update HC event ring dequeue pointer */ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 854c7de94a8d..88547628dcf6 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1526,11 +1526,9 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag ep_index = xhci_get_endpoint_index(&urb->ep->desc); ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state; - if (!HCD_HW_ACCESSIBLE(hcd)) { - if (!in_interrupt()) - xhci_dbg(xhci, "urb submitted during PCI suspend\n"); + if (!HCD_HW_ACCESSIBLE(hcd)) return -ESHUTDOWN; - } + if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) { xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n"); return -ENODEV; -- Gitee From 9a6ac01afde11f550614d89621404f3dc7d8e386 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:56 +0200 Subject: [PATCH 04/16] xhci: remove xhci_test_trb_in_td_math early development check ANBZ: #26347 commit 54f9927dfe2266402a226d5f51d38236bdca0590 upstream Time to remove this test trb in td math check that was added in early stage of xhci driver development. It verified that the size, alignment and boundaries of the event and command rings allocated by the driver itself are correct. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci-mem.c | 160 ------------------------------------ 1 file changed, 160 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 5af2e1409070..ca3bc792d8ac 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1946,164 +1946,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->usb3_rhub.bus_state.bus_suspended = 0; } -static int xhci_test_trb_in_td(struct xhci_hcd *xhci, - struct xhci_segment *input_seg, - union xhci_trb *start_trb, - union xhci_trb *end_trb, - dma_addr_t input_dma, - struct xhci_segment *result_seg, - char *test_name, int test_number) -{ - unsigned long long start_dma; - unsigned long long end_dma; - struct xhci_segment *seg; - - start_dma = xhci_trb_virt_to_dma(input_seg, start_trb); - end_dma = xhci_trb_virt_to_dma(input_seg, end_trb); - - seg = trb_in_td(xhci, input_seg, start_trb, end_trb, input_dma, false); - if (seg != result_seg) { - xhci_warn(xhci, "WARN: %s TRB math test %d failed!\n", - test_name, test_number); - xhci_warn(xhci, "Tested TRB math w/ seg %p and " - "input DMA 0x%llx\n", - input_seg, - (unsigned long long) input_dma); - xhci_warn(xhci, "starting TRB %p (0x%llx DMA), " - "ending TRB %p (0x%llx DMA)\n", - start_trb, start_dma, - end_trb, end_dma); - xhci_warn(xhci, "Expected seg %p, got seg %p\n", - result_seg, seg); - trb_in_td(xhci, input_seg, start_trb, end_trb, input_dma, - true); - return -1; - } - return 0; -} - -/* TRB math checks for xhci_trb_in_td(), using the command and event rings. */ -static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci) -{ - struct { - dma_addr_t input_dma; - struct xhci_segment *result_seg; - } simple_test_vector [] = { - /* A zeroed DMA field should fail */ - { 0, NULL }, - /* One TRB before the ring start should fail */ - { xhci->event_ring->first_seg->dma - 16, NULL }, - /* One byte before the ring start should fail */ - { xhci->event_ring->first_seg->dma - 1, NULL }, - /* Starting TRB should succeed */ - { xhci->event_ring->first_seg->dma, xhci->event_ring->first_seg }, - /* Ending TRB should succeed */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16, - xhci->event_ring->first_seg }, - /* One byte after the ring end should fail */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16 + 1, NULL }, - /* One TRB after the ring end should fail */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT)*16, NULL }, - /* An address of all ones should fail */ - { (dma_addr_t) (~0), NULL }, - }; - struct { - struct xhci_segment *input_seg; - union xhci_trb *start_trb; - union xhci_trb *end_trb; - dma_addr_t input_dma; - struct xhci_segment *result_seg; - } complex_test_vector [] = { - /* Test feeding a valid DMA address from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->event_ring->first_seg->trbs, - .end_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg = NULL, - }, - /* Test feeding a valid end TRB from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->event_ring->first_seg->trbs, - .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg = NULL, - }, - /* Test feeding a valid start and end TRB from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->cmd_ring->first_seg->trbs, - .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg = NULL, - }, - /* TRB in this ring, but after this TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[0], - .end_trb = &xhci->event_ring->first_seg->trbs[3], - .input_dma = xhci->event_ring->first_seg->dma + 4*16, - .result_seg = NULL, - }, - /* TRB in this ring, but before this TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[3], - .end_trb = &xhci->event_ring->first_seg->trbs[6], - .input_dma = xhci->event_ring->first_seg->dma + 2*16, - .result_seg = NULL, - }, - /* TRB in this ring, but after this wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], - .end_trb = &xhci->event_ring->first_seg->trbs[1], - .input_dma = xhci->event_ring->first_seg->dma + 2*16, - .result_seg = NULL, - }, - /* TRB in this ring, but before this wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], - .end_trb = &xhci->event_ring->first_seg->trbs[1], - .input_dma = xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 4)*16, - .result_seg = NULL, - }, - /* TRB not in this ring, and we have a wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], - .end_trb = &xhci->event_ring->first_seg->trbs[1], - .input_dma = xhci->cmd_ring->first_seg->dma + 2*16, - .result_seg = NULL, - }, - }; - - unsigned int num_tests; - int i, ret; - - num_tests = ARRAY_SIZE(simple_test_vector); - for (i = 0; i < num_tests; i++) { - ret = xhci_test_trb_in_td(xhci, - xhci->event_ring->first_seg, - xhci->event_ring->first_seg->trbs, - &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - simple_test_vector[i].input_dma, - simple_test_vector[i].result_seg, - "Simple", i); - if (ret < 0) - return ret; - } - - num_tests = ARRAY_SIZE(complex_test_vector); - for (i = 0; i < num_tests; i++) { - ret = xhci_test_trb_in_td(xhci, - complex_test_vector[i].input_seg, - complex_test_vector[i].start_trb, - complex_test_vector[i].end_trb, - complex_test_vector[i].input_dma, - complex_test_vector[i].result_seg, - "Complex", i); - if (ret < 0) - return ret; - } - xhci_dbg(xhci, "TRB math tests passed.\n"); - return 0; -} - static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) { u64 temp; @@ -2538,8 +2380,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) 0, flags); if (!xhci->event_ring) goto fail; - if (xhci_check_trb_in_td_math(xhci) < 0) - goto fail; ret = xhci_alloc_erst(xhci, xhci->event_ring, &xhci->erst, flags); if (ret) -- Gitee From c3df57fcfd92b3aad099529ea24448ff734c405b Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:55 +0200 Subject: [PATCH 05/16] xhci: fix event ring segment table related masks and variables in header ANBZ: #26347 commit 8c1cbec9db1ab044167a7594c88bb5906c9d3ee4 upstream xHC controller can supports up to 1024 interrupters. To fit these change the max_interrupters varable from u8 to u16. Add a separate mask for the reserve and preserve bits [5:0] in the erst base register and use it instead of the ERST_PRT_MASK. ERSR_PTR_MASK [3:0] is intended for masking bits in the event ring dequeue pointer register. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci-mem.c | 4 ++-- drivers/usb/host/xhci.h | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index ca3bc792d8ac..86a37607f1a5 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2401,8 +2401,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) "// Set ERST base address for ir_set 0 = 0x%llx", (unsigned long long)xhci->erst.erst_dma_addr); val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base); - val_64 &= ERST_PTR_MASK; - val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK); + val_64 &= ERST_BASE_RSVDP; + val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_BASE_RSVDP); xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base); /* Set the event ring dequeue address */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 26ccf1375700..2f136cd13aa3 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -513,6 +513,9 @@ struct xhci_intr_reg { /* Preserve bits 16:31 of erst_size */ #define ERST_SIZE_MASK (0xffff << 16) +/* erst_base bitmasks */ +#define ERST_BASE_RSVDP (0x3f) + /* erst_dequeue bitmasks */ /* Dequeue ERST Segment Index (DESI) - Segment number (or alias) * where the current dequeue pointer lies. This is an optional HW hint. @@ -1762,7 +1765,7 @@ struct xhci_hcd { u8 sbrn; u16 hci_version; u8 max_slots; - u8 max_interrupters; + u16 max_interrupters; u8 max_ports; u8 isoc_threshold; /* imod_interval in ns (I * 250ns) */ -- Gitee From 8b86ace63e15d4e1909950e55c542f58834320ce Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:57 +0200 Subject: [PATCH 06/16] xhci: Refactor interrupter code for initial multi interrupter support. ANBZ: #26347 commit b17a57f89f69069458d0a9d9b04281ce48da7ebb upstream xHC supports several interrupters, each with its own mmio register set, event ring and MSI/MSI-X vector. Transfers can be assigned different interrupters when queued. See xhci 4.17 for details. Current driver only supports one interrupter. Create a xhci_interrupter structure containing an event ring, pointer to mmio registers for this interrupter, variables to store registers over s3 suspend, erst, etc. Add functions to create and free an interrupter, and pass an interrupter pointer to functions that deal with events. Secondary interrupters are also useful without having an interrupt vector. One use case is the xHCI audio sideband offloading where a DSP can take care of specific audio endpoints. When all transfer events of an offloaded endpoint can be mapped to a separate interrupter event ring the DSP can poll this ring, and we can mask these events preventing waking up the CPU. Only minor functional changes such as clearing some of the interrupter registers when freeing the interrupter. Still create only one primary interrupter. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-4-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci-debugfs.c | 2 +- drivers/usb/host/xhci-mem.c | 168 +++++++++++++++++++++----------- drivers/usb/host/xhci-ring.c | 68 +++++++------ drivers/usb/host/xhci.c | 54 ++++++---- drivers/usb/host/xhci.h | 24 +++-- 5 files changed, 196 insertions(+), 120 deletions(-) diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index dc832ddf7033..0bc7fe11f749 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -692,7 +692,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci) "command-ring", xhci->debugfs_root); - xhci_debugfs_create_ring_dir(xhci, &xhci->event_ring, + xhci_debugfs_create_ring_dir(xhci, &xhci->interrupter->event_ring, "event-ring", xhci->debugfs_root); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 86a37607f1a5..2f7055d23339 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1833,17 +1833,43 @@ int xhci_alloc_erst(struct xhci_hcd *xhci, return 0; } -void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) +static void +xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { - size_t size; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + size_t erst_size; + u64 tmp64; + u32 tmp; - size = sizeof(struct xhci_erst_entry) * (erst->num_entries); - if (erst->entries) - dma_free_coherent(dev, size, - erst->entries, - erst->erst_dma_addr); - erst->entries = NULL; + if (!ir) + return; + + erst_size = sizeof(struct xhci_erst_entry) * (ir->erst.num_entries); + if (ir->erst.entries) + dma_free_coherent(dev, erst_size, + ir->erst.entries, + ir->erst.erst_dma_addr); + ir->erst.entries = NULL; + + /* + * Clean out interrupter registers except ERSTBA. Clearing either the + * low or high 32 bits of ERSTBA immediately causes the controller to + * dereference the partially cleared 64 bit address, causing IOMMU error. + */ + tmp = readl(&ir->ir_set->erst_size); + tmp &= ERST_SIZE_MASK; + writel(tmp, &ir->ir_set->erst_size); + + tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + tmp64 &= (u64) ERST_PTR_MASK; + xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue); + + /* free interrrupter event ring */ + if (ir->event_ring) + xhci_ring_free(xhci, ir->event_ring); + ir->event_ring = NULL; + + kfree(ir); } void xhci_mem_cleanup(struct xhci_hcd *xhci) @@ -1853,12 +1879,9 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) cancel_delayed_work_sync(&xhci->cmd_timer); - xhci_free_erst(xhci, &xhci->erst); - - if (xhci->event_ring) - xhci_ring_free(xhci, xhci->event_ring); - xhci->event_ring = NULL; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed event ring"); + xhci_free_interrupter(xhci, xhci->interrupter); + xhci->interrupter = NULL; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring"); if (xhci->lpm_command) xhci_free_command(xhci, xhci->lpm_command); @@ -1946,18 +1969,18 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->usb3_rhub.bus_state.bus_suspended = 0; } -static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) +static void xhci_set_hc_event_deq(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { u64 temp; dma_addr_t deq; - deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, - xhci->event_ring->dequeue); + deq = xhci_trb_virt_to_dma(ir->event_ring->deq_seg, + ir->event_ring->dequeue); if (!deq) xhci_warn(xhci, "WARN something wrong with SW event ring " "dequeue ptr.\n"); /* Update HC event ring dequeue pointer */ - temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); temp &= ERST_PTR_MASK; /* Don't clear the EHB bit (which is RW1C) because * there might be more events to service. @@ -1967,7 +1990,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) "// Write event ring dequeue pointer, " "preserving EHB bit"); xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp, - &xhci->ir_set->erst_dequeue); + &ir->ir_set->erst_dequeue); } static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, @@ -2233,6 +2256,68 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) return 0; } +static struct xhci_interrupter * +xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags) +{ + struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + struct xhci_interrupter *ir; + u64 erst_base; + u32 erst_size; + int ret; + + if (intr_num > xhci->max_interrupters) { + xhci_warn(xhci, "Can't allocate interrupter %d, max interrupters %d\n", + intr_num, xhci->max_interrupters); + return NULL; + } + + if (xhci->interrupter) { + xhci_warn(xhci, "Can't allocate already set up interrupter %d\n", intr_num); + return NULL; + } + + ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); + if (!ir) + return NULL; + + ir->ir_set = &xhci->run_regs->ir_set[intr_num]; + ir->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, + 0, flags); + if (!ir->event_ring) { + xhci_warn(xhci, "Failed to allocate interrupter %d event ring\n", intr_num); + goto fail_ir; + } + + ret = xhci_alloc_erst(xhci, ir->event_ring, &ir->erst, flags); + if (ret) { + xhci_warn(xhci, "Failed to allocate interrupter %d erst\n", intr_num); + goto fail_ev; + + } + /* set ERST count with the number of entries in the segment table */ + erst_size = readl(&ir->ir_set->erst_size); + erst_size &= ERST_SIZE_MASK; + erst_size |= ERST_NUM_SEGS; + writel(erst_size, &ir->ir_set->erst_size); + + erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); + erst_base &= ERST_PTR_MASK; + erst_base |= (ir->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK); + xhci_write_64(xhci, erst_base, &ir->ir_set->erst_base); + + /* Set the event ring dequeue address of this interrupter */ + xhci_set_hc_event_deq(xhci, ir); + + return ir; + +fail_ev: + xhci_ring_free(xhci, ir->event_ring); +fail_ir: + kfree(ir); + + return NULL; +} + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { dma_addr_t dma; @@ -2240,7 +2325,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) unsigned int val, val2; u64 val_64; u32 page_size, temp; - int i, ret; + int i; INIT_LIST_HEAD(&xhci->cmd_list); @@ -2369,46 +2454,13 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) " from cap regs base addr", val); xhci->dba = (void __iomem *) xhci->cap_regs + val; /* Set ir_set to interrupt register set 0 */ - xhci->ir_set = &xhci->run_regs->ir_set[0]; - - /* - * Event ring setup: Allocate a normal ring, but also setup - * the event ring segment table (ERST). Section 4.9.3. - */ - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring"); - xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, - 0, flags); - if (!xhci->event_ring) - goto fail; - - ret = xhci_alloc_erst(xhci, xhci->event_ring, &xhci->erst, flags); - if (ret) - goto fail; - - /* set ERST count with the number of entries in the segment table */ - val = readl(&xhci->ir_set->erst_size); - val &= ERST_SIZE_MASK; - val |= ERST_NUM_SEGS; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Write ERST size = %i to ir_set 0 (some bits preserved)", - val); - writel(val, &xhci->ir_set->erst_size); + /* allocate and set up primary interrupter with an event ring. */ xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Set ERST entries to point to event ring."); - /* set the segment table base address */ - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Set ERST base address for ir_set 0 = 0x%llx", - (unsigned long long)xhci->erst.erst_dma_addr); - val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base); - val_64 &= ERST_BASE_RSVDP; - val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_BASE_RSVDP); - xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base); - - /* Set the event ring dequeue address */ - xhci_set_hc_event_deq(xhci); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Wrote ERST address to ir_set 0."); + "Allocating primary event ring"); + xhci->interrupter = xhci_alloc_interrupter(xhci, 0, flags); + if (!xhci->interrupter) + goto fail; /* * XXX: Might need to set the Interrupter Moderation Register to diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 5d6b32b38678..a8a6888dfae2 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1664,7 +1664,8 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci) } static void handle_port_status(struct xhci_hcd *xhci, - union xhci_trb *event) + struct xhci_interrupter *ir, + union xhci_trb *event) { struct usb_hcd *hcd; u32 port_id; @@ -1687,7 +1688,7 @@ static void handle_port_status(struct xhci_hcd *xhci, if ((port_id <= 0) || (port_id > max_ports)) { xhci_warn(xhci, "Port change event with invalid port ID %d\n", port_id); - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); return; } @@ -1817,7 +1818,7 @@ static void handle_port_status(struct xhci_hcd *xhci, cleanup: /* Update event ring dequeue pointer before dropping the lock */ - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); /* Don't make the USB core poll the roothub if we got a bad port status * change event. Besides, at that point we can't tell which roothub @@ -2412,7 +2413,8 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, * At this point, the host controller is probably hosed and should be reset. */ static int handle_tx_event(struct xhci_hcd *xhci, - struct xhci_transfer_event *event) + struct xhci_interrupter *ir, + struct xhci_transfer_event *event) { struct xhci_virt_device *xdev; struct xhci_virt_ep *ep; @@ -2763,7 +2765,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, * processing missed tds. */ if (!handling_skipped_tds) - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); /* * If ep->skip is set, it means there are missed tds on the @@ -2778,8 +2780,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, err_out: xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n", (unsigned long long) xhci_trb_virt_to_dma( - xhci->event_ring->deq_seg, - xhci->event_ring->dequeue), + ir->event_ring->deq_seg, + ir->event_ring->dequeue), lower_32_bits(le64_to_cpu(event->buffer)), upper_32_bits(le64_to_cpu(event->buffer)), le32_to_cpu(event->transfer_len), @@ -2793,7 +2795,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, * Returns >0 for "possibly more events to process" (caller should call again), * otherwise 0 if done. In future, <0 returns should indicate error code. */ -static int xhci_handle_event(struct xhci_hcd *xhci) +static int xhci_handle_event(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { union xhci_trb *event; int update_ptrs = 1; @@ -2801,18 +2803,18 @@ static int xhci_handle_event(struct xhci_hcd *xhci) int ret; /* Event ring hasn't been allocated yet. */ - if (!xhci->event_ring || !xhci->event_ring->dequeue) { - xhci_err(xhci, "ERROR event ring not ready\n"); + if (!ir || !ir->event_ring || !ir->event_ring->dequeue) { + xhci_err(xhci, "ERROR interrupter not ready\n"); return -ENOMEM; } - event = xhci->event_ring->dequeue; + event = ir->event_ring->dequeue; /* Does the HC or OS own the TRB? */ if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) != - xhci->event_ring->cycle_state) + ir->event_ring->cycle_state) return 0; - trace_xhci_handle_event(xhci->event_ring, &event->generic); + trace_xhci_handle_event(ir->event_ring, &event->generic); /* * Barrier between reading the TRB_CYCLE (valid) flag above and any @@ -2827,11 +2829,11 @@ static int xhci_handle_event(struct xhci_hcd *xhci) handle_cmd_completion(xhci, &event->event_cmd); break; case TRB_PORT_STATUS: - handle_port_status(xhci, event); + handle_port_status(xhci, ir, event); update_ptrs = 0; break; case TRB_TRANSFER: - ret = handle_tx_event(xhci, &event->trans_event); + ret = handle_tx_event(xhci, ir, &event->trans_event); if (ret >= 0) update_ptrs = 0; break; @@ -2855,7 +2857,7 @@ static int xhci_handle_event(struct xhci_hcd *xhci) if (update_ptrs) /* Update SW event ring dequeue pointer */ - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); /* Are there more items on the event ring? Caller will call us again to * check. @@ -2869,16 +2871,17 @@ static int xhci_handle_event(struct xhci_hcd *xhci) * - To avoid "Event Ring Full Error" condition */ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, - union xhci_trb *event_ring_deq) + struct xhci_interrupter *ir, + union xhci_trb *event_ring_deq) { u64 temp_64; dma_addr_t deq; - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp_64 = xhci_read_64(xhci, &ir->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 (event_ring_deq != ir->event_ring->dequeue) { + deq = xhci_trb_virt_to_dma(ir->event_ring->deq_seg, + ir->event_ring->dequeue); if (deq == 0) xhci_warn(xhci, "WARN something wrong with SW event ring dequeue ptr\n"); /* @@ -2896,7 +2899,7 @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, /* Clear the event handler busy flag (RW1C) */ temp_64 |= ERST_EHB; - xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); + xhci_write_64(xhci, temp_64, &ir->ir_set->erst_dequeue); } /* @@ -2908,6 +2911,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); union xhci_trb *event_ring_deq; + struct xhci_interrupter *ir; irqreturn_t ret = IRQ_NONE; unsigned long flags; u64 temp_64; @@ -2941,11 +2945,13 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) status |= STS_EINT; writel(status, &xhci->op_regs->status); + /* This is the handler of the primary interrupter */ + ir = xhci->interrupter; if (!hcd->msi_enabled) { u32 irq_pending; - irq_pending = readl(&xhci->ir_set->irq_pending); + irq_pending = readl(&ir->ir_set->irq_pending); irq_pending |= IMAN_IP; - writel(irq_pending, &xhci->ir_set->irq_pending); + writel(irq_pending, &ir->ir_set->irq_pending); } if (xhci->xhc_state & XHCI_STATE_DYING || @@ -2955,27 +2961,27 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) /* Clear the event handler busy flag (RW1C); * the event ring should be empty. */ - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp_64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); xhci_write_64(xhci, temp_64 | ERST_EHB, - &xhci->ir_set->erst_dequeue); + &ir->ir_set->erst_dequeue); ret = IRQ_HANDLED; goto out; } - event_ring_deq = xhci->event_ring->dequeue; + event_ring_deq = ir->event_ring->dequeue; /* FIXME this should be a delayed service routine * that clears the EHB. */ - while (xhci_handle_event(xhci) > 0) { + while (xhci_handle_event(xhci, ir) > 0) { if (event_loop++ < TRBS_PER_SEGMENT / 2) continue; - xhci_update_erst_dequeue(xhci, event_ring_deq); - event_ring_deq = xhci->event_ring->dequeue; + xhci_update_erst_dequeue(xhci, ir, event_ring_deq); + event_ring_deq = ir->event_ring->dequeue; event_loop = 0; } - xhci_update_erst_dequeue(xhci, event_ring_deq); + xhci_update_erst_dequeue(xhci, ir, event_ring_deq); ret = IRQ_HANDLED; out: diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 88547628dcf6..c773f67853b0 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -606,6 +606,7 @@ static int xhci_init(struct usb_hcd *hcd) static int xhci_run_finished(struct xhci_hcd *xhci) { + struct xhci_interrupter *ir = xhci->interrupter; unsigned long flags; u32 temp; @@ -621,8 +622,8 @@ static int xhci_run_finished(struct xhci_hcd *xhci) writel(temp, &xhci->op_regs->command); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Enable primary interrupter"); - temp = readl(&xhci->ir_set->irq_pending); - writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending); + temp = readl(&ir->ir_set->irq_pending); + writel(ER_IRQ_ENABLE(temp), &ir->ir_set->irq_pending); if (xhci_start(xhci)) { xhci_halt(xhci); @@ -660,7 +661,7 @@ int xhci_run(struct usb_hcd *hcd) u64 temp_64; int ret; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - + struct xhci_interrupter *ir = xhci->interrupter; /* Start the xHCI host controller running only after the USB 2.0 roothub * is setup. */ @@ -675,17 +676,17 @@ int xhci_run(struct usb_hcd *hcd) if (ret) return ret; - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp_64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); temp_64 &= ~ERST_PTR_MASK; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "ERST deq = 64'h%0lx", (long unsigned int) temp_64); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Set the interrupt modulation register"); - temp = readl(&xhci->ir_set->irq_control); + temp = readl(&ir->ir_set->irq_control); temp &= ~ER_IRQ_INTERVAL_MASK; temp |= (xhci->imod_interval / 250) & ER_IRQ_INTERVAL_MASK; - writel(temp, &xhci->ir_set->irq_control); + writel(temp, &ir->ir_set->irq_control); if (xhci->quirks & XHCI_NEC_HOST) { struct xhci_command *command; @@ -759,8 +760,8 @@ static void xhci_stop(struct usb_hcd *hcd) "// Disabling event ring interrupts"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); + temp = readl(&xhci->interrupter->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory"); xhci_mem_cleanup(xhci); @@ -827,28 +828,36 @@ EXPORT_SYMBOL_GPL(xhci_shutdown); #ifdef CONFIG_PM static void xhci_save_registers(struct xhci_hcd *xhci) { + struct xhci_interrupter *ir = xhci->interrupter; + xhci->s3.command = readl(&xhci->op_regs->command); xhci->s3.dev_nt = readl(&xhci->op_regs->dev_notification); xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); xhci->s3.config_reg = readl(&xhci->op_regs->config_reg); - xhci->s3.erst_size = readl(&xhci->ir_set->erst_size); - xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base); - xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - xhci->s3.irq_pending = readl(&xhci->ir_set->irq_pending); - xhci->s3.irq_control = readl(&xhci->ir_set->irq_control); + + if (!ir) + return; + + ir->s3_erst_size = readl(&ir->ir_set->erst_size); + ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); + ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + ir->s3_irq_pending = readl(&ir->ir_set->irq_pending); + ir->s3_irq_control = readl(&ir->ir_set->irq_control); } static void xhci_restore_registers(struct xhci_hcd *xhci) { + struct xhci_interrupter *ir = xhci->interrupter; + writel(xhci->s3.command, &xhci->op_regs->command); writel(xhci->s3.dev_nt, &xhci->op_regs->dev_notification); xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr); writel(xhci->s3.config_reg, &xhci->op_regs->config_reg); - writel(xhci->s3.erst_size, &xhci->ir_set->erst_size); - xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base); - xhci_write_64(xhci, xhci->s3.erst_dequeue, &xhci->ir_set->erst_dequeue); - writel(xhci->s3.irq_pending, &xhci->ir_set->irq_pending); - writel(xhci->s3.irq_control, &xhci->ir_set->irq_control); + writel(ir->s3_erst_size, &ir->ir_set->erst_size); + xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base); + xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue); + writel(ir->s3_irq_pending, &ir->ir_set->irq_pending); + writel(ir->s3_irq_control, &ir->ir_set->irq_control); } static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) @@ -1207,8 +1216,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); + temp = readl(&xhci->interrupter->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); @@ -5335,6 +5344,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x100) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); + /* xhci-plat or xhci-pci might have set max_interrupters already */ + if ((!xhci->max_interrupters) || + xhci->max_interrupters > HCS_MAX_INTRS(xhci->hcs_params1)) + xhci->max_interrupters = HCS_MAX_INTRS(xhci->hcs_params1); + xhci->quirks |= quirks; get_quirks(dev, xhci); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 2f136cd13aa3..8f46bf0c1003 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1676,11 +1676,6 @@ struct s3_save { u32 dev_nt; u64 dcbaa_ptr; u32 config_reg; - u32 irq_pending; - u32 irq_control; - u32 erst_size; - u64 erst_base; - u64 erst_dequeue; }; /* Use for lpm */ @@ -1707,7 +1702,18 @@ struct xhci_bus_state { struct completion u3exit_done[USB_MAXCHILDREN]; }; - +struct xhci_interrupter { + struct xhci_ring *event_ring; + struct xhci_erst erst; + struct xhci_intr_reg __iomem *ir_set; + unsigned int intr_num; + /* For interrupter registers save and restore over suspend/resume */ + u32 s3_irq_pending; + u32 s3_irq_control; + u32 s3_erst_size; + u64 s3_erst_base; + u64 s3_erst_dequeue; +}; /* * It can take up to 20 ms to transition from RExit to U0 on the * Intel Lynx Point LP xHCI host. @@ -1749,8 +1755,6 @@ struct xhci_hcd { struct xhci_op_regs __iomem *op_regs; struct xhci_run_regs __iomem *run_regs; struct xhci_doorbell_array __iomem *dba; - /* Our HCD's current interrupter register set */ - struct xhci_intr_reg __iomem *ir_set; /* Cached register copies of read-only HC data */ __u32 hcs_params1; @@ -1784,6 +1788,7 @@ struct xhci_hcd { struct reset_control *reset; /* data structures */ struct xhci_device_context_array *dcbaa; + struct xhci_interrupter *interrupter; struct xhci_ring *cmd_ring; unsigned int cmd_ring_state; #define CMD_RING_STATE_RUNNING (1 << 0) @@ -1794,8 +1799,7 @@ struct xhci_hcd { struct delayed_work cmd_timer; struct completion cmd_ring_stop_completion; struct xhci_command *current_cmd; - struct xhci_ring *event_ring; - struct xhci_erst erst; + /* Scratchpad */ struct xhci_scratchpad *scratchpad; /* Store LPM test failed devices' information */ -- Gitee From 7cc8b139a0f892f7486e0cffbb9b2be039749cb2 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:58 +0200 Subject: [PATCH 07/16] xhci: add helpers for enabling and disabling interrupters ANBZ: #26347 commit 52dd0483e822d097fd6f522af2438a9b6f6eb0a9 upstream Simple helpers to set and clear the IE (interrupter enable) bit for an interrupter. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-5-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index c773f67853b0..9a4325f8f17a 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -288,6 +288,32 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) xhci_info(xhci, "Fault detected\n"); } +static int xhci_enable_interrupter(struct xhci_interrupter *ir) +{ + u32 iman; + + if (!ir || !ir->ir_set) + return -EINVAL; + + iman = readl(&ir->ir_set->irq_pending); + writel(ER_IRQ_ENABLE(iman), &ir->ir_set->irq_pending); + + return 0; +} + +static int xhci_disable_interrupter(struct xhci_interrupter *ir) +{ + u32 iman; + + if (!ir || !ir->ir_set) + return -EINVAL; + + iman = readl(&ir->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(iman), &ir->ir_set->irq_pending); + + return 0; +} + #ifdef CONFIG_USB_PCI /* * Set up MSI @@ -603,7 +629,6 @@ static int xhci_init(struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ - static int xhci_run_finished(struct xhci_hcd *xhci) { struct xhci_interrupter *ir = xhci->interrupter; @@ -622,8 +647,7 @@ static int xhci_run_finished(struct xhci_hcd *xhci) writel(temp, &xhci->op_regs->command); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Enable primary interrupter"); - temp = readl(&ir->ir_set->irq_pending); - writel(ER_IRQ_ENABLE(temp), &ir->ir_set->irq_pending); + xhci_enable_interrupter(ir); if (xhci_start(xhci)) { xhci_halt(xhci); @@ -724,6 +748,7 @@ static void xhci_stop(struct usb_hcd *hcd) { u32 temp; struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_interrupter *ir = xhci->interrupter; mutex_lock(&xhci->mutex); @@ -760,8 +785,7 @@ static void xhci_stop(struct usb_hcd *hcd) "// Disabling event ring interrupts"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->interrupter->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); + xhci_disable_interrupter(ir); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory"); xhci_mem_cleanup(xhci); @@ -1216,8 +1240,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->interrupter->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); + xhci_disable_interrupter(xhci->interrupter); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); -- Gitee From b63998f0ed9a8aca611245b19ff8a6bec39cd37b Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 2 Jun 2023 17:40:03 +0300 Subject: [PATCH 08/16] xhci: split allocate interrupter into separate alloacte and add parts ANBZ: #26347 commit 4bf398e15aa410e25fa46a5684c7e5bf682ecc48 upstream The current function that both allocates and adds the interrupter isn't optimal when using several interrupters. The array of interrupters need to be protected with a lock while adding or removing interrupters. If memory is allocated under the default xhci spinlock then GFP_KERNEL can't be used. There is no need to allocate the interrupter memory under the lock, so split this code into separate unlocked allocate part, and a lock protected add part. Signed-off-by: Mathias Nyman Message-ID: <20230602144009.1225632-6-mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci-mem.c | 78 +++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 2f7055d23339..bb65b311168d 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1856,13 +1856,15 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) * low or high 32 bits of ERSTBA immediately causes the controller to * dereference the partially cleared 64 bit address, causing IOMMU error. */ - tmp = readl(&ir->ir_set->erst_size); - tmp &= ERST_SIZE_MASK; - writel(tmp, &ir->ir_set->erst_size); - - tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); - tmp64 &= (u64) ERST_PTR_MASK; - xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue); + if (ir->ir_set) { + tmp = readl(&ir->ir_set->erst_size); + tmp &= ERST_SIZE_MASK; + writel(tmp, &ir->ir_set->erst_size); + + tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + tmp64 &= (u64) ERST_PTR_MASK; + xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue); + } /* free interrrupter event ring */ if (ir->event_ring) @@ -2257,43 +2259,50 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) } static struct xhci_interrupter * -xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags) +xhci_alloc_interrupter(struct xhci_hcd *xhci, gfp_t flags) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; struct xhci_interrupter *ir; - u64 erst_base; - u32 erst_size; int ret; - if (intr_num > xhci->max_interrupters) { - xhci_warn(xhci, "Can't allocate interrupter %d, max interrupters %d\n", - intr_num, xhci->max_interrupters); - return NULL; - } - - if (xhci->interrupter) { - xhci_warn(xhci, "Can't allocate already set up interrupter %d\n", intr_num); - return NULL; - } - ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); if (!ir) return NULL; - ir->ir_set = &xhci->run_regs->ir_set[intr_num]; ir->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, 0, flags); if (!ir->event_ring) { - xhci_warn(xhci, "Failed to allocate interrupter %d event ring\n", intr_num); - goto fail_ir; + xhci_warn(xhci, "Failed to allocate interrupter event ring\n"); + kfree(ir); + return NULL; } ret = xhci_alloc_erst(xhci, ir->event_ring, &ir->erst, flags); if (ret) { - xhci_warn(xhci, "Failed to allocate interrupter %d erst\n", intr_num); - goto fail_ev; + xhci_warn(xhci, "Failed to allocate interrupter erst\n"); + xhci_ring_free(xhci, ir->event_ring); + kfree(ir); + return NULL; + } + + return ir; +} + +static int +xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, + unsigned int intr_num) +{ + u64 erst_base; + u32 erst_size; + if (intr_num > xhci->max_interrupters) { + xhci_warn(xhci, "Can't add interrupter %d, max interrupters %d\n", + intr_num, xhci->max_interrupters); + return -EINVAL; } + + ir->ir_set = &xhci->run_regs->ir_set[intr_num]; + /* set ERST count with the number of entries in the segment table */ erst_size = readl(&ir->ir_set->erst_size); erst_size &= ERST_SIZE_MASK; @@ -2308,14 +2317,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags /* Set the event ring dequeue address of this interrupter */ xhci_set_hc_event_deq(xhci, ir); - return ir; - -fail_ev: - xhci_ring_free(xhci, ir->event_ring); -fail_ir: - kfree(ir); - - return NULL; + return 0; } int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) @@ -2453,15 +2455,17 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) "// Doorbell array is located at offset 0x%x" " from cap regs base addr", val); xhci->dba = (void __iomem *) xhci->cap_regs + val; - /* Set ir_set to interrupt register set 0 */ - /* allocate and set up primary interrupter with an event ring. */ + /* Allocate and set up primary interrupter 0 with an event ring. */ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring"); - xhci->interrupter = xhci_alloc_interrupter(xhci, 0, flags); + xhci->interrupter = xhci_alloc_interrupter(xhci, flags); if (!xhci->interrupter) goto fail; + if (xhci_add_interrupter(xhci, xhci->interrupter, 0)) + goto fail; + /* * XXX: Might need to set the Interrupter Moderation Register to * something other than the default (~1ms minimum between interrupts). -- Gitee From d1f337bf8cb000a3f38249e6d64cbdc620925f6a Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Thu, 19 Oct 2023 13:29:10 +0300 Subject: [PATCH 09/16] xhci: Use more than one Event Ring segment ANBZ: #26347 commit 28084d3fcc3c8445542917f32e382c45b5343cc2 upstream Users have reported log spam created by "Event Ring Full" xHC event TRBs. These are caused by interrupt latency in conjunction with a very busy set of devices on the bus. The errors are benign, but throughput will suffer as the xHC will pause processing of transfers until the Event Ring is drained by the kernel. Commit dc0ffbea5729 ("usb: host: xhci: update event ring dequeue pointer on purpose") mitigated the issue by advancing the Event Ring Dequeue Pointer already after half a segment has been processed. Nevertheless, providing a larger Event Ring would be useful to cope with load peaks. Expand the number of event TRB slots available by increasing the number of Event Ring segments in the ERST. Controllers have a hardware-defined limit as to the number of ERST entries they can process, but with up to 32k it can be excessively high (sec 5.3.4). So cap the actual number at 2 (configurable through the ERST_MAX_SEGS macro), which seems like a reasonable quantity. It is supported by any xHC because the limit in the HCSPARAMS2 register is defined as a power of 2. Renesas uPD720201 and VIA VL805 controllers do not support more than 2 ERST entries. An alternative to increasing the number of Event Ring segments would be an increase of the segment size. But that requires allocating multiple contiguous pages, which may be impossible if memory is fragmented. Signed-off-by: Jonathan Bell Signed-off-by: Lukas Wunner Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231019102924.2797346-6-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci-mem.c | 10 +++++++--- drivers/usb/host/xhci.h | 5 +++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index bb65b311168d..b4ab338c55f9 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2263,14 +2263,18 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, gfp_t flags) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; struct xhci_interrupter *ir; + unsigned int num_segs; int ret; ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); if (!ir) return NULL; - ir->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, - 0, flags); + num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2), + ERST_MAX_SEGS); + + ir->event_ring = xhci_ring_alloc(xhci, num_segs, 1, TYPE_EVENT, 0, + flags); if (!ir->event_ring) { xhci_warn(xhci, "Failed to allocate interrupter event ring\n"); kfree(ir); @@ -2306,7 +2310,7 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, /* set ERST count with the number of entries in the segment table */ erst_size = readl(&ir->ir_set->erst_size); erst_size &= ERST_SIZE_MASK; - erst_size |= ERST_NUM_SEGS; + erst_size |= ir->event_ring->num_segs; writel(erst_size, &ir->ir_set->erst_size); erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 8f46bf0c1003..422fc680c69e 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1659,8 +1659,9 @@ struct urb_priv { * Each segment table entry is 4*32bits long. 1K seems like an ok size: * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table, * meaning 64 ring segments. - * Initial allocated size of the ERST, in number of entries */ -#define ERST_NUM_SEGS 1 + * Reasonable limit for number of Event Ring segments (spec allows 32k) + */ +#define ERST_MAX_SEGS 2 /* Initial allocated size of the ERST, in number of entries */ #define ERST_SIZE 64 /* Initial number of event segment rings allocated */ -- Gitee From bd0fc10fe9c46f54635a32d82577e941eaf1c7fa Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 17 Mar 2023 17:47:08 +0200 Subject: [PATCH 10/16] xhci: mem: Join string literals back ANBZ: #26347 commit 4feb07d0ada339f7edc0dc3b24581f201848a65b upstream For easy grepping on debug purposes join string literals back in the messages. No functional change. Signed-off-by: Andy Shevchenko Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230317154715.535523-8-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci-mem.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index b4ab338c55f9..467d48902a44 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -630,8 +630,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, int ret; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - xhci_dbg(xhci, "Allocating %u streams and %u " - "stream context array entries.\n", + xhci_dbg(xhci, "Allocating %u streams and %u stream context array entries.\n", num_streams, num_stream_ctxs); if (xhci->cmd_ring_reserved_trbs == MAX_RSVD_CMD_TRBS) { xhci_dbg(xhci, "Command ring has no reserved TRBs available\n"); @@ -1979,8 +1978,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci, struct xhci_interrupter deq = xhci_trb_virt_to_dma(ir->event_ring->deq_seg, ir->event_ring->dequeue); if (!deq) - xhci_warn(xhci, "WARN something wrong with SW event ring " - "dequeue ptr.\n"); + xhci_warn(xhci, "WARN something wrong with SW event ring dequeue ptr.\n"); /* Update HC event ring dequeue pointer */ temp = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); temp &= ERST_PTR_MASK; @@ -1989,8 +1987,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci, struct xhci_interrupter */ temp &= ~ERST_EHB; xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Write event ring dequeue pointer, " - "preserving EHB bit"); + "// Write event ring dequeue pointer, preserving EHB bit"); xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp, &ir->ir_set->erst_dequeue); } @@ -2023,8 +2020,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, } else if (major_revision <= 0x02) { rhub = &xhci->usb2_rhub; } else { - xhci_warn(xhci, "Ignoring unknown port speed, " - "Ext Cap %p, revision = 0x%x\n", + xhci_warn(xhci, "Ignoring unknown port speed, Ext Cap %p, revision = 0x%x\n", addr, major_revision); /* Ignoring port protocol we can't understand. FIXME */ return; @@ -2039,9 +2035,8 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, port_offset = XHCI_EXT_PORT_OFF(temp); port_count = XHCI_EXT_PORT_COUNT(temp); xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Ext Cap %p, port offset = %u, " - "count = %u, revision = 0x%x", - addr, port_offset, port_count, major_revision); + "Ext Cap %p, port offset = %u, count = %u, revision = 0x%x", + addr, port_offset, port_count, major_revision); /* Port count includes the current port offset */ if (port_offset == 0 || (port_offset + port_count - 1) > num_ports) /* WTF? "Valid values are ‘1’ to MaxPorts" */ @@ -2098,10 +2093,8 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, struct xhci_port *hw_port = &xhci->hw_ports[i]; /* Duplicate entry. Ignore the port if the revisions differ. */ if (hw_port->rhub) { - xhci_warn(xhci, "Duplicate port entry, Ext Cap %p," - " port %u\n", addr, i); - xhci_warn(xhci, "Port was marked as USB %u, " - "duplicated as USB %u\n", + xhci_warn(xhci, "Duplicate port entry, Ext Cap %p, port %u\n", addr, i); + xhci_warn(xhci, "Port was marked as USB %u, duplicated as USB %u\n", hw_port->rhub->maj_rev, major_revision); /* Only adjust the roothub port counts if we haven't * found a similar duplicate. @@ -2456,8 +2449,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) val = readl(&xhci->cap_regs->db_off); val &= DBOFF_MASK; xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Doorbell array is located at offset 0x%x" - " from cap regs base addr", val); + "// Doorbell array is located at offset 0x%x from cap regs base addr", + val); xhci->dba = (void __iomem *) xhci->cap_regs + val; /* Allocate and set up primary interrupter 0 with an event ring. */ -- Gitee From 9ced3d74876e69f94c31e1133c70f7e775eefdf9 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 15 Sep 2023 17:31:07 +0300 Subject: [PATCH 11/16] xhci: Clear EHB bit only at end of interrupt handler ANBZ: #26347 commit 15f3ef070933817fac2bcbdb9c85bff9e54e9f80 upstream The Event Handler Busy bit shall be cleared by software when the Event Ring is empty. The xHC is thereby informed that it may raise another interrupt once it has enqueued new events (sec 4.17.2). However since commit dc0ffbea5729 ("usb: host: xhci: update event ring dequeue pointer on purpose"), the EHB bit is already cleared after half a segment has been processed. As a result, spurious interrupts may occur: - xhci_irq() processes half a segment, clears EHB, continues processing remaining events. - xHC enqueues new events. Because EHB has been cleared, xHC sets Interrupt Pending bit. Interrupt moderation countdown begins. - Meanwhile xhci_irq() continues processing events. Interrupt moderation countdown reaches zero, so an MSI interrupt is signaled. - xhci_irq() empties the Event Ring, clears EHB again and is done. - Because an MSI interrupt has been signaled, xhci_irq() is run again. It discovers there's nothing to do and returns IRQ_NONE. Avoid by clearing the EHB bit only at the end of xhci_irq(). Fixes: dc0ffbea5729 ("usb: host: xhci: update event ring dequeue pointer on purpose") Signed-off-by: Lukas Wunner Cc: stable@vger.kernel.org # v5.5+ Cc: Peter Chen Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230915143108.1532163-4-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci-ring.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index a8a6888dfae2..e59659d74570 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2872,7 +2872,8 @@ static int xhci_handle_event(struct xhci_hcd *xhci, struct xhci_interrupter *ir) */ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, struct xhci_interrupter *ir, - union xhci_trb *event_ring_deq) + union xhci_trb *event_ring_deq, + bool clear_ehb) { u64 temp_64; dma_addr_t deq; @@ -2893,12 +2894,13 @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, return; /* Update HC event ring dequeue pointer */ - temp_64 &= ERST_PTR_MASK; + temp_64 &= ERST_DESI_MASK; temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK); } /* Clear the event handler busy flag (RW1C) */ - temp_64 |= ERST_EHB; + if (clear_ehb) + temp_64 |= ERST_EHB; xhci_write_64(xhci, temp_64, &ir->ir_set->erst_dequeue); } @@ -2975,13 +2977,13 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) while (xhci_handle_event(xhci, ir) > 0) { if (event_loop++ < TRBS_PER_SEGMENT / 2) continue; - xhci_update_erst_dequeue(xhci, ir, event_ring_deq); + xhci_update_erst_dequeue(xhci, ir, event_ring_deq, false); event_ring_deq = ir->event_ring->dequeue; event_loop = 0; } - xhci_update_erst_dequeue(xhci, ir, event_ring_deq); + xhci_update_erst_dequeue(xhci, ir, event_ring_deq, true); ret = IRQ_HANDLED; out: -- Gitee From 4b69d8a1b06556f4f46b60e67e04088da5013e7c Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 19 Oct 2023 13:29:09 +0300 Subject: [PATCH 12/16] xhci: Set DESI bits in ERDP register correctly ANBZ: #26347 commit 044818a6cd808b38a5d179a5fb9940417de4ba24 upstream When using more than one Event Ring segment (ERSTSZ > 1), software shall set the DESI bits in the ERDP register to the number of the segment to which the upper ERDP bits are pointing. The xHC may use the DESI bits as a shortcut to determine whether it needs to check for an Event Ring Full condition: If it's enqueueing events in a different segment, it need not compare its internal Enqueue Pointer with the Dequeue Pointer in the upper bits of the ERDP register (sec 5.5.2.3.3). Not setting the DESI bits correctly can result in the xHC enqueueing events past the Dequeue Pointer. On Renesas uPD720201 host controllers, incorrect DESI bits cause an interrupt storm. For comparison, VIA VL805 host controllers do not exhibit such problems. Perhaps they do not take advantage of the optimization afforded by the DESI bits. To fix the issue, assign the segment number to each struct xhci_segment in xhci_segment_alloc(). When advancing the Dequeue Pointer in xhci_update_erst_dequeue(), write the segment number to the DESI bits. On driver probe, set the DESI bits to zero in xhci_set_hc_event_deq() as processing starts in segment 0. Likewise on driver teardown, clear the DESI bits to zero in xhci_free_interrupter() when clearing the upper bits of the ERDP register. Previously those functions (incorrectly) treated the DESI bits as if they're declared RsvdP. Signed-off-by: Lukas Wunner Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231019102924.2797346-5-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci-mem.c | 25 +++++++++++-------------- drivers/usb/host/xhci-ring.c | 2 +- drivers/usb/host/xhci.h | 1 + 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 467d48902a44..f4f9ed5f3845 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -28,6 +28,7 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, unsigned int cycle_state, unsigned int max_packet, + unsigned int num, gfp_t flags) { struct xhci_segment *seg; @@ -59,6 +60,7 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, for (i = 0; i < TRBS_PER_SEGMENT; i++) seg->trbs[i].link.control |= cpu_to_le32(TRB_CYCLE); } + seg->num = num; seg->dma = dma; seg->next = NULL; @@ -324,6 +326,7 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, enum xhci_ring_type type, unsigned int max_packet, gfp_t flags) { struct xhci_segment *prev; + unsigned int num = 0; bool chain_links; /* Set chain bit for 0.95 hosts, and for isoc rings on AMD 0.96 host */ @@ -331,16 +334,17 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, (type == TYPE_ISOC && (xhci->quirks & XHCI_AMD_0x96_HOST))); - prev = xhci_segment_alloc(xhci, cycle_state, max_packet, flags); + prev = xhci_segment_alloc(xhci, cycle_state, max_packet, num, flags); if (!prev) return -ENOMEM; - num_segs--; + num++; *first = prev; - while (num_segs > 0) { + while (num < num_segs) { struct xhci_segment *next; - next = xhci_segment_alloc(xhci, cycle_state, max_packet, flags); + next = xhci_segment_alloc(xhci, cycle_state, max_packet, num, + flags); if (!next) { prev = *first; while (prev) { @@ -353,7 +357,7 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, xhci_link_segments(prev, next, type, chain_links); prev = next; - num_segs--; + num++; } xhci_link_segments(prev, *first, type, chain_links); *last = prev; @@ -1837,7 +1841,6 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; size_t erst_size; - u64 tmp64; u32 tmp; if (!ir) @@ -1860,9 +1863,7 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) tmp &= ERST_SIZE_MASK; writel(tmp, &ir->ir_set->erst_size); - tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); - tmp64 &= (u64) ERST_PTR_MASK; - xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue); + xhci_write_64(xhci, ERST_EHB, &ir->ir_set->erst_dequeue); } /* free interrrupter event ring */ @@ -1972,7 +1973,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) static void xhci_set_hc_event_deq(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { - u64 temp; dma_addr_t deq; deq = xhci_trb_virt_to_dma(ir->event_ring->deq_seg, @@ -1980,15 +1980,12 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci, struct xhci_interrupter if (!deq) xhci_warn(xhci, "WARN something wrong with SW event ring dequeue ptr.\n"); /* Update HC event ring dequeue pointer */ - temp = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); - temp &= ERST_PTR_MASK; /* Don't clear the EHB bit (which is RW1C) because * there might be more events to service. */ - temp &= ~ERST_EHB; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Write event ring dequeue pointer, preserving EHB bit"); - xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp, + xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK), &ir->ir_set->erst_dequeue); } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index e59659d74570..d7caf575d78d 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2894,7 +2894,7 @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, return; /* Update HC event ring dequeue pointer */ - temp_64 &= ERST_DESI_MASK; + temp_64 = ir->event_ring->deq_seg->num & ERST_DESI_MASK; temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK); } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 422fc680c69e..b8f429b60e64 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1533,6 +1533,7 @@ struct xhci_segment { union xhci_trb *trbs; /* private to HCD */ struct xhci_segment *next; + unsigned int num; dma_addr_t dma; /* Max packet sized bounce buffer for td-fragmant alignment */ dma_addr_t bounce_dma; -- Gitee From 1af7f14bad0458502edaa1bea308c37e75733172 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 19 Oct 2023 13:29:21 +0300 Subject: [PATCH 13/16] xhci: split free interrupter into separate remove and free parts ANBZ: #26347 commit 47f503cf5f799ec02e5f4b7c3b9afe145eca2aef upstream The current function that both removes and frees an interrupter isn't optimal when using several interrupters. The array of interrupters need to be protected with a lock while removing interrupters, but the default xhci spin lock can't be used while freeing the interrupters event ring segment table as dma_free_coherent() should be called with IRQs enabled. There is no need to free the interrupter under the lock, so split this code into separate unlocked free part, and a lock protected remove part. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20231019102924.2797346-17-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci-mem.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index f4f9ed5f3845..433e60d04448 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1837,22 +1837,13 @@ int xhci_alloc_erst(struct xhci_hcd *xhci, } static void -xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) +xhci_remove_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { - struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - size_t erst_size; u32 tmp; if (!ir) return; - erst_size = sizeof(struct xhci_erst_entry) * (ir->erst.num_entries); - if (ir->erst.entries) - dma_free_coherent(dev, erst_size, - ir->erst.entries, - ir->erst.erst_dma_addr); - ir->erst.entries = NULL; - /* * Clean out interrupter registers except ERSTBA. Clearing either the * low or high 32 bits of ERSTBA immediately causes the controller to @@ -1865,10 +1856,28 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) xhci_write_64(xhci, ERST_EHB, &ir->ir_set->erst_dequeue); } +} + +static void +xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) +{ + struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + size_t erst_size; - /* free interrrupter event ring */ + if (!ir) + return; + + erst_size = sizeof(struct xhci_erst_entry) * ir->erst.num_entries; + if (ir->erst.entries) + dma_free_coherent(dev, erst_size, + ir->erst.entries, + ir->erst.erst_dma_addr); + ir->erst.entries = NULL; + + /* free interrupter event ring */ if (ir->event_ring) xhci_ring_free(xhci, ir->event_ring); + ir->event_ring = NULL; kfree(ir); @@ -1881,6 +1890,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) cancel_delayed_work_sync(&xhci->cmd_timer); + xhci_remove_interrupter(xhci, xhci->interrupter); xhci_free_interrupter(xhci, xhci->interrupter); xhci->interrupter = NULL; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring"); -- Gitee From 0d457d9b687646332119d9edecccf5a3c245bb52 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Tue, 2 Jan 2024 13:45:09 -0800 Subject: [PATCH 14/16] xhci: add support to allocate several interrupters ANBZ: #26347 commit c99b38c412343053e9af187e595793c8805bb9b8 upstream Modify the XHCI drivers to accommodate for handling multiple event rings in case there are multiple interrupters. Add the required APIs so clients are able to allocate/request for an interrupter ring, and pass this information back to the client driver. This allows for users to handle the resource accordingly, such as passing the event ring base address to an audio DSP. There is no actual support for multiple MSI/MSI-X vectors. [export xhci_initialize_ring_info() -wcheng] Signed-off-by: Mathias Nyman Signed-off-by: Wesley Cheng Link: https://lore.kernel.org/r/20240102214549.22498-2-quic_wcheng@quicinc.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci-debugfs.c | 2 +- drivers/usb/host/xhci-mem.c | 108 ++++++++++++++++++++++++++++---- drivers/usb/host/xhci-ring.c | 2 +- drivers/usb/host/xhci.c | 51 +++++++++------ drivers/usb/host/xhci.h | 6 +- 5 files changed, 137 insertions(+), 32 deletions(-) diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index 0bc7fe11f749..06a42b68446f 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -692,7 +692,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci) "command-ring", xhci->debugfs_root); - xhci_debugfs_create_ring_dir(xhci, &xhci->interrupter->event_ring, + xhci_debugfs_create_ring_dir(xhci, &xhci->interrupters[0]->event_ring, "event-ring", xhci->debugfs_root); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 433e60d04448..27a8d0a9647c 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -318,6 +318,7 @@ void xhci_initialize_ring_info(struct xhci_ring *ring, */ ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1; } +EXPORT_SYMBOL_GPL(xhci_initialize_ring_info); /* Allocate segments and link them for a ring */ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, @@ -1883,6 +1884,31 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) kfree(ir); } +void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrupter *ir) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + unsigned int intr_num; + + /* interrupter 0 is primary interrupter, don't touch it */ + if (!ir || !ir->intr_num || ir->intr_num >= xhci->max_interrupters) + xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n"); + + /* fixme, should we check xhci->interrupter[intr_num] == ir */ + /* fixme locking */ + + spin_lock_irq(&xhci->lock); + + intr_num = ir->intr_num; + + xhci_remove_interrupter(xhci, ir); + xhci->interrupters[intr_num] = NULL; + + spin_unlock_irq(&xhci->lock); + + xhci_free_interrupter(xhci, ir); +} +EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter); + void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; @@ -1890,10 +1916,14 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) cancel_delayed_work_sync(&xhci->cmd_timer); - xhci_remove_interrupter(xhci, xhci->interrupter); - xhci_free_interrupter(xhci, xhci->interrupter); - xhci->interrupter = NULL; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring"); + for (i = 0; i < xhci->max_interrupters; i++) { + if (xhci->interrupters[i]) { + xhci_remove_interrupter(xhci, xhci->interrupters[i]); + xhci_free_interrupter(xhci, xhci->interrupters[i]); + xhci->interrupters[i] = NULL; + } + } + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed interrupters"); if (xhci->lpm_command) xhci_free_command(xhci, xhci->lpm_command); @@ -1966,6 +1996,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) for (i = 0; i < xhci->num_port_caps; i++) kfree(xhci->port_caps[i].psi); kfree(xhci->port_caps); + kfree(xhci->interrupters); xhci->num_port_caps = 0; xhci->usb2_rhub.ports = NULL; @@ -1974,6 +2005,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->rh_bw = NULL; xhci->ext_caps = NULL; xhci->port_caps = NULL; + xhci->interrupters = NULL; xhci->page_size = 0; xhci->page_shift = 0; @@ -2259,18 +2291,20 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) } static struct xhci_interrupter * -xhci_alloc_interrupter(struct xhci_hcd *xhci, gfp_t flags) +xhci_alloc_interrupter(struct xhci_hcd *xhci, int segs, gfp_t flags) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; struct xhci_interrupter *ir; - unsigned int num_segs; + unsigned int num_segs = segs; int ret; ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); if (!ir) return NULL; - num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2), + /* number of ring segments should be greater than 0 */ + if (segs <= 0) + num_segs = min_t(unsigned int, 1 << HCS_ERST_MAX(xhci->hcs_params2), ERST_MAX_SEGS); ir->event_ring = xhci_ring_alloc(xhci, num_segs, 1, TYPE_EVENT, 0, @@ -2305,6 +2339,13 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, return -EINVAL; } + if (xhci->interrupters[intr_num]) { + xhci_warn(xhci, "Interrupter %d\n already set up", intr_num); + return -EINVAL; + } + + xhci->interrupters[intr_num] = ir; + ir->intr_num = intr_num; ir->ir_set = &xhci->run_regs->ir_set[intr_num]; /* set ERST count with the number of entries in the segment table */ @@ -2324,10 +2365,52 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, return 0; } +struct xhci_interrupter * +xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_interrupter *ir; + unsigned int i; + int err = -ENOSPC; + + if (!xhci->interrupters || xhci->max_interrupters <= 1) + return NULL; + + ir = xhci_alloc_interrupter(xhci, num_seg, GFP_KERNEL); + if (!ir) + return NULL; + + spin_lock_irq(&xhci->lock); + + /* Find available secondary interrupter, interrupter 0 is reserved for primary */ + for (i = 1; i < xhci->max_interrupters; i++) { + if (xhci->interrupters[i] == NULL) { + err = xhci_add_interrupter(xhci, ir, i); + break; + } + } + + spin_unlock_irq(&xhci->lock); + + if (err) { + xhci_warn(xhci, "Failed to add secondary interrupter, max interrupters %d\n", + xhci->max_interrupters); + xhci_free_interrupter(xhci, ir); + return NULL; + } + + xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n", + i, xhci->max_interrupters); + + return ir; +} +EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter); + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { - dma_addr_t dma; + struct xhci_interrupter *ir; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + dma_addr_t dma; unsigned int val, val2; u64 val_64; u32 page_size, temp; @@ -2463,11 +2546,14 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) /* Allocate and set up primary interrupter 0 with an event ring. */ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring"); - xhci->interrupter = xhci_alloc_interrupter(xhci, flags); - if (!xhci->interrupter) + xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters), + flags, dev_to_node(dev)); + + ir = xhci_alloc_interrupter(xhci, 0, flags); + if (!ir) goto fail; - if (xhci_add_interrupter(xhci, xhci->interrupter, 0)) + if (xhci_add_interrupter(xhci, ir, 0)) goto fail; /* diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index d7caf575d78d..73ff80d97d65 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2948,7 +2948,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) writel(status, &xhci->op_regs->status); /* This is the handler of the primary interrupter */ - ir = xhci->interrupter; + ir = xhci->interrupters[0]; if (!hcd->msi_enabled) { u32 irq_pending; irq_pending = readl(&ir->ir_set->irq_pending); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 9a4325f8f17a..28f369cfd805 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -631,7 +631,7 @@ static int xhci_init(struct usb_hcd *hcd) static int xhci_run_finished(struct xhci_hcd *xhci) { - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir = xhci->interrupters[0]; unsigned long flags; u32 temp; @@ -685,7 +685,7 @@ int xhci_run(struct usb_hcd *hcd) u64 temp_64; int ret; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir = xhci->interrupters[0]; /* Start the xHCI host controller running only after the USB 2.0 roothub * is setup. */ @@ -748,7 +748,7 @@ static void xhci_stop(struct usb_hcd *hcd) { u32 temp; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir = xhci->interrupters[0]; mutex_lock(&xhci->mutex); @@ -852,36 +852,51 @@ EXPORT_SYMBOL_GPL(xhci_shutdown); #ifdef CONFIG_PM static void xhci_save_registers(struct xhci_hcd *xhci) { - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir; + unsigned int i; xhci->s3.command = readl(&xhci->op_regs->command); xhci->s3.dev_nt = readl(&xhci->op_regs->dev_notification); xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); xhci->s3.config_reg = readl(&xhci->op_regs->config_reg); - if (!ir) - return; + /* save both primary and all secondary interrupters */ + /* fixme, shold we lock to prevent race with remove secondary interrupter? */ + for (i = 0; i < xhci->max_interrupters; i++) { + ir = xhci->interrupters[i]; + if (!ir) + continue; - ir->s3_erst_size = readl(&ir->ir_set->erst_size); - ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); - ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); - ir->s3_irq_pending = readl(&ir->ir_set->irq_pending); - ir->s3_irq_control = readl(&ir->ir_set->irq_control); + ir->s3_erst_size = readl(&ir->ir_set->erst_size); + ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); + ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + ir->s3_irq_pending = readl(&ir->ir_set->irq_pending); + ir->s3_irq_control = readl(&ir->ir_set->irq_control); + } } static void xhci_restore_registers(struct xhci_hcd *xhci) { - struct xhci_interrupter *ir = xhci->interrupter; + struct xhci_interrupter *ir; + unsigned int i; writel(xhci->s3.command, &xhci->op_regs->command); writel(xhci->s3.dev_nt, &xhci->op_regs->dev_notification); xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr); writel(xhci->s3.config_reg, &xhci->op_regs->config_reg); - writel(ir->s3_erst_size, &ir->ir_set->erst_size); - xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base); - xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue); - writel(ir->s3_irq_pending, &ir->ir_set->irq_pending); - writel(ir->s3_irq_control, &ir->ir_set->irq_control); + + /* FIXME should we lock to protect against freeing of interrupters */ + for (i = 0; i < xhci->max_interrupters; i++) { + ir = xhci->interrupters[i]; + if (!ir) + continue; + + writel(ir->s3_erst_size, &ir->ir_set->erst_size); + xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base); + xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue); + writel(ir->s3_irq_pending, &ir->ir_set->irq_pending); + writel(ir->s3_irq_control, &ir->ir_set->irq_control); + } } static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) @@ -1240,7 +1255,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - xhci_disable_interrupter(xhci->interrupter); + xhci_disable_interrupter(xhci->interrupters[0]); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index b8f429b60e64..659515045dec 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1790,7 +1790,7 @@ struct xhci_hcd { struct reset_control *reset; /* data structures */ struct xhci_device_context_array *dcbaa; - struct xhci_interrupter *interrupter; + struct xhci_interrupter **interrupters; struct xhci_ring *cmd_ring; unsigned int cmd_ring_state; #define CMD_RING_STATE_RUNNING (1 << 0) @@ -2079,6 +2079,10 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, int type, gfp_t flags); void xhci_free_container_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx); +struct xhci_interrupter * +xhci_create_secondary_interrupter(struct usb_hcd *hcd, int num_seg); +void xhci_remove_secondary_interrupter(struct usb_hcd + *hcd, struct xhci_interrupter *ir); /* xHCI host controller glue */ typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); -- Gitee From 53e09db53f3ceb516c445e75d29b2baf580347dc Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 10 Apr 2025 18:18:27 +0300 Subject: [PATCH 15/16] xhci: Limit time spent with xHC interrupts disabled during bus resume ANBZ: #26347 commit bea5892d0ed274e03655223d1977cf59f9aff2f2 upstream Current xhci bus resume implementation prevents xHC host from generating interrupts during high-speed USB 2 and super-speed USB 3 bus resume. Only reason to disable interrupts during bus resume would be to prevent the interrupt handler from interfering with the resume process of USB 2 ports. Host initiated resume of USB 2 ports is done in two stages. The xhci driver first transitions the port from 'U3' to 'Resume' state, then wait in Resume for 20ms, and finally moves port to U0 state. xhci driver can't prevent interrupts by keeping the xhci spinlock due to this 20ms sleep. Limit interrupt disabling to the USB 2 port resume case only. resuming USB 2 ports in bus resume is only done in special cases where USB 2 ports had to be forced to suspend during bus suspend. The current way of preventing interrupts by clearing the 'Interrupt Enable' (INTE) bit in USBCMD register won't prevent the Interrupter registers 'Interrupt Pending' (IP), 'Event Handler Busy' (EHB) and USBSTS register Event Interrupt (EINT) bits from being set. New interrupts can't be issued before those bits are properly clered. Disable interrupts by clearing the interrupter register 'Interrupt Enable' (IE) bit instead. This way IP, EHB and INTE won't be set before IE is enabled again and a new interrupt is triggered. Reported-by: Devyn Liu Closes: https://lore.kernel.org/linux-usb/b1a9e2d51b4d4ff7a304f77c5be8164e@huawei.com/ Cc: stable@vger.kernel.org Tested-by: Devyn Liu Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20250410151828.2868740-6-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci-hub.c | 30 ++++++++++++++++-------------- drivers/usb/host/xhci.c | 4 ++-- drivers/usb/host/xhci.h | 2 ++ 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 94adae8b19f0..7fb683d06e0b 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1762,9 +1762,10 @@ int xhci_bus_resume(struct usb_hcd *hcd) int slot_id; int sret; u32 next_state; - u32 temp, portsc; + u32 portsc; struct xhci_hub *rhub; struct xhci_port **ports; + bool disabled_irq = false; rhub = xhci_get_rhub(hcd); ports = rhub->ports; @@ -1780,17 +1781,20 @@ int xhci_bus_resume(struct usb_hcd *hcd) return -ESHUTDOWN; } - /* delay the irqs */ - temp = readl(&xhci->op_regs->command); - temp &= ~CMD_EIE; - writel(temp, &xhci->op_regs->command); - /* bus specific resume for ports we suspended at bus_suspend */ - if (hcd->speed >= HCD_USB3) + if (hcd->speed >= HCD_USB3) { next_state = XDEV_U0; - else + } else { next_state = XDEV_RESUME; - + if (bus_state->bus_suspended) { + /* + * prevent port event interrupts from interfering + * with usb2 port resume process + */ + xhci_disable_interrupter(xhci->interrupters[0]); + disabled_irq = true; + } + } port_index = max_ports; while (port_index--) { portsc = readl(ports[port_index]->addr); @@ -1859,11 +1863,9 @@ int xhci_bus_resume(struct usb_hcd *hcd) (void) readl(&xhci->op_regs->command); bus_state->next_statechange = jiffies + msecs_to_jiffies(5); - /* re-enable irqs */ - temp = readl(&xhci->op_regs->command); - temp |= CMD_EIE; - writel(temp, &xhci->op_regs->command); - temp = readl(&xhci->op_regs->command); + /* re-enable interrupter */ + if (disabled_irq) + xhci_enable_interrupter(xhci->interrupters[0]); spin_unlock_irqrestore(&xhci->lock, flags); return 0; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 28f369cfd805..cf7ddeea0f37 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -288,7 +288,7 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) xhci_info(xhci, "Fault detected\n"); } -static int xhci_enable_interrupter(struct xhci_interrupter *ir) +int xhci_enable_interrupter(struct xhci_interrupter *ir) { u32 iman; @@ -301,7 +301,7 @@ static int xhci_enable_interrupter(struct xhci_interrupter *ir) return 0; } -static int xhci_disable_interrupter(struct xhci_interrupter *ir) +int xhci_disable_interrupter(struct xhci_interrupter *ir) { u32 iman; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 659515045dec..8131052fb53f 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2111,6 +2111,8 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags); +int xhci_enable_interrupter(struct xhci_interrupter *ir); +int xhci_disable_interrupter(struct xhci_interrupter *ir); /* xHCI ring, segment, TRB, and TD functions */ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb); -- Gitee From df48257f8a2298dd02462618bb6298a01b21c741 Mon Sep 17 00:00:00 2001 From: Longfang Liu Date: Mon, 11 Sep 2023 18:22:16 +0800 Subject: [PATCH 16/16] xhci:fix USB xhci controller issue ANBZ: #26347 commit 1a86940388049d27b6cb387c041c7d18e25af830 openeuler driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I7ZPUX CVE: NA ---------------------------------------------------------------------- When the current HiSilicon USB xhci controller formats the faulty U disk, it will trigger a controller exception error. This will cause errors in the control logic of the xhci controller and driver software. In the end, all USB devices on the xhci controller cannot be used. By introducing a noop command operation, restore the logic of the xhci controller and driver software, and restore all USB devices on the xhci controller to normal. Signed-off-by: Longfang Liu Signed-off-by: JiangShui Yang Signed-off-by: Lin Ruier <2878455989@qq.com> --- drivers/usb/host/xhci-pci.c | 4 ++++ drivers/usb/host/xhci-ring.c | 10 ++++++---- drivers/usb/host/xhci.h | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 0a3f26b66b23..c990f34b65ca 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -368,6 +368,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (xhci->quirks & XHCI_RESET_ON_RESUME) xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "QUIRK: Resetting on resume"); + + if (pdev->vendor == PCI_VENDOR_ID_HUAWEI && + (pdev->device == 0xa23c || pdev->device == 0xa23d)) + xhci->quirks |= XHCI_USB3_NOOP; } #ifdef CONFIG_ACPI diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 73ff80d97d65..603260541c13 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1489,14 +1489,16 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, cmd_trb = xhci->cmd_ring->dequeue; trace_xhci_handle_command(xhci->cmd_ring, &cmd_trb->generic); - + cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status)); cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, cmd_trb); /* * Check whether the completion event is for our internal kept * command. */ - if (!cmd_dequeue_dma || cmd_dma != (u64)cmd_dequeue_dma) { + if (!cmd_dequeue_dma || ((cmd_dma != (u64)cmd_dequeue_dma) && + !((xhci->quirks & XHCI_USB3_NOOP) && (cmd_comp_code == + COMP_COMMAND_RING_STOPPED)))) { xhci_warn(xhci, "ERROR mismatched command completion event\n"); return; @@ -1506,8 +1508,6 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, cancel_delayed_work(&xhci->cmd_timer); - cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status)); - /* If CMD ring stopped we own the trbs between enqueue and dequeue */ if (cmd_comp_code == COMP_COMMAND_RING_STOPPED) { complete_all(&xhci->cmd_ring_stop_completion); @@ -1529,6 +1529,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, if (cmd_comp_code == COMP_COMMAND_ABORTED) { xhci->cmd_ring_state = CMD_RING_STATE_STOPPED; if (cmd->status == COMP_COMMAND_ABORTED) { + if (xhci->quirks & XHCI_USB3_NOOP) + trb_to_noop(cmd->command_trb, TRB_CMD_NOOP); if (xhci->current_cmd == cmd) xhci->current_cmd = NULL; goto event_handled; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 8131052fb53f..4c7062c067c5 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1844,6 +1844,7 @@ struct xhci_hcd { #define XHCI_STATE_HALTED (1 << 1) #define XHCI_STATE_REMOVING (1 << 2) unsigned long long quirks; +#define XHCI_USB3_NOOP BIT_ULL(63) #define XHCI_LINK_TRB_QUIRK BIT_ULL(0) #define XHCI_RESET_EP_QUIRK BIT_ULL(1) #define XHCI_NEC_HOST BIT_ULL(2) -- Gitee