From 2df34b18fef2832dc555de923f41fe487d22ab5c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 23 Apr 2024 12:51:57 -0700 Subject: 6.8-stable patches 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 | 4 + ...nderbolt-introduce-tb_path_deactivate_hop.patch | 53 ++++++ .../thunderbolt-introduce-tb_port_reset.patch | 187 +++++++++++++++++++ ...-support-thunderbolt-2-3-and-usb4-routers.patch | 174 +++++++++++++++++ ...set-topology-created-by-the-boot-firmware.patch | 207 +++++++++++++++++++++ 5 files changed, 625 insertions(+) create mode 100644 queue-6.8/thunderbolt-introduce-tb_path_deactivate_hop.patch create mode 100644 queue-6.8/thunderbolt-introduce-tb_port_reset.patch create mode 100644 queue-6.8/thunderbolt-make-tb_switch_reset-support-thunderbolt-2-3-and-usb4-routers.patch create mode 100644 queue-6.8/thunderbolt-reset-topology-created-by-the-boot-firmware.patch diff --git a/queue-6.8/series b/queue-6.8/series index 4f2bf462b4..b708fd4142 100644 --- a/queue-6.8/series +++ b/queue-6.8/series @@ -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 index 0000000000..9dd8a7af5a --- /dev/null +++ b/queue-6.8/thunderbolt-introduce-tb_path_deactivate_hop.patch @@ -0,0 +1,53 @@ +From b35c1d7b11da8c08b14147bbe87c2c92f7a83f8b Mon Sep 17 00:00:00 2001 +From: Sanath S +Date: Sat, 13 Jan 2024 11:42:23 +0200 +Subject: thunderbolt: Introduce tb_path_deactivate_hop() + +From: Sanath S + +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 +Signed-off-by: Mika Westerberg +Cc: Mario Limonciello +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..cb63fd311f --- /dev/null +++ b/queue-6.8/thunderbolt-introduce-tb_port_reset.patch @@ -0,0 +1,187 @@ +From 01da6b99d49f60b1edead44e33569b1a2e9f49b7 Mon Sep 17 00:00:00 2001 +From: Sanath S +Date: Sat, 13 Jan 2024 11:39:57 +0200 +Subject: thunderbolt: Introduce tb_port_reset() + +From: Sanath S + +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 +Signed-off-by: Mika Westerberg +Cc: Mario Limonciello +Signed-off-by: Greg Kroah-Hartman +--- + 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 + */ + ++#include ++ + #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 index 0000000000..1fd6d14ed6 --- /dev/null +++ b/queue-6.8/thunderbolt-make-tb_switch_reset-support-thunderbolt-2-3-and-usb4-routers.patch @@ -0,0 +1,174 @@ +From ec8162b3f0683ae08a21f20517cf49272b07ee0b Mon Sep 17 00:00:00 2001 +From: Sanath S +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 + +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 +Signed-off-by: Mika Westerberg +Cc: Mario Limonciello +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..b3615cdd30 --- /dev/null +++ b/queue-6.8/thunderbolt-reset-topology-created-by-the-boot-firmware.patch @@ -0,0 +1,207 @@ +From 59a54c5f3dbde00b8ad30aef27fe35b1fe07bf5c Mon Sep 17 00:00:00 2001 +From: Sanath S +Date: Sat, 13 Jan 2024 11:52:48 +0200 +Subject: thunderbolt: Reset topology created by the boot firmware + +From: Sanath S + +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 +Signed-off-by: Sanath S +Signed-off-by: Mika Westerberg +Signed-off-by: Greg Kroah-Hartman +--- + 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); -- cgit 1.2.3-korg