* [PATCH v3 0/4] DWC3 link tunneling state reporting
@ 2026-09-01 10:05 Konrad Dybcio
2026-09-01 10:05 ` [PATCH v3 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value Konrad Dybcio
` (3 more replies)
0 siblings, 4 replies; 9+ messages in thread
From: Konrad Dybcio @ 2026-09-01 10:05 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 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
---
Konrad Dybcio (4):
usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value
usb: xhci: Honor PORTSC.TM if valid
usb: xhci: Allow custom op for usb_link_tunnel_mode reporting
usb: dwc3: Notify XHCI core of tunneled status
drivers/usb/dwc3/core.c | 16 ++++++++++++++++
drivers/usb/dwc3/core.h | 18 ++++++++++++++++++
drivers/usb/dwc3/host.c | 12 ++++++++++++
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 | 24 +++++++++++++++++++-----
drivers/usb/host/xhci-plat.c | 2 ++
drivers/usb/host/xhci-plat.h | 1 +
drivers/usb/host/xhci-port.h | 3 ++-
drivers/usb/host/xhci.c | 11 ++++++++++-
drivers/usb/host/xhci.h | 6 +++++-
12 files changed, 100 insertions(+), 8 deletions(-)
---
base-commit: 89c07d98716a13454ec3fd9f97689e812cc71bd4
change-id: 20260504-topic-dwc3_tunneling_state-50588c0e1ae3
Best regards,
--
Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value
2026-09-01 10:05 [PATCH v3 0/4] DWC3 link tunneling state reporting Konrad Dybcio
@ 2026-09-01 10:05 ` Konrad Dybcio
2026-09-05 0:26 ` Thinh Nguyen
2026-09-01 10:05 ` [PATCH v3 2/4] usb: xhci: Honor PORTSC.TM if valid Konrad Dybcio
` (2 subsequent siblings)
3 siblings, 1 reply; 9+ messages in thread
From: Konrad Dybcio @ 2026-09-01 10:05 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.
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 related [flat|nested] 9+ messages in thread
* [PATCH v3 2/4] usb: xhci: Honor PORTSC.TM if valid
2026-09-01 10:05 [PATCH v3 0/4] DWC3 link tunneling state reporting Konrad Dybcio
2026-09-01 10:05 ` [PATCH v3 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value Konrad Dybcio
@ 2026-09-01 10:05 ` Konrad Dybcio
2026-09-05 0:27 ` Thinh Nguyen
2026-09-01 10:05 ` [PATCH v3 3/4] usb: xhci: Allow custom op for usb_link_tunnel_mode reporting Konrad Dybcio
2026-09-01 10:05 ` [PATCH v3 4/4] usb: dwc3: Notify XHCI core of tunneled status Konrad Dybcio
3 siblings, 1 reply; 9+ messages in thread
From: Konrad Dybcio @ 2026-09-01 10:05 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.
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 related [flat|nested] 9+ messages in thread
* [PATCH v3 3/4] usb: xhci: Allow custom op for usb_link_tunnel_mode reporting
2026-09-01 10:05 [PATCH v3 0/4] DWC3 link tunneling state reporting Konrad Dybcio
2026-09-01 10:05 ` [PATCH v3 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value Konrad Dybcio
2026-09-01 10:05 ` [PATCH v3 2/4] usb: xhci: Honor PORTSC.TM if valid Konrad Dybcio
@ 2026-09-01 10:05 ` Konrad Dybcio
2026-09-05 0:29 ` Thinh Nguyen
2026-09-01 10:05 ` [PATCH v3 4/4] usb: dwc3: Notify XHCI core of tunneled status Konrad Dybcio
3 siblings, 1 reply; 9+ messages in thread
From: Konrad Dybcio @ 2026-09-01 10:05 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.
Currently, retrieving that information is only possibe on Intel XHCI
hosts, through a vendor-specific capability. Extend xhci-plat to allow
plumbing a custom one.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/usb/host/xhci-hub.c | 13 +++++++++----
drivers/usb/host/xhci-plat.c | 2 ++
drivers/usb/host/xhci-plat.h | 1 +
drivers/usb/host/xhci.c | 3 ++-
drivers/usb/host/xhci.h | 5 ++++-
5 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 80f07bd5081d..c585e4d02d84 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -749,7 +749,7 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
}
/**
- * xhci_port_is_tunneled() - Check if USB3 connection is tunneled over USB4
+ * xhci_port_tunnel_mode() - Check if USB3 connection is tunneled over USB4
* @xhci: xhci host controller
* @port: USB3 port to be checked.
*
@@ -763,7 +763,7 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
* 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,
+enum usb_link_tunnel_mode xhci_port_tunnel_mode(struct xhci_hcd *xhci,
struct xhci_port *port)
{
struct usb_hcd *hcd;
@@ -782,8 +782,13 @@ enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
/* 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;
+ to_pci_dev(hcd->self.controller)->vendor != PCI_VENDOR_ID_INTEL) {
+ /* Last chance - if the controller has a custom tunnel_mode op, try that */
+ if (xhci->tunnel_mode)
+ return xhci->tunnel_mode(xhci_to_hcd(xhci), port->hcd_portnum);
+ else
+ return USB_LINK_UNKNOWN;
+ }
base = &xhci->cap_regs->hc_capbase;
offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 6fd595f81a30..c19bc5d7a7b5 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -244,6 +244,8 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
priv = hcd_to_xhci_priv(hcd);
/* Just copy data for now */
*priv = *priv_match;
+
+ xhci->tunnel_mode = priv->tunnel_mode;
}
device_set_wakeup_capable(&pdev->dev, true);
diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
index 00751d851831..c5042766a486 100644
--- a/drivers/usb/host/xhci-plat.h
+++ b/drivers/usb/host/xhci-plat.h
@@ -22,6 +22,7 @@ struct xhci_plat_priv {
int (*suspend_quirk)(struct usb_hcd *);
int (*resume_quirk)(struct usb_hcd *);
int (*post_resume_quirk)(struct usb_hcd *);
+ enum usb_link_tunnel_mode (*tunnel_mode)(struct usb_hcd *hcd, int portnum);
};
#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..a7e3c46937fa 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4762,7 +4762,8 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
if (hcd->speed >= HCD_USB3 && !udev->parent->parent) {
port = xhci->usb3_rhub.ports[udev->portnum - 1];
- udev->tunnel_mode = xhci_port_is_tunneled(xhci, port);
+ udev->tunnel_mode = xhci_port_tunnel_mode(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)
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 480fcbac4525..2b5c0b304c6b 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1675,6 +1675,9 @@ struct xhci_hcd {
struct list_head regset_list;
void *dbc;
+
+ enum usb_link_tunnel_mode (*tunnel_mode)(struct usb_hcd *hcd, int portnum);
+
/* platform-specific data -- must come last */
unsigned long priv[] __aligned(sizeof(s64));
};
@@ -1981,7 +1984,7 @@ 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,
+enum usb_link_tunnel_mode xhci_port_tunnel_mode(struct xhci_hcd *xhci,
struct xhci_port *port);
void xhci_hc_died(struct xhci_hcd *xhci);
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 4/4] usb: dwc3: Notify XHCI core of tunneled status
2026-09-01 10:05 [PATCH v3 0/4] DWC3 link tunneling state reporting Konrad Dybcio
` (2 preceding siblings ...)
2026-09-01 10:05 ` [PATCH v3 3/4] usb: xhci: Allow custom op for usb_link_tunnel_mode reporting Konrad Dybcio
@ 2026-09-01 10:05 ` Konrad Dybcio
2026-09-05 0:30 ` Thinh Nguyen
3 siblings, 1 reply; 9+ messages in thread
From: Konrad Dybcio @ 2026-09-01 10:05 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.
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/usb/dwc3/core.c | 16 ++++++++++++++++
drivers/usb/dwc3/core.h | 18 ++++++++++++++++++
drivers/usb/dwc3/host.c | 12 ++++++++++++
3 files changed, 46 insertions(+)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index fd5c2cd36c59..6af8937525ff 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -162,6 +162,22 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
}
EXPORT_SYMBOL_GPL(dwc3_set_prtcap);
+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;
+}
+
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..1b9c4f94c2fd 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -78,8 +78,20 @@ static void dwc3_xhci_plat_start(struct usb_hcd *hcd)
dwc3_enable_susphy(dwc, true);
}
+static enum usb_link_tunnel_mode dwc3_xhci_tunnel_mode(struct usb_hcd *hcd, int portnum)
+{
+ struct platform_device *pdev;
+ struct dwc3 *dwc;
+
+ pdev = to_platform_device(hcd->self.controller);
+ dwc = dev_get_drvdata(pdev->dev.parent);
+
+ return dwc3_link_tunnel_mode(dwc, portnum);
+}
+
static const struct xhci_plat_priv dwc3_xhci_plat_quirk = {
.plat_start = dwc3_xhci_plat_start,
+ .tunnel_mode = dwc3_xhci_tunnel_mode,
};
static void dwc3_host_fill_xhci_irq_res(struct dwc3 *dwc,
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v3 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value
2026-09-01 10:05 ` [PATCH v3 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value Konrad Dybcio
@ 2026-09-05 0:26 ` Thinh Nguyen
0 siblings, 0 replies; 9+ messages in thread
From: Thinh Nguyen @ 2026-09-05 0:26 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman,
linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
usb4-upstream@oss.qualcomm.com, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter, Konrad Dybcio
On Tue, Sep 01, 2026, Konrad Dybcio wrote:
> 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.
>
> 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
>
Reviewed-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Thanks,
Thinh
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 2/4] usb: xhci: Honor PORTSC.TM if valid
2026-09-01 10:05 ` [PATCH v3 2/4] usb: xhci: Honor PORTSC.TM if valid Konrad Dybcio
@ 2026-09-05 0:27 ` Thinh Nguyen
0 siblings, 0 replies; 9+ messages in thread
From: Thinh Nguyen @ 2026-09-05 0:27 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman,
linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
usb4-upstream@oss.qualcomm.com, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter, Konrad Dybcio
On Tue, Sep 01, 2026, Konrad Dybcio wrote:
> 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.
>
> 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
>
Reviewed-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Thanks,
Thinh
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 3/4] usb: xhci: Allow custom op for usb_link_tunnel_mode reporting
2026-09-01 10:05 ` [PATCH v3 3/4] usb: xhci: Allow custom op for usb_link_tunnel_mode reporting Konrad Dybcio
@ 2026-09-05 0:29 ` Thinh Nguyen
0 siblings, 0 replies; 9+ messages in thread
From: Thinh Nguyen @ 2026-09-05 0:29 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman,
linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
usb4-upstream@oss.qualcomm.com, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter, Konrad Dybcio
On Tue, Sep 01, 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.
>
> Currently, retrieving that information is only possibe on Intel XHCI
> hosts, through a vendor-specific capability. Extend xhci-plat to allow
> plumbing a custom one.
>
> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> ---
> drivers/usb/host/xhci-hub.c | 13 +++++++++----
> drivers/usb/host/xhci-plat.c | 2 ++
> drivers/usb/host/xhci-plat.h | 1 +
> drivers/usb/host/xhci.c | 3 ++-
> drivers/usb/host/xhci.h | 5 ++++-
> 5 files changed, 18 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
> index 80f07bd5081d..c585e4d02d84 100644
> --- a/drivers/usb/host/xhci-hub.c
> +++ b/drivers/usb/host/xhci-hub.c
> @@ -749,7 +749,7 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
> }
>
> /**
> - * xhci_port_is_tunneled() - Check if USB3 connection is tunneled over USB4
> + * xhci_port_tunnel_mode() - Check if USB3 connection is tunneled over USB4
> * @xhci: xhci host controller
> * @port: USB3 port to be checked.
> *
> @@ -763,7 +763,7 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
> * 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,
> +enum usb_link_tunnel_mode xhci_port_tunnel_mode(struct xhci_hcd *xhci,
> struct xhci_port *port)
> {
> struct usb_hcd *hcd;
> @@ -782,8 +782,13 @@ enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
> /* 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;
> + to_pci_dev(hcd->self.controller)->vendor != PCI_VENDOR_ID_INTEL) {
> + /* Last chance - if the controller has a custom tunnel_mode op, try that */
> + if (xhci->tunnel_mode)
> + return xhci->tunnel_mode(xhci_to_hcd(xhci), port->hcd_portnum);
> + else
> + return USB_LINK_UNKNOWN;
> + }
>
> base = &xhci->cap_regs->hc_capbase;
> offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);
> diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
> index 6fd595f81a30..c19bc5d7a7b5 100644
> --- a/drivers/usb/host/xhci-plat.c
> +++ b/drivers/usb/host/xhci-plat.c
> @@ -244,6 +244,8 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
> priv = hcd_to_xhci_priv(hcd);
> /* Just copy data for now */
> *priv = *priv_match;
> +
> + xhci->tunnel_mode = priv->tunnel_mode;
> }
>
> device_set_wakeup_capable(&pdev->dev, true);
> diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
> index 00751d851831..c5042766a486 100644
> --- a/drivers/usb/host/xhci-plat.h
> +++ b/drivers/usb/host/xhci-plat.h
> @@ -22,6 +22,7 @@ struct xhci_plat_priv {
> int (*suspend_quirk)(struct usb_hcd *);
> int (*resume_quirk)(struct usb_hcd *);
> int (*post_resume_quirk)(struct usb_hcd *);
> + enum usb_link_tunnel_mode (*tunnel_mode)(struct usb_hcd *hcd, int portnum);
> };
>
> #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..a7e3c46937fa 100644
> --- a/drivers/usb/host/xhci.c
> +++ b/drivers/usb/host/xhci.c
> @@ -4762,7 +4762,8 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
> if (hcd->speed >= HCD_USB3 && !udev->parent->parent) {
> port = xhci->usb3_rhub.ports[udev->portnum - 1];
>
> - udev->tunnel_mode = xhci_port_is_tunneled(xhci, port);
> + udev->tunnel_mode = xhci_port_tunnel_mode(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)
> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> index 480fcbac4525..2b5c0b304c6b 100644
> --- a/drivers/usb/host/xhci.h
> +++ b/drivers/usb/host/xhci.h
> @@ -1675,6 +1675,9 @@ struct xhci_hcd {
> struct list_head regset_list;
>
> void *dbc;
> +
> + enum usb_link_tunnel_mode (*tunnel_mode)(struct usb_hcd *hcd, int portnum);
> +
> /* platform-specific data -- must come last */
> unsigned long priv[] __aligned(sizeof(s64));
> };
> @@ -1981,7 +1984,7 @@ 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,
> +enum usb_link_tunnel_mode xhci_port_tunnel_mode(struct xhci_hcd *xhci,
> struct xhci_port *port);
> void xhci_hc_died(struct xhci_hcd *xhci);
>
>
> --
> 2.55.0
>
Reviewed-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Thanks,
Thinh
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 4/4] usb: dwc3: Notify XHCI core of tunneled status
2026-09-01 10:05 ` [PATCH v3 4/4] usb: dwc3: Notify XHCI core of tunneled status Konrad Dybcio
@ 2026-09-05 0:30 ` Thinh Nguyen
0 siblings, 0 replies; 9+ messages in thread
From: Thinh Nguyen @ 2026-09-05 0:30 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman,
linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
usb4-upstream@oss.qualcomm.com, Raghavendra Thoorpu,
Mika Westerberg, Sven Peter, Konrad Dybcio
On Tue, Sep 01, 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.
>
> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> ---
> drivers/usb/dwc3/core.c | 16 ++++++++++++++++
> drivers/usb/dwc3/core.h | 18 ++++++++++++++++++
> drivers/usb/dwc3/host.c | 12 ++++++++++++
> 3 files changed, 46 insertions(+)
>
> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> index fd5c2cd36c59..6af8937525ff 100644
> --- a/drivers/usb/dwc3/core.c
> +++ b/drivers/usb/dwc3/core.c
> @@ -162,6 +162,22 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
> }
> EXPORT_SYMBOL_GPL(dwc3_set_prtcap);
>
> +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;
> +}
> +
> 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..1b9c4f94c2fd 100644
> --- a/drivers/usb/dwc3/host.c
> +++ b/drivers/usb/dwc3/host.c
> @@ -78,8 +78,20 @@ static void dwc3_xhci_plat_start(struct usb_hcd *hcd)
> dwc3_enable_susphy(dwc, true);
> }
>
> +static enum usb_link_tunnel_mode dwc3_xhci_tunnel_mode(struct usb_hcd *hcd, int portnum)
> +{
> + struct platform_device *pdev;
> + struct dwc3 *dwc;
> +
> + pdev = to_platform_device(hcd->self.controller);
> + dwc = dev_get_drvdata(pdev->dev.parent);
> +
> + return dwc3_link_tunnel_mode(dwc, portnum);
> +}
> +
> static const struct xhci_plat_priv dwc3_xhci_plat_quirk = {
> .plat_start = dwc3_xhci_plat_start,
> + .tunnel_mode = dwc3_xhci_tunnel_mode,
> };
>
> static void dwc3_host_fill_xhci_irq_res(struct dwc3 *dwc,
>
> --
> 2.55.0
>
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Thanks,
Thinh
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-09-05 0:30 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-01 10:05 [PATCH v3 0/4] DWC3 link tunneling state reporting Konrad Dybcio
2026-09-01 10:05 ` [PATCH v3 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value Konrad Dybcio
2026-09-05 0:26 ` Thinh Nguyen
2026-09-01 10:05 ` [PATCH v3 2/4] usb: xhci: Honor PORTSC.TM if valid Konrad Dybcio
2026-09-05 0:27 ` Thinh Nguyen
2026-09-01 10:05 ` [PATCH v3 3/4] usb: xhci: Allow custom op for usb_link_tunnel_mode reporting Konrad Dybcio
2026-09-05 0:29 ` Thinh Nguyen
2026-09-01 10:05 ` [PATCH v3 4/4] usb: dwc3: Notify XHCI core of tunneled status Konrad Dybcio
2026-09-05 0:30 ` Thinh Nguyen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).