@@ -1013,6 +1013,7 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port,
struct tb_port *dst_port, int requested_up,
int requested_down)
{
+ bool link_recovery_up = false, link_recovery_down = false;
bool clx = false, clx_disabled = false, downstream;
struct tb_switch *sw;
struct tb_port *up;
@@ -1075,15 +1076,19 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port,
continue;
/*
- * Disable CL states before doing any transitions. We
- * delayed it until now that we know there is a real
- * transition taking place.
+ * Disable CL states and Link Recovery before doing any
+ * transitions. We delayed it until now that we know
+ * there is a real transition taking place.
*/
if (!clx_disabled) {
clx = tb_disable_clx(sw);
clx_disabled = true;
}
+ /* Ignore non-critical errors of disabling Link Recovery */
+ link_recovery_up = (0 < usb4_port_link_recovery_disable(up));
+ link_recovery_down = (0 < usb4_port_link_recovery_disable(down));
+
tb_sw_dbg(up->sw, "configuring asymmetric link\n");
/*
@@ -1091,6 +1096,13 @@ static int tb_configure_asym(struct tb *tb, struct tb_port *src_port,
* transtion the link into asymmetric now.
*/
ret = tb_switch_set_link_width(up->sw, width_up);
+
+ /* Re-enable Link Recovery if they were previously enabled */
+ if (link_recovery_up)
+ usb4_port_link_recovery_enable(up);
+ if (link_recovery_down)
+ usb4_port_link_recovery_enable(down);
+
if (ret) {
tb_sw_warn(up->sw, "failed to set link width\n");
break;
@@ -1332,6 +1332,9 @@ int usb4_port_router_online(struct tb_port *port);
int usb4_port_enumerate_retimers(struct tb_port *port);
bool usb4_port_clx_supported(struct tb_port *port);
+int usb4_port_link_recovery_enable(struct tb_port *port);
+int usb4_port_link_recovery_disable(struct tb_port *port);
+
bool usb4_port_asym_supported(struct tb_port *port);
int usb4_port_asym_set_link_width(struct tb_port *port, enum tb_link_width width);
int usb4_port_asym_start(struct tb_port *port);
@@ -398,6 +398,7 @@ struct tb_regs_port_header {
#define PORT_CS_19_WOD BIT(17)
#define PORT_CS_19_WOU4 BIT(18)
#define PORT_CS_19_START_ASYM BIT(24)
+#define PORT_CS_19_ELR BIT(31)
/* Display Port adapter registers */
#define ADP_DP_CS_0 0x00
@@ -10,6 +10,7 @@
#include <linux/delay.h>
#include <linux/ktime.h>
#include <linux/units.h>
+#include <linux/string_helpers.h>
#include "sb_regs.h"
#include "tb.h"
@@ -1518,6 +1519,82 @@ bool usb4_port_clx_supported(struct tb_port *port)
return !!(val & PORT_CS_18_CPS);
}
+static int __usb4_port_link_recovery_enable(struct tb_port *port, bool enable)
+{
+ bool was_enable;
+ int ret;
+ u32 val;
+
+ if (!port->cap_usb4)
+ return -EINVAL;
+
+ ret = tb_port_read(port, &val, TB_CFG_PORT,
+ port->cap_usb4 + PORT_CS_19, 1);
+ if (ret)
+ return ret;
+
+ was_enable = !!(val & PORT_CS_19_ELR);
+
+ if (enable)
+ val |= PORT_CS_19_ELR;
+ else
+ val &= ~PORT_CS_19_ELR;
+
+ ret = tb_port_write(port, &val, TB_CFG_PORT,
+ port->cap_usb4 + PORT_CS_19, 1);
+ if (ret)
+ return ret;
+
+ tb_port_dbg(port, "link recovery %s\n", str_enabled_disabled(enable));
+ return was_enable ? 1 : 0;
+}
+
+/**
+ * usb4_port_link_recovery_enable() - Enable the Link Recovery
+ * @port: USB4 port
+ *
+ * Enables the Link Recovery for @port.
+ *
+ * Returns:
+ * * %-EINVAL - Capability not present.
+ * * %-ENODEV - Switch unplugged.
+ * * %-ETIMEDOUT - Config space access timed out.
+ * * %0 - Successfully enabled.
+ * * %1 - Already enabled.
+ */
+int usb4_port_link_recovery_enable(struct tb_port *port)
+{
+ int ret = __usb4_port_link_recovery_enable(port, true);
+
+ if (ret < 0)
+ tb_port_warn(port, "failed to enable link recovery\n");
+
+ return ret;
+}
+
+/**
+ * usb4_port_link_recovery_disable() - Disable the Link Recovery
+ * @port: USB4 port
+ *
+ * Disables the Link Recovery for @port.
+ *
+ * Returns:
+ * * %-EINVAL - Capability not present.
+ * * %-ENODEV - Switch unplugged.
+ * * %-ETIMEDOUT - Config space access timed out.
+ * * %0 - Already disabled.
+ * * %1 - Successfully disabled.
+ */
+int usb4_port_link_recovery_disable(struct tb_port *port)
+{
+ int ret = __usb4_port_link_recovery_enable(port, false);
+
+ if (ret < 0)
+ tb_port_warn(port, "failed to disable link recovery\n");
+
+ return ret;
+}
+
/**
* usb4_port_asym_supported() - If the port supports asymmetric link
* @port: USB4 port