From 5ce2b52f305f2797c6382cf9346fc004302d823a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Mon, 11 Aug 2025 16:56:36 +0100 Subject: [PATCH 1/3] dma: dma-axi-dmac: fix SW cyclic transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If 'hw_cyclic' is false we should still be able to do cyclic transfers in "software". That was not working for the case where 'desc->num_sgs' is 1 because 'chan->next_desc' is never set with the current desc which means that the cyclic transfer only runs once and in the next SOT interrupt we do nothing since vchan_next_desc() will return NULL. Fix it by setting 'chan->next_desc' as soon as we get a new desc via vchan_next_desc(). Fixes: 0e3b67b348b8 ("dmaengine: Add support for the Analog Devices AXI-DMAC DMA controller") Signed-off-by: Nuno Sá --- drivers/dma/dma-axi-dmac.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 78ce6504fffd25..83ae1eb9b81579 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -250,6 +250,7 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) return; list_move_tail(&vdesc->node, &chan->active_descs); desc = to_axi_dmac_desc(vdesc); + chan->next_desc = desc; } sg = &desc->sg[desc->num_submitted]; @@ -268,8 +269,6 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) else chan->next_desc = NULL; flags |= AXI_DMAC_FLAG_LAST; - } else { - chan->next_desc = desc; } sg->hw->id = axi_dmac_read(dmac, AXI_DMAC_REG_TRANSFER_ID); From abf3c4ec3d993bb5fc1ddaa77badb9a48cd0b755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Mon, 11 Aug 2025 16:56:37 +0100 Subject: [PATCH 2/3] dma: dma-axi-dmac: fix HW scatter-gather not looking at the queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For HW scatter gather transfers we still need to look for the queue. The HW is capable of queueing 3 concurrent transfers and if we try more than that we'll get the submit queue full and should return. Otherwise, if we go ahead and program the new transfer, we end up discarding it. Fixes: e97dc7435972 ("dmaengine: axi-dmac: Add support for scatter-gather transfers") Signed-off-by: Nuno Sá --- drivers/dma/dma-axi-dmac.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 83ae1eb9b81579..bbc02b10c1a1da 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -236,11 +236,9 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) unsigned int flags = 0; unsigned int val; - if (!chan->hw_sg) { - val = axi_dmac_read(dmac, AXI_DMAC_REG_START_TRANSFER); - if (val) /* Queue is full, wait for the next SOT IRQ */ - return; - } + val = axi_dmac_read(dmac, AXI_DMAC_REG_START_TRANSFER); + if (val) /* Queue is full, wait for the next SOT IRQ */ + return; desc = chan->next_desc; From cdd870f5e9b88c2425a243aa43c85616f04e2d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Mon, 11 Aug 2025 16:56:38 +0100 Subject: [PATCH 3/3] dma: dma-axi-dmac: support bigger than 32bits addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some supported platforms as ARCH_ZYNQMP, part of the memory is mapped above 32bit addresses and since the DMA mask, by default, is set to 32bits, we would need to rely on swiotlb (which incurs a performance penalty) for the DMA mappings. Thus, we can write either the SRC or DEST high addresses with 1's and read them back. The last bit set on the return value will reflect the IP address bus width and so we can update the device DMA mask accordingly. While at it, support bigger that 32 bits transfers in IP without HW scatter gather support. Signed-off-by: Nuno Sá --- drivers/dma/dma-axi-dmac.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index bbc02b10c1a1da..0eb8029797ddcf 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -69,7 +69,9 @@ #define AXI_DMAC_REG_START_TRANSFER 0x408 #define AXI_DMAC_REG_FLAGS 0x40c #define AXI_DMAC_REG_DEST_ADDRESS 0x410 +#define AXI_DMAC_REG_DEST_ADDRESS_HIGH 0x490 #define AXI_DMAC_REG_SRC_ADDRESS 0x414 +#define AXI_DMAC_REG_SRC_ADDRESS_HIGH 0x494 #define AXI_DMAC_REG_X_LENGTH 0x418 #define AXI_DMAC_REG_Y_LENGTH 0x41c #define AXI_DMAC_REG_DEST_STRIDE 0x420 @@ -274,11 +276,14 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) if (!chan->hw_sg) { if (axi_dmac_dest_is_mem(chan)) { axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS, sg->hw->dest_addr); + axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS_HIGH, + sg->hw->dest_addr >> 32); axi_dmac_write(dmac, AXI_DMAC_REG_DEST_STRIDE, sg->hw->dst_stride); } if (axi_dmac_src_is_mem(chan)) { axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS, sg->hw->src_addr); + axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS_HIGH, sg->hw->src_addr >> 32); axi_dmac_write(dmac, AXI_DMAC_REG_SRC_STRIDE, sg->hw->src_stride); } } @@ -1016,6 +1021,9 @@ static int axi_dmac_read_chan_config(struct device *dev, struct axi_dmac *dmac) static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version) { struct axi_dmac_chan *chan = &dmac->chan; + struct device *dev = dmac->dma_dev.dev; + u32 mask; + int ret; axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, AXI_DMAC_FLAG_CYCLIC); if (axi_dmac_read(dmac, AXI_DMAC_REG_FLAGS) == AXI_DMAC_FLAG_CYCLIC) @@ -1050,6 +1058,22 @@ static int axi_dmac_detect_caps(struct axi_dmac *dmac, unsigned int version) return -ENODEV; } + if (axi_dmac_dest_is_mem(chan)) { + axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS_HIGH, 0xffffffff); + mask = axi_dmac_read(dmac, AXI_DMAC_REG_DEST_ADDRESS_HIGH); + } else { + axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS_HIGH, 0xffffffff); + mask = axi_dmac_read(dmac, AXI_DMAC_REG_SRC_ADDRESS_HIGH); + } + + mask = 32 + fls(mask); + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(mask)); + if (ret) { + dev_err(dev, "DMA mask set error %d\n", ret); + return ret; + } + if (version >= ADI_AXI_PCORE_VER(4, 2, 'a')) chan->hw_partial_xfer = true;