diff options
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/xhci-ext-caps.h | 5 | ||||
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 31 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 11 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 1 |
4 files changed, 48 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h index 96eb36a58738..67ecf7320c62 100644 --- a/drivers/usb/host/xhci-ext-caps.h +++ b/drivers/usb/host/xhci-ext-caps.h @@ -42,6 +42,7 @@ #define XHCI_EXT_CAPS_DEBUG 10 /* Vendor caps */ #define XHCI_EXT_CAPS_VENDOR_INTEL 192 +#define XHCI_EXT_CAPS_INTEL_SPR_SHADOW 206 /* USB Legacy Support Capability - section 7.1.1 */ #define XHCI_HC_BIOS_OWNED (1 << 16) #define XHCI_HC_OS_OWNED (1 << 24) @@ -64,6 +65,10 @@ #define XHCI_HLC (1 << 19) #define XHCI_BLC (1 << 20) +/* 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 */ + /* command register values to disable interrupts and halt the HC */ /* start/stop HC execution - do not write unless HC is halted*/ #define XHCI_CMD_RUN (1 << 0) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 61f083de6e19..4ba910eadd3f 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -752,6 +752,37 @@ 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: true if USB3 connection is tunneled over USB4 + */ +bool xhci_port_is_tunneled(struct xhci_hcd *xhci, struct xhci_port *port) +{ + void __iomem *base; + u32 offset; + + 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 true; + } + + return false; +} + void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, u32 link_state) { diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index efdf4c228b8c..1ea2c91106b7 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4525,6 +4525,17 @@ static int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_port *port; u32 capability; + /* 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_port_is_tunneled(xhci, port)) + dev_dbg(&udev->dev, "tunneled over USB4 link\n"); + else + dev_dbg(&udev->dev, "native USB 3.x link\n"); + return 0; + } + if (hcd->speed >= HCD_USB3 || !udev->lpm_capable || !xhci->hw_lpm_support) return 0; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index df2c81db21a1..e87a6b2f139d 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1929,6 +1929,7 @@ 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); +bool xhci_port_is_tunneled(struct xhci_hcd *xhci, struct xhci_port *port); void xhci_hc_died(struct xhci_hcd *xhci); |