Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xhci: Add USB4 tunnel detection for USB3 devices on Intel hosts

Knowledge about tunneled devices is useful in order to correctly describe
the relationship between tunneled USB3 device and USB4 Host Interface,
ensuring proper suspend and resume order, and to be able to power down
Thunderbolt if there is no need for tunneling.

Intel hosts share if a USB3 connection is native or tunneled via vendor
specific "SPR eSS PORT" registers.

These vendor registers are available if host supports a vendor specific
SPR shadow extended capability with ID 206. Registers are per USB3 port
and 0x20 apart.

Knowing the tunneling status of the device connected to roothub is enough
as all its children will have the same status.

Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Link: https://lore.kernel.org/r/20240830152630.3943215-2-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Mathias Nyman and committed by
Greg Kroah-Hartman
948ce83f 9299f12c

+48
+5
drivers/usb/host/xhci-ext-caps.h
··· 42 42 #define XHCI_EXT_CAPS_DEBUG 10 43 43 /* Vendor caps */ 44 44 #define XHCI_EXT_CAPS_VENDOR_INTEL 192 45 + #define XHCI_EXT_CAPS_INTEL_SPR_SHADOW 206 45 46 /* USB Legacy Support Capability - section 7.1.1 */ 46 47 #define XHCI_HC_BIOS_OWNED (1 << 16) 47 48 #define XHCI_HC_OS_OWNED (1 << 24) ··· 64 63 /* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */ 65 64 #define XHCI_HLC (1 << 19) 66 65 #define XHCI_BLC (1 << 20) 66 + 67 + /* Intel SPR shadow capability */ 68 + #define XHCI_INTEL_SPR_ESS_PORT_OFFSET 0x8ac4 /* SuperSpeed port control */ 69 + #define XHCI_INTEL_SPR_TUNEN BIT(4) /* Tunnel mode enabled */ 67 70 68 71 /* command register values to disable interrupts and halt the HC */ 69 72 /* start/stop HC execution - do not write unless HC is halted*/
+31
drivers/usb/host/xhci-hub.c
··· 752 752 return xhci_reset(xhci, XHCI_RESET_SHORT_USEC); 753 753 } 754 754 755 + /** 756 + * xhci_port_is_tunneled() - Check if USB3 connection is tunneled over USB4 757 + * @xhci: xhci host controller 758 + * @port: USB3 port to be checked. 759 + * 760 + * Some hosts can detect if a USB3 connection is native USB3 or tunneled over 761 + * USB4. Intel hosts expose this via vendor specific extended capability 206 762 + * eSS PORT registers TUNEN (tunnel enabled) bit. 763 + * 764 + * A USB3 device must be connected to the port to detect the tunnel. 765 + * 766 + * Return: true if USB3 connection is tunneled over USB4 767 + */ 768 + bool xhci_port_is_tunneled(struct xhci_hcd *xhci, struct xhci_port *port) 769 + { 770 + void __iomem *base; 771 + u32 offset; 772 + 773 + base = &xhci->cap_regs->hc_capbase; 774 + offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW); 775 + 776 + if (offset && offset <= XHCI_INTEL_SPR_ESS_PORT_OFFSET) { 777 + offset = XHCI_INTEL_SPR_ESS_PORT_OFFSET + port->hcd_portnum * 0x20; 778 + 779 + if (readl(base + offset) & XHCI_INTEL_SPR_TUNEN) 780 + return true; 781 + } 782 + 783 + return false; 784 + } 785 + 755 786 void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, 756 787 u32 link_state) 757 788 {
+11
drivers/usb/host/xhci.c
··· 4525 4525 struct xhci_port *port; 4526 4526 u32 capability; 4527 4527 4528 + /* Check if USB3 device at root port is tunneled over USB4 */ 4529 + if (hcd->speed >= HCD_USB3 && !udev->parent->parent) { 4530 + port = xhci->usb3_rhub.ports[udev->portnum - 1]; 4531 + 4532 + if (xhci_port_is_tunneled(xhci, port)) 4533 + dev_dbg(&udev->dev, "tunneled over USB4 link\n"); 4534 + else 4535 + dev_dbg(&udev->dev, "native USB 3.x link\n"); 4536 + return 0; 4537 + } 4538 + 4528 4539 if (hcd->speed >= HCD_USB3 || !udev->lpm_capable || !xhci->hw_lpm_support) 4529 4540 return 0; 4530 4541
+1
drivers/usb/host/xhci.h
··· 1929 1929 int xhci_hub_status_data(struct usb_hcd *hcd, char *buf); 1930 1930 int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1); 1931 1931 struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd); 1932 + bool xhci_port_is_tunneled(struct xhci_hcd *xhci, struct xhci_port *port); 1932 1933 1933 1934 void xhci_hc_died(struct xhci_hcd *xhci); 1934 1935