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@vger.kernel.org Signed-off-by: James Hilliard --- 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