Skip to content

Commit ec8162b

Browse files
Sanath Swesteri
Sanath S
authored andcommitted
thunderbolt: Make tb_switch_reset() support Thunderbolt 2, 3 and USB4 routers
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 <[email protected]> Signed-off-by: Mika Westerberg <[email protected]>
1 parent b35c1d7 commit ec8162b

File tree

2 files changed

+111
-14
lines changed

2 files changed

+111
-14
lines changed

drivers/thunderbolt/switch.c

Lines changed: 109 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1538,29 +1538,124 @@ static void tb_dump_switch(const struct tb *tb, const struct tb_switch *sw)
15381538
regs->__unknown1, regs->__unknown4);
15391539
}
15401540

1541+
static int tb_switch_reset_host(struct tb_switch *sw)
1542+
{
1543+
if (sw->generation > 1) {
1544+
struct tb_port *port;
1545+
1546+
tb_switch_for_each_port(sw, port) {
1547+
int i, ret;
1548+
1549+
/*
1550+
* For lane adapters we issue downstream port
1551+
* reset and clear up path config spaces.
1552+
*
1553+
* For protocol adapters we disable the path and
1554+
* clear path config space one by one (from 8 to
1555+
* Max Input HopID of the adapter).
1556+
*/
1557+
if (tb_port_is_null(port) && !tb_is_upstream_port(port)) {
1558+
ret = tb_port_reset(port);
1559+
if (ret)
1560+
return ret;
1561+
} else if (tb_port_is_usb3_down(port) ||
1562+
tb_port_is_usb3_up(port)) {
1563+
tb_usb3_port_enable(port, false);
1564+
} else if (tb_port_is_dpin(port) ||
1565+
tb_port_is_dpout(port)) {
1566+
tb_dp_port_enable(port, false);
1567+
} else if (tb_port_is_pcie_down(port) ||
1568+
tb_port_is_pcie_up(port)) {
1569+
tb_pci_port_enable(port, false);
1570+
} else {
1571+
continue;
1572+
}
1573+
1574+
/* Cleanup path config space of protocol adapter */
1575+
for (i = TB_PATH_MIN_HOPID;
1576+
i <= port->config.max_in_hop_id; i++) {
1577+
ret = tb_path_deactivate_hop(port, i);
1578+
if (ret)
1579+
return ret;
1580+
}
1581+
}
1582+
} else {
1583+
struct tb_cfg_result res;
1584+
1585+
/* Thunderbolt 1 uses the "reset" config space packet */
1586+
res.err = tb_sw_write(sw, ((u32 *) &sw->config) + 2,
1587+
TB_CFG_SWITCH, 2, 2);
1588+
if (res.err)
1589+
return res.err;
1590+
res = tb_cfg_reset(sw->tb->ctl, tb_route(sw));
1591+
if (res.err > 0)
1592+
return -EIO;
1593+
else if (res.err < 0)
1594+
return res.err;
1595+
}
1596+
1597+
return 0;
1598+
}
1599+
1600+
static int tb_switch_reset_device(struct tb_switch *sw)
1601+
{
1602+
return tb_port_reset(tb_switch_downstream_port(sw));
1603+
}
1604+
1605+
static bool tb_switch_enumerated(struct tb_switch *sw)
1606+
{
1607+
u32 val;
1608+
int ret;
1609+
1610+
/*
1611+
* Read directly from the hardware because we use this also
1612+
* during system sleep where sw->config.enabled is already set
1613+
* by us.
1614+
*/
1615+
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_3, 1);
1616+
if (ret)
1617+
return false;
1618+
1619+
return !!(val & ROUTER_CS_3_V);
1620+
}
1621+
15411622
/**
1542-
* tb_switch_reset() - reconfigure route, enable and send TB_CFG_PKG_RESET
1543-
* @sw: Switch to reset
1623+
* tb_switch_reset() - Perform reset to the router
1624+
* @sw: Router to reset
15441625
*
1545-
* Return: Returns 0 on success or an error code on failure.
1626+
* Issues reset to the router @sw. Can be used for any router. For host
1627+
* routers, resets all the downstream ports and cleans up path config
1628+
* spaces accordingly. For device routers issues downstream port reset
1629+
* through the parent router, so as side effect there will be unplug
1630+
* soon after this is finished.
1631+
*
1632+
* If the router is not enumerated does nothing.
1633+
*
1634+
* Returns %0 on success or negative errno in case of failure.
15461635
*/
15471636
int tb_switch_reset(struct tb_switch *sw)
15481637
{
1549-
struct tb_cfg_result res;
1638+
int ret;
15501639

1551-
if (sw->generation > 1)
1640+
/*
1641+
* We cannot access the port config spaces unless the router is
1642+
* already enumerated. If the router is not enumerated it is
1643+
* equal to being reset so we can skip that here.
1644+
*/
1645+
if (!tb_switch_enumerated(sw))
15521646
return 0;
15531647

1554-
tb_sw_dbg(sw, "resetting switch\n");
1648+
tb_sw_dbg(sw, "resetting\n");
15551649

1556-
res.err = tb_sw_write(sw, ((u32 *) &sw->config) + 2,
1557-
TB_CFG_SWITCH, 2, 2);
1558-
if (res.err)
1559-
return res.err;
1560-
res = tb_cfg_reset(sw->tb->ctl, tb_route(sw));
1561-
if (res.err > 0)
1562-
return -EIO;
1563-
return res.err;
1650+
if (tb_route(sw))
1651+
ret = tb_switch_reset_device(sw);
1652+
else
1653+
ret = tb_switch_reset_host(sw);
1654+
1655+
if (ret)
1656+
tb_sw_warn(sw, "failed to reset\n");
1657+
1658+
return ret;
15641659
}
15651660

15661661
/**

drivers/thunderbolt/tb_regs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ struct tb_regs_switch_header {
194194
#define USB4_VERSION_MAJOR_MASK GENMASK(7, 5)
195195

196196
#define ROUTER_CS_1 0x01
197+
#define ROUTER_CS_3 0x03
198+
#define ROUTER_CS_3_V BIT(31)
197199
#define ROUTER_CS_4 0x04
198200
/* Used with the router cmuv field */
199201
#define ROUTER_CS_4_CMUV_V1 0x10

0 commit comments

Comments
 (0)