From: Wesley Cheng <quic_wcheng@quicinc.com> To: <srinivas.kandagatla@linaro.org>, <mathias.nyman@intel.com>, <perex@perex.cz>, <broonie@kernel.org>, <lgirdwood@gmail.com>, <krzysztof.kozlowski+dt@linaro.org>, <agross@kernel.org>, <Thinh.Nguyen@synopsys.com>, <bgoswami@quicinc.com>, <andersson@kernel.org>, <robh+dt@kernel.org>, <gregkh@linuxfoundation.org>, <tiwai@suse.com> Cc: <linux-kernel@vger.kernel.org>, <linux-arm-msm@vger.kernel.org>, <alsa-devel@alsa-project.org>, <devicetree@vger.kernel.org>, <linux-usb@vger.kernel.org>, <quic_jackp@quicinc.com>, <quic_plai@quicinc.com>, Mathias Nyman <mathias.nyman@linux.intel.com>, Wesley Cheng <quic_wcheng@quicinc.com> Subject: [PATCH v3 01/28] xhci: Add support to allocate several interrupters Date: Wed, 8 Mar 2023 15:57:24 -0800 [thread overview] Message-ID: <20230308235751.495-2-quic_wcheng@quicinc.com> (raw) In-Reply-To: <20230308235751.495-1-quic_wcheng@quicinc.com> From: Mathias Nyman <mathias.nyman@linux.intel.com> Introduce xHCI APIs to allow for clients to allocate and free interrupters. This allocates an array of interrupters, which is based on the max_interrupters parameter. The primary interrupter is set as the first entry in the array, and secondary interrupters following after. Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com> --- drivers/usb/host/xhci-debugfs.c | 2 +- drivers/usb/host/xhci-mem.c | 97 +++++++++++++++++++++++++++++++-- drivers/usb/host/xhci-ring.c | 2 +- drivers/usb/host/xhci.c | 51 +++++++++++------ drivers/usb/host/xhci.h | 2 +- include/linux/usb/xhci-intr.h | 86 +++++++++++++++++++++++++++++ 6 files changed, 214 insertions(+), 26 deletions(-) create mode 100644 include/linux/usb/xhci-intr.h 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 d0a9467aa5fc..c303a8e1a33d 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1858,6 +1858,30 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) kfree(ir); } +/* + * Free a secondary interrupter slot. This will allow for other users to request for + * the secondary interrupter in the future. + */ +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; + unsigned long flags; + + if (!ir || !ir->intr_num || ir->intr_num > xhci->max_interrupters) { + xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n"); + return; + } + + /* fixme, shuld we check xhci->interrupter[intr_num] == ir */ + intr_num = ir->intr_num; + xhci_free_interrupter(xhci, ir); + spin_lock_irqsave(&xhci->lock, flags); + xhci->interrupters[intr_num] = NULL; + spin_unlock_irqrestore(&xhci->lock, flags); +} +EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter); + void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; @@ -1865,8 +1889,15 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) cancel_delayed_work_sync(&xhci->cmd_timer); - xhci_free_interrupter(xhci, xhci->interrupter); - xhci->interrupter = NULL; + for (i = 1; i < xhci->max_interrupters; i++) { + if (xhci->interrupters[i]) + xhci_remove_secondary_interrupter(xhci_to_hcd(xhci), + xhci->interrupters[i]); + } + + /* free the primary interrupter, interrupter number 0 */ + xhci_free_interrupter(xhci, xhci->interrupters[0]); + xhci->interrupters[0] = NULL; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring"); if (xhci->cmd_ring) @@ -1937,6 +1968,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; @@ -1945,6 +1977,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; @@ -2258,7 +2291,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags return NULL; } - if (xhci->interrupter) { + if (xhci->interrupters[intr_num]) { xhci_warn(xhci, "Can't allocate already set up interrupter %d\n", intr_num); return NULL; } @@ -2305,6 +2338,56 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags return NULL; } +/* + * Allocate a XHCI secondary interrupter slot. If the user requests a specific intr + * number, then check if the slot is available. Otherwise, fetch the first available + * entry within the interrupter array. + */ +struct xhci_interrupter * +xhci_create_secondary_interrupter(struct usb_hcd *hcd, int intr_num) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_interrupter *ir; + unsigned int i; + unsigned int idx = 0; + unsigned long flags; + + if (!xhci->interrupters || intr_num > xhci->max_interrupters) + return NULL; + + spin_lock_irqsave(&xhci->lock, flags); + /* find available secondary interrupter, interrupter 0 is reserved for primary */ + if (intr_num > 0) { + idx = intr_num; + } else { + for (i = 1; i < xhci->max_interrupters; i++) { + if (xhci->interrupters[i] == NULL) { + idx = i; + break; + } + } + } + + if (idx > 0) { + ir = xhci_alloc_interrupter(xhci, idx, GFP_KERNEL); + if (!ir) { + spin_unlock_irqrestore(&xhci->lock, flags); + return NULL; + } + ir->intr_num = idx; + xhci->interrupters[idx] = ir; + spin_unlock_irqrestore(&xhci->lock, flags); + + return ir; + } + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_warn(xhci, "Can't add new secondary interrupter, max interrupters %d\n", + xhci->max_interrupters); + + return NULL; +} +EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter); + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { dma_addr_t dma; @@ -2429,8 +2512,12 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) /* allocate and set up primary interrupter with an event ring. */ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring"); - xhci->interrupter = xhci_alloc_interrupter(xhci, 0, flags); - if (!xhci->interrupter) + + xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters), + flags, dev_to_node(dev)); + + xhci->interrupters[0] = xhci_alloc_interrupter(xhci, 0, flags); + if (!xhci->interrupters[0]) goto fail; xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index eb788c60c1c0..445a79f36a60 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3054,7 +3054,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 6183ce8574b1..88435b9cd66e 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -638,7 +638,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; @@ -690,7 +690,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. */ @@ -758,7 +758,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); @@ -857,36 +857,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) @@ -1251,7 +1266,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 786002bb35db..43118ce83cca 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1800,7 +1800,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) diff --git a/include/linux/usb/xhci-intr.h b/include/linux/usb/xhci-intr.h new file mode 100644 index 000000000000..738b0f0481a6 --- /dev/null +++ b/include/linux/usb/xhci-intr.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LINUX_XHCI_INTR_H +#define __LINUX_XHCI_INTR_H + +#include <linux/kernel.h> + +struct xhci_erst_entry { + /* 64-bit event ring segment address */ + __le64 seg_addr; + __le32 seg_size; + /* Set to zero */ + __le32 rsvd; +}; + +enum xhci_ring_type { + TYPE_CTRL = 0, + TYPE_ISOC, + TYPE_BULK, + TYPE_INTR, + TYPE_STREAM, + TYPE_COMMAND, + TYPE_EVENT, +}; + +struct xhci_erst { + struct xhci_erst_entry *entries; + unsigned int num_entries; + /* xhci->event_ring keeps track of segment dma addresses */ + dma_addr_t erst_dma_addr; + /* Num entries the ERST can contain */ + unsigned int erst_size; +}; + +struct xhci_segment { + union xhci_trb *trbs; + /* private to HCD */ + struct xhci_segment *next; + dma_addr_t dma; + /* Max packet sized bounce buffer for td-fragmant alignment */ + dma_addr_t bounce_dma; + void *bounce_buf; + unsigned int bounce_offs; + unsigned int bounce_len; +}; + +struct xhci_ring { + struct xhci_segment *first_seg; + struct xhci_segment *last_seg; + union xhci_trb *enqueue; + struct xhci_segment *enq_seg; + union xhci_trb *dequeue; + struct xhci_segment *deq_seg; + struct list_head td_list; + /* + * Write the cycle state into the TRB cycle field to give ownership of + * the TRB to the host controller (if we are the producer), or to check + * if we own the TRB (if we are the consumer). See section 4.9.1. + */ + u32 cycle_state; + unsigned int stream_id; + unsigned int num_segs; + unsigned int num_trbs_free; + unsigned int num_trbs_free_temp; + unsigned int bounce_buf_len; + enum xhci_ring_type type; + bool last_td_was_short; + struct radix_tree_root *trb_address_map; +}; + +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; +}; + +struct xhci_interrupter * +xhci_create_secondary_interrupter(struct usb_hcd *hcd, int intr_num); +void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrupter *ir); +#endif
WARNING: multiple messages have this Message-ID (diff)
From: Wesley Cheng <quic_wcheng@quicinc.com> To: <srinivas.kandagatla@linaro.org>, <mathias.nyman@intel.com>, <perex@perex.cz>, <broonie@kernel.org>, <lgirdwood@gmail.com>, <krzysztof.kozlowski+dt@linaro.org>, <agross@kernel.org>, <Thinh.Nguyen@synopsys.com>, <bgoswami@quicinc.com>, <andersson@kernel.org>, <robh+dt@kernel.org>, <gregkh@linuxfoundation.org>, <tiwai@suse.com> Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, alsa-devel@alsa-project.org, devicetree@vger.kernel.org, linux-usb@vger.kernel.org, quic_jackp@quicinc.com, quic_plai@quicinc.com, Mathias Nyman <mathias.nyman@linux.intel.com>, Wesley Cheng <quic_wcheng@quicinc.com> Subject: [PATCH v3 01/28] xhci: Add support to allocate several interrupters Date: Wed, 8 Mar 2023 15:57:24 -0800 [thread overview] Message-ID: <20230308235751.495-2-quic_wcheng@quicinc.com> (raw) In-Reply-To: <20230308235751.495-1-quic_wcheng@quicinc.com> From: Mathias Nyman <mathias.nyman@linux.intel.com> Introduce xHCI APIs to allow for clients to allocate and free interrupters. This allocates an array of interrupters, which is based on the max_interrupters parameter. The primary interrupter is set as the first entry in the array, and secondary interrupters following after. Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com> --- drivers/usb/host/xhci-debugfs.c | 2 +- drivers/usb/host/xhci-mem.c | 97 +++++++++++++++++++++++++++++++-- drivers/usb/host/xhci-ring.c | 2 +- drivers/usb/host/xhci.c | 51 +++++++++++------ drivers/usb/host/xhci.h | 2 +- include/linux/usb/xhci-intr.h | 86 +++++++++++++++++++++++++++++ 6 files changed, 214 insertions(+), 26 deletions(-) create mode 100644 include/linux/usb/xhci-intr.h 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 d0a9467aa5fc..c303a8e1a33d 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1858,6 +1858,30 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) kfree(ir); } +/* + * Free a secondary interrupter slot. This will allow for other users to request for + * the secondary interrupter in the future. + */ +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; + unsigned long flags; + + if (!ir || !ir->intr_num || ir->intr_num > xhci->max_interrupters) { + xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n"); + return; + } + + /* fixme, shuld we check xhci->interrupter[intr_num] == ir */ + intr_num = ir->intr_num; + xhci_free_interrupter(xhci, ir); + spin_lock_irqsave(&xhci->lock, flags); + xhci->interrupters[intr_num] = NULL; + spin_unlock_irqrestore(&xhci->lock, flags); +} +EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter); + void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; @@ -1865,8 +1889,15 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) cancel_delayed_work_sync(&xhci->cmd_timer); - xhci_free_interrupter(xhci, xhci->interrupter); - xhci->interrupter = NULL; + for (i = 1; i < xhci->max_interrupters; i++) { + if (xhci->interrupters[i]) + xhci_remove_secondary_interrupter(xhci_to_hcd(xhci), + xhci->interrupters[i]); + } + + /* free the primary interrupter, interrupter number 0 */ + xhci_free_interrupter(xhci, xhci->interrupters[0]); + xhci->interrupters[0] = NULL; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring"); if (xhci->cmd_ring) @@ -1937,6 +1968,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; @@ -1945,6 +1977,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; @@ -2258,7 +2291,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags return NULL; } - if (xhci->interrupter) { + if (xhci->interrupters[intr_num]) { xhci_warn(xhci, "Can't allocate already set up interrupter %d\n", intr_num); return NULL; } @@ -2305,6 +2338,56 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags return NULL; } +/* + * Allocate a XHCI secondary interrupter slot. If the user requests a specific intr + * number, then check if the slot is available. Otherwise, fetch the first available + * entry within the interrupter array. + */ +struct xhci_interrupter * +xhci_create_secondary_interrupter(struct usb_hcd *hcd, int intr_num) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_interrupter *ir; + unsigned int i; + unsigned int idx = 0; + unsigned long flags; + + if (!xhci->interrupters || intr_num > xhci->max_interrupters) + return NULL; + + spin_lock_irqsave(&xhci->lock, flags); + /* find available secondary interrupter, interrupter 0 is reserved for primary */ + if (intr_num > 0) { + idx = intr_num; + } else { + for (i = 1; i < xhci->max_interrupters; i++) { + if (xhci->interrupters[i] == NULL) { + idx = i; + break; + } + } + } + + if (idx > 0) { + ir = xhci_alloc_interrupter(xhci, idx, GFP_KERNEL); + if (!ir) { + spin_unlock_irqrestore(&xhci->lock, flags); + return NULL; + } + ir->intr_num = idx; + xhci->interrupters[idx] = ir; + spin_unlock_irqrestore(&xhci->lock, flags); + + return ir; + } + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_warn(xhci, "Can't add new secondary interrupter, max interrupters %d\n", + xhci->max_interrupters); + + return NULL; +} +EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter); + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { dma_addr_t dma; @@ -2429,8 +2512,12 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) /* allocate and set up primary interrupter with an event ring. */ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring"); - xhci->interrupter = xhci_alloc_interrupter(xhci, 0, flags); - if (!xhci->interrupter) + + xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters), + flags, dev_to_node(dev)); + + xhci->interrupters[0] = xhci_alloc_interrupter(xhci, 0, flags); + if (!xhci->interrupters[0]) goto fail; xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index eb788c60c1c0..445a79f36a60 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3054,7 +3054,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 6183ce8574b1..88435b9cd66e 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -638,7 +638,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; @@ -690,7 +690,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. */ @@ -758,7 +758,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); @@ -857,36 +857,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) @@ -1251,7 +1266,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 786002bb35db..43118ce83cca 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1800,7 +1800,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) diff --git a/include/linux/usb/xhci-intr.h b/include/linux/usb/xhci-intr.h new file mode 100644 index 000000000000..738b0f0481a6 --- /dev/null +++ b/include/linux/usb/xhci-intr.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LINUX_XHCI_INTR_H +#define __LINUX_XHCI_INTR_H + +#include <linux/kernel.h> + +struct xhci_erst_entry { + /* 64-bit event ring segment address */ + __le64 seg_addr; + __le32 seg_size; + /* Set to zero */ + __le32 rsvd; +}; + +enum xhci_ring_type { + TYPE_CTRL = 0, + TYPE_ISOC, + TYPE_BULK, + TYPE_INTR, + TYPE_STREAM, + TYPE_COMMAND, + TYPE_EVENT, +}; + +struct xhci_erst { + struct xhci_erst_entry *entries; + unsigned int num_entries; + /* xhci->event_ring keeps track of segment dma addresses */ + dma_addr_t erst_dma_addr; + /* Num entries the ERST can contain */ + unsigned int erst_size; +}; + +struct xhci_segment { + union xhci_trb *trbs; + /* private to HCD */ + struct xhci_segment *next; + dma_addr_t dma; + /* Max packet sized bounce buffer for td-fragmant alignment */ + dma_addr_t bounce_dma; + void *bounce_buf; + unsigned int bounce_offs; + unsigned int bounce_len; +}; + +struct xhci_ring { + struct xhci_segment *first_seg; + struct xhci_segment *last_seg; + union xhci_trb *enqueue; + struct xhci_segment *enq_seg; + union xhci_trb *dequeue; + struct xhci_segment *deq_seg; + struct list_head td_list; + /* + * Write the cycle state into the TRB cycle field to give ownership of + * the TRB to the host controller (if we are the producer), or to check + * if we own the TRB (if we are the consumer). See section 4.9.1. + */ + u32 cycle_state; + unsigned int stream_id; + unsigned int num_segs; + unsigned int num_trbs_free; + unsigned int num_trbs_free_temp; + unsigned int bounce_buf_len; + enum xhci_ring_type type; + bool last_td_was_short; + struct radix_tree_root *trb_address_map; +}; + +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; +}; + +struct xhci_interrupter * +xhci_create_secondary_interrupter(struct usb_hcd *hcd, int intr_num); +void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrupter *ir); +#endif
next prev parent reply other threads:[~2023-03-08 23:58 UTC|newest] Thread overview: 137+ messages / expand[flat|nested] mbox.gz Atom feed top 2023-03-08 23:57 [PATCH v3 00/28] Introduce QC USB SND audio offloading support Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng [this message] 2023-03-08 23:57 ` [PATCH v3 01/28] xhci: Add support to allocate several interrupters Wesley Cheng 2023-03-09 10:34 ` Oliver Neukum 2023-03-09 10:34 ` Oliver Neukum via Alsa-devel 2023-03-09 10:51 ` Takashi Iwai 2023-03-09 10:51 ` Takashi Iwai 2023-03-10 15:07 ` Mathias Nyman 2023-03-13 20:08 ` Wesley Cheng 2023-03-13 20:08 ` Wesley Cheng 2023-04-25 1:17 ` Wesley Cheng 2023-04-25 1:17 ` Wesley Cheng 2023-03-13 20:32 ` Wesley Cheng 2023-03-13 20:32 ` Wesley Cheng 2023-06-23 22:37 ` Wesley Cheng 2023-06-26 13:55 ` Mathias Nyman 2023-06-26 15:05 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 02/28] usb: xhci: Add XHCI APIs to support USB offloading Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-09 6:38 ` Greg KH 2023-03-09 6:38 ` Greg KH 2023-03-09 19:51 ` Wesley Cheng 2023-03-09 19:51 ` Wesley Cheng 2023-03-10 12:17 ` Claudiu.Beznea 2023-03-10 12:17 ` Claudiu.Beznea--- via Alsa-devel 2023-03-08 23:57 ` [PATCH v3 03/28] usb: host: xhci-mem: Cleanup pending secondary event ring events Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 04/28] ASoC: Add SOC USB APIs for adding an USB backend Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-09 6:41 ` Greg KH 2023-03-09 6:41 ` Greg KH 2023-03-08 23:57 ` [PATCH v3 05/28] ASoC: dt-bindings: qcom,q6dsp-lpass-ports: Add USB_RX port Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-09 9:00 ` Srinivas Kandagatla 2023-03-08 23:57 ` [PATCH v3 06/28] ASoC: qcom: qdsp6: Introduce USB AFE port to q6dsp Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-09 9:01 ` Srinivas Kandagatla 2023-03-08 23:57 ` [PATCH v3 07/28] ASoC: qdsp6: q6afe: Increase APR timeout Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 08/28] ASoC: qcom: Add USB backend ASoC driver for Q6 Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-09 9:01 ` Srinivas Kandagatla 2023-03-09 19:38 ` Wesley Cheng 2023-03-09 19:38 ` Wesley Cheng 2023-03-10 7:21 ` Srinivas Kandagatla 2023-03-25 1:15 ` Wesley Cheng 2023-03-25 1:15 ` Wesley Cheng 2023-03-10 12:22 ` Claudiu.Beznea 2023-03-10 12:22 ` Claudiu.Beznea--- via Alsa-devel 2023-03-08 23:57 ` [PATCH v3 09/28] sound: usb: card: Introduce USB SND platform op callbacks Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-09 6:44 ` Greg KH 2023-03-09 6:44 ` Greg KH 2023-03-09 14:10 ` Takashi Iwai 2023-03-09 14:10 ` Takashi Iwai 2023-03-09 11:16 ` Oliver Neukum 2023-03-09 11:16 ` Oliver Neukum via Alsa-devel 2023-03-08 23:57 ` [PATCH v3 10/28] sound: usb: Export USB SND APIs for modules Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-09 6:29 ` Greg KH 2023-03-09 6:29 ` Greg KH 2023-03-08 23:57 ` [PATCH v3 11/28] dt-bindings: usb: dwc3: Add snps,num-hc-interrupters definition Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-10 8:50 ` Krzysztof Kozlowski 2023-03-11 13:40 ` Rob Herring 2023-03-11 13:40 ` Rob Herring 2023-08-29 2:05 ` Wesley Cheng 2023-08-29 6:33 ` Krzysztof Kozlowski 2023-08-29 7:19 ` Wesley Cheng 2023-08-29 7:42 ` Krzysztof Kozlowski 2023-08-29 7:50 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 12/28] usb: dwc3: Add DT parameter to specify maximum number of interrupters Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 13/28] usb: host: xhci-plat: Set XHCI max interrupters if property is present Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 14/28] sound: usb: pcm: Export fixed rate check USB SND API Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 15/28] sound: usb: Introduce QC USB SND offloading support Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-09 8:29 ` Takashi Sakamoto 2023-03-09 8:29 ` Takashi Sakamoto 2023-03-09 17:54 ` Pierre-Louis Bossart 2023-03-08 23:57 ` [PATCH v3 16/28] sound: usb: card: Check for support for requested audio format Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 17/28] sound: soc: soc-usb: Add PCM format check API for USB backend Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 18/28] sound: soc: qcom: qusb6: Ensure PCM format is supported by USB audio device Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 19/28] sound: usb: Prevent starting of audio stream if in use Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 20/28] ASoC: dt-bindings: Add Q6USB backend bindings Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-10 8:54 ` Krzysztof Kozlowski 2023-06-23 0:15 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 21/28] ASoC: dt-bindings: Update example for enabling USB offload on SM8250 Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-10 8:56 ` Krzysztof Kozlowski 2023-06-23 0:51 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 22/28] ASoC: qcom: qdsp6: q6afe: Split USB AFE dev_token param into separate API Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-09 9:01 ` Srinivas Kandagatla 2023-03-09 19:39 ` Wesley Cheng 2023-03-09 19:39 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 23/28] sound: Pass USB SND card and PCM information to SOC USB Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 24/28] sound: soc: qdsp6: Add SND kcontrol to select offload device Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 25/28] sound: soc: qdsp6: Add SND kcontrol for fetching offload status Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 26/28] sound: usb: qc_audio_offload: Use card and PCM index from QMI request Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-08 23:57 ` [PATCH v3 27/28] sound: usb: card: Allow for rediscovery of connected USB SND devices Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-09 11:32 ` Oliver Neukum via Alsa-devel 2023-03-09 11:32 ` Oliver Neukum 2023-03-08 23:57 ` [PATCH v3 28/28] sound: soc: soc-usb: Rediscover USB SND devices on USB port add Wesley Cheng 2023-03-08 23:57 ` Wesley Cheng 2023-03-09 6:46 ` [PATCH v3 00/28] Introduce QC USB SND audio offloading support Greg KH 2023-03-09 6:46 ` Greg KH 2023-03-09 17:13 ` Pierre-Louis Bossart 2023-03-09 21:10 ` Wesley Cheng 2023-03-09 21:10 ` Wesley Cheng 2023-03-10 0:37 ` Pierre-Louis Bossart 2023-03-13 23:43 ` Wesley Cheng 2023-03-13 23:43 ` Wesley Cheng 2023-03-14 0:42 ` Pierre-Louis Bossart 2023-03-14 1:42 ` Wesley Cheng 2023-03-14 1:42 ` Wesley Cheng 2023-03-14 2:22 ` Pierre-Louis Bossart 2023-03-15 0:08 ` Wesley Cheng 2023-03-15 0:08 ` Wesley Cheng 2023-03-15 14:30 ` Pierre-Louis Bossart 2023-03-15 16:29 ` Mark Brown 2023-03-15 16:29 ` Mark Brown 2023-03-15 19:42 ` Wesley Cheng 2023-03-15 19:42 ` Wesley Cheng
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20230308235751.495-2-quic_wcheng@quicinc.com \ --to=quic_wcheng@quicinc.com \ --cc=Thinh.Nguyen@synopsys.com \ --cc=agross@kernel.org \ --cc=alsa-devel@alsa-project.org \ --cc=andersson@kernel.org \ --cc=bgoswami@quicinc.com \ --cc=broonie@kernel.org \ --cc=devicetree@vger.kernel.org \ --cc=gregkh@linuxfoundation.org \ --cc=krzysztof.kozlowski+dt@linaro.org \ --cc=lgirdwood@gmail.com \ --cc=linux-arm-msm@vger.kernel.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux-usb@vger.kernel.org \ --cc=mathias.nyman@intel.com \ --cc=mathias.nyman@linux.intel.com \ --cc=perex@perex.cz \ --cc=quic_jackp@quicinc.com \ --cc=quic_plai@quicinc.com \ --cc=robh+dt@kernel.org \ --cc=srinivas.kandagatla@linaro.org \ --cc=tiwai@suse.com \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.