Skip to content

Commit 6a40fb8

Browse files
Fomysvinodkoul
authored andcommitted
dmaengine: xilinx: xdma: Fix synchronization issue
The current xdma_synchronize method does not properly wait for the last transfer to be done. Due to limitations of the XMDA engine, it is not possible to stop a transfer in the middle of a descriptor. Said otherwise, if a stop is requested at the end of descriptor "N" and the OS is fast enough, the DMA controller will effectively stop immediately. However, if the OS is slightly too slow to request the stop and the DMA engine starts descriptor "N+1", the N+1 transfer will be performed until its end. This means that after a terminate_all, the last descriptor must remain valid and the synchronization must wait for this last descriptor to be terminated. Fixes: 855c2e1 ("dmaengine: xilinx: xdma: Rework xdma_terminate_all()") Fixes: f5c392d ("dmaengine: xilinx: xdma: Add terminate_all/synchronize callbacks") Cc: [email protected] Suggested-by: Miquel Raynal <[email protected]> Signed-off-by: Louis Chauvet <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Vinod Koul <[email protected]>
1 parent 5b9706b commit 6a40fb8

File tree

2 files changed

+21
-8
lines changed

2 files changed

+21
-8
lines changed

drivers/dma/xilinx/xdma-regs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ struct xdma_hw_desc {
117117
CHAN_CTRL_IE_WRITE_ERROR | \
118118
CHAN_CTRL_IE_DESC_ERROR)
119119

120+
/* bits of the channel status register */
121+
#define XDMA_CHAN_STATUS_BUSY BIT(0)
122+
120123
#define XDMA_CHAN_STATUS_MASK CHAN_CTRL_START
121124

122125
#define XDMA_CHAN_ERROR_MASK (CHAN_CTRL_IE_DESC_ALIGN_MISMATCH | \

drivers/dma/xilinx/xdma.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ struct xdma_chan {
7171
enum dma_transfer_direction dir;
7272
struct dma_slave_config cfg;
7373
u32 irq;
74+
struct completion last_interrupt;
75+
bool stop_requested;
7476
};
7577

7678
/**
@@ -376,6 +378,8 @@ static int xdma_xfer_start(struct xdma_chan *xchan)
376378
return ret;
377379

378380
xchan->busy = true;
381+
xchan->stop_requested = false;
382+
reinit_completion(&xchan->last_interrupt);
379383

380384
return 0;
381385
}
@@ -387,21 +391,14 @@ static int xdma_xfer_start(struct xdma_chan *xchan)
387391
static int xdma_xfer_stop(struct xdma_chan *xchan)
388392
{
389393
int ret;
390-
u32 val;
391394
struct xdma_device *xdev = xchan->xdev_hdl;
392395

393396
/* clear run stop bit to prevent any further auto-triggering */
394397
ret = regmap_write(xdev->rmap, xchan->base + XDMA_CHAN_CONTROL_W1C,
395398
CHAN_CTRL_RUN_STOP);
396399
if (ret)
397400
return ret;
398-
399-
/* Clear the channel status register */
400-
ret = regmap_read(xdev->rmap, xchan->base + XDMA_CHAN_STATUS_RC, &val);
401-
if (ret)
402-
return ret;
403-
404-
return 0;
401+
return ret;
405402
}
406403

407404
/**
@@ -474,6 +471,8 @@ static int xdma_alloc_channels(struct xdma_device *xdev,
474471
xchan->xdev_hdl = xdev;
475472
xchan->base = base + i * XDMA_CHAN_STRIDE;
476473
xchan->dir = dir;
474+
xchan->stop_requested = false;
475+
init_completion(&xchan->last_interrupt);
477476

478477
ret = xdma_channel_init(xchan);
479478
if (ret)
@@ -521,6 +520,7 @@ static int xdma_terminate_all(struct dma_chan *chan)
521520
spin_lock_irqsave(&xdma_chan->vchan.lock, flags);
522521

523522
xdma_chan->busy = false;
523+
xdma_chan->stop_requested = true;
524524
vd = vchan_next_desc(&xdma_chan->vchan);
525525
if (vd) {
526526
list_del(&vd->node);
@@ -542,6 +542,13 @@ static int xdma_terminate_all(struct dma_chan *chan)
542542
static void xdma_synchronize(struct dma_chan *chan)
543543
{
544544
struct xdma_chan *xdma_chan = to_xdma_chan(chan);
545+
struct xdma_device *xdev = xdma_chan->xdev_hdl;
546+
int st = 0;
547+
548+
/* If the engine continues running, wait for the last interrupt */
549+
regmap_read(xdev->rmap, xdma_chan->base + XDMA_CHAN_STATUS, &st);
550+
if (st & XDMA_CHAN_STATUS_BUSY)
551+
wait_for_completion_timeout(&xdma_chan->last_interrupt, msecs_to_jiffies(1000));
545552

546553
vchan_synchronize(&xdma_chan->vchan);
547554
}
@@ -876,6 +883,9 @@ static irqreturn_t xdma_channel_isr(int irq, void *dev_id)
876883
u32 st;
877884
bool repeat_tx;
878885

886+
if (xchan->stop_requested)
887+
complete(&xchan->last_interrupt);
888+
879889
spin_lock(&xchan->vchan.lock);
880890

881891
/* get submitted request */

0 commit comments

Comments
 (0)