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: 7a1808f82a ("thunderbolt: Handle ring interrupt by reading interrupt status register")
Cc: Sanju Mehta <Sanju.Mehta@amd.com>
Cc: stable@vger.kernel.org
Tested-by: Anson Tsao <anson.tsao@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
This commit is contained in:
Mario Limonciello 2023-03-10 11:20:50 -06:00 committed by Mika Westerberg
parent 1716efdb07
commit 468c49f447
2 changed files with 29 additions and 17 deletions

View file

@ -71,24 +71,31 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
u32 step, shift, ivr, misc; u32 step, shift, ivr, misc;
void __iomem *ivr_base; void __iomem *ivr_base;
int index; int index;
int bit;
if (ring->is_tx) if (ring->is_tx)
index = ring->hop; index = ring->hop;
else else
index = ring->hop + ring->nhi->hop_count; index = ring->hop + ring->nhi->hop_count;
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT) { /*
/* * Intel routers support a bit that isn't part of
* Ask the hardware to clear interrupt status * the USB4 spec to ask the hardware to clear
* bits automatically since we already know * interrupt status bits automatically since
* which interrupt was triggered. * we already know which interrupt was triggered.
*/ *
misc = ioread32(ring->nhi->iobase + REG_DMA_MISC); * Other routers explicitly disable auto-clear
if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) { * to prevent conditions that may occur where two
misc |= REG_DMA_MISC_INT_AUTO_CLEAR; * MSIX interrupts are simultaneously active and
iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC); * reading the register clears both of them.
} */
} misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT)
bit = REG_DMA_MISC_INT_AUTO_CLEAR;
else
bit = REG_DMA_MISC_DISABLE_AUTO_CLEAR;
if (!(misc & bit))
iowrite32(misc | bit, 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;
step = index / REG_INT_VEC_ALLOC_REGS * REG_INT_VEC_ALLOC_BITS; step = index / REG_INT_VEC_ALLOC_REGS * REG_INT_VEC_ALLOC_BITS;
@ -393,14 +400,17 @@ EXPORT_SYMBOL_GPL(tb_ring_poll_complete);
static void ring_clear_msix(const struct tb_ring *ring) static void ring_clear_msix(const struct tb_ring *ring)
{ {
int bit;
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT) if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT)
return; return;
bit = ring_interrupt_index(ring) & 31;
if (ring->is_tx) if (ring->is_tx)
ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE); iowrite32(BIT(bit), ring->nhi->iobase + REG_RING_INT_CLEAR);
else else
ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE + iowrite32(BIT(bit), ring->nhi->iobase + REG_RING_INT_CLEAR +
4 * (ring->nhi->hop_count / 32)); 4 * (ring->nhi->hop_count / 32));
} }
static irqreturn_t ring_msix(int irq, void *data) static irqreturn_t ring_msix(int irq, void *data)

View file

@ -77,12 +77,13 @@ struct ring_desc {
/* /*
* three bitfields: tx, rx, rx overflow * three bitfields: tx, rx, rx overflow
* Every bitfield contains one bit for every hop (REG_HOP_COUNT). Registers are * Every bitfield contains one bit for every hop (REG_HOP_COUNT).
* cleared on read. New interrupts are fired only after ALL registers have been * New interrupts are fired only after ALL registers have been
* read (even those containing only disabled rings). * read (even those containing only disabled rings).
*/ */
#define REG_RING_NOTIFY_BASE 0x37800 #define REG_RING_NOTIFY_BASE 0x37800
#define RING_NOTIFY_REG_COUNT(nhi) ((31 + 3 * nhi->hop_count) / 32) #define RING_NOTIFY_REG_COUNT(nhi) ((31 + 3 * nhi->hop_count) / 32)
#define REG_RING_INT_CLEAR 0x37808
/* /*
* two bitfields: rx, tx * two bitfields: rx, tx
@ -105,6 +106,7 @@ struct ring_desc {
#define REG_DMA_MISC 0x39864 #define REG_DMA_MISC 0x39864
#define REG_DMA_MISC_INT_AUTO_CLEAR BIT(2) #define REG_DMA_MISC_INT_AUTO_CLEAR BIT(2)
#define REG_DMA_MISC_DISABLE_AUTO_CLEAR BIT(17)
#define REG_INMAIL_DATA 0x39900 #define REG_INMAIL_DATA 0x39900