[PATCH v4 7/9] dma: dw: Avoid partial transfers

Miquel Raynal
2022-03-10T15:58:31Z

As investigated by Phil Edworthy <phil.edworthy@renesas.com> on RZN1 a
while ago, pausing a partial transfer only causes data to be written to
memory that is a multiple of the memory width setting. Such a situation
can happen eg. because of a char timeout interrupt on a UART. In this
case, the current ->terminate_all() implementation does not always flush
the remaining data as it should.

In order to workaround this, a solutions is to resume and then pause
again the transfer before termination. The resume call in practice
actually flushes the remaining data.

Reported-by: Phil Edworthy <phil.edworthy@renesas.com>
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/dma/dw/core.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
index 7ab83fe601ed..2f6183177ba5 100644
--- a/drivers/dma/dw/core.c
+++ b/drivers/dma/dw/core.c
@@ -862,6 +862,10 @@ static int dwc_terminate_all(struct dma_chan *chan)

clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags);

+ /* Ensure the last byte(s) are drained before disabling the channel */
+ if (test_bit(DW_DMA_IS_PAUSED, &dwc->flags))
+ dwc_chan_resume(dwc, true);
+
dwc_chan_pause(dwc, true);

dwc_chan_disable(dw, dwc);
--
2.27.0