]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.8-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 23 Apr 2024 19:51:57 +0000 (12:51 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 23 Apr 2024 19:51:57 +0000 (12:51 -0700)
added patches:
thunderbolt-introduce-tb_path_deactivate_hop.patch
thunderbolt-introduce-tb_port_reset.patch
thunderbolt-make-tb_switch_reset-support-thunderbolt-2-3-and-usb4-routers.patch
thunderbolt-reset-topology-created-by-the-boot-firmware.patch

queue-6.8/series
queue-6.8/thunderbolt-introduce-tb_path_deactivate_hop.patch [new file with mode: 0644]
queue-6.8/thunderbolt-introduce-tb_port_reset.patch [new file with mode: 0644]
queue-6.8/thunderbolt-make-tb_switch_reset-support-thunderbolt-2-3-and-usb4-routers.patch [new file with mode: 0644]
queue-6.8/thunderbolt-reset-topology-created-by-the-boot-firmware.patch [new file with mode: 0644]

index 4f2bf462b456f25a8a85559347a7e9c2d8e75da8..b708fd4142b36fef22858c0039299df55d141735 100644 (file)
@@ -64,6 +64,10 @@ iommufd-add-config-needed-for-iommufd_fail_nth.patch
 drm-nv04-fix-out-of-bounds-access.patch
 drm-v3d-don-t-increment-enabled_ns-twice.patch
 userfaultfd-change-src_folio-after-ensuring-it-s-unpinned-in-uffdio_move.patch
+thunderbolt-introduce-tb_port_reset.patch
+thunderbolt-introduce-tb_path_deactivate_hop.patch
+thunderbolt-make-tb_switch_reset-support-thunderbolt-2-3-and-usb4-routers.patch
+thunderbolt-reset-topology-created-by-the-boot-firmware.patch
 drm-panel-visionox-rm69299-don-t-unregister-dsi-devi.patch
 drm-radeon-make-fstrict-flex-arrays-3-happy.patch
 alsa-hda-realtek-fix-volumn-control-of-thinkbook-16p.patch
diff --git a/queue-6.8/thunderbolt-introduce-tb_path_deactivate_hop.patch b/queue-6.8/thunderbolt-introduce-tb_path_deactivate_hop.patch
new file mode 100644 (file)
index 0000000..9dd8a7a
--- /dev/null
@@ -0,0 +1,53 @@
+From b35c1d7b11da8c08b14147bbe87c2c92f7a83f8b Mon Sep 17 00:00:00 2001
+From: Sanath S <Sanath.S@amd.com>
+Date: Sat, 13 Jan 2024 11:42:23 +0200
+Subject: thunderbolt: Introduce tb_path_deactivate_hop()
+
+From: Sanath S <Sanath.S@amd.com>
+
+commit b35c1d7b11da8c08b14147bbe87c2c92f7a83f8b upstream.
+
+This function can be used to clear path config space of an adapter. Make
+it available for other files in this driver.
+
+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/path.c |   13 +++++++++++++
+ drivers/thunderbolt/tb.h   |    1 +
+ 2 files changed, 14 insertions(+)
+
+--- a/drivers/thunderbolt/path.c
++++ b/drivers/thunderbolt/path.c
+@@ -446,6 +446,19 @@ static int __tb_path_deactivate_hop(stru
+       return -ETIMEDOUT;
+ }
++/**
++ * tb_path_deactivate_hop() - Deactivate one path in path config space
++ * @port: Lane or protocol adapter
++ * @hop_index: HopID of the path to be cleared
++ *
++ * This deactivates or clears a single path config space entry at
++ * @hop_index. Returns %0 in success and negative errno otherwise.
++ */
++int tb_path_deactivate_hop(struct tb_port *port, int hop_index)
++{
++      return __tb_path_deactivate_hop(port, hop_index, true);
++}
++
+ static void __tb_path_deactivate_hops(struct tb_path *path, int first_hop)
+ {
+       int i, res;
+--- a/drivers/thunderbolt/tb.h
++++ b/drivers/thunderbolt/tb.h
+@@ -1154,6 +1154,7 @@ struct tb_path *tb_path_alloc(struct tb
+ void tb_path_free(struct tb_path *path);
+ int tb_path_activate(struct tb_path *path);
+ void tb_path_deactivate(struct tb_path *path);
++int tb_path_deactivate_hop(struct tb_port *port, int hop_index);
+ bool tb_path_is_invalid(struct tb_path *path);
+ bool tb_path_port_on_path(const struct tb_path *path,
+                         const struct tb_port *port);
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;
diff --git a/queue-6.8/thunderbolt-make-tb_switch_reset-support-thunderbolt-2-3-and-usb4-routers.patch b/queue-6.8/thunderbolt-make-tb_switch_reset-support-thunderbolt-2-3-and-usb4-routers.patch
new file mode 100644 (file)
index 0000000..1fd6d14
--- /dev/null
@@ -0,0 +1,174 @@
+From ec8162b3f0683ae08a21f20517cf49272b07ee0b Mon Sep 17 00:00:00 2001
+From: Sanath S <Sanath.S@amd.com>
+Date: Sat, 13 Jan 2024 11:47:26 +0200
+Subject: thunderbolt: Make tb_switch_reset() support Thunderbolt 2, 3 and USB4 routers
+
+From: Sanath S <Sanath.S@amd.com>
+
+commit ec8162b3f0683ae08a21f20517cf49272b07ee0b upstream.
+
+Currently tb_switch_reset() only did something for Thunderbolt 1
+devices. Expand this to support all generations, including USB4, and
+both host and device 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/switch.c  |  123 +++++++++++++++++++++++++++++++++++++-----
+ drivers/thunderbolt/tb_regs.h |    2 
+ 2 files changed, 111 insertions(+), 14 deletions(-)
+
+--- a/drivers/thunderbolt/switch.c
++++ b/drivers/thunderbolt/switch.c
+@@ -1541,29 +1541,124 @@ static void tb_dump_switch(const struct
+              regs->__unknown1, regs->__unknown4);
+ }
++static int tb_switch_reset_host(struct tb_switch *sw)
++{
++      if (sw->generation > 1) {
++              struct tb_port *port;
++
++              tb_switch_for_each_port(sw, port) {
++                      int i, ret;
++
++                      /*
++                       * For lane adapters we issue downstream port
++                       * reset and clear up path config spaces.
++                       *
++                       * For protocol adapters we disable the path and
++                       * clear path config space one by one (from 8 to
++                       * Max Input HopID of the adapter).
++                       */
++                      if (tb_port_is_null(port) && !tb_is_upstream_port(port)) {
++                              ret = tb_port_reset(port);
++                              if (ret)
++                                      return ret;
++                      } else if (tb_port_is_usb3_down(port) ||
++                                 tb_port_is_usb3_up(port)) {
++                              tb_usb3_port_enable(port, false);
++                      } else if (tb_port_is_dpin(port) ||
++                                 tb_port_is_dpout(port)) {
++                              tb_dp_port_enable(port, false);
++                      } else if (tb_port_is_pcie_down(port) ||
++                                 tb_port_is_pcie_up(port)) {
++                              tb_pci_port_enable(port, false);
++                      } else {
++                              continue;
++                      }
++
++                      /* Cleanup path config space of protocol adapter */
++                      for (i = TB_PATH_MIN_HOPID;
++                           i <= port->config.max_in_hop_id; i++) {
++                              ret = tb_path_deactivate_hop(port, i);
++                              if (ret)
++                                      return ret;
++                      }
++              }
++      } else {
++              struct tb_cfg_result res;
++
++              /* Thunderbolt 1 uses the "reset" config space packet */
++              res.err = tb_sw_write(sw, ((u32 *) &sw->config) + 2,
++                                    TB_CFG_SWITCH, 2, 2);
++              if (res.err)
++                      return res.err;
++              res = tb_cfg_reset(sw->tb->ctl, tb_route(sw));
++              if (res.err > 0)
++                      return -EIO;
++              else if (res.err < 0)
++                      return res.err;
++      }
++
++      return 0;
++}
++
++static int tb_switch_reset_device(struct tb_switch *sw)
++{
++      return tb_port_reset(tb_switch_downstream_port(sw));
++}
++
++static bool tb_switch_enumerated(struct tb_switch *sw)
++{
++      u32 val;
++      int ret;
++
++      /*
++       * Read directly from the hardware because we use this also
++       * during system sleep where sw->config.enabled is already set
++       * by us.
++       */
++      ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_3, 1);
++      if (ret)
++              return false;
++
++      return !!(val & ROUTER_CS_3_V);
++}
++
+ /**
+- * tb_switch_reset() - reconfigure route, enable and send TB_CFG_PKG_RESET
+- * @sw: Switch to reset
++ * tb_switch_reset() - Perform reset to the router
++ * @sw: Router to reset
++ *
++ * Issues reset to the router @sw. Can be used for any router. For host
++ * routers, resets all the downstream ports and cleans up path config
++ * spaces accordingly. For device routers issues downstream port reset
++ * through the parent router, so as side effect there will be unplug
++ * soon after this is finished.
++ *
++ * If the router is not enumerated does nothing.
+  *
+- * Return: Returns 0 on success or an error code on failure.
++ * Returns %0 on success or negative errno in case of failure.
+  */
+ int tb_switch_reset(struct tb_switch *sw)
+ {
+-      struct tb_cfg_result res;
++      int ret;
+-      if (sw->generation > 1)
++      /*
++       * We cannot access the port config spaces unless the router is
++       * already enumerated. If the router is not enumerated it is
++       * equal to being reset so we can skip that here.
++       */
++      if (!tb_switch_enumerated(sw))
+               return 0;
+-      tb_sw_dbg(sw, "resetting switch\n");
++      tb_sw_dbg(sw, "resetting\n");
++
++      if (tb_route(sw))
++              ret = tb_switch_reset_device(sw);
++      else
++              ret = tb_switch_reset_host(sw);
++
++      if (ret)
++              tb_sw_warn(sw, "failed to reset\n");
+-      res.err = tb_sw_write(sw, ((u32 *) &sw->config) + 2,
+-                            TB_CFG_SWITCH, 2, 2);
+-      if (res.err)
+-              return res.err;
+-      res = tb_cfg_reset(sw->tb->ctl, tb_route(sw));
+-      if (res.err > 0)
+-              return -EIO;
+-      return res.err;
++      return ret;
+ }
+ /**
+--- a/drivers/thunderbolt/tb_regs.h
++++ b/drivers/thunderbolt/tb_regs.h
+@@ -194,6 +194,8 @@ struct tb_regs_switch_header {
+ #define USB4_VERSION_MAJOR_MASK                       GENMASK(7, 5)
+ #define ROUTER_CS_1                           0x01
++#define ROUTER_CS_3                           0x03
++#define ROUTER_CS_3_V                         BIT(31)
+ #define ROUTER_CS_4                           0x04
+ /* Used with the router cmuv field */
+ #define ROUTER_CS_4_CMUV_V1                   0x10
diff --git a/queue-6.8/thunderbolt-reset-topology-created-by-the-boot-firmware.patch b/queue-6.8/thunderbolt-reset-topology-created-by-the-boot-firmware.patch
new file mode 100644 (file)
index 0000000..b3615cd
--- /dev/null
@@ -0,0 +1,207 @@
+From 59a54c5f3dbde00b8ad30aef27fe35b1fe07bf5c Mon Sep 17 00:00:00 2001
+From: Sanath S <Sanath.S@amd.com>
+Date: Sat, 13 Jan 2024 11:52:48 +0200
+Subject: thunderbolt: Reset topology created by the boot firmware
+
+From: Sanath S <Sanath.S@amd.com>
+
+commit 59a54c5f3dbde00b8ad30aef27fe35b1fe07bf5c upstream.
+
+Boot firmware (typically BIOS) might have created tunnels of its own.
+The tunnel configuration that it does might be sub-optimal. For instance
+it may only support HBR2 monitors so the DisplayPort tunnels it created
+may limit Linux graphics drivers. In addition there is an issue on some
+AMD based systems where the BIOS does not allocate enough PCIe resources
+for future topology extension. By resetting the USB4 topology the PCIe
+links will be reset as well allowing Linux to re-allocate.
+
+This aligns the behavior with Windows Connection Manager.
+
+We already issued host router reset for USB4 v2 routers, now extend it
+to USB4 v1 routers as well. For pre-USB4 (that's Apple systems) we leave
+it as is and continue to discover the existing tunnels.
+
+Suggested-by: Mario Limonciello <mario.limonciello@amd.com>
+Signed-off-by: Sanath S <Sanath.S@amd.com>
+Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/thunderbolt/domain.c |    5 +++--
+ drivers/thunderbolt/icm.c    |    2 +-
+ drivers/thunderbolt/nhi.c    |   19 +++++++++++++------
+ drivers/thunderbolt/tb.c     |   26 +++++++++++++++++++-------
+ drivers/thunderbolt/tb.h     |    4 ++--
+ 5 files changed, 38 insertions(+), 18 deletions(-)
+
+--- a/drivers/thunderbolt/domain.c
++++ b/drivers/thunderbolt/domain.c
+@@ -423,6 +423,7 @@ err_free:
+ /**
+  * tb_domain_add() - Add domain to the system
+  * @tb: Domain to add
++ * @reset: Issue reset to the host router
+  *
+  * Starts the domain and adds it to the system. Hotplugging devices will
+  * work after this has been returned successfully. In order to remove
+@@ -431,7 +432,7 @@ err_free:
+  *
+  * Return: %0 in case of success and negative errno in case of error
+  */
+-int tb_domain_add(struct tb *tb)
++int tb_domain_add(struct tb *tb, bool reset)
+ {
+       int ret;
+@@ -460,7 +461,7 @@ int tb_domain_add(struct tb *tb)
+       /* Start the domain */
+       if (tb->cm_ops->start) {
+-              ret = tb->cm_ops->start(tb);
++              ret = tb->cm_ops->start(tb, reset);
+               if (ret)
+                       goto err_domain_del;
+       }
+--- a/drivers/thunderbolt/icm.c
++++ b/drivers/thunderbolt/icm.c
+@@ -2144,7 +2144,7 @@ static int icm_runtime_resume(struct tb
+       return 0;
+ }
+-static int icm_start(struct tb *tb)
++static int icm_start(struct tb *tb, bool not_used)
+ {
+       struct icm *icm = tb_priv(tb);
+       int ret;
+--- a/drivers/thunderbolt/nhi.c
++++ b/drivers/thunderbolt/nhi.c
+@@ -1221,7 +1221,7 @@ static void nhi_check_iommu(struct tb_nh
+               str_enabled_disabled(port_ok));
+ }
+-static void nhi_reset(struct tb_nhi *nhi)
++static bool nhi_reset(struct tb_nhi *nhi)
+ {
+       ktime_t timeout;
+       u32 val;
+@@ -1229,11 +1229,11 @@ static void nhi_reset(struct tb_nhi *nhi
+       val = ioread32(nhi->iobase + REG_CAPS);
+       /* Reset only v2 and later routers */
+       if (FIELD_GET(REG_CAPS_VERSION_MASK, val) < REG_CAPS_VERSION_2)
+-              return;
++              return false;
+       if (!host_reset) {
+               dev_dbg(&nhi->pdev->dev, "skipping host router reset\n");
+-              return;
++              return false;
+       }
+       iowrite32(REG_RESET_HRR, nhi->iobase + REG_RESET);
+@@ -1244,12 +1244,14 @@ static void nhi_reset(struct tb_nhi *nhi
+               val = ioread32(nhi->iobase + REG_RESET);
+               if (!(val & REG_RESET_HRR)) {
+                       dev_warn(&nhi->pdev->dev, "host router reset successful\n");
+-                      return;
++                      return true;
+               }
+               usleep_range(10, 20);
+       } while (ktime_before(ktime_get(), timeout));
+       dev_warn(&nhi->pdev->dev, "timeout resetting host router\n");
++
++      return false;
+ }
+ static int nhi_init_msi(struct tb_nhi *nhi)
+@@ -1331,6 +1333,7 @@ static int nhi_probe(struct pci_dev *pde
+       struct device *dev = &pdev->dev;
+       struct tb_nhi *nhi;
+       struct tb *tb;
++      bool reset;
+       int res;
+       if (!nhi_imr_valid(pdev))
+@@ -1365,7 +1368,11 @@ static int nhi_probe(struct pci_dev *pde
+       nhi_check_quirks(nhi);
+       nhi_check_iommu(nhi);
+-      nhi_reset(nhi);
++      /*
++       * Only USB4 v2 hosts support host reset so if we already did
++       * that then don't do it again when the domain is initialized.
++       */
++      reset = nhi_reset(nhi) ? false : host_reset;
+       res = nhi_init_msi(nhi);
+       if (res)
+@@ -1392,7 +1399,7 @@ static int nhi_probe(struct pci_dev *pde
+       dev_dbg(dev, "NHI initialized, starting thunderbolt\n");
+-      res = tb_domain_add(tb);
++      res = tb_domain_add(tb, reset);
+       if (res) {
+               /*
+                * At this point the RX/TX rings might already have been
+--- a/drivers/thunderbolt/tb.c
++++ b/drivers/thunderbolt/tb.c
+@@ -2628,7 +2628,7 @@ static int tb_scan_finalize_switch(struc
+       return 0;
+ }
+-static int tb_start(struct tb *tb)
++static int tb_start(struct tb *tb, bool reset)
+ {
+       struct tb_cm *tcm = tb_priv(tb);
+       int ret;
+@@ -2669,12 +2669,24 @@ static int tb_start(struct tb *tb)
+       tb_switch_tmu_configure(tb->root_switch, TB_SWITCH_TMU_MODE_LOWRES);
+       /* Enable TMU if it is off */
+       tb_switch_tmu_enable(tb->root_switch);
+-      /* Full scan to discover devices added before the driver was loaded. */
+-      tb_scan_switch(tb->root_switch);
+-      /* Find out tunnels created by the boot firmware */
+-      tb_discover_tunnels(tb);
+-      /* Add DP resources from the DP tunnels created by the boot firmware */
+-      tb_discover_dp_resources(tb);
++
++      /*
++       * Boot firmware might have created tunnels of its own. Since we
++       * cannot be sure they are usable for us, tear them down and
++       * reset the ports to handle it as new hotplug for USB4 v1
++       * routers (for USB4 v2 and beyond we already do host reset).
++       */
++      if (reset && usb4_switch_version(tb->root_switch) == 1) {
++              tb_switch_reset(tb->root_switch);
++      } else {
++              /* Full scan to discover devices added before the driver was loaded. */
++              tb_scan_switch(tb->root_switch);
++              /* Find out tunnels created by the boot firmware */
++              tb_discover_tunnels(tb);
++              /* Add DP resources from the DP tunnels created by the boot firmware */
++              tb_discover_dp_resources(tb);
++      }
++
+       /*
+        * If the boot firmware did not create USB 3.x tunnels create them
+        * now for the whole topology.
+--- a/drivers/thunderbolt/tb.h
++++ b/drivers/thunderbolt/tb.h
+@@ -487,7 +487,7 @@ struct tb_path {
+  */
+ struct tb_cm_ops {
+       int (*driver_ready)(struct tb *tb);
+-      int (*start)(struct tb *tb);
++      int (*start)(struct tb *tb, bool reset);
+       void (*stop)(struct tb *tb);
+       int (*suspend_noirq)(struct tb *tb);
+       int (*resume_noirq)(struct tb *tb);
+@@ -750,7 +750,7 @@ int tb_xdomain_init(void);
+ void tb_xdomain_exit(void);
+ struct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize);
+-int tb_domain_add(struct tb *tb);
++int tb_domain_add(struct tb *tb, bool reset);
+ void tb_domain_remove(struct tb *tb);
+ int tb_domain_suspend_noirq(struct tb *tb);
+ int tb_domain_resume_noirq(struct tb *tb);