* [PATCH 00/23] xhci features for usb-next
@ 2025-11-19 14:23 Mathias Nyman
2025-11-19 14:23 ` [PATCH 01/23] usb: xhci: limit run_graceperiod for only usb 3.0 devices Mathias Nyman
` (22 more replies)
0 siblings, 23 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:23 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Mathias Nyman
Hi Greg
xhci patches for usb-next including a new debugfs entry for port link info
registers, graceperiod tuning, and a lot of reworks, cleanups and other
improvements.
Thanks
Mathias
Hongyu Xie (1):
usb: xhci: limit run_graceperiod for only usb 3.0 devices
Marco Crivellari (1):
usb: xhci: replace use of system_wq with system_percpu_wq
Mathias Nyman (2):
xhci: Add helper to find trb from its dma address
xhci: simplify and rework trb_in_td()
Michal Pecio (2):
usb: xhci: Assume that endpoints halt as specified
usb: xhci: Don't unchain link TRBs on quirky HCs
Niklas Neronin (16):
usb: xhci: rework xhci_decode_portsc()
usb: xhci: add tracing for PORTSC register writes
usb: xhci: add helper to read PORTSC register
usb: xhci: add USB Port Register Set struct
usb: xhci: implement USB Port Register Set struct
usb: xhci: remove deprecated TODO comment
usb: xhci: remove unused trace operation and argument
usb: xhci: use cached HCSPARAMS1 value
usb: xhci: simplify handling of Structural Parameters 1 values
usb: xhci: limit number of ports to 127
usb: xhci: limit number of interrupts to 128
usb: xhci: improve xhci-caps.h comments
usb: xhci: simplify Isochronous Scheduling Threshold handling
usb: xhci: simplify Max Scratchpad buffer macros
usb: xhci: drop xhci-caps.h dependence on xhci-ext-caps.h
usb: xhci: standardize single bit-field macros
Rai, Amardeep (1):
usb: xhci: Add debugfs support for xHCI Port Link Info (PORTLI)
register.
drivers/usb/host/xhci-caps.h | 167 +++++++++++++---------
drivers/usb/host/xhci-dbgcap.c | 8 +-
drivers/usb/host/xhci-debugfs.c | 57 ++++++--
drivers/usb/host/xhci-hub.c | 125 ++++++++---------
drivers/usb/host/xhci-mem.c | 41 +++---
drivers/usb/host/xhci-pci.c | 6 +-
drivers/usb/host/xhci-port.h | 5 +
drivers/usb/host/xhci-ring.c | 242 ++++++++++++++------------------
drivers/usb/host/xhci-tegra.c | 12 +-
drivers/usb/host/xhci-trace.h | 25 ++--
drivers/usb/host/xhci.c | 92 ++++++------
drivers/usb/host/xhci.h | 116 ++++++++-------
12 files changed, 468 insertions(+), 428 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 01/23] usb: xhci: limit run_graceperiod for only usb 3.0 devices
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
@ 2025-11-19 14:23 ` Mathias Nyman
2025-11-19 14:23 ` [PATCH 02/23] xhci: Add helper to find trb from its dma address Mathias Nyman
` (21 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:23 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Hongyu Xie, Mathias Nyman
From: Hongyu Xie <xiehongyu1@kylinos.cn>
run_graceperiod blocks usb 2.0 devices from auto suspending after
xhci_start for 500ms.
Log shows:
[ 13.387170] xhci_hub_control:1271: xhci-hcd PNP0D10:03: Get port status 7-1 read: 0x2a0, return 0x100
[ 13.387177] hub_event:5779: hub 7-0:1.0: state 7 ports 1 chg 0000 evt 0000
[ 13.387182] hub_suspend:3903: hub 7-0:1.0: hub_suspend
[ 13.387188] hcd_bus_suspend:2250: usb usb7: bus auto-suspend, wakeup 1
[ 13.387191] hcd_bus_suspend:2279: usb usb7: suspend raced with wakeup event
[ 13.387193] hcd_bus_resume:2303: usb usb7: usb auto-resume
[ 13.387296] hub_event:5779: hub 3-0:1.0: state 7 ports 1 chg 0000 evt 0000
[ 13.393343] handle_port_status:2034: xhci-hcd PNP0D10:02: handle_port_status: starting usb5 port polling.
[ 13.393353] xhci_hub_control:1271: xhci-hcd PNP0D10:02: Get port status 5-1 read: 0x206e1, return 0x10101
[ 13.400047] hub_suspend:3903: hub 3-0:1.0: hub_suspend
[ 13.403077] hub_resume:3948: hub 7-0:1.0: hub_resume
[ 13.403080] xhci_hub_control:1271: xhci-hcd PNP0D10:03: Get port status 7-1 read: 0x2a0, return 0x100
[ 13.403085] hub_event:5779: hub 7-0:1.0: state 7 ports 1 chg 0000 evt 0000
[ 13.403087] hub_suspend:3903: hub 7-0:1.0: hub_suspend
[ 13.403090] hcd_bus_suspend:2250: usb usb7: bus auto-suspend, wakeup 1
[ 13.403093] hcd_bus_suspend:2279: usb usb7: suspend raced with wakeup event
[ 13.403095] hcd_bus_resume:2303: usb usb7: usb auto-resume
[ 13.405002] handle_port_status:1913: xhci-hcd PNP0D10:04: Port change event, 9-1, id 1, portsc: 0x6e1
[ 13.405016] hub_activate:1169: usb usb5-port1: status 0101 change 0001
[ 13.405026] xhci_clear_port_change_bit:658: xhci-hcd PNP0D10:02: clear port1 connect change, portsc: 0x6e1
[ 13.413275] hcd_bus_suspend:2250: usb usb3: bus auto-suspend, wakeup 1
[ 13.419081] hub_resume:3948: hub 7-0:1.0: hub_resume
[ 13.419086] xhci_hub_control:1271: xhci-hcd PNP0D10:03: Get port status 7-1 read: 0x2a0, return 0x100
[ 13.419095] hub_event:5779: hub 7-0:1.0: state 7 ports 1 chg 0000 evt 0000
[ 13.419100] hub_suspend:3903: hub 7-0:1.0: hub_suspend
[ 13.419106] hcd_bus_suspend:2250: usb usb7: bus auto-suspend, wakeup 1
[ 13.419110] hcd_bus_suspend:2279: usb usb7: suspend raced with wakeup event
[ 13.419112] hcd_bus_resume:2303: usb usb7: usb auto-resume
[ 13.420455] handle_port_status:2034: xhci-hcd PNP0D10:04: handle_port_status: starting usb9 port polling.
[ 13.420493] handle_port_status:1913: xhci-hcd PNP0D10:05: Port change event, 10-1, id 1, portsc: 0x6e1
[ 13.425332] hcd_bus_suspend:2279: usb usb3: suspend raced with wakeup event
[ 13.431931] handle_port_status:2034: xhci-hcd PNP0D10:05: handle_port_status: starting usb10 port polling.
[ 13.435080] hub_resume:3948: hub 7-0:1.0: hub_resume
[ 13.435084] xhci_hub_control:1271: xhci-hcd PNP0D10:03: Get port status 7-1 read: 0x2a0, return 0x100
[ 13.435092] hub_event:5779: hub 7-0:1.0: state 7 ports 1 chg 0000 evt 0000
[ 13.435096] hub_suspend:3903: hub 7-0:1.0: hub_suspend
[ 13.435102] hcd_bus_suspend:2250: usb usb7: bus auto-suspend, wakeup 1
[ 13.435106] hcd_bus_suspend:2279: usb usb7: suspend raced with wakeup event
usb7 and other usb 2.0 root hub were rapidly toggling between suspend
and resume states. More, "suspend raced with wakeup event" confuses people.
So, limit run_graceperiod for only usb 3.0 devices
Signed-off-by: Hongyu Xie <xiehongyu1@kylinos.cn>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-hub.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index b3a59ce1b3f4..5e1442e91743 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1671,7 +1671,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
* SS devices are only visible to roothub after link training completes.
* Keep polling roothubs for a grace period after xHC start
*/
- if (xhci->run_graceperiod) {
+ if (hcd->speed >= HCD_USB3 && xhci->run_graceperiod) {
if (time_before(jiffies, xhci->run_graceperiod))
status = 1;
else
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 02/23] xhci: Add helper to find trb from its dma address
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
2025-11-19 14:23 ` [PATCH 01/23] usb: xhci: limit run_graceperiod for only usb 3.0 devices Mathias Nyman
@ 2025-11-19 14:23 ` Mathias Nyman
2025-11-19 14:23 ` [PATCH 03/23] xhci: simplify and rework trb_in_td() Mathias Nyman
` (20 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:23 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Mathias Nyman
Add a xhci_dma_to_trb() helper, and use it to find the transfer TRB
early in handle_tx_event() based on the dma address found in the
event TRB.
With this helper we can avoid using 'ep_seg' transfer TRB segment
variable as both a a boolean to indicate if the transfer TRB is part
of the next queued TD, and to actually find the transfer TRB based
on ep_seg and ep_trb_dma.
This is a first step in reworking and cleaning up trb_in_td() and
handle_tx_event()
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-ring.c | 28 ++++++++++++++++++++++------
1 file changed, 22 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 8e209aa33ea7..7fccca7e1c62 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -82,6 +82,23 @@ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg,
return seg->dma + (segment_offset * sizeof(*trb));
}
+static union xhci_trb *xhci_dma_to_trb(struct xhci_segment *start_seg,
+ dma_addr_t dma,
+ struct xhci_segment **match_seg)
+{
+ struct xhci_segment *seg;
+
+ xhci_for_each_ring_seg(start_seg, seg) {
+ if (in_range(dma, seg->dma, TRB_SEGMENT_SIZE)) {
+ if (match_seg)
+ *match_seg = seg;
+ return &seg->trbs[(dma - seg->dma) / sizeof(union xhci_trb)];
+ }
+ }
+
+ return NULL;
+}
+
static bool trb_is_noop(union xhci_trb *trb)
{
return TRB_TYPE_NOOP_LE32(trb->generic.field[3]);
@@ -2658,7 +2675,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
int ep_index;
struct xhci_td *td = NULL;
dma_addr_t ep_trb_dma;
- struct xhci_segment *ep_seg;
union xhci_trb *ep_trb;
int status = -EINPROGRESS;
struct xhci_ep_ctx *ep_ctx;
@@ -2689,6 +2705,9 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if (!ep_ring)
return handle_transferless_tx_event(xhci, ep, trb_comp_code);
+ /* find the transfer trb this events points to */
+ ep_trb = xhci_dma_to_trb(ep_ring->deq_seg, ep_trb_dma, NULL);
+
/* Look for common error cases */
switch (trb_comp_code) {
/* Skip codes that require special handling depending on
@@ -2862,10 +2881,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
td = list_first_entry(&ep_ring->td_list, struct xhci_td,
td_list);
- /* Is this a TRB in the currently executing TD? */
- ep_seg = trb_in_td(td, ep_trb_dma);
-
- if (!ep_seg) {
+ /* Is this TRB not part of the currently executing TD? */
+ if (!trb_in_td(td, ep_trb_dma)) {
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
/* this event is unlikely to match any TD, don't skip them all */
@@ -2948,7 +2965,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if (ring_xrun_event)
return 0;
- ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) / sizeof(*ep_trb)];
trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb, ep_trb_dma);
/*
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 03/23] xhci: simplify and rework trb_in_td()
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
2025-11-19 14:23 ` [PATCH 01/23] usb: xhci: limit run_graceperiod for only usb 3.0 devices Mathias Nyman
2025-11-19 14:23 ` [PATCH 02/23] xhci: Add helper to find trb from its dma address Mathias Nyman
@ 2025-11-19 14:23 ` Mathias Nyman
2025-11-19 14:23 ` [PATCH 04/23] usb: xhci: rework xhci_decode_portsc() Mathias Nyman
` (19 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:23 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Mathias Nyman
The trb_in_td() checking is quite complex, especially when checking for
TRBs in ranges that can span several segments.
Simplify the search by creating a position index for each TRB on the
ring, and just compare the position indexes.
Add a more generic dma_in_range() helper that checks if a trb dma
address is in the range between a start and end trb and call it from
trb_in_td()
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-ring.c | 72 ++++++++++++++----------------------
1 file changed, 28 insertions(+), 44 deletions(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 7fccca7e1c62..aa7fc4d6f97c 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -160,6 +160,11 @@ static void trb_to_noop(union xhci_trb *trb, u32 noop_type)
}
}
+static unsigned int trb_to_pos(struct xhci_segment *seg, union xhci_trb *trb)
+{
+ return seg->num * TRBS_PER_SEGMENT + (trb - seg->trbs);
+}
+
/* Updates trb to point to the next TRB in the ring, and updates seg if the next
* TRB is in a new segment. This does not skip over link TRBs, and it does not
* effect the ring dequeue or enqueue pointers.
@@ -299,55 +304,34 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
inc_enq_past_link(xhci, ring, chain);
}
-/*
- * If the suspect DMA address is a TRB in this TD, this function returns that
- * TRB's segment. Otherwise it returns 0.
- */
-static struct xhci_segment *trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma)
+static bool dma_in_range(dma_addr_t dma,
+ struct xhci_segment *start_seg, union xhci_trb *start_trb,
+ struct xhci_segment *end_seg, union xhci_trb *end_trb)
{
- dma_addr_t start_dma;
- dma_addr_t end_seg_dma;
- dma_addr_t end_trb_dma;
- struct xhci_segment *cur_seg;
+ unsigned int pos, start, end;
+ struct xhci_segment *pos_seg;
+ union xhci_trb *pos_trb = xhci_dma_to_trb(start_seg, dma, &pos_seg);
- start_dma = xhci_trb_virt_to_dma(td->start_seg, td->start_trb);
- cur_seg = td->start_seg;
+ /* Is the trb dma address even part of the whole ring? */
+ if (!pos_trb)
+ return false;
- do {
- if (start_dma == 0)
- return NULL;
- /* We may get an event for a Link TRB in the middle of a TD */
- end_seg_dma = xhci_trb_virt_to_dma(cur_seg,
- &cur_seg->trbs[TRBS_PER_SEGMENT - 1]);
- /* If the end TRB isn't in this segment, this is set to 0 */
- end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->end_trb);
-
- if (end_trb_dma > 0) {
- /* The end TRB is in this segment, so suspect should be here */
- if (start_dma <= end_trb_dma) {
- if (suspect_dma >= start_dma && suspect_dma <= end_trb_dma)
- return cur_seg;
- } else {
- /* Case for one segment with
- * a TD wrapped around to the top
- */
- if ((suspect_dma >= start_dma &&
- suspect_dma <= end_seg_dma) ||
- (suspect_dma >= cur_seg->dma &&
- suspect_dma <= end_trb_dma))
- return cur_seg;
- }
- return NULL;
- }
- /* Might still be somewhere in this segment */
- if (suspect_dma >= start_dma && suspect_dma <= end_seg_dma)
- return cur_seg;
+ pos = trb_to_pos(pos_seg, pos_trb);
+ start = trb_to_pos(start_seg, start_trb);
+ end = trb_to_pos(end_seg, end_trb);
- cur_seg = cur_seg->next;
- start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]);
- } while (cur_seg != td->start_seg);
+ /* end position is smaller than start, search range wraps around */
+ if (end < start)
+ return !(pos > end && pos < start);
- return NULL;
+ return (pos >= start && pos <= end);
+}
+
+/* If the suspect DMA address is a TRB in this TD, this function returns true */
+static bool trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma)
+{
+ return dma_in_range(suspect_dma, td->start_seg, td->start_trb,
+ td->end_seg, td->end_trb);
}
/*
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 04/23] usb: xhci: rework xhci_decode_portsc()
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (2 preceding siblings ...)
2025-11-19 14:23 ` [PATCH 03/23] xhci: simplify and rework trb_in_td() Mathias Nyman
@ 2025-11-19 14:23 ` Mathias Nyman
2025-11-19 14:23 ` [PATCH 05/23] usb: xhci: add tracing for PORTSC register writes Mathias Nyman
` (18 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:23 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
Rework xhci_decode_portsc(), which is used for PORTSC tracing, to make
the output more compact and general.
The function now first prints the multi-bit fields (port speed and link
state), followed by the abbreviated names of each individual bit as
defined in the xHCI specification. This reduces message length and makes
the output easier to read.
This change prepares for upcoming patches that will trace all PORTSC
writes, requiring the same decoding logic to handle both reads and writes.
This is particularly important for Read-Write-1-to-Clear (RW1C) bits,
where the semantics differ between read and write operations. For
example, when reading the Port Enabled bit, a set bit means the port is
enabled; when writing, a set bit indicates the port is being disabled.
The decoder now also includes the following fields:
Port Link State Write Strobe (LWS)
Device Removable (DR)
Warm Port Reset (WPR)
==== Examples Traces ====
Before:
0x00201201 Powered Connected Disabled Link:U0 PortSpeed:4 Change: PRC Wake:
0x0a0002a0 Powered Not-connected Disabled Link:RxDetect PortSpeed:0 \
Change: Wake: WCE WOE
After:
0x00201201 Speed=4 Link=U0 CCS PP PRC
0x0a0002a0 Speed=0 Link=RxDetect PP WCE WOE
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci.h | 57 +++++++++++++++++++++++++----------------
1 file changed, 35 insertions(+), 22 deletions(-)
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 58a51f09cceb..8e1311f90fdb 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -2399,25 +2399,48 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc)
if (portsc == ~(u32)0)
return str;
- ret += sprintf(str + ret, "%s %s %s Link:%s PortSpeed:%d ",
- portsc & PORT_POWER ? "Powered" : "Powered-off",
- portsc & PORT_CONNECT ? "Connected" : "Not-connected",
- portsc & PORT_PE ? "Enabled" : "Disabled",
- xhci_portsc_link_state_string(portsc),
- DEV_PORT_SPEED(portsc));
+ ret += sprintf(str + ret, "Speed=%d ", DEV_PORT_SPEED(portsc));
+ ret += sprintf(str + ret, "Link=%s ", xhci_portsc_link_state_string(portsc));
+ /* RO/ROS: Read-only */
+ if (portsc & PORT_CONNECT)
+ ret += sprintf(str + ret, "CCS ");
if (portsc & PORT_OC)
- ret += sprintf(str + ret, "OverCurrent ");
+ ret += sprintf(str + ret, "OCA "); /* No set for USB2 ports */
+ if (portsc & PORT_CAS)
+ ret += sprintf(str + ret, "CAS ");
+ if (portsc & PORT_DEV_REMOVE)
+ ret += sprintf(str + ret, "DR ");
+
+ /* RWS; writing 1 sets the bit, writing 0 clears the bit. */
+ if (portsc & PORT_POWER)
+ ret += sprintf(str + ret, "PP ");
+ if (portsc & PORT_WKCONN_E)
+ ret += sprintf(str + ret, "WCE ");
+ if (portsc & PORT_WKDISC_E)
+ ret += sprintf(str + ret, "WDE ");
+ if (portsc & PORT_WKOC_E)
+ ret += sprintf(str + ret, "WOE ");
+
+ /* RW; writing 1 sets the bit, writing 0 clears the bit */
+ if (portsc & PORT_LINK_STROBE)
+ ret += sprintf(str + ret, "LWS "); /* LWS 0 write is ignored */
+
+ /* RW1S; writing 1 sets the bit, writing 0 has no effect */
if (portsc & PORT_RESET)
- ret += sprintf(str + ret, "In-Reset ");
+ ret += sprintf(str + ret, "PR ");
+ if (portsc & PORT_WR)
+ ret += sprintf(str + ret, "WPR "); /* RsvdZ for USB2 ports */
- ret += sprintf(str + ret, "Change: ");
+ /* RW1CS; writing 1 clears the bit, writing 0 has no effect. */
+ if (portsc & PORT_PE)
+ ret += sprintf(str + ret, "PED ");
if (portsc & PORT_CSC)
ret += sprintf(str + ret, "CSC ");
if (portsc & PORT_PEC)
- ret += sprintf(str + ret, "PEC ");
+ ret += sprintf(str + ret, "PEC "); /* No set for USB3 ports */
if (portsc & PORT_WRC)
- ret += sprintf(str + ret, "WRC ");
+ ret += sprintf(str + ret, "WRC "); /* RsvdZ for USB2 ports */
if (portsc & PORT_OCC)
ret += sprintf(str + ret, "OCC ");
if (portsc & PORT_RC)
@@ -2425,17 +2448,7 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc)
if (portsc & PORT_PLC)
ret += sprintf(str + ret, "PLC ");
if (portsc & PORT_CEC)
- ret += sprintf(str + ret, "CEC ");
- if (portsc & PORT_CAS)
- ret += sprintf(str + ret, "CAS ");
-
- ret += sprintf(str + ret, "Wake: ");
- if (portsc & PORT_WKCONN_E)
- ret += sprintf(str + ret, "WCE ");
- if (portsc & PORT_WKDISC_E)
- ret += sprintf(str + ret, "WDE ");
- if (portsc & PORT_WKOC_E)
- ret += sprintf(str + ret, "WOE ");
+ ret += sprintf(str + ret, "CEC "); /* RsvdZ for USB2 ports */
return str;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 05/23] usb: xhci: add tracing for PORTSC register writes
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (3 preceding siblings ...)
2025-11-19 14:23 ` [PATCH 04/23] usb: xhci: rework xhci_decode_portsc() Mathias Nyman
@ 2025-11-19 14:23 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 06/23] usb: xhci: add helper to read PORTSC register Mathias Nyman
` (17 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:23 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
Introduce a dedicated write function for the USB Port Register Set (PORTSC)
that includes tracing capabilities for values written to the PORTSC
register. This enhancement minimizes code duplication and improves
debugging.
The PORTSC register is part of the Host Controller USB Port Register Set,
comprising 4 x 32-bit registers. As the first register, PORTSC is accessed
directly via 'port->addr'. Future commits will introduce a dedicated Port
register struct to further streamline access.
By adding the xhci_portsc_writel() function prior to these changes, we
significantly reduce the number of same line modifications required.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-debugfs.c | 2 +-
drivers/usb/host/xhci-hub.c | 33 ++++++++++++++++-----------------
drivers/usb/host/xhci-pci.c | 2 +-
drivers/usb/host/xhci-trace.h | 5 +++++
drivers/usb/host/xhci.c | 9 ++++++++-
drivers/usb/host/xhci.h | 1 +
6 files changed, 32 insertions(+), 20 deletions(-)
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index c6d44977193f..df99fffc6120 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -367,7 +367,7 @@ static ssize_t xhci_port_write(struct file *file, const char __user *ubuf,
portsc = xhci_port_state_to_neutral(portsc);
portsc &= ~PORT_PLS_MASK;
portsc |= PORT_LINK_STROBE | XDEV_COMP_MODE;
- writel(portsc, port->addr);
+ xhci_portsc_writel(port, portsc);
spin_unlock_irqrestore(&xhci->lock, flags);
} else {
return -EINVAL;
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 5e1442e91743..d0300c097803 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -570,7 +570,7 @@ static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port)
portsc = xhci_port_state_to_neutral(portsc);
/* Write 1 to disable the port */
- writel(portsc | PORT_PE, port->addr);
+ xhci_portsc_writel(port, portsc | PORT_PE);
portsc = readl(port->addr);
xhci_dbg(xhci, "disable port %d-%d, portsc: 0x%x\n",
@@ -578,7 +578,7 @@ static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port)
}
static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
- u16 wIndex, __le32 __iomem *addr, u32 port_status)
+ u16 wIndex, struct xhci_port *port, u32 port_status)
{
char *port_change_bit;
u32 status;
@@ -621,8 +621,8 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
return;
}
/* Change bits are all write 1 to clear */
- writel(port_status | status, addr);
- port_status = readl(addr);
+ xhci_portsc_writel(port, port_status | status);
+ port_status = readl(port->addr);
xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n",
wIndex + 1, port_change_bit, port_status);
@@ -659,11 +659,11 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port,
if (on) {
/* Power on */
- writel(temp | PORT_POWER, port->addr);
+ xhci_portsc_writel(port, temp | PORT_POWER);
readl(port->addr);
} else {
/* Power off */
- writel(temp & ~PORT_POWER, port->addr);
+ xhci_portsc_writel(port, temp & ~PORT_POWER);
}
spin_unlock_irqrestore(&xhci->lock, *flags);
@@ -805,7 +805,7 @@ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
temp = xhci_port_state_to_neutral(portsc);
temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | link_state;
- writel(temp, port->addr);
+ xhci_portsc_writel(port, temp);
xhci_dbg(xhci, "Set port %d-%d link state, portsc: 0x%x, write 0x%x",
port->rhub->hcd->self.busnum, port->hcd_portnum + 1,
@@ -835,7 +835,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
else
temp &= ~PORT_WKOC_E;
- writel(temp, port->addr);
+ xhci_portsc_writel(port, temp);
}
/* Test and clear port RWC bit */
@@ -848,7 +848,7 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port,
if (temp & port_bit) {
temp = xhci_port_state_to_neutral(temp);
temp |= port_bit;
- writel(temp, port->addr);
+ xhci_portsc_writel(port, temp);
}
}
@@ -1371,7 +1371,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
temp |= PORT_CSC | PORT_PEC | PORT_WRC |
PORT_OCC | PORT_RC | PORT_PLC |
PORT_CEC;
- writel(temp | PORT_PE, port->addr);
+ xhci_portsc_writel(port, temp | PORT_PE);
temp = readl(port->addr);
break;
}
@@ -1500,7 +1500,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case USB_PORT_FEAT_RESET:
temp = (temp | PORT_RESET);
- writel(temp, port->addr);
+ xhci_portsc_writel(port, temp);
temp = readl(port->addr);
xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n",
@@ -1514,7 +1514,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case USB_PORT_FEAT_BH_PORT_RESET:
temp |= PORT_WR;
- writel(temp, port->addr);
+ xhci_portsc_writel(port, temp);
temp = readl(port->addr);
break;
case USB_PORT_FEAT_U1_TIMEOUT:
@@ -1603,8 +1603,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_C_ENABLE:
case USB_PORT_FEAT_C_PORT_LINK_STATE:
case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
- xhci_clear_port_change_bit(xhci, wValue, wIndex,
- port->addr, temp);
+ xhci_clear_port_change_bit(xhci, wValue, wIndex, port, temp);
break;
case USB_PORT_FEAT_ENABLE:
xhci_disable_port(xhci, port);
@@ -1829,7 +1828,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
spin_lock_irqsave(&xhci->lock, flags);
}
}
- writel(portsc_buf[port_index], ports[port_index]->addr);
+ xhci_portsc_writel(ports[port_index], portsc_buf[port_index]);
}
hcd->state = HC_STATE_SUSPENDED;
bus_state->next_statechange = jiffies + msecs_to_jiffies(10);
@@ -1863,7 +1862,7 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port)
/* clear wakeup/change bits, and do a warm port reset */
portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
portsc |= PORT_WR;
- writel(portsc, port->addr);
+ xhci_portsc_writel(port, portsc);
/* flush write */
readl(port->addr);
return true;
@@ -1942,7 +1941,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
}
/* disable wake for all ports, write new link state if needed */
portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
- writel(portsc, ports[port_index]->addr);
+ xhci_portsc_writel(ports[port_index], portsc);
}
/* USB2 specific resume signaling delay and U0 link state transition */
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index f67a4d956204..b1192648aee7 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -919,7 +919,7 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)
xhci_dbg(xhci, "port %d-%d in U3 without wakeup, disable it\n",
port->rhub->hcd->self.busnum, port->hcd_portnum + 1);
portsc = xhci_port_state_to_neutral(portsc);
- writel(portsc | PORT_PE, port->addr);
+ xhci_portsc_writel(port, portsc | PORT_PE);
}
return 0;
diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
index 9abc904f1749..481becbcbf81 100644
--- a/drivers/usb/host/xhci-trace.h
+++ b/drivers/usb/host/xhci-trace.h
@@ -575,6 +575,11 @@ DEFINE_EVENT(xhci_log_portsc, xhci_hub_status_data,
TP_ARGS(port, portsc)
);
+DEFINE_EVENT(xhci_log_portsc, xhci_portsc_writel,
+ TP_PROTO(struct xhci_port *port, u32 portsc),
+ TP_ARGS(port, portsc)
+);
+
DECLARE_EVENT_CLASS(xhci_log_doorbell,
TP_PROTO(u32 slot, u32 doorbell),
TP_ARGS(slot, doorbell),
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 0cb45b95e4f5..84e109dbabe8 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -41,6 +41,13 @@ static unsigned long long quirks;
module_param(quirks, ullong, S_IRUGO);
MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default");
+void xhci_portsc_writel(struct xhci_port *port, u32 val)
+{
+ trace_xhci_portsc_writel(port, val);
+ writel(val, port->addr);
+}
+EXPORT_SYMBOL_GPL(xhci_portsc_writel);
+
static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring)
{
struct xhci_segment *seg;
@@ -909,7 +916,7 @@ static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci,
t2 |= PORT_CSC;
if (t1 != t2) {
- writel(t2, rhub->ports[i]->addr);
+ xhci_portsc_writel(rhub->ports[i], t2);
xhci_dbg(xhci, "config port %d-%d wake bits, portsc: 0x%x, write: 0x%x\n",
rhub->hcd->self.busnum, i + 1, portsc, t2);
}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 8e1311f90fdb..3b6b2d0d4c60 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1961,6 +1961,7 @@ void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num);
int xhci_usb_endpoint_maxp(struct usb_device *udev,
struct usb_host_endpoint *host_ep);
+void xhci_portsc_writel(struct xhci_port *port, u32 val);
/* xHCI roothub code */
void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 06/23] usb: xhci: add helper to read PORTSC register
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (4 preceding siblings ...)
2025-11-19 14:23 ` [PATCH 05/23] usb: xhci: add tracing for PORTSC register writes Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 07/23] usb: xhci: add USB Port Register Set struct Mathias Nyman
` (16 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Peter Chen, Peter Chen, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
Add a dedicated helper function to read the USB Port Status and Control
(PORTSC) register. This complements xhci_portsc_writel() and improves code
clarity by providing a clear counterpart for reading the register.
Suggested-by: Peter Chen <peter.chen@kernel.org>
Reviewed-by: Peter Chen <peter.chen@kerne.org>
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-debugfs.c | 4 +-
drivers/usb/host/xhci-hub.c | 68 ++++++++++++++++-----------------
drivers/usb/host/xhci-pci.c | 2 +-
drivers/usb/host/xhci-ring.c | 2 +-
drivers/usb/host/xhci-tegra.c | 12 +++---
drivers/usb/host/xhci.c | 14 +++++--
drivers/usb/host/xhci.h | 1 +
7 files changed, 55 insertions(+), 48 deletions(-)
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index df99fffc6120..d32ac8f84691 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -329,7 +329,7 @@ static int xhci_portsc_show(struct seq_file *s, void *unused)
u32 portsc;
char str[XHCI_MSG_MAX];
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
seq_printf(s, "%s\n", xhci_decode_portsc(str, portsc));
return 0;
@@ -359,7 +359,7 @@ static ssize_t xhci_port_write(struct file *file, const char __user *ubuf,
return count;
spin_lock_irqsave(&xhci->lock, flags);
/* compliance mode can only be enabled on ports in RxDetect */
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
if ((portsc & PORT_PLS_MASK) != XDEV_RXDETECT) {
spin_unlock_irqrestore(&xhci->lock, flags);
return -EPERM;
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index d0300c097803..2927b8a80327 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -299,7 +299,7 @@ static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
*/
memset(port_removable, 0, sizeof(port_removable));
for (i = 0; i < ports; i++) {
- portsc = readl(rhub->ports[i]->addr);
+ portsc = xhci_portsc_readl(rhub->ports[i]);
/* If a device is removable, PORTSC reports a 0, same as in the
* hub descriptor DeviceRemovable bits.
*/
@@ -356,7 +356,7 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
port_removable = 0;
/* bit 0 is reserved, bit 1 is for port 1, etc. */
for (i = 0; i < ports; i++) {
- portsc = readl(rhub->ports[i]->addr);
+ portsc = xhci_portsc_readl(rhub->ports[i]);
if (portsc & PORT_DEV_REMOVE)
port_removable |= 1 << (i + 1);
}
@@ -566,13 +566,13 @@ static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port)
return;
}
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
portsc = xhci_port_state_to_neutral(portsc);
/* Write 1 to disable the port */
xhci_portsc_writel(port, portsc | PORT_PE);
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
xhci_dbg(xhci, "disable port %d-%d, portsc: 0x%x\n",
hcd->self.busnum, port->hcd_portnum + 1, portsc);
}
@@ -622,7 +622,7 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
}
/* Change bits are all write 1 to clear */
xhci_portsc_writel(port, port_status | status);
- port_status = readl(port->addr);
+ port_status = xhci_portsc_readl(port);
xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n",
wIndex + 1, port_change_bit, port_status);
@@ -650,7 +650,7 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port,
u32 temp;
hcd = port->rhub->hcd;
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
xhci_dbg(xhci, "set port power %d-%d %s, portsc: 0x%x\n",
hcd->self.busnum, port->hcd_portnum + 1, on ? "ON" : "OFF", temp);
@@ -660,7 +660,7 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port,
if (on) {
/* Power on */
xhci_portsc_writel(port, temp | PORT_POWER);
- readl(port->addr);
+ xhci_portsc_readl(port);
} else {
/* Power off */
xhci_portsc_writel(port, temp & ~PORT_POWER);
@@ -801,7 +801,7 @@ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
u32 temp;
u32 portsc;
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
temp = xhci_port_state_to_neutral(portsc);
temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | link_state;
@@ -817,7 +817,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
{
u32 temp;
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
temp = xhci_port_state_to_neutral(temp);
if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT)
@@ -844,7 +844,7 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port,
{
u32 temp;
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
if (temp & port_bit) {
temp = xhci_port_state_to_neutral(temp);
temp |= port_bit;
@@ -1002,7 +1002,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,
}
xhci_ring_device(xhci, port->slot_id);
} else {
- int port_status = readl(port->addr);
+ int port_status = xhci_portsc_readl(port);
xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n",
hcd->self.busnum, wIndex + 1, port_status);
@@ -1263,7 +1263,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
wIndex--;
port = ports[portnum1 - 1];
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
if (temp == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
@@ -1309,7 +1309,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
port = ports[portnum1 - 1];
wIndex--;
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
if (temp == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
@@ -1319,7 +1319,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
/* FIXME: What new port features do we need to support? */
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
if ((temp & PORT_PLS_MASK) != XDEV_U0) {
/* Resume the port to U0 first */
xhci_set_link_state(xhci, port, XDEV_U0);
@@ -1331,7 +1331,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
* a port unless the port reports that it is in the
* enabled (PED = ‘1’,PLS < ‘3’) state.
*/
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
if ((temp & PORT_PE) == 0 || (temp & PORT_RESET)
|| (temp & PORT_PLS_MASK) >= XDEV_U3) {
xhci_warn(xhci, "USB core suspending port %d-%d not in U0/U1/U2\n",
@@ -1354,11 +1354,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
msleep(10); /* wait device to enter */
spin_lock_irqsave(&xhci->lock, flags);
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
bus_state->suspended_ports |= 1 << wIndex;
break;
case USB_PORT_FEAT_LINK_STATE:
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
/* Disable port */
if (link_state == USB_SS_PORT_LS_SS_DISABLED) {
xhci_dbg(xhci, "Disable port %d-%d\n",
@@ -1372,7 +1372,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
PORT_OCC | PORT_RC | PORT_PLC |
PORT_CEC;
xhci_portsc_writel(port, temp | PORT_PE);
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
break;
}
@@ -1381,7 +1381,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_dbg(xhci, "Enable port %d-%d\n",
hcd->self.busnum, portnum1);
xhci_set_link_state(xhci, port, link_state);
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
break;
}
@@ -1414,7 +1414,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
hcd->self.busnum, portnum1);
xhci_set_link_state(xhci, port, link_state);
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
break;
}
/* Port must be enabled */
@@ -1462,7 +1462,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n",
hcd->self.busnum, portnum1);
spin_lock_irqsave(&xhci->lock, flags);
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
break;
}
@@ -1480,12 +1480,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
spin_unlock_irqrestore(&xhci->lock, flags);
while (retries--) {
usleep_range(4000, 8000);
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
if ((temp & PORT_PLS_MASK) == XDEV_U3)
break;
}
spin_lock_irqsave(&xhci->lock, flags);
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
bus_state->suspended_ports |= 1 << wIndex;
}
break;
@@ -1502,20 +1502,20 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
temp = (temp | PORT_RESET);
xhci_portsc_writel(port, temp);
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n",
hcd->self.busnum, portnum1, temp);
break;
case USB_PORT_FEAT_REMOTE_WAKE_MASK:
xhci_set_remote_wake_mask(xhci, port, wake_mask);
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
xhci_dbg(xhci, "set port remote wake mask, actual port %d-%d status = 0x%x\n",
hcd->self.busnum, portnum1, temp);
break;
case USB_PORT_FEAT_BH_PORT_RESET:
temp |= PORT_WR;
xhci_portsc_writel(port, temp);
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
break;
case USB_PORT_FEAT_U1_TIMEOUT:
if (hcd->speed < HCD_USB3)
@@ -1547,7 +1547,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
goto error;
}
/* unblock any posted writes */
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
break;
case ClearPortFeature:
if (!portnum1 || portnum1 > max_ports)
@@ -1556,7 +1556,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
port = ports[portnum1 - 1];
wIndex--;
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
if (temp == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
@@ -1566,7 +1566,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
temp = xhci_port_state_to_neutral(temp);
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
- temp = readl(port->addr);
+ temp = xhci_portsc_readl(port);
xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n");
xhci_dbg(xhci, "PORTSC %04x\n", temp);
if (temp & PORT_RESET)
@@ -1681,7 +1681,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
/* For each port, did anything change? If so, set that bit in buf. */
for (i = 0; i < max_ports; i++) {
- temp = readl(ports[i]->addr);
+ temp = xhci_portsc_readl(ports[i]);
if (temp == ~(u32)0) {
xhci_hc_died(xhci);
retval = -ENODEV;
@@ -1750,7 +1750,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
u32 t1, t2;
int retries = 10;
retry:
- t1 = readl(ports[port_index]->addr);
+ t1 = xhci_portsc_readl(ports[port_index]);
t2 = xhci_port_state_to_neutral(t1);
portsc_buf[port_index] = 0;
@@ -1849,7 +1849,7 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port)
{
u32 portsc;
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
/* if any of these are set we are not stuck */
if (portsc & (PORT_CONNECT | PORT_CAS))
@@ -1864,7 +1864,7 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port)
portsc |= PORT_WR;
xhci_portsc_writel(port, portsc);
/* flush write */
- readl(port->addr);
+ xhci_portsc_readl(port);
return true;
}
@@ -1911,7 +1911,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
}
port_index = max_ports;
while (port_index--) {
- portsc = readl(ports[port_index]->addr);
+ portsc = xhci_portsc_readl(ports[port_index]);
/* warm reset CAS limited ports stuck in polling/compliance */
if ((xhci->quirks & XHCI_MISSING_CAS) &&
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index b1192648aee7..2ba0261a29c1 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -898,7 +898,7 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)
for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) {
port = &xhci->hw_ports[i];
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
if ((portsc & PORT_PLS_MASK) != XDEV_U3)
continue;
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index aa7fc4d6f97c..88022d221c70 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2027,7 +2027,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
hcd = port->rhub->hcd;
bus_state = &port->rhub->bus_state;
hcd_portnum = port->hcd_portnum;
- portsc = readl(port->addr);
+ portsc = xhci_portsc_readl(port);
xhci_dbg(xhci, "Port change event, %d-%d, id %d, portsc: 0x%x\n",
hcd->self.busnum, hcd_portnum + 1, port_id, portsc);
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 5255b1002893..1e23f198a005 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -2036,7 +2036,7 @@ static bool xhci_hub_ports_suspended(struct xhci_hub *hub)
u32 value;
for (i = 0; i < hub->num_ports; i++) {
- value = readl(hub->ports[i]->addr);
+ value = xhci_portsc_readl(hub->ports[i]);
if ((value & PORT_PE) == 0)
continue;
@@ -2162,7 +2162,7 @@ static void tegra_xhci_enable_phy_sleepwalk_wake(struct tegra_xusb *tegra)
if (!is_host_mode_phy(tegra, i, j))
continue;
- portsc = readl(rhub->ports[index]->addr);
+ portsc = xhci_portsc_readl(rhub->ports[index]);
speed = tegra_xhci_portsc_to_speed(tegra, portsc);
tegra_xusb_padctl_enable_phy_sleepwalk(padctl, phy, speed);
tegra_xusb_padctl_enable_phy_wake(padctl, phy);
@@ -2257,7 +2257,7 @@ static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool is_auto_resume)
for (i = 0; i < xhci->usb2_rhub.num_ports; i++) {
if (!xhci->usb2_rhub.ports[i])
continue;
- portsc = readl(xhci->usb2_rhub.ports[i]->addr);
+ portsc = xhci_portsc_readl(xhci->usb2_rhub.ports[i]);
tegra->lp0_utmi_pad_mask &= ~BIT(i);
if (((portsc & PORT_PLS_MASK) == XDEV_U3) || ((portsc & DEV_SPEED_MASK) == XDEV_FS))
tegra->lp0_utmi_pad_mask |= BIT(i);
@@ -2790,7 +2790,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value,
while (i--) {
if (!test_bit(i, &bus_state->resuming_ports))
continue;
- portsc = readl(ports[i]->addr);
+ portsc = xhci_portsc_readl(ports[i]);
if ((portsc & PORT_PLS_MASK) == XDEV_RESUME)
tegra_phy_xusb_utmi_pad_power_on(
tegra_xusb_get_phy(tegra, "usb2", (int) i));
@@ -2808,7 +2808,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value,
if (!index || index > rhub->num_ports)
return -EPIPE;
ports = rhub->ports;
- portsc = readl(ports[port]->addr);
+ portsc = xhci_portsc_readl(ports[port]);
if (portsc & PORT_CONNECT)
tegra_phy_xusb_utmi_pad_power_on(phy);
}
@@ -2827,7 +2827,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value,
if ((type_req == ClearPortFeature) && (value == USB_PORT_FEAT_C_CONNECTION)) {
ports = rhub->ports;
- portsc = readl(ports[port]->addr);
+ portsc = xhci_portsc_readl(ports[port]);
if (!(portsc & PORT_CONNECT)) {
/* We don't suspend the PAD while HNP role swap happens on the OTG
* port
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 84e109dbabe8..6b47b218cb24 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -48,6 +48,12 @@ void xhci_portsc_writel(struct xhci_port *port, u32 val)
}
EXPORT_SYMBOL_GPL(xhci_portsc_writel);
+u32 xhci_portsc_readl(struct xhci_port *port)
+{
+ return readl(port->addr);
+}
+EXPORT_SYMBOL_GPL(xhci_portsc_readl);
+
static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring)
{
struct xhci_segment *seg;
@@ -380,7 +386,7 @@ static void compliance_mode_recovery(struct timer_list *t)
return;
for (i = 0; i < rhub->num_ports; i++) {
- temp = readl(rhub->ports[i]->addr);
+ temp = xhci_portsc_readl(rhub->ports[i]);
if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) {
/*
* Compliance Mode Detected. Letting USB Core
@@ -903,7 +909,7 @@ static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci,
spin_lock_irqsave(&xhci->lock, flags);
for (i = 0; i < rhub->num_ports; i++) {
- portsc = readl(rhub->ports[i]->addr);
+ portsc = xhci_portsc_readl(rhub->ports[i]);
t1 = xhci_port_state_to_neutral(portsc);
t2 = t1;
@@ -943,7 +949,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci)
port_index = xhci->usb2_rhub.num_ports;
ports = xhci->usb2_rhub.ports;
while (port_index--) {
- portsc = readl(ports[port_index]->addr);
+ portsc = xhci_portsc_readl(ports[port_index]);
if (portsc & PORT_CHANGE_MASK ||
(portsc & PORT_PLS_MASK) == XDEV_RESUME)
return true;
@@ -951,7 +957,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci)
port_index = xhci->usb3_rhub.num_ports;
ports = xhci->usb3_rhub.ports;
while (port_index--) {
- portsc = readl(ports[port_index]->addr);
+ portsc = xhci_portsc_readl(ports[port_index]);
if (portsc & (PORT_CHANGE_MASK | PORT_CAS) ||
(portsc & PORT_PLS_MASK) == XDEV_RESUME)
return true;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 3b6b2d0d4c60..bddf9c15d813 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1962,6 +1962,7 @@ void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num);
int xhci_usb_endpoint_maxp(struct usb_device *udev,
struct usb_host_endpoint *host_ep);
void xhci_portsc_writel(struct xhci_port *port, u32 val);
+u32 xhci_portsc_readl(struct xhci_port *port);
/* xHCI roothub code */
void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 07/23] usb: xhci: add USB Port Register Set struct
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (5 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 06/23] usb: xhci: add helper to read PORTSC register Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 08/23] usb: xhci: implement " Mathias Nyman
` (15 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
Introduce a new struct for the Host Controller USB Port Register Set to
enhance readability and maintainability.
The Host Controller Operational Registers (struct 'xhci_op_regs') span from
offset 0x0 to 0x3FF and consist of fixed fields. Following these fixed
fields are the Host Controller USB Port Register Sets, which are dynamic
and repeat from 1 to MaxPorts, as defined by HCSPARAMS1.
Currently, the struct 'xhci_op_regs' includes:
__le32 port_status_base; The first PORTSC
__le32 port_power_base; The first PORTPMSC
__le32 port_link_base; The first PORTLI
__le32 reserved5; The first PORTHLPMC, not reserved
__le32 reserved6[NUM_PORT_REGS*254]; Port registers 2 to MaxPorts
Replace this with the simpler:
struct xhci_port_regs port_regs[]; Port registers 1 to MaxPorts
Host Controller USB Port Register Set:
| Offset | Mnemonic | Register Name
--------------------------------------------------------------------------
| 0x0 | PORTSC | Port Status and Control
| 0x4 | PORTPMSC | Port Power Management Status and Control
| 0x8 | PORTLI | Port Link Info
| 0xC | PORTHLPMC | Port Hardware LPM Control
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-mem.c | 3 +--
drivers/usb/host/xhci.h | 36 ++++++++++++++++--------------------
2 files changed, 17 insertions(+), 22 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 6e5b6057de79..ea3cfc229cd0 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -2201,8 +2201,7 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
return -ENOMEM;
for (i = 0; i < num_ports; i++) {
- xhci->hw_ports[i].addr = &xhci->op_regs->port_status_base +
- NUM_PORT_REGS * i;
+ xhci->hw_ports[i].addr = &xhci->op_regs->port_regs[i].portsc;
xhci->hw_ports[i].hw_portnum = i;
init_completion(&xhci->hw_ports[i].rexit_done);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index bddf9c15d813..d3ba50462589 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -66,14 +66,25 @@ struct xhci_cap_regs {
/* Reserved up to (CAPLENGTH - 0x1C) */
};
-/* Number of registers per port */
-#define NUM_PORT_REGS 4
-
#define PORTSC 0
#define PORTPMSC 1
#define PORTLI 2
#define PORTHLPMC 3
+/*
+ * struct xhci_port_regs - Host Controller USB Port Register Set. xHCI spec 5.4.8
+ * @portsc: Port Status and Control
+ * @portpmsc: Port Power Management Status and Control
+ * @portli: Port Link Info
+ * @porthlmpc: Port Hardware LPM Control
+ */
+struct xhci_port_regs {
+ __le32 portsc;
+ __le32 portpmsc;
+ __le32 portli;
+ __le32 porthlmpc;
+};
+
/**
* struct xhci_op_regs - xHCI Host Controller Operational Registers.
* @command: USBCMD - xHC command register
@@ -85,16 +96,7 @@ struct xhci_cap_regs {
* @cmd_ring: CRP - 64-bit Command Ring Pointer
* @dcbaa_ptr: DCBAAP - 64-bit Device Context Base Address Array Pointer
* @config_reg: CONFIG - Configure Register
- * @port_status_base: PORTSCn - base address for Port Status and Control
- * Each port has a Port Status and Control register,
- * followed by a Port Power Management Status and Control
- * register, a Port Link Info register, and a reserved
- * register.
- * @port_power_base: PORTPMSCn - base address for
- * Port Power Management Status and Control
- * @port_link_base: PORTLIn - base address for Port Link Info (current
- * Link PM state and control) for USB 2.1 and USB 3.0
- * devices.
+ * @port_regs: Port Register Sets, from 1 to MaxPorts (defined by HCSPARAMS1).
*/
struct xhci_op_regs {
__le32 command;
@@ -110,13 +112,7 @@ struct xhci_op_regs {
__le32 config_reg;
/* rsvd: offset 0x3C-3FF */
__le32 reserved4[241];
- /* port 1 registers, which serve as a base address for other ports */
- __le32 port_status_base;
- __le32 port_power_base;
- __le32 port_link_base;
- __le32 reserved5;
- /* registers for ports 2-255 */
- __le32 reserved6[NUM_PORT_REGS*254];
+ struct xhci_port_regs port_regs[];
};
/* USBCMD - USB command - command bitmasks */
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 08/23] usb: xhci: implement USB Port Register Set struct
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (6 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 07/23] usb: xhci: add USB Port Register Set struct Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 09/23] usb: xhci: Assume that endpoints halt as specified Mathias Nyman
` (14 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
Previously, each port's 'addr' field pointed to the base of the Host
Controller USB Port Register Set, and specific registers were accessed
using macros such as (port->addr + PORTPMSC).
This patch replaces the raw '__le32 __iomem *addr' pointer with a typed
'struct xhci_port_regs __iomem *port_reg' pointer. With this change,
individual registers can be accessed directly through the structure
fields:
Before:
port->addr
port->addr + PORTPMSC
port->addr + PORTLI
port->addr + PORTHLPMC
After:
port->port_reg->portsc
port->port_reg->portpmsc
port->port_reg->portli
port->port_reg->porthlpmc
This improves code readability and makes register access more intuitive
by using named struct members instead of pointer arithmetic and macros.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-hub.c | 16 ++++++++--------
drivers/usb/host/xhci-mem.c | 2 +-
drivers/usb/host/xhci.c | 29 ++++++++++++++---------------
drivers/usb/host/xhci.h | 7 +------
4 files changed, 24 insertions(+), 30 deletions(-)
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 2927b8a80327..c4c85312b04c 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -683,9 +683,9 @@ static void xhci_port_set_test_mode(struct xhci_hcd *xhci,
/* xhci only supports test mode for usb2 ports */
port = xhci->usb2_rhub.ports[wIndex];
- temp = readl(port->addr + PORTPMSC);
+ temp = readl(&port->port_reg->portpmsc);
temp |= test_mode << PORT_TEST_MODE_SHIFT;
- writel(temp, port->addr + PORTPMSC);
+ writel(temp, &port->port_reg->portpmsc);
xhci->test_mode = test_mode;
if (test_mode == USB_TEST_FORCE_ENABLE)
xhci_start(xhci);
@@ -1288,7 +1288,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
retval = -EINVAL;
break;
}
- port_li = readl(port->addr + PORTLI);
+ port_li = readl(&port->port_reg->portli);
status = xhci_get_ext_port_status(temp, port_li);
put_unaligned_le32(status, &buf[4]);
}
@@ -1520,18 +1520,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_U1_TIMEOUT:
if (hcd->speed < HCD_USB3)
goto error;
- temp = readl(port->addr + PORTPMSC);
+ temp = readl(&port->port_reg->portpmsc);
temp &= ~PORT_U1_TIMEOUT_MASK;
temp |= PORT_U1_TIMEOUT(timeout);
- writel(temp, port->addr + PORTPMSC);
+ writel(temp, &port->port_reg->portpmsc);
break;
case USB_PORT_FEAT_U2_TIMEOUT:
if (hcd->speed < HCD_USB3)
goto error;
- temp = readl(port->addr + PORTPMSC);
+ temp = readl(&port->port_reg->portpmsc);
temp &= ~PORT_U2_TIMEOUT_MASK;
temp |= PORT_U2_TIMEOUT(timeout);
- writel(temp, port->addr + PORTPMSC);
+ writel(temp, &port->port_reg->portpmsc);
break;
case USB_PORT_FEAT_TEST:
/* 4.19.6 Port Test Modes (USB2 Test Mode) */
@@ -1962,7 +1962,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
/* poll for U0 link state complete, both USB2 and USB3 */
for_each_set_bit(port_index, &bus_state->bus_suspended, BITS_PER_LONG) {
- sret = xhci_handshake(ports[port_index]->addr, PORT_PLC,
+ sret = xhci_handshake(&ports[port_index]->port_reg->portsc, PORT_PLC,
PORT_PLC, 10 * 1000);
if (sret) {
xhci_warn(xhci, "port %d-%d resume PLC timeout\n",
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index ea3cfc229cd0..9a6a8d9f3770 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -2201,7 +2201,7 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
return -ENOMEM;
for (i = 0; i < num_ports; i++) {
- xhci->hw_ports[i].addr = &xhci->op_regs->port_regs[i].portsc;
+ xhci->hw_ports[i].port_reg = &xhci->op_regs->port_regs[i];
xhci->hw_ports[i].hw_portnum = i;
init_completion(&xhci->hw_ports[i].rexit_done);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 6b47b218cb24..593b9d3aa9b6 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -44,13 +44,13 @@ MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default");
void xhci_portsc_writel(struct xhci_port *port, u32 val)
{
trace_xhci_portsc_writel(port, val);
- writel(val, port->addr);
+ writel(val, &port->port_reg->portsc);
}
EXPORT_SYMBOL_GPL(xhci_portsc_writel);
u32 xhci_portsc_readl(struct xhci_port *port)
{
- return readl(port->addr);
+ return readl(&port->port_reg->portsc);
}
EXPORT_SYMBOL_GPL(xhci_portsc_readl);
@@ -4649,7 +4649,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_port **ports;
- __le32 __iomem *pm_addr, *hlpm_addr;
+ struct xhci_port_regs __iomem *port_reg;
u32 pm_val, hlpm_val, field;
unsigned int port_num;
unsigned long flags;
@@ -4674,9 +4674,8 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
ports = xhci->usb2_rhub.ports;
port_num = udev->portnum - 1;
- pm_addr = ports[port_num]->addr + PORTPMSC;
- pm_val = readl(pm_addr);
- hlpm_addr = ports[port_num]->addr + PORTHLPMC;
+ port_reg = ports[port_num]->port_reg;
+ pm_val = readl(&port_reg->portpmsc);
xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
str_enable_disable(enable), port_num + 1);
@@ -4705,30 +4704,30 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
spin_lock_irqsave(&xhci->lock, flags);
hlpm_val = xhci_calculate_usb2_hw_lpm_params(udev);
- writel(hlpm_val, hlpm_addr);
+ writel(hlpm_val, &port_reg->porthlmpc);
/* flush write */
- readl(hlpm_addr);
+ readl(&port_reg->porthlmpc);
} else {
hird = xhci_calculate_hird_besl(xhci, udev);
}
pm_val &= ~PORT_HIRD_MASK;
pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id);
- writel(pm_val, pm_addr);
- pm_val = readl(pm_addr);
+ writel(pm_val, &port_reg->portpmsc);
+ pm_val = readl(&port_reg->portpmsc);
pm_val |= PORT_HLE;
- writel(pm_val, pm_addr);
+ writel(pm_val, &port_reg->portpmsc);
/* flush write */
- readl(pm_addr);
+ readl(&port_reg->portpmsc);
} else {
pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK | PORT_L1DS_MASK);
- writel(pm_val, pm_addr);
+ writel(pm_val, &port_reg->portpmsc);
/* flush write */
- readl(pm_addr);
+ readl(&port_reg->portpmsc);
if (udev->usb2_hw_lpm_besl_capable) {
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_change_max_exit_latency(xhci, udev, 0);
- readl_poll_timeout(ports[port_num]->addr, pm_val,
+ readl_poll_timeout(&ports[port_num]->port_reg->portsc, pm_val,
(pm_val & PORT_PLS_MASK) == XDEV_U0,
100, 10000);
return 0;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index d3ba50462589..adabe26b413b 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -66,11 +66,6 @@ struct xhci_cap_regs {
/* Reserved up to (CAPLENGTH - 0x1C) */
};
-#define PORTSC 0
-#define PORTPMSC 1
-#define PORTLI 2
-#define PORTHLPMC 3
-
/*
* struct xhci_port_regs - Host Controller USB Port Register Set. xHCI spec 5.4.8
* @portsc: Port Status and Control
@@ -1470,7 +1465,7 @@ struct xhci_port_cap {
};
struct xhci_port {
- __le32 __iomem *addr;
+ struct xhci_port_regs __iomem *port_reg;
int hw_portnum;
int hcd_portnum;
struct xhci_hub *rhub;
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 09/23] usb: xhci: Assume that endpoints halt as specified
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (7 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 08/23] usb: xhci: implement " Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 10/23] usb: xhci: Don't unchain link TRBs on quirky HCs Mathias Nyman
` (13 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Michal Pecio, Mathias Nyman
From: Michal Pecio <michal.pecio@gmail.com>
xHCI 4.8.3 recommends that software should simply assume endpoints to
halt after certain events, without looking at the Endpoint Context for
confirmation, because HCs may be slow to update that.
While no cases of such "slowness" appear to be known, different problem
exists on AMD Promontory chipsets: they may halt and generate a transfer
event, but fail to ever update the Endpoint Context at all, at least not
until some command is queued and fails with Context State Error. This is
easily triggered by disconnecting D- of a full speed serial device.
Possibly similar bug in non-AMD hardware has been reported to linux-usb.
In such case, failed TD is given back without erasing from the ring and
endpoint isn't reset. If some URB is unlinked later, Stop Endpoint fails
and its handler resets the endpoint. On next submission it will restart
on the stale TD. Outcome is UAF on success, or another halt on error and
then Dequeue doesn't move and URBs are stuck. Unlinking and resubmitting
the URBs causes unlimited ring expansion if the situation repeats.
This can be solved by ignoring Endpoint Context State and trusting that
endpoints halt when required, except one known case in ancient hardware.
The check for "Already resolving halted ep" becomes redundant, because
for these completion codes we now jump to xhci_handle_halted_endpoint()
which deals with pending EP_HALTED internally.
Link: https://lore.kernel.org/linux-usb/20250311234139.0e73e138@foxbook/
Link: https://lore.kernel.org/linux-usb/20250918055527.4157212-1-zhangjinpeng@kylinos.cn/
Signed-off-by: Michal Pecio <michal.pecio@gmail.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-ring.c | 73 ++++++++++++------------------------
1 file changed, 23 insertions(+), 50 deletions(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 88022d221c70..95005f9a3504 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2180,24 +2180,31 @@ static void xhci_clear_hub_tt_buffer(struct xhci_hcd *xhci, struct xhci_td *td,
* External device side is also halted in functional stall cases. Class driver
* will clear the device halt with a CLEAR_FEATURE(ENDPOINT_HALT) request later.
*/
-static bool xhci_halted_host_endpoint(struct xhci_ep_ctx *ep_ctx, unsigned int comp_code)
+static bool xhci_halted_host_endpoint(struct xhci_hcd *xhci, struct xhci_ep_ctx *ep_ctx,
+ unsigned int comp_code)
{
- /* Stall halts both internal and device side endpoint */
- if (comp_code == COMP_STALL_ERROR)
- return true;
+ int ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2));
- /* TRB completion codes that may require internal halt cleanup */
- if (comp_code == COMP_USB_TRANSACTION_ERROR ||
- comp_code == COMP_BABBLE_DETECTED_ERROR ||
- comp_code == COMP_SPLIT_TRANSACTION_ERROR)
+ switch (comp_code) {
+ case COMP_STALL_ERROR:
+ /* on xHCI this always halts, including protocol stall */
+ return true;
+ case COMP_BABBLE_DETECTED_ERROR:
/*
* The 0.95 spec says a babbling control endpoint is not halted.
* The 0.96 spec says it is. Some HW claims to be 0.95
* compliant, but it halts the control endpoint anyway.
* Check endpoint context if endpoint is halted.
*/
- if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED)
- return true;
+ if (xhci->hci_version <= 0x95 && ep_type == CTRL_EP)
+ return GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED;
+
+ fallthrough;
+ case COMP_USB_TRANSACTION_ERROR:
+ case COMP_SPLIT_TRANSACTION_ERROR:
+ /* these errors halt all non-isochronous endpoints */
+ return ep_type != ISOC_IN_EP && ep_type != ISOC_OUT_EP;
+ }
return false;
}
@@ -2234,41 +2241,9 @@ static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
* the ring dequeue pointer or take this TD off any lists yet.
*/
return;
- case COMP_USB_TRANSACTION_ERROR:
- case COMP_BABBLE_DETECTED_ERROR:
- case COMP_SPLIT_TRANSACTION_ERROR:
- /*
- * If endpoint context state is not halted we might be
- * racing with a reset endpoint command issued by a unsuccessful
- * stop endpoint completion (context error). In that case the
- * td should be on the cancelled list, and EP_HALTED flag set.
- *
- * Or then it's not halted due to the 0.95 spec stating that a
- * babbling control endpoint should not halt. The 0.96 spec
- * again says it should. Some HW claims to be 0.95 compliant,
- * but it halts the control endpoint anyway.
- */
- if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_HALTED) {
- /*
- * If EP_HALTED is set and TD is on the cancelled list
- * the TD and dequeue pointer will be handled by reset
- * ep command completion
- */
- if ((ep->ep_state & EP_HALTED) &&
- !list_empty(&td->cancelled_td_list)) {
- xhci_dbg(xhci, "Already resolving halted ep for 0x%llx\n",
- (unsigned long long)xhci_trb_virt_to_dma(
- td->start_seg, td->start_trb));
- return;
- }
- /* endpoint not halted, don't reset it */
- break;
- }
- /* Almost same procedure as for STALL_ERROR below */
- xhci_clear_hub_tt_buffer(xhci, td, ep);
- xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET);
- return;
- case COMP_STALL_ERROR:
+ }
+
+ if (xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code)) {
/*
* xhci internal endpoint state will go to a "halt" state for
* any stall, including default control pipe protocol stall.
@@ -2279,14 +2254,12 @@ static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
* stall later. Hub TT buffer should only be cleared for FS/LS
* devices behind HS hubs for functional stalls.
*/
- if (ep->ep_index != 0)
+ if (!(ep->ep_index == 0 && trb_comp_code == COMP_STALL_ERROR))
xhci_clear_hub_tt_buffer(xhci, td, ep);
xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET);
return; /* xhci_handle_halted_endpoint marked td cancelled */
- default:
- break;
}
xhci_dequeue_td(xhci, td, ep_ring, td->status);
@@ -2363,7 +2336,7 @@ static void process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
case COMP_STOPPED_LENGTH_INVALID:
goto finish_td;
default:
- if (!xhci_halted_host_endpoint(ep_ctx, trb_comp_code))
+ if (!xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code))
break;
xhci_dbg(xhci, "TRB error %u, halted endpoint index = %u\n",
trb_comp_code, ep->ep_index);
@@ -2973,7 +2946,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
return 0;
check_endpoint_halted:
- if (xhci_halted_host_endpoint(ep_ctx, trb_comp_code))
+ if (xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code))
xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET);
return 0;
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 10/23] usb: xhci: Don't unchain link TRBs on quirky HCs
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (8 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 09/23] usb: xhci: Assume that endpoints halt as specified Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 11/23] usb: xhci: replace use of system_wq with system_percpu_wq Mathias Nyman
` (12 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Michal Pecio, Mathias Nyman
From: Michal Pecio <michal.pecio@gmail.com>
Some old HCs ignore transfer ring link TRBs whose chain bit is unset.
This breaks endpoint operation and sometimes makes it execute other
ring's TDs, which may corrupt their buffers or cause unwanted device
action. We avoid this by chaining all link TRBs on affected rings.
Fix an omission which allows them to be unchained by cancelling TDs.
The patch was tested by reproducing this condition on an isochronous
endpoint (non-power-of-two TDs are sometimes split not to cross 64K)
and printing link TRBs in trb_to_noop() on good and buggy HCs.
Actual hardware malfunction is rare since it requires Missed Service
Error shortly before the unchained link TRB, at least on NEC and AMD.
I have never seen it after commit bb0ba4cb1065 ("usb: xhci: Apply the
link chain quirk on NEC isoc endpoints"), but it's Russian roulette
and I can't test all affected hosts and workloads. Fairly often MSEs
happen after cancellation because the endpoint was stopped.
Signed-off-by: Michal Pecio <michal.pecio@gmail.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-ring.c | 27 ++++++++++++++++-----------
1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 95005f9a3504..5acec9143811 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -145,11 +145,11 @@ static void inc_td_cnt(struct urb *urb)
urb_priv->num_tds_done++;
}
-static void trb_to_noop(union xhci_trb *trb, u32 noop_type)
+static void trb_to_noop(union xhci_trb *trb, u32 noop_type, bool unchain_links)
{
if (trb_is_link(trb)) {
- /* unchain chained link TRBs */
- trb->link.control &= cpu_to_le32(~TRB_CHAIN);
+ if (unchain_links)
+ trb->link.control &= cpu_to_le32(~TRB_CHAIN);
} else {
trb->generic.field[0] = 0;
trb->generic.field[1] = 0;
@@ -466,7 +466,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
xhci_dbg(xhci, "Turn aborted command %p to no-op\n",
i_cmd->command_trb);
- trb_to_noop(i_cmd->command_trb, TRB_CMD_NOOP);
+ trb_to_noop(i_cmd->command_trb, TRB_CMD_NOOP, false);
/*
* caller waiting for completion is called when command
@@ -798,13 +798,18 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
* (The last TRB actually points to the ring enqueue pointer, which is not part
* of this TD.) This is used to remove partially enqueued isoc TDs from a ring.
*/
-static void td_to_noop(struct xhci_td *td, bool flip_cycle)
+static void td_to_noop(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
+ struct xhci_td *td, bool flip_cycle)
{
+ bool unchain_links;
struct xhci_segment *seg = td->start_seg;
union xhci_trb *trb = td->start_trb;
+ /* link TRBs should now be unchained, but some old HCs expect otherwise */
+ unchain_links = !xhci_link_chain_quirk(xhci, ep->ring ? ep->ring->type : TYPE_STREAM);
+
while (1) {
- trb_to_noop(trb, TRB_TR_NOOP);
+ trb_to_noop(trb, TRB_TR_NOOP, unchain_links);
/* flip cycle if asked to */
if (flip_cycle && trb != td->start_trb && trb != td->end_trb)
@@ -1092,16 +1097,16 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
"Found multiple active URBs %p and %p in stream %u?\n",
td->urb, cached_td->urb,
td->urb->stream_id);
- td_to_noop(cached_td, false);
+ td_to_noop(xhci, ep, cached_td, false);
cached_td->cancel_status = TD_CLEARED;
}
- td_to_noop(td, false);
+ td_to_noop(xhci, ep, td, false);
td->cancel_status = TD_CLEARING_CACHE;
cached_td = td;
break;
}
} else {
- td_to_noop(td, false);
+ td_to_noop(xhci, ep, td, false);
td->cancel_status = TD_CLEARED;
}
}
@@ -1126,7 +1131,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
continue;
xhci_warn(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n",
td->urb);
- td_to_noop(td, false);
+ td_to_noop(xhci, ep, td, false);
td->cancel_status = TD_CLEARED;
}
}
@@ -4241,7 +4246,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
*/
urb_priv->td[0].end_trb = ep_ring->enqueue;
/* Every TRB except the first & last will have its cycle bit flipped. */
- td_to_noop(&urb_priv->td[0], true);
+ td_to_noop(xhci, xep, &urb_priv->td[0], true);
/* Reset the ring enqueue back to the first TRB and its cycle bit. */
ep_ring->enqueue = urb_priv->td[0].start_trb;
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 11/23] usb: xhci: replace use of system_wq with system_percpu_wq
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (9 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 10/23] usb: xhci: Don't unchain link TRBs on quirky HCs Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 12/23] usb: xhci: remove deprecated TODO comment Mathias Nyman
` (11 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Marco Crivellari, Tejun Heo, Mathias Nyman
From: Marco Crivellari <marco.crivellari@suse.com>
Currently if a user enqueues a work item using schedule_delayed_work() the
used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use
WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to
schedule_work() that is using system_wq and queue_work(), that makes use
again of WORK_CPU_UNBOUND.
This lack of consistency cannot be addressed without refactoring the API.
This continues the effort to refactor workqueue APIs, which began with
the introduction of new workqueues and a new alloc_workqueue flag in:
commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq")
commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag")
Switch to using system_percpu_wq because system_wq is going away as part of
a workqueue restructuring.
Suggested-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Marco Crivellari <marco.crivellari@suse.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-dbgcap.c | 8 ++++----
drivers/usb/host/xhci-ring.c | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index ecda964e018a..9da4f3b452cb 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -374,7 +374,7 @@ int dbc_ep_queue(struct dbc_request *req)
ret = dbc_ep_do_queue(req);
spin_unlock_irqrestore(&dbc->lock, flags);
- mod_delayed_work(system_wq, &dbc->event_work, 0);
+ mod_delayed_work(system_percpu_wq, &dbc->event_work, 0);
trace_xhci_dbc_queue_request(req);
@@ -677,7 +677,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc)
return ret;
}
- return mod_delayed_work(system_wq, &dbc->event_work,
+ return mod_delayed_work(system_percpu_wq, &dbc->event_work,
msecs_to_jiffies(dbc->poll_interval));
}
@@ -1023,7 +1023,7 @@ static void xhci_dbc_handle_events(struct work_struct *work)
return;
}
- mod_delayed_work(system_wq, &dbc->event_work,
+ mod_delayed_work(system_percpu_wq, &dbc->event_work,
msecs_to_jiffies(poll_interval));
}
@@ -1274,7 +1274,7 @@ static ssize_t dbc_poll_interval_ms_store(struct device *dev,
dbc->poll_interval = value;
- mod_delayed_work(system_wq, &dbc->event_work, 0);
+ mod_delayed_work(system_percpu_wq, &dbc->event_work, 0);
return size;
}
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 5acec9143811..f1582360d96a 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -435,7 +435,7 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci)
static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci)
{
- return mod_delayed_work(system_wq, &xhci->cmd_timer,
+ return mod_delayed_work(system_percpu_wq, &xhci->cmd_timer,
msecs_to_jiffies(xhci->current_cmd->timeout_ms));
}
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 12/23] usb: xhci: remove deprecated TODO comment
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (10 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 11/23] usb: xhci: replace use of system_wq with system_percpu_wq Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 13/23] usb: xhci: remove unused trace operation and argument Mathias Nyman
` (10 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
The Device Context Base Address Array (DCBAA) contains pointers to device
contexts. These fields are 64-bit registers, capable of holding 64-bit
addresses.
When struct 'xhci_device_context_array' was introduced in commit [1],
the entries were represented as pairs of 'u32', requiring a custom helper
function to set 64-bit addresses. This was later made redundant by
commit [2], which changed the representation to a single 'u64', allowing
direct assignment.
The associated TODO comment referencing the old 32-bit representation is
no longer relevant and is removed.
Link: https://git.kernel.org/torvalds/c/a74588f94655 [1]
Link: https://git.kernel.org/torvalds/c/8e595a5d30a5 [2]
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index adabe26b413b..3d644d16d9fb 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -791,7 +791,6 @@ struct xhci_device_context_array {
/* private xHCD pointers */
dma_addr_t dma;
};
-/* TODO: write function to set the 64-bit device DMA address */
/*
* TODO: change this to be dynamically sized at HC mem init time since the HC
* might not be able to handle the maximum number of devices possible.
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 13/23] usb: xhci: remove unused trace operation and argument
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (11 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 12/23] usb: xhci: remove deprecated TODO comment Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 14/23] usb: xhci: use cached HCSPARAMS1 value Mathias Nyman
` (9 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
Remove endpoint number 'ep_num' argument and memory operation from
xhci_log_ctx() trace function. These changes were added in commit
1d27fabec068 ("xhci: add xhci_address_ctx trace event") on Aug 14, 2013
and have never been used.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-trace.h | 18 ++++--------------
drivers/usb/host/xhci.c | 11 ++++-------
2 files changed, 8 insertions(+), 21 deletions(-)
diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
index 481becbcbf81..8e5b8e1282f7 100644
--- a/drivers/usb/host/xhci-trace.h
+++ b/drivers/usb/host/xhci-trace.h
@@ -71,18 +71,13 @@ DEFINE_EVENT(xhci_log_msg, xhci_dbg_ring_expansion,
);
DECLARE_EVENT_CLASS(xhci_log_ctx,
- TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
- unsigned int ep_num),
- TP_ARGS(xhci, ctx, ep_num),
+ TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx),
+ TP_ARGS(xhci, ctx),
TP_STRUCT__entry(
__field(int, ctx_64)
__field(unsigned, ctx_type)
__field(dma_addr_t, ctx_dma)
__field(u8 *, ctx_va)
- __field(unsigned, ctx_ep_num)
- __dynamic_array(u32, ctx_data,
- ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 8) *
- ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1))
),
TP_fast_assign(
@@ -90,10 +85,6 @@ DECLARE_EVENT_CLASS(xhci_log_ctx,
__entry->ctx_type = ctx->type;
__entry->ctx_dma = ctx->dma;
__entry->ctx_va = ctx->bytes;
- __entry->ctx_ep_num = ep_num;
- memcpy(__get_dynamic_array(ctx_data), ctx->bytes,
- ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 32) *
- ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1));
),
TP_printk("ctx_64=%d, ctx_type=%u, ctx_dma=@%llx, ctx_va=@%p",
__entry->ctx_64, __entry->ctx_type,
@@ -102,9 +93,8 @@ DECLARE_EVENT_CLASS(xhci_log_ctx,
);
DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx,
- TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
- unsigned int ep_num),
- TP_ARGS(xhci, ctx, ep_num)
+ TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx),
+ TP_ARGS(xhci, ctx)
);
DECLARE_EVENT_CLASS(xhci_log_trb,
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 593b9d3aa9b6..7ac8198d0d7b 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4379,8 +4379,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG);
ctrl_ctx->drop_flags = 0;
- trace_xhci_address_ctx(xhci, virt_dev->in_ctx,
- le32_to_cpu(slot_ctx->dev_info) >> 27);
+ trace_xhci_address_ctx(xhci, virt_dev->in_ctx);
trace_xhci_address_ctrl_ctx(ctrl_ctx);
spin_lock_irqsave(&xhci->lock, flags);
@@ -4440,7 +4439,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
xhci_err(xhci,
"ERROR: unexpected setup %s command completion code 0x%x.\n",
act, command->status);
- trace_xhci_address_ctx(xhci, virt_dev->out_ctx, 1);
+ trace_xhci_address_ctx(xhci, virt_dev->out_ctx);
ret = -EINVAL;
break;
}
@@ -4458,14 +4457,12 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
"Output Context DMA address = %#08llx",
(unsigned long long)virt_dev->out_ctx->dma);
- trace_xhci_address_ctx(xhci, virt_dev->in_ctx,
- le32_to_cpu(slot_ctx->dev_info) >> 27);
+ trace_xhci_address_ctx(xhci, virt_dev->in_ctx);
/*
* USB core uses address 1 for the roothubs, so we add one to the
* address given back to us by the HC.
*/
- trace_xhci_address_ctx(xhci, virt_dev->out_ctx,
- le32_to_cpu(slot_ctx->dev_info) >> 27);
+ trace_xhci_address_ctx(xhci, virt_dev->out_ctx);
/* Zero the input context control for later use */
ctrl_ctx->add_flags = 0;
ctrl_ctx->drop_flags = 0;
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 14/23] usb: xhci: use cached HCSPARAMS1 value
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (12 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 13/23] usb: xhci: remove unused trace operation and argument Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 15/23] usb: xhci: simplify handling of Structural Parameters 1 values Mathias Nyman
` (8 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
The Structural Parameters 1 (HCSPARAMS1) register is read and cached in
'xhci->hcs_params1' during host controller initialization. Since this
register is read-only and its value remains constant for the lifetime of
the controller, re-reading it later is unnecessary.
Replace subsequent register reads with the cached 'xhci->hcs_params1'
value to avoid redundant MMIO access.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 7ac8198d0d7b..dbe64ea47936 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4235,8 +4235,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(
- readl(&xhci->cap_regs->hcs_params1)));
+ HCS_MAX_SLOTS(xhci->hcs_params1));
xhci_free_command(xhci, command);
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 15/23] usb: xhci: simplify handling of Structural Parameters 1 values
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (13 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 14/23] usb: xhci: use cached HCSPARAMS1 value Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 16/23] usb: xhci: limit number of ports to 127 Mathias Nyman
` (7 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
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>
---
drivers/usb/host/xhci-debugfs.c | 15 ++++-----------
drivers/usb/host/xhci-hub.c | 2 +-
drivers/usb/host/xhci-mem.c | 31 +++++++++++++------------------
drivers/usb/host/xhci-pci.c | 2 +-
drivers/usb/host/xhci-ring.c | 6 ++----
drivers/usb/host/xhci.c | 21 ++++++++++-----------
drivers/usb/host/xhci.h | 3 ++-
7 files changed, 33 insertions(+), 47 deletions(-)
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index d32ac8f84691..ae50b667a548 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -613,20 +613,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);
}
}
@@ -634,7 +630,6 @@ static void xhci_debugfs_create_ports(struct xhci_hcd *xhci,
static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed,
struct seq_file *s)
{
- unsigned int num_ports;
unsigned int i;
int ret;
struct xhci_container_ctx *ctx;
@@ -645,8 +640,6 @@ static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed,
if (ret < 0)
return ret;
- num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
-
ctx = xhci_alloc_port_bw_ctx(xhci, 0);
if (!ctx) {
pm_runtime_put_sync(dev);
@@ -661,7 +654,7 @@ static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed,
/* print all roothub ports available bandwidth
* refer to xhci rev1_2 protocol 6.2.6 , byte 0 is reserved
*/
- for (i = 1; i < num_ports+1; i++)
+ for (i = 1; i <= xhci->max_ports; i++)
seq_printf(s, "port[%d] available bw: %d%%.\n", i,
ctx->bytes[i]);
err_out:
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index c4c85312b04c..cf358e5c6642 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 9a6a8d9f3770..2bbbf64a32c8 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -951,7 +951,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(
@@ -1899,7 +1899,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);
@@ -1918,8 +1918,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;
@@ -1928,7 +1927,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);
@@ -1964,7 +1963,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);
@@ -2165,7 +2164,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;
@@ -2188,19 +2187,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].port_reg = &xhci->op_regs->port_regs[i];
xhci->hw_ports[i].hw_portnum = i;
@@ -2208,11 +2205,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);
@@ -2244,9 +2240,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 2ba0261a29c1..585b2f3117b0 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -896,7 +896,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 = xhci_portsc_readl(port);
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index f1582360d96a..0ac7f9870d3d 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1394,7 +1394,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++)
@@ -1994,7 +1994,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;
@@ -2006,9 +2005,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 dbe64ea47936..ad5ef294d4f3 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -291,8 +291,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;
@@ -484,15 +483,13 @@ static void xhci_hcd_page_size(struct xhci_hcd *xhci)
static void xhci_enable_max_dev_slots(struct xhci_hcd *xhci)
{
u32 config_reg;
- u32 max_slots;
- max_slots = HCS_MAX_SLOTS(xhci->hcs_params1);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xHC can handle at most %d device slots",
- max_slots);
+ xhci->max_slots);
config_reg = readl(&xhci->op_regs->config_reg);
config_reg &= ~HCS_SLOTS_MASK;
- config_reg |= max_slots;
+ config_reg |= xhci->max_slots;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Setting Max device slots reg = 0x%x",
config_reg);
@@ -4235,7 +4232,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;
}
@@ -5416,6 +5413,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;
@@ -5441,7 +5439,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));
@@ -5449,10 +5447,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->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 3d644d16d9fb..fface54dbc7a 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1500,7 +1500,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;
@@ -1511,6 +1510,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.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 16/23] usb: xhci: limit number of ports to 127
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (14 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 15/23] usb: xhci: simplify handling of Structural Parameters 1 values Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 17/23] usb: xhci: limit number of interrupts to 128 Mathias Nyman
` (6 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
The xHCI driver allocates various port-related structures based on the
maximum number of ports reported by the controller. The Number of Ports
(MaxPorts) field occupies bits 31:24 of the HCSPARAMS1 register and can
represent values up to 255. However, the 'HCS_MAX_PORTS()' macro currently
reads bits 30:24, effectively limiting the maximum to 127.
Fixing the macro increases the reported port limit to 255, which in turn
increases memory usage regardless of how many ports are actually used.
To maintain compatibility and control memory consumption, set
'xhci->max_ports' to the minimum of the value read from 'HCS_MAX_PORTS()'
and 127 (MAX_HC_PORTS). This preserves the existing limit while making
the restriction explicit and easier to adjust in the future.
Summary:
* Port allocations are now limited to 127.
* HC max ports macro now correctly reads the MaxPorts value.
* Macro 'MAX_HC_PORTS' can be modified to set the port limit.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-caps.h | 4 ++--
drivers/usb/host/xhci.c | 2 +-
drivers/usb/host/xhci.h | 5 ++++-
3 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h
index 89bc83e4f1eb..8390c969389e 100644
--- a/drivers/usb/host/xhci-caps.h
+++ b/drivers/usb/host/xhci-caps.h
@@ -12,8 +12,8 @@
#define HCS_SLOTS_MASK 0xff
/* bits 8:18, Max Interrupters */
#define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff)
-/* bits 24:31, Max Ports - max value is 0x7F = 127 ports */
-#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f)
+/* bits 31:24, Max Ports - max value is 255 */
+#define HCS_MAX_PORTS(p) (((p) >> 24) & 0xff)
/* HCSPARAMS2 - hcs_params2 - bitmasks */
/* bits 0:3, frames or uframes that SW needs to queue transactions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index ad5ef294d4f3..cb82b15fc203 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -5448,7 +5448,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
xhci->max_slots = HCS_MAX_SLOTS(hcs_params1);
- xhci->max_ports = HCS_MAX_PORTS(hcs_params1);
+ xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS);
/* xhci-plat or xhci-pci might have set max_interrupters already */
if ((!xhci->max_interrupters) || xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1))
xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index fface54dbc7a..e04bc491a22b 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -34,7 +34,10 @@
/* Max number of USB devices for any host controller - limit in section 6.1 */
#define MAX_HC_SLOTS 256
-/* Section 5.3.3 - MaxPorts */
+/*
+ * Max Number of Ports. xHCI specification section 5.3.3
+ * Valid values are in the range of 1 to 255.
+ */
#define MAX_HC_PORTS 127
/*
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 17/23] usb: xhci: limit number of interrupts to 128
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (15 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 16/23] usb: xhci: limit number of ports to 127 Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 18/23] usb: xhci: improve xhci-caps.h comments Mathias Nyman
` (5 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
The xHCI driver defines only 128 interrupter register slots, yet allows up
to 2047 interrupters. According to the xHCI specification, the maximum
valid number of interrupters is 1024. These mismatches can lead to
out-of-range accesses and excessive memory use.
The Number of Interrupters (MaxIntrs) field occupies bits 18:8 of the
HCSPARAMS1 register, which can yield a value up to 2047, although the
specification limits it to 1024. Cap the value using the 'MAX_HC_INTRS'
macro.
Set 'xhci->max_intrs' to the minimum of the value reported by the
HCSPARAMS1 register and 'MAX_HC_INTRS'. The interrupter register slot
array is defined for 1024 entries, serving only as a structural template
and not increasing memory usage.
Although the xHCI specification allows up to 1024 interrupters, raising
'MAX_HC_INTRS' above 128 provides no practical benefit. The driver only
uses the primary interrupter (0), and secondary interrupters (1+) are
rarely, if ever, used in practice. No reports exist of usage beyond 128.
Therefore, I have limited it to 128.
Summary:
* Interrupter allocations are now limited to 128 from 2047.
* Interrupter Register template slots are set to 1024 from 128.
* Macro 'MAX_HC_INTRS' can be modified to set the interrupter limit.
==== Detailed interrupter explanation ====
There are two relevant components:
Interrupter array:
This holds the software interrupter structures and is allocated by the
xhci driver. The number of interrupters allocated is determined by the
HCSPARAMS1 register field, which specifies the supported interrupter
count.
Interrupter register slots:
This is a template struct used to access the hardware's runtime
registers. It is not allocated by the driver, the hardware defines and
owns this memory region, and the driver only maps it for MMIO access.
Each entry in the interrupter array points to its corresponding
interrupter register slot in the hardware region once that interrupter
is enabled.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci.c | 13 ++++++-------
drivers/usb/host/xhci.h | 7 ++++++-
2 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index cb82b15fc203..1e4032bf1223 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -250,7 +250,6 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci)
struct iommu_domain *domain;
int err, i;
u64 val;
- u32 intrs;
/*
* Some Renesas controllers get into a weird state if they are
@@ -291,9 +290,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, xhci->max_interrupters, ARRAY_SIZE(xhci->run_regs->ir_set));
-
- for (i = 0; i < intrs; i++) {
+ for (i = 0; i < xhci->max_interrupters; i++) {
struct xhci_intr_reg __iomem *ir;
ir = &xhci->run_regs->ir_set[i];
@@ -5450,7 +5447,9 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
xhci->max_slots = HCS_MAX_SLOTS(hcs_params1);
xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS);
/* xhci-plat or xhci-pci might have set max_interrupters already */
- if ((!xhci->max_interrupters) || xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1))
+ if (!xhci->max_interrupters)
+ xhci->max_interrupters = min(HCS_MAX_INTRS(hcs_params1), MAX_HC_INTRS);
+ else if (xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1))
xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1);
xhci->quirks |= quirks;
@@ -5670,8 +5669,8 @@ static int __init xhci_hcd_init(void)
BUILD_BUG_ON(sizeof(struct xhci_erst_entry) != 4*32/8);
BUILD_BUG_ON(sizeof(struct xhci_cap_regs) != 8*32/8);
BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8);
- /* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */
- BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
+ /* xhci_run_regs has eight fields and embeds 1024 xhci_intr_regs */
+ BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*1024)*32/8);
if (usb_disabled())
return -ENODEV;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index e04bc491a22b..2b0796f6d00e 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -39,6 +39,11 @@
* Valid values are in the range of 1 to 255.
*/
#define MAX_HC_PORTS 127
+/*
+ * Max number of Interrupter Register Sets. xHCI specification section 5.3.3
+ * Valid values are in the range of 1 to 1024.
+ */
+#define MAX_HC_INTRS 128
/*
* xHCI register interface.
@@ -278,7 +283,7 @@ struct xhci_intr_reg {
struct xhci_run_regs {
__le32 microframe_index;
__le32 rsvd[7];
- struct xhci_intr_reg ir_set[128];
+ struct xhci_intr_reg ir_set[1024];
};
/**
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 18/23] usb: xhci: improve xhci-caps.h comments
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (16 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 17/23] usb: xhci: limit number of interrupts to 128 Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 19/23] usb: xhci: simplify Isochronous Scheduling Threshold handling Mathias Nyman
` (4 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
No functional changes.
This patch updates comments in xhci-caps.h for better readability and
consistency. Each Capability Register bit field now includes a brief
description of its name and valid range, following a uniform comment
format across the file.
These updates are based on the xHCI specification, revision 1.2.
Bit field comment format:
/* <bit range> - <Field name>,<noteworthy information if any> */
Why print the bit range?
The bit range aids in identifying missing macros and reserved bit ranges.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-caps.h | 100 ++++++++++++++++++++---------------
1 file changed, 57 insertions(+), 43 deletions(-)
diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h
index 8390c969389e..8a435786f950 100644
--- a/drivers/usb/host/xhci-caps.h
+++ b/drivers/usb/host/xhci-caps.h
@@ -1,93 +1,107 @@
/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * xHCI Host Controller Capability Registers.
+ * xHCI Specification Section 5.3, Revision 1.2.
+ */
-/* hc_capbase bitmasks */
-/* bits 7:0 - how long is the Capabilities register */
+/* hc_capbase - bitmasks */
+/* bits 7:0 - Capability Registers Length */
#define HC_LENGTH(p) XHCI_HC_LENGTH(p)
-/* bits 31:16 */
+/* bits 15:8 - Rsvd */
+/* bits 31:16 - Host Controller Interface Version Number */
#define HC_VERSION(p) (((p) >> 16) & 0xffff)
/* HCSPARAMS1 - hcs_params1 - bitmasks */
-/* bits 0:7, Max Device Slots */
+/* bits 7:0 - Number of Device Slots */
#define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff)
#define HCS_SLOTS_MASK 0xff
-/* bits 8:18, Max Interrupters */
+/* bits 18:8 - Number of Interrupters, max values is 1024 */
#define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff)
/* bits 31:24, Max Ports - max value is 255 */
#define HCS_MAX_PORTS(p) (((p) >> 24) & 0xff)
/* HCSPARAMS2 - hcs_params2 - bitmasks */
-/* bits 0:3, frames or uframes that SW needs to queue transactions
- * ahead of the HW to meet periodic deadlines */
+/*
+ * bits 3:0 - Isochronous Scheduling Threshold, frames or uframes that SW
+ * needs to queue transactions ahead of the HW to meet periodic deadlines.
+ */
#define HCS_IST(p) (((p) >> 0) & 0xf)
-/* bits 4:7, max number of Event Ring segments */
+/* bits 7:4 - Event Ring Segment Table Max, 2^(n) */
#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf)
-/* bits 21:25 Hi 5 bits of Scratchpad buffers SW must allocate for the HW */
-/* bit 26 Scratchpad restore - for save/restore HW state - not used yet */
-/* bits 27:31 Lo 5 bits of Scratchpad buffers SW must allocate for the HW */
+/* bits 20:8 - Rsvd */
+/* bits 25:21 - Max Scratchpad Buffers (Hi), 5 Most significant bits */
+/* bit 26 - Scratchpad restore, for save/restore HW state */
+/* bits 31:27 - Max Scratchpad Buffers (Lo), 5 Least significant bits */
#define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f))
/* HCSPARAMS3 - hcs_params3 - bitmasks */
-/* bits 0:7, Max U1 to U0 latency for the roothub ports */
+/* bits 7:0 - U1 Device Exit Latency, Max U1 to U0 latency for the roothub ports */
#define HCS_U1_LATENCY(p) (((p) >> 0) & 0xff)
-/* bits 16:31, Max U2 to U0 latency for the roothub ports */
+/* bits 15:8 - Rsvd */
+/* bits 31:16 - U2 Device Exit Latency, Max U2 to U0 latency for the roothub ports */
#define HCS_U2_LATENCY(p) (((p) >> 16) & 0xffff)
-/* HCCPARAMS - hcc_params - bitmasks */
-/* true: HC can use 64-bit address pointers */
+/* HCCPARAMS1 - hcc_params - bitmasks */
+/* bit 0 - 64-bit Addressing Capability */
#define HCC_64BIT_ADDR(p) ((p) & (1 << 0))
-/* true: HC can do bandwidth negotiation */
+/* bit 1 - BW Negotiation Capability */
#define HCC_BANDWIDTH_NEG(p) ((p) & (1 << 1))
-/* true: HC uses 64-byte Device Context structures
- * FIXME 64-byte context structures aren't supported yet.
- */
+/* bit 2 - Context Size */
#define HCC_64BYTE_CONTEXT(p) ((p) & (1 << 2))
-/* true: HC has port power switches */
+#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32)
+/* bit 3 - Port Power Control */
#define HCC_PPC(p) ((p) & (1 << 3))
-/* true: HC has port indicators */
+/* bit 4 - Port Indicators */
#define HCS_INDICATOR(p) ((p) & (1 << 4))
-/* true: HC has Light HC Reset Capability */
+/* bit 5 - Light HC Reset Capability */
#define HCC_LIGHT_RESET(p) ((p) & (1 << 5))
-/* true: HC supports latency tolerance messaging */
+/* bit 6 - Latency Tolerance Messaging Capability */
#define HCC_LTC(p) ((p) & (1 << 6))
-/* true: no secondary Stream ID Support */
+/* bit 7 - No Secondary Stream ID Support */
#define HCC_NSS(p) ((p) & (1 << 7))
-/* true: HC supports Stopped - Short Packet */
+/* bit 8 - Parse All Event Data */
+/* bit 9 - Short Packet Capability */
#define HCC_SPC(p) ((p) & (1 << 9))
-/* true: HC has Contiguous Frame ID Capability */
+/* bit 10 - Stopped EDTLA Capability */
+/* bit 11 - Contiguous Frame ID Capability */
#define HCC_CFC(p) ((p) & (1 << 11))
-/* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */
+/* bits 15:12 - Max size for Primary Stream Arrays, 2^(n+1) */
#define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1))
-/* Extended Capabilities pointer from PCI base - section 5.3.6 */
+/* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */
#define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p)
-#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32)
-
-/* db_off bitmask - bits 31:2 Doorbell Array Offset */
+/* DBOFF - db_off - bitmasks */
+/* bits 1:0 - Rsvd */
+/* bits 31:2 - Doorbell Array Offset */
#define DBOFF_MASK (0xfffffffc)
-/* run_regs_off bitmask - bits 0:4 reserved */
+/* RTSOFF - run_regs_off - bitmasks */
+/* bits 4:0 - Rsvd */
+/* bits 31:5 - Runtime Register Space Offse */
#define RTSOFF_MASK (~0x1f)
/* HCCPARAMS2 - hcc_params2 - bitmasks */
-/* true: HC supports U3 entry Capability */
+/* bit 0 - U3 Entry Capability */
#define HCC2_U3C(p) ((p) & (1 << 0))
-/* true: HC supports Configure endpoint command Max exit latency too large */
+/* bit 1 - Configure Endpoint Command Max Exit Latency Too Large Capability */
#define HCC2_CMC(p) ((p) & (1 << 1))
-/* true: HC supports Force Save context Capability */
+/* bit 2 - Force Save Context Capabilitu */
#define HCC2_FSC(p) ((p) & (1 << 2))
-/* true: HC supports Compliance Transition Capability */
+/* bit 3 - Compliance Transition Capability, false: compliance is enabled by default */
#define HCC2_CTC(p) ((p) & (1 << 3))
-/* true: HC support Large ESIT payload Capability > 48k */
+/* bit 4 - Large ESIT Payload Capability, true: HC support ESIT payload > 48k */
#define HCC2_LEC(p) ((p) & (1 << 4))
-/* true: HC support Configuration Information Capability */
+/* bit 5 - Configuration Information Capability */
#define HCC2_CIC(p) ((p) & (1 << 5))
-/* true: HC support Extended TBC Capability, Isoc burst count > 65535 */
+/* bit 6 - Extended TBC Capability, true: Isoc burst count > 65535 */
#define HCC2_ETC(p) ((p) & (1 << 6))
-/* true: HC support Extended TBC TRB Status Capability */
+/* bit 7 - Extended TBC TRB Status Capability */
#define HCC2_ETC_TSC(p) ((p) & (1 << 7))
-/* true: HC support Get/Set Extended Property Capability */
+/* bit 8 - Get/Set Extended Property Capability */
#define HCC2_GSC(p) ((p) & (1 << 8))
-/* true: HC support Virtualization Based Trusted I/O Capability */
+/* bit 9 - Virtualization Based Trusted I/O Capability */
#define HCC2_VTC(p) ((p) & (1 << 9))
-/* true: HC support Double BW on a eUSB2 HS ISOC EP */
+/* bit 10 - Rsvd */
+/* bit 11 - HC support Double BW on a eUSB2 HS ISOC EP */
#define HCC2_EUSB2_DIC(p) ((p) & (1 << 11))
+/* bits 31:12 - Rsvd */
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 19/23] usb: xhci: simplify Isochronous Scheduling Threshold handling
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (17 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 18/23] usb: xhci: improve xhci-caps.h comments Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 20/23] usb: xhci: simplify Max Scratchpad buffer macros Mathias Nyman
` (3 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
The IST is represented by bits 2:0, with bit 3 indicating the unit of
measurement, Frames or Microframes. Introduce xhci_ist_microframes(),
which returns the IST value in Microframes, simplifying the code and
reducing duplication.
Improve documentation in xhci-caps.h to clarify the IST register specifics,
including the unit conversion details. These change removes the need to
explain it each time the IST values is retrieved.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-caps.h | 9 ++++++++-
drivers/usb/host/xhci-ring.c | 26 ++++++++++++--------------
2 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h
index 8a435786f950..e772d5f30d36 100644
--- a/drivers/usb/host/xhci-caps.h
+++ b/drivers/usb/host/xhci-caps.h
@@ -24,8 +24,15 @@
/*
* bits 3:0 - Isochronous Scheduling Threshold, frames or uframes that SW
* needs to queue transactions ahead of the HW to meet periodic deadlines.
+ * - Bits 2:0: Threshold value
+ * - Bit 3: Unit indicator
+ * - '1': Threshold in Frames
+ * - '0': Threshold in Microframes (uframes)
+ * Note: 1 Frame = 8 Microframes
+ * xHCI specification section 5.3.4.
*/
-#define HCS_IST(p) (((p) >> 0) & 0xf)
+#define HCS_IST_VALUE(p) ((p) & 0x7)
+#define HCS_IST_UNIT(p) ((p) & (1 << 3))
/* bits 7:4 - Event Ring Segment Table Max, 2^(n) */
#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf)
/* bits 20:8 - Rsvd */
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 0ac7f9870d3d..104fd6f83265 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -3961,6 +3961,16 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci,
return total_packet_count - 1;
}
+/* Returns the Isochronous Scheduling Threshold in Microframes. 1 Frame is 8 Microframes. */
+static int xhci_ist_microframes(struct xhci_hcd *xhci)
+{
+ int ist = HCS_IST_VALUE(xhci->hcs_params2);
+
+ if (HCS_IST_UNIT(xhci->hcs_params2))
+ ist *= 8;
+ return ist;
+}
+
/*
* Calculates Frame ID field of the isochronous TRB identifies the
* target frame that the Interval associated with this Isochronous
@@ -3980,17 +3990,7 @@ static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci,
else
start_frame = (urb->start_frame + index * urb->interval) >> 3;
- /* Isochronous Scheduling Threshold (IST, bits 0~3 in HCSPARAMS2):
- *
- * If bit [3] of IST is cleared to '0', software can add a TRB no
- * later than IST[2:0] Microframes before that TRB is scheduled to
- * be executed.
- * If bit [3] of IST is set to '1', software can add a TRB no later
- * than IST[2:0] Frames before that TRB is scheduled to be executed.
- */
- ist = HCS_IST(xhci->hcs_params2) & 0x7;
- if (HCS_IST(xhci->hcs_params2) & (1 << 3))
- ist <<= 3;
+ ist = xhci_ist_microframes(xhci);
/* Software shall not schedule an Isoch TD with a Frame ID value that
* is less than the Start Frame ID or greater than the End Frame ID,
@@ -4311,9 +4311,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
* Round up to the next frame and consider the time before trb really
* gets scheduled by hardare.
*/
- ist = HCS_IST(xhci->hcs_params2) & 0x7;
- if (HCS_IST(xhci->hcs_params2) & (1 << 3))
- ist <<= 3;
+ ist = xhci_ist_microframes(xhci);
start_frame += ist + XHCI_CFC_DELAY;
start_frame = roundup(start_frame, 8);
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 20/23] usb: xhci: simplify Max Scratchpad buffer macros
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (18 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 19/23] usb: xhci: simplify Isochronous Scheduling Threshold handling Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 21/23] usb: xhci: drop xhci-caps.h dependence on xhci-ext-caps.h Mathias Nyman
` (2 subsequent siblings)
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
Max Scratchpad Buffers consist of two bit-fields:
bits 25:21 - Max Scratchpad Buffers High, 5 Most significant bits
bits 27:31 - Max Scratchpad Buffers Low, 5 Least significant bits
Combined they create the Max Scratchpad Buffers value.
Add two new macros, 'HCS_MAX_SP_HI' and 'HCS_MAX_SP_LO', to separately
extract the high and low parts of the Max Scratchpad Buffers. These are
then combined using 'HCS_MAX_SCRATCHPAD' macro. This change simplifies
the code and makes it similar to other split value register macros in the
xhci driver.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-caps.h | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h
index e772d5f30d36..af47aebc5ba8 100644
--- a/drivers/usb/host/xhci-caps.h
+++ b/drivers/usb/host/xhci-caps.h
@@ -37,9 +37,11 @@
#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf)
/* bits 20:8 - Rsvd */
/* bits 25:21 - Max Scratchpad Buffers (Hi), 5 Most significant bits */
+#define HCS_MAX_SP_HI(p) (((p) >> 21) & 0x1f)
/* bit 26 - Scratchpad restore, for save/restore HW state */
/* bits 31:27 - Max Scratchpad Buffers (Lo), 5 Least significant bits */
-#define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f))
+#define HCS_MAX_SP_LO(p) (((p) >> 27) & 0x1f)
+#define HCS_MAX_SCRATCHPAD(p) (HCS_MAX_SP_HI(p) << 5 | HCS_MAX_SP_LO(p))
/* HCSPARAMS3 - hcs_params3 - bitmasks */
/* bits 7:0 - U1 Device Exit Latency, Max U1 to U0 latency for the roothub ports */
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 21/23] usb: xhci: drop xhci-caps.h dependence on xhci-ext-caps.h
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (19 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 20/23] usb: xhci: simplify Max Scratchpad buffer macros Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 22/23] usb: xhci: standardize single bit-field macros Mathias Nyman
2025-11-19 14:24 ` [PATCH 23/23] usb: xhci: Add debugfs support for xHCI Port Link Info (PORTLI) register Mathias Nyman
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
Drop the dependency of xhci-caps.h on xhci-ext-caps.h by eliminating 2
instances where macros in xhci-caps.h were redefined from xhci-ext-caps.h.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-caps.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h
index af47aebc5ba8..99557df89f88 100644
--- a/drivers/usb/host/xhci-caps.h
+++ b/drivers/usb/host/xhci-caps.h
@@ -6,7 +6,7 @@
/* hc_capbase - bitmasks */
/* bits 7:0 - Capability Registers Length */
-#define HC_LENGTH(p) XHCI_HC_LENGTH(p)
+#define HC_LENGTH(p) ((p) & 0xff)
/* bits 15:8 - Rsvd */
/* bits 31:16 - Host Controller Interface Version Number */
#define HC_VERSION(p) (((p) >> 16) & 0xffff)
@@ -77,7 +77,7 @@
/* bits 15:12 - Max size for Primary Stream Arrays, 2^(n+1) */
#define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1))
/* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */
-#define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p)
+#define HCC_EXT_CAPS(p) (((p) >> 16) & 0xffff)
/* DBOFF - db_off - bitmasks */
/* bits 1:0 - Rsvd */
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 22/23] usb: xhci: standardize single bit-field macros
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (20 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 21/23] usb: xhci: drop xhci-caps.h dependence on xhci-ext-caps.h Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2025-11-19 14:24 ` [PATCH 23/23] usb: xhci: Add debugfs support for xHCI Port Link Info (PORTLI) register Mathias Nyman
22 siblings, 0 replies; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Sakari Ailus, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
Convert single bit-field macros to simple masks. The change makes the
masks more universal. Multi bit-field macros are changed in the next
commit. After both changes, all masks in xhci-caps.h will follow the
same format. I plan to introduce this change to all xhci macros.
Bit shift operations on a 32-bit signed can be problematic on some
architectures. Instead use BIT() macro, which returns a 64-bit unsigned
value. This ensures that the shift operation is performed on an unsigned
type, which is safer and more portable across different architectures.
Using unsigned integers for bit shifts avoids issues related to sign bits
and ensures consistent behavior.
Switch from 32-bit to 64-bit?
As far as I am aware, this does not cause any issues.
Performing bitwise operations between 32 and 64 bit values, the smaller
operand is promoted to match the size of the larger one, resulting in a
64-bit operation. This promotion extends the 32-bit value to 64 bits,
by zero-padding (for unsigned).
Will the change to 64-bit slow down the xhci driver?
On a 64-bit architecture - No. On a 32-bit architecture, yes? but in my
opinion the performance decrease does not outweigh the readability and
other benefits of using BIT() macro.
Why not use FIELD_GET() and FIELD_PREP()?
While they can be used for single bit macros, I prefer to use simple
bitwise operation directly. Because, it takes less space, is less overhead
and is as clear as if using FIELD_GET() and FIELD_PREP().
Why not use test_bit() macro?
Same reason as with FIELD_GET() and FIELD_PREP().
Suggested-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Reviewed-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-caps.h | 48 +++++++++++++++++----------------
drivers/usb/host/xhci-debugfs.c | 2 +-
drivers/usb/host/xhci-hub.c | 6 ++---
drivers/usb/host/xhci-mem.c | 7 +++--
drivers/usb/host/xhci-ring.c | 8 +++---
drivers/usb/host/xhci-trace.h | 2 +-
drivers/usb/host/xhci.c | 2 +-
7 files changed, 38 insertions(+), 37 deletions(-)
diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h
index 99557df89f88..52153c4a43a8 100644
--- a/drivers/usb/host/xhci-caps.h
+++ b/drivers/usb/host/xhci-caps.h
@@ -4,6 +4,8 @@
* xHCI Specification Section 5.3, Revision 1.2.
*/
+#include <linux/bits.h>
+
/* hc_capbase - bitmasks */
/* bits 7:0 - Capability Registers Length */
#define HC_LENGTH(p) ((p) & 0xff)
@@ -32,7 +34,7 @@
* xHCI specification section 5.3.4.
*/
#define HCS_IST_VALUE(p) ((p) & 0x7)
-#define HCS_IST_UNIT(p) ((p) & (1 << 3))
+#define HCS_IST_UNIT BIT(3)
/* bits 7:4 - Event Ring Segment Table Max, 2^(n) */
#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf)
/* bits 20:8 - Rsvd */
@@ -52,28 +54,28 @@
/* HCCPARAMS1 - hcc_params - bitmasks */
/* bit 0 - 64-bit Addressing Capability */
-#define HCC_64BIT_ADDR(p) ((p) & (1 << 0))
+#define HCC_64BIT_ADDR BIT(0)
/* bit 1 - BW Negotiation Capability */
-#define HCC_BANDWIDTH_NEG(p) ((p) & (1 << 1))
+#define HCC_BANDWIDTH_NEG BIT(1)
/* bit 2 - Context Size */
-#define HCC_64BYTE_CONTEXT(p) ((p) & (1 << 2))
-#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32)
+#define HCC_64BYTE_CONTEXT BIT(2)
+#define CTX_SIZE(_hcc) (_hcc & HCC_64BYTE_CONTEXT ? 64 : 32)
/* bit 3 - Port Power Control */
-#define HCC_PPC(p) ((p) & (1 << 3))
+#define HCC_PPC BIT(3)
/* bit 4 - Port Indicators */
-#define HCS_INDICATOR(p) ((p) & (1 << 4))
+#define HCS_INDICATOR BIT(4)
/* bit 5 - Light HC Reset Capability */
-#define HCC_LIGHT_RESET(p) ((p) & (1 << 5))
+#define HCC_LIGHT_RESET BIT(5)
/* bit 6 - Latency Tolerance Messaging Capability */
-#define HCC_LTC(p) ((p) & (1 << 6))
+#define HCC_LTC BIT(6)
/* bit 7 - No Secondary Stream ID Support */
-#define HCC_NSS(p) ((p) & (1 << 7))
+#define HCC_NSS BIT(7)
/* bit 8 - Parse All Event Data */
/* bit 9 - Short Packet Capability */
-#define HCC_SPC(p) ((p) & (1 << 9))
+#define HCC_SPC BIT(9)
/* bit 10 - Stopped EDTLA Capability */
/* bit 11 - Contiguous Frame ID Capability */
-#define HCC_CFC(p) ((p) & (1 << 11))
+#define HCC_CFC BIT(11)
/* bits 15:12 - Max size for Primary Stream Arrays, 2^(n+1) */
#define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1))
/* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */
@@ -91,26 +93,26 @@
/* HCCPARAMS2 - hcc_params2 - bitmasks */
/* bit 0 - U3 Entry Capability */
-#define HCC2_U3C(p) ((p) & (1 << 0))
+#define HCC2_U3C BIT(0)
/* bit 1 - Configure Endpoint Command Max Exit Latency Too Large Capability */
-#define HCC2_CMC(p) ((p) & (1 << 1))
+#define HCC2_CMC BIT(1)
/* bit 2 - Force Save Context Capabilitu */
-#define HCC2_FSC(p) ((p) & (1 << 2))
+#define HCC2_FSC BIT(2)
/* bit 3 - Compliance Transition Capability, false: compliance is enabled by default */
-#define HCC2_CTC(p) ((p) & (1 << 3))
+#define HCC2_CTC BIT(3)
/* bit 4 - Large ESIT Payload Capability, true: HC support ESIT payload > 48k */
-#define HCC2_LEC(p) ((p) & (1 << 4))
+#define HCC2_LEC BIT(4)
/* bit 5 - Configuration Information Capability */
-#define HCC2_CIC(p) ((p) & (1 << 5))
+#define HCC2_CIC BIT(5)
/* bit 6 - Extended TBC Capability, true: Isoc burst count > 65535 */
-#define HCC2_ETC(p) ((p) & (1 << 6))
+#define HCC2_ETC BIT(6)
/* bit 7 - Extended TBC TRB Status Capability */
-#define HCC2_ETC_TSC(p) ((p) & (1 << 7))
+#define HCC2_ETC_TSC BIT(7)
/* bit 8 - Get/Set Extended Property Capability */
-#define HCC2_GSC(p) ((p) & (1 << 8))
+#define HCC2_GSC BIT(8)
/* bit 9 - Virtualization Based Trusted I/O Capability */
-#define HCC2_VTC(p) ((p) & (1 << 9))
+#define HCC2_VTC BIT(9)
/* bit 10 - Rsvd */
/* bit 11 - HC support Double BW on a eUSB2 HS ISOC EP */
-#define HCC2_EUSB2_DIC(p) ((p) & (1 << 11))
+#define HCC2_EUSB2_DIC BIT(11)
/* bits 31:12 - Rsvd */
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index ae50b667a548..5322911576eb 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -355,7 +355,7 @@ static ssize_t xhci_port_write(struct file *file, const char __user *ubuf,
if (!strncmp(buf, "compliance", 10)) {
/* If CTC is clear, compliance is enabled by default */
- if (!HCC2_CTC(xhci->hcc_params2))
+ if (!(xhci->hcc_params2 & HCC2_CTC))
return count;
spin_lock_irqsave(&xhci->lock, flags);
/* compliance mode can only be enabled on ports in RxDetect */
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index cf358e5c6642..04cc3d681495 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -110,7 +110,7 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf,
ss_cap->bU2DevExitLat = 0; /* set later */
reg = readl(&xhci->cap_regs->hcc_params);
- if (HCC_LTC(reg))
+ if (reg & HCC_LTC)
ss_cap->bmAttributes |= USB_LTM_SUPPORT;
if ((xhci->quirks & XHCI_LPM_SUPPORT)) {
@@ -263,7 +263,7 @@ static void xhci_common_hub_descriptor(struct xhci_hcd *xhci,
desc->bNbrPorts = ports;
temp = 0;
/* Bits 1:0 - support per-port power switching, or power always on */
- if (HCC_PPC(xhci->hcc_params))
+ if (xhci->hcc_params & HCC_PPC)
temp |= HUB_CHAR_INDV_PORT_LPSM;
else
temp |= HUB_CHAR_NO_LPSM;
@@ -1400,7 +1400,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
* automatically entered as on 1.0 and prior.
*/
if (link_state == USB_SS_PORT_LS_COMP_MOD) {
- if (!HCC2_CTC(xhci->hcc_params2)) {
+ if (!(xhci->hcc_params2 & HCC2_CTC)) {
xhci_dbg(xhci, "CTC flag is 0, port already supports entering compliance mode\n");
break;
}
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 2bbbf64a32c8..c708bdd69f16 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -463,7 +463,7 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
return NULL;
ctx->type = type;
- ctx->size = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024;
+ ctx->size = xhci->hcc_params & HCC_64BYTE_CONTEXT ? 2048 : 1024;
if (type == XHCI_CTX_TYPE_INPUT)
ctx->size += CTX_SIZE(xhci->hcc_params);
@@ -1344,7 +1344,7 @@ static u32 xhci_get_endpoint_mult(struct xhci_hcd *xhci,
bool lec;
/* xHCI 1.1 with LEC set does not use mult field, except intel eUSB2 */
- lec = xhci->hci_version > 0x100 && HCC2_LEC(xhci->hcc_params2);
+ lec = xhci->hci_version > 0x100 && (xhci->hcc_params2 & HCC2_LEC);
/* eUSB2 double isoc bw devices are the only USB2 devices using mult */
if (usb_endpoint_is_hs_isoc_double(udev, ep) &&
@@ -1433,8 +1433,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
ring_type = usb_endpoint_type(&ep->desc);
/* Ensure host supports double isoc bandwidth for eUSB2 devices */
- if (usb_endpoint_is_hs_isoc_double(udev, ep) &&
- !HCC2_EUSB2_DIC(xhci->hcc_params2)) {
+ if (usb_endpoint_is_hs_isoc_double(udev, ep) && !(xhci->hcc_params2 & HCC2_EUSB2_DIC)) {
dev_dbg(&udev->dev, "Double Isoc Bandwidth not supported by xhci\n");
return -EINVAL;
}
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 104fd6f83265..2247fd9dd163 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -3966,7 +3966,7 @@ static int xhci_ist_microframes(struct xhci_hcd *xhci)
{
int ist = HCS_IST_VALUE(xhci->hcs_params2);
- if (HCS_IST_UNIT(xhci->hcs_params2))
+ if (xhci->hcs_params2 & HCS_IST_UNIT)
ist *= 8;
return ist;
}
@@ -4135,7 +4135,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
/* use SIA as default, if frame id is used overwrite it */
sia_frame_id = TRB_SIA;
if (!(urb->transfer_flags & URB_ISO_ASAP) &&
- HCC_CFC(xhci->hcc_params)) {
+ (xhci->hcc_params & HCC_CFC)) {
frame_id = xhci_get_isoc_frame_id(xhci, urb, i);
if (frame_id >= 0)
sia_frame_id = TRB_FRAME_ID(frame_id);
@@ -4219,7 +4219,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
}
/* store the next frame id */
- if (HCC_CFC(xhci->hcc_params))
+ if (xhci->hcc_params & HCC_CFC)
xep->next_frame_id = urb->start_frame + num_tds * urb->interval;
if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
@@ -4298,7 +4298,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
check_interval(urb, ep_ctx);
/* Calculate the start frame and put it in urb->start_frame. */
- if (HCC_CFC(xhci->hcc_params) && !list_empty(&ep_ring->td_list)) {
+ if ((xhci->hcc_params & HCC_CFC) && !list_empty(&ep_ring->td_list)) {
if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) {
urb->start_frame = xep->next_frame_id;
goto skip_start_over;
diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
index 8e5b8e1282f7..724cba2dbb78 100644
--- a/drivers/usb/host/xhci-trace.h
+++ b/drivers/usb/host/xhci-trace.h
@@ -81,7 +81,7 @@ DECLARE_EVENT_CLASS(xhci_log_ctx,
),
TP_fast_assign(
- __entry->ctx_64 = HCC_64BYTE_CONTEXT(xhci->hcc_params);
+ __entry->ctx_64 = xhci->hcc_params & HCC_64BYTE_CONTEXT;
__entry->ctx_type = ctx->type;
__entry->ctx_dma = ctx->dma;
__entry->ctx_va = ctx->bytes;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 1e4032bf1223..57a74ffadc24 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -5495,7 +5495,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
/* Set dma_mask and coherent_dma_mask to 64-bits,
* if xHC supports 64-bit addressing */
- if (HCC_64BIT_ADDR(xhci->hcc_params) &&
+ if ((xhci->hcc_params & HCC_64BIT_ADDR) &&
!dma_set_mask(dev, DMA_BIT_MASK(64))) {
xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 23/23] usb: xhci: Add debugfs support for xHCI Port Link Info (PORTLI) register.
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
` (21 preceding siblings ...)
2025-11-19 14:24 ` [PATCH 22/23] usb: xhci: standardize single bit-field macros Mathias Nyman
@ 2025-11-19 14:24 ` Mathias Nyman
2026-03-04 9:42 ` Michal Pecio
22 siblings, 1 reply; 27+ messages in thread
From: Mathias Nyman @ 2025-11-19 14:24 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Rai, Amardeep, Rai, Mathias Nyman
From: "Rai, Amardeep" <amardeep.rai@intel.com>
Each xHCI roothub port has a Port Link Info (PORTLI) register that is
used by USB3 and eUSB2V2 ports.
USB3 ports show link error count, rx lane count, and tx lane count.
eUSB2V2 ports show Rx Data Rate (RDR) and Tx Data Rate (TDR).
Rx/Tx Data Rate is a multiple of USB2 2.0 HS 480 Mb/s data rates,
and is only valid if a eUSB2V2 device is connected (CCS=1).
0 = "USB 2.0 HS" normal HS 480 Mb/s, no eUSB2V2 in use
1 = "HS1" Assymetric eUSB2V2 where this direction runs normal 480Mb/s
2 = "HS2" 960Mb/s
...
10 = "HS10" 4.8 Gb/s, max eUSB2V2 rate
PORTLI is Reserved and preserve "RsvdP" for normal USB2 ports
Sample output of USB3 port PORTLI:
cat /sys/kernel/debug/usb/xhci/0000:00:14.0/ports/port14/portli
0x00000000 LEC=0 RLC=0 TLC=0
Signed-off-by: Rai, Amardeep <amardeep.rai@intel.com>
Co-developed-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-caps.h | 4 +++-
drivers/usb/host/xhci-debugfs.c | 34 +++++++++++++++++++++++++++++++++
drivers/usb/host/xhci-port.h | 5 +++++
3 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h
index 52153c4a43a8..2f59b6ab1e45 100644
--- a/drivers/usb/host/xhci-caps.h
+++ b/drivers/usb/host/xhci-caps.h
@@ -115,4 +115,6 @@
/* bit 10 - Rsvd */
/* bit 11 - HC support Double BW on a eUSB2 HS ISOC EP */
#define HCC2_EUSB2_DIC BIT(11)
-/* bits 31:12 - Rsvd */
+/* bit 12 - HC support eUSB2V2 capability */
+#define HCC2_E2V2C BIT(12)
+/* bits 31:13 - Rsvd */
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index 5322911576eb..c1eb1036ede9 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -383,6 +383,39 @@ static const struct file_operations port_fops = {
.release = single_release,
};
+static int xhci_portli_show(struct seq_file *s, void *unused)
+{
+ struct xhci_port *port = s->private;
+ struct xhci_hcd *xhci = hcd_to_xhci(port->rhub->hcd);
+ u32 portli;
+
+ portli = readl(&port->port_reg->portli);
+
+ /* PORTLI fields are valid if port is a USB3 or eUSB2V2 port */
+ if (port->rhub == &xhci->usb3_rhub)
+ seq_printf(s, "0x%08x LEC=%u RLC=%u TLC=%u\n", portli,
+ PORT_LEC(portli), PORT_RX_LANES(portli), PORT_TX_LANES(portli));
+ else if (xhci->hcc_params2 & HCC2_E2V2C)
+ seq_printf(s, "0x%08x RDR=%u TDR=%u\n", portli,
+ PORTLI_RDR(portli), PORTLI_TDR(portli));
+ else
+ seq_printf(s, "0x%08x RsvdP\n", portli);
+
+ return 0;
+}
+
+static int xhci_portli_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, xhci_portli_show, inode->i_private);
+}
+
+static const struct file_operations portli_fops = {
+ .open = xhci_portli_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static void xhci_debugfs_create_files(struct xhci_hcd *xhci,
struct xhci_file_map *files,
size_t nentries, void *data,
@@ -624,6 +657,7 @@ static void xhci_debugfs_create_ports(struct xhci_hcd *xhci,
dir = debugfs_create_dir(port_name, parent);
port = &xhci->hw_ports[i];
debugfs_create_file("portsc", 0644, dir, port, &port_fops);
+ debugfs_create_file("portli", 0444, dir, port, &portli_fops);
}
}
diff --git a/drivers/usb/host/xhci-port.h b/drivers/usb/host/xhci-port.h
index f19efb966d18..889b5fb0fcd8 100644
--- a/drivers/usb/host/xhci-port.h
+++ b/drivers/usb/host/xhci-port.h
@@ -144,9 +144,14 @@
#define PORT_TEST_MODE_SHIFT 28
/* USB3 Protocol PORTLI Port Link Information */
+#define PORT_LEC(p) ((p) & 0xffff)
#define PORT_RX_LANES(p) (((p) >> 16) & 0xf)
#define PORT_TX_LANES(p) (((p) >> 20) & 0xf)
+/* eUSB2v2 protocol PORTLI Port Link information, RsvdP for normal USB2 */
+#define PORTLI_RDR(p) ((p) & 0xf)
+#define PORTLI_TDR(p) (((p) >> 4) & 0xf)
+
/* USB2 Protocol PORTHLPMC */
#define PORT_HIRDM(p)((p) & 3)
#define PORT_L1_TIMEOUT(p)(((p) & 0xff) << 2)
--
2.43.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH 23/23] usb: xhci: Add debugfs support for xHCI Port Link Info (PORTLI) register.
2025-11-19 14:24 ` [PATCH 23/23] usb: xhci: Add debugfs support for xHCI Port Link Info (PORTLI) register Mathias Nyman
@ 2026-03-04 9:42 ` Michal Pecio
2026-03-04 14:25 ` Mathias Nyman
0 siblings, 1 reply; 27+ messages in thread
From: Michal Pecio @ 2026-03-04 9:42 UTC (permalink / raw)
To: Mathias Nyman; +Cc: gregkh, linux-usb, Rai Amardeep
On Wed, 19 Nov 2025 16:24:17 +0200, Mathias Nyman wrote:
> From: "Rai, Amardeep" <amardeep.rai@intel.com>
>
> Each xHCI roothub port has a Port Link Info (PORTLI) register that is
> used by USB3 and eUSB2V2 ports.
>
> USB3 ports show link error count, rx lane count, and tx lane count.
>
> eUSB2V2 ports show Rx Data Rate (RDR) and Tx Data Rate (TDR).
>
> Rx/Tx Data Rate is a multiple of USB2 2.0 HS 480 Mb/s data rates,
> and is only valid if a eUSB2V2 device is connected (CCS=1).
>
> 0 = "USB 2.0 HS" normal HS 480 Mb/s, no eUSB2V2 in use
> 1 = "HS1" Assymetric eUSB2V2 where this direction runs normal 480Mb/s
> 2 = "HS2" 960Mb/s
> ...
> 10 = "HS10" 4.8 Gb/s, max eUSB2V2 rate
>
> PORTLI is Reserved and preserve "RsvdP" for normal USB2 ports
>
> Sample output of USB3 port PORTLI:
> cat /sys/kernel/debug/usb/xhci/0000:00:14.0/ports/port14/portli
> 0x00000000 LEC=0 RLC=0 TLC=0
>
> Signed-off-by: Rai, Amardeep <amardeep.rai@intel.com>
> Co-developed-by: Mathias Nyman <mathias.nyman@linux.intel.com>
> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Hi,
This patch causes an oops when there are more port registers counted in
xhci->max_ports than ports reported by Supported Protocol capabilities.
On my HW it's due to max_ports being more than maximum port number, but
it seems that gaps between ports of different speeds are also possible.
> +static int xhci_portli_show(struct seq_file *s, void *unused)
> +{
> + struct xhci_port *port = s->private;
> + struct xhci_hcd *xhci = hcd_to_xhci(port->rhub->hcd);
In such cases port->rhub will be NULL so we can't reach xhci. One
obvious solution (which works for me) is an explicit NULL check here
and another seq_printf just for this case, followed by early return.
Or maybe not bother printing anything and just return?
Or make this check before creating the files in the first place?
> + u32 portli;
> +
> + portli = readl(&port->port_reg->portli);
> +
> + /* PORTLI fields are valid if port is a USB3 or eUSB2V2 port */
> + if (port->rhub == &xhci->usb3_rhub)
> + seq_printf(s, "0x%08x LEC=%u RLC=%u TLC=%u\n", portli,
> + PORT_LEC(portli), PORT_RX_LANES(portli), PORT_TX_LANES(portli));
> + else if (xhci->hcc_params2 & HCC2_E2V2C)
> + seq_printf(s, "0x%08x RDR=%u TDR=%u\n", portli,
> + PORTLI_RDR(portli), PORTLI_TDR(portli));
Or I suppose we could alternatively use PORTSC to determine speed
and remove the dependency on hcc_params2 by always decoding eUSB2v2
fields whether the HW supports it or not. It's debugfs, who cares.
> + else
> + seq_printf(s, "0x%08x RsvdP\n", portli);
> +
> + return 0;
> +}
Regards,
Michal
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 23/23] usb: xhci: Add debugfs support for xHCI Port Link Info (PORTLI) register.
2026-03-04 9:42 ` Michal Pecio
@ 2026-03-04 14:25 ` Mathias Nyman
2026-03-04 16:40 ` Michal Pecio
0 siblings, 1 reply; 27+ messages in thread
From: Mathias Nyman @ 2026-03-04 14:25 UTC (permalink / raw)
To: Michal Pecio; +Cc: gregkh, linux-usb, Rai Amardeep
On 3/4/26 11:42, Michal Pecio wrote:
> On Wed, 19 Nov 2025 16:24:17 +0200, Mathias Nyman wrote:
>> From: "Rai, Amardeep" <amardeep.rai@intel.com>
>>
>> Each xHCI roothub port has a Port Link Info (PORTLI) register that is
>> used by USB3 and eUSB2V2 ports.
>>
>> USB3 ports show link error count, rx lane count, and tx lane count.
>>
>> eUSB2V2 ports show Rx Data Rate (RDR) and Tx Data Rate (TDR).
>>
>> Rx/Tx Data Rate is a multiple of USB2 2.0 HS 480 Mb/s data rates,
>> and is only valid if a eUSB2V2 device is connected (CCS=1).
>>
>> 0 = "USB 2.0 HS" normal HS 480 Mb/s, no eUSB2V2 in use
>> 1 = "HS1" Assymetric eUSB2V2 where this direction runs normal 480Mb/s
>> 2 = "HS2" 960Mb/s
>> ...
>> 10 = "HS10" 4.8 Gb/s, max eUSB2V2 rate
>>
>> PORTLI is Reserved and preserve "RsvdP" for normal USB2 ports
>>
>> Sample output of USB3 port PORTLI:
>> cat /sys/kernel/debug/usb/xhci/0000:00:14.0/ports/port14/portli
>> 0x00000000 LEC=0 RLC=0 TLC=0
>>
>> Signed-off-by: Rai, Amardeep <amardeep.rai@intel.com>
>> Co-developed-by: Mathias Nyman <mathias.nyman@linux.intel.com>
>> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
>
> Hi,
>
> This patch causes an oops when there are more port registers counted in
> xhci->max_ports than ports reported by Supported Protocol capabilities.
> On my HW it's due to max_ports being more than maximum port number, but
> it seems that gaps between ports of different speeds are also possible.
>
>> +static int xhci_portli_show(struct seq_file *s, void *unused)
>> +{
>> + struct xhci_port *port = s->private;
>> + struct xhci_hcd *xhci = hcd_to_xhci(port->rhub->hcd);
>
> In such cases port->rhub will be NULL so we can't reach xhci. One
> obvious solution (which works for me) is an explicit NULL check here
> and another seq_printf just for this case, followed by early return.
>
Thanks for reporting and debugging this.
I was able to fake a similar scenario by leaving out a port in xhci_add_in_port().
It triggered the same oops.
The null pointer check you suggested below fixes it for me. Does it work for you?
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index c1eb1036ede9..5ff5b761bccf 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -386,11 +386,19 @@ static const struct file_operations port_fops = {
static int xhci_portli_show(struct seq_file *s, void *unused)
{
struct xhci_port *port = s->private;
- struct xhci_hcd *xhci = hcd_to_xhci(port->rhub->hcd);
+ struct xhci_hcd *xhci;
u32 portli;
portli = readl(&port->port_reg->portli);
+ /* port without protocol capability isn't added to a roothub */
+ if (!port->rhub) {
+ seq_printf(s, "0x%08x\n", portli);
+ return 0;
+ }
+
+ xhci = hcd_to_xhci(port->rhub->hcd);
+
/* PORTLI fields are valid if port is a USB3 or eUSB2V2 port */
if (port->rhub == &xhci->usb3_rhub)
seq_printf(s, "0x%08x LEC=%u RLC=%u TLC=%u\n", portli,
Thanks
Mathias
^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH 23/23] usb: xhci: Add debugfs support for xHCI Port Link Info (PORTLI) register.
2026-03-04 14:25 ` Mathias Nyman
@ 2026-03-04 16:40 ` Michal Pecio
0 siblings, 0 replies; 27+ messages in thread
From: Michal Pecio @ 2026-03-04 16:40 UTC (permalink / raw)
To: Mathias Nyman; +Cc: gregkh, linux-usb, Rai Amardeep
On Wed, 4 Mar 2026 16:25:45 +0200, Mathias Nyman wrote:
> I was able to fake a similar scenario by leaving out a port in
> xhci_add_in_port(). It triggered the same oops.
>
> The null pointer check you suggested below fixes it for me. Does it
> work for you?
It should, it's practically identical to my own.
I added "unused" to the printf.
^ permalink raw reply [flat|nested] 27+ messages in thread
end of thread, other threads:[~2026-03-04 16:40 UTC | newest]
Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-19 14:23 [PATCH 00/23] xhci features for usb-next Mathias Nyman
2025-11-19 14:23 ` [PATCH 01/23] usb: xhci: limit run_graceperiod for only usb 3.0 devices Mathias Nyman
2025-11-19 14:23 ` [PATCH 02/23] xhci: Add helper to find trb from its dma address Mathias Nyman
2025-11-19 14:23 ` [PATCH 03/23] xhci: simplify and rework trb_in_td() Mathias Nyman
2025-11-19 14:23 ` [PATCH 04/23] usb: xhci: rework xhci_decode_portsc() Mathias Nyman
2025-11-19 14:23 ` [PATCH 05/23] usb: xhci: add tracing for PORTSC register writes Mathias Nyman
2025-11-19 14:24 ` [PATCH 06/23] usb: xhci: add helper to read PORTSC register Mathias Nyman
2025-11-19 14:24 ` [PATCH 07/23] usb: xhci: add USB Port Register Set struct Mathias Nyman
2025-11-19 14:24 ` [PATCH 08/23] usb: xhci: implement " Mathias Nyman
2025-11-19 14:24 ` [PATCH 09/23] usb: xhci: Assume that endpoints halt as specified Mathias Nyman
2025-11-19 14:24 ` [PATCH 10/23] usb: xhci: Don't unchain link TRBs on quirky HCs Mathias Nyman
2025-11-19 14:24 ` [PATCH 11/23] usb: xhci: replace use of system_wq with system_percpu_wq Mathias Nyman
2025-11-19 14:24 ` [PATCH 12/23] usb: xhci: remove deprecated TODO comment Mathias Nyman
2025-11-19 14:24 ` [PATCH 13/23] usb: xhci: remove unused trace operation and argument Mathias Nyman
2025-11-19 14:24 ` [PATCH 14/23] usb: xhci: use cached HCSPARAMS1 value Mathias Nyman
2025-11-19 14:24 ` [PATCH 15/23] usb: xhci: simplify handling of Structural Parameters 1 values Mathias Nyman
2025-11-19 14:24 ` [PATCH 16/23] usb: xhci: limit number of ports to 127 Mathias Nyman
2025-11-19 14:24 ` [PATCH 17/23] usb: xhci: limit number of interrupts to 128 Mathias Nyman
2025-11-19 14:24 ` [PATCH 18/23] usb: xhci: improve xhci-caps.h comments Mathias Nyman
2025-11-19 14:24 ` [PATCH 19/23] usb: xhci: simplify Isochronous Scheduling Threshold handling Mathias Nyman
2025-11-19 14:24 ` [PATCH 20/23] usb: xhci: simplify Max Scratchpad buffer macros Mathias Nyman
2025-11-19 14:24 ` [PATCH 21/23] usb: xhci: drop xhci-caps.h dependence on xhci-ext-caps.h Mathias Nyman
2025-11-19 14:24 ` [PATCH 22/23] usb: xhci: standardize single bit-field macros Mathias Nyman
2025-11-19 14:24 ` [PATCH 23/23] usb: xhci: Add debugfs support for xHCI Port Link Info (PORTLI) register Mathias Nyman
2026-03-04 9:42 ` Michal Pecio
2026-03-04 14:25 ` Mathias Nyman
2026-03-04 16:40 ` Michal Pecio
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox