From c154060016a9db2ac889bfdb0a3c1322f9be8ded Mon Sep 17 00:00:00 2001 From: Koichiro Den Date: Sat, 18 Jul 2026 03:06:34 +0900 Subject: [PATCH] dmaengine: dw-edma: Complete descriptors before pausing If PAUSE is requested while the final burst of a descriptor is in flight, the DONE interrupt takes the PAUSE path without checking whether the descriptor has been depleted. The depleted descriptor remains on the issued list and the channel enters EDMA_ST_PAUSE. On resume, dw_edma_start_transfer() can select that depleted descriptor again even though no burst remains, leaving the channel in an invalid busy state. Check for descriptor completion before acknowledging PAUSE. If there is no work to start on resume, leave the channel idle. Also ignore DONE interrupts while the channel is paused so a stale or repeated interrupt cannot change its state or start queued work. Fixes: e63d79d1ffcd ("dmaengine: Add Synopsys eDMA IP core driver") Cc: stable@vger.kernel.org Reviewed-by: Frank Li Signed-off-by: Koichiro Den Link: https://patch.msgid.link/20260717180639.2643243-5-den@valinux.co.jp Signed-off-by: Vinod Koul --- drivers/dma/dw-edma/dw-edma-core.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c index c0d9f7e373f3..7a567f924538 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -255,7 +255,8 @@ static int dw_edma_device_resume(struct dma_chan *dchan) err = -EPERM; } else { chan->status = EDMA_ST_BUSY; - dw_edma_start_transfer(chan); + if (!dw_edma_start_transfer(chan)) + chan->status = EDMA_ST_IDLE; } return err; @@ -601,10 +602,16 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) unsigned long flags; spin_lock_irqsave(&chan->vc.lock, flags); + if (chan->status == EDMA_ST_PAUSE) { + spin_unlock_irqrestore(&chan->vc.lock, flags); + return; + } + vd = vchan_next_desc(&chan->vc); if (vd) { switch (chan->request) { case EDMA_REQ_NONE: + case EDMA_REQ_PAUSE: desc = vd2dw_edma_desc(vd); if (desc->start_burst >= desc->nburst) { dw_hdma_set_callback_result(vd, @@ -613,6 +620,12 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) vchan_cookie_complete(vd); } + if (chan->request == EDMA_REQ_PAUSE) { + chan->request = EDMA_REQ_NONE; + chan->status = EDMA_ST_PAUSE; + break; + } + /* Continue transferring if there are remaining chunks or issued requests. */ chan->status = dw_edma_start_transfer(chan) ? EDMA_ST_BUSY : EDMA_ST_IDLE; @@ -624,11 +637,6 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) chan->status = EDMA_ST_IDLE; break; - case EDMA_REQ_PAUSE: - chan->request = EDMA_REQ_NONE; - chan->status = EDMA_ST_PAUSE; - break; - default: break; }