thunderbolt: Wait for the lanes to actually bond

It may take some time until the two lanes enter bonded state so poll for
the link width to match what is expected before going forward. This ensures
the link is in expected state before we start establishing paths through
it.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
This commit is contained in:
Mika Westerberg 2021-03-22 16:54:54 +02:00
Родитель 02c5e7c2db
Коммит e7051beab8
3 изменённых файлов: 58 добавлений и 2 удалений

Просмотреть файл

@ -991,8 +991,11 @@ static int tb_port_set_link_width(struct tb_port *port, unsigned int width)
* tb_port_lane_bonding_enable() - Enable bonding on port
* @port: port to enable
*
* Enable bonding by setting the link width of the port and the
* other port in case of dual link port.
* Enable bonding by setting the link width of the port and the other
* port in case of dual link port. Does not wait for the link to
* actually reach the bonded state so caller needs to call
* tb_port_wait_for_link_width() before enabling any paths through the
* link to make sure the link is in expected state.
*
* Return: %0 in case of success and negative errno in case of error
*/
@ -1043,6 +1046,36 @@ void tb_port_lane_bonding_disable(struct tb_port *port)
tb_port_set_link_width(port, 1);
}
/**
* tb_port_wait_for_link_width() - Wait until link reaches specific width
* @port: Port to wait for
* @width: Expected link width (%1 or %2)
* @timeout_msec: Timeout in ms how long to wait
*
* Should be used after both ends of the link have been bonded (or
* bonding has been disabled) to wait until the link actually reaches
* the expected state. Returns %-ETIMEDOUT if the @width was not reached
* within the given timeout, %0 if it did.
*/
int tb_port_wait_for_link_width(struct tb_port *port, int width,
int timeout_msec)
{
ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec);
int ret;
do {
ret = tb_port_get_link_width(port);
if (ret < 0)
return ret;
else if (ret == width)
return 0;
usleep_range(1000, 2000);
} while (ktime_before(ktime_get(), timeout));
return -ETIMEDOUT;
}
static int tb_port_start_lane_initialization(struct tb_port *port)
{
int ret;
@ -2432,6 +2465,12 @@ int tb_switch_lane_bonding_enable(struct tb_switch *sw)
return ret;
}
ret = tb_port_wait_for_link_width(down, 2, 100);
if (ret) {
tb_port_warn(down, "timeout enabling lane bonding\n");
return ret;
}
tb_switch_update_link_attributes(sw);
tb_sw_dbg(sw, "lane bonding enabled\n");
@ -2462,6 +2501,13 @@ void tb_switch_lane_bonding_disable(struct tb_switch *sw)
tb_port_lane_bonding_disable(up);
tb_port_lane_bonding_disable(down);
/*
* It is fine if we get other errors as the router might have
* been unplugged.
*/
if (tb_port_wait_for_link_width(down, 1, 100) == -ETIMEDOUT)
tb_sw_warn(sw, "timeout disabling lane bonding\n");
tb_switch_update_link_attributes(sw);
tb_sw_dbg(sw, "lane bonding disabled\n");
}

Просмотреть файл

@ -883,6 +883,8 @@ int tb_port_get_link_width(struct tb_port *port);
int tb_port_state(struct tb_port *port);
int tb_port_lane_bonding_enable(struct tb_port *port);
void tb_port_lane_bonding_disable(struct tb_port *port);
int tb_port_wait_for_link_width(struct tb_port *port, int width,
int timeout_msec);
int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap);

Просмотреть файл

@ -1527,6 +1527,12 @@ int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd)
return ret;
}
ret = tb_port_wait_for_link_width(port, 2, 100);
if (ret) {
tb_port_warn(port, "timeout enabling lane bonding\n");
return ret;
}
tb_xdomain_update_link_attributes(xd);
dev_dbg(&xd->dev, "lane bonding enabled\n");
@ -1548,6 +1554,8 @@ void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd)
port = tb_port_at(xd->route, tb_xdomain_parent(xd));
if (port->dual_link_port) {
tb_port_lane_bonding_disable(port);
if (tb_port_wait_for_link_width(port, 1, 100) == -ETIMEDOUT)
tb_port_warn(port, "timeout disabling lane bonding\n");
tb_port_disable(port->dual_link_port);
tb_xdomain_update_link_attributes(xd);