thunderbolt: Make tb_switch_reset() support Thunderbolt 2, 3 and USB4 routers
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>
This commit is contained in:
parent
dc32d754d7
commit
90cdb98254
|
@ -1492,29 +1492,124 @@ static void tb_dump_switch(const struct tb *tb, const struct tb_switch *sw)
|
|||
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
|
||||
*
|
||||
* Return: Returns 0 on success or an error code on failure.
|
||||
* 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.
|
||||
*
|
||||
* 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");
|
||||
|
||||
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;
|
||||
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");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue