Skip to content

Commit 1d069bf

Browse files
michaelolbrichVinod Koul
authored andcommitted
dmaengine: imx-sdma: ack channel 0 IRQ in the interrupt handler
Currently the handler ignores the channel 0 interrupt and thus doesn't ack it properly. This is done in order to allow sdma_run_channel0() to poll on the irq status bit, as this function may be called in atomic context, but needs to know when the channel has finished. This works mostly, as the polling happens under a spinlock, disabling IRQs on the local CPU, leaving only a very slight race window for a spurious IRQ to happen if the handler is executed on another CPU in an SMP system. Still this is clearly suboptimal. This behavior turns into a real problem on an RT system, where the spinlock doesn't disable IRQs on the local CPU. Not acking the IRQ in the handler in such a setup is very likely to drown the CPU in an IRQ storm, leaving it unable to make any progress in the polling loop, leading to the IRQ never being acked. Fix this by properly acknowledging the channel 0 IRQ in the handler. As the IRQ status bit can no longer be used to poll for the channel completion, switch over to using the SDMA_H_STATSTOP register for this purpose, where bit 0 is cleared by the hardware when the channel is done. Signed-off-by: Michael Olbrich <[email protected]> Signed-off-by: Lucas Stach <[email protected]> Signed-off-by: Vinod Koul <[email protected]>
1 parent 6a2cf55 commit 1d069bf

File tree

1 file changed

+8
-15
lines changed

1 file changed

+8
-15
lines changed

drivers/dma/imx-sdma.c

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919

2020
#include <linux/init.h>
21+
#include <linux/iopoll.h>
2122
#include <linux/module.h>
2223
#include <linux/types.h>
2324
#include <linux/bitops.h>
@@ -571,28 +572,20 @@ static void sdma_enable_channel(struct sdma_engine *sdma, int channel)
571572
static int sdma_run_channel0(struct sdma_engine *sdma)
572573
{
573574
int ret;
574-
unsigned long timeout = 500;
575+
u32 reg;
575576

576577
sdma_enable_channel(sdma, 0);
577578

578-
while (!(ret = readl_relaxed(sdma->regs + SDMA_H_INTR) & 1)) {
579-
if (timeout-- <= 0)
580-
break;
581-
udelay(1);
582-
}
583-
584-
if (ret) {
585-
/* Clear the interrupt status */
586-
writel_relaxed(ret, sdma->regs + SDMA_H_INTR);
587-
} else {
579+
ret = readl_relaxed_poll_timeout_atomic(sdma->regs + SDMA_H_STATSTOP,
580+
reg, !(reg & 1), 1, 500);
581+
if (ret)
588582
dev_err(sdma->dev, "Timeout waiting for CH0 ready\n");
589-
}
590583

591584
/* Set bits of CONFIG register with dynamic context switching */
592585
if (readl(sdma->regs + SDMA_H_CONFIG) == 0)
593586
writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG);
594587

595-
return ret ? 0 : -ETIMEDOUT;
588+
return ret;
596589
}
597590

598591
static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size,
@@ -727,9 +720,9 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id)
727720
unsigned long stat;
728721

729722
stat = readl_relaxed(sdma->regs + SDMA_H_INTR);
730-
/* not interested in channel 0 interrupts */
731-
stat &= ~1;
732723
writel_relaxed(stat, sdma->regs + SDMA_H_INTR);
724+
/* channel 0 is special and not handled here, see run_channel0() */
725+
stat &= ~1;
733726

734727
while (stat) {
735728
int channel = fls(stat) - 1;

0 commit comments

Comments
 (0)