Commit 3fb10ea4 authored by Rajmohan Mani's avatar Rajmohan Mani Committed by Mika Westerberg
Browse files

thunderbolt: Add support for retimer NVM upgrade when there is no link



With help from platform firmware (ACPI) it is possible to power on
retimers even when there is no USB4 link (e.g nothing is connected to
the USB4 ports). This allows us to bring the USB4 sideband up so that we
can access retimers and upgrade their NVM firmware.

If the platform has support for this, we expose two additional
attributes under USB4 ports: offline and rescan. These can be used to
bring the port offline, rescan for the retimers and put the port online
again. The retimer NVM upgrade itself works the same way than with cable
connected.

Signed-off-by: default avatarRajmohan Mani <rajmohan.mani@intel.com>
Co-developed-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 3406de7c
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -297,6 +297,32 @@ Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
Description:	Returns the current link mode. Possible values are
		"usb4", "tbt" and "none".

What:		/sys/bus/thunderbolt/devices/usb4_portX/offline
Date:		Sep 2021
KernelVersion:	v5.14
Contact:	Rajmohan Mani <rajmohan.mani@intel.com>
Description:	Writing 1 to this attribute puts the USB4 port into
		offline mode. Only allowed when there is nothing
		connected to the port (link attribute returns "none").
		Once the port is in offline mode it does not receive any
		hotplug events. This is used to update NVM firmware of
		on-board retimers. Writing 0 puts the port back to
		online mode.

		This attribute is only visible if the platform supports
		powering on retimers when there is no cable connected.

What:		/sys/bus/thunderbolt/devices/usb4_portX/rescan
Date:		Sep 2021
KernelVersion:	v5.14
Contact:	Rajmohan Mani <rajmohan.mani@intel.com>
Description:	When the USB4 port is in offline mode writing 1 to this
		attribute forces rescan of the sideband for on-board
		retimers. Each retimer appear under the USB4 port as if
		the USB4 link was up. These retimers act in the same way
		as if the cable was connected so upgrading their NVM
		firmware can be done the usual way.

What:		/sys/bus/thunderbolt/devices/<device>:<port>.<index>/device
Date:		Oct 2020
KernelVersion:	v5.9
+29 −0
Original line number Diff line number Diff line
@@ -256,6 +256,35 @@ Note names of the NVMem devices ``nvm_activeN`` and ``nvm_non_activeN``
depend on the order they are registered in the NVMem subsystem. N in
the name is the identifier added by the NVMem subsystem.

Upgrading on-board retimer NVM when there is no cable connected
---------------------------------------------------------------
If the platform supports, it may be possible to upgrade the retimer NVM
firmware even when there is nothing connected to the USB4
ports. When this is the case the ``usb4_portX`` devices have two special
attributes: ``offline`` and ``rescan``. The way to upgrade the firmware
is to first put the USB4 port into offline mode::

  # echo 1 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/offline

This step makes sure the port does not respond to any hotplug events,
and also ensures the retimers are powered on. The next step is to scan
for the retimers::

  # echo 1 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/rescan

This enumerates and adds the on-board retimers. Now retimer NVM can be
upgraded in the same way than with cable connected (see previous
section). However, the retimer is not disconnected as we are offline
mode) so after writing ``1`` to ``nvm_authenticate`` one should wait for
5 or more seconds before running rescan again::

  # echo 1 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/rescan

This point if everything went fine, the port can be put back to
functional state again::

  # echo 0 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/offline

Upgrading NVM when host controller is in safe mode
--------------------------------------------------
If the existing NVM is not properly authenticated (or is missing) the
+15 −9
Original line number Diff line number Diff line
@@ -401,19 +401,18 @@ static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index)
/**
 * tb_retimer_scan() - Scan for on-board retimers under port
 * @port: USB4 port to scan
 * @add: If true also registers found retimers
 *
 * Tries to enumerate on-board retimers connected to @port. Found
 * retimers are registered as children of @port. Does not scan for cable
 * retimers for now.
 * Brings the sideband into a state where retimers can be accessed.
 * Then Tries to enumerate on-board retimers connected to @port. Found
 * retimers are registered as children of @port if @add is set.  Does
 * not scan for cable retimers for now.
 */
int tb_retimer_scan(struct tb_port *port)
int tb_retimer_scan(struct tb_port *port, bool add)
{
	u32 status[TB_MAX_RETIMER_INDEX + 1] = {};
	int ret, i, last_idx = 0;

	if (!port->cap_usb4)
		return 0;

	/*
	 * Send broadcast RT to make sure retimer indices facing this
	 * port are set.
@@ -422,6 +421,13 @@ int tb_retimer_scan(struct tb_port *port)
	if (ret)
		return ret;

	/*
	 * Enable sideband channel for each retimer. We can do this
	 * regardless whether there is device connected or not.
	 */
	for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++)
		usb4_port_retimer_set_inbound_sbtx(port, i);

	/*
	 * Before doing anything else, read the authentication status.
	 * If the retimer has it set, store it for the new retimer
@@ -453,10 +459,10 @@ int tb_retimer_scan(struct tb_port *port)
		rt = tb_port_find_retimer(port, i);
		if (rt) {
			put_device(&rt->dev);
		} else {
		} else if (add) {
			ret = tb_retimer_add(port, i, status[i]);
			if (ret && ret != -EOPNOTSUPP)
				return ret;
				break;
		}
	}

+32 −16
Original line number Diff line number Diff line
@@ -1153,6 +1153,33 @@ static int tb_port_start_lane_initialization(struct tb_port *port)
	return ret == -EINVAL ? 0 : ret;
}

/*
 * Returns true if the port had something (router, XDomain) connected
 * before suspend.
 */
static bool tb_port_resume(struct tb_port *port)
{
	bool has_remote = tb_port_has_remote(port);

	if (port->usb4) {
		usb4_port_device_resume(port->usb4);
	} else if (!has_remote) {
		/*
		 * For disconnected downstream lane adapters start lane
		 * initialization now so we detect future connects.
		 *
		 * For XDomain start the lane initialzation now so the
		 * link gets re-established.
		 *
		 * This is only needed for non-USB4 ports.
		 */
		if (!tb_is_upstream_port(port) || port->xdomain)
			tb_port_start_lane_initialization(port);
	}

	return has_remote || port->xdomain;
}

/**
 * tb_port_is_enabled() - Is the adapter port enabled
 * @port: Port to check
@@ -2915,22 +2942,11 @@ int tb_switch_resume(struct tb_switch *sw)

	/* check for surviving downstream switches */
	tb_switch_for_each_port(sw, port) {
		if (!tb_port_has_remote(port) && !port->xdomain) {
			/*
			 * For disconnected downstream lane adapters
			 * start lane initialization now so we detect
			 * future connects.
			 */
			if (!tb_is_upstream_port(port) && tb_port_is_null(port))
				tb_port_start_lane_initialization(port);
		if (!tb_port_is_null(port))
			continue;

		if (!tb_port_resume(port))
			continue;
		} else if (port->xdomain) {
			/*
			 * Start lane initialization for XDomain so the
			 * link gets re-established.
			 */
			tb_port_start_lane_initialization(port);
		}

		if (tb_wait_for_port(port, true) <= 0) {
			tb_port_warn(port,
@@ -2939,7 +2955,7 @@ int tb_switch_resume(struct tb_switch *sw)
				tb_sw_set_unplugged(port->remote->sw);
			else if (port->xdomain)
				port->xdomain->is_unplugged = true;
		} else if (tb_port_has_remote(port) || port->xdomain) {
		} else {
			/*
			 * Always unlock the port so the downstream
			 * switch/domain is accessible.
+2 −2
Original line number Diff line number Diff line
@@ -595,7 +595,7 @@ static void tb_scan_port(struct tb_port *port)
		return;
	}

	tb_retimer_scan(port);
	tb_retimer_scan(port, true);

	sw = tb_switch_alloc(port->sw->tb, &port->sw->dev,
			     tb_downstream_route(port));
@@ -662,7 +662,7 @@ static void tb_scan_port(struct tb_port *port)
		tb_sw_warn(sw, "failed to enable TMU\n");

	/* Scan upstream retimers */
	tb_retimer_scan(upstream_port);
	tb_retimer_scan(upstream_port, true);

	/*
	 * Create USB 3.x tunnels only when the switch is plugged to the
Loading