* [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
* 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
* [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 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®