summaryrefslogtreecommitdiff
path: root/drivers/thunderbolt/xdomain.c
diff options
context:
space:
mode:
authorGil Fine <gil.fine@intel.com>2022-09-29 13:00:09 +0300
committerMika Westerberg <mika.westerberg@linux.intel.com>2023-06-16 09:53:27 +0300
commite111fb92513771cbcae70d0aa855c3ca20f48c1b (patch)
tree606ddb3983cae272f53c896161b3965c9700bcdb /drivers/thunderbolt/xdomain.c
parent6e21007d0f7e6723bb67e79aa3d0081419c403e8 (diff)
downloadlinux-e111fb92513771cbcae70d0aa855c3ca20f48c1b.tar.xz
thunderbolt: Add support for USB4 v2 80 Gb/s link
USB4 v2 bumps the per-lane speed up to 40 Gb/s. Also the lanes are always bonded which gives 80 Gb/s symmetric link (and 120/40 Gb/s asymmetric). This updates the speed and width of routers and XDomain connections to support the Gen 4 link. For now we keep the link as is even if it is already asymmetric. While there make tb_port_set_link_width() static. Signed-off-by: Gil Fine <gil.fine@intel.com> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Diffstat (limited to 'drivers/thunderbolt/xdomain.c')
-rw-r--r--drivers/thunderbolt/xdomain.c82
1 files changed, 68 insertions, 14 deletions
diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c
index 8389961b2d45..5b5566862318 100644
--- a/drivers/thunderbolt/xdomain.c
+++ b/drivers/thunderbolt/xdomain.c
@@ -1290,13 +1290,16 @@ static int tb_xdomain_link_state_change(struct tb_xdomain *xd,
static int tb_xdomain_bond_lanes_uuid_high(struct tb_xdomain *xd)
{
+ unsigned int width, width_mask;
struct tb_port *port;
- int ret, width;
+ int ret;
if (xd->target_link_width == LANE_ADP_CS_1_TARGET_WIDTH_SINGLE) {
- width = 1;
+ width = TB_LINK_WIDTH_SINGLE;
+ width_mask = width;
} else if (xd->target_link_width == LANE_ADP_CS_1_TARGET_WIDTH_DUAL) {
- width = 2;
+ width = TB_LINK_WIDTH_DUAL;
+ width_mask = width | TB_LINK_WIDTH_ASYM_TX | TB_LINK_WIDTH_ASYM_RX;
} else {
if (xd->state_retries-- > 0) {
dev_dbg(&xd->dev,
@@ -1328,15 +1331,16 @@ static int tb_xdomain_bond_lanes_uuid_high(struct tb_xdomain *xd)
return ret;
}
- ret = tb_port_wait_for_link_width(port, width, XDOMAIN_BONDING_TIMEOUT);
+ ret = tb_port_wait_for_link_width(port, width_mask,
+ XDOMAIN_BONDING_TIMEOUT);
if (ret) {
dev_warn(&xd->dev, "error waiting for link width to become %d\n",
- width);
+ width_mask);
return ret;
}
- port->bonded = width == 2;
- port->dual_link_port->bonded = width == 2;
+ port->bonded = width > TB_LINK_WIDTH_SINGLE;
+ port->dual_link_port->bonded = width > TB_LINK_WIDTH_SINGLE;
tb_port_update_credits(port);
tb_xdomain_update_link_attributes(xd);
@@ -1735,16 +1739,57 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(rx_speed, 0444, speed_show, NULL);
static DEVICE_ATTR(tx_speed, 0444, speed_show, NULL);
-static ssize_t lanes_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t rx_lanes_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
+ unsigned int width;
- return sysfs_emit(buf, "%u\n", xd->link_width);
+ switch (xd->link_width) {
+ case TB_LINK_WIDTH_SINGLE:
+ case TB_LINK_WIDTH_ASYM_RX:
+ width = 1;
+ break;
+ case TB_LINK_WIDTH_DUAL:
+ width = 2;
+ break;
+ case TB_LINK_WIDTH_ASYM_TX:
+ width = 3;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
+ return sysfs_emit(buf, "%u\n", width);
}
+static DEVICE_ATTR(rx_lanes, 0444, rx_lanes_show, NULL);
+
+static ssize_t tx_lanes_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
+ unsigned int width;
-static DEVICE_ATTR(rx_lanes, 0444, lanes_show, NULL);
-static DEVICE_ATTR(tx_lanes, 0444, lanes_show, NULL);
+ switch (xd->link_width) {
+ case TB_LINK_WIDTH_SINGLE:
+ case TB_LINK_WIDTH_ASYM_TX:
+ width = 1;
+ break;
+ case TB_LINK_WIDTH_DUAL:
+ width = 2;
+ break;
+ case TB_LINK_WIDTH_ASYM_RX:
+ width = 3;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
+ return sysfs_emit(buf, "%u\n", width);
+}
+static DEVICE_ATTR(tx_lanes, 0444, tx_lanes_show, NULL);
static struct attribute *xdomain_attrs[] = {
&dev_attr_device.attr,
@@ -1974,6 +2019,7 @@ void tb_xdomain_remove(struct tb_xdomain *xd)
*/
int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd)
{
+ unsigned int width_mask;
struct tb_port *port;
int ret;
@@ -1997,7 +2043,12 @@ int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd)
return ret;
}
- ret = tb_port_wait_for_link_width(port, 2, XDOMAIN_BONDING_TIMEOUT);
+ /* Any of the widths are all bonded */
+ width_mask = TB_LINK_WIDTH_DUAL | TB_LINK_WIDTH_ASYM_TX |
+ TB_LINK_WIDTH_ASYM_RX;
+
+ ret = tb_port_wait_for_link_width(port, width_mask,
+ XDOMAIN_BONDING_TIMEOUT);
if (ret) {
tb_port_warn(port, "failed to enable lane bonding\n");
return ret;
@@ -2024,8 +2075,11 @@ void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd)
port = tb_xdomain_downstream_port(xd);
if (port->dual_link_port) {
+ int ret;
+
tb_port_lane_bonding_disable(port);
- if (tb_port_wait_for_link_width(port, 1, 100) == -ETIMEDOUT)
+ ret = tb_port_wait_for_link_width(port, TB_LINK_WIDTH_SINGLE, 100);
+ if (ret == -ETIMEDOUT)
tb_port_warn(port, "timeout disabling lane bonding\n");
tb_port_disable(port->dual_link_port);
tb_port_update_credits(port);