]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
thunderbolt: Start lane initialization after sleep
authorMika Westerberg <mika.westerberg@linux.intel.com>
Thu, 26 Nov 2020 09:52:43 +0000 (12:52 +0300)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Mon, 11 Jan 2021 14:15:42 +0000 (17:15 +0300)
USB4 spec says that for TBT3 compatible device routers the connection
manager needs to set SLI (Start Lane Initialization) to get the lanes
that were not connected back to functional state after sleep. Same needs
to be done if the link was XDomain.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: Yehezkel Bernat <YehezkelShB@gmail.com>
drivers/thunderbolt/lc.c
drivers/thunderbolt/switch.c
drivers/thunderbolt/tb.h
drivers/thunderbolt/tb_regs.h

index 41e6c738f6c8a5b48d3b7c15cd9bf91b0019353b..bc671730a11f395ccbd461f4e7a2d54d0a2bf39f 100644 (file)
@@ -158,6 +158,41 @@ void tb_lc_unconfigure_xdomain(struct tb_port *port)
        tb_lc_set_xdomain_configured(port, false);
 }
 
+/**
+ * tb_lc_start_lane_initialization() - Start lane initialization
+ * @port: Device router lane 0 adapter
+ *
+ * Starts lane initialization for @port after the router resumed from
+ * sleep. Should be called for those downstream lane adapters that were
+ * not connected (tb_lc_configure_port() was not called) before sleep.
+ *
+ * Returns %0 in success and negative errno in case of failure.
+ */
+int tb_lc_start_lane_initialization(struct tb_port *port)
+{
+       struct tb_switch *sw = port->sw;
+       int ret, cap;
+       u32 ctrl;
+
+       if (!tb_route(sw))
+               return 0;
+
+       if (sw->generation < 2)
+               return 0;
+
+       cap = find_port_lc_cap(port);
+       if (cap < 0)
+               return cap;
+
+       ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
+       if (ret)
+               return ret;
+
+       ctrl |= TB_LC_SX_CTRL_SLI;
+
+       return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
+}
+
 static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
                              unsigned int flags)
 {
index 602e1835bf618186feb830d95c32f15a475264d6..ad992e6204d9efb97dc54d0e7ee17890ad7803a3 100644 (file)
@@ -1065,6 +1065,17 @@ void tb_port_lane_bonding_disable(struct tb_port *port)
        tb_port_set_link_width(port, 1);
 }
 
+static int tb_port_start_lane_initialization(struct tb_port *port)
+{
+       int ret;
+
+       if (tb_switch_is_usb4(port->sw))
+               return 0;
+
+       ret = tb_lc_start_lane_initialization(port);
+       return ret == -EINVAL ? 0 : ret;
+}
+
 /**
  * tb_port_is_enabled() - Is the adapter port enabled
  * @port: Port to check
@@ -2694,8 +2705,22 @@ int tb_switch_resume(struct tb_switch *sw)
 
        /* check for surviving downstream switches */
        tb_switch_for_each_port(sw, port) {
-               if (!tb_port_has_remote(port) && !port->xdomain)
+               if (!tb_port_has_remote(port) && !port->xdomain) {
+                       /*
+                        * For disconnected downstream lane adapters
+                        * start lane initialization now so we detect
+                        * future connects.
+                        */
+                       if (!tb_is_upstream_port(port) && tb_port_is_null(port))
+                               tb_port_start_lane_initialization(port);
                        continue;
+               } else if (port->xdomain) {
+                       /*
+                        * Start lane initialization for XDomain so the
+                        * link gets re-established.
+                        */
+                       tb_port_start_lane_initialization(port);
+               }
 
                if (tb_wait_for_port(port, true) <= 0) {
                        tb_port_warn(port,
index 554feda1e35932f8124d0580a09f15099c4fdb8b..34ae83b9e52a7029191788c199169cc5fe4d1d52 100644 (file)
@@ -923,6 +923,7 @@ int tb_lc_configure_port(struct tb_port *port);
 void tb_lc_unconfigure_port(struct tb_port *port);
 int tb_lc_configure_xdomain(struct tb_port *port);
 void tb_lc_unconfigure_xdomain(struct tb_port *port);
+int tb_lc_start_lane_initialization(struct tb_port *port);
 int tb_lc_set_wake(struct tb_switch *sw, unsigned int flags);
 int tb_lc_set_sleep(struct tb_switch *sw);
 bool tb_lc_lane_bonding_possible(struct tb_switch *sw);
index ae427a953489c13050b54da7d9f1651de52cb853..626751e062920f7d76ec770d6dc08c4d50c49a19 100644 (file)
@@ -464,6 +464,7 @@ struct tb_regs_hop {
 #define TB_LC_SX_CTRL_L1D              BIT(17)
 #define TB_LC_SX_CTRL_L2C              BIT(20)
 #define TB_LC_SX_CTRL_L2D              BIT(21)
+#define TB_LC_SX_CTRL_SLI              BIT(29)
 #define TB_LC_SX_CTRL_UPSTREAM         BIT(30)
 #define TB_LC_SX_CTRL_SLP              BIT(31)