summaryrefslogtreecommitdiff
path: root/drivers/thunderbolt
diff options
context:
space:
mode:
authorGil Fine <gil.fine@linux.intel.com>2023-12-04 15:14:58 +0200
committerMika Westerberg <mika.westerberg@linux.intel.com>2023-12-22 12:58:39 +0200
commitba2a2a86de04e67bb1d7f8251894eb11eed062e9 (patch)
tree3f46dee63826431f3cb3e2bb1a1243b1f20ea7bd /drivers/thunderbolt
parent2cd3da4e37453019e21a486d9de3144f46b4fdf7 (diff)
thunderbolt: Keep link as asymmetric if preferred by hardware
In case of the link is brought up as asymmetric (due to hardware preference), we honor that and don't transition it to symmetric, unless a router with symmetric link got plugged below, in the topology (and a bandwidth allows transition to symmetric). Signed-off-by: Gil Fine <gil.fine@linux.intel.com> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Diffstat (limited to 'drivers/thunderbolt')
-rw-r--r--drivers/thunderbolt/switch.c13
-rw-r--r--drivers/thunderbolt/tb.c28
-rw-r--r--drivers/thunderbolt/tb.h2
3 files changed, 36 insertions, 7 deletions
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 7873b173a351..e5a622b9acc3 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -2773,6 +2773,19 @@ static void tb_switch_link_init(struct tb_switch *sw)
if (down->dual_link_port)
down->dual_link_port->bonded = bonded;
tb_port_update_credits(down);
+
+ if (tb_port_get_link_generation(up) < 4)
+ return;
+
+ /*
+ * Set the Gen 4 preferred link width. This is what the router
+ * prefers when the link is brought up. If the router does not
+ * support asymmetric link configuration, this also will be set
+ * to TB_LINK_WIDTH_DUAL.
+ */
+ sw->preferred_link_width = sw->link_width;
+ tb_sw_dbg(sw, "preferred link width %s\n",
+ tb_width_name(sw->preferred_link_width));
}
/**
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index bd211e97ee4b..825cc2f4d8ef 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -1173,14 +1173,15 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port,
* @dst_port: Destination adapter
* @requested_up: New lower bandwidth request upstream (Mb/s)
* @requested_down: New lower bandwidth request downstream (Mb/s)
+ * @keep_asym: Keep asymmetric link if preferred
*
* Goes over each link from @src_port to @dst_port and tries to
* transition the link to symmetric if the currently consumed bandwidth
- * allows.
+ * allows and link asymmetric preference is ignored (if @keep_asym is %false).
*/
static int tb_configure_sym(struct tb *tb, struct tb_port *src_port,
struct tb_port *dst_port, int requested_up,
- int requested_down)
+ int requested_down, bool keep_asym)
{
bool clx = false, clx_disabled = false, downstream;
struct tb_switch *sw;
@@ -1229,6 +1230,19 @@ static int tb_configure_sym(struct tb *tb, struct tb_port *src_port,
if (up->sw->link_width == TB_LINK_WIDTH_DUAL)
continue;
+ /*
+ * Here consumed < threshold so we can transition the
+ * link to symmetric.
+ *
+ * However, if the router prefers asymmetric link we
+ * honor that (unless @keep_asym is %false).
+ */
+ if (keep_asym &&
+ up->sw->preferred_link_width > TB_LINK_WIDTH_DUAL) {
+ tb_sw_dbg(up->sw, "keeping preferred asymmetric link\n");
+ continue;
+ }
+
/* Disable CL states before doing any transitions */
if (!clx_disabled) {
clx = tb_disable_clx(sw);
@@ -1282,7 +1296,7 @@ static void tb_configure_link(struct tb_port *down, struct tb_port *up,
struct tb_port *host_port;
host_port = tb_port_at(tb_route(sw), tb->root_switch);
- tb_configure_sym(tb, host_port, up, 0, 0);
+ tb_configure_sym(tb, host_port, up, 0, 0, false);
}
/* Set the link configured */
@@ -1467,7 +1481,7 @@ static void tb_deactivate_and_free_tunnel(struct tb_tunnel *tunnel)
* If bandwidth on a link is < asym_threshold
* transition the link to symmetric.
*/
- tb_configure_sym(tb, src_port, dst_port, 0, 0);
+ tb_configure_sym(tb, src_port, dst_port, 0, 0, true);
/* Now we can allow the domain to runtime suspend again */
pm_runtime_mark_last_busy(&dst_port->sw->dev);
pm_runtime_put_autosuspend(&dst_port->sw->dev);
@@ -2289,7 +2303,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up,
* If bandwidth on a link is < asym_threshold transition
* the link to symmetric.
*/
- tb_configure_sym(tb, in, out, *requested_up, *requested_down);
+ tb_configure_sym(tb, in, out, *requested_up, *requested_down, true);
/*
* If requested bandwidth is less or equal than what is
* currently allocated to that tunnel we simply change
@@ -2332,7 +2346,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up,
ret = tb_configure_asym(tb, in, out, *requested_up,
*requested_down);
if (ret) {
- tb_configure_sym(tb, in, out, 0, 0);
+ tb_configure_sym(tb, in, out, 0, 0, true);
return ret;
}
@@ -2340,7 +2354,7 @@ static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up,
requested_down);
if (ret) {
tb_tunnel_warn(tunnel, "failed to allocate bandwidth\n");
- tb_configure_sym(tb, in, out, 0, 0);
+ tb_configure_sym(tb, in, out, 0, 0, true);
}
} else {
ret = -ENOBUFS;
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 1760c21e5b12..997c5a536905 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -125,6 +125,7 @@ struct tb_switch_tmu {
* @device_name: Name of the device (or %NULL if not known)
* @link_speed: Speed of the link in Gb/s
* @link_width: Width of the upstream facing link
+ * @preferred_link_width: Router preferred link width (only set for Gen 4 links)
* @link_usb4: Upstream link is USB4
* @generation: Switch Thunderbolt generation
* @cap_plug_events: Offset to the plug events capability (%0 if not found)
@@ -178,6 +179,7 @@ struct tb_switch {
const char *device_name;
unsigned int link_speed;
enum tb_link_width link_width;
+ enum tb_link_width preferred_link_width;
bool link_usb4;
unsigned int generation;
int cap_plug_events;