Commit a8d46a7f authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Vinod Koul

dmaengine: rcar-dmac: ensure CHCR DE bit is actually 0 after clearing

DMAC reads data from source device, and buffered it until transferable
size for sink device. Because of this behavior, DMAC is including
buffered data .

Now, CHCR DE bit is controlling DMA transfer enable/disable.

If DE bit was cleared during data transferring, 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 clearing.

This patch adds new rcar_dmac_chcr_de_barrier() and call it after CHCR
register access.
Signed-off-by: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Tested-by: default avatarHiroyuki Yokoyama <hiroyuki.yokoyama.vx@renesas.com>
Tested-by: default avatarRyo Kodama <ryo.kodama.vz@renesas.com>
Tested-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: default avatarVinod Koul <vinod.koul@intel.com>
parent 4fbd8d19
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/delay.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/dmaengine.h> #include <linux/dmaengine.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -741,6 +742,24 @@ static int rcar_dmac_fill_hwdesc(struct rcar_dmac_chan *chan, ...@@ -741,6 +742,24 @@ static int rcar_dmac_fill_hwdesc(struct rcar_dmac_chan *chan,
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* Stop and reset * Stop and reset
*/ */
static void rcar_dmac_chcr_de_barrier(struct rcar_dmac_chan *chan)
{
u32 chcr;
unsigned 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) 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) ...@@ -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 | chcr &= ~(RCAR_DMACHCR_DSE | RCAR_DMACHCR_DSIE | RCAR_DMACHCR_IE |
RCAR_DMACHCR_TE | RCAR_DMACHCR_DE); RCAR_DMACHCR_TE | RCAR_DMACHCR_DE);
rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr); 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) 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) ...@@ -1481,6 +1501,8 @@ static irqreturn_t rcar_dmac_isr_channel(int irq, void *dev)
if (chcr & RCAR_DMACHCR_TE) if (chcr & RCAR_DMACHCR_TE)
mask |= RCAR_DMACHCR_DE; mask |= RCAR_DMACHCR_DE;
rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr & ~mask); 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) if (chcr & RCAR_DMACHCR_DSE)
ret |= rcar_dmac_isr_desc_stage_end(chan); ret |= rcar_dmac_isr_desc_stage_end(chan);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment