Commit 1d069bfa authored by Michael Olbrich's avatar Michael Olbrich Committed by Vinod Koul

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: default avatarMichael Olbrich <m.olbrich@pengutronix.de>
Signed-off-by: default avatarLucas Stach <l.stach@pengutronix.de>
Signed-off-by: default avatarVinod Koul <vinod.koul@intel.com>
parent 6a2cf55d
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
*/ */
#include <linux/init.h> #include <linux/init.h>
#include <linux/iopoll.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/bitops.h> #include <linux/bitops.h>
...@@ -571,28 +572,20 @@ static void sdma_enable_channel(struct sdma_engine *sdma, int channel) ...@@ -571,28 +572,20 @@ static void sdma_enable_channel(struct sdma_engine *sdma, int channel)
static int sdma_run_channel0(struct sdma_engine *sdma) static int sdma_run_channel0(struct sdma_engine *sdma)
{ {
int ret; int ret;
unsigned long timeout = 500; u32 reg;
sdma_enable_channel(sdma, 0); sdma_enable_channel(sdma, 0);
while (!(ret = readl_relaxed(sdma->regs + SDMA_H_INTR) & 1)) { ret = readl_relaxed_poll_timeout_atomic(sdma->regs + SDMA_H_STATSTOP,
if (timeout-- <= 0) reg, !(reg & 1), 1, 500);
break; if (ret)
udelay(1);
}
if (ret) {
/* Clear the interrupt status */
writel_relaxed(ret, sdma->regs + SDMA_H_INTR);
} else {
dev_err(sdma->dev, "Timeout waiting for CH0 ready\n"); dev_err(sdma->dev, "Timeout waiting for CH0 ready\n");
}
/* Set bits of CONFIG register with dynamic context switching */ /* Set bits of CONFIG register with dynamic context switching */
if (readl(sdma->regs + SDMA_H_CONFIG) == 0) if (readl(sdma->regs + SDMA_H_CONFIG) == 0)
writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG); writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG);
return ret ? 0 : -ETIMEDOUT; return ret;
} }
static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size, 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) ...@@ -727,9 +720,9 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id)
unsigned long stat; unsigned long stat;
stat = readl_relaxed(sdma->regs + SDMA_H_INTR); stat = readl_relaxed(sdma->regs + SDMA_H_INTR);
/* not interested in channel 0 interrupts */
stat &= ~1;
writel_relaxed(stat, sdma->regs + SDMA_H_INTR); writel_relaxed(stat, sdma->regs + SDMA_H_INTR);
/* channel 0 is special and not handled here, see run_channel0() */
stat &= ~1;
while (stat) { while (stat) {
int channel = fls(stat) - 1; int channel = fls(stat) - 1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment