dmaengine: apple-admac: Handle 'global' interrupt flags

In addition to TX channel and RX channel interrupt flags there's
another class of 'global' interrupt flags with unknown semantics. Those
weren't being handled up to now, and they are the suspected cause of
stuck IRQ states that have been sporadically occurring. Check the global
flags and clear them if raised.

Fixes: b127315d9a ("dmaengine: apple-admac: Add Apple ADMAC driver")
Signed-off-by: Martin Povišer <povik+lin@cutebit.org>
Link: https://lore.kernel.org/r/20230224152222.26732-1-povik+lin@cutebit.org
Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
Martin Povišer 2023-02-24 16:22:20 +01:00 committed by Vinod Koul
parent b771baf398
commit a288fd158f
1 changed files with 10 additions and 2 deletions

View File

@ -75,6 +75,7 @@
#define REG_TX_INTSTATE(idx) (0x0030 + (idx) * 4) #define REG_TX_INTSTATE(idx) (0x0030 + (idx) * 4)
#define REG_RX_INTSTATE(idx) (0x0040 + (idx) * 4) #define REG_RX_INTSTATE(idx) (0x0040 + (idx) * 4)
#define REG_GLOBAL_INTSTATE(idx) (0x0050 + (idx) * 4)
#define REG_CHAN_INTSTATUS(ch, idx) (0x8010 + (ch) * 0x200 + (idx) * 4) #define REG_CHAN_INTSTATUS(ch, idx) (0x8010 + (ch) * 0x200 + (idx) * 4)
#define REG_CHAN_INTMASK(ch, idx) (0x8020 + (ch) * 0x200 + (idx) * 4) #define REG_CHAN_INTMASK(ch, idx) (0x8020 + (ch) * 0x200 + (idx) * 4)
@ -672,13 +673,14 @@ static void admac_handle_chan_int(struct admac_data *ad, int no)
static irqreturn_t admac_interrupt(int irq, void *devid) static irqreturn_t admac_interrupt(int irq, void *devid)
{ {
struct admac_data *ad = devid; struct admac_data *ad = devid;
u32 rx_intstate, tx_intstate; u32 rx_intstate, tx_intstate, global_intstate;
int i; int i;
rx_intstate = readl_relaxed(ad->base + REG_RX_INTSTATE(ad->irq_index)); rx_intstate = readl_relaxed(ad->base + REG_RX_INTSTATE(ad->irq_index));
tx_intstate = readl_relaxed(ad->base + REG_TX_INTSTATE(ad->irq_index)); tx_intstate = readl_relaxed(ad->base + REG_TX_INTSTATE(ad->irq_index));
global_intstate = readl_relaxed(ad->base + REG_GLOBAL_INTSTATE(ad->irq_index));
if (!tx_intstate && !rx_intstate) if (!tx_intstate && !rx_intstate && !global_intstate)
return IRQ_NONE; return IRQ_NONE;
for (i = 0; i < ad->nchannels; i += 2) { for (i = 0; i < ad->nchannels; i += 2) {
@ -693,6 +695,12 @@ static irqreturn_t admac_interrupt(int irq, void *devid)
rx_intstate >>= 1; rx_intstate >>= 1;
} }
if (global_intstate) {
dev_warn(ad->dev, "clearing unknown global interrupt flag: %x\n",
global_intstate);
writel_relaxed(~(u32) 0, ad->base + REG_GLOBAL_INTSTATE(ad->irq_index));
}
return IRQ_HANDLED; return IRQ_HANDLED;
} }