From 73d7dcf84dac5512c50448ff6adf084f1a9bd6f9 Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Tue, 16 Apr 2019 18:35:04 +0200 Subject: [PATCH] dmaengine: imx-sdma: restart stopped cyclic transfers For cyclic DMA transfers, we have at least two cases where we can run out descriptors available to the engine: - Interrups are disabled for too long and all buffers a filled with data. - DMA errors (such as generated by baud rate mismatch with imx-uart) use up all descriptors before we can react. In this case, SDMA stops the channel and no further transfers are done until the respective channel is disabled and re-enabled. The best we can do in this case is to check if the transfer should still be enabled (it could have been disabled during sdma_update_channel_loop), but the SDMA channel is stopped. In this case, we re-start the channel. To avoid racing with changes to the sdmac->status field (which is written and restored in sdma_update_channel_loop), we add a new flag (IMX_DMA_ACTIVE) to indicate that the channel is currently active. Signed-off-by: Jan Luebbe --- drivers/dma/imx-sdma.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 58fa8520892b..8774259af24c 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -383,6 +383,7 @@ struct sdma_channel { }; #define IMX_DMA_SG_LOOP BIT(0) +#define IMX_DMA_ACTIVE BIT(1) #define MAX_DMA_CHANNELS 32 #define MXC_SDMA_DEFAULT_PRIORITY 1 @@ -658,6 +659,9 @@ static int sdma_config_ownership(struct sdma_channel *sdmac, static void sdma_enable_channel(struct sdma_engine *sdma, int channel) { + struct sdma_channel *sdmac = &sdma->channel[channel]; + + sdmac->flags |= IMX_DMA_ACTIVE; writel(BIT(channel), sdma->regs + SDMA_H_START); } @@ -774,6 +778,7 @@ static void sdma_start_desc(struct sdma_channel *sdmac) static void sdma_update_channel_loop(struct sdma_channel *sdmac) { + struct sdma_engine *sdma = sdmac->sdma; struct sdma_buffer_descriptor *bd; int error = 0; enum dma_status old_status = sdmac->status; @@ -820,6 +825,13 @@ static void sdma_update_channel_loop(struct sdma_channel *sdmac) if (error) sdmac->status = old_status; } + + if ((sdmac->flags & IMX_DMA_ACTIVE) && + !(readl_relaxed(sdma->regs + SDMA_H_STATSTOP) & BIT(sdmac->channel))) { + dev_err_ratelimited(sdma->dev, "SDMA channel %d: cyclic transfer disabled by HW, reenabling\n", + sdmac->channel); + writel(BIT(sdmac->channel), sdma->regs + SDMA_H_START); + }; } static void mxc_sdma_handle_channel_normal(struct sdma_channel *data) @@ -1049,6 +1061,7 @@ static int sdma_disable_channel(struct dma_chan *chan) struct sdma_engine *sdma = sdmac->sdma; int channel = sdmac->channel; + sdmac->flags &= ~IMX_DMA_ACTIVE; writel_relaxed(BIT(channel), sdma->regs + SDMA_H_STATSTOP); sdmac->status = DMA_ERROR; -- 2.23.0