All of lore.kernel.org
 help / color / mirror / Atom feed
* [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; 11+ 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] 11+ 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; 11+ 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 related	[flat|nested] 11+ 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; 11+ 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 related	[flat|nested] 11+ 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; 11+ 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 related	[flat|nested] 11+ 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; 11+ 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 related	[flat|nested] 11+ 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
  2026-10-13 16:25     ` Konrad Dybcio
  0 siblings, 1 reply; 11+ 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@vger.kernel.org, linux-kernel@vger.kernel.org,
	usb4-upstream@oss.qualcomm.com, 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] 11+ 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
  2026-10-20 14:41     ` Konrad Dybcio
  0 siblings, 1 reply; 11+ 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@vger.kernel.org, linux-kernel@vger.kernel.org,
	usb4-upstream@oss.qualcomm.com, 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] 11+ messages in thread

* Re: [PATCH v4 3/4] usb: xhci: Use update_device overrides for tunnel reporting
  2026-10-03  0:30   ` Thinh Nguyen
@ 2026-10-13 16:25     ` Konrad Dybcio
  2026-10-21  1:41       ` Thinh Nguyen
  0 siblings, 1 reply; 11+ messages in thread
From: Konrad Dybcio @ 2026-10-13 16:25 UTC (permalink / raw)
  To: Thinh Nguyen, Konrad Dybcio
  Cc: 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

On 10/3/26 2:30 AM, Thinh Nguyen wrote:
> 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>
>> ---

[...]

>>  #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?

It's like that in the original code, before moving xhci_update_device()
out of its current place

Konrad

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v4 4/4] usb: dwc3: Notify XHCI core of tunneled status
  2026-10-03  1:23   ` Thinh Nguyen
@ 2026-10-20 14:41     ` Konrad Dybcio
  2026-10-21  1:48       ` Thinh Nguyen
  0 siblings, 1 reply; 11+ messages in thread
From: Konrad Dybcio @ 2026-10-20 14:41 UTC (permalink / raw)
  To: Thinh Nguyen, Konrad Dybcio, Krzysztof Kozlowski
  Cc: 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

On 10/3/26 3:23 AM, Thinh Nguyen wrote:
> 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>
>> ---

[...]

>> +	/* 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)

We have both CIO and non-CIO hosts with the exact same compatible
strings on the SoCs that support it :/

Ack to other comments. If we need to wait for this dt property,
would you be open to getting patches 1-3 merged in meantime?

Or maybe we can lean on the fact that there exist dwc3+ACPI devices
that could consume this property too and sneak by ;)

Konrad

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v4 3/4] usb: xhci: Use update_device overrides for tunnel reporting
  2026-10-13 16:25     ` Konrad Dybcio
@ 2026-10-21  1:41       ` Thinh Nguyen
  0 siblings, 0 replies; 11+ messages in thread
From: Thinh Nguyen @ 2026-10-21  1:41 UTC (permalink / raw)
  To: Konrad Dybcio
  Cc: Thinh Nguyen, Konrad Dybcio, 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

On Tue, Oct 13, 2026, Konrad Dybcio wrote:
> On 10/3/26 2:30 AM, Thinh Nguyen wrote:
> > 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>
> >> ---
> 
> [...]
> 
> >>  #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?
> 
> It's like that in the original code, before moving xhci_update_device()
> out of its current place
> 

Ok. But tunnel detection isn't really PM-specific. With this change
the vendor overrides run regardless of CONFIG_PM. This can be fixed
later in a separate patch.

Reviewed-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>

Thanks,
Thinh

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v4 4/4] usb: dwc3: Notify XHCI core of tunneled status
  2026-10-20 14:41     ` Konrad Dybcio
@ 2026-10-21  1:48       ` Thinh Nguyen
  0 siblings, 0 replies; 11+ messages in thread
From: Thinh Nguyen @ 2026-10-21  1:48 UTC (permalink / raw)
  To: Konrad Dybcio
  Cc: Thinh Nguyen, Konrad Dybcio, Krzysztof Kozlowski,
	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

On Tue, Oct 20, 2026, Konrad Dybcio wrote:
> On 10/3/26 3:23 AM, Thinh Nguyen wrote:
> > 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>
> >> ---
> 
> [...]
> 
> >> +	/* 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)
> 
> We have both CIO and non-CIO hosts with the exact same compatible
> strings on the SoCs that support it :/

Ok.

> 
> Ack to other comments. If we need to wait for this dt property,
> would you be open to getting patches 1-3 merged in meantime?

I'm fine with 1-3 going in first, pending on Mathias's review.

> 
> Or maybe we can lean on the fact that there exist dwc3+ACPI devices
> that could consume this property too and sneak by ;)
> 

I'd prefer to wait for the DT property to be accepted first, since the
binding is still under discussion. I'd rather not use the ACPI case as
justification unless there's an actual user.

Thanks,
Thinh

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-10-21  1:48 UTC | newest]

Thread overview: 11+ 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-10-13 16:25     ` Konrad Dybcio
2026-10-21  1:41       ` 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
2026-10-20 14:41     ` Konrad Dybcio
2026-10-21  1:48       ` Thinh Nguyen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.