aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/thunderbolt/switch.c
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2021-03-22 16:54:54 +0200
committerMika Westerberg <mika.westerberg@linux.intel.com>2021-06-01 10:48:59 +0300
commite7051beab8393dc614f7ea3969aa03bc490db1d6 (patch)
tree0bdfb38a1b95f4af72306eb075789978b2bb353b /drivers/thunderbolt/switch.c
parentthunderbolt: Move nfc_credits field to struct tb_path_hop (diff)
downloadwireguard-linux-e7051beab8393dc614f7ea3969aa03bc490db1d6.tar.xz
wireguard-linux-e7051beab8393dc614f7ea3969aa03bc490db1d6.zip
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>
Diffstat (limited to 'drivers/thunderbolt/switch.c')
-rw-r--r--drivers/thunderbolt/switch.c50
1 files changed, 48 insertions, 2 deletions
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 0edc452c2ac9..d5420eefe25d 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -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");
}