* [PATCH v5 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller
@ 2026-08-26 2:40 Yuanshen Cao
2026-08-26 2:40 ` [PATCH v5 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling Yuanshen Cao
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Yuanshen Cao @ 2026-08-26 2:40 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard
Cc: dmaengine, linux-arm-kernel, linux-sunxi, linux-kernel,
devicetree, Yuanshen Cao, Frank Li, Krzysztof Kozlowski
Hi everyone,
This patch series introduces support for the Allwinner A733 DMA
controller in the `sun6i-dma` driver.
The A733 DMA controller differs from previous generations in several key
ways:
- Support for higher addresses (up to 32G).
- A different interrupt register layout and mapping.
- A different number of channels per interrupt register.
To support these differences without introducing complex conditional
logic throughout the driver, this series refactors the
`sun6i_dma_config` structure by adding hardware-specific parameters such
as interrupt register offsets, address masks, and channel counts per
register. This allows the driver to support the A733 and future hardware
revisions. This approach also achieves the same functionality as the DMA
drivers in Radxa BSP Package[1].
The series is organized as follows:
1. Refactor the configuration structure for interrupt and register operations.
2. Add support for variable address widths using masks.
3. Add support for variable channels per interrupt register.
4. Update the device tree bindings documentation.
5. Implement the A733-specific configuration and register mappings.
Tested on Radxa Cubie A7Z.
[1] https://github.com/radxa/allwinner-bsp/blob/cubie-aiot-v1.4.8/drivers/dma/sunxi-dma.c
Thanks!
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
Changes in v5:
- Use field_prep in bitfield.h instead of the custom version.
- Add the missing function pointers for sun60i_a733_dma_cfg.
- Link to v4: https://patch.msgid.link/20260826-sun60i-a733-dma-v4-0-dc2f745f40dc@gmail.com
Changes in v4:
- Refactor configuration structure to use irq stride and offsets instead
of function pointers.
- Use GENMASK and field_prep in set_addr for different high addresses.
- Link to v3: https://patch.msgid.link/20260622-sun60i-a733-dma-v3-0-f697ef296cbc@gmail.com
Changes in v3:
- Reword patches for clarity.
- Link to v2: https://patch.msgid.link/20260621-sun60i-a733-dma-v2-0-340f205891cc@gmail.com
Changes in v2:
- Implement SUN6I_DMA_IRQ_A31_COMMON_OPS macro to avoid duplicate.
- Move set_addr into helper function and revert back sun6i_dma_set_addr.
- Rename chan_num to irq_req to avoid misleading name as suggested by
sashiko.
- Reorder and reword the dtbinding patch for more clarity.
- Link to v1: https://patch.msgid.link/20260619-sun60i-a733-dma-v1-0-da4b649fc72a@gmail.com
---
Yuanshen Cao (5):
dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling
dmaengine: sun6i-dma: Support variable address widths using masks
dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping
dt-bindings: dmaengine: sun50i-a64-dma: Add allwinner,sun60i-a733-dma compatible string
dmaengine: sun6i-dma: Implement support for Allwinner A733 DMA controller
.../bindings/dma/allwinner,sun50i-a64-dma.yaml | 2 +
drivers/dma/sun6i-dma.c | 153 ++++++++++++++++-----
2 files changed, 122 insertions(+), 33 deletions(-)
---
base-commit: 8d3ae59288f1e7d58d76558a6ee96d533bc5019f
change-id: 20260619-sun60i-a733-dma-c2455149165d
Best regards,
--
Yuanshen Cao <alex.caoys@gmail.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v5 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling
2026-08-26 2:40 [PATCH v5 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller Yuanshen Cao
@ 2026-08-26 2:40 ` Yuanshen Cao
2026-08-26 2:40 ` [PATCH v5 2/5] dmaengine: sun6i-dma: Support variable address widths using masks Yuanshen Cao
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Yuanshen Cao @ 2026-08-26 2:40 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard
Cc: dmaengine, linux-arm-kernel, linux-sunxi, linux-kernel,
devicetree, Yuanshen Cao
The `sun6i-dma` driver currently uses hardcoded logic for interrupt
handling (reading/writing interrupt enable and status registers) and
register dumping.
To support the Allwinner A733, which has a different register layout and
interrupt handling logic, add `irq_stride`, `irq_en_offset`, and
`irq_stat_offset` to `struct sun6i_dma_config`. Implement generic
`sun6i_read/write_irq_*` functions using these new configuration fields
to accommodate different hardware revisions.
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
drivers/dma/sun6i-dma.c | 87 +++++++++++++++++++++++++++++++++++++------------
1 file changed, 67 insertions(+), 20 deletions(-)
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index f47a326dd7ff..f305fbfb3545 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -27,7 +27,6 @@
/*
* Common registers
*/
-#define DMA_IRQ_EN(x) ((x) * 0x04)
#define DMA_IRQ_HALF BIT(0)
#define DMA_IRQ_PKG BIT(1)
#define DMA_IRQ_QUEUE BIT(2)
@@ -36,8 +35,6 @@
#define DMA_IRQ_CHAN_WIDTH 4
-#define DMA_IRQ_STAT(x) ((x) * 0x04 + 0x10)
-
#define DMA_STAT 0x30
/* Offset between DMA_IRQ_EN and DMA_IRQ_STAT limits number of channels */
@@ -52,6 +49,14 @@
#define SUNXI_H3_SECURE_REG 0x20
#define SUNXI_H3_DMA_GATE 0x28
#define SUNXI_H3_DMA_GATE_ENABLE 0x4
+
+/*
+ * Interrupts specific registers
+ */
+#define DMA_IRQ_STRIDE_A31 0x04
+#define DMA_IRQ_EN_OFFSET_A31 0x00
+#define DMA_IRQ_STAT_OFFSET_A31 0x10
+
/*
* Channels specific registers
*/
@@ -144,6 +149,9 @@ struct sun6i_dma_config {
u32 dst_addr_widths;
bool has_high_addr;
bool has_mbus_clk;
+ u32 irq_stride;
+ u32 irq_en_offset;
+ u32 irq_stat_offset;
};
/*
@@ -234,19 +242,43 @@ to_sun6i_desc(struct dma_async_tx_descriptor *tx)
return container_of(tx, struct sun6i_desc, vd.tx);
}
+static u32 sun6i_read_irq_en(struct sun6i_dma_dev *sdev, u32 irq_reg)
+{
+ return readl(sdev->base + irq_reg * sdev->cfg->irq_stride + sdev->cfg->irq_en_offset);
+}
+
+static void sun6i_write_irq_en(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 irq_val)
+{
+ writel(irq_val, sdev->base + irq_reg * sdev->cfg->irq_stride + sdev->cfg->irq_en_offset);
+}
+
+static u32 sun6i_read_irq_stat(struct sun6i_dma_dev *sdev, u32 irq_reg)
+{
+ return readl(sdev->base + irq_reg * sdev->cfg->irq_stride + sdev->cfg->irq_stat_offset);
+}
+
+static void sun6i_write_irq_stat(struct sun6i_dma_dev *sdev, u32 irq_reg, u32 status)
+{
+ writel(status, sdev->base + irq_reg * sdev->cfg->irq_stride + sdev->cfg->irq_stat_offset);
+}
+
static inline void sun6i_dma_dump_com_regs(struct sun6i_dma_dev *sdev)
{
- dev_dbg(sdev->slave.dev, "Common register:\n"
- "\tmask0(%04x): 0x%08x\n"
- "\tmask1(%04x): 0x%08x\n"
- "\tpend0(%04x): 0x%08x\n"
- "\tpend1(%04x): 0x%08x\n"
- "\tstats(%04x): 0x%08x\n",
- DMA_IRQ_EN(0), readl(sdev->base + DMA_IRQ_EN(0)),
- DMA_IRQ_EN(1), readl(sdev->base + DMA_IRQ_EN(1)),
- DMA_IRQ_STAT(0), readl(sdev->base + DMA_IRQ_STAT(0)),
- DMA_IRQ_STAT(1), readl(sdev->base + DMA_IRQ_STAT(1)),
- DMA_STAT, readl(sdev->base + DMA_STAT));
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ dev_dbg(sdev->slave.dev, "Common register:\n"
+ "chan num %d\n"
+ "\tmask(%04x): 0x%08x\n"
+ "\tpend(%04x): 0x%08x\n"
+ "\tstats(%04x): 0x%08x\n",
+ i,
+ i * sdev->cfg->irq_stride + sdev->cfg->irq_en_offset,
+ sun6i_read_irq_en(sdev, i),
+ i * sdev->cfg->irq_stride + sdev->cfg->irq_stat_offset,
+ sun6i_read_irq_stat(sdev, i),
+ DMA_STAT, readl(sdev->base + DMA_STAT));
+ }
}
static inline void sun6i_dma_dump_chan_regs(struct sun6i_dma_dev *sdev,
@@ -460,11 +492,11 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan)
vchan->irq_type = vchan->cyclic ? DMA_IRQ_PKG : DMA_IRQ_QUEUE;
- irq_val = readl(sdev->base + DMA_IRQ_EN(irq_reg));
+ irq_val = sun6i_read_irq_en(sdev, irq_reg);
irq_val &= ~((DMA_IRQ_HALF | DMA_IRQ_PKG | DMA_IRQ_QUEUE) <<
(irq_offset * DMA_IRQ_CHAN_WIDTH));
irq_val |= vchan->irq_type << (irq_offset * DMA_IRQ_CHAN_WIDTH);
- writel(irq_val, sdev->base + DMA_IRQ_EN(irq_reg));
+ sun6i_write_irq_en(sdev, irq_reg, irq_val);
writel(pchan->desc->p_lli, pchan->base + DMA_CHAN_LLI_ADDR);
writel(DMA_CHAN_ENABLE_START, pchan->base + DMA_CHAN_ENABLE);
@@ -549,14 +581,14 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
u32 status;
for (i = 0; i < sdev->num_pchans / DMA_IRQ_CHAN_NR; i++) {
- status = readl(sdev->base + DMA_IRQ_STAT(i));
+ status = sun6i_read_irq_stat(sdev, i);
if (!status)
continue;
dev_dbg(sdev->slave.dev, "DMA irq status %s: 0x%x\n",
str_high_low(i), status);
- writel(status, sdev->base + DMA_IRQ_STAT(i));
+ sun6i_write_irq_stat(sdev, i, status);
for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) {
pchan = sdev->pchans + j;
@@ -1072,9 +1104,11 @@ static struct dma_chan *sun6i_dma_of_xlate(struct of_phandle_args *dma_spec,
static inline void sun6i_kill_tasklet(struct sun6i_dma_dev *sdev)
{
+ int i;
+
/* Disable all interrupts from DMA */
- writel(0, sdev->base + DMA_IRQ_EN(0));
- writel(0, sdev->base + DMA_IRQ_EN(1));
+ for (i = 0; i < 2; i++)
+ sun6i_write_irq_en(sdev, i, 0);
/* Prevent spurious interrupts from scheduling the tasklet */
atomic_inc(&sdev->tasklet_shutdown);
@@ -1098,6 +1132,11 @@ static inline void sun6i_dma_free(struct sun6i_dma_dev *sdev)
}
}
+#define SUN6I_DMA_IRQ_A31_COMMON_CFG \
+ .irq_stride = DMA_IRQ_STRIDE_A31, \
+ .irq_en_offset = DMA_IRQ_EN_OFFSET_A31, \
+ .irq_stat_offset = DMA_IRQ_STAT_OFFSET_A31,
+
/*
* For A31:
*
@@ -1129,6 +1168,7 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = {
.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ SUN6I_DMA_IRQ_A31_COMMON_CFG
};
/*
@@ -1152,6 +1192,7 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = {
.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ SUN6I_DMA_IRQ_A31_COMMON_CFG
};
static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
@@ -1170,6 +1211,7 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ SUN6I_DMA_IRQ_A31_COMMON_CFG
};
/*
@@ -1197,6 +1239,7 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
+ SUN6I_DMA_IRQ_A31_COMMON_CFG
};
/*
@@ -1218,6 +1261,7 @@ static struct sun6i_dma_config sun50i_a64_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
+ SUN6I_DMA_IRQ_A31_COMMON_CFG
};
/*
@@ -1241,6 +1285,7 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
.has_high_addr = true,
.has_mbus_clk = true,
+ SUN6I_DMA_IRQ_A31_COMMON_CFG
};
/*
@@ -1263,6 +1308,7 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
.has_mbus_clk = true,
+ SUN6I_DMA_IRQ_A31_COMMON_CFG
};
/*
@@ -1286,6 +1332,7 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = {
.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ SUN6I_DMA_IRQ_A31_COMMON_CFG
};
static const struct of_device_id sun6i_dma_match[] = {
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v5 2/5] dmaengine: sun6i-dma: Support variable address widths using masks
2026-08-26 2:40 [PATCH v5 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller Yuanshen Cao
2026-08-26 2:40 ` [PATCH v5 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling Yuanshen Cao
@ 2026-08-26 2:40 ` Yuanshen Cao
2026-08-26 2:40 ` [PATCH v5 3/5] dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping Yuanshen Cao
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Yuanshen Cao @ 2026-08-26 2:40 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard
Cc: dmaengine, linux-arm-kernel, linux-sunxi, linux-kernel,
devicetree, Yuanshen Cao
The Allwinner A733 DMA controller supports higher addresses (up to
32G) compared to previous generations. The existing `sun6i_dma_set_addr`
function uses hardcoded logic for setting the high-address bits in the
LLI parameters.
Add `src_high_addr_mask` and `dst_high_addr_mask` to `struct
sun6i_dma_config` to handle different high-address bitfield mappings.
Update `sun6i_dma_set_addr` to use these masks via `field_prep()`,
allowing the driver to support variable address widths.
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
drivers/dma/sun6i-dma.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index f305fbfb3545..1ffd870d9594 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -7,6 +7,7 @@
* Maxime Ripard <maxime.ripard@free-electrons.com>
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
@@ -103,8 +104,8 @@
* The LLI link physical address is also mangled, but we avoid dealing
* with that by allocating LLIs from the DMA32 zone.
*/
-#define SRC_HIGH_ADDR(x) (((x) & 0x3U) << 16)
-#define DST_HIGH_ADDR(x) (((x) & 0x3U) << 18)
+#define SRC_HIGH_ADDR_MASK GENMASK(17, 16)
+#define DST_HIGH_ADDR_MASK GENMASK(19, 18)
/*
* Various hardware related defines
@@ -147,7 +148,8 @@ struct sun6i_dma_config {
u32 dst_burst_lengths;
u32 src_addr_widths;
u32 dst_addr_widths;
- bool has_high_addr;
+ u32 src_high_addr_mask;
+ u32 dst_high_addr_mask;
bool has_mbus_clk;
u32 irq_stride;
u32 irq_en_offset;
@@ -687,9 +689,10 @@ static inline void sun6i_dma_set_addr(struct sun6i_dma_dev *sdev,
v_lli->src = lower_32_bits(src);
v_lli->dst = lower_32_bits(dst);
- if (sdev->cfg->has_high_addr)
- v_lli->para |= SRC_HIGH_ADDR(upper_32_bits(src)) |
- DST_HIGH_ADDR(upper_32_bits(dst));
+ if (sdev->cfg->src_high_addr_mask)
+ v_lli->para |=
+ field_prep(sdev->cfg->src_high_addr_mask, upper_32_bits(src)) |
+ field_prep(sdev->cfg->dst_high_addr_mask, upper_32_bits(dst));
}
static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
@@ -1283,7 +1286,8 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
- .has_high_addr = true,
+ .src_high_addr_mask = SRC_HIGH_ADDR_MASK,
+ .dst_high_addr_mask = DST_HIGH_ADDR_MASK,
.has_mbus_clk = true,
SUN6I_DMA_IRQ_A31_COMMON_CFG
};
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v5 3/5] dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping
2026-08-26 2:40 [PATCH v5 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller Yuanshen Cao
2026-08-26 2:40 ` [PATCH v5 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling Yuanshen Cao
2026-08-26 2:40 ` [PATCH v5 2/5] dmaengine: sun6i-dma: Support variable address widths using masks Yuanshen Cao
@ 2026-08-26 2:40 ` Yuanshen Cao
2026-08-26 2:40 ` [PATCH v5 4/5] dt-bindings: dmaengine: sun50i-a64-dma: Add allwinner,sun60i-a733-dma compatible string Yuanshen Cao
2026-08-26 2:40 ` [PATCH v5 5/5] dmaengine: sun6i-dma: Implement support for Allwinner A733 DMA controller Yuanshen Cao
4 siblings, 0 replies; 6+ messages in thread
From: Yuanshen Cao @ 2026-08-26 2:40 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard
Cc: dmaengine, linux-arm-kernel, linux-sunxi, linux-kernel,
devicetree, Yuanshen Cao
The `sun6i-dma` driver previously assumed a fixed number of channels
per interrupt register. For example, `sun6i_kill_tasklet` was hardcoded
to disable interrupts only for registers 0 and 1. `DMA_MAX_CHANNELS` was
also not in used previously, and the old SoCs never has more than 16
channels.
The A733 has a different interrupt structure where the number of
channels per register may differ. Add `num_channels_per_reg` to `struct
sun6i_dma_config` to make the interrupt handling logic handware-agnostic
Update `sun6i_dma_interrupt`, `sun6i_dma_start_desc`, and
`sun6i_kill_tasklet` to use this value.
Additionally, set `DMA_MAX_CHANNELS` to 16 to ensure loops over
interrupts are correctly bounded, aligning with the hardware
specifications.
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
drivers/dma/sun6i-dma.c | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index 1ffd870d9594..ffb63212bea7 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -32,14 +32,13 @@
#define DMA_IRQ_PKG BIT(1)
#define DMA_IRQ_QUEUE BIT(2)
-#define DMA_IRQ_CHAN_NR 8
#define DMA_IRQ_CHAN_WIDTH 4
#define DMA_STAT 0x30
/* Offset between DMA_IRQ_EN and DMA_IRQ_STAT limits number of channels */
-#define DMA_MAX_CHANNELS (DMA_IRQ_CHAN_NR * 0x10 / 4)
+#define DMA_MAX_CHANNELS 16
/*
* sun8i specific registers
@@ -58,6 +57,8 @@
#define DMA_IRQ_EN_OFFSET_A31 0x00
#define DMA_IRQ_STAT_OFFSET_A31 0x10
+#define DMA_IRQ_CHAN_NR_A31 8
+
/*
* Channels specific registers
*/
@@ -154,6 +155,7 @@ struct sun6i_dma_config {
u32 irq_stride;
u32 irq_en_offset;
u32 irq_stat_offset;
+ u32 num_channels_per_reg;
};
/*
@@ -268,7 +270,7 @@ static inline void sun6i_dma_dump_com_regs(struct sun6i_dma_dev *sdev)
{
int i;
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < DIV_ROUND_UP(sdev->num_pchans, sdev->cfg->num_channels_per_reg); i++) {
dev_dbg(sdev->slave.dev, "Common register:\n"
"chan num %d\n"
"\tmask(%04x): 0x%08x\n"
@@ -489,8 +491,8 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan)
sun6i_dma_dump_lli(vchan, pchan->desc->v_lli, pchan->desc->p_lli);
- irq_reg = pchan->idx / DMA_IRQ_CHAN_NR;
- irq_offset = pchan->idx % DMA_IRQ_CHAN_NR;
+ irq_reg = pchan->idx / sdev->cfg->num_channels_per_reg;
+ irq_offset = pchan->idx % sdev->cfg->num_channels_per_reg;
vchan->irq_type = vchan->cyclic ? DMA_IRQ_PKG : DMA_IRQ_QUEUE;
@@ -582,7 +584,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
int i, j, ret = IRQ_NONE;
u32 status;
- for (i = 0; i < sdev->num_pchans / DMA_IRQ_CHAN_NR; i++) {
+ for (i = 0; i < sdev->num_pchans / sdev->cfg->num_channels_per_reg; i++) {
status = sun6i_read_irq_stat(sdev, i);
if (!status)
continue;
@@ -592,7 +594,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
sun6i_write_irq_stat(sdev, i, status);
- for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) {
+ for (j = 0; (j < sdev->cfg->num_channels_per_reg) && status; j++) {
pchan = sdev->pchans + j;
vchan = pchan->vchan;
if (vchan && (status & vchan->irq_type)) {
@@ -1110,7 +1112,7 @@ static inline void sun6i_kill_tasklet(struct sun6i_dma_dev *sdev)
int i;
/* Disable all interrupts from DMA */
- for (i = 0; i < 2; i++)
+ for (i = 0; i < DMA_MAX_CHANNELS / sdev->cfg->num_channels_per_reg; i++)
sun6i_write_irq_en(sdev, i, 0);
/* Prevent spurious interrupts from scheduling the tasklet */
@@ -1138,7 +1140,8 @@ static inline void sun6i_dma_free(struct sun6i_dma_dev *sdev)
#define SUN6I_DMA_IRQ_A31_COMMON_CFG \
.irq_stride = DMA_IRQ_STRIDE_A31, \
.irq_en_offset = DMA_IRQ_EN_OFFSET_A31, \
- .irq_stat_offset = DMA_IRQ_STAT_OFFSET_A31,
+ .irq_stat_offset = DMA_IRQ_STAT_OFFSET_A31, \
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR_A31,
/*
* For A31:
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v5 4/5] dt-bindings: dmaengine: sun50i-a64-dma: Add allwinner,sun60i-a733-dma compatible string
2026-08-26 2:40 [PATCH v5 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller Yuanshen Cao
` (2 preceding siblings ...)
2026-08-26 2:40 ` [PATCH v5 3/5] dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping Yuanshen Cao
@ 2026-08-26 2:40 ` Yuanshen Cao
2026-08-26 2:40 ` [PATCH v5 5/5] dmaengine: sun6i-dma: Implement support for Allwinner A733 DMA controller Yuanshen Cao
4 siblings, 0 replies; 6+ messages in thread
From: Yuanshen Cao @ 2026-08-26 2:40 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard
Cc: dmaengine, linux-arm-kernel, linux-sunxi, linux-kernel,
devicetree, Yuanshen Cao, Frank Li, Krzysztof Kozlowski
Add `allwinner,sun60i-a733-dma` to the list of compatible strings for the
`sun50i-a64-dma` dtbinding documentation.
While the A733 DMA controller shares many similarities with the sun50i-a64
DMA controller, it requires a specific configuration due to differences in:
- Interrupt register layout and mapping.
- Number of channels per interrupt register.
- Support for higher (32G) address widths in LLI parameters.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml b/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml
index c3e14eb6cfff..1cc3304b7414 100644
--- a/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml
+++ b/Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml
@@ -25,6 +25,7 @@ properties:
- allwinner,sun50i-a64-dma
- allwinner,sun50i-a100-dma
- allwinner,sun50i-h6-dma
+ - allwinner,sun60i-a733-dma
- items:
- const: allwinner,sun8i-r40-dma
- const: allwinner,sun50i-a64-dma
@@ -70,6 +71,7 @@ if:
- allwinner,sun20i-d1-dma
- allwinner,sun50i-a100-dma
- allwinner,sun50i-h6-dma
+ - allwinner,sun60i-a733-dma
then:
properties:
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v5 5/5] dmaengine: sun6i-dma: Implement support for Allwinner A733 DMA controller
2026-08-26 2:40 [PATCH v5 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller Yuanshen Cao
` (3 preceding siblings ...)
2026-08-26 2:40 ` [PATCH v5 4/5] dt-bindings: dmaengine: sun50i-a64-dma: Add allwinner,sun60i-a733-dma compatible string Yuanshen Cao
@ 2026-08-26 2:40 ` Yuanshen Cao
4 siblings, 0 replies; 6+ messages in thread
From: Yuanshen Cao @ 2026-08-26 2:40 UTC (permalink / raw)
To: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard
Cc: dmaengine, linux-arm-kernel, linux-sunxi, linux-kernel,
devicetree, Yuanshen Cao
Implement support for the Allwinner A733 DMA controller by defining
A733-specific register offsets, bitfield masks, and the
`sun60i_a733_dma_cfg` configuration structure.
This includes the IRQ stride, offsets, and channel counts specific to the
A733, as well as the 32G mask for high-address fields in the LLI. Add
`sun60i_a733_dma_cfg`, which ties all the refactored functionality
together for A733.
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
drivers/dma/sun6i-dma.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index ffb63212bea7..66281e01972d 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -54,10 +54,14 @@
* Interrupts specific registers
*/
#define DMA_IRQ_STRIDE_A31 0x04
+#define DMA_IRQ_STRIDE_A733 0x40
#define DMA_IRQ_EN_OFFSET_A31 0x00
+#define DMA_IRQ_EN_OFFSET_A733 0x134
#define DMA_IRQ_STAT_OFFSET_A31 0x10
+#define DMA_IRQ_STAT_OFFSET_A733 0x138
#define DMA_IRQ_CHAN_NR_A31 8
+#define DMA_IRQ_CHAN_NR_A733 1
/*
* Channels specific registers
@@ -107,6 +111,8 @@
*/
#define SRC_HIGH_ADDR_MASK GENMASK(17, 16)
#define DST_HIGH_ADDR_MASK GENMASK(19, 18)
+#define SRC_HIGH_ADDR_32G_MASK GENMASK(13, 11)
+#define DST_HIGH_ADDR_32G_MASK GENMASK(17, 15)
/*
* Various hardware related defines
@@ -1318,6 +1324,32 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
SUN6I_DMA_IRQ_A31_COMMON_CFG
};
+/*
+ * The A733 binding uses the number of dma channels from the
+ * device tree node.
+ */
+static struct sun6i_dma_config sun60i_a733_dma_cfg = {
+ .clock_autogate_enable = sun6i_enable_clock_autogate_h3,
+ .set_burst_length = sun6i_set_burst_length_h3,
+ .set_drq = sun6i_set_drq_h6,
+ .set_mode = sun6i_set_mode_h6,
+ .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
+ .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
+ .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES),
+ .src_high_addr_mask = SRC_HIGH_ADDR_32G_MASK,
+ .dst_high_addr_mask = DST_HIGH_ADDR_32G_MASK,
+ .has_mbus_clk = true,
+ .irq_stride = DMA_IRQ_STRIDE_A733,
+ .irq_en_offset = DMA_IRQ_EN_OFFSET_A733,
+ .irq_stat_offset = DMA_IRQ_STAT_OFFSET_A733,
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR_A733,
+};
+
/*
* The V3s have only 8 physical channels, a maximum DRQ port id of 23,
* and a total of 24 usable source and destination endpoints.
@@ -1352,6 +1384,7 @@ static const struct of_device_id sun6i_dma_match[] = {
{ .compatible = "allwinner,sun50i-a64-dma", .data = &sun50i_a64_dma_cfg },
{ .compatible = "allwinner,sun50i-a100-dma", .data = &sun50i_a100_dma_cfg },
{ .compatible = "allwinner,sun50i-h6-dma", .data = &sun50i_h6_dma_cfg },
+ { .compatible = "allwinner,sun60i-a733-dma", .data = &sun60i_a733_dma_cfg },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sun6i_dma_match);
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-08-26 2:41 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-26 2:40 [PATCH v5 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller Yuanshen Cao
2026-08-26 2:40 ` [PATCH v5 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling Yuanshen Cao
2026-08-26 2:40 ` [PATCH v5 2/5] dmaengine: sun6i-dma: Support variable address widths using masks Yuanshen Cao
2026-08-26 2:40 ` [PATCH v5 3/5] dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping Yuanshen Cao
2026-08-26 2:40 ` [PATCH v5 4/5] dt-bindings: dmaengine: sun50i-a64-dma: Add allwinner,sun60i-a733-dma compatible string Yuanshen Cao
2026-08-26 2:40 ` [PATCH v5 5/5] dmaengine: sun6i-dma: Implement support for Allwinner A733 DMA controller Yuanshen Cao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox