summaryrefslogtreecommitdiff
path: root/drivers/thunderbolt/lc.c
diff options
context:
space:
mode:
authorSanath S <Sanath.S@amd.com>2024-01-13 12:39:57 +0300
committerMika Westerberg <mika.westerberg@linux.intel.com>2024-01-22 14:21:06 +0300
commit01da6b99d49f60b1edead44e33569b1a2e9f49b7 (patch)
treee71543de189d5b6c50aca793f117a91424366362 /drivers/thunderbolt/lc.c
parent6613476e225e090cc9aad49be7fa504e290dd33d (diff)
downloadlinux-01da6b99d49f60b1edead44e33569b1a2e9f49b7.tar.xz
thunderbolt: Introduce tb_port_reset()
Introduce a function that issues Downstream Port Reset to a USB4 port. This supports Thunderbolt 2, 3 and USB4 routers. Signed-off-by: Sanath S <Sanath.S@amd.com> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Diffstat (limited to 'drivers/thunderbolt/lc.c')
-rw-r--r--drivers/thunderbolt/lc.c45
1 files changed, 45 insertions, 0 deletions
diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c
index 633970fbe9b0..63cb4b6afb71 100644
--- a/drivers/thunderbolt/lc.c
+++ b/drivers/thunderbolt/lc.c
@@ -6,6 +6,8 @@
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
*/
+#include <linux/delay.h>
+
#include "tb.h"
/**
@@ -45,6 +47,49 @@ static int find_port_lc_cap(struct tb_port *port)
return sw->cap_lc + start + phys * size;
}
+/**
+ * tb_lc_reset_port() - Trigger downstream port reset through LC
+ * @port: Port that is reset
+ *
+ * Triggers downstream port reset through link controller registers.
+ * Returns %0 in case of success negative errno otherwise. Only supports
+ * non-USB4 routers with link controller (that's Thunderbolt 2 and
+ * Thunderbolt 3).
+ */
+int tb_lc_reset_port(struct tb_port *port)
+{
+ struct tb_switch *sw = port->sw;
+ int cap, ret;
+ u32 mode;
+
+ if (sw->generation < 2)
+ return -EINVAL;
+
+ cap = find_port_lc_cap(port);
+ if (cap < 0)
+ return cap;
+
+ ret = tb_sw_read(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1);
+ if (ret)
+ return ret;
+
+ mode |= TB_LC_PORT_MODE_DPR;
+
+ ret = tb_sw_write(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1);
+ if (ret)
+ return ret;
+
+ fsleep(10000);
+
+ ret = tb_sw_read(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1);
+ if (ret)
+ return ret;
+
+ mode &= ~TB_LC_PORT_MODE_DPR;
+
+ return tb_sw_write(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1);
+}
+
static int tb_lc_set_port_configured(struct tb_port *port, bool configured)
{
bool upstream = tb_is_upstream_port(port);