summaryrefslogtreecommitdiff
path: root/drivers/thunderbolt
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2021-08-24 15:31:33 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2021-08-24 15:31:33 +0200
commitbfa109d761a46d5116f3d4f216152653d8b4815b (patch)
treebe73c531e4369422a6368d13564e3e377bdb7e55 /drivers/thunderbolt
parent85fb1a27b128f6404cb9a08f3a4a094e92a78ac0 (diff)
parent42716425ad7e1b6529ec61c260c11176841f4b5f (diff)
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
Diffstat (limited to 'drivers/thunderbolt')
-rw-r--r--drivers/thunderbolt/nhi.c46
-rw-r--r--drivers/thunderbolt/switch.c28
2 files changed, 57 insertions, 17 deletions
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index fa44332845a1..c73da0532be4 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -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,14 +68,17 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
else
index = ring->hop + ring->nhi->hop_count;
- /*
- * 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);
+ 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.
+ */
+ 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;
@@ -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");
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 10d6b228cc94..3014146081c1 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -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;