| From 0a59811e19787a9cd15cb4e866637cf49d5740c3 Mon Sep 17 00:00:00 2001 |
| From: Sugar Zhang <sugar.zhang@rock-chips.com> |
| Date: Wed, 3 Apr 2019 19:06:22 +0800 |
| Subject: dmaengine: pl330: _stop: clear interrupt status |
| |
| [ Upstream commit 2da254cc7908105a60a6bb219d18e8dced03dcb9 ] |
| |
| 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> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/dma/pl330.c | 10 +++++++--- |
| 1 file changed, 7 insertions(+), 3 deletions(-) |
| |
| diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c |
| index eec79fdf27a5b..56695ffb5d377 100644 |
| --- a/drivers/dma/pl330.c |
| +++ b/drivers/dma/pl330.c |
| @@ -966,6 +966,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); |
| @@ -978,10 +979,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' */ |
| -- |
| 2.20.1 |
| |