Skip to content

Commit bfa109d

Browse files
committed
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
2 parents 85fb1a2 + 4271642 commit bfa109d

File tree

3 files changed

+59
-17
lines changed

3 files changed

+59
-17
lines changed

drivers/thunderbolt/nhi.c

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535

3636
#define NHI_MAILBOX_TIMEOUT 500 /* ms */
3737

38+
#define QUIRK_AUTO_CLEAR_INT BIT(0)
39+
3840
static int ring_interrupt_index(struct tb_ring *ring)
3941
{
4042
int bit = ring->hop;
@@ -66,14 +68,17 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
6668
else
6769
index = ring->hop + ring->nhi->hop_count;
6870

69-
/*
70-
* Ask the hardware to clear interrupt status bits automatically
71-
* since we already know which interrupt was triggered.
72-
*/
73-
misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
74-
if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) {
75-
misc |= REG_DMA_MISC_INT_AUTO_CLEAR;
76-
iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC);
71+
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT) {
72+
/*
73+
* Ask the hardware to clear interrupt status
74+
* bits automatically since we already know
75+
* which interrupt was triggered.
76+
*/
77+
misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
78+
if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) {
79+
misc |= REG_DMA_MISC_INT_AUTO_CLEAR;
80+
iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC);
81+
}
7782
}
7883

7984
ivr_base = ring->nhi->iobase + REG_INT_VEC_ALLOC_BASE;
@@ -377,11 +382,24 @@ void tb_ring_poll_complete(struct tb_ring *ring)
377382
}
378383
EXPORT_SYMBOL_GPL(tb_ring_poll_complete);
379384

385+
static void ring_clear_msix(const struct tb_ring *ring)
386+
{
387+
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT)
388+
return;
389+
390+
if (ring->is_tx)
391+
ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE);
392+
else
393+
ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE +
394+
4 * (ring->nhi->hop_count / 32));
395+
}
396+
380397
static irqreturn_t ring_msix(int irq, void *data)
381398
{
382399
struct tb_ring *ring = data;
383400

384401
spin_lock(&ring->nhi->lock);
402+
ring_clear_msix(ring);
385403
spin_lock(&ring->lock);
386404
__ring_interrupt(ring);
387405
spin_unlock(&ring->lock);
@@ -1074,6 +1092,16 @@ static void nhi_shutdown(struct tb_nhi *nhi)
10741092
nhi->ops->shutdown(nhi);
10751093
}
10761094

1095+
static void nhi_check_quirks(struct tb_nhi *nhi)
1096+
{
1097+
/*
1098+
* Intel hardware supports auto clear of the interrupt status
1099+
* reqister right after interrupt is being issued.
1100+
*/
1101+
if (nhi->pdev->vendor == PCI_VENDOR_ID_INTEL)
1102+
nhi->quirks |= QUIRK_AUTO_CLEAR_INT;
1103+
}
1104+
10771105
static int nhi_init_msi(struct tb_nhi *nhi)
10781106
{
10791107
struct pci_dev *pdev = nhi->pdev;
@@ -1190,6 +1218,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
11901218
if (!nhi->tx_rings || !nhi->rx_rings)
11911219
return -ENOMEM;
11921220

1221+
nhi_check_quirks(nhi);
1222+
11931223
res = nhi_init_msi(nhi);
11941224
if (res) {
11951225
dev_err(&pdev->dev, "cannot enable MSI, aborting\n");

drivers/thunderbolt/switch.c

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,12 @@ static int tb_init_port(struct tb_port *port)
724724
int res;
725725
int cap;
726726

727+
INIT_LIST_HEAD(&port->list);
728+
729+
/* Control adapter does not have configuration space */
730+
if (!port->port)
731+
return 0;
732+
727733
res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8);
728734
if (res) {
729735
if (res == -ENODEV) {
@@ -736,7 +742,7 @@ static int tb_init_port(struct tb_port *port)
736742
}
737743

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

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

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

775781
tb_dump_port(port->sw->tb, port);
776-
777-
INIT_LIST_HEAD(&port->list);
778782
return 0;
779-
780783
}
781784

782785
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,
14981501

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

15031507
sw = tb_to_switch(dev);
@@ -1514,15 +1518,17 @@ static int disapprove_switch(struct device *dev, void *not_used)
15141518
return ret;
15151519

15161520
sw->authorized = 0;
1517-
kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
1521+
kobject_uevent_env(&sw->dev.kobj, KOBJ_CHANGE, envp);
15181522
}
15191523

15201524
return 0;
15211525
}
15221526

15231527
static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
15241528
{
1529+
char envp_string[13];
15251530
int ret = -EINVAL;
1531+
char *envp[] = { envp_string, NULL };
15261532

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

15601566
if (!ret) {
15611567
sw->authorized = val;
1562-
/* Notify status change to the userspace */
1563-
kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
1568+
/*
1569+
* Notify status change to the userspace, informing the new
1570+
* value of /sys/bus/thunderbolt/devices/.../authorized.
1571+
*/
1572+
sprintf(envp_string, "AUTHORIZED=%u", sw->authorized);
1573+
kobject_uevent_env(&sw->dev.kobj, KOBJ_CHANGE, envp);
15641574
}
15651575

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

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

include/linux/thunderbolt.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc)
468468
* @interrupt_work: Work scheduled to handle ring interrupt when no
469469
* MSI-X is used.
470470
* @hop_count: Number of rings (end point hops) supported by NHI.
471+
* @quirks: NHI specific quirks if any
471472
*/
472473
struct tb_nhi {
473474
spinlock_t lock;
@@ -480,6 +481,7 @@ struct tb_nhi {
480481
bool going_away;
481482
struct work_struct interrupt_work;
482483
u32 hop_count;
484+
unsigned long quirks;
483485
};
484486

485487
/**

0 commit comments

Comments
 (0)