diff options
Diffstat (limited to 'drivers/thunderbolt/usb4.c')
-rw-r--r-- | drivers/thunderbolt/usb4.c | 147 |
1 files changed, 112 insertions, 35 deletions
diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 05ddb224c4..1515eff8cc 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -290,7 +290,7 @@ int usb4_switch_setup(struct tb_switch *sw) } /* TBT3 supported by the CM */ - val |= ROUTER_CS_5_C3S; + val &= ~ROUTER_CS_5_CNS; return tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1); } @@ -1455,6 +1455,112 @@ bool usb4_port_clx_supported(struct tb_port *port) } /** + * usb4_port_asym_supported() - If the port supports asymmetric link + * @port: USB4 port + * + * Checks if the port and the cable supports asymmetric link and returns + * %true in that case. + */ +bool usb4_port_asym_supported(struct tb_port *port) +{ + u32 val; + + if (!port->cap_usb4) + return false; + + if (tb_port_read(port, &val, TB_CFG_PORT, port->cap_usb4 + PORT_CS_18, 1)) + return false; + + return !!(val & PORT_CS_18_CSA); +} + +/** + * usb4_port_asym_set_link_width() - Set link width to asymmetric or symmetric + * @port: USB4 port + * @width: Asymmetric width to configure + * + * Sets USB4 port link width to @width. Can be called for widths where + * usb4_port_asym_width_supported() returned @true. + */ +int usb4_port_asym_set_link_width(struct tb_port *port, enum tb_link_width width) +{ + u32 val; + int ret; + + if (!port->cap_phy) + return -EINVAL; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); + if (ret) + return ret; + + val &= ~LANE_ADP_CS_1_TARGET_WIDTH_ASYM_MASK; + switch (width) { + case TB_LINK_WIDTH_DUAL: + val |= FIELD_PREP(LANE_ADP_CS_1_TARGET_WIDTH_ASYM_MASK, + LANE_ADP_CS_1_TARGET_WIDTH_ASYM_DUAL); + break; + case TB_LINK_WIDTH_ASYM_TX: + val |= FIELD_PREP(LANE_ADP_CS_1_TARGET_WIDTH_ASYM_MASK, + LANE_ADP_CS_1_TARGET_WIDTH_ASYM_TX); + break; + case TB_LINK_WIDTH_ASYM_RX: + val |= FIELD_PREP(LANE_ADP_CS_1_TARGET_WIDTH_ASYM_MASK, + LANE_ADP_CS_1_TARGET_WIDTH_ASYM_RX); + break; + default: + return -EINVAL; + } + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); +} + +/** + * usb4_port_asym_start() - Start symmetry change and wait for completion + * @port: USB4 port + * + * Start symmetry change of the link to asymmetric or symmetric + * (according to what was previously set in tb_port_set_link_width(). + * Wait for completion of the change. + * + * Returns %0 in case of success, %-ETIMEDOUT if case of timeout or + * a negative errno in case of a failure. + */ +int usb4_port_asym_start(struct tb_port *port) +{ + int ret; + u32 val; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_usb4 + PORT_CS_19, 1); + if (ret) + return ret; + + val &= ~PORT_CS_19_START_ASYM; + val |= FIELD_PREP(PORT_CS_19_START_ASYM, 1); + + ret = tb_port_write(port, &val, TB_CFG_PORT, + port->cap_usb4 + PORT_CS_19, 1); + if (ret) + return ret; + + /* + * Wait for PORT_CS_19_START_ASYM to be 0. This means the USB4 + * port started the symmetry transition. + */ + ret = usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_19, + PORT_CS_19_START_ASYM, 0, 1000); + if (ret) + return ret; + + /* Then wait for the transtion to be completed */ + return usb4_port_wait_for_bit(port, port->cap_usb4 + PORT_CS_18, + PORT_CS_18_TIP, 0, 5000); +} + +/** * usb4_port_margining_caps() - Read USB4 port marginig capabilities * @port: USB4 port * @caps: Array with at least two elements to hold the results @@ -1946,35 +2052,6 @@ int usb4_usb3_port_max_link_rate(struct tb_port *port) return usb4_usb3_port_max_bandwidth(port, ret); } -/** - * usb4_usb3_port_actual_link_rate() - Established USB3 link rate - * @port: USB3 adapter port - * - * Return actual established link rate of a USB3 adapter in Mb/s. If the - * link is not up returns %0 and negative errno in case of failure. - */ -int usb4_usb3_port_actual_link_rate(struct tb_port *port) -{ - int ret, lr; - u32 val; - - if (!tb_port_is_usb3_down(port) && !tb_port_is_usb3_up(port)) - return -EINVAL; - - ret = tb_port_read(port, &val, TB_CFG_PORT, - port->cap_adap + ADP_USB3_CS_4, 1); - if (ret) - return ret; - - if (!(val & ADP_USB3_CS_4_ULV)) - return 0; - - lr = val & ADP_USB3_CS_4_ALR_MASK; - ret = lr == ADP_USB3_CS_4_ALR_20G ? 20000 : 10000; - - return usb4_usb3_port_max_bandwidth(port, ret); -} - static int usb4_usb3_port_cm_request(struct tb_port *port, bool request) { int ret; @@ -2234,13 +2311,13 @@ int usb4_usb3_port_release_bandwidth(struct tb_port *port, int *upstream_bw, goto err_request; /* - * Always keep 1000 Mb/s to make sure xHCI has at least some + * Always keep 900 Mb/s to make sure xHCI has at least some * bandwidth available for isochronous traffic. */ - if (consumed_up < 1000) - consumed_up = 1000; - if (consumed_down < 1000) - consumed_down = 1000; + if (consumed_up < 900) + consumed_up = 900; + if (consumed_down < 900) + consumed_down = 900; ret = usb4_usb3_port_write_allocated_bandwidth(port, consumed_up, consumed_down); |