From: Gil Fine Date: Wed, 6 May 2026 12:37:06 +0000 (+0300) Subject: thunderbolt: Verify PCIe adapter in detect state before tunnel setup X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=69a7b98770b7e80deec0465d97710611a0e51774;p=thirdparty%2Fkernel%2Flinux.git thunderbolt: Verify PCIe adapter in detect state before tunnel setup The USB4 Connection Manager guide suggests that a PCIe downstream and PCIe upstream adapters of the USB4 router is in the Detect state before setting up a PCIe tunnel. Add this check by verifying the LTSSM field in ADP_PCIE_CS_0 before tunnel setup. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index e60f1bc3764e..003db653418a 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1481,6 +1481,7 @@ int usb4_dp_port_allocate_bandwidth(struct tb_port *port, int bw); int usb4_dp_port_requested_bandwidth(struct tb_port *port); int usb4_pci_port_set_ext_encapsulation(struct tb_port *port, bool enable); +int usb4_pci_port_ltssm_state(struct tb_port *port); static inline bool tb_is_usb4_port_device(const struct device *dev) { diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index c0bf136236e6..75131fcfe044 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -473,10 +473,25 @@ struct tb_regs_port_header { /* PCIe adapter registers */ #define ADP_PCIE_CS_0 0x00 +#define ADP_PCIE_CS_0_LTSSM_MASK GENMASK(28, 25) #define ADP_PCIE_CS_0_PE BIT(31) #define ADP_PCIE_CS_1 0x01 #define ADP_PCIE_CS_1_EE BIT(0) +enum tb_pcie_ltssm_state { + USB4_PCIE_LTSSM_DETECT, + USB4_PCIE_LTSSM_POLLING, + USB4_PCIE_LTSSM_CONFIG, + USB4_PCIE_LTSSM_CONFIG_IDLE, + USB4_PCIE_LTSSM_RECOVERY, + USB4_PCIE_LTSSM_RECOVERY_IDLE, + USB4_PCIE_LTSSM_L0, + USB4_PCIE_LTSSM_L1, + USB4_PCIE_LTSSM_L2, + USB4_PCIE_LTSSM_DISABLED, + USB4_PCIE_LTSSM_HOT_RESET, +}; + /* USB adapter registers */ #define ADP_USB3_CS_0 0x00 #define ADP_USB3_CS_0_V BIT(30) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index f38f7753b6e4..b7f32305f14a 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -290,6 +290,40 @@ static inline void tb_tunnel_changed(struct tb_tunnel *tunnel) tunnel->src_port, tunnel->dst_port); } +static int tb_pci_port_ltssm_state_detect(struct tb_port *port) +{ + ktime_t timeout = ktime_add_ms(ktime_get(), 500); + + do { + int ret; + + ret = usb4_pci_port_ltssm_state(port); + if (ret < 0) + return ret; + if (ret == USB4_PCIE_LTSSM_DETECT) + return 0; + + fsleep(50); + } while (ktime_before(ktime_get(), timeout)); + + return -ETIMEDOUT; +} + +static int tb_pci_pre_activate(struct tb_tunnel *tunnel) +{ + struct tb_port *down = tunnel->src_port; + struct tb_port *up = tunnel->dst_port; + int ret; + + ret = tb_switch_is_usb4(down->sw) ? + tb_pci_port_ltssm_state_detect(down) : 0; + if (ret) + return ret; + + return tb_switch_is_usb4(up->sw) ? + tb_pci_port_ltssm_state_detect(up) : 0; +} + static int tb_pci_set_ext_encapsulation(struct tb_tunnel *tunnel, bool enable) { struct tb_port *port = tb_upstream_port(tunnel->dst_port->sw); @@ -505,6 +539,7 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up, if (!tunnel) return NULL; + tunnel->pre_activate = tb_pci_pre_activate; tunnel->activate = tb_pci_activate; tunnel->src_port = down; tunnel->dst_port = up; diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 9e810b2ae0b5..6f76bcaefa49 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -3145,3 +3145,27 @@ int usb4_pci_port_set_ext_encapsulation(struct tb_port *port, bool enable) return tb_port_write(port, &val, TB_CFG_PORT, port->cap_adap + ADP_PCIE_CS_1, 1); } + +/** + * usb4_pci_port_ltssm_state() - Read PCIe adapter LTSSM state + * @port: PCIe adapter + * + * Return: + * * LTSSM state of @port. + * * Negative errno - On failure. + */ +int usb4_pci_port_ltssm_state(struct tb_port *port) +{ + u32 val; + int ret; + + if (!tb_port_is_pcie_down(port) && !tb_port_is_pcie_up(port)) + return -EINVAL; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_PCIE_CS_0, 1); + if (ret) + return ret; + + return FIELD_GET(ADP_PCIE_CS_0_LTSSM_MASK, val); +}