thunderbolt: Enable CL2 low power state
authorMika Westerberg <mika.westerberg@linux.intel.com>
Sun, 6 Nov 2022 11:07:43 +0000 (13:07 +0200)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Fri, 16 Jun 2023 06:53:28 +0000 (09:53 +0300)
For USB4 v2 routers we can also enable CL2 which allows better power
savings and thermal management than CL0s and CL1.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
drivers/thunderbolt/clx.c
drivers/thunderbolt/tb.c

index 604cceb..13d217a 100644 (file)
@@ -17,17 +17,22 @@ MODULE_PARM_DESC(clx, "allow low power states on the high-speed lanes (default:
 
 static const char *clx_name(unsigned int clx)
 {
-       if (!clx)
-               return "disabled";
-
-       if (clx & TB_CL2)
+       switch (clx) {
+       case TB_CL0S | TB_CL1 | TB_CL2:
                return "CL0s/CL1/CL2";
-       if (clx & TB_CL1)
+       case TB_CL1 | TB_CL2:
+               return "CL1/CL2";
+       case TB_CL0S | TB_CL2:
+               return "CL0s/CL2";
+       case TB_CL0S | TB_CL1:
                return "CL0s/CL1";
-       if (clx & TB_CL0S)
+       case TB_CL0S:
                return "CL0s";
-
-       return "unknown";
+       case 0:
+               return "disabled";
+       default:
+               return "unknown";
+       }
 }
 
 static int tb_port_pm_secondary_set(struct tb_port *port, bool secondary)
@@ -104,6 +109,8 @@ static int tb_port_clx_set(struct tb_port *port, unsigned int clx, bool enable)
                mask |= LANE_ADP_CS_1_CL0S_ENABLE;
        if (clx & TB_CL1)
                mask |= LANE_ADP_CS_1_CL1_ENABLE;
+       if (clx & TB_CL2)
+               mask |= LANE_ADP_CS_1_CL2_ENABLE;
 
        if (!mask)
                return -EOPNOTSUPP;
@@ -291,8 +298,6 @@ bool tb_switch_clx_is_supported(const struct tb_switch *sw)
 static bool validate_mask(unsigned int clx)
 {
        /* Previous states need to be enabled */
-       if (clx & TB_CL2)
-               return (clx & (TB_CL0S | TB_CL1)) == (TB_CL0S | TB_CL1);
        if (clx & TB_CL1)
                return (clx & TB_CL0S) == TB_CL0S;
        return true;
@@ -331,8 +336,10 @@ int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx)
            !tb_switch_clx_is_supported(sw))
                return 0;
 
-       /* CL2 is not yet supported */
-       if (clx & TB_CL2)
+       /* Only support CL2 for v2 routers */
+       if ((clx & TB_CL2) &&
+           (usb4_switch_version(parent_sw) < 2 ||
+            usb4_switch_version(sw) < 2))
                return -EOPNOTSUPP;
 
        ret = tb_switch_pm_secondary_resolve(sw);
index ff03497..79efc85 100644 (file)
@@ -244,6 +244,7 @@ static void tb_discover_dp_resources(struct tb *tb)
 static int tb_enable_clx(struct tb_switch *sw)
 {
        struct tb_cm *tcm = tb_priv(sw->tb);
+       unsigned int clx = TB_CL0S | TB_CL1;
        const struct tb_tunnel *tunnel;
        int ret;
 
@@ -275,10 +276,12 @@ static int tb_enable_clx(struct tb_switch *sw)
        }
 
        /*
-        * CL0s and CL1 are enabled and supported together.
-        * Silently ignore CLx enabling in case CLx is not supported.
+        * Initially try with CL2. If that's not supported by the
+        * topology try with CL0s and CL1 and then give up.
         */
-       ret = tb_switch_clx_enable(sw, TB_CL0S | TB_CL1);
+       ret = tb_switch_clx_enable(sw, clx | TB_CL2);
+       if (ret == -EOPNOTSUPP)
+               ret = tb_switch_clx_enable(sw, clx);
        return ret == -EOPNOTSUPP ? 0 : ret;
 }