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

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
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
#define NHI_MAILBOX_TIMEOUT 500 /* ms */ #define NHI_MAILBOX_TIMEOUT 500 /* ms */
#define QUIRK_AUTO_CLEAR_INT BIT(0)
static int ring_interrupt_index(struct tb_ring *ring) static int ring_interrupt_index(struct tb_ring *ring)
{ {
int bit = ring->hop; int bit = ring->hop;
...@@ -66,14 +68,17 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active) ...@@ -66,14 +68,17 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
else else
index = ring->hop + ring->nhi->hop_count; 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
misc = ioread32(ring->nhi->iobase + REG_DMA_MISC); * which interrupt was triggered.
if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) { */
misc |= REG_DMA_MISC_INT_AUTO_CLEAR; misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
iowrite32(misc, 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; ivr_base = ring->nhi->iobase + REG_INT_VEC_ALLOC_BASE;
...@@ -377,11 +382,24 @@ void tb_ring_poll_complete(struct tb_ring *ring) ...@@ -377,11 +382,24 @@ void tb_ring_poll_complete(struct tb_ring *ring)
} }
EXPORT_SYMBOL_GPL(tb_ring_poll_complete); 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) static irqreturn_t ring_msix(int irq, void *data)
{ {
struct tb_ring *ring = data; struct tb_ring *ring = data;
spin_lock(&ring->nhi->lock); spin_lock(&ring->nhi->lock);
ring_clear_msix(ring);
spin_lock(&ring->lock); spin_lock(&ring->lock);
__ring_interrupt(ring); __ring_interrupt(ring);
spin_unlock(&ring->lock); spin_unlock(&ring->lock);
...@@ -1074,6 +1092,16 @@ static void nhi_shutdown(struct tb_nhi *nhi) ...@@ -1074,6 +1092,16 @@ static void nhi_shutdown(struct tb_nhi *nhi)
nhi->ops->shutdown(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) static int nhi_init_msi(struct tb_nhi *nhi)
{ {
struct pci_dev *pdev = nhi->pdev; struct pci_dev *pdev = nhi->pdev;
...@@ -1190,6 +1218,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -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) if (!nhi->tx_rings || !nhi->rx_rings)
return -ENOMEM; return -ENOMEM;
nhi_check_quirks(nhi);
res = nhi_init_msi(nhi); res = nhi_init_msi(nhi);
if (res) { if (res) {
dev_err(&pdev->dev, "cannot enable MSI, aborting\n"); dev_err(&pdev->dev, "cannot enable MSI, aborting\n");
......
...@@ -724,6 +724,12 @@ static int tb_init_port(struct tb_port *port) ...@@ -724,6 +724,12 @@ static int tb_init_port(struct tb_port *port)
int res; int res;
int cap; 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); res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8);
if (res) { if (res) {
if (res == -ENODEV) { if (res == -ENODEV) {
...@@ -736,7 +742,7 @@ static int tb_init_port(struct tb_port *port) ...@@ -736,7 +742,7 @@ static int tb_init_port(struct tb_port *port)
} }
/* Port 0 is the switch itself and has no PHY. */ /* 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); cap = tb_port_find_cap(port, TB_PORT_CAP_PHY);
if (cap > 0) if (cap > 0)
...@@ -762,7 +768,7 @@ static int tb_init_port(struct tb_port *port) ...@@ -762,7 +768,7 @@ static int tb_init_port(struct tb_port *port)
if (!port->ctl_credits) if (!port->ctl_credits)
port->ctl_credits = 2; port->ctl_credits = 2;
} else if (port->port != 0) { } else {
cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP); cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP);
if (cap > 0) if (cap > 0)
port->cap_adap = cap; port->cap_adap = cap;
...@@ -773,10 +779,7 @@ static int tb_init_port(struct tb_port *port) ...@@ -773,10 +779,7 @@ static int tb_init_port(struct tb_port *port)
ADP_CS_4_TOTAL_BUFFERS_SHIFT; ADP_CS_4_TOTAL_BUFFERS_SHIFT;
tb_dump_port(port->sw->tb, port); tb_dump_port(port->sw->tb, port);
INIT_LIST_HEAD(&port->list);
return 0; return 0;
} }
static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid, 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, ...@@ -1498,6 +1501,7 @@ static ssize_t authorized_show(struct device *dev,
static int disapprove_switch(struct device *dev, void *not_used) static int disapprove_switch(struct device *dev, void *not_used)
{ {
char *envp[] = { "AUTHORIZED=0", NULL };
struct tb_switch *sw; struct tb_switch *sw;
sw = tb_to_switch(dev); sw = tb_to_switch(dev);
...@@ -1514,7 +1518,7 @@ static int disapprove_switch(struct device *dev, void *not_used) ...@@ -1514,7 +1518,7 @@ static int disapprove_switch(struct device *dev, void *not_used)
return ret; return ret;
sw->authorized = 0; sw->authorized = 0;
kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE); kobject_uevent_env(&sw->dev.kobj, KOBJ_CHANGE, envp);
} }
return 0; return 0;
...@@ -1522,7 +1526,9 @@ static int disapprove_switch(struct device *dev, void *not_used) ...@@ -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) static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
{ {
char envp_string[13];
int ret = -EINVAL; int ret = -EINVAL;
char *envp[] = { envp_string, NULL };
if (!mutex_trylock(&sw->tb->lock)) if (!mutex_trylock(&sw->tb->lock))
return restart_syscall(); return restart_syscall();
...@@ -1559,8 +1565,12 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val) ...@@ -1559,8 +1565,12 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
if (!ret) { if (!ret) {
sw->authorized = val; 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: unlock:
...@@ -2443,7 +2453,7 @@ static void tb_switch_default_link_ports(struct tb_switch *sw) ...@@ -2443,7 +2453,7 @@ static void tb_switch_default_link_ports(struct tb_switch *sw)
{ {
int i; 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 *port = &sw->ports[i];
struct tb_port *subordinate; struct tb_port *subordinate;
......
...@@ -468,6 +468,7 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc) ...@@ -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 * @interrupt_work: Work scheduled to handle ring interrupt when no
* MSI-X is used. * MSI-X is used.
* @hop_count: Number of rings (end point hops) supported by NHI. * @hop_count: Number of rings (end point hops) supported by NHI.
* @quirks: NHI specific quirks if any
*/ */
struct tb_nhi { struct tb_nhi {
spinlock_t lock; spinlock_t lock;
...@@ -480,6 +481,7 @@ struct tb_nhi { ...@@ -480,6 +481,7 @@ struct tb_nhi {
bool going_away; bool going_away;
struct work_struct interrupt_work; struct work_struct interrupt_work;
u32 hop_count; u32 hop_count;
unsigned long quirks;
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment