summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAmelie Delaunay <amelie.delaunay@foss.st.com>2025-11-21 14:36:58 +0100
committerVinod Koul <vkoul@kernel.org>2025-12-16 21:36:31 +0530
commitdea737e31c2c62df5a45871bfb4ceb90a112dbd8 (patch)
tree59cd7b681a398d4bc008013653e6675e39a986e8 /drivers
parentd26eb4a75a4a2bbf27305e62ad82cedf5f8c577c (diff)
dmaengine: stm32-dma3: restore channel semaphore status after suspend
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/dma/stm32/stm32-dma3.c75
1 files changed, 74 insertions, 1 deletions
diff --git a/drivers/dma/stm32/stm32-dma3.c b/drivers/dma/stm32/stm32-dma3.c
index a1583face7ec..29ea510fa539 100644
--- a/drivers/dma/stm32/stm32-dma3.c
+++ b/drivers/dma/stm32/stm32-dma3.c
@@ -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)
};