summaryrefslogtreecommitdiff
path: root/drivers/thunderbolt/tunnel.c
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2023-09-08 12:07:47 +0300
committerMika Westerberg <mika.westerberg@linux.intel.com>2023-10-20 18:18:01 +0300
commitce91d793ab8bc55804cdac2536bc33c249e75196 (patch)
tree4324fd4134b62028ab66f11dfa84f3674a2911f4 /drivers/thunderbolt/tunnel.c
parent582e70b0d3a412d15389a3c9c07a44791b311715 (diff)
downloadlinux-ce91d793ab8bc55804cdac2536bc33c249e75196.tar.xz
thunderbolt: Set path power management packet support bit for USB4 v2 routers
USB4 v2 spec allows USB4 links that are part of a pass through tunnel (such as DisplayPort and USB 3.x Gen T) to enter lower CL states, which provide better power management. For this USB4 v2 routers in their path config space of lane 0 adapter include a new bit PMPS (PM packet support) that needs to be set. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Diffstat (limited to 'drivers/thunderbolt/tunnel.c')
-rw-r--r--drivers/thunderbolt/tunnel.c35
1 files changed, 26 insertions, 9 deletions
diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c
index 876b8f07f716..db0546c62cb3 100644
--- a/drivers/thunderbolt/tunnel.c
+++ b/drivers/thunderbolt/tunnel.c
@@ -134,6 +134,16 @@ static unsigned int tb_available_credits(const struct tb_port *port,
return credits > 0 ? credits : 0;
}
+static void tb_init_pm_support(struct tb_path_hop *hop)
+{
+ struct tb_port *out_port = hop->out_port;
+ struct tb_port *in_port = hop->in_port;
+
+ if (tb_port_is_null(in_port) && tb_port_is_null(out_port) &&
+ usb4_switch_version(in_port->sw) >= 2)
+ hop->pm_support = true;
+}
+
static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths,
enum tb_tunnel_type type)
{
@@ -1213,7 +1223,7 @@ static void tb_dp_init_aux_credits(struct tb_path_hop *hop)
hop->initial_credits = 1;
}
-static void tb_dp_init_aux_path(struct tb_path *path)
+static void tb_dp_init_aux_path(struct tb_path *path, bool pm_support)
{
struct tb_path_hop *hop;
@@ -1224,8 +1234,11 @@ static void tb_dp_init_aux_path(struct tb_path *path)
path->priority = TB_DP_AUX_PRIORITY;
path->weight = TB_DP_AUX_WEIGHT;
- tb_path_for_each_hop(path, hop)
+ tb_path_for_each_hop(path, hop) {
tb_dp_init_aux_credits(hop);
+ if (pm_support)
+ tb_init_pm_support(hop);
+ }
}
static int tb_dp_init_video_credits(struct tb_path_hop *hop)
@@ -1257,7 +1270,7 @@ static int tb_dp_init_video_credits(struct tb_path_hop *hop)
return 0;
}
-static int tb_dp_init_video_path(struct tb_path *path)
+static int tb_dp_init_video_path(struct tb_path *path, bool pm_support)
{
struct tb_path_hop *hop;
@@ -1274,6 +1287,8 @@ static int tb_dp_init_video_path(struct tb_path *path)
ret = tb_dp_init_video_credits(hop);
if (ret)
return ret;
+ if (pm_support)
+ tb_init_pm_support(hop);
}
return 0;
@@ -1365,7 +1380,7 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
goto err_free;
}
tunnel->paths[TB_DP_VIDEO_PATH_OUT] = path;
- if (tb_dp_init_video_path(tunnel->paths[TB_DP_VIDEO_PATH_OUT]))
+ if (tb_dp_init_video_path(tunnel->paths[TB_DP_VIDEO_PATH_OUT], false))
goto err_free;
path = tb_path_discover(in, TB_DP_AUX_TX_HOPID, NULL, -1, NULL, "AUX TX",
@@ -1373,14 +1388,14 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
if (!path)
goto err_deactivate;
tunnel->paths[TB_DP_AUX_PATH_OUT] = path;
- tb_dp_init_aux_path(tunnel->paths[TB_DP_AUX_PATH_OUT]);
+ tb_dp_init_aux_path(tunnel->paths[TB_DP_AUX_PATH_OUT], false);
path = tb_path_discover(tunnel->dst_port, -1, in, TB_DP_AUX_RX_HOPID,
&port, "AUX RX", alloc_hopid);
if (!path)
goto err_deactivate;
tunnel->paths[TB_DP_AUX_PATH_IN] = path;
- tb_dp_init_aux_path(tunnel->paths[TB_DP_AUX_PATH_IN]);
+ tb_dp_init_aux_path(tunnel->paths[TB_DP_AUX_PATH_IN], false);
/* Validate that the tunnel is complete */
if (!tb_port_is_dpout(tunnel->dst_port)) {
@@ -1435,6 +1450,7 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
struct tb_tunnel *tunnel;
struct tb_path **paths;
struct tb_path *path;
+ bool pm_support;
if (WARN_ON(!in->cap_adap || !out->cap_adap))
return NULL;
@@ -1456,26 +1472,27 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
tunnel->max_down = max_down;
paths = tunnel->paths;
+ pm_support = usb4_switch_version(in->sw) >= 2;
path = tb_path_alloc(tb, in, TB_DP_VIDEO_HOPID, out, TB_DP_VIDEO_HOPID,
link_nr, "Video");
if (!path)
goto err_free;
- tb_dp_init_video_path(path);
+ tb_dp_init_video_path(path, pm_support);
paths[TB_DP_VIDEO_PATH_OUT] = path;
path = tb_path_alloc(tb, in, TB_DP_AUX_TX_HOPID, out,
TB_DP_AUX_TX_HOPID, link_nr, "AUX TX");
if (!path)
goto err_free;
- tb_dp_init_aux_path(path);
+ tb_dp_init_aux_path(path, pm_support);
paths[TB_DP_AUX_PATH_OUT] = path;
path = tb_path_alloc(tb, out, TB_DP_AUX_RX_HOPID, in,
TB_DP_AUX_RX_HOPID, link_nr, "AUX RX");
if (!path)
goto err_free;
- tb_dp_init_aux_path(path);
+ tb_dp_init_aux_path(path, pm_support);
paths[TB_DP_AUX_PATH_IN] = path;
return tunnel;