lkml.org 
[lkml]   [2019]   [May]   [22]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH AUTOSEL 5.1 075/375] dmaengine: pl330: _stop: clear interrupt status
    Date
    From: Sugar Zhang <sugar.zhang@rock-chips.com>

    [ 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
    \
     
     \ /
      Last update: 2019-05-22 22:00    [W:2.666 / U:0.972 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site