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>
This commit is contained in:
Gil Fine 2023-12-04 15:14:58 +02:00 committed by Mika Westerberg
parent 2cd3da4e37
commit ba2a2a86de
3 changed files with 36 additions and 7 deletions

View File

@ -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));
}
/**

View File

@ -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;

View File

@ -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;