Commit 409ffc4d authored by Amelie Delaunay's avatar Amelie Delaunay Committed by Vinod Koul
Browse files

dmaengine: stm32-dma: fix sleeping function called from invalid context



This patch fixes BUG: sleeping function called from invalid context in
stm32_dma_disable_chan function.

The goal of this function is to force channel disable if it has not been
disabled by hardware. This consists in clearing STM32_DMA_SCR_EN bit and
read it as 0 to ensure the channel is well disabled and the last transfer
is over.

In previous implementation, the waiting loop was based on a do...while (1)
with a call to cond_resched to give the scheduler a chance to run a higher
priority process.

But in some conditions, stm32_dma_disable_chan can be called while
preemption is disabled, on a stm32_dma_stop call for example. So
cond_resched must not be used.

To avoid this, use readl_relaxed_poll_timeout_atomic to poll
STM32_DMA_SCR_EN bit cleared.

Signed-off-by: default avatarAmelie Delaunay <amelie.delaunay@st.com>
Link: https://lore.kernel.org/r/20200129153628.29329-8-amelie.delaunay@st.com


Signed-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent 32ce1088
Loading
Loading
Loading
Loading
+9 −18
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/iopoll.h>
#include <linux/jiffies.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -421,29 +422,19 @@ static void stm32_dma_irq_clear(struct stm32_dma_chan *chan, u32 flags)
static int stm32_dma_disable_chan(struct stm32_dma_chan *chan)
{
	struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
	unsigned long timeout = jiffies + msecs_to_jiffies(5000);
	u32 dma_scr, id;
	u32 dma_scr, id, reg;

	id = chan->id;
	dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(id));
	reg = STM32_DMA_SCR(id);
	dma_scr = stm32_dma_read(dmadev, reg);

	if (dma_scr & STM32_DMA_SCR_EN) {
		dma_scr &= ~STM32_DMA_SCR_EN;
		stm32_dma_write(dmadev, STM32_DMA_SCR(id), dma_scr);

		do {
			dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(id));
			dma_scr &= STM32_DMA_SCR_EN;
			if (!dma_scr)
				break;
		stm32_dma_write(dmadev, reg, dma_scr);

			if (time_after_eq(jiffies, timeout)) {
				dev_err(chan2dev(chan), "%s: timeout!\n",
					__func__);
				return -EBUSY;
			}
			cond_resched();
		} while (1);
		return readl_relaxed_poll_timeout_atomic(dmadev->base + reg,
					dma_scr, !(dma_scr & STM32_DMA_SCR_EN),
					10, 1000000);
	}

	return 0;