Skip to content

Commit

Permalink
thunderbolt: Make tb_switch_reset() support Thunderbolt 2, 3 and USB4…
Browse files Browse the repository at this point in the history
… routers

commit ec8162b 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>
  • Loading branch information
Sanath S authored and gregkh committed Apr 27, 2024
1 parent dc32d75 commit 90cdb98
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 14 deletions.
123 changes: 109 additions & 14 deletions drivers/thunderbolt/switch.c
Expand Up @@ -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;
}

/**
Expand Down
2 changes: 2 additions & 0 deletions drivers/thunderbolt/tb_regs.h
Expand Up @@ -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
Expand Down

0 comments on commit 90cdb98

Please sign in to comment.