Commit 00a738b8 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'thunderbolt-for-v5.14-rc1' of...

Merge tag 'thunderbolt-for-v5.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt into usb-next

Mika writes:

thunderbolt: Changes for v5.14 merge window

This includes following Thunderbolt/USB4 changes for the v5.14 merge
window:

  * Add self-authenticate quirk for a new Dell dock
  * NVM improvements
  * Align wake configuration with the USB4 connection manager guide
  * USB4 buffer allocation support
  * Retimer NVM firmware upgrade support when there is no device
    attached
  * Support for Intel Alder Lake integrated Thunderbolt/USB4 controller
  * A couple of miscellaneous cleanups.

All these have been in linux-next with no reported issues.

* tag 'thunderbolt-for-v5.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt: (29 commits)
  thunderbolt: Fix DROM handling for USB4 DROM
  thunderbolt: Add support for Intel Alder Lake
  thunderbolt: No need to include <linux/acpi.h> in usb4_port.c
  thunderbolt: Poll 10ms for REG_FW_STS_NVM_AUTH_DONE to be set
  thunderbolt: Add device links only when software connection manager is used
  thunderbolt: Bond lanes only when dual_link_port != NULL in alloc_dev_default()
  thunderbolt: Check for NVM authentication status after the operation started
  thunderbolt: Add WRITE_ONLY and AUTHENTICATE_ONLY NVM operations for retimers
  thunderbolt: Allow router NVM authenticate separately
  thunderbolt: Move nvm_write_ops to tb.h
  thunderbolt: Add support for retimer NVM upgrade when there is no link
  thunderbolt: Add additional USB4 port operations for retimer access
  thunderbolt: Add support for ACPI _DSM to power on/off retimers
  thunderbolt: Add USB4 port devices
  thunderbolt: Log the link as TBT instead of TBT3
  thunderbolt: Add KUnit tests for credit allocation
  thunderbolt: Add quirk for Intel Goshen Ridge DP credits
  thunderbolt: Allocate credits according to router preferences
  thunderbolt: Update port credits after bonding is enabled/disabled
  thunderbolt: Read router preferred credit allocation information
  ...
parents ab37ac69 b18f9013
Loading
Loading
Loading
Loading
+59 −23
Original line number Diff line number Diff line
@@ -213,12 +213,15 @@ Description: When new NVM image is written to the non-active NVM
		restarted with the new NVM firmware. If the image
		verification fails an error code is returned instead.

		This file will accept writing values "1" or "2"
		This file will accept writing values "1", "2" or "3".

		- Writing "1" will flush the image to the storage
		  area and authenticate the image in one action.
		- Writing "2" will run some basic validation on the image
		  and flush it to the storage area.
		- Writing "3" will authenticate the image that is
		  currently written in the storage area. This is only
		  supported with USB4 devices and retimers.

		When read holds status of the last authentication
		operation if an error occurred during the process. This
@@ -226,6 +229,20 @@ Description: When new NVM image is written to the non-active NVM
		based mailbox before the device is power cycled. Writing
		0 here clears the status.

What:		/sys/bus/thunderbolt/devices/.../nvm_authenticate_on_disconnect
Date:		Oct 2020
KernelVersion:	v5.9
Contact:	Mario Limonciello <mario.limonciello@dell.com>
Description:	For supported devices, automatically authenticate the new Thunderbolt
		image when the device is disconnected from the host system.

		This file will accept writing values "1" or "2"

		- Writing "1" will flush the image to the storage
		  area and prepare the device for authentication on disconnect.
		- Writing "2" will run some basic validation on the image
		  and flush it to the storage area.

What:		/sys/bus/thunderbolt/devices/<xdomain>.<service>/key
Date:		Jan 2018
KernelVersion:	4.15
@@ -276,6 +293,39 @@ Contact: thunderbolt-software@lists.01.org
Description:	This contains XDomain service specific settings as
		bitmask. Format: %x

What:		/sys/bus/thunderbolt/devices/usb4_portX/link
Date:		Sep 2021
KernelVersion:	v5.14
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
@@ -308,17 +358,3 @@ Date: Oct 2020
KernelVersion:	v5.9
Contact:	Mika Westerberg <mika.westerberg@linux.intel.com>
Description:	Retimer vendor identifier read from the hardware.

What:		/sys/bus/thunderbolt/devices/.../nvm_authenticate_on_disconnect
Date:		Oct 2020
KernelVersion:	v5.9
Contact:	Mario Limonciello <mario.limonciello@dell.com>
Description:	For supported devices, automatically authenticate the new Thunderbolt
		image when the device is disconnected from the host system.

		This file will accept writing values "1" or "2"

		- Writing "1" will flush the image to the storage
		  area and prepare the device for authentication on disconnect.
		- Writing "2" will run some basic validation on the image
		  and flush it to the storage area.
+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
+1 −1
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@
obj-${CONFIG_USB4} := thunderbolt.o
thunderbolt-objs := nhi.o nhi_ops.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.o
thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o tmu.o usb4.o
thunderbolt-objs += nvm.o retimer.o quirks.o
thunderbolt-objs += usb4_port.o nvm.o retimer.o quirks.o

thunderbolt-${CONFIG_ACPI} += acpi.o
thunderbolt-$(CONFIG_DEBUG_FS) += debugfs.o
+206 −0
Original line number Diff line number Diff line
@@ -180,3 +180,209 @@ bool tb_acpi_is_xdomain_allowed(void)
		return osc_sb_native_usb4_control & OSC_USB_XDOMAIN;
	return true;
}

/* UUID for retimer _DSM: e0053122-795b-4122-8a5e-57be1d26acb3 */
static const guid_t retimer_dsm_guid =
	GUID_INIT(0xe0053122, 0x795b, 0x4122,
		  0x8a, 0x5e, 0x57, 0xbe, 0x1d, 0x26, 0xac, 0xb3);

#define RETIMER_DSM_QUERY_ONLINE_STATE	1
#define RETIMER_DSM_SET_ONLINE_STATE	2

static int tb_acpi_retimer_set_power(struct tb_port *port, bool power)
{
	struct usb4_port *usb4 = port->usb4;
	union acpi_object argv4[2];
	struct acpi_device *adev;
	union acpi_object *obj;
	int ret;

	if (!usb4->can_offline)
		return 0;

	adev = ACPI_COMPANION(&usb4->dev);
	if (WARN_ON(!adev))
		return 0;

	/* Check if we are already powered on (and in correct mode) */
	obj = acpi_evaluate_dsm_typed(adev->handle, &retimer_dsm_guid, 1,
				      RETIMER_DSM_QUERY_ONLINE_STATE, NULL,
				      ACPI_TYPE_INTEGER);
	if (!obj) {
		tb_port_warn(port, "ACPI: query online _DSM failed\n");
		return -EIO;
	}

	ret = obj->integer.value;
	ACPI_FREE(obj);

	if (power == ret)
		return 0;

	tb_port_dbg(port, "ACPI: calling _DSM to power %s retimers\n",
		    power ? "on" : "off");

	argv4[0].type = ACPI_TYPE_PACKAGE;
	argv4[0].package.count = 1;
	argv4[0].package.elements = &argv4[1];
	argv4[1].integer.type = ACPI_TYPE_INTEGER;
	argv4[1].integer.value = power;

	obj = acpi_evaluate_dsm_typed(adev->handle, &retimer_dsm_guid, 1,
				      RETIMER_DSM_SET_ONLINE_STATE, argv4,
				      ACPI_TYPE_INTEGER);
	if (!obj) {
		tb_port_warn(port,
			     "ACPI: set online state _DSM evaluation failed\n");
		return -EIO;
	}

	ret = obj->integer.value;
	ACPI_FREE(obj);

	if (ret >= 0) {
		if (power)
			return ret == 1 ? 0 : -EBUSY;
		return 0;
	}

	tb_port_warn(port, "ACPI: set online state _DSM failed with error %d\n", ret);
	return -EIO;
}

/**
 * tb_acpi_power_on_retimers() - Call platform to power on retimers
 * @port: USB4 port
 *
 * Calls platform to turn on power to all retimers behind this USB4
 * port. After this function returns successfully the caller can
 * continue with the normal retimer flows (as specified in the USB4
 * spec). Note if this returns %-EBUSY it means the type-C port is in
 * non-USB4/TBT mode (there is non-USB4/TBT device connected).
 *
 * This should only be called if the USB4/TBT link is not up.
 *
 * Returns %0 on success.
 */
int tb_acpi_power_on_retimers(struct tb_port *port)
{
	return tb_acpi_retimer_set_power(port, true);
}

/**
 * tb_acpi_power_off_retimers() - Call platform to power off retimers
 * @port: USB4 port
 *
 * This is the opposite of tb_acpi_power_on_retimers(). After returning
 * successfully the normal operations with the @port can continue.
 *
 * Returns %0 on success.
 */
int tb_acpi_power_off_retimers(struct tb_port *port)
{
	return tb_acpi_retimer_set_power(port, false);
}

static bool tb_acpi_bus_match(struct device *dev)
{
	return tb_is_switch(dev) || tb_is_usb4_port_device(dev);
}

static struct acpi_device *tb_acpi_find_port(struct acpi_device *adev,
					     const struct tb_port *port)
{
	struct acpi_device *port_adev;

	if (!adev)
		return NULL;

	/*
	 * Device routers exists under the downstream facing USB4 port
	 * of the parent router. Their _ADR is always 0.
	 */
	list_for_each_entry(port_adev, &adev->children, node) {
		if (acpi_device_adr(port_adev) == port->port)
			return port_adev;
	}

	return NULL;
}

static struct acpi_device *tb_acpi_switch_find_companion(struct tb_switch *sw)
{
	struct acpi_device *adev = NULL;
	struct tb_switch *parent_sw;

	parent_sw = tb_switch_parent(sw);
	if (parent_sw) {
		struct tb_port *port = tb_port_at(tb_route(sw), parent_sw);
		struct acpi_device *port_adev;

		port_adev = tb_acpi_find_port(ACPI_COMPANION(&parent_sw->dev), port);
		if (port_adev)
			adev = acpi_find_child_device(port_adev, 0, false);
	} else {
		struct tb_nhi *nhi = sw->tb->nhi;
		struct acpi_device *parent_adev;

		parent_adev = ACPI_COMPANION(&nhi->pdev->dev);
		if (parent_adev)
			adev = acpi_find_child_device(parent_adev, 0, false);
	}

	return adev;
}

static struct acpi_device *tb_acpi_find_companion(struct device *dev)
{
	/*
	 * The Thunderbolt/USB4 hierarchy looks like following:
	 *
	 * Device (NHI)
	 *   Device (HR)		// Host router _ADR == 0
	 *      Device (DFP0)		// Downstream port _ADR == lane 0 adapter
	 *        Device (DR)		// Device router _ADR == 0
	 *          Device (UFP)	// Upstream port _ADR == lane 0 adapter
	 *      Device (DFP1)		// Downstream port _ADR == lane 0 adapter number
	 *
	 * At the moment we bind the host router to the corresponding
	 * Linux device.
	 */
	if (tb_is_switch(dev))
		return tb_acpi_switch_find_companion(tb_to_switch(dev));
	else if (tb_is_usb4_port_device(dev))
		return tb_acpi_find_port(ACPI_COMPANION(dev->parent),
					 tb_to_usb4_port_device(dev)->port);
	return NULL;
}

static void tb_acpi_setup(struct device *dev)
{
	struct acpi_device *adev = ACPI_COMPANION(dev);
	struct usb4_port *usb4 = tb_to_usb4_port_device(dev);

	if (!adev || !usb4)
		return;

	if (acpi_check_dsm(adev->handle, &retimer_dsm_guid, 1,
			   BIT(RETIMER_DSM_QUERY_ONLINE_STATE) |
			   BIT(RETIMER_DSM_SET_ONLINE_STATE)))
		usb4->can_offline = true;
}

static struct acpi_bus_type tb_acpi_bus = {
	.name = "thunderbolt",
	.match = tb_acpi_bus_match,
	.find_companion = tb_acpi_find_companion,
	.setup = tb_acpi_setup,
};

int tb_acpi_init(void)
{
	return register_acpi_bus_type(&tb_acpi_bus);
}

void tb_acpi_exit(void)
{
	unregister_acpi_bus_type(&tb_acpi_bus);
}
+15 −79
Original line number Diff line number Diff line
@@ -299,15 +299,13 @@ static int dma_port_request(struct tb_dma_port *dma, u32 in,
	return status_to_errno(out);
}

static int dma_port_flash_read_block(struct tb_dma_port *dma, u32 address,
				     void *buf, u32 size)
static int dma_port_flash_read_block(void *data, unsigned int dwaddress,
				     void *buf, size_t dwords)
{
	struct tb_dma_port *dma = data;
	struct tb_switch *sw = dma->sw;
	u32 in, dwaddress, dwords;
	int ret;

	dwaddress = address / 4;
	dwords = size / 4;
	u32 in;

	in = MAIL_IN_CMD_FLASH_READ << MAIL_IN_CMD_SHIFT;
	if (dwords < MAIL_DATA_DWORDS)
@@ -323,14 +321,13 @@ static int dma_port_flash_read_block(struct tb_dma_port *dma, u32 address,
			     dma->base + MAIL_DATA, dwords, DMA_PORT_TIMEOUT);
}

static int dma_port_flash_write_block(struct tb_dma_port *dma, u32 address,
				      const void *buf, u32 size)
static int dma_port_flash_write_block(void *data, unsigned int dwaddress,
				      const void *buf, size_t dwords)
{
	struct tb_dma_port *dma = data;
	struct tb_switch *sw = dma->sw;
	u32 in, dwaddress, dwords;
	int ret;

	dwords = size / 4;
	u32 in;

	/* Write the block to MAIL_DATA registers */
	ret = dma_port_write(sw->tb->ctl, buf, tb_route(sw), dma->port,
@@ -341,12 +338,8 @@ static int dma_port_flash_write_block(struct tb_dma_port *dma, u32 address,
	in = MAIL_IN_CMD_FLASH_WRITE << MAIL_IN_CMD_SHIFT;

	/* CSS header write is always done to the same magic address */
	if (address >= DMA_PORT_CSS_ADDRESS) {
		dwaddress = DMA_PORT_CSS_ADDRESS;
	if (dwaddress >= DMA_PORT_CSS_ADDRESS)
		in |= MAIL_IN_CSS;
	} else {
		dwaddress = address / 4;
	}

	in |= ((dwords - 1) << MAIL_IN_DWORDS_SHIFT) & MAIL_IN_DWORDS_MASK;
	in |= (dwaddress << MAIL_IN_ADDRESS_SHIFT) & MAIL_IN_ADDRESS_MASK;
@@ -365,36 +358,8 @@ static int dma_port_flash_write_block(struct tb_dma_port *dma, u32 address,
int dma_port_flash_read(struct tb_dma_port *dma, unsigned int address,
			void *buf, size_t size)
{
	unsigned int retries = DMA_PORT_RETRIES;

	do {
		unsigned int offset;
		size_t nbytes;
		int ret;

		offset = address & 3;
		nbytes = min_t(size_t, size + offset, MAIL_DATA_DWORDS * 4);

		ret = dma_port_flash_read_block(dma, address, dma->buf,
						ALIGN(nbytes, 4));
		if (ret) {
			if (ret == -ETIMEDOUT) {
				if (retries--)
					continue;
				ret = -EIO;
			}
			return ret;
		}

		nbytes -= offset;
		memcpy(buf, dma->buf + offset, nbytes);

		size -= nbytes;
		address += nbytes;
		buf += nbytes;
	} while (size > 0);

	return 0;
	return tb_nvm_read_data(address, buf, size, DMA_PORT_RETRIES,
				dma_port_flash_read_block, dma);
}

/**
@@ -411,40 +376,11 @@ int dma_port_flash_read(struct tb_dma_port *dma, unsigned int address,
int dma_port_flash_write(struct tb_dma_port *dma, unsigned int address,
			 const void *buf, size_t size)
{
	unsigned int retries = DMA_PORT_RETRIES;
	unsigned int offset;

	if (address >= DMA_PORT_CSS_ADDRESS) {
		offset = 0;
		if (size > DMA_PORT_CSS_MAX_SIZE)
	if (address >= DMA_PORT_CSS_ADDRESS && size > DMA_PORT_CSS_MAX_SIZE)
		return -E2BIG;
	} else {
		offset = address & 3;
		address = address & ~3;
	}

	do {
		u32 nbytes = min_t(u32, size, MAIL_DATA_DWORDS * 4);
		int ret;

		memcpy(dma->buf + offset, buf, nbytes);

		ret = dma_port_flash_write_block(dma, address, buf, nbytes);
		if (ret) {
			if (ret == -ETIMEDOUT) {
				if (retries--)
					continue;
				ret = -EIO;
			}
			return ret;
		}

		size -= nbytes;
		address += nbytes;
		buf += nbytes;
	} while (size > 0);

	return 0;
	return tb_nvm_write_data(address, buf, size, DMA_PORT_RETRIES,
				 dma_port_flash_write_block, dma);
}

/**
Loading