Commit 4180468e authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'thunderbolt-for-v5.5' of...

Merge tag 'thunderbolt-for-v5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt into char-misc-next

Mika writes:

thunderbolt: Changes for v5.5 merge window

This adds Thunderbolt 3 support for the software connection manager. It
is currently only used in Apple systems. Previously the driver started
the firmware connection manager on those but it is not necessary anymore
with these patches (we still leave user an option to start the firmware
in case there are problems with the software connection manager).

This includes:

  - Expose 'generation' attribute under each device in sysfs
  - Converting register names to follow the USB4 spec.
  - Lane bonding support
  - Expose link speed and width in sysfs
  - Display Port handshake needed for Titan Ridge devices
  - Display Port pairing and resource management
  - Display Port bandwidth management

* tag 'thunderbolt-for-v5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt: (21 commits)
  thunderbolt: Do not start firmware unless asked by the user
  thunderbolt: Add bandwidth management for Display Port tunnels
  thunderbolt: Add Display Port adapter pairing and resource management
  thunderbolt: Add Display Port CM handshake for Titan Ridge devices
  thunderbolt: Add downstream PCIe port mappings for Alpine and Titan Ridge
  thunderbolt: Expand controller name in tb_switch_is_xy()
  thunderbolt: Add default linking between lane adapters if not provided by DROM
  thunderbolt: Add support for lane bonding
  thunderbolt: Refactor add_switch() into two functions
  thunderbolt: Add helper macro to iterate over switch ports
  thunderbolt: Make tb_sw_write() take const parameter
  thunderbolt: Convert DP adapter register names to follow the USB4 spec
  thunderbolt: Convert PCIe adapter register names to follow the USB4 spec
  thunderbolt: Convert basic adapter register names to follow the USB4 spec
  thunderbolt: Log error if adding switch fails
  thunderbolt: Log switch route string on config read/write timeout
  thunderbolt: Introduce tb_switch_is_icm()
  thunderbolt: Add 'generation' attribute for devices
  thunderbolt: Drop unnecessary read when writing LC command in Ice Lake
  thunderbolt: Fix lockdep circular locking depedency warning
  ...
parents 52f6efdf 354a7a77
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -80,6 +80,14 @@ Contact: thunderbolt-software@lists.01.org
Description:	This attribute contains 1 if Thunderbolt device was already
		authorized on boot and 0 otherwise.

What: /sys/bus/thunderbolt/devices/.../generation
Date:		Jan 2020
KernelVersion:	5.5
Contact:	Christian Kellner <christian@kellner.me>
Description:	This attribute contains the generation of the Thunderbolt
		controller associated with the device. It will contain 4
		for USB4.

What: /sys/bus/thunderbolt/devices/.../key
Date:		Sep 2017
KernelVersion:	4.13
@@ -104,6 +112,34 @@ Contact: thunderbolt-software@lists.01.org
Description:	This attribute contains name of this device extracted from
		the device DROM.

What:		/sys/bus/thunderbolt/devices/.../rx_speed
Date:		Jan 2020
KernelVersion:	5.5
Contact:	Mika Westerberg <mika.westerberg@linux.intel.com>
Description:	This attribute reports the device RX speed per lane.
		All RX lanes run at the same speed.

What:		/sys/bus/thunderbolt/devices/.../rx_lanes
Date:		Jan 2020
KernelVersion:	5.5
Contact:	Mika Westerberg <mika.westerberg@linux.intel.com>
Description:	This attribute reports number of RX lanes the device is
		using simultaneusly through its upstream port.

What:		/sys/bus/thunderbolt/devices/.../tx_speed
Date:		Jan 2020
KernelVersion:	5.5
Contact:	Mika Westerberg <mika.westerberg@linux.intel.com>
Description:	This attribute reports the TX speed per lane.
		All TX lanes run at the same speed.

What:		/sys/bus/thunderbolt/devices/.../tx_lanes
Date:		Jan 2020
KernelVersion:	5.5
Contact:	Mika Westerberg <mika.westerberg@linux.intel.com>
Description:	This attribute reports number of TX lanes the device is
		using simultaneusly through its upstream port.

What:		/sys/bus/thunderbolt/devices/.../vendor
Date:		Sep 2017
KernelVersion:	4.13
+3 −3
Original line number Diff line number Diff line
@@ -33,9 +33,9 @@ static int tb_port_enable_tmu(struct tb_port *port, bool enable)
	 * Legacy devices need to have TMU access enabled before port
	 * space can be fully accessed.
	 */
	if (tb_switch_is_lr(sw))
	if (tb_switch_is_light_ridge(sw))
		offset = 0x26;
	else if (tb_switch_is_er(sw))
	else if (tb_switch_is_eagle_ridge(sw))
		offset = 0x2a;
	else
		return 0;
@@ -60,7 +60,7 @@ static void tb_port_dummy_read(struct tb_port *port)
	 * reading stale data on next read perform one dummy read after
	 * port capabilities are walked.
	 */
	if (tb_switch_is_lr(port->sw)) {
	if (tb_switch_is_light_ridge(port->sw)) {
		u32 dummy;

		tb_port_read(port, &dummy, TB_CFG_PORT, 0, 1);
+4 −4
Original line number Diff line number Diff line
@@ -962,8 +962,8 @@ int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port,
		return tb_cfg_get_error(ctl, space, &res);

	case -ETIMEDOUT:
		tb_ctl_warn(ctl, "timeout reading config space %u from %#x\n",
			    space, offset);
		tb_ctl_warn(ctl, "%llx: timeout reading config space %u from %#x\n",
			    route, space, offset);
		break;

	default:
@@ -988,8 +988,8 @@ int tb_cfg_write(struct tb_ctl *ctl, const void *buffer, u64 route, u32 port,
		return tb_cfg_get_error(ctl, space, &res);

	case -ETIMEDOUT:
		tb_ctl_warn(ctl, "timeout writing config space %u to %#x\n",
			    space, offset);
		tb_ctl_warn(ctl, "%llx: timeout writing config space %u to %#x\n",
			    route, space, offset);
		break;

	default:
+0 −11
Original line number Diff line number Diff line
@@ -514,17 +514,6 @@ int tb_drom_read(struct tb_switch *sw)
		 * no entries). Hardcode the configuration here.
		 */
		tb_drom_read_uid_only(sw, &sw->uid);

		sw->ports[1].link_nr = 0;
		sw->ports[2].link_nr = 1;
		sw->ports[1].dual_link_port = &sw->ports[2];
		sw->ports[2].dual_link_port = &sw->ports[1];

		sw->ports[3].link_nr = 0;
		sw->ports[4].link_nr = 1;
		sw->ports[3].dual_link_port = &sw->ports[4];
		sw->ports[4].dual_link_port = &sw->ports[3];

		return 0;
	}

+100 −57
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@

#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/platform_data/x86/apple.h>
@@ -43,6 +44,10 @@
#define ICM_APPROVE_TIMEOUT		10000	/* ms */
#define ICM_MAX_LINK			4

static bool start_icm;
module_param(start_icm, bool, 0444);
MODULE_PARM_DESC(start_icm, "start ICM firmware if it is not running (default: false)");

/**
 * struct icm - Internal connection manager private data
 * @request_lock: Makes sure only one message is send to ICM at time
@@ -147,6 +152,17 @@ static const struct intel_vss *parse_intel_vss(const void *ep_name, size_t size)
	return NULL;
}

static bool intel_vss_is_rtd3(const void *ep_name, size_t size)
{
	const struct intel_vss *vss;

	vss = parse_intel_vss(ep_name, size);
	if (vss)
		return !!(vss->flags & INTEL_VSS_FLAGS_RTD3);

	return false;
}

static inline struct tb *icm_to_tb(struct icm *icm)
{
	return ((void *)icm - sizeof(struct tb));
@@ -339,6 +355,14 @@ static void icm_veto_end(struct tb *tb)
	}
}

static bool icm_firmware_running(const struct tb_nhi *nhi)
{
	u32 val;

	val = ioread32(nhi->iobase + REG_FW_STS);
	return !!(val & REG_FW_STS_ICM_EN);
}

static bool icm_fr_is_supported(struct tb *tb)
{
	return !x86_apple_machine;
@@ -562,58 +586,42 @@ static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
	return 0;
}

static struct tb_switch *add_switch(struct tb_switch *parent_sw, u64 route,
				    const uuid_t *uuid, const u8 *ep_name,
				    size_t ep_name_size, u8 connection_id,
				    u8 connection_key, u8 link, u8 depth,
				    enum tb_security_level security_level,
				    bool authorized, bool boot)
static struct tb_switch *alloc_switch(struct tb_switch *parent_sw, u64 route,
				      const uuid_t *uuid)
{
	const struct intel_vss *vss;
	struct tb *tb = parent_sw->tb;
	struct tb_switch *sw;
	int ret;

	pm_runtime_get_sync(&parent_sw->dev);

	sw = tb_switch_alloc(parent_sw->tb, &parent_sw->dev, route);
	if (IS_ERR(sw))
		goto out;
	sw = tb_switch_alloc(tb, &parent_sw->dev, route);
	if (IS_ERR(sw)) {
		tb_warn(tb, "failed to allocate switch at %llx\n", route);
		return sw;
	}

	sw->uuid = kmemdup(uuid, sizeof(*uuid), GFP_KERNEL);
	if (!sw->uuid) {
		tb_sw_warn(sw, "cannot allocate memory for switch\n");
		tb_switch_put(sw);
		goto out;
		return ERR_PTR(-ENOMEM);
	}
	sw->connection_id = connection_id;
	sw->connection_key = connection_key;
	sw->link = link;
	sw->depth = depth;
	sw->authorized = authorized;
	sw->security_level = security_level;
	sw->boot = boot;

	init_completion(&sw->rpm_complete);
	return sw;
}

	vss = parse_intel_vss(ep_name, ep_name_size);
	if (vss)
		sw->rpm = !!(vss->flags & INTEL_VSS_FLAGS_RTD3);
static int add_switch(struct tb_switch *parent_sw, struct tb_switch *sw)
{
	u64 route = tb_route(sw);
	int ret;

	/* Link the two switches now */
	tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw);
	tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw);

	ret = tb_switch_add(sw);
	if (ret) {
	if (ret)
		tb_port_at(tb_route(sw), parent_sw)->remote = NULL;
		tb_switch_put(sw);
		sw = ERR_PTR(ret);
	}

out:
	pm_runtime_mark_last_busy(&parent_sw->dev);
	pm_runtime_put_autosuspend(&parent_sw->dev);

	return sw;
	return ret;
}

static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw,
@@ -697,11 +705,11 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
		(const struct icm_fr_event_device_connected *)hdr;
	enum tb_security_level security_level;
	struct tb_switch *sw, *parent_sw;
	bool boot, dual_lane, speed_gen3;
	struct icm *icm = tb_priv(tb);
	bool authorized = false;
	struct tb_xdomain *xd;
	u8 link, depth;
	bool boot;
	u64 route;
	int ret;

@@ -714,6 +722,8 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
	security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >>
			 ICM_FLAGS_SLEVEL_SHIFT;
	boot = pkg->link_info & ICM_LINK_INFO_BOOT;
	dual_lane = pkg->hdr.flags & ICM_FLAGS_DUAL_LANE;
	speed_gen3 = pkg->hdr.flags & ICM_FLAGS_SPEED_GEN3;

	if (pkg->link_info & ICM_LINK_INFO_REJECTED) {
		tb_info(tb, "switch at %u.%u was rejected by ICM firmware because topology limit exceeded\n",
@@ -811,10 +821,27 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
		return;
	}

	add_switch(parent_sw, route, &pkg->ep_uuid, (const u8 *)pkg->ep_name,
		   sizeof(pkg->ep_name), pkg->connection_id,
		   pkg->connection_key, link, depth, security_level,
		   authorized, boot);
	pm_runtime_get_sync(&parent_sw->dev);

	sw = alloc_switch(parent_sw, route, &pkg->ep_uuid);
	if (!IS_ERR(sw)) {
		sw->connection_id = pkg->connection_id;
		sw->connection_key = pkg->connection_key;
		sw->link = link;
		sw->depth = depth;
		sw->authorized = authorized;
		sw->security_level = security_level;
		sw->boot = boot;
		sw->link_speed = speed_gen3 ? 20 : 10;
		sw->link_width = dual_lane ? 2 : 1;
		sw->rpm = intel_vss_is_rtd3(pkg->ep_name, sizeof(pkg->ep_name));

		if (add_switch(parent_sw, sw))
			tb_switch_put(sw);
	}

	pm_runtime_mark_last_busy(&parent_sw->dev);
	pm_runtime_put_autosuspend(&parent_sw->dev);

	tb_switch_put(parent_sw);
}
@@ -1142,10 +1169,10 @@ __icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr,
{
	const struct icm_tr_event_device_connected *pkg =
		(const struct icm_tr_event_device_connected *)hdr;
	bool authorized, boot, dual_lane, speed_gen3;
	enum tb_security_level security_level;
	struct tb_switch *sw, *parent_sw;
	struct tb_xdomain *xd;
	bool authorized, boot;
	u64 route;

	icm_postpone_rescan(tb);
@@ -1163,6 +1190,8 @@ __icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr,
	security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >>
			 ICM_FLAGS_SLEVEL_SHIFT;
	boot = pkg->link_info & ICM_LINK_INFO_BOOT;
	dual_lane = pkg->hdr.flags & ICM_FLAGS_DUAL_LANE;
	speed_gen3 = pkg->hdr.flags & ICM_FLAGS_SPEED_GEN3;

	if (pkg->link_info & ICM_LINK_INFO_REJECTED) {
		tb_info(tb, "switch at %llx was rejected by ICM firmware because topology limit exceeded\n",
@@ -1205,11 +1234,27 @@ __icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr,
		return;
	}

	sw = add_switch(parent_sw, route, &pkg->ep_uuid, (const u8 *)pkg->ep_name,
			sizeof(pkg->ep_name), pkg->connection_id, 0, 0, 0,
			security_level, authorized, boot);
	if (!IS_ERR(sw) && force_rtd3)
		sw->rpm = true;
	pm_runtime_get_sync(&parent_sw->dev);

	sw = alloc_switch(parent_sw, route, &pkg->ep_uuid);
	if (!IS_ERR(sw)) {
		sw->connection_id = pkg->connection_id;
		sw->authorized = authorized;
		sw->security_level = security_level;
		sw->boot = boot;
		sw->link_speed = speed_gen3 ? 20 : 10;
		sw->link_width = dual_lane ? 2 : 1;
		sw->rpm = force_rtd3;
		if (!sw->rpm)
			sw->rpm = intel_vss_is_rtd3(pkg->ep_name,
						    sizeof(pkg->ep_name));

		if (add_switch(parent_sw, sw))
			tb_switch_put(sw);
	}

	pm_runtime_mark_last_busy(&parent_sw->dev);
	pm_runtime_put_autosuspend(&parent_sw->dev);

	tb_switch_put(parent_sw);
}
@@ -1349,9 +1394,12 @@ static bool icm_ar_is_supported(struct tb *tb)
	/*
	 * Starting from Alpine Ridge we can use ICM on Apple machines
	 * as well. We just need to reset and re-enable it first.
	 * However, only start it if explicitly asked by the user.
	 */
	if (!x86_apple_machine)
	if (icm_firmware_running(tb->nhi))
		return true;
	if (!start_icm)
		return false;

	/*
	 * Find the upstream PCIe port in case we need to do reset
@@ -1704,8 +1752,7 @@ static int icm_firmware_start(struct tb *tb, struct tb_nhi *nhi)
	u32 val;

	/* Check if the ICM firmware is already running */
	val = ioread32(nhi->iobase + REG_FW_STS);
	if (val & REG_FW_STS_ICM_EN)
	if (icm_firmware_running(nhi))
		return 0;

	dev_dbg(&nhi->pdev->dev, "starting ICM firmware\n");
@@ -1893,14 +1940,12 @@ static int icm_suspend(struct tb *tb)
 */
static void icm_unplug_children(struct tb_switch *sw)
{
	unsigned int i;
	struct tb_port *port;

	if (tb_route(sw))
		sw->is_unplugged = true;

	for (i = 1; i <= sw->config.max_port_number; i++) {
		struct tb_port *port = &sw->ports[i];

	tb_switch_for_each_port(sw, port) {
		if (port->xdomain)
			port->xdomain->is_unplugged = true;
		else if (tb_port_has_remote(port))
@@ -1936,11 +1981,9 @@ static void remove_unplugged_switch(struct tb_switch *sw)

static void icm_free_unplugged_children(struct tb_switch *sw)
{
	unsigned int i;

	for (i = 1; i <= sw->config.max_port_number; i++) {
		struct tb_port *port = &sw->ports[i];
	struct tb_port *port;

	tb_switch_for_each_port(sw, port) {
		if (port->xdomain && port->xdomain->is_unplugged) {
			tb_xdomain_remove(port->xdomain);
			port->xdomain = NULL;
@@ -2216,7 +2259,7 @@ struct tb *icm_probe(struct tb_nhi *nhi)

	case PCI_DEVICE_ID_INTEL_ICL_NHI0:
	case PCI_DEVICE_ID_INTEL_ICL_NHI1:
		icm->is_supported = icm_ar_is_supported;
		icm->is_supported = icm_fr_is_supported;
		icm->driver_ready = icm_icl_driver_ready;
		icm->set_uuid = icm_icl_set_uuid;
		icm->device_connected = icm_icl_device_connected;
Loading