[PATCH 6.18 296/309] dmaengine: xilinx: xilinx_dma: Fix residue calculation for cyclic DMA

Greg Kroah-Hartman
2026-03-31T17:09:25Z

From: Marek Vasut <marex@nabladev.com>

[ Upstream commit f61d145999d61948a23cd436ebbfa4c3b9ab8987 ]

The cyclic DMA calculation is currently entirely broken and reports
residue only for the first segment. The problem is twofold.

First, when the first descriptor finishes, it is moved from active_list
to done_list, but it is never returned back into the active_list. The
xilinx_dma_tx_status() expects the descriptor to be in the active_list
to report any meaningful residue information, which never happens after
the first descriptor finishes. Fix this up in xilinx_dma_start_transfer()
and if the descriptor is cyclic, lift it from done_list and place it back
into active_list list.

Second, the segment .status fields of the descriptor remain dirty. Once
the DMA did one pass on the descriptor, the .status fields are populated
with data by the DMA, but the .status fields are not cleared before reuse
during the next cyclic DMA round. The xilinx_dma_get_residue() recognizes
that as if the descriptor was complete and had 0 residue, which is bogus.
Reinitialize the status field before placing the descriptor back into the
active_list.

Fixes: c0bba3a99f07 ("dmaengine: vdma: Add Support for Xilinx AXI Direct Memory Access Engine")
Signed-off-by: Marek Vasut <marex@nabladev.com>
Link: https://patch.msgid.link/20260316221943.160375-1-marex@nabladev.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/dma/xilinx/xilinx_dma.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index e6d10079ec670..ccfcc2b801f82 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -1546,8 +1546,29 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)
if (chan->err)
return;

- if (list_empty(&chan->pending_list))
+ if (list_empty(&chan->pending_list)) {
+ if (chan->cyclic) {
+ struct xilinx_dma_tx_descriptor *desc;
+ struct list_head *entry;
+
+ desc = list_last_entry(&chan->done_list,
+ struct xilinx_dma_tx_descriptor, node);
+ list_for_each(entry, &desc->segments) {
+ struct xilinx_axidma_tx_segment *axidma_seg;
+ struct xilinx_axidma_desc_hw *axidma_hw;
+ axidma_seg = list_entry(entry,
+ struct xilinx_axidma_tx_segment,
+ node);
+ axidma_hw = &axidma_seg->hw;
+ axidma_hw->status = 0;
+ }
+
+ list_splice_tail_init(&chan->done_list, &chan->active_list);
+ chan->desc_pendingcount = 0;
+ chan->idle = false;
+ }
return;
+ }

if (!chan->idle)
return;
--
2.53.0