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

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

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

Mika writes:

thunderbolt: Changes for v5.18 merge window

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

  * Improvements for Intel Alpine and Titan Ridge support
  * Replace acpi_bus_get_device() with acpi_fetch_acpi_dev()
  * Improvements around DROM handling on AMD hardware
  * A couple of cleanups.

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

* tag 'thunderbolt-for-v5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt:
  thunderbolt: Rename EEPROM handling bits to match USB4 spec
  thunderbolt: Clarify register definitions for `tb_cap_plug_events`
  thunderbolt: Do not make DROM read success compulsory
  thunderbolt: Do not resume routers if UID is not set
  thunderbolt: Retry DROM reads for more failure scenarios
  thunderbolt: Replace acpi_bus_get_device()
  thunderbolt: Add internal xHCI connect flows for Thunderbolt 3 devices
  thunderbolt: Add missing device ID to tb_switch_is_alpine_ridge()
  thunderbolt: Disable LTTPR on Intel Titan Ridge
  thunderbolt: Remove useless DMA-32 fallback configuration
parents 6edc3f89 144c4a77
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -14,15 +14,15 @@
static acpi_status tb_acpi_add_link(acpi_handle handle, u32 level, void *data,
				    void **return_value)
{
	struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
	struct fwnode_reference_args args;
	struct fwnode_handle *fwnode;
	struct tb_nhi *nhi = data;
	struct acpi_device *adev;
	struct pci_dev *pdev;
	struct device *dev;
	int ret;

	if (acpi_bus_get_device(handle, &adev))
	if (!adev)
		return AE_OK;

	fwnode = acpi_fwnode_handle(adev);
+22 −19
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@
 */
static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
{
	return tb_sw_write(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1);
	return tb_sw_write(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + ROUTER_CS_4, 1);
}

/*
@@ -25,7 +25,7 @@ static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
 */
static int tb_eeprom_ctl_read(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
{
	return tb_sw_read(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1);
	return tb_sw_read(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + ROUTER_CS_4, 1);
}

enum tb_eeprom_transfer {
@@ -46,18 +46,18 @@ static int tb_eeprom_active(struct tb_switch *sw, bool enable)
	if (res)
		return res;
	if (enable) {
		ctl.access_high = 1;
		ctl.bit_banging_enable = 1;
		res = tb_eeprom_ctl_write(sw, &ctl);
		if (res)
			return res;
		ctl.access_low = 0;
		ctl.fl_cs = 0;
		return tb_eeprom_ctl_write(sw, &ctl);
	} else {
		ctl.access_low = 1;
		ctl.fl_cs = 1;
		res = tb_eeprom_ctl_write(sw, &ctl);
		if (res)
			return res;
		ctl.access_high = 0;
		ctl.bit_banging_enable = 0;
		return tb_eeprom_ctl_write(sw, &ctl);
	}
}
@@ -65,8 +65,8 @@ static int tb_eeprom_active(struct tb_switch *sw, bool enable)
/*
 * tb_eeprom_transfer - transfer one bit
 *
 * If TB_EEPROM_IN is passed, then the bit can be retrieved from ctl->data_in.
 * If TB_EEPROM_OUT is passed, then ctl->data_out will be written.
 * If TB_EEPROM_IN is passed, then the bit can be retrieved from ctl->fl_do.
 * If TB_EEPROM_OUT is passed, then ctl->fl_di will be written.
 */
static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
			      enum tb_eeprom_transfer direction)
@@ -77,7 +77,7 @@ static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
		if (res)
			return res;
	}
	ctl->clock = 1;
	ctl->fl_sk = 1;
	res = tb_eeprom_ctl_write(sw, ctl);
	if (res)
		return res;
@@ -86,7 +86,7 @@ static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
		if (res)
			return res;
	}
	ctl->clock = 0;
	ctl->fl_sk = 0;
	return tb_eeprom_ctl_write(sw, ctl);
}

@@ -101,7 +101,7 @@ static int tb_eeprom_out(struct tb_switch *sw, u8 val)
	if (res)
		return res;
	for (i = 0; i < 8; i++) {
		ctl.data_out = val & 0x80;
		ctl.fl_di = val & 0x80;
		res = tb_eeprom_transfer(sw, &ctl, TB_EEPROM_OUT);
		if (res)
			return res;
@@ -126,7 +126,7 @@ static int tb_eeprom_in(struct tb_switch *sw, u8 *val)
		res = tb_eeprom_transfer(sw, &ctl, TB_EEPROM_IN);
		if (res)
			return res;
		*val |= ctl.data_in;
		*val |= ctl.fl_do;
	}
	return 0;
}
@@ -553,9 +553,9 @@ static int tb_drom_parse(struct tb_switch *sw)
	crc = tb_crc8((u8 *) &header->uid, 8);
	if (crc != header->uid_crc8) {
		tb_sw_warn(sw,
			"DROM UID CRC8 mismatch (expected: %#x, got: %#x), aborting\n",
			"DROM UID CRC8 mismatch (expected: %#x, got: %#x)\n",
			header->uid_crc8, crc);
		return -EINVAL;
		return -EILSEQ;
	}
	if (!sw->uid)
		sw->uid = header->uid;
@@ -654,6 +654,7 @@ int tb_drom_read(struct tb_switch *sw)
	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;
@@ -662,7 +663,11 @@ int tb_drom_read(struct tb_switch *sw)
	header = (void *) sw->drom;

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

@@ -683,11 +688,9 @@ int tb_drom_read(struct tb_switch *sw)

	/* If the DROM parsing fails, wait a moment and retry once */
	if (res == -EILSEQ && retries--) {
		tb_sw_warn(sw, "parsing DROM failed, retrying\n");
		tb_sw_warn(sw, "parsing DROM failed\n");
		msleep(100);
		res = tb_drom_read_n(sw, 0, sw->drom, size);
		if (!res)
			goto parse;
		goto read;
	}

	if (!res)
+110 −0
Original line number Diff line number Diff line
@@ -217,6 +217,116 @@ bool tb_lc_is_clx_supported(struct tb_port *port)
	return !!(val & TB_LC_LINK_ATTR_CPS);
}

/**
 * tb_lc_is_usb_plugged() - Is there USB device connected to port
 * @port: Device router lane 0 adapter
 *
 * Returns true if the @port has USB type-C device connected.
 */
bool tb_lc_is_usb_plugged(struct tb_port *port)
{
	struct tb_switch *sw = port->sw;
	int cap, ret;
	u32 val;

	if (sw->generation != 3)
		return false;

	cap = find_port_lc_cap(port);
	if (cap < 0)
		return false;

	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_CS_42, 1);
	if (ret)
		return false;

	return !!(val & TB_LC_CS_42_USB_PLUGGED);
}

/**
 * tb_lc_is_xhci_connected() - Is the internal xHCI connected
 * @port: Device router lane 0 adapter
 *
 * Returns true if the internal xHCI has been connected to @port.
 */
bool tb_lc_is_xhci_connected(struct tb_port *port)
{
	struct tb_switch *sw = port->sw;
	int cap, ret;
	u32 val;

	if (sw->generation != 3)
		return false;

	cap = find_port_lc_cap(port);
	if (cap < 0)
		return false;

	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
	if (ret)
		return false;

	return !!(val & TB_LC_LINK_REQ_XHCI_CONNECT);
}

static int __tb_lc_xhci_connect(struct tb_port *port, bool connect)
{
	struct tb_switch *sw = port->sw;
	int cap, ret;
	u32 val;

	if (sw->generation != 3)
		return -EINVAL;

	cap = find_port_lc_cap(port);
	if (cap < 0)
		return cap;

	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
	if (ret)
		return ret;

	if (connect)
		val |= TB_LC_LINK_REQ_XHCI_CONNECT;
	else
		val &= ~TB_LC_LINK_REQ_XHCI_CONNECT;

	return tb_sw_write(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
}

/**
 * tb_lc_xhci_connect() - Connect internal xHCI
 * @port: Device router lane 0 adapter
 *
 * Tells LC to connect the internal xHCI to @port. Returns %0 on success
 * and negative errno in case of failure. Can be called for Thunderbolt 3
 * routers only.
 */
int tb_lc_xhci_connect(struct tb_port *port)
{
	int ret;

	ret = __tb_lc_xhci_connect(port, true);
	if (ret)
		return ret;

	tb_port_dbg(port, "xHCI connected\n");
	return 0;
}

/**
 * tb_lc_xhci_disconnect() - Disconnect internal xHCI
 * @port: Device router lane 0 adapter
 *
 * Tells LC to disconnect the internal xHCI from @port. Can be called
 * for Thunderbolt 3 routers only.
 */
void tb_lc_xhci_disconnect(struct tb_port *port)
{
	__tb_lc_xhci_connect(port, false);
	tb_port_dbg(port, "xHCI disconnected\n");
}

static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
			      unsigned int flags)
{
+1 −2
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/delay.h>
@@ -1229,8 +1230,6 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
	spin_lock_init(&nhi->lock);

	res = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
	if (res)
		res = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
	if (res) {
		dev_err(&pdev->dev, "failed to set DMA mask\n");
		return res;
+76 −5
Original line number Diff line number Diff line
@@ -1528,7 +1528,13 @@ static int tb_plug_events_active(struct tb_switch *sw, bool active)
		case PCI_DEVICE_ID_INTEL_PORT_RIDGE:
			break;
		default:
			data |= 4;
			/*
			 * Skip Alpine Ridge, it needs to have vendor
			 * specific USB hotplug event enabled for the
			 * internal xHCI to work.
			 */
			if (!tb_switch_is_alpine_ridge(sw))
				data |= TB_PLUG_EVENTS_USB_DISABLE;
		}
	} else {
		data = data | 0x7c;
@@ -2778,10 +2784,8 @@ int tb_switch_add(struct tb_switch *sw)

		/* read drom */
		ret = tb_drom_read(sw);
		if (ret) {
			dev_err(&sw->dev, "reading DROM failed\n");
			return ret;
		}
		if (ret)
			dev_warn(&sw->dev, "reading DROM failed: %d\n", ret);
		tb_sw_dbg(sw, "uid: %#llx\n", sw->uid);

		tb_check_quirks(sw);
@@ -2974,6 +2978,10 @@ int tb_switch_resume(struct tb_switch *sw)
			return err;
		}

		/* We don't have any way to confirm this was the same device */
		if (!sw->uid)
			return -ENODEV;

		if (tb_switch_is_usb4(sw))
			err = usb4_switch_read_uid(sw, &uid);
		else
@@ -3689,3 +3697,66 @@ int tb_switch_pcie_l1_enable(struct tb_switch *sw)
	/* Write to Upstream PCIe bridge #0 aka Up0 */
	return tb_switch_pcie_bridge_write(sw, 0, 0x143, 0x0c5806b1);
}

/**
 * tb_switch_xhci_connect() - Connect internal xHCI
 * @sw: Router whose xHCI to connect
 *
 * Can be called to any router. For Alpine Ridge and Titan Ridge
 * performs special flows that bring the xHCI functional for any device
 * connected to the type-C port. Call only after PCIe tunnel has been
 * established. The function only does the connect if not done already
 * so can be called several times for the same router.
 */
int tb_switch_xhci_connect(struct tb_switch *sw)
{
	bool usb_port1, usb_port3, xhci_port1, xhci_port3;
	struct tb_port *port1, *port3;
	int ret;

	port1 = &sw->ports[1];
	port3 = &sw->ports[3];

	if (tb_switch_is_alpine_ridge(sw)) {
		usb_port1 = tb_lc_is_usb_plugged(port1);
		usb_port3 = tb_lc_is_usb_plugged(port3);
		xhci_port1 = tb_lc_is_xhci_connected(port1);
		xhci_port3 = tb_lc_is_xhci_connected(port3);

		/* Figure out correct USB port to connect */
		if (usb_port1 && !xhci_port1) {
			ret = tb_lc_xhci_connect(port1);
			if (ret)
				return ret;
		}
		if (usb_port3 && !xhci_port3)
			return tb_lc_xhci_connect(port3);
	} else if (tb_switch_is_titan_ridge(sw)) {
		ret = tb_lc_xhci_connect(port1);
		if (ret)
			return ret;
		return tb_lc_xhci_connect(port3);
	}

	return 0;
}

/**
 * tb_switch_xhci_disconnect() - Disconnect internal xHCI
 * @sw: Router whose xHCI to disconnect
 *
 * The opposite of tb_switch_xhci_connect(). Disconnects xHCI on both
 * ports.
 */
void tb_switch_xhci_disconnect(struct tb_switch *sw)
{
	if (sw->generation == 3) {
		struct tb_port *port1 = &sw->ports[1];
		struct tb_port *port3 = &sw->ports[3];

		tb_lc_xhci_disconnect(port1);
		tb_port_dbg(port1, "disconnected xHCI\n");
		tb_lc_xhci_disconnect(port3);
		tb_port_dbg(port3, "disconnected xHCI\n");
	}
}
Loading