* [PATCH 00/15] xhci features for usb-next
@ 2026-06-03 9:11 Mathias Nyman
2026-06-03 9:11 ` [PATCH 01/15] usb: xhci: fix typo in xhci_set_port_power() comment Mathias Nyman
` (14 more replies)
0 siblings, 15 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Mathias Nyman
Hi Greg
xhci features for usb-next with generic improvements, cleanups,
refactoring and some DbC hung state detection and recovery
Mathias Nyman (6):
xhci: dbc: Fix sysfs ABI Documentation for xhci dbc states
xhci: dbc: serialize enabling and disabling dbc
xhci: dbc: add helper to set and clear DbC DCE enable bit
xhci: dbc: add timestamps to DbC state changes in a new helper.
xhci: dbc: detect and recover hung DbC during enumeraton
xhci: Prevent queuing new commands if xhci is inaccessible
Michal Pecio (4):
usb: xhci: Simplify xhci_quiesce()
usb: xhci: Remove skip_isoc_td()
usb: xhci: Remove isochronous URB_SHORT_NOT_OK handling
usb: xhci: Improve Soft Retries after short transfers
Niklas Neronin (4):
usb: xhci: remove legacy 'num_trbs_free' tracking
usb: xhci: refactor DCBAA struct
usb: xhci: allocate DCBAA based on host controller max slots
usb: xhci: allocate internal DCBAA mirror dynamically
Stepan Ionichev (1):
usb: xhci: fix typo in xhci_set_port_power() comment
.../testing/sysfs-bus-pci-drivers-xhci_hcd | 2 +-
drivers/usb/host/xhci-dbgcap.c | 144 ++++++++++++------
drivers/usb/host/xhci-dbgcap.h | 3 +
drivers/usb/host/xhci-hub.c | 2 +-
drivers/usb/host/xhci-mem.c | 60 ++++----
drivers/usb/host/xhci-ring.c | 53 +++----
drivers/usb/host/xhci.c | 23 ++-
drivers/usb/host/xhci.h | 29 ++--
8 files changed, 177 insertions(+), 139 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 01/15] usb: xhci: fix typo in xhci_set_port_power() comment
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
2026-06-03 9:11 ` [PATCH 02/15] usb: xhci: remove legacy 'num_trbs_free' tracking Mathias Nyman
` (13 subsequent siblings)
14 siblings, 0 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Stepan Ionichev, Mathias Nyman
From: Stepan Ionichev <sozdayvek@gmail.com>
Fix a spelling mistake (re-aquire -> re-acquire) in the function
header comment.
No functional change.
Signed-off-by: Stepan Ionichev <sozdayvek@gmail.com>
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 bacd0ddd0d09..712eaf330a76 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -639,7 +639,7 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd)
/*
* xhci_set_port_power() must be called with xhci->lock held.
- * It will release and re-aquire the lock while calling ACPI
+ * It will release and re-acquire the lock while calling ACPI
* method.
*/
static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port,
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 02/15] usb: xhci: remove legacy 'num_trbs_free' tracking
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
2026-06-03 9:11 ` [PATCH 01/15] usb: xhci: fix typo in xhci_set_port_power() comment Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
2026-06-03 9:11 ` [PATCH 03/15] usb: xhci: Simplify xhci_quiesce() Mathias Nyman
` (12 subsequent siblings)
14 siblings, 0 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
Keeping track of free TRBs in a ring by adding and subtracting each time
a enqueue or dequeue pointer is modified has proven to be buggy and
complicated, especially over long periods of time.
The xhci driver has already moved to calculating free TRBs dynamically
based on ring size and the enqueue/dequeue positions.
The DbC path is the last user of 'num_trbs_free'. Rather than maintaining
two separate accounting mechanisms, remove the field entirely and switch
DbC to use xhci_num_trbs_free(). Since 'num_trbs_free' undercounts by one,
and xhci_num_trbs_free() does not, the check for sufficient free TRBs is
adjusted.
Signed-off-by: Niklas Neronin <niklas.neronin@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-dbgcap.c | 5 +----
drivers/usb/host/xhci-mem.c | 6 ------
drivers/usb/host/xhci-ring.c | 2 +-
drivers/usb/host/xhci.h | 2 +-
4 files changed, 3 insertions(+), 12 deletions(-)
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index 7e6f7d72f03e..6a9f73fecb73 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -275,7 +275,6 @@ xhci_dbc_queue_trb(struct xhci_ring *ring, u32 field1,
trace_xhci_dbc_gadget_ep_queue(ring, &trb->generic,
xhci_trb_virt_to_dma(ring->enq_seg,
ring->enqueue));
- ring->num_trbs_free--;
next = ++(ring->enqueue);
if (TRB_TYPE_LINK_LE32(next->link.control)) {
next->link.control ^= cpu_to_le32(TRB_CYCLE);
@@ -296,7 +295,7 @@ static int xhci_dbc_queue_bulk_tx(struct dbc_ep *dep,
num_trbs = count_trbs(req->dma, req->length);
WARN_ON(num_trbs != 1);
- if (ring->num_trbs_free < num_trbs)
+ if (xhci_num_trbs_free(ring) <= num_trbs)
return -EBUSY;
addr = req->dma;
@@ -796,7 +795,6 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event)
}
if (r->status == -COMP_STALL_ERROR) {
dev_warn(dbc->dev, "Give back stale stalled req\n");
- ring->num_trbs_free++;
xhci_dbc_giveback(r, 0);
}
}
@@ -861,7 +859,6 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event)
break;
}
- ring->num_trbs_free++;
req->actual = req->length - remain_length;
xhci_dbc_giveback(req, status);
}
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 997fe90f54e5..289461c06bf9 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -324,12 +324,6 @@ void xhci_initialize_ring_info(struct xhci_ring *ring)
* handling ring expansion, set the cycle state equal to the old ring.
*/
ring->cycle_state = 1;
-
- /*
- * Each segment has a link TRB, and leave an extra TRB for SW
- * accounting purpose
- */
- ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1;
}
EXPORT_SYMBOL_GPL(xhci_initialize_ring_info);
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index e47e644b296e..f62db238276d 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -340,7 +340,7 @@ static bool trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma)
* Only for transfer and command rings where driver is the producer, not for
* event rings.
*/
-static unsigned int xhci_num_trbs_free(struct xhci_ring *ring)
+unsigned int xhci_num_trbs_free(struct xhci_ring *ring)
{
struct xhci_segment *enq_seg = ring->enq_seg;
union xhci_trb *enq = ring->enqueue;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index aeecd301f207..e73c498449e8 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1376,7 +1376,6 @@ struct xhci_ring {
u32 cycle_state;
unsigned int stream_id;
unsigned int num_segs;
- unsigned int num_trbs_free; /* used only by xhci DbC */
unsigned int bounce_buf_len;
enum xhci_ring_type type;
u32 old_trb_comp_code;
@@ -1955,6 +1954,7 @@ void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
void xhci_cleanup_command_queue(struct xhci_hcd *xhci);
void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring);
unsigned int count_trbs(u64 addr, u64 len);
+unsigned int xhci_num_trbs_free(struct xhci_ring *ring);
int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
int suspend, gfp_t gfp_flags);
void xhci_process_cancelled_tds(struct xhci_virt_ep *ep);
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 03/15] usb: xhci: Simplify xhci_quiesce()
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
2026-06-03 9:11 ` [PATCH 01/15] usb: xhci: fix typo in xhci_set_port_power() comment Mathias Nyman
2026-06-03 9:11 ` [PATCH 02/15] usb: xhci: remove legacy 'num_trbs_free' tracking Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
2026-06-03 9:11 ` [PATCH 04/15] usb: xhci: Remove skip_isoc_td() Mathias Nyman
` (11 subsequent siblings)
14 siblings, 0 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Michal Pecio, Mathias Nyman
From: Michal Pecio <michal.pecio@gmail.com>
The function reads USBCMD, clears some bits and writes it back.
Its treatment of the Run bit is weird: the bit is usually written
as 0, as we would expect, but it may also be written as 1 if both
its current value and USBSTS.HCHalted are observed as 1.
Per xHCI 5.4.2, HCHalted is 0 whenever Run is 1, so the above can
only happen due to buggy HW or SW, e.g. concurrent xhci_quiesce()
and xhci_start() execution.
It's unclear why we should treat such cases specially and write
the bit as 1. The logic comes from original PoC implementation
and has never been explained. Just write 0 every time, which
looks like the safer choice when the intent is to stop the xHC.
We could get in trouble if clearing Run causes some very broken
xHC to start running after it was halted, but no such case has
been documented. It seems the logic was just poorly thought out.
Signed-off-by: Michal Pecio <michal.pecio@gmail.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci.c | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index a54f5b57f205..0bf0446b4c87 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -102,17 +102,10 @@ int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us)
*/
void xhci_quiesce(struct xhci_hcd *xhci)
{
- u32 halted;
u32 cmd;
- u32 mask;
-
- mask = ~(XHCI_IRQS);
- halted = readl(&xhci->op_regs->status) & STS_HALT;
- if (!halted)
- mask &= ~CMD_RUN;
cmd = readl(&xhci->op_regs->command);
- cmd &= mask;
+ cmd &= ~(CMD_RUN | XHCI_IRQS);
writel(cmd, &xhci->op_regs->command);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 04/15] usb: xhci: Remove skip_isoc_td()
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
` (2 preceding siblings ...)
2026-06-03 9:11 ` [PATCH 03/15] usb: xhci: Simplify xhci_quiesce() Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
2026-06-03 9:11 ` [PATCH 05/15] usb: xhci: Remove isochronous URB_SHORT_NOT_OK handling Mathias Nyman
` (10 subsequent siblings)
14 siblings, 0 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Michal Pecio, Mathias Nyman
From: Michal Pecio <michal.pecio@gmail.com>
This function is pointless because usb_submit_urb() initializes all
isoc frame descriptors to -EXDEV and 0 length so that HCDs don't need
to do anything with transfers which were never executed.
Other HCDs rely on this (e.g. EHCI itd_complete()), so we can too.
This gets rid of a potentially dangereous function which could corrupt
memory if we weren't super careful to only call it on isoc URBs.
Also, set status to 0 rather than any random status determined by the
later TD which caused skipping. This status will be ignored anyway.
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 | 26 +++++---------------------
1 file changed, 5 insertions(+), 21 deletions(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index f62db238276d..1fbf43a51037 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2492,26 +2492,6 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
finish_td(xhci, ep, ep_ring, td, trb_comp_code);
}
-static void skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
- struct xhci_virt_ep *ep, int status)
-{
- struct urb_priv *urb_priv;
- struct usb_iso_packet_descriptor *frame;
- int idx;
-
- urb_priv = td->urb->hcpriv;
- idx = urb_priv->num_tds_done;
- frame = &td->urb->iso_frame_desc[idx];
-
- /* The transfer is partly done. */
- frame->status = -EXDEV;
-
- /* calc actual length */
- frame->actual_length = 0;
-
- xhci_dequeue_td(xhci, td, ep->ring, status);
-}
-
/*
* Process bulk and interrupt tds, update urb status and actual_length.
*/
@@ -2854,7 +2834,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if (trb_comp_code == COMP_STOPPED_LENGTH_INVALID)
return 0;
- skip_isoc_td(xhci, td, ep, status);
+ /*
+ * TD was missed, skip it. Core already initialized frame->status
+ * to -EXDEV and frame->actual_length to 0, nothing more to do.
+ */
+ xhci_dequeue_td(xhci, td, ep_ring, 0);
if (!list_empty(&ep_ring->td_list)) {
if (ring_xrun_event) {
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 05/15] usb: xhci: Remove isochronous URB_SHORT_NOT_OK handling
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
` (3 preceding siblings ...)
2026-06-03 9:11 ` [PATCH 04/15] usb: xhci: Remove skip_isoc_td() Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
2026-06-03 9:11 ` [PATCH 06/15] usb: xhci: Improve Soft Retries after short transfers Mathias Nyman
` (9 subsequent siblings)
14 siblings, 0 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Michal Pecio, Mathias Nyman
From: Michal Pecio <michal.pecio@gmail.com>
This URB flag was never supposed to have any effect on isoc endpoints.
No kernel code uses the flag except usb_sg_init(), on non-isoc only.
USBFS can't use it on isoc because proc_do_submiturb() rejects it.
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 | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 1fbf43a51037..a5342d49a65b 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2395,7 +2395,6 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
u32 trb_comp_code;
bool sum_trbs_for_length = false;
u32 remaining, requested, ep_trb_len;
- int short_framestatus;
trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
urb_priv = td->urb->hcpriv;
@@ -2404,8 +2403,6 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
requested = frame->length;
remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
ep_trb_len = TRB_LEN(le32_to_cpu(ep_trb->generic.field[2]));
- short_framestatus = td->urb->transfer_flags & URB_SHORT_NOT_OK ?
- -EREMOTEIO : 0;
/* handle completion code */
switch (trb_comp_code) {
@@ -2413,15 +2410,12 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
/* Don't overwrite status if TD had an error, see xHCI 4.9.1 */
if (td->error_mid_td)
break;
- if (remaining) {
- frame->status = short_framestatus;
+ if (remaining)
sum_trbs_for_length = true;
- break;
- }
frame->status = 0;
break;
case COMP_SHORT_PACKET:
- frame->status = short_framestatus;
+ frame->status = 0;
sum_trbs_for_length = true;
break;
case COMP_BANDWIDTH_OVERRUN_ERROR:
@@ -2456,7 +2450,7 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
break;
case COMP_STOPPED_SHORT_PACKET:
/* field normally containing residue now contains transferred */
- frame->status = short_framestatus;
+ frame->status = 0;
requested = remaining;
break;
case COMP_STOPPED_LENGTH_INVALID:
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 06/15] usb: xhci: Improve Soft Retries after short transfers
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
` (4 preceding siblings ...)
2026-06-03 9:11 ` [PATCH 05/15] usb: xhci: Remove isochronous URB_SHORT_NOT_OK handling Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
2026-06-03 9:11 ` [PATCH 07/15] xhci: dbc: Fix sysfs ABI Documentation for xhci dbc states Mathias Nyman
` (8 subsequent siblings)
14 siblings, 0 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Michal Pecio, Mathias Nyman
From: Michal Pecio <michal.pecio@gmail.com>
A short transfer is a successful one, so reset the error count.
Otherwise, endpoints which always complete short are limited to
three retries per endpoint life rather than per URB.
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 | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index a5342d49a65b..608b6f3ec9f6 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2516,6 +2516,7 @@ static void process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
td->status = 0;
break;
case COMP_SHORT_PACKET:
+ ep->err_count = 0;
td->status = 0;
break;
case COMP_STOPPED_SHORT_PACKET:
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 07/15] xhci: dbc: Fix sysfs ABI Documentation for xhci dbc states
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
` (5 preceding siblings ...)
2026-06-03 9:11 ` [PATCH 06/15] usb: xhci: Improve Soft Retries after short transfers Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
2026-06-03 9:11 ` [PATCH 08/15] xhci: dbc: serialize enabling and disabling dbc Mathias Nyman
` (7 subsequent siblings)
14 siblings, 0 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Mathias Nyman
Reading the 'dbc' sysfs file won't return the "stalled" string
anymore as "stalled" is no longer considered a DbC state since
commit 9044ad57b60b ("xhci: dbc: Fix STALL transfer event handling")
in 6.12 kernel.
Remove it from sysfs-bus-pci-drivers-xhci_hcd ABI documentation
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd b/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd
index d10e6de3adb2..98a8376a83d2 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd
+++ b/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd
@@ -22,7 +22,7 @@ Description:
Reading this attribute gives the state of the DbC. It
can be one of the following states: disabled, enabled,
- initialized, connected, configured and stalled.
+ initialized, connected or configured.
What: /sys/bus/pci/drivers/xhci_hcd/.../dbc_idVendor
Date: March 2023
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 08/15] xhci: dbc: serialize enabling and disabling dbc
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
` (6 preceding siblings ...)
2026-06-03 9:11 ` [PATCH 07/15] xhci: dbc: Fix sysfs ABI Documentation for xhci dbc states Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
2026-06-15 6:11 ` Borah, Chaitanya Kumar
2026-06-03 9:11 ` [PATCH 09/15] xhci: dbc: add helper to set and clear DbC DCE enable bit Mathias Nyman
` (6 subsequent siblings)
14 siblings, 1 reply; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Mathias Nyman
DbC can be enabled and disabled via sysfs, serialize those
with a mutex to make sure everything is done in the correct
order.
remove xhci_do_dbc_stop() and integrate the register write and
dbc->state setting into xhci_do_stop()
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-dbgcap.c | 66 ++++++++++++++++++++--------------
drivers/usb/host/xhci-dbgcap.h | 1 +
2 files changed, 40 insertions(+), 27 deletions(-)
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index 6a9f73fecb73..49ae546c4103 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -661,17 +661,6 @@ static int xhci_do_dbc_start(struct xhci_dbc *dbc)
return 0;
}
-static int xhci_do_dbc_stop(struct xhci_dbc *dbc)
-{
- if (dbc->state == DS_DISABLED)
- return -EINVAL;
-
- writel(0, &dbc->regs->control);
- dbc->state = DS_DISABLED;
-
- return 0;
-}
-
static int xhci_dbc_start(struct xhci_dbc *dbc)
{
int ret;
@@ -683,29 +672,37 @@ static int xhci_dbc_start(struct xhci_dbc *dbc)
spin_lock_irqsave(&dbc->lock, flags);
ret = xhci_do_dbc_start(dbc);
+ if (ret)
+ goto err_unlock;
+
spin_unlock_irqrestore(&dbc->lock, flags);
- if (ret) {
- pm_runtime_put(dbc->dev); /* note this was self.controller */
- return ret;
- }
+ mod_delayed_work(system_percpu_wq, &dbc->event_work,
+ msecs_to_jiffies(dbc->poll_interval));
- return mod_delayed_work(system_percpu_wq, &dbc->event_work,
- msecs_to_jiffies(dbc->poll_interval));
+ return 0;
+
+err_unlock:
+
+ spin_unlock_irqrestore(&dbc->lock, flags);
+ pm_runtime_put(dbc->dev); /* note this was self.controller */
+
+ return ret;
}
static void xhci_dbc_stop(struct xhci_dbc *dbc)
{
- int ret;
unsigned long flags;
WARN_ON(!dbc);
+ spin_lock(&dbc->lock);
+
switch (dbc->state) {
case DS_DISABLED:
+ spin_unlock(&dbc->lock);
return;
case DS_CONFIGURED:
- spin_lock(&dbc->lock);
xhci_dbc_flush_requests(dbc);
spin_unlock(&dbc->lock);
@@ -713,19 +710,20 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
dbc->driver->disconnect(dbc);
break;
default:
+ spin_unlock(&dbc->lock);
break;
}
cancel_delayed_work_sync(&dbc->event_work);
spin_lock_irqsave(&dbc->lock, flags);
- ret = xhci_do_dbc_stop(dbc);
+ writel(0, &dbc->regs->control);
+ dbc->state = DS_DISABLED;
spin_unlock_irqrestore(&dbc->lock, flags);
- if (ret)
- return;
xhci_dbc_mem_cleanup(dbc);
- pm_runtime_put_sync(dbc->dev); /* note, was self.controller */
+
+ pm_runtime_put(dbc->dev); /* note, was self.controller */
}
static void
@@ -1072,12 +1070,17 @@ static ssize_t dbc_store(struct device *dev,
xhci = hcd_to_xhci(dev_get_drvdata(dev));
dbc = xhci->dbc;
- if (sysfs_streq(buf, "enable"))
+ if (sysfs_streq(buf, "enable")) {
+ mutex_lock(&dbc->enable_mutex);
xhci_dbc_start(dbc);
- else if (sysfs_streq(buf, "disable"))
+ mutex_unlock(&dbc->enable_mutex);
+ } else if (sysfs_streq(buf, "disable")) {
+ mutex_lock(&dbc->enable_mutex);
xhci_dbc_stop(dbc);
- else
+ mutex_unlock(&dbc->enable_mutex);
+ } else {
return -EINVAL;
+ }
return count;
}
@@ -1443,6 +1446,7 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *
INIT_DELAYED_WORK(&dbc->event_work, xhci_dbc_handle_events);
spin_lock_init(&dbc->lock);
+ mutex_init(&dbc->enable_mutex);
ret = sysfs_create_groups(&dev->kobj, dbc_dev_groups);
if (ret)
@@ -1460,8 +1464,9 @@ void xhci_dbc_remove(struct xhci_dbc *dbc)
if (!dbc)
return;
/* stop hw, stop wq and call dbc->ops->stop() */
+ mutex_lock(&dbc->enable_mutex);
xhci_dbc_stop(dbc);
-
+ mutex_unlock(&dbc->enable_mutex);
/* remove sysfs files */
sysfs_remove_groups(&dbc->dev->kobj, dbc_dev_groups);
@@ -1514,6 +1519,8 @@ int xhci_dbc_suspend(struct xhci_hcd *xhci)
if (!dbc)
return 0;
+ mutex_lock(&dbc->enable_mutex);
+
switch (dbc->state) {
case DS_ENABLED:
case DS_CONNECTED:
@@ -1525,6 +1532,7 @@ int xhci_dbc_suspend(struct xhci_hcd *xhci)
}
xhci_dbc_stop(dbc);
+ mutex_unlock(&dbc->enable_mutex);
return 0;
}
@@ -1537,11 +1545,15 @@ int xhci_dbc_resume(struct xhci_hcd *xhci)
if (!dbc)
return 0;
+ mutex_lock(&dbc->enable_mutex);
+
if (dbc->resume_required) {
dbc->resume_required = 0;
xhci_dbc_start(dbc);
}
+ mutex_unlock(&dbc->enable_mutex);
+
return ret;
}
#endif /* CONFIG_PM */
diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
index 20ae4e7617f2..f4c0169a1b4d 100644
--- a/drivers/usb/host/xhci-dbgcap.h
+++ b/drivers/usb/host/xhci-dbgcap.h
@@ -140,6 +140,7 @@ struct dbc_driver {
struct xhci_dbc {
spinlock_t lock; /* device access */
+ struct mutex enable_mutex;
struct device *dev;
struct xhci_hcd *xhci;
struct dbc_regs __iomem *regs;
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 09/15] xhci: dbc: add helper to set and clear DbC DCE enable bit
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
` (7 preceding siblings ...)
2026-06-03 9:11 ` [PATCH 08/15] xhci: dbc: serialize enabling and disabling dbc Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
2026-06-03 9:11 ` [PATCH 10/15] xhci: dbc: add timestamps to DbC state changes in a new helper Mathias Nyman
` (5 subsequent siblings)
14 siblings, 0 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Mathias Nyman
Add xhci_dbc_enable_dce() helper to enable or disable DbC by manipulating
DCE bit correctly. It will be used for stuck DbC recovery attempts in
addition to normal DbC enable and disable functionality
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-dbgcap.c | 29 ++++++++++++++++++-----------
1 file changed, 18 insertions(+), 11 deletions(-)
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index 49ae546c4103..8cf0f0356bf1 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -628,18 +628,30 @@ static void xhci_dbc_mem_cleanup(struct xhci_dbc *dbc)
dbc->ring_evt = NULL;
}
+static int xhci_dbc_enable_dce(struct xhci_dbc *dbc, bool enable)
+{
+ u32 done_state = 0;
+ u32 ctrl = 0;
+
+ if (enable) {
+ ctrl = readl(&dbc->regs->control);
+ ctrl |= DBC_CTRL_DBC_ENABLE | DBC_CTRL_PORT_ENABLE;
+ done_state = DBC_CTRL_DBC_ENABLE;
+ }
+
+ writel(ctrl, &dbc->regs->control);
+ return xhci_handshake(&dbc->regs->control, DBC_CTRL_DBC_ENABLE,
+ done_state, 1000);
+}
+
static int xhci_do_dbc_start(struct xhci_dbc *dbc)
{
int ret;
- u32 ctrl;
if (dbc->state != DS_DISABLED)
return -EINVAL;
- writel(0, &dbc->regs->control);
- ret = xhci_handshake(&dbc->regs->control,
- DBC_CTRL_DBC_ENABLE,
- 0, 1000);
+ ret = xhci_dbc_enable_dce(dbc, false);
if (ret)
return ret;
@@ -647,12 +659,7 @@ static int xhci_do_dbc_start(struct xhci_dbc *dbc)
if (ret)
return ret;
- ctrl = readl(&dbc->regs->control);
- writel(ctrl | DBC_CTRL_DBC_ENABLE | DBC_CTRL_PORT_ENABLE,
- &dbc->regs->control);
- ret = xhci_handshake(&dbc->regs->control,
- DBC_CTRL_DBC_ENABLE,
- DBC_CTRL_DBC_ENABLE, 1000);
+ ret = xhci_dbc_enable_dce(dbc, true);
if (ret)
return ret;
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 10/15] xhci: dbc: add timestamps to DbC state changes in a new helper.
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
` (8 preceding siblings ...)
2026-06-03 9:11 ` [PATCH 09/15] xhci: dbc: add helper to set and clear DbC DCE enable bit Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
2026-06-03 9:11 ` [PATCH 11/15] xhci: dbc: detect and recover hung DbC during enumeraton Mathias Nyman
` (4 subsequent siblings)
14 siblings, 0 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Mathias Nyman
The timestamp helps us track when a state changed the last time.
It allows us to detect if DbC is stuck in connected state for too long,
and can later be used to enable runtime suspend if there is no activity
for some time
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-dbgcap.c | 18 ++++++++++++------
drivers/usb/host/xhci-dbgcap.h | 1 +
2 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index 8cf0f0356bf1..34441ffa5e15 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -644,6 +644,12 @@ static int xhci_dbc_enable_dce(struct xhci_dbc *dbc, bool enable)
done_state, 1000);
}
+static void xhci_dbc_set_state(struct xhci_dbc *dbc, enum dbc_state new_state)
+{
+ dbc->state_timestamp = jiffies;
+ dbc->state = new_state;
+}
+
static int xhci_do_dbc_start(struct xhci_dbc *dbc)
{
int ret;
@@ -663,7 +669,7 @@ static int xhci_do_dbc_start(struct xhci_dbc *dbc)
if (ret)
return ret;
- dbc->state = DS_ENABLED;
+ xhci_dbc_set_state(dbc, DS_ENABLED);
return 0;
}
@@ -725,7 +731,7 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
spin_lock_irqsave(&dbc->lock, flags);
writel(0, &dbc->regs->control);
- dbc->state = DS_DISABLED;
+ xhci_dbc_set_state(dbc, DS_DISABLED);
spin_unlock_irqrestore(&dbc->lock, flags);
xhci_dbc_mem_cleanup(dbc);
@@ -896,7 +902,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
case DS_ENABLED:
portsc = readl(&dbc->regs->portsc);
if (portsc & DBC_PORTSC_CONN_STATUS) {
- dbc->state = DS_CONNECTED;
+ xhci_dbc_set_state(dbc, DS_CONNECTED);
dev_info(dbc->dev, "DbC connected\n");
}
@@ -904,7 +910,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
case DS_CONNECTED:
ctrl = readl(&dbc->regs->control);
if (ctrl & DBC_CTRL_DBC_RUN) {
- dbc->state = DS_CONFIGURED;
+ xhci_dbc_set_state(dbc, DS_CONFIGURED);
dev_info(dbc->dev, "DbC configured\n");
portsc = readl(&dbc->regs->portsc);
writel(portsc, &dbc->regs->portsc);
@@ -919,7 +925,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
if (!(portsc & DBC_PORTSC_PORT_ENABLED) &&
!(portsc & DBC_PORTSC_CONN_STATUS)) {
dev_info(dbc->dev, "DbC cable unplugged\n");
- dbc->state = DS_ENABLED;
+ xhci_dbc_set_state(dbc, DS_ENABLED);
xhci_dbc_flush_requests(dbc);
xhci_dbc_reinit_ep_rings(dbc);
return EVT_DISC;
@@ -929,7 +935,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
if (portsc & DBC_PORTSC_RESET_CHANGE) {
dev_info(dbc->dev, "DbC port reset\n");
writel(portsc, &dbc->regs->portsc);
- dbc->state = DS_ENABLED;
+ xhci_dbc_set_state(dbc, DS_ENABLED);
xhci_dbc_flush_requests(dbc);
xhci_dbc_reinit_ep_rings(dbc);
return EVT_DISC;
diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
index f4c0169a1b4d..b3efea43a6be 100644
--- a/drivers/usb/host/xhci-dbgcap.h
+++ b/drivers/usb/host/xhci-dbgcap.h
@@ -163,6 +163,7 @@ struct xhci_dbc {
struct delayed_work event_work;
unsigned int poll_interval; /* ms */
unsigned long xfer_timestamp;
+ unsigned long state_timestamp;
unsigned resume_required:1;
struct dbc_ep eps[2];
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 11/15] xhci: dbc: detect and recover hung DbC during enumeraton
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
` (9 preceding siblings ...)
2026-06-03 9:11 ` [PATCH 10/15] xhci: dbc: add timestamps to DbC state changes in a new helper Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
2026-06-03 9:11 ` [PATCH 12/15] xhci: Prevent queuing new commands if xhci is inaccessible Mathias Nyman
` (3 subsequent siblings)
14 siblings, 0 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Mathias Nyman
Add a timeout between the detection of the debug host connection and
the DbC Run transition to ‘1’. Toggle the DCE bit to re-enable DbC in
order to retry the debug device enumeration process if the DbC run
transition takes too long.
Set the timeout to 2 seconds
See xhci specification section 7.6.4.1 "Debug Capability Initialization"
Also detect cable disconnect during enable and connected state.
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-dbgcap.c | 28 +++++++++++++++++++++++++++-
drivers/usb/host/xhci-dbgcap.h | 1 +
2 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index 34441ffa5e15..b1cabf5582fa 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -901,23 +901,49 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
return EVT_ERR;
case DS_ENABLED:
portsc = readl(&dbc->regs->portsc);
+ ctrl = readl(&dbc->regs->control);
+
if (portsc & DBC_PORTSC_CONN_STATUS) {
xhci_dbc_set_state(dbc, DS_CONNECTED);
dev_info(dbc->dev, "DbC connected\n");
+ } else if (!(ctrl & DBC_CTRL_DBC_ENABLE)) {
+ dev_err(dbc->dev, "unexpected DbC disable, xHC reset?\n");
}
return EVT_DONE;
case DS_CONNECTED:
ctrl = readl(&dbc->regs->control);
+ portsc = readl(&dbc->regs->portsc);
if (ctrl & DBC_CTRL_DBC_RUN) {
xhci_dbc_set_state(dbc, DS_CONFIGURED);
dev_info(dbc->dev, "DbC configured\n");
- portsc = readl(&dbc->regs->portsc);
writel(portsc, &dbc->regs->portsc);
ret = EVT_GSER;
break;
}
+ /* Connection lost */
+ if (!(portsc & DBC_PORTSC_CONN_STATUS)) {
+ /* covers DCE == 0 as it also sets CONN_STATUS to 0 */
+ dev_warn(dbc->dev, "DbC connection lost mid enumeration\n");
+ xhci_dbc_set_state(dbc, DS_ENABLED);
+
+ return EVT_DONE;
+ }
+
+ /* Enumeration timeout */
+ if (time_is_before_jiffies(dbc->state_timestamp +
+ msecs_to_jiffies(DBC_ENUMERATION_TIMEOUT))) {
+ dev_err(dbc->dev, "DbC enumeration timeout, re-enabling DbC\n");
+ dev_dbg(dbc->dev, "dcctrl %x, dcportsc %x\n", ctrl, portsc);
+
+ /* Toggle DCE to retry enumeration */
+ ret = xhci_dbc_enable_dce(dbc, false);
+ udelay(100);
+ ret = xhci_dbc_enable_dce(dbc, true);
+ xhci_dbc_set_state(dbc, DS_ENABLED);
+ }
+
return EVT_DONE;
case DS_CONFIGURED:
/* Handle cable unplug event: */
diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
index b3efea43a6be..df7aca8bfe99 100644
--- a/drivers/usb/host/xhci-dbgcap.h
+++ b/drivers/usb/host/xhci-dbgcap.h
@@ -113,6 +113,7 @@ struct dbc_ep {
#define DBC_POLL_INTERVAL_DEFAULT 64 /* milliseconds */
#define DBC_POLL_INTERVAL_MAX 5000 /* milliseconds */
#define DBC_XFER_INACTIVITY_TIMEOUT 10 /* milliseconds */
+#define DBC_ENUMERATION_TIMEOUT 2000 /* milliseconds */
/*
* Private structure for DbC hardware state:
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 12/15] xhci: Prevent queuing new commands if xhci is inaccessible
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
` (10 preceding siblings ...)
2026-06-03 9:11 ` [PATCH 11/15] xhci: dbc: detect and recover hung DbC during enumeraton Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
2026-06-03 9:11 ` [PATCH 13/15] usb: xhci: refactor DCBAA struct Mathias Nyman
` (2 subsequent siblings)
14 siblings, 0 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Mathias Nyman
Refuse to queue a new command on the command ring if xHC is marked
inaccessible with the HCD_FLAG_HW_ACCESSIBLE.
HCD_FLAG_HW_ACCESSIBLE is set and cleared in suspend and resume.
Also print a warning if xhci is being suspended with commands
still pending on the command ring.
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
drivers/usb/host/xhci-ring.c | 6 ++++++
drivers/usb/host/xhci.c | 4 ++++
2 files changed, 10 insertions(+)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 608b6f3ec9f6..83209db29962 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -4332,6 +4332,7 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
u32 field3, u32 field4, bool command_must_succeed)
{
int reserved_trbs = xhci->cmd_ring_reserved_trbs;
+ struct usb_hcd *hcd = xhci_to_hcd(xhci);
int ret;
if ((xhci->xhc_state & XHCI_STATE_DYING) ||
@@ -4341,6 +4342,11 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
return -ESHUTDOWN;
}
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
+ xhci_warn(xhci, "Can't queue command, xHC not accessible\n");
+ return -ESHUTDOWN;
+ }
+
if (!command_must_succeed)
reserved_trbs++;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 0bf0446b4c87..0646fedf2042 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -992,6 +992,10 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
/* step 1: stop endpoint */
/* skipped assuming that port suspend has done */
+ /* Check if command ring is empty */
+ if (!list_empty(&xhci->cmd_list))
+ xhci_warn(xhci, "Suspending and stopping xHC with pending command!\n");
+
/* step 2: clear Run/Stop bit */
command = readl(&xhci->op_regs->command);
command &= ~CMD_RUN;
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 13/15] usb: xhci: refactor DCBAA struct
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
` (11 preceding siblings ...)
2026-06-03 9:11 ` [PATCH 12/15] xhci: Prevent queuing new commands if xhci is inaccessible Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
2026-06-03 9:11 ` [PATCH 14/15] usb: xhci: allocate DCBAA based on host controller max slots Mathias Nyman
2026-06-03 9:11 ` [PATCH 15/15] usb: xhci: allocate internal DCBAA mirror dynamically Mathias Nyman
14 siblings, 0 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
Embed the 'xhci_device_context_array' structure directly within 'xhci_hcd'
instead of allocating it as a separate block. Only the array of device
context addresses is now allocated separately.
Since the device context addresses are no longer part of an array
structure, rename 'dev_context_ptrs' to 'ctx_array' for clearer access
semantics.
Also remove the redundant comment next to the 'ctx_array' allocation;
using dma_alloc_coherent() for 64-bit * N allocations guarantees both
physically contiguous and properly aligned for 64-byte boundaries.
The xHCI section (5.4.6) refers to DCBAAP instead of DCBAA (6.1).
This change does not modify the number of host controller slots but
simplifies memory management and prepares the driver for a variable number
of HC slots in the future.
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 | 40 ++++++++++++++++++------------------
drivers/usb/host/xhci-ring.c | 2 +-
drivers/usb/host/xhci.c | 6 +++---
drivers/usb/host/xhci.h | 13 +++++++-----
4 files changed, 32 insertions(+), 29 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 289461c06bf9..019dac47c5d6 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -877,8 +877,8 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev,
/* If device ctx array still points to _this_ device, clear it */
if (dev->out_ctx &&
- xhci->dcbaa->dev_context_ptrs[slot_id] == cpu_to_le64(dev->out_ctx->dma))
- xhci->dcbaa->dev_context_ptrs[slot_id] = 0;
+ xhci->dcbaa.ctx_array[slot_id] == cpu_to_le64(dev->out_ctx->dma))
+ xhci->dcbaa.ctx_array[slot_id] = 0;
trace_xhci_free_virt_device(dev);
@@ -1016,11 +1016,11 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
dev->udev = udev;
/* Point to output device context in dcbaa. */
- xhci->dcbaa->dev_context_ptrs[slot_id] = cpu_to_le64(dev->out_ctx->dma);
+ xhci->dcbaa.ctx_array[slot_id] = cpu_to_le64(dev->out_ctx->dma);
xhci_dbg(xhci, "Set slot id %d dcbaa entry %p to 0x%llx\n",
slot_id,
- &xhci->dcbaa->dev_context_ptrs[slot_id],
- le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id]));
+ &xhci->dcbaa.ctx_array[slot_id],
+ le64_to_cpu(xhci->dcbaa.ctx_array[slot_id]));
trace_xhci_alloc_virt_device(dev);
@@ -1671,7 +1671,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
if (!xhci->scratchpad->sp_buffers)
goto fail_sp3;
- xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma);
+ xhci->dcbaa.ctx_array[0] = cpu_to_le64(xhci->scratchpad->sp_dma);
for (i = 0; i < num_sp; i++) {
dma_addr_t dma;
void *buf = dma_alloc_coherent(dev, xhci->page_size, &dma,
@@ -1927,6 +1927,7 @@ void xhci_rh_bw_cleanup(struct xhci_hcd *xhci)
void xhci_mem_cleanup(struct xhci_hcd *xhci)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
+ struct xhci_device_context_array *dcbaa;
int i;
cancel_delayed_work_sync(&xhci->cmd_timer);
@@ -1972,10 +1973,12 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Freed medium stream array pool");
- if (xhci->dcbaa)
- dma_free_coherent(dev, sizeof(*xhci->dcbaa),
- xhci->dcbaa, xhci->dcbaa->dma);
- xhci->dcbaa = NULL;
+ dcbaa = &xhci->dcbaa;
+ if (dcbaa->ctx_array) {
+ dma_free_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), MAX_HC_SLOTS),
+ dcbaa->ctx_array, dcbaa->dma);
+ dcbaa->ctx_array = NULL;
+ }
scratchpad_free(xhci);
@@ -2403,23 +2406,20 @@ EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter);
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
{
- struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
- dma_addr_t dma;
+ struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
+ struct xhci_device_context_array *dcbaa = &xhci->dcbaa;
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__);
- /*
- * xHCI section 5.4.6 - Device Context array must be
- * "physically contiguous and 64-byte (cache line) aligned".
- */
- xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, flags);
- if (!xhci->dcbaa)
+ dcbaa->ctx_array =
+ dma_alloc_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), MAX_HC_SLOTS),
+ &dcbaa->dma, flags);
+ if (!dcbaa->ctx_array)
goto fail;
- xhci->dcbaa->dma = dma;
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Device context base array address = %pad (DMA), %p (virt)",
- &xhci->dcbaa->dma, xhci->dcbaa);
+ &dcbaa->dma, dcbaa->ctx_array);
/*
* Initialize the ring segment pool. The ring must be a contiguous
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 83209db29962..3c304372bad3 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1613,7 +1613,7 @@ static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id,
/* Delete default control endpoint resources */
xhci_free_device_endpoint_resources(xhci, virt_dev, true);
if (cmd_comp_code == COMP_SUCCESS) {
- xhci->dcbaa->dev_context_ptrs[slot_id] = 0;
+ xhci->dcbaa.ctx_array[slot_id] = 0;
xhci->devs[slot_id] = NULL;
}
}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 0646fedf2042..14d7f866f220 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -551,7 +551,7 @@ static void xhci_init(struct usb_hcd *hcd)
xhci_set_cmd_ring_deq(xhci);
/* Set Device Context Base Address Array pointer */
- xhci_write_64(xhci, xhci->dcbaa->dma, &xhci->op_regs->dcbaa_ptr);
+ xhci_write_64(xhci, xhci->dcbaa.dma, &xhci->op_regs->dcbaa_ptr);
/* Set Doorbell array pointer */
xhci_set_doorbell_ptr(xhci);
@@ -4457,9 +4457,9 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
"Slot ID %d dcbaa entry @%p = %#016llx",
udev->slot_id,
- &xhci->dcbaa->dev_context_ptrs[udev->slot_id],
+ &xhci->dcbaa.ctx_array[udev->slot_id],
(unsigned long long)
- le64_to_cpu(xhci->dcbaa->dev_context_ptrs[udev->slot_id]));
+ le64_to_cpu(xhci->dcbaa.ctx_array[udev->slot_id]));
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
"Output Context DMA address = %#08llx",
(unsigned long long)virt_dev->out_ctx->dma);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index e73c498449e8..9d57def08ea6 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -792,12 +792,15 @@ struct xhci_tt_bw_info {
/**
* struct xhci_device_context_array
- * @dev_context_ptr array of 64-bit DMA addresses for device contexts
+ * @ctx_array: Pointer to an array of addresses
+ * @dma: DMA address to @ctx_array
+ *
+ * Device Context Base Address Array (DCBAA) - Section 6.1.
+ * ctx_array[0]: Scratchpad Buffer Array Base Address
+ * ctx_array[1-MaxSlots]: Device Context Base Address
*/
struct xhci_device_context_array {
- /* 64-bit device addresses; we only write 32-bit addresses */
- __le64 dev_context_ptrs[MAX_HC_SLOTS];
- /* private xHCD pointers */
+ __le64 *ctx_array;
dma_addr_t dma;
};
/*
@@ -1531,7 +1534,7 @@ struct xhci_hcd {
/* optional reset controller */
struct reset_control *reset;
/* data structures */
- struct xhci_device_context_array *dcbaa;
+ struct xhci_device_context_array dcbaa;
struct xhci_interrupter **interrupters;
struct xhci_ring *cmd_ring;
unsigned int cmd_ring_state;
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 14/15] usb: xhci: allocate DCBAA based on host controller max slots
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
` (12 preceding siblings ...)
2026-06-03 9:11 ` [PATCH 13/15] usb: xhci: refactor DCBAA struct Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
2026-06-03 9:11 ` [PATCH 15/15] usb: xhci: allocate internal DCBAA mirror dynamically Mathias Nyman
14 siblings, 0 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
Allocate the Device Context Base Address Array (DCBAA) according to the
maximum number of device slots supported by the host controller, instead
of always allocating the absolute maximum of 255 entries.
The xHCI specification defines the DCBAA size as (MaxSlotsEnabled + 1)
entries. In the xhci driver there is currently no distinction between
MaxSlots and MaxSlotsEnabled, as all available slots are enabled during
initialization. As a result, 'max_slots' effectively represents both
values.
This change allows the xHCI driver to respect custom slot limits, reduces
unnecessary memory usage, and removes the obsolete "TODO" comment.
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 | 6 +++---
drivers/usb/host/xhci-ring.c | 4 ++--
drivers/usb/host/xhci.h | 7 ++-----
3 files changed, 7 insertions(+), 10 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 019dac47c5d6..939151e5440e 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1975,7 +1975,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
dcbaa = &xhci->dcbaa;
if (dcbaa->ctx_array) {
- dma_free_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), MAX_HC_SLOTS),
+ dma_free_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), xhci->max_slots + 1),
dcbaa->ctx_array, dcbaa->dma);
dcbaa->ctx_array = NULL;
}
@@ -2411,8 +2411,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__);
- dcbaa->ctx_array =
- dma_alloc_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), MAX_HC_SLOTS),
+ xhci->dcbaa.ctx_array =
+ dma_alloc_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), xhci->max_slots + 1),
&dcbaa->dma, flags);
if (!dcbaa->ctx_array)
goto fail;
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 3c304372bad3..4f98d8269625 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -609,7 +609,7 @@ static struct xhci_virt_ep *xhci_get_virt_ep(struct xhci_hcd *xhci,
unsigned int slot_id,
unsigned int ep_index)
{
- if (slot_id == 0 || slot_id >= MAX_HC_SLOTS) {
+ if (slot_id == 0 || slot_id > xhci->max_slots) {
xhci_warn(xhci, "Invalid slot_id %u\n", slot_id);
return NULL;
}
@@ -1804,7 +1804,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
struct xhci_command *cmd;
u32 cmd_type;
- if (slot_id >= MAX_HC_SLOTS) {
+ if (slot_id > xhci->max_slots) {
xhci_warn(xhci, "Invalid slot_id %u\n", slot_id);
return;
}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 9d57def08ea6..cb80acac97d4 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -792,7 +792,8 @@ struct xhci_tt_bw_info {
/**
* struct xhci_device_context_array
- * @ctx_array: Pointer to an array of addresses
+ * @ctx_array: Pointer to an array of addresses. The array size depends on Max
+ * Slots read from HCSPARAMS1.
* @dma: DMA address to @ctx_array
*
* Device Context Base Address Array (DCBAA) - Section 6.1.
@@ -803,10 +804,6 @@ struct xhci_device_context_array {
__le64 *ctx_array;
dma_addr_t dma;
};
-/*
- * 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.
- */
struct xhci_transfer_event {
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 15/15] usb: xhci: allocate internal DCBAA mirror dynamically
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
` (13 preceding siblings ...)
2026-06-03 9:11 ` [PATCH 14/15] usb: xhci: allocate DCBAA based on host controller max slots Mathias Nyman
@ 2026-06-03 9:11 ` Mathias Nyman
14 siblings, 0 replies; 20+ messages in thread
From: Mathias Nyman @ 2026-06-03 9:11 UTC (permalink / raw)
To: gregkh; +Cc: linux-usb, Niklas Neronin, Mathias Nyman
From: Niklas Neronin <niklas.neronin@linux.intel.com>
Allocate the internal virtual device array dynamically based on the
maximum number of slots reported by the host controller. Previously,
the array was always allocated to the absolute maximum of 255 entries.
Repurpose the 'MAX_HC_SLOTS' macro to limit the number of enabled slots.
This mirrors how the maximum number of ports and interrupters are handled.
The allocation now uses kcalloc_node(), which zeroes the memory
automatically, making the explicit memset() call unnecessary.
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 | 14 ++++++++++++--
drivers/usb/host/xhci.c | 4 +---
drivers/usb/host/xhci.h | 9 ++++++---
3 files changed, 19 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 939151e5440e..a5e7f363922f 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1947,8 +1947,11 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring");
xhci_cleanup_command_queue(xhci);
- for (i = xhci->max_slots; i > 0; i--)
- xhci_free_virt_devices_depth_first(xhci, i);
+ if (xhci->devs) {
+ for (i = xhci->max_slots; i > 0; i--)
+ xhci_free_virt_devices_depth_first(xhci, i);
+ kfree(xhci->devs);
+ }
dma_pool_destroy(xhci->segment_pool);
xhci->segment_pool = NULL;
@@ -2005,6 +2008,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci->rh_bw = NULL;
xhci->port_caps = NULL;
xhci->interrupters = NULL;
+ xhci->devs = NULL;
xhci->usb2_rhub.bus_state.bus_suspended = 0;
xhci->usb3_rhub.bus_state.bus_suspended = 0;
@@ -2411,6 +2415,12 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__);
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating internal virtual device array");
+ xhci->devs = kcalloc_node(xhci->max_slots + 1, sizeof(*xhci->devs), flags,
+ dev_to_node(dev));
+ if (!xhci->devs)
+ goto fail;
+
xhci->dcbaa.ctx_array =
dma_alloc_coherent(dev, array_size(sizeof(*dcbaa->ctx_array), xhci->max_slots + 1),
&dcbaa->dma, flags);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 14d7f866f220..6922cc5496c1 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -5457,7 +5457,7 @@ 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_slots = min(HCS_MAX_SLOTS(hcs_params1), MAX_HC_SLOTS);
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)
@@ -5530,8 +5530,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
init_completion(&xhci->cmd_ring_stop_completion);
xhci_hcd_page_size(xhci);
- memset(xhci->devs, 0, MAX_HC_SLOTS * sizeof(*xhci->devs));
-
/* Allocate xHCI data structures */
retval = xhci_mem_init(xhci, GFP_KERNEL);
if (retval)
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index cb80acac97d4..d02046a573e4 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -33,8 +33,11 @@
/* xHCI PCI Configuration Registers */
#define XHCI_SBRN_OFFSET (0x60)
-/* Max number of USB devices for any host controller - limit in section 6.1 */
-#define MAX_HC_SLOTS 256
+/*
+ * Max number of Devices Slots. xHCI specification section 5.3.3
+ * Valid values are in the range of 1 to 255.
+ */
+#define MAX_HC_SLOTS 255
/*
* Max Number of Ports. xHCI specification section 5.3.3
* Valid values are in the range of 1 to 255.
@@ -1551,7 +1554,7 @@ struct xhci_hcd {
/* these are not thread safe so use mutex */
struct mutex mutex;
/* Internal mirror of the HW's dcbaa */
- struct xhci_virt_device *devs[MAX_HC_SLOTS];
+ struct xhci_virt_device **devs;
/* For keeping track of bandwidth domains per roothub. */
struct xhci_root_port_bw_info *rh_bw;
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH 08/15] xhci: dbc: serialize enabling and disabling dbc
2026-06-03 9:11 ` [PATCH 08/15] xhci: dbc: serialize enabling and disabling dbc Mathias Nyman
@ 2026-06-15 6:11 ` Borah, Chaitanya Kumar
2026-06-15 8:47 ` Mathias Nyman
0 siblings, 1 reply; 20+ messages in thread
From: Borah, Chaitanya Kumar @ 2026-06-15 6:11 UTC (permalink / raw)
To: Mathias Nyman, gregkh
Cc: linux-usb, intel-gfx@lists.freedesktop.org,
intel-xe@lists.freedesktop.org, ravitejax.veesam
Hello Mathias,
On 6/3/2026 2:41 PM, Mathias Nyman wrote:
> DbC can be enabled and disabled via sysfs, serialize those
> with a mutex to make sure everything is done in the correct
> order.
>
> remove xhci_do_dbc_stop() and integrate the register write and
> dbc->state setting into xhci_do_stop()
>
This patch seems to cause a regression in our CI. [1]
We could not revert the patch because of merge conflicts but resetting
to the parent commit seems to heal the issue.
Could you please check why the patch causes this regression and provide
a fix if necessary?
==
Chaitanya
[1]
https://intel-gfx-ci.01.org/tree/xe-linux-next/xe-linux-next-20260611/bat-lnl-2/boot0.txt
Bisect log:
git bisect start
# status: waiting for both good and bad commits
# good: [382f7d3b4c9740ac06199207796ed9b71582c7c5] Merge remote-tracking
branch 'regulator/for-7.2' into regulator-next
git bisect good 382f7d3b4c9740ac06199207796ed9b71582c7c5
# status: waiting for bad commit, 1 good commit known
# bad: [49e02880ec0a8c378e811bc9d85da188d7c6204c] Add linux-next
specific files for 20260609
git bisect bad 49e02880ec0a8c378e811bc9d85da188d7c6204c
# skip: [821165b2f4b76644407d4e5284d9c41363e7caec] Merge branch 'master'
of
https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
git bisect skip 821165b2f4b76644407d4e5284d9c41363e7caec
# good: [5c65b96b549ea2dcfde497436bf9e048deb87758] Bluetooth: hci_sync:
reject oversized Broadcast Announcement prepend
git bisect good 5c65b96b549ea2dcfde497436bf9e048deb87758
# good: [c17dfc8bb0b61ed36b984449eae8f246878052eb] m68k: coldfire:
select legacy gpiolib interface for mcfqspi
git bisect good c17dfc8bb0b61ed36b984449eae8f246878052eb
# good: [28db4b2625174a47b3f6b79d996b0804aaae8ab2] wifi: iwlwifi: mld:
fix NAN max channel switch time unit
git bisect good 28db4b2625174a47b3f6b79d996b0804aaae8ab2
# skip: [4fdfc0229a10464e8b2a4a56d9aeec8a4057a424] Merge branch
'spi-nor/next' of
https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git
git bisect skip 4fdfc0229a10464e8b2a4a56d9aeec8a4057a424
# good: [ce0e10c130c30ad480d83b32d8508183113d585e] staging: rtl8723bs:
move constant to right side of test in comparison
git bisect good ce0e10c130c30ad480d83b32d8508183113d585e
# skip: [9755c6cf85febe7756c6dddb6405b16bb393783c] Merge branch
'nand/next' of https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git
git bisect skip 9755c6cf85febe7756c6dddb6405b16bb393783c
# good: [fe017012c5825f22bd856249e4f3cf57e31027b8] Merge tag
'drm-intel-gt-next-2026-05-29' of
https://gitlab.freedesktop.org/drm/i915/kernel into drm-next
git bisect good fe017012c5825f22bd856249e4f3cf57e31027b8
# skip: [ff34647c5266a05cbd0a4807cb0333b3a3856194] Merge branch 'main'
of https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git
git bisect skip ff34647c5266a05cbd0a4807cb0333b3a3856194
# good: [1389ab9bf9f627d4daed86f492091b00f110aa86] PCI: loongson: Do not
ignore downstream devices on external bridges
git bisect good 1389ab9bf9f627d4daed86f492091b00f110aa86
# good: [fb1758e74b8061aacfbce7bbb7a7cc650537e167] crypto: ccp - Do not
initialize SNP for SEV ioctls
git bisect good fb1758e74b8061aacfbce7bbb7a7cc650537e167
# good: [79e33ddc62c03cce6c29f0792454e1d618228acf] Merge patch series
"cleanup block-style layouts exports"
git bisect good 79e33ddc62c03cce6c29f0792454e1d618228acf
# good: [ddf9d68a368d8cae5510923a99bd2d9a7275a091] media: i2c: adv7604:
Add range checks for chip info
git bisect good ddf9d68a368d8cae5510923a99bd2d9a7275a091
# good: [764f409b840ab400253215e765a72b903feb6afd] phy: qcom-qmp: Use
explicit QSERDES COM v2 register definitions
git bisect good 764f409b840ab400253215e765a72b903feb6afd
# good: [b5b481063f5a68ef8361ba025bf655fc451f5d93] arm64: dts: amlogic:
meson-gxl-s905d-phicomm-n1: add bluetooth node
git bisect good b5b481063f5a68ef8361ba025bf655fc451f5d93
# good: [057bb70f531c3e33c18c067716f39f8413b252aa] RDMA/uverbs: Push out
CQ buffer umem processing into a helper
git bisect good 057bb70f531c3e33c18c067716f39f8413b252aa
# good: [6e302107d9c9fcfee48db57b175e82de39301645] can: virtio: Fix
comment in UAPI header
git bisect good 6e302107d9c9fcfee48db57b175e82de39301645
# good: [95937cbb6d51e0c6f8ac937bd043fb3b442391ec] arm64: dts: mediatek:
mt8192-asurada: Fix SPI-NOR flash compatible
git bisect good 95937cbb6d51e0c6f8ac937bd043fb3b442391ec
# good: [52ae64602394bc9a8e7b67f5e4e70c56e31699a7] phy:
phy-can-transceiver: Drop unused include
git bisect good 52ae64602394bc9a8e7b67f5e4e70c56e31699a7
# good: [d8eb890079d19e4b5ac8073038c4fea006992e00] media: rockchip: rga:
use card type to specify rga type
git bisect good d8eb890079d19e4b5ac8073038c4fea006992e00
# skip: [0f8575ca3a10c62d352be357c54d949b9d181fcd] Merge branch
'for-next' of
https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git
git bisect skip 0f8575ca3a10c62d352be357c54d949b9d181fcd
# good: [fc13a4b9b9c8cb0b8e5ba54b21712d00f810496c] firmware: samsung:
acpm: Add devm_acpm_get_by_phandle helper
git bisect good fc13a4b9b9c8cb0b8e5ba54b21712d00f810496c
# good: [91f5d698478f3d07230cf9ca4dfaf67e0316a53d] cpufreq: governor:
Fix data races on per-CPU idle/nice baselines
git bisect good 91f5d698478f3d07230cf9ca4dfaf67e0316a53d
# good: [106ce4a01c179b8de664767cf5c7367210b66dba] dt-bindings: net:
starfive,jh7110-dwmac: Remove jh8100
git bisect good 106ce4a01c179b8de664767cf5c7367210b66dba
# good: [a1b94131042930114d0257c44362ee16bca18f4a] dt-bindings:
firmware: qcom,scm: Document SCM for Nord SoC
git bisect good a1b94131042930114d0257c44362ee16bca18f4a
# good: [1b203f7677100fa761f87641f35e72fe4e31a429] Merge branch
'net-mdio-realtek-rtl9300-refactor-initialization-and-port-lookup'
git bisect good 1b203f7677100fa761f87641f35e72fe4e31a429
# skip: [534ed190a0f1997553d0ee91403b361134f23f9d] Merge branch
'for-next' of
https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git
git bisect skip 534ed190a0f1997553d0ee91403b361134f23f9d
# good: [b2362b824f977a0a08f8d16c36510cabd6f58ce8] iio: light: vcnl4000:
drop enum id table in favor of chip structs
git bisect good b2362b824f977a0a08f8d16c36510cabd6f58ce8
# good: [9893f32976b5dd8ca6ed43a97794531d8d49981e] staging: rtl8723bs:
fix unbalanced braces in 3 files
git bisect good 9893f32976b5dd8ca6ed43a97794531d8d49981e
# good: [deb02080ca5d3f015cf71e56067a39ef2f141998] drm/amdgpu/userq:
remove amdgpu_userq_create/destroy_object wrapper
git bisect good deb02080ca5d3f015cf71e56067a39ef2f141998
# good: [4503ca234c1c5995609f77ce41e66d0f8e08cbfd] arm64: dts:
imx91-11x11-evk: add reset gpios for ethernet PHYs
git bisect good 4503ca234c1c5995609f77ce41e66d0f8e08cbfd
# good: [650fd10092529ca2dea85cb39b35a995808fd6dd] Bluetooth: l2cap: fix
MPS check in l2cap_ecred_reconf_req
git bisect good 650fd10092529ca2dea85cb39b35a995808fd6dd
# good: [a4c6d5d146157228bc03429cf7926d8a14072c84] gpu: nova-core:
enable GA100
git bisect good a4c6d5d146157228bc03429cf7926d8a14072c84
# good: [859e49597088b445173ed45cf4c4a44aff5e3f5a] perf callchain: Don't
pass evsel and sample
git bisect good 859e49597088b445173ed45cf4c4a44aff5e3f5a
# good: [8bdb0b3830eaf588fcd7c76c1893d05d871600d2] usb: typec:
intel_pmc_mux: combine kzalloc + kcalloc
git bisect good 8bdb0b3830eaf588fcd7c76c1893d05d871600d2
# good: [3b0ed04bc852887a9164e1bbf521652e8ef3eb92] fbdev: s3fb: fix
potential memory leak in s3_pci_probe()
git bisect good 3b0ed04bc852887a9164e1bbf521652e8ef3eb92
# good: [d8a4cef90b1a4ae9196a5bfba683eb9a0c75acdc] clk: spacemit: k3:
Switch to pll2_d6 as parent for PCIe clock
git bisect good d8a4cef90b1a4ae9196a5bfba683eb9a0c75acdc
# good: [b008ad40670b67e1cf6cfaa0692d830fc70dd94a] arm64: dts: renesas:
salvator-common: Rename clk-multiplier to clock-controller
git bisect good b008ad40670b67e1cf6cfaa0692d830fc70dd94a
# good: [206bb143689e493a7ebb0f2259e462c35125a89a] gpu: nova-core:
Blackwell: compute PMU-reserved framebuffer size
git bisect good 206bb143689e493a7ebb0f2259e462c35125a89a
# bad: [c029eea1cd96fa3653b4461c0ca7d5a360ead289] Merge branch
'tty-next' of https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
git bisect bad c029eea1cd96fa3653b4461c0ca7d5a360ead289
# skip: [611be4400f3b3a82f864749ead45c32e16d34833] Merge branch 'next'
of https://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm.git
git bisect skip 611be4400f3b3a82f864749ead45c32e16d34833
# good: [09d555136e011b7a083f1f7b40b86b6894e94a3f] Bluetooth: btrtl: fix
RTL8761B/BU broken LE extended scan
git bisect good 09d555136e011b7a083f1f7b40b86b6894e94a3f
# good: [48b68337bf6523f16b2afbb0d3e059eb211e3c85] firewire: core:
append _auto suffix for non-once iso resource operations
git bisect good 48b68337bf6523f16b2afbb0d3e059eb211e3c85
# good: [552ad802cad94bd759f85c170d07cc53f7d5deb9] Merge branch
'bpf-extend-btf-uapi-vlen-kinds-to-use-unused-bits'
git bisect good 552ad802cad94bd759f85c170d07cc53f7d5deb9
# skip: [956fef287b67de54fd2c863f1452e7e64fbe2fa3] Merge branch
'devfreq-next' of
https://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
git bisect skip 956fef287b67de54fd2c863f1452e7e64fbe2fa3
# good: [e6eff926482d0f6ebe038a15348158682f5711a0] buffer: Remove
mark_buffer_async_write()
git bisect good e6eff926482d0f6ebe038a15348158682f5711a0
# good: [b7b0f9bb1acc4e8f7becbd39aaa1db1c0539da87] ASoC: cs35l56:
Replace open-coded SoundWire regmap with generic regmap-sdw
git bisect good b7b0f9bb1acc4e8f7becbd39aaa1db1c0539da87
# skip: [7cceaa0da03cbd23e4831511a5e813ca7ead8bce] Merge branch
'linux-next' of
https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git
git bisect skip 7cceaa0da03cbd23e4831511a5e813ca7ead8bce
# good: [19f5e1ab965ab995794ea43866ae78fb02b381b0] Merge branch
'for-linux-next' of https://gitlab.freedesktop.org/drm/i915/kernel.git
git bisect good 19f5e1ab965ab995794ea43866ae78fb02b381b0
# good: [957d69dbe13b4c5452374f58859a179e9297ae32] Merge branch
'for-next' of
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
git bisect good 957d69dbe13b4c5452374f58859a179e9297ae32
# good: [74edfb4e12192b69c463bd9c3761ef753cee27d2] Merge branch 'next'
of git://git.kernel.org/pub/scm/virt/kvm/kvm.git
git bisect good 74edfb4e12192b69c463bd9c3761ef753cee27d2
# good: [be8c98cace3802ed8cb5f5a6fce0587db6349aea] Merge branch
'for-next' of
https://git.kernel.org/pub/scm/linux/kernel/git/tj/sched_ext.git
git bisect good be8c98cace3802ed8cb5f5a6fce0587db6349aea
# good: [927ff9df74b956bdccc8e9ac94ef483251cb484b] Merge branch
'driver-core-next' of
https://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git
git bisect good 927ff9df74b956bdccc8e9ac94ef483251cb484b
# bad: [bc35c68502637e8be9f03033a52b9cf086d7cc36] Merge branch
'usb-next' of https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git
git bisect bad bc35c68502637e8be9f03033a52b9cf086d7cc36
# good: [7fd6fb1fe40b992b2c39c3f586624ad93ee66c22] dt-bindings: usb:
ci-hdrc-usb2: allow up to 3 clocks for qcom,ci-hdrc
git bisect good 7fd6fb1fe40b992b2c39c3f586624ad93ee66c22
# good: [22d91cef94b5b86cff0d68ebfce7741740672704] usb: cdnsp: Add
support for device-only configuration
git bisect good 22d91cef94b5b86cff0d68ebfce7741740672704
# bad: [520058b73ba336380ecf7ea412263de9a7573df8] xhci: dbc: serialize
enabling and disabling dbc
git bisect bad 520058b73ba336380ecf7ea412263de9a7573df8
# good: [d20405dcdfb616cbae5c3e17e790a969ea03469d] usb: xhci: Remove
skip_isoc_td()
git bisect good d20405dcdfb616cbae5c3e17e790a969ea03469d
# good: [e765ab012f73717238c95ab9c34bfc3c767fa48c] usb: xhci: Improve
Soft Retries after short transfers
git bisect good e765ab012f73717238c95ab9c34bfc3c767fa48c
# good: [dc2ff8ba9b9093eb0564407e46e71520b054fc85] xhci: dbc: Fix sysfs
ABI Documentation for xhci dbc states
git bisect good dc2ff8ba9b9093eb0564407e46e71520b054fc85
# first bad commit: [520058b73ba336380ecf7ea412263de9a7573df8] xhci:
dbc: serialize enabling and disabling dbc
> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
> ---
> drivers/usb/host/xhci-dbgcap.c | 66 ++++++++++++++++++++--------------
> drivers/usb/host/xhci-dbgcap.h | 1 +
> 2 files changed, 40 insertions(+), 27 deletions(-)
>
> diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
> index 6a9f73fecb73..49ae546c4103 100644
> --- a/drivers/usb/host/xhci-dbgcap.c
> +++ b/drivers/usb/host/xhci-dbgcap.c
> @@ -661,17 +661,6 @@ static int xhci_do_dbc_start(struct xhci_dbc *dbc)
> return 0;
> }
>
> -static int xhci_do_dbc_stop(struct xhci_dbc *dbc)
> -{
> - if (dbc->state == DS_DISABLED)
> - return -EINVAL;
> -
> - writel(0, &dbc->regs->control);
> - dbc->state = DS_DISABLED;
> -
> - return 0;
> -}
> -
> static int xhci_dbc_start(struct xhci_dbc *dbc)
> {
> int ret;
> @@ -683,29 +672,37 @@ static int xhci_dbc_start(struct xhci_dbc *dbc)
>
> spin_lock_irqsave(&dbc->lock, flags);
> ret = xhci_do_dbc_start(dbc);
> + if (ret)
> + goto err_unlock;
> +
> spin_unlock_irqrestore(&dbc->lock, flags);
>
> - if (ret) {
> - pm_runtime_put(dbc->dev); /* note this was self.controller */
> - return ret;
> - }
> + mod_delayed_work(system_percpu_wq, &dbc->event_work,
> + msecs_to_jiffies(dbc->poll_interval));
>
> - return mod_delayed_work(system_percpu_wq, &dbc->event_work,
> - msecs_to_jiffies(dbc->poll_interval));
> + return 0;
> +
> +err_unlock:
> +
> + spin_unlock_irqrestore(&dbc->lock, flags);
> + pm_runtime_put(dbc->dev); /* note this was self.controller */
> +
> + return ret;
> }
>
> static void xhci_dbc_stop(struct xhci_dbc *dbc)
> {
> - int ret;
> unsigned long flags;
>
> WARN_ON(!dbc);
>
> + spin_lock(&dbc->lock);
> +
> switch (dbc->state) {
> case DS_DISABLED:
> + spin_unlock(&dbc->lock);
> return;
> case DS_CONFIGURED:
> - spin_lock(&dbc->lock);
> xhci_dbc_flush_requests(dbc);
> spin_unlock(&dbc->lock);
>
> @@ -713,19 +710,20 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
> dbc->driver->disconnect(dbc);
> break;
> default:
> + spin_unlock(&dbc->lock);
> break;
> }
>
> cancel_delayed_work_sync(&dbc->event_work);
>
> spin_lock_irqsave(&dbc->lock, flags);
> - ret = xhci_do_dbc_stop(dbc);
> + writel(0, &dbc->regs->control);
> + dbc->state = DS_DISABLED;
> spin_unlock_irqrestore(&dbc->lock, flags);
> - if (ret)
> - return;
>
> xhci_dbc_mem_cleanup(dbc);
> - pm_runtime_put_sync(dbc->dev); /* note, was self.controller */
> +
> + pm_runtime_put(dbc->dev); /* note, was self.controller */
> }
>
> static void
> @@ -1072,12 +1070,17 @@ static ssize_t dbc_store(struct device *dev,
> xhci = hcd_to_xhci(dev_get_drvdata(dev));
> dbc = xhci->dbc;
>
> - if (sysfs_streq(buf, "enable"))
> + if (sysfs_streq(buf, "enable")) {
> + mutex_lock(&dbc->enable_mutex);
> xhci_dbc_start(dbc);
> - else if (sysfs_streq(buf, "disable"))
> + mutex_unlock(&dbc->enable_mutex);
> + } else if (sysfs_streq(buf, "disable")) {
> + mutex_lock(&dbc->enable_mutex);
> xhci_dbc_stop(dbc);
> - else
> + mutex_unlock(&dbc->enable_mutex);
> + } else {
> return -EINVAL;
> + }
>
> return count;
> }
> @@ -1443,6 +1446,7 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *
>
> INIT_DELAYED_WORK(&dbc->event_work, xhci_dbc_handle_events);
> spin_lock_init(&dbc->lock);
> + mutex_init(&dbc->enable_mutex);
>
> ret = sysfs_create_groups(&dev->kobj, dbc_dev_groups);
> if (ret)
> @@ -1460,8 +1464,9 @@ void xhci_dbc_remove(struct xhci_dbc *dbc)
> if (!dbc)
> return;
> /* stop hw, stop wq and call dbc->ops->stop() */
> + mutex_lock(&dbc->enable_mutex);
> xhci_dbc_stop(dbc);
> -
> + mutex_unlock(&dbc->enable_mutex);
> /* remove sysfs files */
> sysfs_remove_groups(&dbc->dev->kobj, dbc_dev_groups);
>
> @@ -1514,6 +1519,8 @@ int xhci_dbc_suspend(struct xhci_hcd *xhci)
> if (!dbc)
> return 0;
>
> + mutex_lock(&dbc->enable_mutex);
> +
> switch (dbc->state) {
> case DS_ENABLED:
> case DS_CONNECTED:
> @@ -1525,6 +1532,7 @@ int xhci_dbc_suspend(struct xhci_hcd *xhci)
> }
>
> xhci_dbc_stop(dbc);
> + mutex_unlock(&dbc->enable_mutex);
>
> return 0;
> }
> @@ -1537,11 +1545,15 @@ int xhci_dbc_resume(struct xhci_hcd *xhci)
> if (!dbc)
> return 0;
>
> + mutex_lock(&dbc->enable_mutex);
> +
> if (dbc->resume_required) {
> dbc->resume_required = 0;
> xhci_dbc_start(dbc);
> }
>
> + mutex_unlock(&dbc->enable_mutex);
> +
> return ret;
> }
> #endif /* CONFIG_PM */
> diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
> index 20ae4e7617f2..f4c0169a1b4d 100644
> --- a/drivers/usb/host/xhci-dbgcap.h
> +++ b/drivers/usb/host/xhci-dbgcap.h
> @@ -140,6 +140,7 @@ struct dbc_driver {
>
> struct xhci_dbc {
> spinlock_t lock; /* device access */
> + struct mutex enable_mutex;
> struct device *dev;
> struct xhci_hcd *xhci;
> struct dbc_regs __iomem *regs;
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 08/15] xhci: dbc: serialize enabling and disabling dbc
2026-06-15 6:11 ` Borah, Chaitanya Kumar
@ 2026-06-15 8:47 ` Mathias Nyman
2026-06-15 8:55 ` [RFT PATCH] xhci: dbc: support runtime suspend while DbC is in enabled state Mathias Nyman
0 siblings, 1 reply; 20+ messages in thread
From: Mathias Nyman @ 2026-06-15 8:47 UTC (permalink / raw)
To: Borah, Chaitanya Kumar, gregkh
Cc: linux-usb, intel-gfx@lists.freedesktop.org,
intel-xe@lists.freedesktop.org, ravitejax.veesam
On 6/15/26 09:11, Borah, Chaitanya Kumar wrote:
>
> Hello Mathias,
>
> On 6/3/2026 2:41 PM, Mathias Nyman wrote:
>> DbC can be enabled and disabled via sysfs, serialize those
>> with a mutex to make sure everything is done in the correct
>> order.
>>
>> remove xhci_do_dbc_stop() and integrate the register write and
>> dbc->state setting into xhci_do_stop()
>>
>
> This patch seems to cause a regression in our CI. [1]
>
> We could not revert the patch because of merge conflicts but resetting to the parent commit seems to heal the issue.
>
> Could you please check why the patch causes this regression and provide
> a fix if necessary?
Ah, I see, deadlock when enabling DbC via sysfs on runtime suspended xhci
Appears this is an issue mid series in intel-next,
The last patch that enabled runtime pm for DbC should also fix this issue.
The DbC runtime pm support patch was dropped last minute from the upstream series.
I'll send it as a RFT, can you check it solve the issue in your CI
Details of deadlock issue while mid series:
dbc_store() { // enable dbc via syfs
mutex_lock(&dbc->enable_mutex);
xhci_dbc_start(dbc);
pm_runtime_get_sync(dbc->dev);
xhci_dbc_resume()
mutex_lock(&dbc->enable_mutex);
Thanks
Mathias
^ permalink raw reply [flat|nested] 20+ messages in thread
* [RFT PATCH] xhci: dbc: support runtime suspend while DbC is in enabled state
2026-06-15 8:47 ` Mathias Nyman
@ 2026-06-15 8:55 ` Mathias Nyman
2026-06-15 12:14 ` Borah, Chaitanya Kumar
0 siblings, 1 reply; 20+ messages in thread
From: Mathias Nyman @ 2026-06-15 8:55 UTC (permalink / raw)
To: chaitanya.kumar.borah, gregkh
Cc: mathias.nyman, intel-gfx, intel-xe, linux-usb, ravitejax.veesam
Allow xHC to runtime suspend if DbC is in 'enabled' state for over
15 seconds without a connect.
Idea is that every time we go to 'enabled' state we make sure DbC runtime
pm usage is '1' and save a timestamp. if the event loop still finds DbC in
enabled state 15 seconds later then it decrease DbC runtime pm usage by
calling pm_runtime_put().
Enabled state is reached either when DbC is enabled by userspace or a
connected/configured DbC is disconnected.
When a connect is detected we make sure DbC usage count is 1.
If DbC has been in 'enabled' state for 15 seconds and DbC usage is
decreased to 0 by pm_runtime_put, then the whole xHC controller may
runtime suspends to PCI D3 state if no other devices are using it
DbC sysfs file will show 'suspended' when xHC is suspended and will wake up
and enable DbC at cable connect, or when user writes 'enable' to the file.
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
.../testing/sysfs-bus-pci-drivers-xhci_hcd | 2 +-
drivers/usb/host/xhci-dbgcap.c | 60 ++++++++++++++++++-
drivers/usb/host/xhci-dbgcap.h | 3 +
3 files changed, 62 insertions(+), 3 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd b/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd
index 98a8376a83d2..991765d84201 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd
+++ b/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd
@@ -22,7 +22,7 @@ Description:
Reading this attribute gives the state of the DbC. It
can be one of the following states: disabled, enabled,
- initialized, connected or configured.
+ initialized, connected, configured or suspended.
What: /sys/bus/pci/drivers/xhci_hcd/.../dbc_idVendor
Date: March 2023
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index 8ff8ac300927..f228c46293ca 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -647,6 +647,31 @@ static int xhci_dbc_enable_dce(struct xhci_dbc *dbc, bool enable)
static void xhci_dbc_set_state(struct xhci_dbc *dbc, enum dbc_state new_state)
{
+ if (dbc->state == new_state)
+ return;
+
+ switch (new_state) {
+ case DS_ENABLED:
+ /*
+ * DbC pm usage is 1 here, both when moved from disconnect or
+ * configured states, or when setting initial DbC enable state.
+ * Just enable pending put
+ */
+ dev_dbg(dbc->dev, "DbC set pending_rpm_put = 1\n");
+ dbc->pending_rpm_put = 1;
+ break;
+ case DS_CONNECTED:
+ if (dbc->pending_rpm_put)
+ /* DbC pm usage still 1, just remove pending put */
+ dbc->pending_rpm_put = 0;
+ else
+ /* DbC pm usage was put to 0, call get */
+ pm_runtime_get(dbc->dev);
+ break;
+ default:
+ break;
+ }
+
dbc->state_timestamp = jiffies;
dbc->state = new_state;
}
@@ -682,7 +707,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc)
WARN_ON(!dbc);
- pm_runtime_get_sync(dbc->dev); /* note this was self.controller */
+ pm_runtime_get(dbc->dev);
spin_lock_irqsave(&dbc->lock, flags);
ret = xhci_do_dbc_start(dbc);
@@ -707,6 +732,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc)
static void xhci_dbc_stop(struct xhci_dbc *dbc)
{
unsigned long flags;
+ bool need_rpm_put = false;
WARN_ON(!dbc);
@@ -732,12 +758,20 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
spin_lock_irqsave(&dbc->lock, flags);
writel(0, &dbc->regs->control);
+
+ if (dbc->state == DS_CONNECTED || dbc->state == DS_CONFIGURED ||
+ dbc->pending_rpm_put)
+ need_rpm_put = true;
+
+ dbc->pending_rpm_put = 0;
+
xhci_dbc_set_state(dbc, DS_DISABLED);
spin_unlock_irqrestore(&dbc->lock, flags);
xhci_dbc_mem_cleanup(dbc);
- pm_runtime_put(dbc->dev); /* note, was self.controller */
+ if (need_rpm_put)
+ pm_runtime_put(dbc->dev);
}
static void
@@ -911,6 +945,12 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
dev_info(dbc->dev, "DbC connected\n");
} else if (!(ctrl & DBC_CTRL_DBC_ENABLE)) {
dev_err(dbc->dev, "unexpected DbC disable, xHC reset?\n");
+ } else if (dbc->pending_rpm_put &&
+ time_is_before_jiffies(dbc->state_timestamp +
+ msecs_to_jiffies(DBC_AUTOSUSPEND_DELAY))) {
+ dbc->pending_rpm_put = 0;
+ dev_dbg(dbc->dev, "DbC Enabled state for 15 seconds, allow rpm suspend\n");
+ pm_runtime_put(dbc->dev);
}
return EVT_DONE;
@@ -1099,6 +1139,9 @@ static ssize_t dbc_show(struct device *dev,
if (dbc->state >= ARRAY_SIZE(dbc_state_strings))
return sysfs_emit(buf, "unknown\n");
+ if (dbc->resume_required)
+ return sysfs_emit(buf, "suspended\n");
+
return sysfs_emit(buf, "%s\n", dbc_state_strings[dbc->state]);
}
@@ -1113,12 +1156,25 @@ static ssize_t dbc_store(struct device *dev,
dbc = xhci->dbc;
if (sysfs_streq(buf, "enable")) {
+ pm_runtime_get_sync(dbc->dev);
+
mutex_lock(&dbc->enable_mutex);
+ /*
+ * DbC may already be enabled here if xhci was suspended with
+ * dbc->resume_required set, and resumed by pm_runtime_get_sync()
+ * above. In this case we end up calling xhci_dbc_start() twice,
+ * second time returns an error but is harmless
+ */
xhci_dbc_start(dbc);
+
mutex_unlock(&dbc->enable_mutex);
+ pm_runtime_put(dbc->dev);
} else if (sysfs_streq(buf, "disable")) {
mutex_lock(&dbc->enable_mutex);
+
+ dbc->resume_required = 0;
xhci_dbc_stop(dbc);
+
mutex_unlock(&dbc->enable_mutex);
} else {
return -EINVAL;
diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
index df7aca8bfe99..5b18efb2c1ea 100644
--- a/drivers/usb/host/xhci-dbgcap.h
+++ b/drivers/usb/host/xhci-dbgcap.h
@@ -114,6 +114,8 @@ struct dbc_ep {
#define DBC_POLL_INTERVAL_MAX 5000 /* milliseconds */
#define DBC_XFER_INACTIVITY_TIMEOUT 10 /* milliseconds */
#define DBC_ENUMERATION_TIMEOUT 2000 /* milliseconds */
+#define DBC_AUTOSUSPEND_DELAY 15000 /* milliseconds */
+
/*
* Private structure for DbC hardware state:
*/
@@ -166,6 +168,7 @@ struct xhci_dbc {
unsigned long xfer_timestamp;
unsigned long state_timestamp;
unsigned resume_required:1;
+ unsigned pending_rpm_put:1;
struct dbc_ep eps[2];
const struct dbc_driver *driver;
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [RFT PATCH] xhci: dbc: support runtime suspend while DbC is in enabled state
2026-06-15 8:55 ` [RFT PATCH] xhci: dbc: support runtime suspend while DbC is in enabled state Mathias Nyman
@ 2026-06-15 12:14 ` Borah, Chaitanya Kumar
0 siblings, 0 replies; 20+ messages in thread
From: Borah, Chaitanya Kumar @ 2026-06-15 12:14 UTC (permalink / raw)
To: Mathias Nyman, gregkh; +Cc: intel-gfx, intel-xe, linux-usb, ravitejax.veesam
Hello Mathias,
On 6/15/2026 2:25 PM, Mathias Nyman wrote:
> Allow xHC to runtime suspend if DbC is in 'enabled' state for over
> 15 seconds without a connect.
>
> Idea is that every time we go to 'enabled' state we make sure DbC runtime
> pm usage is '1' and save a timestamp. if the event loop still finds DbC in
> enabled state 15 seconds later then it decrease DbC runtime pm usage by
> calling pm_runtime_put().
> Enabled state is reached either when DbC is enabled by userspace or a
> connected/configured DbC is disconnected.
>
> When a connect is detected we make sure DbC usage count is 1.
>
> If DbC has been in 'enabled' state for 15 seconds and DbC usage is
> decreased to 0 by pm_runtime_put, then the whole xHC controller may
> runtime suspends to PCI D3 state if no other devices are using it
>
> DbC sysfs file will show 'suspended' when xHC is suspended and will wake up
> and enable DbC at cable connect, or when user writes 'enable' to the file.
>
The patch helps. Thank you!
Anytime time line on getting it merged?
==
Chaitanya
> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
> ---
> .../testing/sysfs-bus-pci-drivers-xhci_hcd | 2 +-
> drivers/usb/host/xhci-dbgcap.c | 60 ++++++++++++++++++-
> drivers/usb/host/xhci-dbgcap.h | 3 +
> 3 files changed, 62 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd b/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd
> index 98a8376a83d2..991765d84201 100644
> --- a/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd
> +++ b/Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd
> @@ -22,7 +22,7 @@ Description:
>
> Reading this attribute gives the state of the DbC. It
> can be one of the following states: disabled, enabled,
> - initialized, connected or configured.
> + initialized, connected, configured or suspended.
>
> What: /sys/bus/pci/drivers/xhci_hcd/.../dbc_idVendor
> Date: March 2023
> diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
> index 8ff8ac300927..f228c46293ca 100644
> --- a/drivers/usb/host/xhci-dbgcap.c
> +++ b/drivers/usb/host/xhci-dbgcap.c
> @@ -647,6 +647,31 @@ static int xhci_dbc_enable_dce(struct xhci_dbc *dbc, bool enable)
>
> static void xhci_dbc_set_state(struct xhci_dbc *dbc, enum dbc_state new_state)
> {
> + if (dbc->state == new_state)
> + return;
> +
> + switch (new_state) {
> + case DS_ENABLED:
> + /*
> + * DbC pm usage is 1 here, both when moved from disconnect or
> + * configured states, or when setting initial DbC enable state.
> + * Just enable pending put
> + */
> + dev_dbg(dbc->dev, "DbC set pending_rpm_put = 1\n");
> + dbc->pending_rpm_put = 1;
> + break;
> + case DS_CONNECTED:
> + if (dbc->pending_rpm_put)
> + /* DbC pm usage still 1, just remove pending put */
> + dbc->pending_rpm_put = 0;
> + else
> + /* DbC pm usage was put to 0, call get */
> + pm_runtime_get(dbc->dev);
> + break;
> + default:
> + break;
> + }
> +
> dbc->state_timestamp = jiffies;
> dbc->state = new_state;
> }
> @@ -682,7 +707,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc)
>
> WARN_ON(!dbc);
>
> - pm_runtime_get_sync(dbc->dev); /* note this was self.controller */
> + pm_runtime_get(dbc->dev);
>
> spin_lock_irqsave(&dbc->lock, flags);
> ret = xhci_do_dbc_start(dbc);
> @@ -707,6 +732,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc)
> static void xhci_dbc_stop(struct xhci_dbc *dbc)
> {
> unsigned long flags;
> + bool need_rpm_put = false;
>
> WARN_ON(!dbc);
>
> @@ -732,12 +758,20 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc)
>
> spin_lock_irqsave(&dbc->lock, flags);
> writel(0, &dbc->regs->control);
> +
> + if (dbc->state == DS_CONNECTED || dbc->state == DS_CONFIGURED ||
> + dbc->pending_rpm_put)
> + need_rpm_put = true;
> +
> + dbc->pending_rpm_put = 0;
> +
> xhci_dbc_set_state(dbc, DS_DISABLED);
> spin_unlock_irqrestore(&dbc->lock, flags);
>
> xhci_dbc_mem_cleanup(dbc);
>
> - pm_runtime_put(dbc->dev); /* note, was self.controller */
> + if (need_rpm_put)
> + pm_runtime_put(dbc->dev);
> }
>
> static void
> @@ -911,6 +945,12 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
> dev_info(dbc->dev, "DbC connected\n");
> } else if (!(ctrl & DBC_CTRL_DBC_ENABLE)) {
> dev_err(dbc->dev, "unexpected DbC disable, xHC reset?\n");
> + } else if (dbc->pending_rpm_put &&
> + time_is_before_jiffies(dbc->state_timestamp +
> + msecs_to_jiffies(DBC_AUTOSUSPEND_DELAY))) {
> + dbc->pending_rpm_put = 0;
> + dev_dbg(dbc->dev, "DbC Enabled state for 15 seconds, allow rpm suspend\n");
> + pm_runtime_put(dbc->dev);
> }
>
> return EVT_DONE;
> @@ -1099,6 +1139,9 @@ static ssize_t dbc_show(struct device *dev,
> if (dbc->state >= ARRAY_SIZE(dbc_state_strings))
> return sysfs_emit(buf, "unknown\n");
>
> + if (dbc->resume_required)
> + return sysfs_emit(buf, "suspended\n");
> +
> return sysfs_emit(buf, "%s\n", dbc_state_strings[dbc->state]);
> }
>
> @@ -1113,12 +1156,25 @@ static ssize_t dbc_store(struct device *dev,
> dbc = xhci->dbc;
>
> if (sysfs_streq(buf, "enable")) {
> + pm_runtime_get_sync(dbc->dev);
> +
> mutex_lock(&dbc->enable_mutex);
> + /*
> + * DbC may already be enabled here if xhci was suspended with
> + * dbc->resume_required set, and resumed by pm_runtime_get_sync()
> + * above. In this case we end up calling xhci_dbc_start() twice,
> + * second time returns an error but is harmless
> + */
> xhci_dbc_start(dbc);
> +
> mutex_unlock(&dbc->enable_mutex);
> + pm_runtime_put(dbc->dev);
> } else if (sysfs_streq(buf, "disable")) {
> mutex_lock(&dbc->enable_mutex);
> +
> + dbc->resume_required = 0;
> xhci_dbc_stop(dbc);
> +
> mutex_unlock(&dbc->enable_mutex);
> } else {
> return -EINVAL;
> diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
> index df7aca8bfe99..5b18efb2c1ea 100644
> --- a/drivers/usb/host/xhci-dbgcap.h
> +++ b/drivers/usb/host/xhci-dbgcap.h
> @@ -114,6 +114,8 @@ struct dbc_ep {
> #define DBC_POLL_INTERVAL_MAX 5000 /* milliseconds */
> #define DBC_XFER_INACTIVITY_TIMEOUT 10 /* milliseconds */
> #define DBC_ENUMERATION_TIMEOUT 2000 /* milliseconds */
> +#define DBC_AUTOSUSPEND_DELAY 15000 /* milliseconds */
> +
> /*
> * Private structure for DbC hardware state:
> */
> @@ -166,6 +168,7 @@ struct xhci_dbc {
> unsigned long xfer_timestamp;
> unsigned long state_timestamp;
> unsigned resume_required:1;
> + unsigned pending_rpm_put:1;
> struct dbc_ep eps[2];
>
> const struct dbc_driver *driver;
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-06-15 12:15 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-03 9:11 [PATCH 00/15] xhci features for usb-next Mathias Nyman
2026-06-03 9:11 ` [PATCH 01/15] usb: xhci: fix typo in xhci_set_port_power() comment Mathias Nyman
2026-06-03 9:11 ` [PATCH 02/15] usb: xhci: remove legacy 'num_trbs_free' tracking Mathias Nyman
2026-06-03 9:11 ` [PATCH 03/15] usb: xhci: Simplify xhci_quiesce() Mathias Nyman
2026-06-03 9:11 ` [PATCH 04/15] usb: xhci: Remove skip_isoc_td() Mathias Nyman
2026-06-03 9:11 ` [PATCH 05/15] usb: xhci: Remove isochronous URB_SHORT_NOT_OK handling Mathias Nyman
2026-06-03 9:11 ` [PATCH 06/15] usb: xhci: Improve Soft Retries after short transfers Mathias Nyman
2026-06-03 9:11 ` [PATCH 07/15] xhci: dbc: Fix sysfs ABI Documentation for xhci dbc states Mathias Nyman
2026-06-03 9:11 ` [PATCH 08/15] xhci: dbc: serialize enabling and disabling dbc Mathias Nyman
2026-06-15 6:11 ` Borah, Chaitanya Kumar
2026-06-15 8:47 ` Mathias Nyman
2026-06-15 8:55 ` [RFT PATCH] xhci: dbc: support runtime suspend while DbC is in enabled state Mathias Nyman
2026-06-15 12:14 ` Borah, Chaitanya Kumar
2026-06-03 9:11 ` [PATCH 09/15] xhci: dbc: add helper to set and clear DbC DCE enable bit Mathias Nyman
2026-06-03 9:11 ` [PATCH 10/15] xhci: dbc: add timestamps to DbC state changes in a new helper Mathias Nyman
2026-06-03 9:11 ` [PATCH 11/15] xhci: dbc: detect and recover hung DbC during enumeraton Mathias Nyman
2026-06-03 9:11 ` [PATCH 12/15] xhci: Prevent queuing new commands if xhci is inaccessible Mathias Nyman
2026-06-03 9:11 ` [PATCH 13/15] usb: xhci: refactor DCBAA struct Mathias Nyman
2026-06-03 9:11 ` [PATCH 14/15] usb: xhci: allocate DCBAA based on host controller max slots Mathias Nyman
2026-06-03 9:11 ` [PATCH 15/15] usb: xhci: allocate internal DCBAA mirror dynamically Mathias Nyman
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox