* [PATCH v4 0/4] DWC3 link tunneling state reporting
@ 2026-09-29 15:54 Konrad Dybcio
2026-09-29 15:54 ` [PATCH v4 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value Konrad Dybcio
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Konrad Dybcio @ 2026-09-29 15:54 UTC (permalink / raw)
To: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman
Cc: linux-usb, linux-kernel, usb4-upstream, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter, Konrad Dybcio
Currently, retrieving the tunneled/native state of a running port is
only possible on Intel XHCI hosts. This series extends that to CIO-
capable DWC31 controllers (see [1]).
Note that this patchset only results in the correct udev->tunnel_mode
value being assigned. Creating the device links (which requires this
information) between the USB4 router and the USB3 controller does not
currently take place on DT-based platforms, but that will be amended
in a follow-up patchset.
Tested on a Qualcomm X1E80100 CRD board, with a (currently) out-of-tree
set of drivers.
[1] I'm not entirely sure about the DWC version range (particularly
wrt DWC32 and newer), so I limited it to a reasonable subset. I'll
happily amend that.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
Changes in v4:
- Rework the check to use .update_device (Mathias)
- Drop tags for the top 2 comments accordingly
- Link to v3: https://patch.msgid.link/20260901-topic-dwc3_tunneling_state-v3-0-22fdced8de55@oss.qualcomm.com
Changes in v3:
- Report USB_LINK_UNKNOWN instead of NATIVE if the DWC3 controller
seems not to support USB4
- Cache the capability of reporting tunneling mode instead of checking
it every single time
- Link to v2: https://patch.msgid.link/20260715-topic-dwc3_tunneling_state-v2-0-026487a08119@oss.qualcomm.com
Changes in v2:
- Add handling for XHCI v1.2's USB3 tunneling and PORTSC.TM for
platforms that advertise it (!!! UNTESTED !!!, I have no access to
HW that exposes this)
- Prefer the aforementioned cap over custom implementations
- Bail out of the DWC3-specific check if `usb4-host-interface` device
property is absent - hence this now depends on:
https://github.com/devicetree-org/dt-schema/pull/196
- Link to v1: https://patch.msgid.link/20260505-topic-dwc3_tunneling_state-v1-0-4aaa6c3c14cb@oss.qualcomm.com
To: Mathias Nyman <mathias.nyman@intel.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Cc: linux-usb@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
Konrad Dybcio (4):
usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value
usb: xhci: Honor PORTSC.TM if valid
usb: xhci: Use update_device overrides for tunnel reporting
usb: dwc3: Notify XHCI core of tunneled status
drivers/usb/dwc3/core.c | 18 ++++++++++++++++
drivers/usb/dwc3/core.h | 18 ++++++++++++++++
drivers/usb/dwc3/host.c | 14 +++++++++++++
drivers/usb/host/xhci-debugfs.c | 9 ++++++++
drivers/usb/host/xhci-debugfs.h | 2 ++
drivers/usb/host/xhci-ext-caps.h | 4 ++++
drivers/usb/host/xhci-hub.c | 44 ----------------------------------------
drivers/usb/host/xhci-pci.c | 38 ++++++++++++++++++++++++++++++++++
drivers/usb/host/xhci-plat.c | 18 ++++++++++++++++
drivers/usb/host/xhci-plat.h | 2 ++
drivers/usb/host/xhci-port.h | 3 ++-
drivers/usb/host/xhci.c | 27 ++++++++++++++++--------
drivers/usb/host/xhci.h | 9 ++++++--
13 files changed, 151 insertions(+), 55 deletions(-)
---
base-commit: f5f84daefcd92d7a630066635ecea1433ed5eac7
change-id: 20260504-topic-dwc3_tunneling_state-50588c0e1ae3
Best regards,
--
Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value
2026-09-29 15:54 [PATCH v4 0/4] DWC3 link tunneling state reporting Konrad Dybcio
@ 2026-09-29 15:54 ` Konrad Dybcio
2026-09-29 15:54 ` [PATCH v4 2/4] usb: xhci: Honor PORTSC.TM if valid Konrad Dybcio
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: Konrad Dybcio @ 2026-09-29 15:54 UTC (permalink / raw)
To: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman
Cc: linux-usb, linux-kernel, usb4-upstream, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
The USB3 Tunneling Support Capability (idx 18) features a single
register. Expose it over debugfs, if supported by the controller.
Reviewed-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/usb/host/xhci-debugfs.c | 9 +++++++++
drivers/usb/host/xhci-debugfs.h | 2 ++
drivers/usb/host/xhci-ext-caps.h | 1 +
3 files changed, 12 insertions(+)
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index 2aa01d99f23d..f4bb66a9845a 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -82,6 +82,10 @@ static const struct debugfs_reg32 xhci_extcap_dbc[] = {
dump_register(EXTCAP_DBC_DEVINFO2),
};
+static const struct debugfs_reg32 xhci_extcap_usb3_tunneling[] = {
+ dump_register(EXTCAP_USB3_TUNNELING),
+};
+
static struct dentry *xhci_debugfs_root;
static struct xhci_regset *xhci_debugfs_alloc_regset(struct xhci_hcd *xhci)
@@ -816,6 +820,11 @@ void xhci_debugfs_init(struct xhci_hcd *xhci)
ARRAY_SIZE(xhci_extcap_dbc),
"reg-ext-dbc");
+ xhci_debugfs_extcap_regset(xhci, XHCI_EXT_CAPS_USB3_TUNNELING,
+ xhci_extcap_usb3_tunneling,
+ ARRAY_SIZE(xhci_extcap_usb3_tunneling),
+ "reg-usb3-tunneling");
+
xhci_debugfs_create_ring_dir(xhci, &xhci->cmd_ring,
"command-ring",
xhci->debugfs_root);
diff --git a/drivers/usb/host/xhci-debugfs.h b/drivers/usb/host/xhci-debugfs.h
index 7c074b4be819..ca6b7f2da8e3 100644
--- a/drivers/usb/host/xhci-debugfs.h
+++ b/drivers/usb/host/xhci-debugfs.h
@@ -70,6 +70,8 @@
#define REG_EXTCAP_DBC_DEVINFO1 0x38
#define REG_EXTCAP_DBC_DEVINFO2 0x3c
+#define REG_EXTCAP_USB3_TUNNELING 0x00
+
#define dump_register(nm) \
{ \
.name = __stringify(nm), \
diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
index 67ecf7320c62..22e53a750e2a 100644
--- a/drivers/usb/host/xhci-ext-caps.h
+++ b/drivers/usb/host/xhci-ext-caps.h
@@ -40,6 +40,7 @@
#define XHCI_EXT_CAPS_ROUTE 5
/* IDs 6-9 reserved */
#define XHCI_EXT_CAPS_DEBUG 10
+#define XHCI_EXT_CAPS_USB3_TUNNELING 18
/* Vendor caps */
#define XHCI_EXT_CAPS_VENDOR_INTEL 192
#define XHCI_EXT_CAPS_INTEL_SPR_SHADOW 206
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 2/4] usb: xhci: Honor PORTSC.TM if valid
2026-09-29 15:54 [PATCH v4 0/4] DWC3 link tunneling state reporting Konrad Dybcio
2026-09-29 15:54 ` [PATCH v4 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value Konrad Dybcio
@ 2026-09-29 15:54 ` Konrad Dybcio
2026-09-29 15:54 ` [PATCH v4 3/4] usb: xhci: Use update_device overrides for tunnel reporting Konrad Dybcio
2026-09-29 15:54 ` [PATCH v4 4/4] usb: dwc3: Notify XHCI core of tunneled status Konrad Dybcio
3 siblings, 0 replies; 7+ messages in thread
From: Konrad Dybcio @ 2026-09-29 15:54 UTC (permalink / raw)
To: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman
Cc: linux-usb, linux-kernel, usb4-upstream, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Currently, the only way for the USB core to determine whether the link
is native or tunneled is via an Intel vendor-specific Extended
Capability.
The XHCI specification v1.2 and newer expose a common ext_cap,
indicating whether BIT(2) of PORTSC can be interpreted as the tunneling
status.
Make use of that and fall back to the existing Intel-specific path if
the ext_cap is absent.
Reviewed-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/usb/host/xhci-ext-caps.h | 3 +++
drivers/usb/host/xhci-hub.c | 11 ++++++++++-
drivers/usb/host/xhci-port.h | 3 ++-
drivers/usb/host/xhci.c | 8 ++++++++
drivers/usb/host/xhci.h | 1 +
5 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
index 22e53a750e2a..af7d624cd38e 100644
--- a/drivers/usb/host/xhci-ext-caps.h
+++ b/drivers/usb/host/xhci-ext-caps.h
@@ -66,6 +66,9 @@
#define XHCI_HLC (1 << 19)
#define XHCI_BLC (1 << 20)
+/* USB3 tunneling support capability - section 7.11 */
+#define XHCI_USB3_TUNNELING_SUPPORTED BIT(16)
+
/* Intel SPR shadow capability */
#define XHCI_INTEL_SPR_ESS_PORT_OFFSET 0x8ac4 /* SuperSpeed port control */
#define XHCI_INTEL_SPR_TUNEN BIT(4) /* Tunnel mode enabled */
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 470bafe1802b..80f07bd5081d 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -769,8 +769,17 @@ enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
struct usb_hcd *hcd;
void __iomem *base;
u32 offset;
+ u32 val;
- /* Don't try and probe this capability for non-Intel hosts */
+ if (xhci->portsc_tunnel_reporting) {
+ val = xhci_portsc_readl(port);
+ if (val & PORT_TM)
+ return USB_LINK_TUNNELED;
+
+ return USB_LINK_NATIVE;
+ }
+
+ /* Fall back to the legacy Intel-specific ext_cap */
hcd = xhci_to_hcd(xhci);
if (!dev_is_pci(hcd->self.controller) ||
to_pci_dev(hcd->self.controller)->vendor != PCI_VENDOR_ID_INTEL)
diff --git a/drivers/usb/host/xhci-port.h b/drivers/usb/host/xhci-port.h
index 889b5fb0fcd8..215c9cadbc34 100644
--- a/drivers/usb/host/xhci-port.h
+++ b/drivers/usb/host/xhci-port.h
@@ -5,7 +5,8 @@
#define PORT_CONNECT (1 << 0)
/* true: port enabled */
#define PORT_PE (1 << 1)
-/* bit 2 reserved and zeroed */
+/* true: port is tunneling traffic over USB4 */
+#define PORT_TM (1 << 2)
/* true: port has an over-current condition */
#define PORT_OC (1 << 3)
/* true: port reset signaling asserted */
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index a9e47e178c28..5fb78613a993 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -5433,9 +5433,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
* quirks
*/
struct device *dev = hcd->self.sysdev;
+ void __iomem *base;
int retval;
u32 hcs_params1;
u32 hc_capbase;
+ u32 tunnel_cap;
/* Accept arbitrarily long scatter-gather lists */
hcd->self.sg_tablesize = ~0;
@@ -5456,6 +5458,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
mutex_init(&xhci->mutex);
xhci->main_hcd = hcd;
xhci->cap_regs = hcd->regs;
+ base = &xhci->cap_regs->hc_capbase;
hc_capbase = readl(&xhci->cap_regs->hc_capbase);
if (hc_capbase == U32_MAX) {
xhci_warn(xhci, "Host controller not accessible, removed?\n");
@@ -5473,6 +5476,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
xhci->hcc_params = readl(&xhci->cap_regs->hcc_params);
if (xhci->hci_version > 0x100)
xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
+ tunnel_cap = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_USB3_TUNNELING);
+ if (tunnel_cap) {
+ xhci->portsc_tunnel_reporting =
+ !!(readl(base + tunnel_cap) & XHCI_USB3_TUNNELING_SUPPORTED);
+ }
xhci->dma_mask_bits = 64;
xhci->max_slots = min(FIELD_GET(HCS_SLOTS_MASK, hcs_params1), MAX_HC_SLOTS);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index c7bfa7f028d3..480fcbac4525 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1662,6 +1662,7 @@ struct xhci_hcd {
/* cached extended protocol port capabilities */
struct xhci_port_cap *port_caps;
unsigned int num_port_caps;
+ bool portsc_tunnel_reporting;
/* Compliance Mode Recovery Data */
struct timer_list comp_mode_recovery_timer;
u32 port_status_u0;
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 3/4] usb: xhci: Use update_device overrides for tunnel reporting
2026-09-29 15:54 [PATCH v4 0/4] DWC3 link tunneling state reporting Konrad Dybcio
2026-09-29 15:54 ` [PATCH v4 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value Konrad Dybcio
2026-09-29 15:54 ` [PATCH v4 2/4] usb: xhci: Honor PORTSC.TM if valid Konrad Dybcio
@ 2026-09-29 15:54 ` Konrad Dybcio
2026-10-03 0:30 ` Thinh Nguyen
2026-09-29 15:54 ` [PATCH v4 4/4] usb: dwc3: Notify XHCI core of tunneled status Konrad Dybcio
3 siblings, 1 reply; 7+ messages in thread
From: Konrad Dybcio @ 2026-09-29 15:54 UTC (permalink / raw)
To: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman
Cc: linux-usb, linux-kernel, usb4-upstream, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
The Thunderbolt framework relies on USB core device links for tunneled
ports, so it needs tunnel state when xHCI updates a device.
Use the .update_device override for controller-specific tunnel
reporting.
Assisted-by: LLM
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/usb/host/xhci-hub.c | 53 --------------------------------------------
drivers/usb/host/xhci-pci.c | 38 +++++++++++++++++++++++++++++++
drivers/usb/host/xhci-plat.c | 18 +++++++++++++++
drivers/usb/host/xhci-plat.h | 2 ++
drivers/usb/host/xhci.c | 19 +++++++++-------
drivers/usb/host/xhci.h | 8 +++++--
6 files changed, 75 insertions(+), 63 deletions(-)
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 80f07bd5081d..31ba7a990e07 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -12,7 +12,6 @@
#include <linux/slab.h>
#include <linux/unaligned.h>
#include <linux/bitfield.h>
-#include <linux/pci.h>
#include "xhci.h"
#include "xhci-trace.h"
@@ -748,58 +747,6 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
return xhci_reset(xhci, XHCI_RESET_SHORT_USEC);
}
-/**
- * xhci_port_is_tunneled() - Check if USB3 connection is tunneled over USB4
- * @xhci: xhci host controller
- * @port: USB3 port to be checked.
- *
- * Some hosts can detect if a USB3 connection is native USB3 or tunneled over
- * USB4. Intel hosts expose this via vendor specific extended capability 206
- * eSS PORT registers TUNEN (tunnel enabled) bit.
- *
- * A USB3 device must be connected to the port to detect the tunnel.
- *
- * Return: link tunnel mode enum, USB_LINK_UNKNOWN if host is incapable of
- * detecting USB3 over USB4 tunnels. USB_LINK_NATIVE or USB_LINK_TUNNELED
- * otherwise.
- */
-enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
- struct xhci_port *port)
-{
- struct usb_hcd *hcd;
- void __iomem *base;
- u32 offset;
- u32 val;
-
- if (xhci->portsc_tunnel_reporting) {
- val = xhci_portsc_readl(port);
- if (val & PORT_TM)
- return USB_LINK_TUNNELED;
-
- return USB_LINK_NATIVE;
- }
-
- /* Fall back to the legacy Intel-specific ext_cap */
- hcd = xhci_to_hcd(xhci);
- if (!dev_is_pci(hcd->self.controller) ||
- to_pci_dev(hcd->self.controller)->vendor != PCI_VENDOR_ID_INTEL)
- return USB_LINK_UNKNOWN;
-
- base = &xhci->cap_regs->hc_capbase;
- offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);
-
- if (offset && offset <= XHCI_INTEL_SPR_ESS_PORT_OFFSET) {
- offset = XHCI_INTEL_SPR_ESS_PORT_OFFSET + port->hcd_portnum * 0x20;
-
- if (readl(base + offset) & XHCI_INTEL_SPR_TUNEN)
- return USB_LINK_TUNNELED;
- else
- return USB_LINK_NATIVE;
- }
-
- return USB_LINK_UNKNOWN;
-}
-
void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
u32 link_state)
{
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index a8889081ae82..61ac267bc84a 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -103,11 +103,13 @@ static int xhci_pci_setup(struct usb_hcd *hcd);
static int xhci_pci_run(struct usb_hcd *hcd);
static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
struct usb_tt *tt, gfp_t mem_flags);
+static int xhci_pci_update_device(struct usb_hcd *hcd, struct usb_device *udev);
static const struct xhci_driver_overrides xhci_pci_overrides __initconst = {
.reset = xhci_pci_setup,
.start = xhci_pci_run,
.update_hub_device = xhci_pci_update_hub_device,
+ .update_device = xhci_pci_update_device,
};
/*
@@ -606,6 +608,42 @@ static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hd
return xhci_update_hub_device(hcd, hdev, tt, mem_flags);
}
+static void xhci_intel_port_is_tunneled(struct usb_hcd *hcd,
+ struct usb_device *udev)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct xhci_port *port;
+ void __iomem *base;
+ u32 offset;
+
+ if (xhci->portsc_tunnel_reporting || udev->parent->parent)
+ return;
+
+ port = xhci->usb3_rhub.ports[udev->portnum - 1];
+
+ base = &xhci->cap_regs->hc_capbase;
+ offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);
+
+ if (offset && offset <= XHCI_INTEL_SPR_ESS_PORT_OFFSET) {
+ offset = XHCI_INTEL_SPR_ESS_PORT_OFFSET + port->hcd_portnum * 0x20;
+
+ if (readl(base + offset) & XHCI_INTEL_SPR_TUNEN)
+ udev->tunnel_mode = USB_LINK_TUNNELED;
+ else
+ udev->tunnel_mode = USB_LINK_NATIVE;
+ }
+}
+
+static int xhci_pci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL && hcd->speed >= HCD_USB3)
+ xhci_intel_port_is_tunneled(hcd, udev);
+
+ return xhci_update_device(hcd, udev);
+}
+
/*
* We need to register our own PCI probe function (instead of the USB core's
* function) in order to create a second roothub under xHCI.
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 6fd595f81a30..641e31976240 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -30,13 +30,31 @@ static struct hc_driver __read_mostly xhci_plat_hc_driver;
static int xhci_plat_setup(struct usb_hcd *hcd);
static int xhci_plat_start(struct usb_hcd *hcd);
+static int xhci_plat_update_device(struct usb_hcd *hcd, struct usb_device *udev);
static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
.extra_priv_size = sizeof(struct xhci_plat_priv),
.reset = xhci_plat_setup,
.start = xhci_plat_start,
+ .update_device = xhci_plat_update_device,
};
+static int xhci_plat_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
+ int ret;
+
+ if (!xhci->portsc_tunnel_reporting && priv->update_device &&
+ hcd->speed >= HCD_USB3 && !udev->parent->parent) {
+ ret = priv->update_device(hcd, udev);
+ if (ret)
+ return ret;
+ }
+
+ return xhci_update_device(hcd, udev);
+}
+
static void xhci_priv_plat_start(struct usb_hcd *hcd)
{
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
index 00751d851831..91333605c46b 100644
--- a/drivers/usb/host/xhci-plat.h
+++ b/drivers/usb/host/xhci-plat.h
@@ -10,6 +10,7 @@
struct device;
struct platform_device;
+struct usb_device;
struct usb_hcd;
struct xhci_plat_priv {
@@ -22,6 +23,7 @@ struct xhci_plat_priv {
int (*suspend_quirk)(struct usb_hcd *);
int (*resume_quirk)(struct usb_hcd *);
int (*post_resume_quirk)(struct usb_hcd *);
+ int (*update_device)(struct usb_hcd *hcd, struct usb_device *udev);
};
#define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 5fb78613a993..b252ce5e8499 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4752,7 +4752,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
return 0;
}
-static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_port *port;
@@ -4760,9 +4760,14 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
/* Check if USB3 device at root port is tunneled over USB4 */
if (hcd->speed >= HCD_USB3 && !udev->parent->parent) {
- port = xhci->usb3_rhub.ports[udev->portnum - 1];
+ if (xhci->portsc_tunnel_reporting) {
+ port = xhci->usb3_rhub.ports[udev->portnum - 1];
+ if (xhci_portsc_readl(port) & PORT_TM)
+ udev->tunnel_mode = USB_LINK_TUNNELED;
+ else
+ udev->tunnel_mode = USB_LINK_NATIVE;
+ }
- udev->tunnel_mode = xhci_port_is_tunneled(xhci, port);
if (udev->tunnel_mode == USB_LINK_UNKNOWN)
dev_dbg(&udev->dev, "link tunnel state unknown\n");
else if (udev->tunnel_mode == USB_LINK_TUNNELED)
@@ -4793,6 +4798,7 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
return 0;
}
+EXPORT_SYMBOL_GPL(xhci_update_device);
/*---------------------- USB 3.0 Link PM functions ------------------------*/
@@ -5242,11 +5248,6 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
return 0;
}
-static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
-{
- return 0;
-}
-
static int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd,
struct usb_device *udev, enum usb3_link_state state)
{
@@ -5695,6 +5696,8 @@ void xhci_init_driver(struct hc_driver *drv,
drv->update_hub_device = over->update_hub_device;
if (over->hub_control)
drv->hub_control = over->hub_control;
+ if (over->update_device)
+ drv->update_device = over->update_device;
}
}
EXPORT_SYMBOL_GPL(xhci_init_driver);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 480fcbac4525..f12a309104dc 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1694,6 +1694,7 @@ struct xhci_driver_overrides {
struct usb_tt *tt, gfp_t mem_flags);
int (*hub_control)(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength);
+ int (*update_device)(struct usb_hcd *hcd, struct usb_device *udev);
};
#define XHCI_CFC_DELAY 10
@@ -1981,15 +1982,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
-enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
- struct xhci_port *port);
void xhci_hc_died(struct xhci_hcd *xhci);
#ifdef CONFIG_PM
+int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev);
int xhci_bus_suspend(struct usb_hcd *hcd);
int xhci_bus_resume(struct usb_hcd *hcd);
unsigned long xhci_get_resuming_ports(struct usb_hcd *hcd);
#else
+static inline int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ return 0;
+}
#define xhci_bus_suspend NULL
#define xhci_bus_resume NULL
#define xhci_get_resuming_ports NULL
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 4/4] usb: dwc3: Notify XHCI core of tunneled status
2026-09-29 15:54 [PATCH v4 0/4] DWC3 link tunneling state reporting Konrad Dybcio
` (2 preceding siblings ...)
2026-09-29 15:54 ` [PATCH v4 3/4] usb: xhci: Use update_device overrides for tunnel reporting Konrad Dybcio
@ 2026-09-29 15:54 ` Konrad Dybcio
2026-10-03 1:23 ` Thinh Nguyen
3 siblings, 1 reply; 7+ messages in thread
From: Konrad Dybcio @ 2026-09-29 15:54 UTC (permalink / raw)
To: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman
Cc: linux-usb, linux-kernel, usb4-upstream, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter, Konrad Dybcio
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
The Thunderbolt framework relies on the USB core to create device links
for tunneled ports, so that the USB3 controller is only kept
runtime-resumed for the duration of the tunneling. This depends on
first knowing whether a connection is tunneled or native.
Add the logic to handle that for DWC3 controllers.
Assisted-by: LLM
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/usb/dwc3/core.c | 18 ++++++++++++++++++
drivers/usb/dwc3/core.h | 18 ++++++++++++++++++
drivers/usb/dwc3/host.c | 14 ++++++++++++++
3 files changed, 50 insertions(+)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index fd5c2cd36c59..4eb430f58983 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -162,6 +162,24 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
}
EXPORT_SYMBOL_GPL(dwc3_set_prtcap);
+#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
+enum usb_link_tunnel_mode dwc3_link_tunnel_mode(struct dwc3 *dwc, u8 port)
+{
+ /* Prior versions had no CIO support */
+ if (!DWC3_VER_IS_WITHIN(DWC31, 191A, ANY))
+ return USB_LINK_UNKNOWN;
+
+ /* Not all DWC3 instances have CIO HW, trust the platform firmware */
+ if (!device_property_present(dwc->dev, "usb4-host-interface"))
+ return USB_LINK_UNKNOWN;
+
+ if (dwc3_readl(dwc, DWC3_CIOCTRL(port)) & DWC3_CIOCTRL_CIO_EN)
+ return USB_LINK_TUNNELED;
+
+ return USB_LINK_NATIVE;
+}
+#endif
+
static void __dwc3_set_mode(struct work_struct *work)
{
struct dwc3 *dwc = work_to_dwc(work);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 608daeb7ef10..decc51cd6505 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -179,6 +179,11 @@
#define DWC3_OEVTEN 0xcc0C
#define DWC3_OSTS 0xcc10
+/* CIO regs */
+#define DWC3_CIO_BASE(n) (0xcd20 + ((n) * 0x30))
+#define DWC3_CIOCTRL(n) (DWC3_CIO_BASE(n) + 0x00)
+#define DWC3_CIOCTRL_CIO_EN BIT(0)
+
#define DWC3_LLUCTL(n) (0xd024 + ((n) * 0x80))
/* Bit fields */
@@ -1314,6 +1319,7 @@ struct dwc3 {
#define DWC31_REVISION_170A 0x3137302a
#define DWC31_REVISION_180A 0x3138302a
#define DWC31_REVISION_190A 0x3139302a
+#define DWC31_REVISION_191A 0x3139312a
#define DWC31_REVISION_200A 0x3230302a
#define DWC32_REVISION_ANY 0x0
@@ -1658,11 +1664,23 @@ static inline void dwc3_pre_run_stop(struct dwc3 *dwc, bool is_on)
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
+
+/**
+ * dwc3_link_tunnel_mode - Check whether the link is tunneled over TBT/USB4
+ * @dwc: Pointer to DWC3 controller context
+ * @port: 0-based port index
+ *
+ * Returns: USB_LINK_TUNNELED if tunneled, USB_LINK_NATIVE if not, or
+ * when the controller does not have USB4 capabilities.
+ */
+enum usb_link_tunnel_mode dwc3_link_tunnel_mode(struct dwc3 *dwc, u8 port);
#else
static inline int dwc3_host_init(struct dwc3 *dwc)
{ return 0; }
static inline void dwc3_host_exit(struct dwc3 *dwc)
{ }
+static inline enum usb_link_tunnel_mode dwc3_link_tunnel_mode(struct dwc3 *dwc, u8 port)
+{ return USB_LINK_UNKNOWN; }
#endif
#if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index c5674161b2b0..a97dbf9cd27a 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -78,8 +78,22 @@ static void dwc3_xhci_plat_start(struct usb_hcd *hcd)
dwc3_enable_susphy(dwc, true);
}
+static int dwc3_xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ struct platform_device *pdev;
+ struct dwc3 *dwc;
+
+ pdev = to_platform_device(hcd->self.controller);
+ dwc = dev_get_drvdata(pdev->dev.parent);
+
+ udev->tunnel_mode = dwc3_link_tunnel_mode(dwc, udev->portnum - 1);
+
+ return 0;
+}
+
static const struct xhci_plat_priv dwc3_xhci_plat_quirk = {
.plat_start = dwc3_xhci_plat_start,
+ .update_device = dwc3_xhci_update_device,
};
static void dwc3_host_fill_xhci_irq_res(struct dwc3 *dwc,
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4 3/4] usb: xhci: Use update_device overrides for tunnel reporting
2026-09-29 15:54 ` [PATCH v4 3/4] usb: xhci: Use update_device overrides for tunnel reporting Konrad Dybcio
@ 2026-10-03 0:30 ` Thinh Nguyen
0 siblings, 0 replies; 7+ messages in thread
From: Thinh Nguyen @ 2026-10-03 0:30 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman, linux-usb,
linux-kernel, usb4-upstream, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter, Konrad Dybcio
On Tue, Sep 29, 2026, Konrad Dybcio wrote:
> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>
> The Thunderbolt framework relies on USB core device links for tunneled
> ports, so it needs tunnel state when xHCI updates a device.
>
> Use the .update_device override for controller-specific tunnel
> reporting.
>
> Assisted-by: LLM
> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> ---
> drivers/usb/host/xhci-hub.c | 53 --------------------------------------------
> drivers/usb/host/xhci-pci.c | 38 +++++++++++++++++++++++++++++++
> drivers/usb/host/xhci-plat.c | 18 +++++++++++++++
> drivers/usb/host/xhci-plat.h | 2 ++
> drivers/usb/host/xhci.c | 19 +++++++++-------
> drivers/usb/host/xhci.h | 8 +++++--
> 6 files changed, 75 insertions(+), 63 deletions(-)
>
> diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
> index 80f07bd5081d..31ba7a990e07 100644
> --- a/drivers/usb/host/xhci-hub.c
> +++ b/drivers/usb/host/xhci-hub.c
> @@ -12,7 +12,6 @@
> #include <linux/slab.h>
> #include <linux/unaligned.h>
> #include <linux/bitfield.h>
> -#include <linux/pci.h>
>
> #include "xhci.h"
> #include "xhci-trace.h"
> @@ -748,58 +747,6 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
> return xhci_reset(xhci, XHCI_RESET_SHORT_USEC);
> }
>
> -/**
> - * xhci_port_is_tunneled() - Check if USB3 connection is tunneled over USB4
> - * @xhci: xhci host controller
> - * @port: USB3 port to be checked.
> - *
> - * Some hosts can detect if a USB3 connection is native USB3 or tunneled over
> - * USB4. Intel hosts expose this via vendor specific extended capability 206
> - * eSS PORT registers TUNEN (tunnel enabled) bit.
> - *
> - * A USB3 device must be connected to the port to detect the tunnel.
> - *
> - * Return: link tunnel mode enum, USB_LINK_UNKNOWN if host is incapable of
> - * detecting USB3 over USB4 tunnels. USB_LINK_NATIVE or USB_LINK_TUNNELED
> - * otherwise.
> - */
> -enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
> - struct xhci_port *port)
> -{
> - struct usb_hcd *hcd;
> - void __iomem *base;
> - u32 offset;
> - u32 val;
> -
> - if (xhci->portsc_tunnel_reporting) {
> - val = xhci_portsc_readl(port);
> - if (val & PORT_TM)
> - return USB_LINK_TUNNELED;
> -
> - return USB_LINK_NATIVE;
> - }
> -
> - /* Fall back to the legacy Intel-specific ext_cap */
> - hcd = xhci_to_hcd(xhci);
> - if (!dev_is_pci(hcd->self.controller) ||
> - to_pci_dev(hcd->self.controller)->vendor != PCI_VENDOR_ID_INTEL)
> - return USB_LINK_UNKNOWN;
> -
> - base = &xhci->cap_regs->hc_capbase;
> - offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);
> -
> - if (offset && offset <= XHCI_INTEL_SPR_ESS_PORT_OFFSET) {
> - offset = XHCI_INTEL_SPR_ESS_PORT_OFFSET + port->hcd_portnum * 0x20;
> -
> - if (readl(base + offset) & XHCI_INTEL_SPR_TUNEN)
> - return USB_LINK_TUNNELED;
> - else
> - return USB_LINK_NATIVE;
> - }
> -
> - return USB_LINK_UNKNOWN;
> -}
> -
> void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
> u32 link_state)
> {
> diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
> index a8889081ae82..61ac267bc84a 100644
> --- a/drivers/usb/host/xhci-pci.c
> +++ b/drivers/usb/host/xhci-pci.c
> @@ -103,11 +103,13 @@ static int xhci_pci_setup(struct usb_hcd *hcd);
> static int xhci_pci_run(struct usb_hcd *hcd);
> static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
> struct usb_tt *tt, gfp_t mem_flags);
> +static int xhci_pci_update_device(struct usb_hcd *hcd, struct usb_device *udev);
>
> static const struct xhci_driver_overrides xhci_pci_overrides __initconst = {
> .reset = xhci_pci_setup,
> .start = xhci_pci_run,
> .update_hub_device = xhci_pci_update_hub_device,
> + .update_device = xhci_pci_update_device,
> };
>
> /*
> @@ -606,6 +608,42 @@ static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hd
> return xhci_update_hub_device(hcd, hdev, tt, mem_flags);
> }
>
> +static void xhci_intel_port_is_tunneled(struct usb_hcd *hcd,
> + struct usb_device *udev)
> +{
> + struct xhci_hcd *xhci = hcd_to_xhci(hcd);
> + struct xhci_port *port;
> + void __iomem *base;
> + u32 offset;
> +
> + if (xhci->portsc_tunnel_reporting || udev->parent->parent)
> + return;
> +
> + port = xhci->usb3_rhub.ports[udev->portnum - 1];
> +
> + base = &xhci->cap_regs->hc_capbase;
> + offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);
> +
> + if (offset && offset <= XHCI_INTEL_SPR_ESS_PORT_OFFSET) {
> + offset = XHCI_INTEL_SPR_ESS_PORT_OFFSET + port->hcd_portnum * 0x20;
> +
> + if (readl(base + offset) & XHCI_INTEL_SPR_TUNEN)
> + udev->tunnel_mode = USB_LINK_TUNNELED;
> + else
> + udev->tunnel_mode = USB_LINK_NATIVE;
> + }
> +}
> +
> +static int xhci_pci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
> +{
> + struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
> +
> + if (pdev->vendor == PCI_VENDOR_ID_INTEL && hcd->speed >= HCD_USB3)
> + xhci_intel_port_is_tunneled(hcd, udev);
> +
> + return xhci_update_device(hcd, udev);
> +}
> +
> /*
> * We need to register our own PCI probe function (instead of the USB core's
> * function) in order to create a second roothub under xHCI.
> diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
> index 6fd595f81a30..641e31976240 100644
> --- a/drivers/usb/host/xhci-plat.c
> +++ b/drivers/usb/host/xhci-plat.c
> @@ -30,13 +30,31 @@ static struct hc_driver __read_mostly xhci_plat_hc_driver;
>
> static int xhci_plat_setup(struct usb_hcd *hcd);
> static int xhci_plat_start(struct usb_hcd *hcd);
> +static int xhci_plat_update_device(struct usb_hcd *hcd, struct usb_device *udev);
>
> static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
> .extra_priv_size = sizeof(struct xhci_plat_priv),
> .reset = xhci_plat_setup,
> .start = xhci_plat_start,
> + .update_device = xhci_plat_update_device,
> };
>
> +static int xhci_plat_update_device(struct usb_hcd *hcd, struct usb_device *udev)
> +{
> + struct xhci_hcd *xhci = hcd_to_xhci(hcd);
> + struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
> + int ret;
> +
> + if (!xhci->portsc_tunnel_reporting && priv->update_device &&
> + hcd->speed >= HCD_USB3 && !udev->parent->parent) {
> + ret = priv->update_device(hcd, udev);
> + if (ret)
> + return ret;
> + }
> +
> + return xhci_update_device(hcd, udev);
> +}
> +
> static void xhci_priv_plat_start(struct usb_hcd *hcd)
> {
> struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
> diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
> index 00751d851831..91333605c46b 100644
> --- a/drivers/usb/host/xhci-plat.h
> +++ b/drivers/usb/host/xhci-plat.h
> @@ -10,6 +10,7 @@
>
> struct device;
> struct platform_device;
> +struct usb_device;
> struct usb_hcd;
>
> struct xhci_plat_priv {
> @@ -22,6 +23,7 @@ struct xhci_plat_priv {
> int (*suspend_quirk)(struct usb_hcd *);
> int (*resume_quirk)(struct usb_hcd *);
> int (*post_resume_quirk)(struct usb_hcd *);
> + int (*update_device)(struct usb_hcd *hcd, struct usb_device *udev);
> };
>
> #define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv)
> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
> index 5fb78613a993..b252ce5e8499 100644
> --- a/drivers/usb/host/xhci.c
> +++ b/drivers/usb/host/xhci.c
> @@ -4752,7 +4752,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
> return 0;
> }
>
> -static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
> +int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
> {
> struct xhci_hcd *xhci = hcd_to_xhci(hcd);
> struct xhci_port *port;
> @@ -4760,9 +4760,14 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
>
> /* Check if USB3 device at root port is tunneled over USB4 */
> if (hcd->speed >= HCD_USB3 && !udev->parent->parent) {
> - port = xhci->usb3_rhub.ports[udev->portnum - 1];
> + if (xhci->portsc_tunnel_reporting) {
> + port = xhci->usb3_rhub.ports[udev->portnum - 1];
> + if (xhci_portsc_readl(port) & PORT_TM)
> + udev->tunnel_mode = USB_LINK_TUNNELED;
> + else
> + udev->tunnel_mode = USB_LINK_NATIVE;
> + }
>
> - udev->tunnel_mode = xhci_port_is_tunneled(xhci, port);
> if (udev->tunnel_mode == USB_LINK_UNKNOWN)
> dev_dbg(&udev->dev, "link tunnel state unknown\n");
> else if (udev->tunnel_mode == USB_LINK_TUNNELED)
> @@ -4793,6 +4798,7 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
>
> return 0;
> }
> +EXPORT_SYMBOL_GPL(xhci_update_device);
>
> /*---------------------- USB 3.0 Link PM functions ------------------------*/
>
> @@ -5242,11 +5248,6 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
> return 0;
> }
>
> -static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
> -{
> - return 0;
> -}
> -
> static int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd,
> struct usb_device *udev, enum usb3_link_state state)
> {
> @@ -5695,6 +5696,8 @@ void xhci_init_driver(struct hc_driver *drv,
> drv->update_hub_device = over->update_hub_device;
> if (over->hub_control)
> drv->hub_control = over->hub_control;
> + if (over->update_device)
> + drv->update_device = over->update_device;
> }
> }
> EXPORT_SYMBOL_GPL(xhci_init_driver);
> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> index 480fcbac4525..f12a309104dc 100644
> --- a/drivers/usb/host/xhci.h
> +++ b/drivers/usb/host/xhci.h
> @@ -1694,6 +1694,7 @@ struct xhci_driver_overrides {
> struct usb_tt *tt, gfp_t mem_flags);
> int (*hub_control)(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
> u16 wIndex, char *buf, u16 wLength);
> + int (*update_device)(struct usb_hcd *hcd, struct usb_device *udev);
> };
>
> #define XHCI_CFC_DELAY 10
> @@ -1981,15 +1982,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
> int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
> int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
> struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
> -enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
> - struct xhci_port *port);
> void xhci_hc_died(struct xhci_hcd *xhci);
>
> #ifdef CONFIG_PM
> +int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev);
Why are we putting this inside this PM guard?
BR,
Thinh
> int xhci_bus_suspend(struct usb_hcd *hcd);
> int xhci_bus_resume(struct usb_hcd *hcd);
> unsigned long xhci_get_resuming_ports(struct usb_hcd *hcd);
> #else
> +static inline int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
> +{
> + return 0;
> +}
> #define xhci_bus_suspend NULL
> #define xhci_bus_resume NULL
> #define xhci_get_resuming_ports NULL
>
> --
> 2.55.0
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4 4/4] usb: dwc3: Notify XHCI core of tunneled status
2026-09-29 15:54 ` [PATCH v4 4/4] usb: dwc3: Notify XHCI core of tunneled status Konrad Dybcio
@ 2026-10-03 1:23 ` Thinh Nguyen
0 siblings, 0 replies; 7+ messages in thread
From: Thinh Nguyen @ 2026-10-03 1:23 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman, linux-usb,
linux-kernel, usb4-upstream, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter, Konrad Dybcio
On Tue, Sep 29, 2026, Konrad Dybcio wrote:
> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>
> The Thunderbolt framework relies on the USB core to create device links
> for tunneled ports, so that the USB3 controller is only kept
> runtime-resumed for the duration of the tunneling. This depends on
> first knowing whether a connection is tunneled or native.
>
> Add the logic to handle that for DWC3 controllers.
>
> Assisted-by: LLM
> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> ---
> drivers/usb/dwc3/core.c | 18 ++++++++++++++++++
> drivers/usb/dwc3/core.h | 18 ++++++++++++++++++
> drivers/usb/dwc3/host.c | 14 ++++++++++++++
> 3 files changed, 50 insertions(+)
>
> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> index fd5c2cd36c59..4eb430f58983 100644
> --- a/drivers/usb/dwc3/core.c
> +++ b/drivers/usb/dwc3/core.c
> @@ -162,6 +162,24 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
> }
> EXPORT_SYMBOL_GPL(dwc3_set_prtcap);
>
> +#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
Move this function to host.c instead of core.c, drop the guards, and
make it static.
> +enum usb_link_tunnel_mode dwc3_link_tunnel_mode(struct dwc3 *dwc, u8 port)
> +{
Maybe also add a check if (port >= dwc->num_usb3_ports) and return
USB_LINK_UNKNOWN?
> + /* Prior versions had no CIO support */
> + if (!DWC3_VER_IS_WITHIN(DWC31, 191A, ANY))
> + return USB_LINK_UNKNOWN;
Regarding your cover letter question about CIO for other IPs. We can
leave that for later when we need to make an update.
> +
> + /* Not all DWC3 instances have CIO HW, trust the platform firmware */
> + if (!device_property_present(dwc->dev, "usb4-host-interface"))
Can we pass this as a dwc3 property from your glue driver instead,
inferred from the compatible string? (ie. add dwc->has_cio)
So we can remove the DT dependency.
> + return USB_LINK_UNKNOWN;
> +
> + if (dwc3_readl(dwc, DWC3_CIOCTRL(port)) & DWC3_CIOCTRL_CIO_EN)
> + return USB_LINK_TUNNELED;
> +
> + return USB_LINK_NATIVE;
> +}
> +#endif
> +
> static void __dwc3_set_mode(struct work_struct *work)
> {
> struct dwc3 *dwc = work_to_dwc(work);
> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
> index 608daeb7ef10..decc51cd6505 100644
> --- a/drivers/usb/dwc3/core.h
> +++ b/drivers/usb/dwc3/core.h
> @@ -179,6 +179,11 @@
> #define DWC3_OEVTEN 0xcc0C
> #define DWC3_OSTS 0xcc10
>
> +/* CIO regs */
> +#define DWC3_CIO_BASE(n) (0xcd20 + ((n) * 0x30))
> +#define DWC3_CIOCTRL(n) (DWC3_CIO_BASE(n) + 0x00)
> +#define DWC3_CIOCTRL_CIO_EN BIT(0)
> +
> #define DWC3_LLUCTL(n) (0xd024 + ((n) * 0x80))
>
> /* Bit fields */
> @@ -1314,6 +1319,7 @@ struct dwc3 {
> #define DWC31_REVISION_170A 0x3137302a
> #define DWC31_REVISION_180A 0x3138302a
> #define DWC31_REVISION_190A 0x3139302a
> +#define DWC31_REVISION_191A 0x3139312a
> #define DWC31_REVISION_200A 0x3230302a
>
> #define DWC32_REVISION_ANY 0x0
> @@ -1658,11 +1664,23 @@ static inline void dwc3_pre_run_stop(struct dwc3 *dwc, bool is_on)
> #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
> int dwc3_host_init(struct dwc3 *dwc);
> void dwc3_host_exit(struct dwc3 *dwc);
> +
> +/**
> + * dwc3_link_tunnel_mode - Check whether the link is tunneled over TBT/USB4
> + * @dwc: Pointer to DWC3 controller context
> + * @port: 0-based port index
> + *
> + * Returns: USB_LINK_TUNNELED if tunneled, USB_LINK_NATIVE if not, or
> + * when the controller does not have USB4 capabilities.
Do we need to describe the USB_LINK_UNKNOWN case?
Thanks,
Thinh
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-03 1:24 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-29 15:54 [PATCH v4 0/4] DWC3 link tunneling state reporting Konrad Dybcio
2026-09-29 15:54 ` [PATCH v4 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value Konrad Dybcio
2026-09-29 15:54 ` [PATCH v4 2/4] usb: xhci: Honor PORTSC.TM if valid Konrad Dybcio
2026-09-29 15:54 ` [PATCH v4 3/4] usb: xhci: Use update_device overrides for tunnel reporting Konrad Dybcio
2026-10-03 0:30 ` Thinh Nguyen
2026-09-29 15:54 ` [PATCH v4 4/4] usb: dwc3: Notify XHCI core of tunneled status Konrad Dybcio
2026-10-03 1:23 ` Thinh Nguyen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®