]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
thunderbolt: Introduce tb_port_reset()
authorSanath S <Sanath.S@amd.com>
Sat, 13 Jan 2024 09:39:57 +0000 (11:39 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 27 Apr 2024 15:11:35 +0000 (17:11 +0200)
commit 01da6b99d49f60b1edead44e33569b1a2e9f49b7 upstream.

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>
Cc: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/thunderbolt/lc.c
drivers/thunderbolt/switch.c
drivers/thunderbolt/tb.h
drivers/thunderbolt/tb_regs.h
drivers/thunderbolt/usb4.c

index 633970fbe9b05904ca4f46876a3561265ed83318..63cb4b6afb718aca9689a01695d9625d83d29d03 100644 (file)
@@ -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);
index 509b99af5087be8e832f6ad85744493f4a12059a..d8e6ca9895e7b1dcb250e53a7eb4aea0a6689e18 100644 (file)
@@ -675,6 +675,13 @@ int tb_port_disable(struct tb_port *port)
        return __tb_port_enable(port, false);
 }
 
+static int tb_port_reset(struct tb_port *port)
+{
+       if (tb_switch_is_usb4(port->sw))
+               return port->cap_usb4 ? usb4_port_reset(port) : 0;
+       return tb_lc_reset_port(port);
+}
+
 /*
  * tb_init_port() - initialize a port
  *
index 4893d2c7ac96864a09995298caa889a36493585b..999d79aa43d581e031f0cbef2c9abeb434e92770 100644 (file)
@@ -1119,6 +1119,7 @@ int tb_drom_read(struct tb_switch *sw);
 int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid);
 
 int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid);
+int tb_lc_reset_port(struct tb_port *port);
 int tb_lc_configure_port(struct tb_port *port);
 void tb_lc_unconfigure_port(struct tb_port *port);
 int tb_lc_configure_xdomain(struct tb_port *port);
@@ -1251,6 +1252,7 @@ void usb4_switch_remove_ports(struct tb_switch *sw);
 
 int usb4_port_unlock(struct tb_port *port);
 int usb4_port_hotplug_enable(struct tb_port *port);
+int usb4_port_reset(struct tb_port *port);
 int usb4_port_configure(struct tb_port *port);
 void usb4_port_unconfigure(struct tb_port *port);
 int usb4_port_configure_xdomain(struct tb_port *port, struct tb_xdomain *xd);
index 580277dc911520a8d96f45de877ce971aceb9c47..b4aed158438bd7ea6d33786c355aeaf7730fe681 100644 (file)
@@ -383,6 +383,7 @@ struct tb_regs_port_header {
 #define PORT_CS_18_WODS                                BIT(17)
 #define PORT_CS_18_WOU4S                       BIT(18)
 #define PORT_CS_19                             0x13
+#define PORT_CS_19_DPR                         BIT(0)
 #define PORT_CS_19_PC                          BIT(3)
 #define PORT_CS_19_PID                         BIT(4)
 #define PORT_CS_19_WOC                         BIT(16)
@@ -579,6 +580,9 @@ struct tb_regs_hop {
 #define TB_LC_POWER                            0x740
 
 /* Link controller registers */
+#define TB_LC_PORT_MODE                                0x26
+#define TB_LC_PORT_MODE_DPR                    BIT(0)
+
 #define TB_LC_CS_42                            0x2a
 #define TB_LC_CS_42_USB_PLUGGED                        BIT(31)
 
index 13c779e23011bb4a77542904bbe22cc23d84ae4d..6b29cbd02ea154ef253d61d8879b7bfbed39b092 100644 (file)
@@ -1113,6 +1113,45 @@ int usb4_port_hotplug_enable(struct tb_port *port)
        return tb_port_write(port, &val, TB_CFG_PORT, ADP_CS_5, 1);
 }
 
+/**
+ * usb4_port_reset() - Issue downstream port reset
+ * @port: USB4 port to reset
+ *
+ * Issues downstream port reset to @port.
+ */
+int usb4_port_reset(struct tb_port *port)
+{
+       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;
+
+       val |= PORT_CS_19_DPR;
+
+       ret = tb_port_write(port, &val, TB_CFG_PORT,
+                           port->cap_usb4 + PORT_CS_19, 1);
+       if (ret)
+               return ret;
+
+       fsleep(10000);
+
+       ret = tb_port_read(port, &val, TB_CFG_PORT,
+                          port->cap_usb4 + PORT_CS_19, 1);
+       if (ret)
+               return ret;
+
+       val &= ~PORT_CS_19_DPR;
+
+       return tb_port_write(port, &val, TB_CFG_PORT,
+                            port->cap_usb4 + PORT_CS_19, 1);
+}
+
 static int usb4_port_set_configured(struct tb_port *port, bool configured)
 {
        int ret;