|
|
@@ -1281,6 +1281,9 @@ static unsigned int rcar_dmac_chan_get_residue(struct rcar_dmac_chan *chan,
|
|
|
enum dma_status status;
|
|
|
unsigned int residue = 0;
|
|
|
unsigned int dptr = 0;
|
|
|
+ unsigned int chcrb;
|
|
|
+ unsigned int tcrb;
|
|
|
+ unsigned int i;
|
|
|
|
|
|
if (!desc)
|
|
|
return 0;
|
|
|
@@ -1328,6 +1331,24 @@ static unsigned int rcar_dmac_chan_get_residue(struct rcar_dmac_chan *chan,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * We need to read two registers.
|
|
|
+ * Make sure the control register does not skip to next chunk
|
|
|
+ * while reading the counter.
|
|
|
+ * Trying it 3 times should be enough: Initial read, retry, retry
|
|
|
+ * for the paranoid.
|
|
|
+ */
|
|
|
+ for (i = 0; i < 3; i++) {
|
|
|
+ chcrb = rcar_dmac_chan_read(chan, RCAR_DMACHCRB) &
|
|
|
+ RCAR_DMACHCRB_DPTR_MASK;
|
|
|
+ tcrb = rcar_dmac_chan_read(chan, RCAR_DMATCRB);
|
|
|
+ /* Still the same? */
|
|
|
+ if (chcrb == (rcar_dmac_chan_read(chan, RCAR_DMACHCRB) &
|
|
|
+ RCAR_DMACHCRB_DPTR_MASK))
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ WARN_ONCE(i >= 3, "residue might be not continuous!");
|
|
|
+
|
|
|
/*
|
|
|
* In descriptor mode the descriptor running pointer is not maintained
|
|
|
* by the interrupt handler, find the running descriptor from the
|
|
|
@@ -1335,8 +1356,7 @@ static unsigned int rcar_dmac_chan_get_residue(struct rcar_dmac_chan *chan,
|
|
|
* mode just use the running descriptor pointer.
|
|
|
*/
|
|
|
if (desc->hwdescs.use) {
|
|
|
- dptr = (rcar_dmac_chan_read(chan, RCAR_DMACHCRB) &
|
|
|
- RCAR_DMACHCRB_DPTR_MASK) >> RCAR_DMACHCRB_DPTR_SHIFT;
|
|
|
+ dptr = chcrb >> RCAR_DMACHCRB_DPTR_SHIFT;
|
|
|
if (dptr == 0)
|
|
|
dptr = desc->nchunks;
|
|
|
dptr--;
|
|
|
@@ -1354,7 +1374,7 @@ static unsigned int rcar_dmac_chan_get_residue(struct rcar_dmac_chan *chan,
|
|
|
}
|
|
|
|
|
|
/* Add the residue for the current chunk. */
|
|
|
- residue += rcar_dmac_chan_read(chan, RCAR_DMATCRB) << desc->xfer_shift;
|
|
|
+ residue += tcrb << desc->xfer_shift;
|
|
|
|
|
|
return residue;
|
|
|
}
|