[PATCH 7/8] dmaengine: stm32-dma: fix sleeping function called from invalid context

From: Amelie Delaunay
Date: Wed Jan 29 2020 - 10:37:22 EST


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: Amelie Delaunay <amelie.delaunay@xxxxxx>
---
drivers/dma/stm32-dma.c | 27 +++++++++------------------
1 file changed, 9 insertions(+), 18 deletions(-)

diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
index 01a2374ae03a..b585e11c2168 100644
--- a/drivers/dma/stm32-dma.c
+++ b/drivers/dma/stm32-dma.c
@@ -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;
-
- if (time_after_eq(jiffies, timeout)) {
- dev_err(chan2dev(chan), "%s: timeout!\n",
- __func__);
- return -EBUSY;
- }
- cond_resched();
- } while (1);
+ stm32_dma_write(dmadev, reg, dma_scr);
+
+ return readl_relaxed_poll_timeout_atomic(dmadev->base + reg,
+ dma_scr, !(dma_scr & STM32_DMA_SCR_EN),
+ 10, 1000000);
}

return 0;
--
2.17.1