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

Merge tag 'thunderbolt-for-v6.4-rc1' of...

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

Mika writes:

thunderbolt: Changes for v6.4 merge window

This includes following Thunderbolt/USB4 changes for the v6.4 merge
window:

  - Refactoring of DROM read code paths
  - Convert to use SI units from units.h
  - A couple of cleanups

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

* tag 'thunderbolt-for-v6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt:
  thunderbolt: Introduce usb4_port_sb_opcode_err_to_errno() helper
  thunderbolt: Make use of SI units from units.h
  thunderbolt: Get rid of redundant 'else'
  thunderbolt: Refactor DROM reading
  thunderbolt: use `tb_eeprom_get_drom_offset` to discover DROM offset
parents 8e86652e 1f15af76
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -341,7 +341,7 @@ static struct acpi_device *tb_acpi_find_companion(struct device *dev)
	 */
	if (tb_is_switch(dev))
		return tb_acpi_switch_find_companion(tb_to_switch(dev));
	else if (tb_is_usb4_port_device(dev))
	if (tb_is_usb4_port_device(dev))
		return acpi_find_child_by_adr(ACPI_COMPANION(dev->parent),
					      tb_to_usb4_port_device(dev)->port->port);
	return NULL;
+1 −1
Original line number Diff line number Diff line
@@ -1033,7 +1033,7 @@ static int tb_cfg_get_error(struct tb_ctl *ctl, enum tb_cfg_space space,

	if (res->tb_error == TB_CFG_ERROR_LOCK)
		return -EACCES;
	else if (res->tb_error == TB_CFG_ERROR_PORT_NOT_CONNECTED)
	if (res->tb_error == TB_CFG_ERROR_PORT_NOT_CONNECTED)
		return -ENOTCONN;

	return -EIO;
+107 −97
Original line number Diff line number Diff line
@@ -416,7 +416,7 @@ static int tb_drom_parse_entries(struct tb_switch *sw, size_t header_size)
		if (pos + 1 == drom_size || pos + entry->len > drom_size
				|| !entry->len) {
			tb_sw_warn(sw, "DROM buffer overrun\n");
			return -EILSEQ;
			return -EIO;
		}

		switch (entry->type) {
@@ -471,14 +471,13 @@ static int tb_drom_copy_efi(struct tb_switch *sw, u16 *size)

static int tb_drom_copy_nvm(struct tb_switch *sw, u16 *size)
{
	u32 drom_offset;
	u16 drom_offset;
	int ret;

	if (!sw->dma_port)
		return -ENODEV;

	ret = tb_sw_read(sw, &drom_offset, TB_CFG_SWITCH,
			 sw->cap_plug_events + 12, 1);
	ret = tb_eeprom_get_drom_offset(sw, &drom_offset);
	if (ret)
		return ret;

@@ -513,7 +512,7 @@ static int tb_drom_copy_nvm(struct tb_switch *sw, u16 *size)
	return ret;
}

static int usb4_copy_host_drom(struct tb_switch *sw, u16 *size)
static int usb4_copy_drom(struct tb_switch *sw, u16 *size)
{
	int ret;

@@ -536,15 +535,40 @@ static int usb4_copy_host_drom(struct tb_switch *sw, u16 *size)
	return ret;
}

static int tb_drom_read_n(struct tb_switch *sw, u16 offset, u8 *val,
			  size_t count)
static int tb_drom_bit_bang(struct tb_switch *sw, u16 *size)
{
	if (tb_switch_is_usb4(sw))
		return usb4_switch_drom_read(sw, offset, val, count);
	return tb_eeprom_read_n(sw, offset, val, count);
	int ret;

	ret = tb_eeprom_read_n(sw, 14, (u8 *)size, 2);
	if (ret)
		return ret;

	*size &= 0x3ff;
	*size += TB_DROM_DATA_START;

	tb_sw_dbg(sw, "reading DROM (length: %#x)\n", *size);
	if (*size < sizeof(struct tb_drom_header)) {
		tb_sw_warn(sw, "DROM too small, aborting\n");
		return -EIO;
	}

static int tb_drom_parse(struct tb_switch *sw)
	sw->drom = kzalloc(*size, GFP_KERNEL);
	if (!sw->drom)
		return -ENOMEM;

	ret = tb_eeprom_read_n(sw, 0, sw->drom, *size);
	if (ret)
		goto err;

	return 0;

err:
	kfree(sw->drom);
	sw->drom = NULL;
	return ret;
}

static int tb_drom_parse_v1(struct tb_switch *sw)
{
	const struct tb_drom_header *header =
		(const struct tb_drom_header *)sw->drom;
@@ -555,7 +579,7 @@ static int tb_drom_parse(struct tb_switch *sw)
		tb_sw_warn(sw,
			"DROM UID CRC8 mismatch (expected: %#x, got: %#x)\n",
			header->uid_crc8, crc);
		return -EILSEQ;
		return -EIO;
	}
	if (!sw->uid)
		sw->uid = header->uid;
@@ -589,85 +613,14 @@ static int usb4_drom_parse(struct tb_switch *sw)
	return tb_drom_parse_entries(sw, USB4_DROM_HEADER_SIZE);
}

/**
 * tb_drom_read() - Copy DROM to sw->drom and parse it
 * @sw: Router whose DROM to read and parse
 *
 * This function reads router DROM and if successful parses the entries and
 * populates the fields in @sw accordingly. Can be called for any router
 * generation.
 *
 * Returns %0 in case of success and negative errno otherwise.
 */
int tb_drom_read(struct tb_switch *sw)
static int tb_drom_parse(struct tb_switch *sw, u16 size)
{
	u16 size;
	struct tb_drom_header *header;
	int res, retries = 1;

	if (sw->drom)
		return 0;

	if (tb_route(sw) == 0) {
		/*
		 * Apple's NHI EFI driver supplies a DROM for the root switch
		 * in a device property. Use it if available.
		 */
		if (tb_drom_copy_efi(sw, &size) == 0)
			goto parse;

		/* Non-Apple hardware has the DROM as part of NVM */
		if (tb_drom_copy_nvm(sw, &size) == 0)
			goto parse;

		/*
		 * USB4 hosts may support reading DROM through router
		 * operations.
		 */
		if (tb_switch_is_usb4(sw)) {
			usb4_switch_read_uid(sw, &sw->uid);
			if (!usb4_copy_host_drom(sw, &size))
				goto parse;
		} else {
			/*
			 * The root switch contains only a dummy drom
			 * (header only, no entries). Hardcode the
			 * configuration here.
			 */
			tb_drom_read_uid_only(sw, &sw->uid);
		}

		return 0;
	}

	res = tb_drom_read_n(sw, 14, (u8 *) &size, 2);
	if (res)
		return res;
	size &= 0x3ff;
	size += TB_DROM_DATA_START;
	tb_sw_dbg(sw, "reading drom (length: %#x)\n", size);
	if (size < sizeof(*header)) {
		tb_sw_warn(sw, "drom too small, aborting\n");
		return -EIO;
	}

	sw->drom = kzalloc(size, GFP_KERNEL);
	if (!sw->drom)
		return -ENOMEM;
read:
	res = tb_drom_read_n(sw, 0, sw->drom, size);
	if (res)
		goto err;

parse:
	header = (void *) sw->drom;
	const struct tb_drom_header *header = (const void *)sw->drom;
	int ret;

	if (header->data_len + TB_DROM_DATA_START != size) {
		tb_sw_warn(sw, "drom size mismatch\n");
		if (retries--) {
			msleep(100);
			goto read;
		}
		tb_sw_warn(sw, "DROM size mismatch\n");
		ret = -EIO;
		goto err;
	}

@@ -675,29 +628,86 @@ int tb_drom_read(struct tb_switch *sw)

	switch (header->device_rom_revision) {
	case 3:
		res = usb4_drom_parse(sw);
		ret = usb4_drom_parse(sw);
		break;
	default:
		tb_sw_warn(sw, "DROM device_rom_revision %#x unknown\n",
			   header->device_rom_revision);
		fallthrough;
	case 1:
		res = tb_drom_parse(sw);
		ret = tb_drom_parse_v1(sw);
		break;
	}

	/* If the DROM parsing fails, wait a moment and retry once */
	if (res == -EILSEQ && retries--) {
	if (ret) {
		tb_sw_warn(sw, "parsing DROM failed\n");
		msleep(100);
		goto read;
		goto err;
	}

	if (!res)
	return 0;

err:
	kfree(sw->drom);
	sw->drom = NULL;
	return -EIO;

	return ret;
}

static int tb_drom_host_read(struct tb_switch *sw)
{
	u16 size;

	if (tb_switch_is_usb4(sw)) {
		usb4_switch_read_uid(sw, &sw->uid);
		if (!usb4_copy_drom(sw, &size))
			return tb_drom_parse(sw, size);
	} else {
		if (!tb_drom_copy_efi(sw, &size))
			return tb_drom_parse(sw, size);

		if (!tb_drom_copy_nvm(sw, &size))
			return tb_drom_parse(sw, size);

		tb_drom_read_uid_only(sw, &sw->uid);
	}

	return 0;
}

static int tb_drom_device_read(struct tb_switch *sw)
{
	u16 size;
	int ret;

	if (tb_switch_is_usb4(sw)) {
		usb4_switch_read_uid(sw, &sw->uid);
		ret = usb4_copy_drom(sw, &size);
	} else {
		ret = tb_drom_bit_bang(sw, &size);
	}

	if (ret)
		return ret;

	return tb_drom_parse(sw, size);
}

/**
 * tb_drom_read() - Copy DROM to sw->drom and parse it
 * @sw: Router whose DROM to read and parse
 *
 * This function reads router DROM and if successful parses the entries and
 * populates the fields in @sw accordingly. Can be called for any router
 * generation.
 *
 * Returns %0 in case of success and negative errno otherwise.
 */
int tb_drom_read(struct tb_switch *sw)
{
	if (sw->drom)
		return 0;

	if (!tb_route(sw))
		return tb_drom_host_read(sw);
	return tb_drom_device_read(sw);
}
+2 −1
Original line number Diff line number Diff line
@@ -526,7 +526,8 @@ static int nhi_alloc_hop(struct tb_nhi *nhi, struct tb_ring *ring)
			 ring->hop);
		ret = -EBUSY;
		goto err_unlock;
	} else if (!ring->is_tx && nhi->rx_rings[ring->hop]) {
	}
	if (!ring->is_tx && nhi->rx_rings[ring->hop]) {
		dev_warn(&nhi->pdev->dev, "RX hop %d already allocated\n",
			 ring->hop);
		ret = -EBUSY;
+2 −2
Original line number Diff line number Diff line
@@ -271,9 +271,9 @@ static int nvm_authenticate(struct tb_switch *sw, bool auth_only)
		}
		sw->nvm->authenticating = true;
		return usb4_switch_nvm_authenticate(sw);
	} else if (auth_only) {
		return -EOPNOTSUPP;
	}
	if (auth_only)
		return -EOPNOTSUPP;

	sw->nvm->authenticating = true;
	if (!tb_route(sw)) {
Loading