diff options
-rw-r--r-- | drivers/thunderbolt/nhi.c | 46 | ||||
-rw-r--r-- | drivers/thunderbolt/switch.c | 28 | ||||
-rw-r--r-- | include/linux/thunderbolt.h | 2 |
3 files changed, 59 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; diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index e7c96c37174f..124e13cb1469 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -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; }; /** |