Commit 18ec9392 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

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

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

Mika writes:

thunderbolt: Changes for v5.4 merge window

The biggest change is the addition of Intel Ice Lake integrated
Thunderbolt support. There are also a couple of smaller changes like
converting the driver to use better device property interface and use
correct format string in service key attribute.

* tag 'thunderbolt-for-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt:
  ACPI / property: Add two new Thunderbolt property GUIDs to the list
  thunderbolt: Add support for Intel Ice Lake
  thunderbolt: Expose active parts of NVM even if upgrade is not supported
  thunderbolt: Hide switch attributes that are not set
  thunderbolt: Do not fail adding switch if some port is not implemented
  thunderbolt: Use 32-bit writes when writing ring producer/consumer
  thunderbolt: Move NVM upgrade support flag to struct icm
  thunderbolt: Correct path indices for PCIe tunnel
  thunderbolt: Show key using %*pE not %*pEp
  thunderbolt: Switch to use device_property_count_uXX()
parents e6508c7e dfda2041
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -39,6 +39,12 @@ static const guid_t prp_guids[] = {
	/* External facing port GUID: efcc06cc-73ac-4bc3-bff0-76143807c389 */
	GUID_INIT(0xefcc06cc, 0x73ac, 0x4bc3,
		  0xbf, 0xf0, 0x76, 0x14, 0x38, 0x07, 0xc3, 0x89),
	/* Thunderbolt GUID for IMR_VALID: c44d002f-69f9-4e7d-a904-a7baabdf43f7 */
	GUID_INIT(0xc44d002f, 0x69f9, 0x4e7d,
		  0xa9, 0x04, 0xa7, 0xba, 0xab, 0xdf, 0x43, 0xf7),
	/* Thunderbolt GUID for WAKE_SUPPORTED: 6c501103-c189-4296-ba72-9bf5a26ebe5d */
	GUID_INIT(0x6c501103, 0xc189, 0x4296,
		  0xba, 0x72, 0x9b, 0xf5, 0xa2, 0x6e, 0xbe, 0x5d),
};

/* ACPI _DSD data subnodes GUID: dbb8e3e6-5886-4ba6-8795-1319f52a966b */
+1 −1
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only
obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.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
+19 −4
Original line number Diff line number Diff line
@@ -930,6 +930,23 @@ struct tb_cfg_result tb_cfg_write_raw(struct tb_ctl *ctl, const void *buffer,
	return res;
}

static int tb_cfg_get_error(struct tb_ctl *ctl, enum tb_cfg_space space,
			    const struct tb_cfg_result *res)
{
	/*
	 * For unimplemented ports access to port config space may return
	 * TB_CFG_ERROR_INVALID_CONFIG_SPACE (alternatively their type is
	 * set to TB_TYPE_INACTIVE). In the former case return -ENODEV so
	 * that the caller can mark the port as disabled.
	 */
	if (space == TB_CFG_PORT &&
	    res->tb_error == TB_CFG_ERROR_INVALID_CONFIG_SPACE)
		return -ENODEV;

	tb_cfg_print_error(ctl, res);
	return -EIO;
}

int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port,
		enum tb_cfg_space space, u32 offset, u32 length)
{
@@ -942,8 +959,7 @@ int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port,

	case 1:
		/* Thunderbolt error, tb_error holds the actual number */
		tb_cfg_print_error(ctl, &res);
		return -EIO;
		return tb_cfg_get_error(ctl, space, &res);

	case -ETIMEDOUT:
		tb_ctl_warn(ctl, "timeout reading config space %u from %#x\n",
@@ -969,8 +985,7 @@ int tb_cfg_write(struct tb_ctl *ctl, const void *buffer, u64 route, u32 port,

	case 1:
		/* Thunderbolt error, tb_error holds the actual number */
		tb_cfg_print_error(ctl, &res);
		return -EIO;
		return tb_cfg_get_error(ctl, space, &res);

	case -ETIMEDOUT:
		tb_ctl_warn(ctl, "timeout writing config space %u to %#x\n",
+1 −5
Original line number Diff line number Diff line
@@ -414,7 +414,7 @@ static int tb_drom_copy_efi(struct tb_switch *sw, u16 *size)
	struct device *dev = &sw->tb->nhi->pdev->dev;
	int len, res;

	len = device_property_read_u8_array(dev, "ThunderboltDROM", NULL, 0);
	len = device_property_count_u8(dev, "ThunderboltDROM");
	if (len < 0 || len < sizeof(struct tb_drom_header))
		return -EINVAL;

@@ -525,10 +525,6 @@ int tb_drom_read(struct tb_switch *sw)
		sw->ports[3].dual_link_port = &sw->ports[4];
		sw->ports[4].dual_link_port = &sw->ports[3];

		/* Port 5 is inaccessible on this gen 1 controller */
		if (sw->config.device_id == PCI_DEVICE_ID_INTEL_LIGHT_RIDGE)
			sw->ports[5].disabled = true;

		return 0;
	}

+178 −16
Original line number Diff line number Diff line
@@ -55,16 +55,20 @@
 * @safe_mode: ICM is in safe mode
 * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported)
 * @rpm: Does the controller support runtime PM (RTD3)
 * @can_upgrade_nvm: Can the NVM firmware be upgrade on this controller
 * @veto: Is RTD3 veto in effect
 * @is_supported: Checks if we can support ICM on this controller
 * @cio_reset: Trigger CIO reset
 * @get_mode: Read and return the ICM firmware mode (optional)
 * @get_route: Find a route string for given switch
 * @save_devices: Ask ICM to save devices to ACL when suspending (optional)
 * @driver_ready: Send driver ready message to ICM
 * @set_uuid: Set UUID for the root switch (optional)
 * @device_connected: Handle device connected ICM message
 * @device_disconnected: Handle device disconnected ICM message
 * @xdomain_connected - Handle XDomain connected ICM message
 * @xdomain_disconnected - Handle XDomain disconnected ICM message
 * @rtd3_veto: Handle RTD3 veto notification ICM message
 */
struct icm {
	struct mutex request_lock;
@@ -74,6 +78,8 @@ struct icm {
	int vnd_cap;
	bool safe_mode;
	bool rpm;
	bool can_upgrade_nvm;
	bool veto;
	bool (*is_supported)(struct tb *tb);
	int (*cio_reset)(struct tb *tb);
	int (*get_mode)(struct tb *tb);
@@ -82,6 +88,7 @@ struct icm {
	int (*driver_ready)(struct tb *tb,
			    enum tb_security_level *security_level,
			    size_t *nboot_acl, bool *rpm);
	void (*set_uuid)(struct tb *tb);
	void (*device_connected)(struct tb *tb,
				 const struct icm_pkg_header *hdr);
	void (*device_disconnected)(struct tb *tb,
@@ -90,6 +97,7 @@ struct icm {
				  const struct icm_pkg_header *hdr);
	void (*xdomain_disconnected)(struct tb *tb,
				     const struct icm_pkg_header *hdr);
	void (*rtd3_veto)(struct tb *tb, const struct icm_pkg_header *hdr);
};

struct icm_notification {
@@ -294,6 +302,43 @@ static int icm_request(struct tb *tb, const void *request, size_t request_size,
	return -ETIMEDOUT;
}

/*
 * If rescan is queued to run (we are resuming), postpone it to give the
 * firmware some more time to send device connected notifications for next
 * devices in the chain.
 */
static void icm_postpone_rescan(struct tb *tb)
{
	struct icm *icm = tb_priv(tb);

	if (delayed_work_pending(&icm->rescan_work))
		mod_delayed_work(tb->wq, &icm->rescan_work,
				 msecs_to_jiffies(500));
}

static void icm_veto_begin(struct tb *tb)
{
	struct icm *icm = tb_priv(tb);

	if (!icm->veto) {
		icm->veto = true;
		/* Keep the domain powered while veto is in effect */
		pm_runtime_get(&tb->dev);
	}
}

static void icm_veto_end(struct tb *tb)
{
	struct icm *icm = tb_priv(tb);

	if (icm->veto) {
		icm->veto = false;
		/* Allow the domain suspend now */
		pm_runtime_mark_last_busy(&tb->dev);
		pm_runtime_put_autosuspend(&tb->dev);
	}
}

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

static void add_switch(struct tb_switch *parent_sw, u64 route,
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,
				    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)
{
	const struct intel_vss *vss;
	struct tb_switch *sw;
	int ret;

	pm_runtime_get_sync(&parent_sw->dev);

@@ -555,14 +602,18 @@ static void add_switch(struct tb_switch *parent_sw, u64 route,
	tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw);
	tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw);

	if (tb_switch_add(sw)) {
	ret = tb_switch_add(sw);
	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;
}

static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw,
@@ -654,6 +705,8 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
	u64 route;
	int ret;

	icm_postpone_rescan(tb);

	link = pkg->link_info & ICM_LINK_INFO_LINK_MASK;
	depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >>
		ICM_LINK_INFO_DEPTH_SHIFT;
@@ -1084,7 +1137,8 @@ static int icm_tr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
}

static void
icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
__icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr,
			  bool force_rtd3)
{
	const struct icm_tr_event_device_connected *pkg =
		(const struct icm_tr_event_device_connected *)hdr;
@@ -1094,6 +1148,8 @@ icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
	bool authorized, boot;
	u64 route;

	icm_postpone_rescan(tb);

	/*
	 * Currently we don't use the QoS information coming with the
	 * device connected message so simply just ignore that extra
@@ -1149,13 +1205,21 @@ icm_tr_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,
		   0, 0, 0, security_level, authorized, boot);
	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;

	tb_switch_put(parent_sw);
}

static void
icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
{
	__icm_tr_device_connected(tb, hdr, false);
}

static void
icm_tr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
{
@@ -1466,6 +1530,61 @@ static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids,
	return 0;
}

static int
icm_icl_driver_ready(struct tb *tb, enum tb_security_level *security_level,
		    size_t *nboot_acl, bool *rpm)
{
	struct icm_tr_pkg_driver_ready_response reply;
	struct icm_pkg_driver_ready request = {
		.hdr.code = ICM_DRIVER_READY,
	};
	int ret;

	memset(&reply, 0, sizeof(reply));
	ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
			  1, 20000);
	if (ret)
		return ret;

	/* Ice Lake always supports RTD3 */
	if (rpm)
		*rpm = true;

	return 0;
}

static void icm_icl_set_uuid(struct tb *tb)
{
	struct tb_nhi *nhi = tb->nhi;
	u32 uuid[4];

	pci_read_config_dword(nhi->pdev, VS_CAP_10, &uuid[0]);
	pci_read_config_dword(nhi->pdev, VS_CAP_11, &uuid[1]);
	uuid[2] = 0xffffffff;
	uuid[3] = 0xffffffff;

	tb->root_switch->uuid = kmemdup(uuid, sizeof(uuid), GFP_KERNEL);
}

static void
icm_icl_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
{
	__icm_tr_device_connected(tb, hdr, true);
}

static void icm_icl_rtd3_veto(struct tb *tb, const struct icm_pkg_header *hdr)
{
	const struct icm_icl_event_rtd3_veto *pkg =
		(const struct icm_icl_event_rtd3_veto *)hdr;

	tb_dbg(tb, "ICM rtd3 veto=0x%08x\n", pkg->veto_reason);

	if (pkg->veto_reason)
		icm_veto_begin(tb);
	else
		icm_veto_end(tb);
}

static void icm_handle_notification(struct work_struct *work)
{
	struct icm_notification *n = container_of(work, typeof(*n), work);
@@ -1493,6 +1612,9 @@ static void icm_handle_notification(struct work_struct *work)
		case ICM_EVENT_XDOMAIN_DISCONNECTED:
			icm->xdomain_disconnected(tb, n->pkg);
			break;
		case ICM_EVENT_RTD3_VETO:
			icm->rtd3_veto(tb, n->pkg);
			break;
		}
	}

@@ -1851,6 +1973,13 @@ static void icm_complete(struct tb *tb)
	if (tb->nhi->going_away)
		return;

	/*
	 * If RTD3 was vetoed before we entered system suspend allow it
	 * again now before driver ready is sent. Firmware sends a new RTD3
	 * veto if it is still the case after we have sent it driver ready
	 * command.
	 */
	icm_veto_end(tb);
	icm_unplug_children(tb->root_switch);

	/*
@@ -1913,14 +2042,12 @@ static int icm_start(struct tb *tb)
	if (IS_ERR(tb->root_switch))
		return PTR_ERR(tb->root_switch);

	/*
	 * NVM upgrade has not been tested on Apple systems and they
	 * don't provide images publicly either. To be on the safe side
	 * prevent root switch NVM upgrade on Macs for now.
	 */
	tb->root_switch->no_nvm_upgrade = x86_apple_machine;
	tb->root_switch->no_nvm_upgrade = !icm->can_upgrade_nvm;
	tb->root_switch->rpm = icm->rpm;

	if (icm->set_uuid)
		icm->set_uuid(tb);

	ret = tb_switch_add(tb->root_switch);
	if (ret) {
		tb_switch_put(tb->root_switch);
@@ -2005,6 +2132,19 @@ static const struct tb_cm_ops icm_tr_ops = {
	.disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths,
};

/* Ice Lake */
static const struct tb_cm_ops icm_icl_ops = {
	.driver_ready = icm_driver_ready,
	.start = icm_start,
	.stop = icm_stop,
	.complete = icm_complete,
	.runtime_suspend = icm_runtime_suspend,
	.runtime_resume = icm_runtime_resume,
	.handle_event = icm_handle_event,
	.approve_xdomain_paths = icm_tr_approve_xdomain_paths,
	.disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths,
};

struct tb *icm_probe(struct tb_nhi *nhi)
{
	struct icm *icm;
@@ -2021,6 +2161,7 @@ struct tb *icm_probe(struct tb_nhi *nhi)
	switch (nhi->pdev->device) {
	case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI:
	case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI:
		icm->can_upgrade_nvm = true;
		icm->is_supported = icm_fr_is_supported;
		icm->get_route = icm_fr_get_route;
		icm->save_devices = icm_fr_save_devices;
@@ -2038,6 +2179,13 @@ struct tb *icm_probe(struct tb_nhi *nhi)
	case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI:
	case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI:
		icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES;
		/*
		 * NVM upgrade has not been tested on Apple systems and
		 * they don't provide images publicly either. To be on
		 * the safe side prevent root switch NVM upgrade on Macs
		 * for now.
		 */
		icm->can_upgrade_nvm = !x86_apple_machine;
		icm->is_supported = icm_ar_is_supported;
		icm->cio_reset = icm_ar_cio_reset;
		icm->get_mode = icm_ar_get_mode;
@@ -2054,6 +2202,7 @@ struct tb *icm_probe(struct tb_nhi *nhi)
	case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI:
	case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI:
		icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES;
		icm->can_upgrade_nvm = !x86_apple_machine;
		icm->is_supported = icm_ar_is_supported;
		icm->cio_reset = icm_tr_cio_reset;
		icm->get_mode = icm_ar_get_mode;
@@ -2064,6 +2213,19 @@ struct tb *icm_probe(struct tb_nhi *nhi)
		icm->xdomain_disconnected = icm_tr_xdomain_disconnected;
		tb->cm_ops = &icm_tr_ops;
		break;

	case PCI_DEVICE_ID_INTEL_ICL_NHI0:
	case PCI_DEVICE_ID_INTEL_ICL_NHI1:
		icm->is_supported = icm_ar_is_supported;
		icm->driver_ready = icm_icl_driver_ready;
		icm->set_uuid = icm_icl_set_uuid;
		icm->device_connected = icm_icl_device_connected;
		icm->device_disconnected = icm_tr_device_disconnected;
		icm->xdomain_connected = icm_tr_xdomain_connected;
		icm->xdomain_disconnected = icm_tr_xdomain_disconnected;
		icm->rtd3_veto = icm_icl_rtd3_veto;
		tb->cm_ops = &icm_icl_ops;
		break;
	}

	if (!icm->is_supported || !icm->is_supported(tb)) {
Loading