]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blobdiff - queue-6.8/thunderbolt-introduce-tb_port_reset.patch
6.8-stable patches
[thirdparty/kernel/stable-queue.git] / queue-6.8 / thunderbolt-introduce-tb_port_reset.patch
diff --git a/queue-6.8/thunderbolt-introduce-tb_port_reset.patch b/queue-6.8/thunderbolt-introduce-tb_port_reset.patch
new file mode 100644 (file)
index 0000000..cb63fd3
--- /dev/null
@@ -0,0 +1,187 @@
+From 01da6b99d49f60b1edead44e33569b1a2e9f49b7 Mon Sep 17 00:00:00 2001
+From: Sanath S <Sanath.S@amd.com>
+Date: Sat, 13 Jan 2024 11:39:57 +0200
+Subject: thunderbolt: Introduce tb_port_reset()
+
+From: Sanath S <Sanath.S@amd.com>
+
+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      |   45 ++++++++++++++++++++++++++++++++++++++++++
+ drivers/thunderbolt/switch.c  |    7 ++++++
+ drivers/thunderbolt/tb.h      |    2 +
+ drivers/thunderbolt/tb_regs.h |    4 +++
+ drivers/thunderbolt/usb4.c    |   39 ++++++++++++++++++++++++++++++++++++
+ 5 files changed, 97 insertions(+)
+
+--- 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_po
+       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);
+--- a/drivers/thunderbolt/switch.c
++++ b/drivers/thunderbolt/switch.c
+@@ -676,6 +676,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
+  *
+--- a/drivers/thunderbolt/tb.h
++++ b/drivers/thunderbolt/tb.h
+@@ -1173,6 +1173,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);
+@@ -1305,6 +1306,7 @@ void usb4_switch_remove_ports(struct tb_
+ 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);
+--- a/drivers/thunderbolt/tb_regs.h
++++ b/drivers/thunderbolt/tb_regs.h
+@@ -389,6 +389,7 @@ struct tb_regs_port_header {
+ #define PORT_CS_18_CSA                                BIT(22)
+ #define PORT_CS_18_TIP                                BIT(24)
+ #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)
+@@ -584,6 +585,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)
+--- a/drivers/thunderbolt/usb4.c
++++ b/drivers/thunderbolt/usb4.c
+@@ -1113,6 +1113,45 @@ int usb4_port_hotplug_enable(struct tb_p
+       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;