Skip to content

Commit 468c49f

Browse files
superm1westeri
authored andcommitted
thunderbolt: Disable interrupt auto clear for rings
When interrupt auto clear is programmed, any read to the interrupt status register will clear all interrupts. If two interrupts have come in before one can be serviced then this will cause lost interrupts. On AMD USB4 routers this has manifested in odd problems particularly with long strings of control tranfers such as reading the DROM via bit banging. Instead of clearing interrupts automatically, clear the bit corresponding to the given ring's interrupt in the ISR. Fixes: 7a1808f ("thunderbolt: Handle ring interrupt by reading interrupt status register") Cc: Sanju Mehta <[email protected]> Cc: [email protected] Tested-by: Anson Tsao <[email protected]> Signed-off-by: Mario Limonciello <[email protected]> Signed-off-by: Mika Westerberg <[email protected]>
1 parent 1716efd commit 468c49f

File tree

2 files changed

+29
-17
lines changed

2 files changed

+29
-17
lines changed

drivers/thunderbolt/nhi.c

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -71,24 +71,31 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
7171
u32 step, shift, ivr, misc;
7272
void __iomem *ivr_base;
7373
int index;
74+
int bit;
7475

7576
if (ring->is_tx)
7677
index = ring->hop;
7778
else
7879
index = ring->hop + ring->nhi->hop_count;
7980

80-
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT) {
81-
/*
82-
* Ask the hardware to clear interrupt status
83-
* bits automatically since we already know
84-
* which interrupt was triggered.
85-
*/
86-
misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
87-
if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) {
88-
misc |= REG_DMA_MISC_INT_AUTO_CLEAR;
89-
iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC);
90-
}
91-
}
81+
/*
82+
* Intel routers support a bit that isn't part of
83+
* the USB4 spec to ask the hardware to clear
84+
* interrupt status bits automatically since
85+
* we already know which interrupt was triggered.
86+
*
87+
* Other routers explicitly disable auto-clear
88+
* to prevent conditions that may occur where two
89+
* MSIX interrupts are simultaneously active and
90+
* reading the register clears both of them.
91+
*/
92+
misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
93+
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT)
94+
bit = REG_DMA_MISC_INT_AUTO_CLEAR;
95+
else
96+
bit = REG_DMA_MISC_DISABLE_AUTO_CLEAR;
97+
if (!(misc & bit))
98+
iowrite32(misc | bit, ring->nhi->iobase + REG_DMA_MISC);
9299

93100
ivr_base = ring->nhi->iobase + REG_INT_VEC_ALLOC_BASE;
94101
step = index / REG_INT_VEC_ALLOC_REGS * REG_INT_VEC_ALLOC_BITS;
@@ -393,14 +400,17 @@ EXPORT_SYMBOL_GPL(tb_ring_poll_complete);
393400

394401
static void ring_clear_msix(const struct tb_ring *ring)
395402
{
403+
int bit;
404+
396405
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT)
397406
return;
398407

408+
bit = ring_interrupt_index(ring) & 31;
399409
if (ring->is_tx)
400-
ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE);
410+
iowrite32(BIT(bit), ring->nhi->iobase + REG_RING_INT_CLEAR);
401411
else
402-
ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE +
403-
4 * (ring->nhi->hop_count / 32));
412+
iowrite32(BIT(bit), ring->nhi->iobase + REG_RING_INT_CLEAR +
413+
4 * (ring->nhi->hop_count / 32));
404414
}
405415

406416
static irqreturn_t ring_msix(int irq, void *data)

drivers/thunderbolt/nhi_regs.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,13 @@ struct ring_desc {
7777

7878
/*
7979
* three bitfields: tx, rx, rx overflow
80-
* Every bitfield contains one bit for every hop (REG_HOP_COUNT). Registers are
81-
* cleared on read. New interrupts are fired only after ALL registers have been
80+
* Every bitfield contains one bit for every hop (REG_HOP_COUNT).
81+
* New interrupts are fired only after ALL registers have been
8282
* read (even those containing only disabled rings).
8383
*/
8484
#define REG_RING_NOTIFY_BASE 0x37800
8585
#define RING_NOTIFY_REG_COUNT(nhi) ((31 + 3 * nhi->hop_count) / 32)
86+
#define REG_RING_INT_CLEAR 0x37808
8687

8788
/*
8889
* two bitfields: rx, tx
@@ -105,6 +106,7 @@ struct ring_desc {
105106

106107
#define REG_DMA_MISC 0x39864
107108
#define REG_DMA_MISC_INT_AUTO_CLEAR BIT(2)
109+
#define REG_DMA_MISC_DISABLE_AUTO_CLEAR BIT(17)
108110

109111
#define REG_INMAIL_DATA 0x39900
110112

0 commit comments

Comments
 (0)