mirror of https://lore.kernel.org/lkml/
 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; 7+ messages in thread
From: Konrad Dybcio @ 2026-09-29 15:54 UTC (permalink / raw)
  To: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman
  Cc: linux-usb, linux-kernel, usb4-upstream, Raghavendra Thoorpu,
	Mika Westerberg, Sven Peter, Konrad Dybcio

Currently, retrieving the tunneled/native state of a running port is
only possible on Intel XHCI hosts. This series extends that to CIO-
capable DWC31 controllers (see [1]).

Note that this patchset only results in the correct udev->tunnel_mode
value being assigned. Creating the device links (which requires this
information) between the USB4 router and the USB3 controller does not
currently take place on DT-based platforms, but that will be amended
in a follow-up patchset.

Tested on a Qualcomm X1E80100 CRD board, with a (currently) out-of-tree
set of drivers.

[1] I'm not entirely sure about the DWC version range (particularly
    wrt DWC32 and newer), so I limited it to a reasonable subset. I'll
    happily amend that.

Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
Changes in v4:
- Rework the check to use .update_device (Mathias)
- Drop tags for the top 2 comments accordingly
- Link to v3: https://patch.msgid.link/20260901-topic-dwc3_tunneling_state-v3-0-22fdced8de55@oss.qualcomm.com

Changes in v3:
- Report USB_LINK_UNKNOWN instead of NATIVE if the DWC3 controller
  seems not to support USB4
- Cache the capability of reporting tunneling mode instead of checking
  it every single time
- Link to v2: https://patch.msgid.link/20260715-topic-dwc3_tunneling_state-v2-0-026487a08119@oss.qualcomm.com

Changes in v2:
- Add handling for XHCI v1.2's USB3 tunneling and PORTSC.TM for
  platforms that advertise it (!!! UNTESTED !!!, I have no access to
  HW that exposes this)
- Prefer the aforementioned cap over custom implementations
- Bail out of the DWC3-specific check if `usb4-host-interface` device
  property is absent - hence this now depends on:
  https://github.com/devicetree-org/dt-schema/pull/196

- Link to v1: https://patch.msgid.link/20260505-topic-dwc3_tunneling_state-v1-0-4aaa6c3c14cb@oss.qualcomm.com

To: Mathias Nyman <mathias.nyman@intel.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Cc: linux-usb@vger.kernel.org
Cc: linux-kernel@vger.kernel.org

---
Konrad Dybcio (4):
      usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value
      usb: xhci: Honor PORTSC.TM if valid
      usb: xhci: Use update_device overrides for tunnel reporting
      usb: dwc3: Notify XHCI core of tunneled status

 drivers/usb/dwc3/core.c          | 18 ++++++++++++++++
 drivers/usb/dwc3/core.h          | 18 ++++++++++++++++
 drivers/usb/dwc3/host.c          | 14 +++++++++++++
 drivers/usb/host/xhci-debugfs.c  |  9 ++++++++
 drivers/usb/host/xhci-debugfs.h  |  2 ++
 drivers/usb/host/xhci-ext-caps.h |  4 ++++
 drivers/usb/host/xhci-hub.c      | 44 ----------------------------------------
 drivers/usb/host/xhci-pci.c      | 38 ++++++++++++++++++++++++++++++++++
 drivers/usb/host/xhci-plat.c     | 18 ++++++++++++++++
 drivers/usb/host/xhci-plat.h     |  2 ++
 drivers/usb/host/xhci-port.h     |  3 ++-
 drivers/usb/host/xhci.c          | 27 ++++++++++++++++--------
 drivers/usb/host/xhci.h          |  9 ++++++--
 13 files changed, 151 insertions(+), 55 deletions(-)
---
base-commit: f5f84daefcd92d7a630066635ecea1433ed5eac7
change-id: 20260504-topic-dwc3_tunneling_state-50588c0e1ae3

Best regards,
--  
Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>


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

* [PATCH v4 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value
  2026-09-29 15:54 [PATCH v4 0/4] DWC3 link tunneling state reporting Konrad Dybcio
@ 2026-09-29 15:54 ` Konrad Dybcio
  2026-09-29 15:54 ` [PATCH v4 2/4] usb: xhci: Honor PORTSC.TM if valid Konrad Dybcio
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Konrad Dybcio @ 2026-09-29 15:54 UTC (permalink / raw)
  To: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman
  Cc: linux-usb, linux-kernel, usb4-upstream, Raghavendra Thoorpu,
	Mika Westerberg, Sven Peter, Konrad Dybcio

From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

The USB3 Tunneling Support Capability (idx 18) features a single
register. Expose it over debugfs, if supported by the controller.

Reviewed-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
 drivers/usb/host/xhci-debugfs.c  | 9 +++++++++
 drivers/usb/host/xhci-debugfs.h  | 2 ++
 drivers/usb/host/xhci-ext-caps.h | 1 +
 3 files changed, 12 insertions(+)

diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index 2aa01d99f23d..f4bb66a9845a 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -82,6 +82,10 @@ static const struct debugfs_reg32 xhci_extcap_dbc[] = {
 	dump_register(EXTCAP_DBC_DEVINFO2),
 };
 
+static const struct debugfs_reg32 xhci_extcap_usb3_tunneling[] = {
+	dump_register(EXTCAP_USB3_TUNNELING),
+};
+
 static struct dentry *xhci_debugfs_root;
 
 static struct xhci_regset *xhci_debugfs_alloc_regset(struct xhci_hcd *xhci)
@@ -816,6 +820,11 @@ void xhci_debugfs_init(struct xhci_hcd *xhci)
 				   ARRAY_SIZE(xhci_extcap_dbc),
 				   "reg-ext-dbc");
 
+	xhci_debugfs_extcap_regset(xhci, XHCI_EXT_CAPS_USB3_TUNNELING,
+				   xhci_extcap_usb3_tunneling,
+				   ARRAY_SIZE(xhci_extcap_usb3_tunneling),
+				   "reg-usb3-tunneling");
+
 	xhci_debugfs_create_ring_dir(xhci, &xhci->cmd_ring,
 				     "command-ring",
 				     xhci->debugfs_root);
diff --git a/drivers/usb/host/xhci-debugfs.h b/drivers/usb/host/xhci-debugfs.h
index 7c074b4be819..ca6b7f2da8e3 100644
--- a/drivers/usb/host/xhci-debugfs.h
+++ b/drivers/usb/host/xhci-debugfs.h
@@ -70,6 +70,8 @@
 #define REG_EXTCAP_DBC_DEVINFO1				0x38
 #define REG_EXTCAP_DBC_DEVINFO2				0x3c
 
+#define REG_EXTCAP_USB3_TUNNELING			0x00
+
 #define dump_register(nm)				\
 {							\
 	.name	= __stringify(nm),			\
diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
index 67ecf7320c62..22e53a750e2a 100644
--- a/drivers/usb/host/xhci-ext-caps.h
+++ b/drivers/usb/host/xhci-ext-caps.h
@@ -40,6 +40,7 @@
 #define XHCI_EXT_CAPS_ROUTE	5
 /* IDs 6-9 reserved */
 #define XHCI_EXT_CAPS_DEBUG	10
+#define XHCI_EXT_CAPS_USB3_TUNNELING	18
 /* Vendor caps */
 #define XHCI_EXT_CAPS_VENDOR_INTEL	192
 #define XHCI_EXT_CAPS_INTEL_SPR_SHADOW	206

-- 
2.55.0


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

* [PATCH v4 2/4] usb: xhci: Honor PORTSC.TM if valid
  2026-09-29 15:54 [PATCH v4 0/4] DWC3 link tunneling state reporting Konrad Dybcio
  2026-09-29 15:54 ` [PATCH v4 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value Konrad Dybcio
@ 2026-09-29 15:54 ` Konrad Dybcio
  2026-09-29 15:54 ` [PATCH v4 3/4] usb: xhci: Use update_device overrides for tunnel reporting Konrad Dybcio
  2026-09-29 15:54 ` [PATCH v4 4/4] usb: dwc3: Notify XHCI core of tunneled status Konrad Dybcio
  3 siblings, 0 replies; 7+ messages in thread
From: Konrad Dybcio @ 2026-09-29 15:54 UTC (permalink / raw)
  To: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman
  Cc: linux-usb, linux-kernel, usb4-upstream, Raghavendra Thoorpu,
	Mika Westerberg, Sven Peter, Konrad Dybcio

From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Currently, the only way for the USB core to determine whether the link
is native or tunneled is via an Intel vendor-specific Extended
Capability.

The XHCI specification v1.2 and newer expose a common ext_cap,
indicating whether BIT(2) of PORTSC can be interpreted as the tunneling
status.

Make use of that and fall back to the existing Intel-specific path if
the ext_cap is absent.

Reviewed-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
 drivers/usb/host/xhci-ext-caps.h |  3 +++
 drivers/usb/host/xhci-hub.c      | 11 ++++++++++-
 drivers/usb/host/xhci-port.h     |  3 ++-
 drivers/usb/host/xhci.c          |  8 ++++++++
 drivers/usb/host/xhci.h          |  1 +
 5 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
index 22e53a750e2a..af7d624cd38e 100644
--- a/drivers/usb/host/xhci-ext-caps.h
+++ b/drivers/usb/host/xhci-ext-caps.h
@@ -66,6 +66,9 @@
 #define XHCI_HLC               (1 << 19)
 #define XHCI_BLC               (1 << 20)
 
+/* USB3 tunneling support capability - section 7.11 */
+#define XHCI_USB3_TUNNELING_SUPPORTED	BIT(16)
+
 /* Intel SPR shadow capability */
 #define XHCI_INTEL_SPR_ESS_PORT_OFFSET  0x8ac4	/* SuperSpeed port control */
 #define XHCI_INTEL_SPR_TUNEN	BIT(4)		/* Tunnel mode enabled */
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 470bafe1802b..80f07bd5081d 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -769,8 +769,17 @@ enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
 	struct usb_hcd *hcd;
 	void __iomem *base;
 	u32 offset;
+	u32 val;
 
-	/* Don't try and probe this capability for non-Intel hosts */
+	if (xhci->portsc_tunnel_reporting) {
+		val = xhci_portsc_readl(port);
+		if (val & PORT_TM)
+			return USB_LINK_TUNNELED;
+
+		return USB_LINK_NATIVE;
+	}
+
+	/* Fall back to the legacy Intel-specific ext_cap */
 	hcd = xhci_to_hcd(xhci);
 	if (!dev_is_pci(hcd->self.controller) ||
 	    to_pci_dev(hcd->self.controller)->vendor != PCI_VENDOR_ID_INTEL)
diff --git a/drivers/usb/host/xhci-port.h b/drivers/usb/host/xhci-port.h
index 889b5fb0fcd8..215c9cadbc34 100644
--- a/drivers/usb/host/xhci-port.h
+++ b/drivers/usb/host/xhci-port.h
@@ -5,7 +5,8 @@
 #define PORT_CONNECT	(1 << 0)
 /* true: port enabled */
 #define PORT_PE		(1 << 1)
-/* bit 2 reserved and zeroed */
+/* true: port is tunneling traffic over USB4 */
+#define PORT_TM		(1 << 2)
 /* true: port has an over-current condition */
 #define PORT_OC		(1 << 3)
 /* true: port reset signaling asserted */
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index a9e47e178c28..5fb78613a993 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -5433,9 +5433,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
 	 * quirks
 	 */
 	struct device		*dev = hcd->self.sysdev;
+	void __iomem		*base;
 	int			retval;
 	u32			hcs_params1;
 	u32			hc_capbase;
+	u32			tunnel_cap;
 
 	/* Accept arbitrarily long scatter-gather lists */
 	hcd->self.sg_tablesize = ~0;
@@ -5456,6 +5458,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
 	mutex_init(&xhci->mutex);
 	xhci->main_hcd = hcd;
 	xhci->cap_regs = hcd->regs;
+	base = &xhci->cap_regs->hc_capbase;
 	hc_capbase = readl(&xhci->cap_regs->hc_capbase);
 	if (hc_capbase == U32_MAX) {
 		xhci_warn(xhci, "Host controller not accessible, removed?\n");
@@ -5473,6 +5476,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
 	xhci->hcc_params = readl(&xhci->cap_regs->hcc_params);
 	if (xhci->hci_version > 0x100)
 		xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
+	tunnel_cap = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_USB3_TUNNELING);
+	if (tunnel_cap) {
+		xhci->portsc_tunnel_reporting =
+			!!(readl(base + tunnel_cap) & XHCI_USB3_TUNNELING_SUPPORTED);
+	}
 
 	xhci->dma_mask_bits = 64;
 	xhci->max_slots = min(FIELD_GET(HCS_SLOTS_MASK, hcs_params1), MAX_HC_SLOTS);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index c7bfa7f028d3..480fcbac4525 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1662,6 +1662,7 @@ struct xhci_hcd {
 	/* cached extended protocol port capabilities */
 	struct xhci_port_cap	*port_caps;
 	unsigned int		num_port_caps;
+	bool			portsc_tunnel_reporting;
 	/* Compliance Mode Recovery Data */
 	struct timer_list	comp_mode_recovery_timer;
 	u32			port_status_u0;

-- 
2.55.0


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

* [PATCH v4 3/4] usb: xhci: Use update_device overrides for tunnel reporting
  2026-09-29 15:54 [PATCH v4 0/4] DWC3 link tunneling state reporting Konrad Dybcio
  2026-09-29 15:54 ` [PATCH v4 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value Konrad Dybcio
  2026-09-29 15:54 ` [PATCH v4 2/4] usb: xhci: Honor PORTSC.TM if valid Konrad Dybcio
@ 2026-09-29 15:54 ` Konrad Dybcio
  2026-10-03  0:30   ` Thinh Nguyen
  2026-09-29 15:54 ` [PATCH v4 4/4] usb: dwc3: Notify XHCI core of tunneled status Konrad Dybcio
  3 siblings, 1 reply; 7+ messages in thread
From: Konrad Dybcio @ 2026-09-29 15:54 UTC (permalink / raw)
  To: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman
  Cc: linux-usb, linux-kernel, usb4-upstream, Raghavendra Thoorpu,
	Mika Westerberg, Sven Peter, Konrad Dybcio

From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

The Thunderbolt framework relies on USB core device links for tunneled
ports, so it needs tunnel state when xHCI updates a device.

Use the .update_device override for controller-specific tunnel
reporting.

Assisted-by: LLM
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
 drivers/usb/host/xhci-hub.c  | 53 --------------------------------------------
 drivers/usb/host/xhci-pci.c  | 38 +++++++++++++++++++++++++++++++
 drivers/usb/host/xhci-plat.c | 18 +++++++++++++++
 drivers/usb/host/xhci-plat.h |  2 ++
 drivers/usb/host/xhci.c      | 19 +++++++++-------
 drivers/usb/host/xhci.h      |  8 +++++--
 6 files changed, 75 insertions(+), 63 deletions(-)

diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 80f07bd5081d..31ba7a990e07 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -12,7 +12,6 @@
 #include <linux/slab.h>
 #include <linux/unaligned.h>
 #include <linux/bitfield.h>
-#include <linux/pci.h>
 
 #include "xhci.h"
 #include "xhci-trace.h"
@@ -748,58 +747,6 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
 	return xhci_reset(xhci, XHCI_RESET_SHORT_USEC);
 }
 
-/**
- * xhci_port_is_tunneled() - Check if USB3 connection is tunneled over USB4
- * @xhci: xhci host controller
- * @port: USB3 port to be checked.
- *
- * Some hosts can detect if a USB3 connection is native USB3 or tunneled over
- * USB4. Intel hosts expose this via vendor specific extended capability 206
- * eSS PORT registers TUNEN (tunnel enabled) bit.
- *
- * A USB3 device must be connected to the port to detect the tunnel.
- *
- * Return: link tunnel mode enum, USB_LINK_UNKNOWN if host is incapable of
- * detecting USB3 over USB4 tunnels. USB_LINK_NATIVE or USB_LINK_TUNNELED
- * otherwise.
- */
-enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
-						struct xhci_port *port)
-{
-	struct usb_hcd *hcd;
-	void __iomem *base;
-	u32 offset;
-	u32 val;
-
-	if (xhci->portsc_tunnel_reporting) {
-		val = xhci_portsc_readl(port);
-		if (val & PORT_TM)
-			return USB_LINK_TUNNELED;
-
-		return USB_LINK_NATIVE;
-	}
-
-	/* Fall back to the legacy Intel-specific ext_cap */
-	hcd = xhci_to_hcd(xhci);
-	if (!dev_is_pci(hcd->self.controller) ||
-	    to_pci_dev(hcd->self.controller)->vendor != PCI_VENDOR_ID_INTEL)
-		return USB_LINK_UNKNOWN;
-
-	base = &xhci->cap_regs->hc_capbase;
-	offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);
-
-	if (offset && offset <= XHCI_INTEL_SPR_ESS_PORT_OFFSET) {
-		offset = XHCI_INTEL_SPR_ESS_PORT_OFFSET + port->hcd_portnum * 0x20;
-
-		if (readl(base + offset) & XHCI_INTEL_SPR_TUNEN)
-			return USB_LINK_TUNNELED;
-		else
-			return USB_LINK_NATIVE;
-	}
-
-	return USB_LINK_UNKNOWN;
-}
-
 void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
 			 u32 link_state)
 {
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index a8889081ae82..61ac267bc84a 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -103,11 +103,13 @@ static int xhci_pci_setup(struct usb_hcd *hcd);
 static int xhci_pci_run(struct usb_hcd *hcd);
 static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
 				      struct usb_tt *tt, gfp_t mem_flags);
+static int xhci_pci_update_device(struct usb_hcd *hcd, struct usb_device *udev);
 
 static const struct xhci_driver_overrides xhci_pci_overrides __initconst = {
 	.reset = xhci_pci_setup,
 	.start = xhci_pci_run,
 	.update_hub_device = xhci_pci_update_hub_device,
+	.update_device = xhci_pci_update_device,
 };
 
 /*
@@ -606,6 +608,42 @@ static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hd
 	return xhci_update_hub_device(hcd, hdev, tt, mem_flags);
 }
 
+static void xhci_intel_port_is_tunneled(struct usb_hcd *hcd,
+					struct usb_device *udev)
+{
+	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
+	struct xhci_port *port;
+	void __iomem *base;
+	u32 offset;
+
+	if (xhci->portsc_tunnel_reporting || udev->parent->parent)
+		return;
+
+	port = xhci->usb3_rhub.ports[udev->portnum - 1];
+
+	base = &xhci->cap_regs->hc_capbase;
+	offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);
+
+	if (offset && offset <= XHCI_INTEL_SPR_ESS_PORT_OFFSET) {
+		offset = XHCI_INTEL_SPR_ESS_PORT_OFFSET + port->hcd_portnum * 0x20;
+
+		if (readl(base + offset) & XHCI_INTEL_SPR_TUNEN)
+			udev->tunnel_mode = USB_LINK_TUNNELED;
+		else
+			udev->tunnel_mode = USB_LINK_NATIVE;
+	}
+}
+
+static int xhci_pci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+	struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+
+	if (pdev->vendor == PCI_VENDOR_ID_INTEL && hcd->speed >= HCD_USB3)
+		xhci_intel_port_is_tunneled(hcd, udev);
+
+	return xhci_update_device(hcd, udev);
+}
+
 /*
  * We need to register our own PCI probe function (instead of the USB core's
  * function) in order to create a second roothub under xHCI.
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 6fd595f81a30..641e31976240 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -30,13 +30,31 @@ static struct hc_driver __read_mostly xhci_plat_hc_driver;
 
 static int xhci_plat_setup(struct usb_hcd *hcd);
 static int xhci_plat_start(struct usb_hcd *hcd);
+static int xhci_plat_update_device(struct usb_hcd *hcd, struct usb_device *udev);
 
 static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
 	.extra_priv_size = sizeof(struct xhci_plat_priv),
 	.reset = xhci_plat_setup,
 	.start = xhci_plat_start,
+	.update_device = xhci_plat_update_device,
 };
 
+static int xhci_plat_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+	struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
+	int ret;
+
+	if (!xhci->portsc_tunnel_reporting && priv->update_device &&
+	    hcd->speed >= HCD_USB3 && !udev->parent->parent) {
+		ret = priv->update_device(hcd, udev);
+		if (ret)
+			return ret;
+	}
+
+	return xhci_update_device(hcd, udev);
+}
+
 static void xhci_priv_plat_start(struct usb_hcd *hcd)
 {
 	struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
index 00751d851831..91333605c46b 100644
--- a/drivers/usb/host/xhci-plat.h
+++ b/drivers/usb/host/xhci-plat.h
@@ -10,6 +10,7 @@
 
 struct device;
 struct platform_device;
+struct usb_device;
 struct usb_hcd;
 
 struct xhci_plat_priv {
@@ -22,6 +23,7 @@ struct xhci_plat_priv {
 	int (*suspend_quirk)(struct usb_hcd *);
 	int (*resume_quirk)(struct usb_hcd *);
 	int (*post_resume_quirk)(struct usb_hcd *);
+	int (*update_device)(struct usb_hcd *hcd, struct usb_device *udev);
 };
 
 #define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 5fb78613a993..b252ce5e8499 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4752,7 +4752,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
 	return 0;
 }
 
-static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
 {
 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
 	struct xhci_port *port;
@@ -4760,9 +4760,14 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
 
 	/* Check if USB3 device at root port is tunneled over USB4 */
 	if (hcd->speed >= HCD_USB3 && !udev->parent->parent) {
-		port = xhci->usb3_rhub.ports[udev->portnum - 1];
+		if (xhci->portsc_tunnel_reporting) {
+			port = xhci->usb3_rhub.ports[udev->portnum - 1];
+			if (xhci_portsc_readl(port) & PORT_TM)
+				udev->tunnel_mode = USB_LINK_TUNNELED;
+			else
+				udev->tunnel_mode = USB_LINK_NATIVE;
+		}
 
-		udev->tunnel_mode = xhci_port_is_tunneled(xhci, port);
 		if (udev->tunnel_mode == USB_LINK_UNKNOWN)
 			dev_dbg(&udev->dev, "link tunnel state unknown\n");
 		else if (udev->tunnel_mode == USB_LINK_TUNNELED)
@@ -4793,6 +4798,7 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(xhci_update_device);
 
 /*---------------------- USB 3.0 Link PM functions ------------------------*/
 
@@ -5242,11 +5248,6 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
 	return 0;
 }
 
-static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
-{
-	return 0;
-}
-
 static int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd,
 			struct usb_device *udev, enum usb3_link_state state)
 {
@@ -5695,6 +5696,8 @@ void xhci_init_driver(struct hc_driver *drv,
 			drv->update_hub_device = over->update_hub_device;
 		if (over->hub_control)
 			drv->hub_control = over->hub_control;
+		if (over->update_device)
+			drv->update_device = over->update_device;
 	}
 }
 EXPORT_SYMBOL_GPL(xhci_init_driver);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 480fcbac4525..f12a309104dc 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1694,6 +1694,7 @@ struct xhci_driver_overrides {
 			    struct usb_tt *tt, gfp_t mem_flags);
 	int (*hub_control)(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 			   u16 wIndex, char *buf, u16 wLength);
+	int (*update_device)(struct usb_hcd *hcd, struct usb_device *udev);
 };
 
 #define	XHCI_CFC_DELAY		10
@@ -1981,15 +1982,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
 int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
 int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
 struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
-enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
-						struct xhci_port *port);
 void xhci_hc_died(struct xhci_hcd *xhci);
 
 #ifdef CONFIG_PM
+int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev);
 int xhci_bus_suspend(struct usb_hcd *hcd);
 int xhci_bus_resume(struct usb_hcd *hcd);
 unsigned long xhci_get_resuming_ports(struct usb_hcd *hcd);
 #else
+static inline int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+	return 0;
+}
 #define	xhci_bus_suspend	NULL
 #define	xhci_bus_resume		NULL
 #define	xhci_get_resuming_ports	NULL

-- 
2.55.0


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

* [PATCH v4 4/4] usb: dwc3: Notify XHCI core of tunneled status
  2026-09-29 15:54 [PATCH v4 0/4] DWC3 link tunneling state reporting Konrad Dybcio
                   ` (2 preceding siblings ...)
  2026-09-29 15:54 ` [PATCH v4 3/4] usb: xhci: Use update_device overrides for tunnel reporting Konrad Dybcio
@ 2026-09-29 15:54 ` Konrad Dybcio
  2026-10-03  1:23   ` Thinh Nguyen
  3 siblings, 1 reply; 7+ messages in thread
From: Konrad Dybcio @ 2026-09-29 15:54 UTC (permalink / raw)
  To: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman
  Cc: linux-usb, linux-kernel, usb4-upstream, Raghavendra Thoorpu,
	Mika Westerberg, Sven Peter, Konrad Dybcio

From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

The Thunderbolt framework relies on the USB core to create device links
for tunneled ports, so that the USB3 controller is only kept
runtime-resumed for the duration of the tunneling. This depends on
first knowing whether a connection is tunneled or native.

Add the logic to handle that for DWC3 controllers.

Assisted-by: LLM
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
 drivers/usb/dwc3/core.c | 18 ++++++++++++++++++
 drivers/usb/dwc3/core.h | 18 ++++++++++++++++++
 drivers/usb/dwc3/host.c | 14 ++++++++++++++
 3 files changed, 50 insertions(+)

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index fd5c2cd36c59..4eb430f58983 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -162,6 +162,24 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
 }
 EXPORT_SYMBOL_GPL(dwc3_set_prtcap);
 
+#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
+enum usb_link_tunnel_mode dwc3_link_tunnel_mode(struct dwc3 *dwc, u8 port)
+{
+	/* Prior versions had no CIO support */
+	if (!DWC3_VER_IS_WITHIN(DWC31, 191A, ANY))
+		return USB_LINK_UNKNOWN;
+
+	/* Not all DWC3 instances have CIO HW, trust the platform firmware */
+	if (!device_property_present(dwc->dev, "usb4-host-interface"))
+		return USB_LINK_UNKNOWN;
+
+	if (dwc3_readl(dwc, DWC3_CIOCTRL(port)) & DWC3_CIOCTRL_CIO_EN)
+		return USB_LINK_TUNNELED;
+
+	return USB_LINK_NATIVE;
+}
+#endif
+
 static void __dwc3_set_mode(struct work_struct *work)
 {
 	struct dwc3 *dwc = work_to_dwc(work);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 608daeb7ef10..decc51cd6505 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -179,6 +179,11 @@
 #define DWC3_OEVTEN		0xcc0C
 #define DWC3_OSTS		0xcc10
 
+/* CIO regs */
+#define DWC3_CIO_BASE(n)	(0xcd20 + ((n) * 0x30))
+#define DWC3_CIOCTRL(n)		(DWC3_CIO_BASE(n) + 0x00)
+#define DWC3_CIOCTRL_CIO_EN	BIT(0)
+
 #define DWC3_LLUCTL(n)		(0xd024 + ((n) * 0x80))
 
 /* Bit fields */
@@ -1314,6 +1319,7 @@ struct dwc3 {
 #define DWC31_REVISION_170A	0x3137302a
 #define DWC31_REVISION_180A	0x3138302a
 #define DWC31_REVISION_190A	0x3139302a
+#define DWC31_REVISION_191A	0x3139312a
 #define DWC31_REVISION_200A	0x3230302a
 
 #define DWC32_REVISION_ANY	0x0
@@ -1658,11 +1664,23 @@ static inline void dwc3_pre_run_stop(struct dwc3 *dwc, bool is_on)
 #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
 int dwc3_host_init(struct dwc3 *dwc);
 void dwc3_host_exit(struct dwc3 *dwc);
+
+/**
+ * dwc3_link_tunnel_mode - Check whether the link is tunneled over TBT/USB4
+ * @dwc: Pointer to DWC3 controller context
+ * @port: 0-based port index
+ *
+ * Returns: USB_LINK_TUNNELED if tunneled, USB_LINK_NATIVE if not, or
+ *          when the controller does not have USB4 capabilities.
+ */
+enum usb_link_tunnel_mode dwc3_link_tunnel_mode(struct dwc3 *dwc, u8 port);
 #else
 static inline int dwc3_host_init(struct dwc3 *dwc)
 { return 0; }
 static inline void dwc3_host_exit(struct dwc3 *dwc)
 { }
+static inline enum usb_link_tunnel_mode dwc3_link_tunnel_mode(struct dwc3 *dwc, u8 port)
+{ return USB_LINK_UNKNOWN; }
 #endif
 
 #if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index c5674161b2b0..a97dbf9cd27a 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -78,8 +78,22 @@ static void dwc3_xhci_plat_start(struct usb_hcd *hcd)
 	dwc3_enable_susphy(dwc, true);
 }
 
+static int dwc3_xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+	struct platform_device *pdev;
+	struct dwc3 *dwc;
+
+	pdev = to_platform_device(hcd->self.controller);
+	dwc = dev_get_drvdata(pdev->dev.parent);
+
+	udev->tunnel_mode = dwc3_link_tunnel_mode(dwc, udev->portnum - 1);
+
+	return 0;
+}
+
 static const struct xhci_plat_priv dwc3_xhci_plat_quirk = {
 	.plat_start = dwc3_xhci_plat_start,
+	.update_device = dwc3_xhci_update_device,
 };
 
 static void dwc3_host_fill_xhci_irq_res(struct dwc3 *dwc,

-- 
2.55.0


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

* Re: [PATCH v4 3/4] usb: xhci: Use update_device overrides for tunnel reporting
  2026-09-29 15:54 ` [PATCH v4 3/4] usb: xhci: Use update_device overrides for tunnel reporting Konrad Dybcio
@ 2026-10-03  0:30   ` Thinh Nguyen
  0 siblings, 0 replies; 7+ messages in thread
From: Thinh Nguyen @ 2026-10-03  0:30 UTC (permalink / raw)
  To: Konrad Dybcio
  Cc: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman, linux-usb,
	linux-kernel, usb4-upstream, Raghavendra Thoorpu,
	Mika Westerberg, Sven Peter, Konrad Dybcio

On Tue, Sep 29, 2026, Konrad Dybcio wrote:
> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> 
> The Thunderbolt framework relies on USB core device links for tunneled
> ports, so it needs tunnel state when xHCI updates a device.
> 
> Use the .update_device override for controller-specific tunnel
> reporting.
> 
> Assisted-by: LLM
> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> ---
>  drivers/usb/host/xhci-hub.c  | 53 --------------------------------------------
>  drivers/usb/host/xhci-pci.c  | 38 +++++++++++++++++++++++++++++++
>  drivers/usb/host/xhci-plat.c | 18 +++++++++++++++
>  drivers/usb/host/xhci-plat.h |  2 ++
>  drivers/usb/host/xhci.c      | 19 +++++++++-------
>  drivers/usb/host/xhci.h      |  8 +++++--
>  6 files changed, 75 insertions(+), 63 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
> index 80f07bd5081d..31ba7a990e07 100644
> --- a/drivers/usb/host/xhci-hub.c
> +++ b/drivers/usb/host/xhci-hub.c
> @@ -12,7 +12,6 @@
>  #include <linux/slab.h>
>  #include <linux/unaligned.h>
>  #include <linux/bitfield.h>
> -#include <linux/pci.h>
>  
>  #include "xhci.h"
>  #include "xhci-trace.h"
> @@ -748,58 +747,6 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
>  	return xhci_reset(xhci, XHCI_RESET_SHORT_USEC);
>  }
>  
> -/**
> - * xhci_port_is_tunneled() - Check if USB3 connection is tunneled over USB4
> - * @xhci: xhci host controller
> - * @port: USB3 port to be checked.
> - *
> - * Some hosts can detect if a USB3 connection is native USB3 or tunneled over
> - * USB4. Intel hosts expose this via vendor specific extended capability 206
> - * eSS PORT registers TUNEN (tunnel enabled) bit.
> - *
> - * A USB3 device must be connected to the port to detect the tunnel.
> - *
> - * Return: link tunnel mode enum, USB_LINK_UNKNOWN if host is incapable of
> - * detecting USB3 over USB4 tunnels. USB_LINK_NATIVE or USB_LINK_TUNNELED
> - * otherwise.
> - */
> -enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
> -						struct xhci_port *port)
> -{
> -	struct usb_hcd *hcd;
> -	void __iomem *base;
> -	u32 offset;
> -	u32 val;
> -
> -	if (xhci->portsc_tunnel_reporting) {
> -		val = xhci_portsc_readl(port);
> -		if (val & PORT_TM)
> -			return USB_LINK_TUNNELED;
> -
> -		return USB_LINK_NATIVE;
> -	}
> -
> -	/* Fall back to the legacy Intel-specific ext_cap */
> -	hcd = xhci_to_hcd(xhci);
> -	if (!dev_is_pci(hcd->self.controller) ||
> -	    to_pci_dev(hcd->self.controller)->vendor != PCI_VENDOR_ID_INTEL)
> -		return USB_LINK_UNKNOWN;
> -
> -	base = &xhci->cap_regs->hc_capbase;
> -	offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);
> -
> -	if (offset && offset <= XHCI_INTEL_SPR_ESS_PORT_OFFSET) {
> -		offset = XHCI_INTEL_SPR_ESS_PORT_OFFSET + port->hcd_portnum * 0x20;
> -
> -		if (readl(base + offset) & XHCI_INTEL_SPR_TUNEN)
> -			return USB_LINK_TUNNELED;
> -		else
> -			return USB_LINK_NATIVE;
> -	}
> -
> -	return USB_LINK_UNKNOWN;
> -}
> -
>  void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
>  			 u32 link_state)
>  {
> diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
> index a8889081ae82..61ac267bc84a 100644
> --- a/drivers/usb/host/xhci-pci.c
> +++ b/drivers/usb/host/xhci-pci.c
> @@ -103,11 +103,13 @@ static int xhci_pci_setup(struct usb_hcd *hcd);
>  static int xhci_pci_run(struct usb_hcd *hcd);
>  static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
>  				      struct usb_tt *tt, gfp_t mem_flags);
> +static int xhci_pci_update_device(struct usb_hcd *hcd, struct usb_device *udev);
>  
>  static const struct xhci_driver_overrides xhci_pci_overrides __initconst = {
>  	.reset = xhci_pci_setup,
>  	.start = xhci_pci_run,
>  	.update_hub_device = xhci_pci_update_hub_device,
> +	.update_device = xhci_pci_update_device,
>  };
>  
>  /*
> @@ -606,6 +608,42 @@ static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hd
>  	return xhci_update_hub_device(hcd, hdev, tt, mem_flags);
>  }
>  
> +static void xhci_intel_port_is_tunneled(struct usb_hcd *hcd,
> +					struct usb_device *udev)
> +{
> +	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
> +	struct xhci_port *port;
> +	void __iomem *base;
> +	u32 offset;
> +
> +	if (xhci->portsc_tunnel_reporting || udev->parent->parent)
> +		return;
> +
> +	port = xhci->usb3_rhub.ports[udev->portnum - 1];
> +
> +	base = &xhci->cap_regs->hc_capbase;
> +	offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);
> +
> +	if (offset && offset <= XHCI_INTEL_SPR_ESS_PORT_OFFSET) {
> +		offset = XHCI_INTEL_SPR_ESS_PORT_OFFSET + port->hcd_portnum * 0x20;
> +
> +		if (readl(base + offset) & XHCI_INTEL_SPR_TUNEN)
> +			udev->tunnel_mode = USB_LINK_TUNNELED;
> +		else
> +			udev->tunnel_mode = USB_LINK_NATIVE;
> +	}
> +}
> +
> +static int xhci_pci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
> +{
> +	struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
> +
> +	if (pdev->vendor == PCI_VENDOR_ID_INTEL && hcd->speed >= HCD_USB3)
> +		xhci_intel_port_is_tunneled(hcd, udev);
> +
> +	return xhci_update_device(hcd, udev);
> +}
> +
>  /*
>   * We need to register our own PCI probe function (instead of the USB core's
>   * function) in order to create a second roothub under xHCI.
> diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
> index 6fd595f81a30..641e31976240 100644
> --- a/drivers/usb/host/xhci-plat.c
> +++ b/drivers/usb/host/xhci-plat.c
> @@ -30,13 +30,31 @@ static struct hc_driver __read_mostly xhci_plat_hc_driver;
>  
>  static int xhci_plat_setup(struct usb_hcd *hcd);
>  static int xhci_plat_start(struct usb_hcd *hcd);
> +static int xhci_plat_update_device(struct usb_hcd *hcd, struct usb_device *udev);
>  
>  static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
>  	.extra_priv_size = sizeof(struct xhci_plat_priv),
>  	.reset = xhci_plat_setup,
>  	.start = xhci_plat_start,
> +	.update_device = xhci_plat_update_device,
>  };
>  
> +static int xhci_plat_update_device(struct usb_hcd *hcd, struct usb_device *udev)
> +{
> +	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
> +	struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
> +	int ret;
> +
> +	if (!xhci->portsc_tunnel_reporting && priv->update_device &&
> +	    hcd->speed >= HCD_USB3 && !udev->parent->parent) {
> +		ret = priv->update_device(hcd, udev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return xhci_update_device(hcd, udev);
> +}
> +
>  static void xhci_priv_plat_start(struct usb_hcd *hcd)
>  {
>  	struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
> diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
> index 00751d851831..91333605c46b 100644
> --- a/drivers/usb/host/xhci-plat.h
> +++ b/drivers/usb/host/xhci-plat.h
> @@ -10,6 +10,7 @@
>  
>  struct device;
>  struct platform_device;
> +struct usb_device;
>  struct usb_hcd;
>  
>  struct xhci_plat_priv {
> @@ -22,6 +23,7 @@ struct xhci_plat_priv {
>  	int (*suspend_quirk)(struct usb_hcd *);
>  	int (*resume_quirk)(struct usb_hcd *);
>  	int (*post_resume_quirk)(struct usb_hcd *);
> +	int (*update_device)(struct usb_hcd *hcd, struct usb_device *udev);
>  };
>  
>  #define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv)
> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
> index 5fb78613a993..b252ce5e8499 100644
> --- a/drivers/usb/host/xhci.c
> +++ b/drivers/usb/host/xhci.c
> @@ -4752,7 +4752,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
>  	return 0;
>  }
>  
> -static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
> +int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
>  {
>  	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
>  	struct xhci_port *port;
> @@ -4760,9 +4760,14 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
>  
>  	/* Check if USB3 device at root port is tunneled over USB4 */
>  	if (hcd->speed >= HCD_USB3 && !udev->parent->parent) {
> -		port = xhci->usb3_rhub.ports[udev->portnum - 1];
> +		if (xhci->portsc_tunnel_reporting) {
> +			port = xhci->usb3_rhub.ports[udev->portnum - 1];
> +			if (xhci_portsc_readl(port) & PORT_TM)
> +				udev->tunnel_mode = USB_LINK_TUNNELED;
> +			else
> +				udev->tunnel_mode = USB_LINK_NATIVE;
> +		}
>  
> -		udev->tunnel_mode = xhci_port_is_tunneled(xhci, port);
>  		if (udev->tunnel_mode == USB_LINK_UNKNOWN)
>  			dev_dbg(&udev->dev, "link tunnel state unknown\n");
>  		else if (udev->tunnel_mode == USB_LINK_TUNNELED)
> @@ -4793,6 +4798,7 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
>  
>  	return 0;
>  }
> +EXPORT_SYMBOL_GPL(xhci_update_device);
>  
>  /*---------------------- USB 3.0 Link PM functions ------------------------*/
>  
> @@ -5242,11 +5248,6 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
>  	return 0;
>  }
>  
> -static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
> -{
> -	return 0;
> -}
> -
>  static int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd,
>  			struct usb_device *udev, enum usb3_link_state state)
>  {
> @@ -5695,6 +5696,8 @@ void xhci_init_driver(struct hc_driver *drv,
>  			drv->update_hub_device = over->update_hub_device;
>  		if (over->hub_control)
>  			drv->hub_control = over->hub_control;
> +		if (over->update_device)
> +			drv->update_device = over->update_device;
>  	}
>  }
>  EXPORT_SYMBOL_GPL(xhci_init_driver);
> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> index 480fcbac4525..f12a309104dc 100644
> --- a/drivers/usb/host/xhci.h
> +++ b/drivers/usb/host/xhci.h
> @@ -1694,6 +1694,7 @@ struct xhci_driver_overrides {
>  			    struct usb_tt *tt, gfp_t mem_flags);
>  	int (*hub_control)(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
>  			   u16 wIndex, char *buf, u16 wLength);
> +	int (*update_device)(struct usb_hcd *hcd, struct usb_device *udev);
>  };
>  
>  #define	XHCI_CFC_DELAY		10
> @@ -1981,15 +1982,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
>  int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
>  int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
>  struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
> -enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
> -						struct xhci_port *port);
>  void xhci_hc_died(struct xhci_hcd *xhci);
>  
>  #ifdef CONFIG_PM
> +int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev);

Why are we putting this inside this PM guard?

BR,
Thinh

>  int xhci_bus_suspend(struct usb_hcd *hcd);
>  int xhci_bus_resume(struct usb_hcd *hcd);
>  unsigned long xhci_get_resuming_ports(struct usb_hcd *hcd);
>  #else
> +static inline int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
> +{
> +	return 0;
> +}
>  #define	xhci_bus_suspend	NULL
>  #define	xhci_bus_resume		NULL
>  #define	xhci_get_resuming_ports	NULL
> 
> -- 
> 2.55.0
> 

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

* Re: [PATCH v4 4/4] usb: dwc3: Notify XHCI core of tunneled status
  2026-09-29 15:54 ` [PATCH v4 4/4] usb: dwc3: Notify XHCI core of tunneled status Konrad Dybcio
@ 2026-10-03  1:23   ` Thinh Nguyen
  0 siblings, 0 replies; 7+ messages in thread
From: Thinh Nguyen @ 2026-10-03  1:23 UTC (permalink / raw)
  To: Konrad Dybcio
  Cc: Thinh Nguyen, Greg Kroah-Hartman, Mathias Nyman, linux-usb,
	linux-kernel, usb4-upstream, Raghavendra Thoorpu,
	Mika Westerberg, Sven Peter, Konrad Dybcio

On Tue, Sep 29, 2026, Konrad Dybcio wrote:
> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> 
> The Thunderbolt framework relies on the USB core to create device links
> for tunneled ports, so that the USB3 controller is only kept
> runtime-resumed for the duration of the tunneling. This depends on
> first knowing whether a connection is tunneled or native.
> 
> Add the logic to handle that for DWC3 controllers.
> 
> Assisted-by: LLM
> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> ---
>  drivers/usb/dwc3/core.c | 18 ++++++++++++++++++
>  drivers/usb/dwc3/core.h | 18 ++++++++++++++++++
>  drivers/usb/dwc3/host.c | 14 ++++++++++++++
>  3 files changed, 50 insertions(+)
> 
> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> index fd5c2cd36c59..4eb430f58983 100644
> --- a/drivers/usb/dwc3/core.c
> +++ b/drivers/usb/dwc3/core.c
> @@ -162,6 +162,24 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
>  }
>  EXPORT_SYMBOL_GPL(dwc3_set_prtcap);
>  
> +#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)

Move this function to host.c instead of core.c, drop the guards, and
make it static.

> +enum usb_link_tunnel_mode dwc3_link_tunnel_mode(struct dwc3 *dwc, u8 port)
> +{

Maybe also add a check if (port >= dwc->num_usb3_ports) and return
USB_LINK_UNKNOWN?

> +	/* Prior versions had no CIO support */
> +	if (!DWC3_VER_IS_WITHIN(DWC31, 191A, ANY))
> +		return USB_LINK_UNKNOWN;

Regarding your cover letter question about CIO for other IPs. We can
leave that for later when we need to make an update.

> +
> +	/* Not all DWC3 instances have CIO HW, trust the platform firmware */
> +	if (!device_property_present(dwc->dev, "usb4-host-interface"))

Can we pass this as a dwc3 property from your glue driver instead,
inferred from the compatible string? (ie. add dwc->has_cio)

So we can remove the DT dependency.

> +		return USB_LINK_UNKNOWN;
> +
> +	if (dwc3_readl(dwc, DWC3_CIOCTRL(port)) & DWC3_CIOCTRL_CIO_EN)
> +		return USB_LINK_TUNNELED;
> +
> +	return USB_LINK_NATIVE;
> +}
> +#endif
> +
>  static void __dwc3_set_mode(struct work_struct *work)
>  {
>  	struct dwc3 *dwc = work_to_dwc(work);
> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
> index 608daeb7ef10..decc51cd6505 100644
> --- a/drivers/usb/dwc3/core.h
> +++ b/drivers/usb/dwc3/core.h
> @@ -179,6 +179,11 @@
>  #define DWC3_OEVTEN		0xcc0C
>  #define DWC3_OSTS		0xcc10
>  
> +/* CIO regs */
> +#define DWC3_CIO_BASE(n)	(0xcd20 + ((n) * 0x30))
> +#define DWC3_CIOCTRL(n)		(DWC3_CIO_BASE(n) + 0x00)
> +#define DWC3_CIOCTRL_CIO_EN	BIT(0)
> +
>  #define DWC3_LLUCTL(n)		(0xd024 + ((n) * 0x80))
>  
>  /* Bit fields */
> @@ -1314,6 +1319,7 @@ struct dwc3 {
>  #define DWC31_REVISION_170A	0x3137302a
>  #define DWC31_REVISION_180A	0x3138302a
>  #define DWC31_REVISION_190A	0x3139302a
> +#define DWC31_REVISION_191A	0x3139312a
>  #define DWC31_REVISION_200A	0x3230302a
>  
>  #define DWC32_REVISION_ANY	0x0
> @@ -1658,11 +1664,23 @@ static inline void dwc3_pre_run_stop(struct dwc3 *dwc, bool is_on)
>  #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
>  int dwc3_host_init(struct dwc3 *dwc);
>  void dwc3_host_exit(struct dwc3 *dwc);
> +
> +/**
> + * dwc3_link_tunnel_mode - Check whether the link is tunneled over TBT/USB4
> + * @dwc: Pointer to DWC3 controller context
> + * @port: 0-based port index
> + *
> + * Returns: USB_LINK_TUNNELED if tunneled, USB_LINK_NATIVE if not, or
> + *          when the controller does not have USB4 capabilities.

Do we need to describe the USB_LINK_UNKNOWN case?

Thanks,
Thinh

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

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

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-29 15:54 [PATCH v4 0/4] DWC3 link tunneling state reporting Konrad Dybcio
2026-09-29 15:54 ` [PATCH v4 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value Konrad Dybcio
2026-09-29 15:54 ` [PATCH v4 2/4] usb: xhci: Honor PORTSC.TM if valid Konrad Dybcio
2026-09-29 15:54 ` [PATCH v4 3/4] usb: xhci: Use update_device overrides for tunnel reporting Konrad Dybcio
2026-10-03  0:30   ` Thinh Nguyen
2026-09-29 15:54 ` [PATCH v4 4/4] usb: dwc3: Notify XHCI core of tunneled status Konrad Dybcio
2026-10-03  1:23   ` Thinh Nguyen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®