[PATCH v2 2/3] dmaengine: dw-axi-dmac: retain descriptors when termination times out

From: James Hilliard

Date: Mon Nov 09 2026 - 22:23:51 EST


dma_chan_terminate_all() warns when the channel enable bit does not clear,
but then frees the descriptors and returns success. Its synchronize
callback only drains virtual-channel callbacks, so terminate_sync() can
also report success while hardware still accesses the transfer.

Return the polling error before detaching descriptors or changing the
peripheral routing. This keeps the transfer resources available while
the channel remains enabled and lets the caller retry termination without
treating the timeout as a successful stop.

Hold the channel lock across disabling, polling and descriptor detachment
so an interrupt handler cannot restart the channel between the stop check
and cleanup.

Fixes: 1deb96c0fa58 ("dmaegine: dw-axi-dmac: Support device_prep_dma_cyclic()")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: James Hilliard <james.hilliard1@xxxxxxxxx>
---
drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
index 457ab9c47ceb..281d7c79246e 100644
--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
@@ -1187,21 +1187,24 @@ static int dma_chan_terminate_all(struct dma_chan *dchan)
int ret;
LIST_HEAD(head);

+ spin_lock_irqsave(&chan->vc.lock, flags);
+
axi_chan_disable(chan);

ret = readx_poll_timeout_atomic(axi_chan_is_hw_enable, chan, enabled, !enabled,
1 * USEC_PER_MSEC, 50 * USEC_PER_MSEC);
- if (ret == -ETIMEDOUT)
+ if (ret) {
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
dev_warn(dmaengine_chan_dev(dchan),
"%s failed to stop\n", axi_chan_name(chan));
+ return ret;
+ }

if (chan->direction != DMA_MEM_TO_MEM)
dw_axi_dma_set_hw_channel(chan, false);
if (chan->direction == DMA_MEM_TO_DEV)
dw_axi_dma_set_byte_halfword(chan, false);

- spin_lock_irqsave(&chan->vc.lock, flags);
-
vchan_get_all_descriptors(&chan->vc, &head);

chan->cyclic = false;

--
2.53.0