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

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

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

Mika writes:

thunderbolt: Changes for v5.15 merge window

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

  * Include authorized value in the KOBJ_CHANGE event of a device router
  * A couple of improvements to get the driver working also with the AMD
    USB4 host controller.

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

* tag 'thunderbolt-for-v5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt:
  thunderbolt: Fix port linking by checking all adapters
  thunderbolt: Do not read control adapter config space
  thunderbolt: Handle ring interrupt by reading interrupt status register
  thunderbolt: Add vendor specific NHI quirk for auto-clearing interrupt status
  thunderbolt: Add authorized value to the KOBJ_CHANGE uevent
parents 85fb1a27 42716425
Loading
Loading
Loading
Loading
+38 −8
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@

#define NHI_MAILBOX_TIMEOUT	500 /* ms */

#define QUIRK_AUTO_CLEAR_INT	BIT(0)

static int ring_interrupt_index(struct tb_ring *ring)
{
	int bit = ring->hop;
@@ -66,15 +68,18 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
		else
			index = ring->hop + ring->nhi->hop_count;

		if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT) {
			/*
		 * Ask the hardware to clear interrupt status bits automatically
		 * since we already know which interrupt was triggered.
			 * Ask the hardware to clear interrupt status
			 * bits automatically since we already know
			 * which interrupt was triggered.
			 */
			misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
			if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) {
				misc |= REG_DMA_MISC_INT_AUTO_CLEAR;
				iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC);
			}
		}

		ivr_base = ring->nhi->iobase + REG_INT_VEC_ALLOC_BASE;
		step = index / REG_INT_VEC_ALLOC_REGS * REG_INT_VEC_ALLOC_BITS;
@@ -377,11 +382,24 @@ void tb_ring_poll_complete(struct tb_ring *ring)
}
EXPORT_SYMBOL_GPL(tb_ring_poll_complete);

static void ring_clear_msix(const struct tb_ring *ring)
{
	if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT)
		return;

	if (ring->is_tx)
		ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE);
	else
		ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE +
			 4 * (ring->nhi->hop_count / 32));
}

static irqreturn_t ring_msix(int irq, void *data)
{
	struct tb_ring *ring = data;

	spin_lock(&ring->nhi->lock);
	ring_clear_msix(ring);
	spin_lock(&ring->lock);
	__ring_interrupt(ring);
	spin_unlock(&ring->lock);
@@ -1074,6 +1092,16 @@ static void nhi_shutdown(struct tb_nhi *nhi)
		nhi->ops->shutdown(nhi);
}

static void nhi_check_quirks(struct tb_nhi *nhi)
{
	/*
	 * Intel hardware supports auto clear of the interrupt status
	 * reqister right after interrupt is being issued.
	 */
	if (nhi->pdev->vendor == PCI_VENDOR_ID_INTEL)
		nhi->quirks |= QUIRK_AUTO_CLEAR_INT;
}

static int nhi_init_msi(struct tb_nhi *nhi)
{
	struct pci_dev *pdev = nhi->pdev;
@@ -1190,6 +1218,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
	if (!nhi->tx_rings || !nhi->rx_rings)
		return -ENOMEM;

	nhi_check_quirks(nhi);

	res = nhi_init_msi(nhi);
	if (res) {
		dev_err(&pdev->dev, "cannot enable MSI, aborting\n");
+19 −9
Original line number Diff line number Diff line
@@ -724,6 +724,12 @@ static int tb_init_port(struct tb_port *port)
	int res;
	int cap;

	INIT_LIST_HEAD(&port->list);

	/* Control adapter does not have configuration space */
	if (!port->port)
		return 0;

	res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8);
	if (res) {
		if (res == -ENODEV) {
@@ -736,7 +742,7 @@ static int tb_init_port(struct tb_port *port)
	}

	/* Port 0 is the switch itself and has no PHY. */
	if (port->config.type == TB_TYPE_PORT && port->port != 0) {
	if (port->config.type == TB_TYPE_PORT) {
		cap = tb_port_find_cap(port, TB_PORT_CAP_PHY);

		if (cap > 0)
@@ -762,7 +768,7 @@ static int tb_init_port(struct tb_port *port)
		if (!port->ctl_credits)
			port->ctl_credits = 2;

	} else if (port->port != 0) {
	} else {
		cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP);
		if (cap > 0)
			port->cap_adap = cap;
@@ -773,10 +779,7 @@ static int tb_init_port(struct tb_port *port)
		ADP_CS_4_TOTAL_BUFFERS_SHIFT;

	tb_dump_port(port->sw->tb, port);

	INIT_LIST_HEAD(&port->list);
	return 0;

}

static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid,
@@ -1498,6 +1501,7 @@ static ssize_t authorized_show(struct device *dev,

static int disapprove_switch(struct device *dev, void *not_used)
{
	char *envp[] = { "AUTHORIZED=0", NULL };
	struct tb_switch *sw;

	sw = tb_to_switch(dev);
@@ -1514,7 +1518,7 @@ static int disapprove_switch(struct device *dev, void *not_used)
			return ret;

		sw->authorized = 0;
		kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
		kobject_uevent_env(&sw->dev.kobj, KOBJ_CHANGE, envp);
	}

	return 0;
@@ -1522,7 +1526,9 @@ static int disapprove_switch(struct device *dev, void *not_used)

static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
{
	char envp_string[13];
	int ret = -EINVAL;
	char *envp[] = { envp_string, NULL };

	if (!mutex_trylock(&sw->tb->lock))
		return restart_syscall();
@@ -1559,8 +1565,12 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)

	if (!ret) {
		sw->authorized = val;
		/* Notify status change to the userspace */
		kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
		/*
		 * Notify status change to the userspace, informing the new
		 * value of /sys/bus/thunderbolt/devices/.../authorized.
		 */
		sprintf(envp_string, "AUTHORIZED=%u", sw->authorized);
		kobject_uevent_env(&sw->dev.kobj, KOBJ_CHANGE, envp);
	}

unlock:
@@ -2443,7 +2453,7 @@ static void tb_switch_default_link_ports(struct tb_switch *sw)
{
	int i;

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

+2 −0
Original line number Diff line number Diff line
@@ -468,6 +468,7 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc)
 * @interrupt_work: Work scheduled to handle ring interrupt when no
 *		    MSI-X is used.
 * @hop_count: Number of rings (end point hops) supported by NHI.
 * @quirks: NHI specific quirks if any
 */
struct tb_nhi {
	spinlock_t lock;
@@ -480,6 +481,7 @@ struct tb_nhi {
	bool going_away;
	struct work_struct interrupt_work;
	u32 hop_count;
	unsigned long quirks;
};

/**