Skip to content

Commit

Permalink
dmaengine: pl330: _stop: clear interrupt status
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Sugar Zhang authored and Vinod Koul committed Apr 26, 2019
1 parent 9a05045 commit 2da254c
Showing 1 changed file with 7 additions and 3 deletions.
10 changes: 7 additions & 3 deletions drivers/dma/pl330.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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' */
Expand Down

0 comments on commit 2da254c

Please sign in to comment.