]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
thunderbolt: Verify PCIe adapter in detect state before tunnel setup
authorGil Fine <gil.fine@linux.intel.com>
Wed, 6 May 2026 12:37:06 +0000 (15:37 +0300)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Wed, 20 May 2026 09:54:34 +0000 (11:54 +0200)
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 <gil.fine@linux.intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
drivers/thunderbolt/tb.h
drivers/thunderbolt/tb_regs.h
drivers/thunderbolt/tunnel.c
drivers/thunderbolt/usb4.c

index e60f1bc3764e7a7f5a83d59058d14478a675ab94..003db653418aea2e609d28622573fed38ae3a42e 100644 (file)
@@ -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)
 {
index c0bf136236e674049cee4fe666ab8e5aa72175b6..75131fcfe04410869609d5a8ab66cf83049bd275 100644 (file)
@@ -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)
index f38f7753b6e4fdffdb0acabcd706b6a75c39b9dc..b7f32305f14a6a23c7083f5511528fc912df6a66 100644 (file)
@@ -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;
index 9e810b2ae0b5ff9c7432368ad61dc02d36b09670..6f76bcaefa4915db541c523bc4013456eb7ab557 100644 (file)
@@ -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);
+}