]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
dmaengine: stm32-dma3: restore channel semaphore status after suspend
authorAmelie Delaunay <amelie.delaunay@foss.st.com>
Fri, 21 Nov 2025 13:36:58 +0000 (14:36 +0100)
committerVinod Koul <vkoul@kernel.org>
Tue, 16 Dec 2025 16:06:31 +0000 (21:36 +0530)
Depending on the power state reached during suspend, the CxSEMCR register
could have been reset, and the semaphore released.
On resume, try to take the semaphore again. If the semaphore cannot be
taken, an error log displaying the channel number and channel user is
generated.

This requires introducing two new functions:
stm32_dma3_pm_suspend(), where the status of each channel is checked
because suspension is not allowed if a channel is still running;
stm32_dma3_pm_resume(), where the channel semaphore is restored if it was
taken before suspend.

Signed-off-by: Amelie Delaunay <amelie.delaunay@foss.st.com>
Link: https://patch.msgid.link/20251121-dma3_improv-v2-3-76a207b13ea6@foss.st.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/stm32/stm32-dma3.c

index a1583face7ec3f952bb9bbff2705c86947811ea8..29ea510fa539748211f0a04ba1a70cca5032724e 100644 (file)
@@ -1237,6 +1237,10 @@ static struct dma_async_tx_descriptor *stm32_dma3_prep_dma_memcpy(struct dma_cha
        bool prevent_refactor = !!FIELD_GET(STM32_DMA3_DT_NOPACK, chan->dt_config.tr_conf) ||
                                !!FIELD_GET(STM32_DMA3_DT_NOREFACT, chan->dt_config.tr_conf);
 
+       /* Semaphore could be lost during suspend/resume */
+       if (chan->semaphore_mode && !chan->semaphore_taken)
+               return NULL;
+
        count = stm32_dma3_get_ll_count(chan, len, prevent_refactor);
 
        swdesc = stm32_dma3_chan_desc_alloc(chan, count);
@@ -1297,6 +1301,10 @@ static struct dma_async_tx_descriptor *stm32_dma3_prep_slave_sg(struct dma_chan
                                !!FIELD_GET(STM32_DMA3_DT_NOREFACT, chan->dt_config.tr_conf);
        int ret;
 
+       /* Semaphore could be lost during suspend/resume */
+       if (chan->semaphore_mode && !chan->semaphore_taken)
+               return NULL;
+
        count = 0;
        for_each_sg(sgl, sg, sg_len, i)
                count += stm32_dma3_get_ll_count(chan, sg_dma_len(sg), prevent_refactor);
@@ -1383,6 +1391,10 @@ static struct dma_async_tx_descriptor *stm32_dma3_prep_dma_cyclic(struct dma_cha
        u32 count, i, ctr1, ctr2;
        int ret;
 
+       /* Semaphore could be lost during suspend/resume */
+       if (chan->semaphore_mode && !chan->semaphore_taken)
+               return NULL;
+
        if (!buf_len || !period_len || period_len > STM32_DMA3_MAX_BLOCK_SIZE) {
                dev_err(chan2dev(chan), "Invalid buffer/period length\n");
                return NULL;
@@ -1932,8 +1944,69 @@ static int stm32_dma3_runtime_resume(struct device *dev)
        return ret;
 }
 
+static int stm32_dma3_pm_suspend(struct device *dev)
+{
+       struct stm32_dma3_ddata *ddata = dev_get_drvdata(dev);
+       struct dma_device *dma_dev = &ddata->dma_dev;
+       struct dma_chan *c;
+       int ccr, ret;
+
+       ret = pm_runtime_resume_and_get(dev);
+       if (ret < 0)
+               return ret;
+
+       list_for_each_entry(c, &dma_dev->channels, device_node) {
+               struct stm32_dma3_chan *chan = to_stm32_dma3_chan(c);
+
+               ccr = readl_relaxed(ddata->base + STM32_DMA3_CCR(chan->id));
+               if (ccr & CCR_EN) {
+                       dev_warn(dev, "Suspend is prevented: %s still in use by %s\n",
+                                dma_chan_name(c), dev_name(c->slave));
+                       pm_runtime_put_sync(dev);
+                       return -EBUSY;
+               }
+       }
+
+       pm_runtime_put_sync(dev);
+
+       pm_runtime_force_suspend(dev);
+
+       return 0;
+}
+
+static int stm32_dma3_pm_resume(struct device *dev)
+{
+       struct stm32_dma3_ddata *ddata = dev_get_drvdata(dev);
+       struct dma_device *dma_dev = &ddata->dma_dev;
+       struct dma_chan *c;
+       int ret;
+
+       ret = pm_runtime_force_resume(dev);
+       if (ret < 0)
+               return ret;
+
+       ret = pm_runtime_resume_and_get(dev);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Channel semaphores need to be restored in case of registers reset during low power.
+        * stm32_dma3_get_chan_sem() will prior check the semaphore status.
+        */
+       list_for_each_entry(c, &dma_dev->channels, device_node) {
+               struct stm32_dma3_chan *chan = to_stm32_dma3_chan(c);
+
+               if (chan->semaphore_mode && chan->semaphore_taken)
+                       stm32_dma3_get_chan_sem(chan);
+       }
+
+       pm_runtime_put_sync(dev);
+
+       return 0;
+}
+
 static const struct dev_pm_ops stm32_dma3_pm_ops = {
-       SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+       SYSTEM_SLEEP_PM_OPS(stm32_dma3_pm_suspend, stm32_dma3_pm_resume)
        RUNTIME_PM_OPS(stm32_dma3_runtime_suspend, stm32_dma3_runtime_resume, NULL)
 };