diff options
Diffstat (limited to 'queue-6.8/thunderbolt-make-tb_switch_reset-support-thunderbolt-2-3-and-usb4-routers.patch')
-rw-r--r-- | queue-6.8/thunderbolt-make-tb_switch_reset-support-thunderbolt-2-3-and-usb4-routers.patch | 174 |
1 files changed, 174 insertions, 0 deletions
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 <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 |