* [PATCH 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling
2026-06-19 4:53 [PATCH 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller Yuanshen Cao
@ 2026-06-19 4:53 ` Yuanshen Cao
2026-06-19 14:26 ` Frank Li
2026-06-19 4:53 ` [PATCH 2/5] dmaengine: sun6i-dma: Add set_addr function pointer for variable address widths Yuanshen Cao
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Yuanshen Cao @ 2026-06-19 4:53 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
This patch is the first step in a refactoring effort to support the
Allwinner A733 DMA controller. Currently, the `sun6i-dma` driver has
several functions related to interrupt handling (reading/writing
interrupt enable and status registers) and register dumping that are
hardcoded.
To support the A733, which has different register layouts and interrupt
handling logic, these functions are being moved into the
`sun6i_dma_config` structure as function pointers. This allows the
driver to use a polymorphic approach where the specific implementation
is determined by the hardware configuration assigned during device
probing.
Changes:
- Added function pointers to `struct sun6i_dma_config` for:
- `dump_com_regs`
- `read_irq_en`
- `write_irq_en`
- `read_irq_stat`
- `write_irq_stat`
- Implemented generic `sun6i_read/write_irq_*` functions for existing
hardware.
- Updated existing `sun6i_dma_config` instances (A31, A23, H3, A64,
A100, H6, V3S) to use these new function pointers.
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
drivers/dma/sun6i-dma.c | 74 +++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 69 insertions(+), 5 deletions(-)
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index a9a254dbf8cb..d92e702320d9 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -138,6 +138,11 @@ struct sun6i_dma_config {
void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst);
void (*set_drq)(u32 *p_cfg, s8 src_drq, s8 dst_drq);
void (*set_mode)(u32 *p_cfg, s8 src_mode, s8 dst_mode);
+ void (*dump_com_regs)(struct sun6i_dma_dev *sdev);
+ u32 (*read_irq_en)(struct sun6i_dma_dev *sdev, u32 chan_num);
+ void (*write_irq_en)(struct sun6i_dma_dev *sdev, u32 chan_num, u32 irq_val);
+ u32 (*read_irq_stat)(struct sun6i_dma_dev *sdev, u32 chan_num);
+ void (*write_irq_stat)(struct sun6i_dma_dev *sdev, u32 chan_num, u32 status);
u32 src_burst_lengths;
u32 dst_burst_lengths;
u32 src_addr_widths;
@@ -347,6 +352,25 @@ static void sun6i_set_mode_h6(u32 *p_cfg, s8 src_mode, s8 dst_mode)
DMA_CHAN_CFG_DST_MODE_H6(dst_mode);
}
+static u32 sun6i_read_irq_en(struct sun6i_dma_dev *sdev, u32 chan_num)
+{
+ return readl(sdev->base + DMA_IRQ_EN(chan_num));
+}
+
+static void sun6i_write_irq_en(struct sun6i_dma_dev *sdev, u32 chan_num, u32 irq_val)
+{
+ writel(irq_val, sdev->base + DMA_IRQ_EN(chan_num));
+}
+static u32 sun6i_read_irq_stat(struct sun6i_dma_dev *sdev, u32 chan_num)
+{
+ return readl(sdev->base + DMA_IRQ_STAT(chan_num));
+}
+
+static void sun6i_write_irq_stat(struct sun6i_dma_dev *sdev, u32 chan_num, u32 status)
+{
+ writel(status, sdev->base + DMA_IRQ_STAT(chan_num));
+}
+
static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan)
{
struct sun6i_desc *txd = pchan->desc;
@@ -460,16 +484,16 @@ 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 = sdev->cfg->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));
+ sdev->cfg->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);
- sun6i_dma_dump_com_regs(sdev);
+ sdev->cfg->dump_com_regs(sdev);
sun6i_dma_dump_chan_regs(sdev, pchan);
return 0;
@@ -549,14 +573,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 = sdev->cfg->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));
+ sdev->cfg->write_irq_stat(sdev, i, status);
for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) {
pchan = sdev->pchans + j;
@@ -1124,6 +1148,11 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_a31,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .dump_com_regs = sun6i_dma_dump_com_regs,
+ .read_irq_en = sun6i_read_irq_en,
+ .write_irq_en = sun6i_write_irq_en,
+ .read_irq_stat = sun6i_read_irq_stat,
+ .write_irq_stat = sun6i_write_irq_stat,
.src_burst_lengths = BIT(1) | BIT(8),
.dst_burst_lengths = BIT(1) | BIT(8),
.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
@@ -1147,6 +1176,11 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_a31,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .dump_com_regs = sun6i_dma_dump_com_regs,
+ .read_irq_en = sun6i_read_irq_en,
+ .write_irq_en = sun6i_write_irq_en,
+ .read_irq_stat = sun6i_read_irq_stat,
+ .write_irq_stat = sun6i_write_irq_stat,
.src_burst_lengths = BIT(1) | BIT(8),
.dst_burst_lengths = BIT(1) | BIT(8),
.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
@@ -1165,6 +1199,11 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_a31,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .dump_com_regs = sun6i_dma_dump_com_regs,
+ .read_irq_en = sun6i_read_irq_en,
+ .write_irq_en = sun6i_write_irq_en,
+ .read_irq_stat = sun6i_read_irq_stat,
+ .write_irq_stat = sun6i_write_irq_stat,
.src_burst_lengths = BIT(1) | BIT(8),
.dst_burst_lengths = BIT(1) | BIT(8),
.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
@@ -1190,6 +1229,11 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_h3,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .dump_com_regs = sun6i_dma_dump_com_regs,
+ .read_irq_en = sun6i_read_irq_en,
+ .write_irq_en = sun6i_write_irq_en,
+ .read_irq_stat = sun6i_read_irq_stat,
+ .write_irq_stat = sun6i_write_irq_stat,
.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) |
@@ -1211,6 +1255,11 @@ static struct sun6i_dma_config sun50i_a64_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_h3,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .dump_com_regs = sun6i_dma_dump_com_regs,
+ .read_irq_en = sun6i_read_irq_en,
+ .write_irq_en = sun6i_write_irq_en,
+ .read_irq_stat = sun6i_read_irq_stat,
+ .write_irq_stat = sun6i_write_irq_stat,
.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) |
@@ -1232,6 +1281,11 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_h3,
.set_drq = sun6i_set_drq_h6,
.set_mode = sun6i_set_mode_h6,
+ .dump_com_regs = sun6i_dma_dump_com_regs,
+ .read_irq_en = sun6i_read_irq_en,
+ .write_irq_en = sun6i_write_irq_en,
+ .read_irq_stat = sun6i_read_irq_stat,
+ .write_irq_stat = sun6i_write_irq_stat,
.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) |
@@ -1255,6 +1309,11 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_h3,
.set_drq = sun6i_set_drq_h6,
.set_mode = sun6i_set_mode_h6,
+ .dump_com_regs = sun6i_dma_dump_com_regs,
+ .read_irq_en = sun6i_read_irq_en,
+ .write_irq_en = sun6i_write_irq_en,
+ .read_irq_stat = sun6i_read_irq_stat,
+ .write_irq_stat = sun6i_write_irq_stat,
.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) |
@@ -1281,6 +1340,11 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_a31,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .dump_com_regs = sun6i_dma_dump_com_regs,
+ .read_irq_en = sun6i_read_irq_en,
+ .write_irq_en = sun6i_write_irq_en,
+ .read_irq_stat = sun6i_read_irq_stat,
+ .write_irq_stat = sun6i_write_irq_stat,
.src_burst_lengths = BIT(1) | BIT(8),
.dst_burst_lengths = BIT(1) | BIT(8),
.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling
2026-06-19 4:53 ` [PATCH 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling Yuanshen Cao
@ 2026-06-19 14:26 ` Frank Li
0 siblings, 0 replies; 11+ messages in thread
From: Frank Li @ 2026-06-19 14:26 UTC (permalink / raw)
To: Yuanshen Cao
Cc: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
On Fri, Jun 19, 2026 at 04:53:30AM +0000, Yuanshen Cao wrote:
>
> This patch is the first step in a refactoring effort to support the
avoid use "this patch/commit", Just
Refactor ... to support Allwinner A733 DMA controller.
> Allwinner A733 DMA controller. Currently, the `sun6i-dma` driver has
> several functions related to interrupt handling (reading/writing
> interrupt enable and status registers) and register dumping that are
> hardcoded.
>
> To support the A733, which has different register layouts and interrupt
> handling logic, these functions are being moved into the
> `sun6i_dma_config` structure as function pointers. This allows the
> driver to use a polymorphic approach where the specific implementation
> is determined by the hardware configuration assigned during device
> probing.
>
> Changes:
> - Added function pointers to `struct sun6i_dma_config` for:
> - `dump_com_regs`
> - `read_irq_en`
> - `write_irq_en`
> - `read_irq_stat`
> - `write_irq_stat`
> - Implemented generic `sun6i_read/write_irq_*` functions for existing
> hardware.
> - Updated existing `sun6i_dma_config` instances (A31, A23, H3, A64,
> A100, H6, V3S) to use these new function pointers.
>
> Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
> ---
> drivers/dma/sun6i-dma.c | 74 +++++++++++++++++++++++++++++++++++++++++++++----
> 1 file changed, 69 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index a9a254dbf8cb..d92e702320d9 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c
> @@ -138,6 +138,11 @@ struct sun6i_dma_config {
> void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst);
> void (*set_drq)(u32 *p_cfg, s8 src_drq, s8 dst_drq);
> void (*set_mode)(u32 *p_cfg, s8 src_mode, s8 dst_mode);
> + void (*dump_com_regs)(struct sun6i_dma_dev *sdev);
> + u32 (*read_irq_en)(struct sun6i_dma_dev *sdev, u32 chan_num);
> + void (*write_irq_en)(struct sun6i_dma_dev *sdev, u32 chan_num, u32 irq_val);
> + u32 (*read_irq_stat)(struct sun6i_dma_dev *sdev, u32 chan_num);
> + void (*write_irq_stat)(struct sun6i_dma_dev *sdev, u32 chan_num, u32 status);
> u32 src_burst_lengths;
> u32 dst_burst_lengths;
> u32 src_addr_widths;
> @@ -347,6 +352,25 @@ static void sun6i_set_mode_h6(u32 *p_cfg, s8 src_mode, s8 dst_mode)
> DMA_CHAN_CFG_DST_MODE_H6(dst_mode);
> }
>
> +static u32 sun6i_read_irq_en(struct sun6i_dma_dev *sdev, u32 chan_num)
> +{
> + return readl(sdev->base + DMA_IRQ_EN(chan_num));
> +}
> +
> +static void sun6i_write_irq_en(struct sun6i_dma_dev *sdev, u32 chan_num, u32 irq_val)
> +{
> + writel(irq_val, sdev->base + DMA_IRQ_EN(chan_num));
> +}
> +static u32 sun6i_read_irq_stat(struct sun6i_dma_dev *sdev, u32 chan_num)
> +{
> + return readl(sdev->base + DMA_IRQ_STAT(chan_num));
> +}
> +
> +static void sun6i_write_irq_stat(struct sun6i_dma_dev *sdev, u32 chan_num, u32 status)
> +{
> + writel(status, sdev->base + DMA_IRQ_STAT(chan_num));
> +}
> +
> static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan)
> {
> struct sun6i_desc *txd = pchan->desc;
> @@ -460,16 +484,16 @@ 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 = sdev->cfg->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));
> + sdev->cfg->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);
>
> - sun6i_dma_dump_com_regs(sdev);
> + sdev->cfg->dump_com_regs(sdev);
> sun6i_dma_dump_chan_regs(sdev, pchan);
>
> return 0;
> @@ -549,14 +573,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 = sdev->cfg->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));
> + sdev->cfg->write_irq_stat(sdev, i, status);
>
> for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) {
> pchan = sdev->pchans + j;
> @@ -1124,6 +1148,11 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_a31,
> .set_drq = sun6i_set_drq_a31,
> .set_mode = sun6i_set_mode_a31,
> + .dump_com_regs = sun6i_dma_dump_com_regs,
> + .read_irq_en = sun6i_read_irq_en,
> + .write_irq_en = sun6i_write_irq_en,
> + .read_irq_stat = sun6i_read_irq_stat,
> + .write_irq_stat = sun6i_write_irq_stat,
Can you define macro like to avoid duplicate these init code. Or set it at
probe if it is NULL.
Frank
> .src_burst_lengths = BIT(1) | BIT(8),
> .dst_burst_lengths = BIT(1) | BIT(8),
> .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1147,6 +1176,11 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_a31,
> .set_drq = sun6i_set_drq_a31,
> .set_mode = sun6i_set_mode_a31,
> + .dump_com_regs = sun6i_dma_dump_com_regs,
> + .read_irq_en = sun6i_read_irq_en,
> + .write_irq_en = sun6i_write_irq_en,
> + .read_irq_stat = sun6i_read_irq_stat,
> + .write_irq_stat = sun6i_write_irq_stat,
> .src_burst_lengths = BIT(1) | BIT(8),
> .dst_burst_lengths = BIT(1) | BIT(8),
> .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1165,6 +1199,11 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_a31,
> .set_drq = sun6i_set_drq_a31,
> .set_mode = sun6i_set_mode_a31,
> + .dump_com_regs = sun6i_dma_dump_com_regs,
> + .read_irq_en = sun6i_read_irq_en,
> + .write_irq_en = sun6i_write_irq_en,
> + .read_irq_stat = sun6i_read_irq_stat,
> + .write_irq_stat = sun6i_write_irq_stat,
> .src_burst_lengths = BIT(1) | BIT(8),
> .dst_burst_lengths = BIT(1) | BIT(8),
> .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1190,6 +1229,11 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_h3,
> .set_drq = sun6i_set_drq_a31,
> .set_mode = sun6i_set_mode_a31,
> + .dump_com_regs = sun6i_dma_dump_com_regs,
> + .read_irq_en = sun6i_read_irq_en,
> + .write_irq_en = sun6i_write_irq_en,
> + .read_irq_stat = sun6i_read_irq_stat,
> + .write_irq_stat = sun6i_write_irq_stat,
> .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) |
> @@ -1211,6 +1255,11 @@ static struct sun6i_dma_config sun50i_a64_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_h3,
> .set_drq = sun6i_set_drq_a31,
> .set_mode = sun6i_set_mode_a31,
> + .dump_com_regs = sun6i_dma_dump_com_regs,
> + .read_irq_en = sun6i_read_irq_en,
> + .write_irq_en = sun6i_write_irq_en,
> + .read_irq_stat = sun6i_read_irq_stat,
> + .write_irq_stat = sun6i_write_irq_stat,
> .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) |
> @@ -1232,6 +1281,11 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_h3,
> .set_drq = sun6i_set_drq_h6,
> .set_mode = sun6i_set_mode_h6,
> + .dump_com_regs = sun6i_dma_dump_com_regs,
> + .read_irq_en = sun6i_read_irq_en,
> + .write_irq_en = sun6i_write_irq_en,
> + .read_irq_stat = sun6i_read_irq_stat,
> + .write_irq_stat = sun6i_write_irq_stat,
> .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) |
> @@ -1255,6 +1309,11 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_h3,
> .set_drq = sun6i_set_drq_h6,
> .set_mode = sun6i_set_mode_h6,
> + .dump_com_regs = sun6i_dma_dump_com_regs,
> + .read_irq_en = sun6i_read_irq_en,
> + .write_irq_en = sun6i_write_irq_en,
> + .read_irq_stat = sun6i_read_irq_stat,
> + .write_irq_stat = sun6i_write_irq_stat,
> .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) |
> @@ -1281,6 +1340,11 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = {
> .set_burst_length = sun6i_set_burst_length_a31,
> .set_drq = sun6i_set_drq_a31,
> .set_mode = sun6i_set_mode_a31,
> + .dump_com_regs = sun6i_dma_dump_com_regs,
> + .read_irq_en = sun6i_read_irq_en,
> + .write_irq_en = sun6i_write_irq_en,
> + .read_irq_stat = sun6i_read_irq_stat,
> + .write_irq_stat = sun6i_write_irq_stat,
> .src_burst_lengths = BIT(1) | BIT(8),
> .dst_burst_lengths = BIT(1) | BIT(8),
> .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
>
> --
> 2.54.0
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/5] dmaengine: sun6i-dma: Add set_addr function pointer for variable address widths
2026-06-19 4:53 [PATCH 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller Yuanshen Cao
2026-06-19 4:53 ` [PATCH 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling Yuanshen Cao
@ 2026-06-19 4:53 ` Yuanshen Cao
2026-06-19 16:02 ` Frank Li
2026-06-19 4:53 ` [PATCH 3/5] dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping Yuanshen Cao
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Yuanshen Cao @ 2026-06-19 4:53 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 A733 DMA controller supports higher address (up to 32G) compared to
previous generations. The existing `sun6i_dma_set_addr` function uses a
hardcoded logic for setting the high-address bits in the LLI parameters.
By moving `set_addr` into the `sun6i_dma_config` structure, we can
provide specialized implementations for different hardware. This allows
the A733 to use a version of `set_addr` that correctly handles its
specific `SRC_HIGH_ADDR_32G` and `DST_HIGH_ADDR_32G` in the `set_addr`
register later in the series.
Changes:
- Added `set_addr` function pointer to `struct sun6i_dma_config`.
- Refactored `sun6i_dma_set_addr` and introduced
`sun6i_dma_set_addr_a100` (keeping the logic for high address
support).
- Updated all existing configuration structs to include the new
`set_addr` pointer.
- Removed `has_high_addr` since the logic is replaced by
`sun6i_dma_set_addr_a100`.
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
drivers/dma/sun6i-dma.c | 36 ++++++++++++++++++++++++++----------
1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index d92e702320d9..059455425e19 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -112,6 +112,7 @@
/* forward declaration */
struct sun6i_dma_dev;
+struct sun6i_dma_lli;
/*
* Hardware channels / ports representation
@@ -138,6 +139,8 @@ struct sun6i_dma_config {
void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst);
void (*set_drq)(u32 *p_cfg, s8 src_drq, s8 dst_drq);
void (*set_mode)(u32 *p_cfg, s8 src_mode, s8 dst_mode);
+ void (*set_addr)(struct sun6i_dma_dev *sdev, struct sun6i_dma_lli *v_lli,
+ dma_addr_t src, dma_addr_t dst);
void (*dump_com_regs)(struct sun6i_dma_dev *sdev);
u32 (*read_irq_en)(struct sun6i_dma_dev *sdev, u32 chan_num);
void (*write_irq_en)(struct sun6i_dma_dev *sdev, u32 chan_num, u32 irq_val);
@@ -147,7 +150,6 @@ struct sun6i_dma_config {
u32 dst_burst_lengths;
u32 src_addr_widths;
u32 dst_addr_widths;
- bool has_high_addr;
bool has_mbus_clk;
};
@@ -675,13 +677,20 @@ static int set_config(struct sun6i_dma_dev *sdev,
static inline void sun6i_dma_set_addr(struct sun6i_dma_dev *sdev,
struct sun6i_dma_lli *v_lli,
dma_addr_t src, dma_addr_t dst)
+{
+ v_lli->src = lower_32_bits(src);
+ v_lli->dst = lower_32_bits(dst);
+}
+
+static inline void sun6i_dma_set_addr_a100(struct sun6i_dma_dev *sdev,
+ struct sun6i_dma_lli *v_lli,
+ dma_addr_t src, dma_addr_t dst)
{
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));
+ v_lli->para |= SRC_HIGH_ADDR(upper_32_bits(src)) |
+ DST_HIGH_ADDR(upper_32_bits(dst));
}
static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
@@ -714,7 +723,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
v_lli->len = len;
v_lli->para = NORMAL_WAIT;
- sun6i_dma_set_addr(sdev, v_lli, src, dest);
+ sdev->cfg->set_addr(sdev, v_lli, src, dest);
burst = convert_burst(8);
width = convert_buswidth(DMA_SLAVE_BUSWIDTH_4_BYTES);
@@ -773,7 +782,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
v_lli->para = NORMAL_WAIT;
if (dir == DMA_MEM_TO_DEV) {
- sun6i_dma_set_addr(sdev, v_lli,
+ sdev->cfg->set_addr(sdev, v_lli,
sg_dma_address(sg),
sconfig->dst_addr);
v_lli->cfg = lli_cfg;
@@ -787,7 +796,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
sg_dma_len(sg), flags);
} else {
- sun6i_dma_set_addr(sdev, v_lli,
+ sdev->cfg->set_addr(sdev, v_lli,
sconfig->src_addr,
sg_dma_address(sg));
v_lli->cfg = lli_cfg;
@@ -858,7 +867,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic(
v_lli->para = NORMAL_WAIT;
if (dir == DMA_MEM_TO_DEV) {
- sun6i_dma_set_addr(sdev, v_lli,
+ sdev->cfg->set_addr(sdev, v_lli,
buf_addr + period_len * i,
sconfig->dst_addr);
v_lli->cfg = lli_cfg;
@@ -870,7 +879,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic(
&sconfig->dst_addr, &buf_addr,
buf_len, flags);
} else {
- sun6i_dma_set_addr(sdev, v_lli,
+ sdev->cfg->set_addr(sdev, v_lli,
sconfig->src_addr,
buf_addr + period_len * i);
v_lli->cfg = lli_cfg;
@@ -1148,6 +1157,7 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_a31,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .set_addr = sun6i_dma_set_addr,
.dump_com_regs = sun6i_dma_dump_com_regs,
.read_irq_en = sun6i_read_irq_en,
.write_irq_en = sun6i_write_irq_en,
@@ -1176,6 +1186,7 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_a31,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .set_addr = sun6i_dma_set_addr,
.dump_com_regs = sun6i_dma_dump_com_regs,
.read_irq_en = sun6i_read_irq_en,
.write_irq_en = sun6i_write_irq_en,
@@ -1199,6 +1210,7 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_a31,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .set_addr = sun6i_dma_set_addr,
.dump_com_regs = sun6i_dma_dump_com_regs,
.read_irq_en = sun6i_read_irq_en,
.write_irq_en = sun6i_write_irq_en,
@@ -1229,6 +1241,7 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_h3,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .set_addr = sun6i_dma_set_addr,
.dump_com_regs = sun6i_dma_dump_com_regs,
.read_irq_en = sun6i_read_irq_en,
.write_irq_en = sun6i_write_irq_en,
@@ -1255,6 +1268,7 @@ static struct sun6i_dma_config sun50i_a64_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_h3,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .set_addr = sun6i_dma_set_addr,
.dump_com_regs = sun6i_dma_dump_com_regs,
.read_irq_en = sun6i_read_irq_en,
.write_irq_en = sun6i_write_irq_en,
@@ -1281,6 +1295,7 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_h3,
.set_drq = sun6i_set_drq_h6,
.set_mode = sun6i_set_mode_h6,
+ .set_addr = sun6i_dma_set_addr_a100,
.dump_com_regs = sun6i_dma_dump_com_regs,
.read_irq_en = sun6i_read_irq_en,
.write_irq_en = sun6i_write_irq_en,
@@ -1296,7 +1311,6 @@ 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,
.has_mbus_clk = true,
};
@@ -1309,6 +1323,7 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_h3,
.set_drq = sun6i_set_drq_h6,
.set_mode = sun6i_set_mode_h6,
+ .set_addr = sun6i_dma_set_addr,
.dump_com_regs = sun6i_dma_dump_com_regs,
.read_irq_en = sun6i_read_irq_en,
.write_irq_en = sun6i_write_irq_en,
@@ -1340,6 +1355,7 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = {
.set_burst_length = sun6i_set_burst_length_a31,
.set_drq = sun6i_set_drq_a31,
.set_mode = sun6i_set_mode_a31,
+ .set_addr = sun6i_dma_set_addr,
.dump_com_regs = sun6i_dma_dump_com_regs,
.read_irq_en = sun6i_read_irq_en,
.write_irq_en = sun6i_write_irq_en,
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH 2/5] dmaengine: sun6i-dma: Add set_addr function pointer for variable address widths
2026-06-19 4:53 ` [PATCH 2/5] dmaengine: sun6i-dma: Add set_addr function pointer for variable address widths Yuanshen Cao
@ 2026-06-19 16:02 ` Frank Li
0 siblings, 0 replies; 11+ messages in thread
From: Frank Li @ 2026-06-19 16:02 UTC (permalink / raw)
To: Yuanshen Cao
Cc: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
On Fri, Jun 19, 2026 at 04:53:31AM +0000, Yuanshen Cao wrote:
>
> The A733 DMA controller supports higher address (up to 32G) compared to
> previous generations. The existing `sun6i_dma_set_addr` function uses a
> hardcoded logic for setting the high-address bits in the LLI parameters.
>
> By moving `set_addr` into the `sun6i_dma_config` structure, we can
> provide specialized implementations for different hardware. This allows
> the A733 to use a version of `set_addr` that correctly handles its
> specific `SRC_HIGH_ADDR_32G` and `DST_HIGH_ADDR_32G` in the `set_addr`
> register later in the series.
>
> Changes:
> - Added `set_addr` function pointer to `struct sun6i_dma_config`.
> - Refactored `sun6i_dma_set_addr` and introduced
> `sun6i_dma_set_addr_a100` (keeping the logic for high address
> support).
> - Updated all existing configuration structs to include the new
> `set_addr` pointer.
> - Removed `has_high_addr` since the logic is replaced by
> `sun6i_dma_set_addr_a100`.
>
> Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
> ---
> drivers/dma/sun6i-dma.c | 36 ++++++++++++++++++++++++++----------
> 1 file changed, 26 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index d92e702320d9..059455425e19 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c
> @@ -112,6 +112,7 @@
>
> /* forward declaration */
> struct sun6i_dma_dev;
> +struct sun6i_dma_lli;
>
> /*
> * Hardware channels / ports representation
> @@ -138,6 +139,8 @@ struct sun6i_dma_config {
> void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst);
> void (*set_drq)(u32 *p_cfg, s8 src_drq, s8 dst_drq);
> void (*set_mode)(u32 *p_cfg, s8 src_mode, s8 dst_mode);
> + void (*set_addr)(struct sun6i_dma_dev *sdev, struct sun6i_dma_lli *v_lli,
> + dma_addr_t src, dma_addr_t dst);
> void (*dump_com_regs)(struct sun6i_dma_dev *sdev);
> u32 (*read_irq_en)(struct sun6i_dma_dev *sdev, u32 chan_num);
> void (*write_irq_en)(struct sun6i_dma_dev *sdev, u32 chan_num, u32 irq_val);
> @@ -147,7 +150,6 @@ struct sun6i_dma_config {
> u32 dst_burst_lengths;
> u32 src_addr_widths;
> u32 dst_addr_widths;
> - bool has_high_addr;
> bool has_mbus_clk;
> };
>
> @@ -675,13 +677,20 @@ static int set_config(struct sun6i_dma_dev *sdev,
> static inline void sun6i_dma_set_addr(struct sun6i_dma_dev *sdev,
> struct sun6i_dma_lli *v_lli,
> dma_addr_t src, dma_addr_t dst)
> +{
> + v_lli->src = lower_32_bits(src);
> + v_lli->dst = lower_32_bits(dst);
> +}
> +
> +static inline void sun6i_dma_set_addr_a100(struct sun6i_dma_dev *sdev,
> + struct sun6i_dma_lli *v_lli,
> + dma_addr_t src, dma_addr_t dst)
> {
> 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));
> + v_lli->para |= SRC_HIGH_ADDR(upper_32_bits(src)) |
> + DST_HIGH_ADDR(upper_32_bits(dst));
> }
>
> static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
> @@ -714,7 +723,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
>
> v_lli->len = len;
> v_lli->para = NORMAL_WAIT;
> - sun6i_dma_set_addr(sdev, v_lli, src, dest);
> + sdev->cfg->set_addr(sdev, v_lli, src, dest);
can you move sdev->cfg->set_addr into helper function sun6i_dma_set_addr())
so need't change other place.
Old sun6i_dma_set_addr() rename sun6i_dma_set_addr_<name>()
sun6i_dma_set_addr()
{
sdev->cfg->set_addr(sdev, v_lli, src, dest)
}
Frank
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 3/5] dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping
2026-06-19 4:53 [PATCH 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller Yuanshen Cao
2026-06-19 4:53 ` [PATCH 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling Yuanshen Cao
2026-06-19 4:53 ` [PATCH 2/5] dmaengine: sun6i-dma: Add set_addr function pointer for variable address widths Yuanshen Cao
@ 2026-06-19 4:53 ` Yuanshen Cao
2026-06-19 15:46 ` Frank Li
2026-06-19 4:53 ` [PATCH 4/5] dmaengine: sun6i-dma: Implement support for Allwinner A733 DMA controller Yuanshen Cao
2026-06-19 4:53 ` [PATCH 5/5] dt-bindings: dma: sun50i-a64-dma: Update device tree bindings documentation for A733 Yuanshen Cao
4 siblings, 1 reply; 11+ messages in thread
From: Yuanshen Cao @ 2026-06-19 4:53 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 previous implementation of `sun6i-dma` had some implicit assumptions
about the number of channels per interrupt register. Specifically,
functions like `sun6i_kill_tasklet` were hardcoded to only disable
interrupts for IRQ 0 and 1. `DMA_MAX_CHANNELS` is also not in used in
the past, 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. This patch introduces
`num_channels_per_reg` to the `sun6i_dma_config`, similar to BSP, to
make the interrupt handling logic hardware-agnostic. It also sets
`DMA_MAX_CHANNELS` to 16 to align with the new BSP code and ensure loops
over interrupts are correctly bounded.
Changes:
- Change `DMA_MAX_CHANNELS` definition to 16.
- Added `num_channels_per_reg` to `struct sun6i_dma_config`.
- Replaced hardcoded IRQ register calculations with values from
`sdev->cfg->num_channels_per_reg`.
- Updated `sun6i_kill_tasklet` to loop through all possible interrupt
registers based on `DMA_MAX_CHANNELS` and the configuration.
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
drivers/dma/sun6i-dma.c | 25 ++++++++++++++++++-------
1 file changed, 18 insertions(+), 7 deletions(-)
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index 059455425e19..fb1c1a28744b 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -41,7 +41,7 @@
#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
@@ -151,6 +151,7 @@ struct sun6i_dma_config {
u32 src_addr_widths;
u32 dst_addr_widths;
bool has_mbus_clk;
+ u32 num_channels_per_reg;
};
/*
@@ -481,8 +482,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;
@@ -574,7 +575,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 = sdev->cfg->read_irq_stat(sdev, i);
if (!status)
continue;
@@ -584,7 +585,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
sdev->cfg->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)) {
@@ -1108,9 +1109,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 < DMA_MAX_CHANNELS / sdev->cfg->num_channels_per_reg; i++)
+ sdev->cfg->write_irq_en(sdev, i, 0);
/* Prevent spurious interrupts from scheduling the tasklet */
atomic_inc(&sdev->tasklet_shutdown);
@@ -1171,6 +1174,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),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
};
/*
@@ -1200,6 +1204,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),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
};
static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
@@ -1224,6 +1229,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),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
};
/*
@@ -1257,6 +1263,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),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
};
/*
@@ -1284,6 +1291,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),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
};
/*
@@ -1311,6 +1319,7 @@ 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),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
.has_mbus_clk = true,
};
@@ -1339,6 +1348,7 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
.has_mbus_clk = true,
};
@@ -1369,6 +1379,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),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR,
};
static const struct of_device_id sun6i_dma_match[] = {
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH 3/5] dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping
2026-06-19 4:53 ` [PATCH 3/5] dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping Yuanshen Cao
@ 2026-06-19 15:46 ` Frank Li
0 siblings, 0 replies; 11+ messages in thread
From: Frank Li @ 2026-06-19 15:46 UTC (permalink / raw)
To: Yuanshen Cao
Cc: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
On Fri, Jun 19, 2026 at 04:53:32AM +0000, Yuanshen Cao wrote:
>
> The previous implementation of `sun6i-dma` had some implicit assumptions
> about the number of channels per interrupt register. Specifically,
> functions like `sun6i_kill_tasklet` were hardcoded to only disable
> interrupts for IRQ 0 and 1. `DMA_MAX_CHANNELS` is also not in used in
> the past, 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. This patch introduces
> `num_channels_per_reg` to the `sun6i_dma_config`, similar to BSP, to
> make the interrupt handling logic hardware-agnostic. It also sets
> `DMA_MAX_CHANNELS` to 16 to align with the new BSP code and ensure loops
> over interrupts are correctly bounded.
>
> Changes:
> - Change `DMA_MAX_CHANNELS` definition to 16.
> - Added `num_channels_per_reg` to `struct sun6i_dma_config`.
> - Replaced hardcoded IRQ register calculations with values from
> `sdev->cfg->num_channels_per_reg`.
> - Updated `sun6i_kill_tasklet` to loop through all possible interrupt
> registers based on `DMA_MAX_CHANNELS` and the configuration.
>
> Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
> ---
> drivers/dma/sun6i-dma.c | 25 ++++++++++++++++++-------
> 1 file changed, 18 insertions(+), 7 deletions(-)
>
...
> @@ -1171,6 +1174,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),
> + .num_channels_per_reg = DMA_IRQ_CHAN_NR,
if previous patch have MACRO, you can put it to there
Frank
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 4/5] dmaengine: sun6i-dma: Implement support for Allwinner A733 DMA controller
2026-06-19 4:53 [PATCH 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller Yuanshen Cao
` (2 preceding siblings ...)
2026-06-19 4:53 ` [PATCH 3/5] dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping Yuanshen Cao
@ 2026-06-19 4:53 ` Yuanshen Cao
2026-06-19 4:53 ` [PATCH 5/5] dt-bindings: dma: sun50i-a64-dma: Update device tree bindings documentation for A733 Yuanshen Cao
4 siblings, 0 replies; 11+ messages in thread
From: Yuanshen Cao @ 2026-06-19 4:53 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
This patch implements the actual support for the Allwinner A733 DMA
controller. It defines the new register offsets and bitfield mappings
required for the A733, which slightly differs from the older `sun6i`
series.
Changes:
- New register macros for A733 interrupt enable `DMA_IRQ_EN_A733` and
status `DMA_IRQ_STAT_A733`.
- New `SRC_HIGH_ADDR_32G` and `DST_HIGH_ADDR_32G` macro to handle the
32G high-address field in the LLI.
- Implemented `sun6i_dma_set_addr_a733` and A733-specific interrupt
register accessors.
- Added `sun60i_a733_dma_config`, which ties all the refactored
functionality together for this specific hardware.
Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
---
drivers/dma/sun6i-dma.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index fb1c1a28744b..9585b4a9e00d 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -52,6 +52,15 @@
#define SUNXI_H3_SECURE_REG 0x20
#define SUNXI_H3_DMA_GATE 0x28
#define SUNXI_H3_DMA_GATE_ENABLE 0x4
+
+/*
+ * sun60i specific registers
+ */
+#define DMA_IRQ_EN_A733(x) ((x) * 0x40 + 0x134)
+#define DMA_IRQ_STAT_A733(x) ((x) * 0x40 + 0x138)
+
+#define DMA_IRQ_CHAN_NR_A733 1
+
/*
* Channels specific registers
*/
@@ -100,6 +109,8 @@
*/
#define SRC_HIGH_ADDR(x) (((x) & 0x3U) << 16)
#define DST_HIGH_ADDR(x) (((x) & 0x3U) << 18)
+#define SRC_HIGH_ADDR_32G(x) (((x) & 0x7U) << 11)
+#define DST_HIGH_ADDR_32G(x) (((x) & 0x7U) << 15)
/*
* Various hardware related defines
@@ -257,6 +268,23 @@ static inline void sun6i_dma_dump_com_regs(struct sun6i_dma_dev *sdev)
DMA_STAT, readl(sdev->base + DMA_STAT));
}
+static inline void sun6i_dma_dump_com_regs_a733(struct sun6i_dma_dev *sdev)
+{
+ int i;
+
+ for (i = 0; i < 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"
+ "\tpend(%04x): 0x%08x\n"
+ "\tstats(%04x): 0x%08x\n",
+ i,
+ DMA_IRQ_EN_A733(i), readl(sdev->base + DMA_IRQ_EN_A733(i)),
+ DMA_IRQ_STAT_A733(i), readl(sdev->base + DMA_IRQ_STAT_A733(i)),
+ DMA_STAT, readl(sdev->base + DMA_STAT));
+ }
+}
+
static inline void sun6i_dma_dump_chan_regs(struct sun6i_dma_dev *sdev,
struct sun6i_pchan *pchan)
{
@@ -360,20 +388,41 @@ static u32 sun6i_read_irq_en(struct sun6i_dma_dev *sdev, u32 chan_num)
return readl(sdev->base + DMA_IRQ_EN(chan_num));
}
+static u32 sun6i_read_irq_en_a733(struct sun6i_dma_dev *sdev, u32 chan_num)
+{
+ return readl(sdev->base + DMA_IRQ_EN_A733(chan_num));
+}
+
static void sun6i_write_irq_en(struct sun6i_dma_dev *sdev, u32 chan_num, u32 irq_val)
{
writel(irq_val, sdev->base + DMA_IRQ_EN(chan_num));
}
+
+static void sun6i_write_irq_en_a733(struct sun6i_dma_dev *sdev, u32 chan_num, u32 irq_val)
+{
+ writel(irq_val, sdev->base + DMA_IRQ_EN_A733(chan_num));
+}
+
static u32 sun6i_read_irq_stat(struct sun6i_dma_dev *sdev, u32 chan_num)
{
return readl(sdev->base + DMA_IRQ_STAT(chan_num));
}
+static u32 sun6i_read_irq_stat_a733(struct sun6i_dma_dev *sdev, u32 chan_num)
+{
+ return readl(sdev->base + DMA_IRQ_STAT_A733(chan_num));
+}
+
static void sun6i_write_irq_stat(struct sun6i_dma_dev *sdev, u32 chan_num, u32 status)
{
writel(status, sdev->base + DMA_IRQ_STAT(chan_num));
}
+static void sun6i_write_irq_stat_a733(struct sun6i_dma_dev *sdev, u32 chan_num, u32 status)
+{
+ writel(status, sdev->base + DMA_IRQ_STAT_A733(chan_num));
+}
+
static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan)
{
struct sun6i_desc *txd = pchan->desc;
@@ -694,6 +743,17 @@ static inline void sun6i_dma_set_addr_a100(struct sun6i_dma_dev *sdev,
DST_HIGH_ADDR(upper_32_bits(dst));
}
+static inline void sun6i_dma_set_addr_a733(struct sun6i_dma_dev *sdev,
+ struct sun6i_dma_lli *v_lli,
+ dma_addr_t src, dma_addr_t dst)
+{
+ v_lli->src = lower_32_bits(src);
+ v_lli->dst = lower_32_bits(dst);
+
+ v_lli->para |= SRC_HIGH_ADDR_32G(upper_32_bits(src)) |
+ DST_HIGH_ADDR_32G(upper_32_bits(dst));
+}
+
static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
size_t len, unsigned long flags)
@@ -1352,6 +1412,33 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
.has_mbus_clk = true,
};
+/*
+ * 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,
+ .set_addr = sun6i_dma_set_addr_a733,
+ .dump_com_regs = sun6i_dma_dump_com_regs_a733,
+ .read_irq_en = sun6i_read_irq_en_a733,
+ .write_irq_en = sun6i_write_irq_en_a733,
+ .read_irq_stat = sun6i_read_irq_stat_a733,
+ .write_irq_stat = sun6i_write_irq_stat_a733,
+ .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),
+ .num_channels_per_reg = DMA_IRQ_CHAN_NR_A733,
+ .has_mbus_clk = true,
+};
+
/*
* The V3s have only 8 physical channels, a maximum DRQ port id of 23,
* and a total of 24 usable source and destination endpoints.
@@ -1392,6 +1479,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.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH 5/5] dt-bindings: dma: sun50i-a64-dma: Update device tree bindings documentation for A733
2026-06-19 4:53 [PATCH 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller Yuanshen Cao
` (3 preceding siblings ...)
2026-06-19 4:53 ` [PATCH 4/5] dmaengine: sun6i-dma: Implement support for Allwinner A733 DMA controller Yuanshen Cao
@ 2026-06-19 4:53 ` Yuanshen Cao
2026-06-19 15:53 ` Frank Li
2026-06-19 15:55 ` Frank Li
4 siblings, 2 replies; 11+ messages in thread
From: Yuanshen Cao @ 2026-06-19 4:53 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
To complete the support for the A733 DMA controller, added
`allwinner,sun60i-a733-dma` to the list of compatible strings for
`allwinner,sun50i-a64-dma` dt-binding documentations..
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.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH 5/5] dt-bindings: dma: sun50i-a64-dma: Update device tree bindings documentation for A733
2026-06-19 4:53 ` [PATCH 5/5] dt-bindings: dma: sun50i-a64-dma: Update device tree bindings documentation for A733 Yuanshen Cao
@ 2026-06-19 15:53 ` Frank Li
2026-06-19 15:55 ` Frank Li
1 sibling, 0 replies; 11+ messages in thread
From: Frank Li @ 2026-06-19 15:53 UTC (permalink / raw)
To: Yuanshen Cao
Cc: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
On Fri, Jun 19, 2026 at 04:53:34AM +0000, Yuanshen Cao wrote:
Subject needn't talk about binding twice
dma: sun50i-a64-dma: Add a733 support
>
> To complete the support for the A733 DMA controller, added
> `allwinner,sun60i-a733-dma` to the list of compatible strings for
> `allwinner,sun50i-a64-dma` dt-binding documentations..
Add allwinner,sun60i-a733-dma compatible string. And list some differene
here to show why need it.
Frank
>
> 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.54.0
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 5/5] dt-bindings: dma: sun50i-a64-dma: Update device tree bindings documentation for A733
2026-06-19 4:53 ` [PATCH 5/5] dt-bindings: dma: sun50i-a64-dma: Update device tree bindings documentation for A733 Yuanshen Cao
2026-06-19 15:53 ` Frank Li
@ 2026-06-19 15:55 ` Frank Li
1 sibling, 0 replies; 11+ messages in thread
From: Frank Li @ 2026-06-19 15:55 UTC (permalink / raw)
To: Yuanshen Cao
Cc: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, dmaengine, linux-arm-kernel, linux-sunxi,
linux-kernel, devicetree
On Fri, Jun 19, 2026 at 04:53:34AM +0000, Yuanshen Cao wrote:
>
> To complete the support for the A733 DMA controller, added
> `allwinner,sun60i-a733-dma` to the list of compatible strings for
> `allwinner,sun50i-a64-dma` dt-binding documentations..
>
> Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
> ---
Sorry, this patch should be moved to where before your driver, which use it
Frank
> 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.54.0
>
^ permalink raw reply [flat|nested] 11+ messages in thread