Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+58 -2
+48 -2
drivers/thunderbolt/switch.c
··· 991 991 * tb_port_lane_bonding_enable() - Enable bonding on port 992 992 * @port: port to enable 993 993 * 994 - * Enable bonding by setting the link width of the port and the 995 - * other port in case of dual link port. 994 + * Enable bonding by setting the link width of the port and the other 995 + * port in case of dual link port. Does not wait for the link to 996 + * actually reach the bonded state so caller needs to call 997 + * tb_port_wait_for_link_width() before enabling any paths through the 998 + * link to make sure the link is in expected state. 996 999 * 997 1000 * Return: %0 in case of success and negative errno in case of error 998 1001 */ ··· 1044 1041 1045 1042 tb_port_set_link_width(port->dual_link_port, 1); 1046 1043 tb_port_set_link_width(port, 1); 1044 + } 1045 + 1046 + /** 1047 + * tb_port_wait_for_link_width() - Wait until link reaches specific width 1048 + * @port: Port to wait for 1049 + * @width: Expected link width (%1 or %2) 1050 + * @timeout_msec: Timeout in ms how long to wait 1051 + * 1052 + * Should be used after both ends of the link have been bonded (or 1053 + * bonding has been disabled) to wait until the link actually reaches 1054 + * the expected state. Returns %-ETIMEDOUT if the @width was not reached 1055 + * within the given timeout, %0 if it did. 1056 + */ 1057 + int tb_port_wait_for_link_width(struct tb_port *port, int width, 1058 + int timeout_msec) 1059 + { 1060 + ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec); 1061 + int ret; 1062 + 1063 + do { 1064 + ret = tb_port_get_link_width(port); 1065 + if (ret < 0) 1066 + return ret; 1067 + else if (ret == width) 1068 + return 0; 1069 + 1070 + usleep_range(1000, 2000); 1071 + } while (ktime_before(ktime_get(), timeout)); 1072 + 1073 + return -ETIMEDOUT; 1047 1074 } 1048 1075 1049 1076 static int tb_port_start_lane_initialization(struct tb_port *port) ··· 2465 2432 return ret; 2466 2433 } 2467 2434 2435 + ret = tb_port_wait_for_link_width(down, 2, 100); 2436 + if (ret) { 2437 + tb_port_warn(down, "timeout enabling lane bonding\n"); 2438 + return ret; 2439 + } 2440 + 2468 2441 tb_switch_update_link_attributes(sw); 2469 2442 2470 2443 tb_sw_dbg(sw, "lane bonding enabled\n"); ··· 2500 2461 2501 2462 tb_port_lane_bonding_disable(up); 2502 2463 tb_port_lane_bonding_disable(down); 2464 + 2465 + /* 2466 + * It is fine if we get other errors as the router might have 2467 + * been unplugged. 2468 + */ 2469 + if (tb_port_wait_for_link_width(down, 1, 100) == -ETIMEDOUT) 2470 + tb_sw_warn(sw, "timeout disabling lane bonding\n"); 2503 2471 2504 2472 tb_switch_update_link_attributes(sw); 2505 2473 tb_sw_dbg(sw, "lane bonding disabled\n");
+2
drivers/thunderbolt/tb.h
··· 883 883 int tb_port_state(struct tb_port *port); 884 884 int tb_port_lane_bonding_enable(struct tb_port *port); 885 885 void tb_port_lane_bonding_disable(struct tb_port *port); 886 + int tb_port_wait_for_link_width(struct tb_port *port, int width, 887 + int timeout_msec); 886 888 887 889 int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec); 888 890 int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap);
+8
drivers/thunderbolt/xdomain.c
··· 1527 1527 return ret; 1528 1528 } 1529 1529 1530 + ret = tb_port_wait_for_link_width(port, 2, 100); 1531 + if (ret) { 1532 + tb_port_warn(port, "timeout enabling lane bonding\n"); 1533 + return ret; 1534 + } 1535 + 1530 1536 tb_xdomain_update_link_attributes(xd); 1531 1537 1532 1538 dev_dbg(&xd->dev, "lane bonding enabled\n"); ··· 1554 1548 port = tb_port_at(xd->route, tb_xdomain_parent(xd)); 1555 1549 if (port->dual_link_port) { 1556 1550 tb_port_lane_bonding_disable(port); 1551 + if (tb_port_wait_for_link_width(port, 1, 100) == -ETIMEDOUT) 1552 + tb_port_warn(port, "timeout disabling lane bonding\n"); 1557 1553 tb_port_disable(port->dual_link_port); 1558 1554 tb_xdomain_update_link_attributes(xd); 1559 1555