Commit ec8162b3 authored by Sanath S's avatar Sanath S Committed by Mika Westerberg
Browse files

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: default avatarSanath S <Sanath.S@amd.com>
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
parent b35c1d7b
Loading
Loading
Loading
Loading
+109 −14
Original line number Diff line number Diff line
@@ -1538,21 +1538,51 @@ static void tb_dump_switch(const struct tb *tb, const struct tb_switch *sw)
	       regs->__unknown1, regs->__unknown4);
}

/**
 * tb_switch_reset() - reconfigure route, enable and send TB_CFG_PKG_RESET
 * @sw: Switch to reset
 *
 * Return: Returns 0 on success or an error code on failure.
 */
int tb_switch_reset(struct tb_switch *sw)
static int tb_switch_reset_host(struct tb_switch *sw)
{
	struct tb_cfg_result res;
	if (sw->generation > 1) {
		struct tb_port *port;

	if (sw->generation > 1)
		return 0;
		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;
			}

	tb_sw_dbg(sw, "resetting switch\n");
			/* 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)
@@ -1560,9 +1590,74 @@ int tb_switch_reset(struct tb_switch *sw)
		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() - 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.
 *
 * Returns %0 on success or negative errno in case of failure.
 */
int tb_switch_reset(struct tb_switch *sw)
{
	int ret;

	/*
	 * 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\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");

	return ret;
}

/**
 * tb_switch_wait_for_bit() - Wait for specified value of bits in offset
 * @sw: Router to read the offset value from
+2 −0
Original line number Diff line number Diff line
@@ -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