From: Sasha Levin <sashal@kernel.org>
To: stable@vger.kernel.org
Cc: Niklas Neronin <niklas.neronin@linux.intel.com>,
Mathias Nyman <mathias.nyman@linux.intel.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Sasha Levin <sashal@kernel.org>
Subject: [PATCH 6.12.y 3/4] usb: xhci: simplify handling of Structural Parameters 1 values
Date: Mon, 31 Aug 2026 12:42:32 -0400 [thread overview]
Message-ID: <20260831164233.269066-3-sashal@kernel.org> (raw)
In-Reply-To: <20260831164233.269066-1-sashal@kernel.org>
From: Niklas Neronin <niklas.neronin@linux.intel.com>
[ Upstream commit df08973556851b29bd78e79db696d992ed1b43f0 ]
The 32-bit read-only HCSPARAMS1 register contains the following fields:
Bits 7:0 - Number of Device Slots (MaxSlots)
Bits 18:8 - Number of Interrupters (MaxIntrs)
Bits 23:19 - Reserved
Bits 31:24 - Number of Ports (MaxPorts)
Since the register value is constant for the lifetime of the controller,
it is cached in 'xhci->hcs_params1'. However, platform drivers may
override the number of interrupters through a separate variable,
'xhci->max_interrupters', leaving only the maximum slots and ports values
still derived from the cached register.
To simplify the code and improve readability, replace 'xhci->hcs_params1'
with two dedicated 'u8' fields: 'xhci->max_slots' and 'xhci->max_ports'.
These values are initialized once and used directly instead of calling
'HCS_MAX_SLOTS()' and 'HCS_MAX_PORTS()' macros.
This change reduces code clutter without increasing memory usage.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Link: https://patch.msgid.link/20251119142417.2820519-16-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
[ Adjusted for 6.12, which lacks several later xhci cleanups:
- The xhci_init() split into xhci_hcd_page_size(),
xhci_enable_max_dev_slots(), xhci_set_cmd_ring_deq(),
xhci_set_doorbell_ptr() and xhci_set_dev_notifications() is not
present, so the xhci_enable_max_dev_slots() hunk is dropped. The
equivalent 6.12 code in xhci_mem_init() reads HCSPARAMS1 straight
from cap_regs and never touched xhci->hcs_params1, so it needs no
change.
- The port bandwidth debugfs files (xhci_port_bw_show() and friends)
do not exist, so that xhci-debugfs.c hunk is dropped.
- struct xhci_port still uses the 'addr' pointer to PORTSC rather than
the 'port_reg' pointer to the whole register set, so only the loop
bound is converted in xhci_setup_port_arrays().
- 6.12 has one extra user of the removed xhci->hcs_params1 field,
xhci_try_enable_msi() in xhci-pci.c, which upstream had already
converted to xhci->max_interrupters before this commit. Convert it
here as well, otherwise removing the field breaks the build. It is
equivalent: xhci_try_enable_msi() runs from the ->start() callback,
i.e. after xhci_gen_setup() has clamped xhci->max_interrupters to
HCS_MAX_INTRS(HCSPARAMS1), and nothing in 6.12 presets it. ]
Stable-dep-of: 78203d5b54a4 ("usb: xhci: bail out of setup if the controller is inaccessible")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/usb/host/xhci-debugfs.c | 10 +++-------
drivers/usb/host/xhci-hub.c | 2 +-
drivers/usb/host/xhci-mem.c | 31 +++++++++++++------------------
drivers/usb/host/xhci-pci.c | 9 ++++-----
drivers/usb/host/xhci-ring.c | 6 ++----
drivers/usb/host/xhci.c | 15 ++++++++-------
drivers/usb/host/xhci.h | 3 ++-
7 files changed, 33 insertions(+), 43 deletions(-)
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index 570210e8a8e87..8e334b7553012 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -629,20 +629,16 @@ void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id)
static void xhci_debugfs_create_ports(struct xhci_hcd *xhci,
struct dentry *parent)
{
- unsigned int num_ports;
char port_name[8];
struct xhci_port *port;
struct dentry *dir;
- num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
-
parent = debugfs_create_dir("ports", parent);
- while (num_ports--) {
- scnprintf(port_name, sizeof(port_name), "port%02d",
- num_ports + 1);
+ for (int i = 0; i < xhci->max_ports; i++) {
+ scnprintf(port_name, sizeof(port_name), "port%02d", i + 1);
dir = debugfs_create_dir(port_name, parent);
- port = &xhci->hw_ports[num_ports];
+ port = &xhci->hw_ports[i];
debugfs_create_file("portsc", 0644, dir, port, &port_fops);
}
}
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 49cba1cdd91bb..e2ea59b1bd24f 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -700,7 +700,7 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci,
/* Disable all Device Slots */
xhci_dbg(xhci, "Disable all slots\n");
spin_unlock_irqrestore(&xhci->lock, *flags);
- for (i = 1; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) {
+ for (i = 1; i <= xhci->max_slots; i++) {
if (!xhci->devs[i])
continue;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 3ef311ca224bc..afe47a79d2528 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -918,7 +918,7 @@ static void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_i
/* is this a hub device that added a tt_info to the tts list */
if (tt_info->slot_id == slot_id) {
/* are any devices using this tt_info? */
- for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) {
+ for (i = 1; i < xhci->max_slots; i++) {
vdev = xhci->devs[i];
if (vdev && (vdev->tt_info == tt_info))
xhci_free_virt_devices_depth_first(
@@ -1864,7 +1864,7 @@ EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter);
void xhci_mem_cleanup(struct xhci_hcd *xhci)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
- int i, j, num_ports;
+ int i, j;
cancel_delayed_work_sync(&xhci->cmd_timer);
@@ -1883,8 +1883,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring");
xhci_cleanup_command_queue(xhci);
- num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
- for (i = 0; i < num_ports && xhci->rh_bw; i++) {
+ for (i = 0; i < xhci->max_ports && xhci->rh_bw; i++) {
struct xhci_interval_bw_table *bwt = &xhci->rh_bw[i].bw_table;
for (j = 0; j < XHCI_MAX_INTERVAL; j++) {
struct list_head *ep = &bwt->interval_bw[j].endpoints;
@@ -1893,7 +1892,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
}
}
- for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--)
+ for (i = xhci->max_slots; i > 0; i--)
xhci_free_virt_devices_depth_first(xhci, i);
dma_pool_destroy(xhci->segment_pool);
@@ -1924,7 +1923,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
if (!xhci->rh_bw)
goto no_bw;
- for (i = 0; i < num_ports; i++) {
+ for (i = 0; i < xhci->max_ports; i++) {
struct xhci_tt_bw_info *tt, *n;
list_for_each_entry_safe(tt, n, &xhci->rh_bw[i].tts, tt_list) {
list_del(&tt->tt_list);
@@ -2125,7 +2124,7 @@ static void xhci_create_rhub_port_array(struct xhci_hcd *xhci,
if (!rhub->ports)
return;
- for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) {
+ for (i = 0; i < xhci->max_ports; i++) {
if (xhci->hw_ports[i].rhub != rhub ||
xhci->hw_ports[i].hcd_portnum == DUPLICATE_ENTRY)
continue;
@@ -2148,19 +2147,17 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
{
void __iomem *base;
u32 offset;
- unsigned int num_ports;
int i, j;
int cap_count = 0;
u32 cap_start;
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
- num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
- xhci->hw_ports = kcalloc_node(num_ports, sizeof(*xhci->hw_ports),
- flags, dev_to_node(dev));
+ xhci->hw_ports = kcalloc_node(xhci->max_ports, sizeof(*xhci->hw_ports),
+ flags, dev_to_node(dev));
if (!xhci->hw_ports)
return -ENOMEM;
- for (i = 0; i < num_ports; i++) {
+ for (i = 0; i < xhci->max_ports; i++) {
xhci->hw_ports[i].addr = &xhci->op_regs->port_regs[i].portsc;
xhci->hw_ports[i].hw_portnum = i;
@@ -2168,11 +2165,10 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
init_completion(&xhci->hw_ports[i].u3exit_done);
}
- xhci->rh_bw = kcalloc_node(num_ports, sizeof(*xhci->rh_bw), flags,
- dev_to_node(dev));
+ xhci->rh_bw = kcalloc_node(xhci->max_ports, sizeof(*xhci->rh_bw), flags, dev_to_node(dev));
if (!xhci->rh_bw)
return -ENOMEM;
- for (i = 0; i < num_ports; i++) {
+ for (i = 0; i < xhci->max_ports; i++) {
struct xhci_interval_bw_table *bw_table;
INIT_LIST_HEAD(&xhci->rh_bw[i].tts);
@@ -2204,9 +2200,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
offset = cap_start;
while (offset) {
- xhci_add_in_port(xhci, num_ports, base + offset, cap_count);
- if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports ==
- num_ports)
+ xhci_add_in_port(xhci, xhci->max_ports, base + offset, cap_count);
+ if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == xhci->max_ports)
break;
offset = xhci_find_next_ext_cap(base, offset,
XHCI_EXT_CAPS_PROTOCOL);
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index bd33cf6769cf6..2c4dc54b0fcaa 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -163,13 +163,12 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
/*
* calculate number of MSI-X vectors supported.
- * - HCS_MAX_INTRS: the max number of interrupts the host can handle,
- * with max number of interrupters based on the xhci HCSPARAMS1.
+ * - max_interrupters: the max number of interrupters requested, capped
+ * to the number the host can handle based on the xhci HCSPARAMS1.
* - num_online_cpus: maximum MSI-X vectors per CPUs core.
* Add additional 1 vector to ensure always available interrupt.
*/
- xhci->nvecs = min(num_online_cpus() + 1,
- HCS_MAX_INTRS(xhci->hcs_params1));
+ xhci->nvecs = min(num_online_cpus() + 1, xhci->max_interrupters);
/* TODO: Check with MSI Soc for sysdev */
xhci->nvecs = pci_alloc_irq_vectors(pdev, 1, xhci->nvecs,
@@ -898,7 +897,7 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)
if (!(xhci->quirks & XHCI_RESET_TO_DEFAULT))
return 0;
- for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) {
+ for (i = 0; i < xhci->max_ports; i++) {
port = &xhci->hw_ports[i];
portsc = readl(port->addr);
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 9d506409fdf69..62c997d445fcc 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1324,7 +1324,7 @@ void xhci_hc_died(struct xhci_hcd *xhci)
xhci_cleanup_command_queue(xhci);
/* return any pending urbs, remove may be waiting for them */
- for (i = 0; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) {
+ for (i = 0; i <= xhci->max_slots; i++) {
if (!xhci->devs[i])
continue;
for (j = 0; j < 31; j++)
@@ -1954,7 +1954,6 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
struct usb_hcd *hcd;
u32 port_id;
u32 portsc, cmd_reg;
- int max_ports;
unsigned int hcd_portnum;
struct xhci_bus_state *bus_state;
bool bogus_port_status = false;
@@ -1966,9 +1965,8 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
"WARN: xHC returned failed port status event\n");
port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0]));
- max_ports = HCS_MAX_PORTS(xhci->hcs_params1);
- if ((port_id <= 0) || (port_id > max_ports)) {
+ if ((port_id <= 0) || (port_id > xhci->max_ports)) {
xhci_warn(xhci, "Port change event with invalid port ID %d\n",
port_id);
return;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 70b7c97e615ff..1c414b89b099c 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -276,8 +276,7 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci)
if (upper_32_bits(val))
xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring);
- intrs = min_t(u32, HCS_MAX_INTRS(xhci->hcs_params1),
- ARRAY_SIZE(xhci->run_regs->ir_set));
+ intrs = min_t(u32, xhci->max_interrupters, ARRAY_SIZE(xhci->run_regs->ir_set));
for (i = 0; i < intrs; i++) {
struct xhci_intr_reg __iomem *ir;
@@ -4040,7 +4039,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
xhci_err(xhci, "Error while assigning device slot ID: %s\n",
xhci_trb_comp_code_string(command->status));
xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n",
- HCS_MAX_SLOTS(xhci->hcs_params1));
+ xhci->max_slots);
xhci_free_command(xhci, command);
return 0;
}
@@ -5225,6 +5224,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
*/
struct device *dev = hcd->self.sysdev;
int retval;
+ u32 hcs_params1;
/* Accept arbitrarily long scatter-gather lists */
hcd->self.sg_tablesize = ~0;
@@ -5250,7 +5250,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
xhci->run_regs = hcd->regs +
(readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK);
/* Cache read-only capability registers */
- xhci->hcs_params1 = readl(&xhci->cap_regs->hcs_params1);
+ hcs_params1 = readl(&xhci->cap_regs->hcs_params1);
xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2);
xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3);
xhci->hci_version = HC_VERSION(readl(&xhci->cap_regs->hc_capbase));
@@ -5259,10 +5259,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
xhci->dma_mask_bits = 64;
+ xhci->max_slots = HCS_MAX_SLOTS(hcs_params1);
+ xhci->max_ports = HCS_MAX_PORTS(hcs_params1);
/* 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);
+ if ((!xhci->max_interrupters) || xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1))
+ xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1);
xhci->quirks |= quirks;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index ccc43473ad90a..2e9a7fa981c20 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1492,7 +1492,6 @@ struct xhci_hcd {
struct xhci_doorbell_array __iomem *dba;
/* Cached register copies of read-only HC data */
- __u32 hcs_params1;
__u32 hcs_params2;
__u32 hcs_params3;
__u32 hcc_params;
@@ -1503,6 +1502,8 @@ struct xhci_hcd {
/* packed release number */
u16 hci_version;
u16 max_interrupters;
+ u8 max_slots;
+ u8 max_ports;
/* imod_interval in ns (I * 250ns) */
u32 imod_interval;
u32 page_size;
--
2.53.0
next prev parent reply other threads:[~2026-08-31 16:42 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-31 12:21 FAILED: patch "[PATCH] usb: xhci: bail out of setup if the controller is" failed to apply to 6.12-stable tree gregkh
2026-08-31 16:42 ` [PATCH 6.12.y 1/4] usb: xhci: add USB Port Register Set struct Sasha Levin
2026-08-31 16:42 ` [PATCH 6.12.y 2/4] usb: xhci: use cached HCSPARAMS1 value Sasha Levin
2026-08-31 16:42 ` Sasha Levin [this message]
2026-08-31 16:42 ` [PATCH 6.12.y 4/4] usb: xhci: bail out of setup if the controller is inaccessible Sasha Levin
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=20260831164233.269066-3-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=gregkh@linuxfoundation.org \
--cc=mathias.nyman@linux.intel.com \
--cc=niklas.neronin@linux.intel.com \
--cc=stable@vger.kernel.org \
/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: link
Be 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.