From 1066e8ae45193732f235df740ac31f3e60fc1fe6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 10 Nov 2017 13:07:55 +0900 Subject: [PATCH 1/3] dmaengine: rcar-dmac: ensure CHCR DE bit is actually 0 after clear DMAC reads data from source device, and buffered it until transferable size for shink deivce. Because of this behavoir, DMAC is including buffered data . Now, CHCR DE bit is controlling DMA transfer enable/disable. If DE bit was cleared during data transfering, or during buffering, it will flush buffered data if source device was peripheral device (The buffered data will be removed if source device was memory). Because of this behavior, driver should ensure that DE bit is actually 0 after cleared. This patch adds new rcar_dmac_chcr_de_barrier() and call it after CHCR register access. Signed-off-by: Kuninori Morimoto --- drivers/dma/sh/rcar-dmac.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index 2b2c7db..16ebd5d 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -741,6 +742,24 @@ static int rcar_dmac_fill_hwdesc(struct rcar_dmac_chan *chan, /* ----------------------------------------------------------------------------- * Stop and reset */ +static void rcar_dmac_chcr_de_barrier(struct rcar_dmac_chan *chan) +{ + u32 chcr; + int i; + + /* + * Ensure that the setting of the DE bit is actually 0 after + * clearing it. + */ + for (i = 0; i < 1024; i++) { + chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR); + if (!(chcr & RCAR_DMACHCR_DE)) + return; + udelay(1); + } + + dev_err(chan->chan.device->dev, "CHCR DE check error\n"); +} static void rcar_dmac_chan_halt(struct rcar_dmac_chan *chan) { @@ -749,6 +768,7 @@ static void rcar_dmac_chan_halt(struct rcar_dmac_chan *chan) chcr &= ~(RCAR_DMACHCR_DSE | RCAR_DMACHCR_DSIE | RCAR_DMACHCR_IE | RCAR_DMACHCR_TE | RCAR_DMACHCR_DE); rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr); + rcar_dmac_chcr_de_barrier(chan); } static void rcar_dmac_chan_reinit(struct rcar_dmac_chan *chan) @@ -1481,6 +1501,8 @@ static irqreturn_t rcar_dmac_isr_channel(int irq, void *dev) if (chcr & RCAR_DMACHCR_TE) mask |= RCAR_DMACHCR_DE; rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr & ~mask); + if (mask & RCAR_DMACHCR_DE) + rcar_dmac_chcr_de_barrier(chan); if (chcr & RCAR_DMACHCR_DSE) ret |= rcar_dmac_isr_desc_stage_end(chan); -- 1.9.1