mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Mathias Nyman <mathias.nyman@linux.intel.com>
To: Konrad Dybcio <konradybcio@kernel.org>,
	Thinh Nguyen <Thinh.Nguyen@synopsys.com>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Mathias Nyman <mathias.nyman@intel.com>
Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
	usb4-upstream@oss.qualcomm.com,
	Raghavendra Thoorpu <rthoorpu@qti.qualcomm.com>,
	Mika Westerberg <mika.westerberg@linux.intel.com>,
	Sven Peter <sven@kernel.org>,
	Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Subject: Re: [PATCH v3 3/4] usb: xhci: Allow custom op for usb_link_tunnel_mode reporting
Date: Tue, 22 Sep 2026 01:10:48 +0300	[thread overview]
Message-ID: <9c0f7132-995e-464c-b9f1-b6f240af5abb@linux.intel.com> (raw)
In-Reply-To: <20260901-topic-dwc3_tunneling_state-v3-3-22fdced8de55@oss.qualcomm.com>

On 9/1/26 13:05, Konrad Dybcio wrote:
> From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> 
> The Thunderbolt framework relies on the USB core to create device links
> for tunneled ports, so that the USB3 controller is only kept
> runtime-resumed for the duration of the tunneling.
> 
> Currently, retrieving that information is only possibe on Intel XHCI
> hosts, through a vendor-specific capability. Extend xhci-plat to allow
> plumbing a custom one.
> 
> Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> ---
>   drivers/usb/host/xhci-hub.c  | 13 +++++++++----
>   drivers/usb/host/xhci-plat.c |  2 ++
>   drivers/usb/host/xhci-plat.h |  1 +
>   drivers/usb/host/xhci.c      |  3 ++-
>   drivers/usb/host/xhci.h      |  5 ++++-
>   5 files changed, 18 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
> index 80f07bd5081d..c585e4d02d84 100644
> --- a/drivers/usb/host/xhci-hub.c
> +++ b/drivers/usb/host/xhci-hub.c
> @@ -749,7 +749,7 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
>   }
>   
>   /**
> - * xhci_port_is_tunneled() - Check if USB3 connection is tunneled over USB4
> + * xhci_port_tunnel_mode() - Check if USB3 connection is tunneled over USB4
>    * @xhci: xhci host controller
>    * @port: USB3 port to be checked.
>    *
> @@ -763,7 +763,7 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci)
>    * detecting USB3 over USB4 tunnels. USB_LINK_NATIVE or USB_LINK_TUNNELED
>    * otherwise.
>    */
> -enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
> +enum usb_link_tunnel_mode xhci_port_tunnel_mode(struct xhci_hcd *xhci,
>   						struct xhci_port *port)
>   {
>   	struct usb_hcd *hcd;
> @@ -782,8 +782,13 @@ enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci,
>   	/* Fall back to the legacy Intel-specific ext_cap */
>   	hcd = xhci_to_hcd(xhci);
>   	if (!dev_is_pci(hcd->self.controller) ||
> -	    to_pci_dev(hcd->self.controller)->vendor != PCI_VENDOR_ID_INTEL)
> -		return USB_LINK_UNKNOWN;
> +	    to_pci_dev(hcd->self.controller)->vendor != PCI_VENDOR_ID_INTEL) {
> +		/* Last chance - if the controller has a custom tunnel_mode op, try that */
> +		if (xhci->tunnel_mode)
> +			return xhci->tunnel_mode(xhci_to_hcd(xhci), port->hcd_portnum);
> +		else
> +			return USB_LINK_UNKNOWN;
> +	}
>   
>   	base = &xhci->cap_regs->hc_capbase;
>   	offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW);
> diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
> index 6fd595f81a30..c19bc5d7a7b5 100644
> --- a/drivers/usb/host/xhci-plat.c
> +++ b/drivers/usb/host/xhci-plat.c
> @@ -244,6 +244,8 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
>   		priv = hcd_to_xhci_priv(hcd);
>   		/* Just copy data for now */
>   		*priv = *priv_match;
> +
> +		xhci->tunnel_mode = priv->tunnel_mode;
>   	}
>   
>   	device_set_wakeup_capable(&pdev->dev, true);
> diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
> index 00751d851831..c5042766a486 100644
> --- a/drivers/usb/host/xhci-plat.h
> +++ b/drivers/usb/host/xhci-plat.h
> @@ -22,6 +22,7 @@ struct xhci_plat_priv {
>   	int (*suspend_quirk)(struct usb_hcd *);
>   	int (*resume_quirk)(struct usb_hcd *);
>   	int (*post_resume_quirk)(struct usb_hcd *);
> +	enum usb_link_tunnel_mode (*tunnel_mode)(struct usb_hcd *hcd, int portnum);
>   };
>   
>   #define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv)
> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
> index 5fb78613a993..a7e3c46937fa 100644
> --- a/drivers/usb/host/xhci.c
> +++ b/drivers/usb/host/xhci.c
> @@ -4762,7 +4762,8 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
>   	if (hcd->speed >= HCD_USB3 && !udev->parent->parent) {
>   		port = xhci->usb3_rhub.ports[udev->portnum - 1];
>   
> -		udev->tunnel_mode = xhci_port_is_tunneled(xhci, port);
> +		udev->tunnel_mode = xhci_port_tunnel_mode(xhci, port);
> +
>   		if (udev->tunnel_mode == USB_LINK_UNKNOWN)
>   			dev_dbg(&udev->dev, "link tunnel state unknown\n");
>   		else if (udev->tunnel_mode == USB_LINK_TUNNELED)
> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> index 480fcbac4525..2b5c0b304c6b 100644
> --- a/drivers/usb/host/xhci.h
> +++ b/drivers/usb/host/xhci.h
> @@ -1675,6 +1675,9 @@ struct xhci_hcd {
>   	struct list_head	regset_list;
>   
>   	void			*dbc;
> +
> +	enum usb_link_tunnel_mode (*tunnel_mode)(struct usb_hcd *hcd, int portnum);
> +

I don't think we should add another function pointer here.

I think it makes more sense that pci and platform driver override hc_driver .update_device.

The PCI override side would handle the intel parts, the platform override the dwc3 case,
and the generic part would check the new PORTSC.TM bit

PCI and generic parts would look something like this: (untested)

diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index a8889081ae82..ad7571aeffc4 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.c b/drivers/usb/host/xhci.c
index d61ed5de69a2..945f6647ffad 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,13 @@ 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];
-
-		udev->tunnel_mode = xhci_port_tunnel_mode(xhci, port);
+		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;
+		}
  
  		if (udev->tunnel_mode == USB_LINK_UNKNOWN)
  			dev_dbg(&udev->dev, "link tunnel state unknown\n");
@@ -5696,6 +5700,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 2b5c0b304c6b..1071879366c6 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1697,6 +1697,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
@@ -1897,6 +1898,8 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
  void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
  int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
  			   struct usb_tt *tt, gfp_t mem_flags);
+int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev);
+
  int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id);
  int xhci_disable_and_free_slot(struct xhci_hcd *xhci, u32 slot_id);
  int xhci_ext_cap_init(struct xhci_hcd *xhci);

Thanks
Mathias


  parent reply	other threads:[~2026-09-21 22:10 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-01 10:05 [PATCH v3 0/4] DWC3 link tunneling state reporting Konrad Dybcio
2026-09-01 10:05 ` [PATCH v3 1/4] usb: xhci: debugfs: Expose the USB3 tunneling ext_cap register value Konrad Dybcio
2026-09-05  0:26   ` Thinh Nguyen
2026-09-01 10:05 ` [PATCH v3 2/4] usb: xhci: Honor PORTSC.TM if valid Konrad Dybcio
2026-09-05  0:27   ` Thinh Nguyen
2026-09-01 10:05 ` [PATCH v3 3/4] usb: xhci: Allow custom op for usb_link_tunnel_mode reporting Konrad Dybcio
2026-09-05  0:29   ` Thinh Nguyen
2026-09-21 22:10   ` Mathias Nyman [this message]
2026-09-01 10:05 ` [PATCH v3 4/4] usb: dwc3: Notify XHCI core of tunneled status Konrad Dybcio
2026-09-05  0:30   ` Thinh Nguyen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=9c0f7132-995e-464c-b9f1-b6f240af5abb@linux.intel.com \
    --to=mathias.nyman@linux.intel.com \
    --cc=Thinh.Nguyen@synopsys.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=konrad.dybcio@oss.qualcomm.com \
    --cc=konradybcio@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=mathias.nyman@intel.com \
    --cc=mika.westerberg@linux.intel.com \
    --cc=rthoorpu@qti.qualcomm.com \
    --cc=sven@kernel.org \
    --cc=usb4-upstream@oss.qualcomm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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®