summaryrefslogtreecommitdiffstats
path: root/drivers/dma/pl330.c
diff options
context:
space:
mode:
authorSugar Zhang <sugar.zhang@rock-chips.com>2019-04-03 13:06:22 +0200
committerVinod Koul <vkoul@kernel.org>2019-04-26 13:21:37 +0200
commit2da254cc7908105a60a6bb219d18e8dced03dcb9 (patch)
tree0d58d112ce9b0c64f73b4f24e0d3b0092891a83c /drivers/dma/pl330.c
parentdmaengine: axi-dmac: Enable DMA_INTERLEAVE capability (diff)
downloadlinux-2da254cc7908105a60a6bb219d18e8dced03dcb9.tar.xz
linux-2da254cc7908105a60a6bb219d18e8dced03dcb9.zip
dmaengine: pl330: _stop: clear interrupt status
This patch kill instructs the DMAC to immediately terminate execution of a thread. and then clear the interrupt status, at last, stop generating interrupts for DMA_SEV. to guarantee the next dma start is clean. otherwise, one interrupt maybe leave to next start and make some mistake. we can reporduce the problem as follows: DMASEV: modify the event-interrupt resource, and if the INTEN sets function as interrupt, the DMAC will set irq<event_num> HIGH to generate interrupt. write INTCLR to clear interrupt. DMA EXECUTING INSTRUCTS DMA TERMINATE | | | | ... _stop | | | spin_lock_irqsave DMASEV | | | | mask INTEN | | | DMAKILL | | | spin_unlock_irqrestore in above case, a interrupt was left, and if we unmask INTEN, the DMAC will set irq<event_num> HIGH to generate interrupt. to fix this, do as follows: DMA EXECUTING INSTRUCTS DMA TERMINATE | | | | ... _stop | | | spin_lock_irqsave DMASEV | | | | DMAKILL | | | clear INTCLR | mask INTEN | | | spin_unlock_irqrestore Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com> Signed-off-by: Vinod Koul <vkoul@kernel.org>
Diffstat (limited to 'drivers/dma/pl330.c')
-rw-r--r--drivers/dma/pl330.c10
1 files changed, 7 insertions, 3 deletions
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index c72f6fd79c43..6e6837214210 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -967,6 +967,7 @@ static void _stop(struct pl330_thread *thrd)
{
void __iomem *regs = thrd->dmac->base;
u8 insn[6] = {0, 0, 0, 0, 0, 0};
+ u32 inten = readl(regs + INTEN);
if (_state(thrd) == PL330_STATE_FAULT_COMPLETING)
UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING);
@@ -979,10 +980,13 @@ static void _stop(struct pl330_thread *thrd)
_emit_KILL(0, insn);
- /* Stop generating interrupts for SEV */
- writel(readl(regs + INTEN) & ~(1 << thrd->ev), regs + INTEN);
-
_execute_DBGINSN(thrd, insn, is_manager(thrd));
+
+ /* clear the event */
+ if (inten & (1 << thrd->ev))
+ writel(1 << thrd->ev, regs + INTCLR);
+ /* Stop generating interrupts for SEV */
+ writel(inten & ~(1 << thrd->ev), regs + INTEN);
}
/* Start doing req 'idx' of thread 'thrd' */