* [PATCH 0/3] Add Amlogic general DMA
@ 2025-12-16 8:03 Xianwei Zhao via B4 Relay
2025-12-16 8:03 ` [PATCH 1/3] dt-bindings: dma: " Xianwei Zhao via B4 Relay
` (2 more replies)
0 siblings, 3 replies; 11+ messages in thread
From: Xianwei Zhao via B4 Relay @ 2025-12-16 8:03 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Kees Cook, Gustavo A. R. Silva
Cc: linux-amlogic, dmaengine, devicetree, linux-kernel,
linux-hardening, Xianwei Zhao
Add DMA driver and bindigns for the Amlogic SoCs.
Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com>
---
Xianwei Zhao (3):
dt-bindings: dma: Add Amlogic general DMA
dma: amlogic: Add general DMA driver for SoCs
MAINTAINERS: Add an entry for Amlogic DMA driver
.../bindings/dma/amlogic,general-dma.yaml | 70 +++
MAINTAINERS | 7 +
drivers/dma/Kconfig | 8 +
drivers/dma/Makefile | 1 +
drivers/dma/amlogic-dma.c | 567 +++++++++++++++++++++
5 files changed, 653 insertions(+)
---
base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
change-id: 20251215-amlogic-dma-79477d5cd264
Best regards,
--
Xianwei Zhao <xianwei.zhao@amlogic.com>
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH 1/3] dt-bindings: dma: Add Amlogic general DMA 2025-12-16 8:03 [PATCH 0/3] Add Amlogic general DMA Xianwei Zhao via B4 Relay @ 2025-12-16 8:03 ` Xianwei Zhao via B4 Relay 2025-12-16 8:45 ` Krzysztof Kozlowski 2025-12-16 8:03 ` [PATCH 2/3] dma: amlogic: Add general DMA driver for SoCs Xianwei Zhao via B4 Relay 2025-12-16 8:03 ` [PATCH 3/3] MAINTAINERS: Add an entry for Amlogic DMA driver Xianwei Zhao via B4 Relay 2 siblings, 1 reply; 11+ messages in thread From: Xianwei Zhao via B4 Relay @ 2025-12-16 8:03 UTC (permalink / raw) To: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Kees Cook, Gustavo A. R. Silva Cc: linux-amlogic, dmaengine, devicetree, linux-kernel, linux-hardening, Xianwei Zhao From: Xianwei Zhao <xianwei.zhao@amlogic.com> Add documentation describing the Amlogic general DMA. Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com> --- .../bindings/dma/amlogic,general-dma.yaml | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/Documentation/devicetree/bindings/dma/amlogic,general-dma.yaml b/Documentation/devicetree/bindings/dma/amlogic,general-dma.yaml new file mode 100644 index 000000000000..8b9cec9b8da0 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/amlogic,general-dma.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/amlogic,general-dma.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic general DMA controller + +description: | + This is a general-purpose peripheral DMA controller. It currently supports + major peripherals including I2C, I3C, PIO, and CAN-BUS. Transmit and receive + for the same peripheral use two separate channels, controlled by different + register sets. I2C and I3C transfer data in 1-byte units, while PIO and + CAN-BUS transfer data in 4-byte units. From the controller’s perspective, + there is no significant difference. + +maintainers: + - Xianwei Zhao <xianwei.zhao@amlogic.com> + +properties: + compatible: + const: amlogic,general-dma + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: sys + + '#dma-cells': + const: 2 + + dma-channels: + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 64 + +required: + - compatible + - reg + - interrupts + - clocks + - '#dma-cells' + - dma-channels + +allOf: + - $ref: dma-controller.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + apb { + #address-cells = <2>; + #size-cells = <2>; + dma-controller@fe400000{ + compatible = "amlogic,general-dma"; + reg = <0x0 0xfe400000 0x0 0x4000>; + interrupts = <GIC_SPI 35 IRQ_TYPE_EDGE_RISING>; + clocks = <&clkc 45>; + #dma-cells = <2>; + dma-channels = <28>; + }; + }; -- 2.52.0 _______________________________________________ linux-amlogic mailing list linux-amlogic@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-amlogic ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/3] dt-bindings: dma: Add Amlogic general DMA 2025-12-16 8:03 ` [PATCH 1/3] dt-bindings: dma: " Xianwei Zhao via B4 Relay @ 2025-12-16 8:45 ` Krzysztof Kozlowski 2025-12-19 1:58 ` Xianwei Zhao 0 siblings, 1 reply; 11+ messages in thread From: Krzysztof Kozlowski @ 2025-12-16 8:45 UTC (permalink / raw) To: xianwei.zhao, Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Kees Cook, Gustavo A. R. Silva Cc: linux-amlogic, dmaengine, devicetree, linux-kernel, linux-hardening On 16/12/2025 09:03, Xianwei Zhao via B4 Relay wrote: > From: Xianwei Zhao <xianwei.zhao@amlogic.com> > > Add documentation describing the Amlogic general DMA. > > Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com> > --- > .../bindings/dma/amlogic,general-dma.yaml | 70 ++++++++++++++++++++++ > 1 file changed, 70 insertions(+) > > diff --git a/Documentation/devicetree/bindings/dma/amlogic,general-dma.yaml b/Documentation/devicetree/bindings/dma/amlogic,general-dma.yaml > new file mode 100644 > index 000000000000..8b9cec9b8da0 > --- /dev/null > +++ b/Documentation/devicetree/bindings/dma/amlogic,general-dma.yaml > @@ -0,0 +1,70 @@ > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause > +%YAML 1.2 > +--- > +$id: http://devicetree.org/schemas/dma/amlogic,general-dma.yaml# > +$schema: http://devicetree.org/meta-schemas/core.yaml# > + > +title: Amlogic general DMA controller > + > +description: | > + This is a general-purpose peripheral DMA controller. It currently supports > + major peripherals including I2C, I3C, PIO, and CAN-BUS. Transmit and receive > + for the same peripheral use two separate channels, controlled by different > + register sets. I2C and I3C transfer data in 1-byte units, while PIO and > + CAN-BUS transfer data in 4-byte units. From the controller’s perspective, > + there is no significant difference. > + > +maintainers: > + - Xianwei Zhao <xianwei.zhao@amlogic.com> > + > +properties: > + compatible: > + const: amlogic,general-dma I don't know what you mean by "general" but it feels like wildcard, not SoC, so that would be a no-go. Read writing bindings or several talks about this. Best regards, Krzysztof _______________________________________________ linux-amlogic mailing list linux-amlogic@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-amlogic ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/3] dt-bindings: dma: Add Amlogic general DMA 2025-12-16 8:45 ` Krzysztof Kozlowski @ 2025-12-19 1:58 ` Xianwei Zhao 0 siblings, 0 replies; 11+ messages in thread From: Xianwei Zhao @ 2025-12-19 1:58 UTC (permalink / raw) To: Krzysztof Kozlowski, Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Kees Cook, Gustavo A. R. Silva Cc: linux-amlogic, dmaengine, devicetree, linux-kernel, linux-hardening Hi Krzysztof, Thanks for your reply. On 2025/12/16 16:45, Krzysztof Kozlowski wrote: > I don't know what you mean by "general" but it feels like wildcard, not > SoC, so that would be a no-go. Read writing bindings or several talks > about this. I will named it using the name of SoC in V2. _______________________________________________ linux-amlogic mailing list linux-amlogic@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-amlogic ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/3] dma: amlogic: Add general DMA driver for SoCs 2025-12-16 8:03 [PATCH 0/3] Add Amlogic general DMA Xianwei Zhao via B4 Relay 2025-12-16 8:03 ` [PATCH 1/3] dt-bindings: dma: " Xianwei Zhao via B4 Relay @ 2025-12-16 8:03 ` Xianwei Zhao via B4 Relay 2025-12-16 8:46 ` Krzysztof Kozlowski ` (2 more replies) 2025-12-16 8:03 ` [PATCH 3/3] MAINTAINERS: Add an entry for Amlogic DMA driver Xianwei Zhao via B4 Relay 2 siblings, 3 replies; 11+ messages in thread From: Xianwei Zhao via B4 Relay @ 2025-12-16 8:03 UTC (permalink / raw) To: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Kees Cook, Gustavo A. R. Silva Cc: linux-amlogic, dmaengine, devicetree, linux-kernel, linux-hardening, Xianwei Zhao From: Xianwei Zhao <xianwei.zhao@amlogic.com> Amlogic SoCs include a general-purpose DMA controller that can be used by multiple peripherals, such as I2C PIO and I3C. Each peripheral group is associated with a dedicated DMA channel in hardware. Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com> --- drivers/dma/Kconfig | 8 + drivers/dma/Makefile | 1 + drivers/dma/amlogic-dma.c | 567 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 576 insertions(+) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 8bb0a119ecd4..fc7f70e22c22 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -85,6 +85,14 @@ config AMCC_PPC440SPE_ADMA help Enable support for the AMCC PPC440SPe RAID engines. +config AMLOGIC_DMA + tristate "Amlogic genneral DMA support" + depends on ARCH_MESON + select DMA_ENGINE + select REGMAP_MMIO + help + Enable support for the Amlogic general DMA engines. + config APPLE_ADMAC tristate "Apple ADMAC support" depends on ARCH_APPLE || COMPILE_TEST diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index a54d7688392b..fc28dade5b69 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_DMATEST) += dmatest.o obj-$(CONFIG_ALTERA_MSGDMA) += altera-msgdma.o obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/ +obj-$(CONFIG_AMLOGIC_DMA) += amlogic-dma.o obj-$(CONFIG_APPLE_ADMAC) += apple-admac.o obj-$(CONFIG_ARM_DMA350) += arm-dma350.o obj-$(CONFIG_AT_HDMAC) += at_hdmac.o diff --git a/drivers/dma/amlogic-dma.c b/drivers/dma/amlogic-dma.c new file mode 100644 index 000000000000..40099002d558 --- /dev/null +++ b/drivers/dma/amlogic-dma.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (C) 2025 Amlogic, Inc. All rights reserved + * Author: Xianwei Zhao <xianwei.zhao@amlogic.com> + */ + +#include <linux/init.h> +#include <linux/bitfield.h> +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/dmaengine.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_dma.h> +#include <linux/list.h> +#include <linux/regmap.h> +#include <asm/irq.h> +#include "dmaengine.h" + +#define RCH_REG_BASE 0x0 +#define WCH_REG_BASE 0x2000 +/* + * Each rch (read from memory) REG offset Rch_offset 0x0 each channel total 0x40 + * rch addr = DMA_base + Rch_offset+ chan_id * 0x40 + reg_offset + */ +#define RCH_READY 0x0 +#define RCH_STATUS 0x4 +#define RCH_CFG 0x8 +#define CFG_CLEAR BIT(25) +#define CFG_PAUSE BIT(26) +#define CFG_ENABLE BIT(27) +#define CFG_DONE BIT(28) +#define RCH_ADDR 0xc +#define RCH_LEN 0x10 +#define RCH_RD_LEN 0x14 +#define RCH_PRT 0x18 +#define RCH_SYCN_STAT 0x1c +#define RCH_ADDR_LOW 0x20 +#define RCH_ADDR_HIGH 0x24 +/* if work on 64, it work with RCH_PRT */ +#define RCH_PTR_HIGH 0x28 + +/* + * Each wch (write to memory) REG offset Wch_offset 0x2000 each channel total 0x40 + * wch addr = DMA_base + Wch_offset+ chan_id * 0x40 + reg_offset + */ +#define WCH_READY 0x0 +#define WCH_TOTAL_LEN 0x4 +#define WCH_CFG 0x8 +#define WCH_ADDR 0xc +#define WCH_LEN 0x10 +#define WCH_RD_LEN 0x14 +#define WCH_PRT 0x18 +#define WCH_CMD_CNT 0x1c +#define WCH_ADDR_LOW 0x20 +#define WCH_ADDR_HIGH 0x24 +/* if work on 64, it work with RCH_PRT */ +#define WCH_PTR_HIGH 0x28 + +/* DMA controller reg */ +#define RCH_INT_MASK 0x1000 +#define WCH_INT_MASK 0x1004 +#define CLEAR_W_BATCH 0x1014 +#define CLEAR_RCH 0x1024 +#define CLEAR_WCH 0x1028 +#define RCH_ACTIVE 0x1038 +#define WCH_ACTIVE 0x103C +#define RCH_DONE 0x104C +#define WCH_DONE 0x1050 +#define RCH_ERR 0x1060 +#define RCH_LEN_ERR 0x1064 +#define WCH_ERR 0x1068 +#define DMA_BATCH_END 0x1078 +#define WCH_EOC_DONE 0x1088 +#define WDMA_RESP_ERR 0x1098 +#define UPT_PKT_SYNC 0x10A8 +#define RCHN_CFG 0x10AC +#define WCHN_CFG 0x10B0 +#define MEM_PD_CFG 0x10B4 +#define MEM_BUS_CFG 0x10B8 +#define DMA_GMV_CFG 0x10BC +#define DMA_GMR_CFG 0x10C0 + +#define DMA_MAX_LINK 8 +#define MAX_CHAN_ID 32 +#define SG_MAX_LEN ((1 << 27) - 1) + +struct aml_dma_sg_link { +#define LINK_LEN GENMASK(26, 0) +#define LINK_IRQ BIT(27) +#define LINK_EOC BIT(28) +#define LINK_LOOP BIT(29) +#define LINK_ERR BIT(30) +#define LINK_OWNER BIT(31) + u32 ctl; + u64 address; + u32 revered; +} __packed; + +struct aml_dma_chan { + struct dma_chan chan; + struct dma_async_tx_descriptor desc; + struct aml_dma_dev *aml_dma; + struct aml_dma_sg_link *sg_link; + dma_addr_t sg_link_phys; + int sg_link_cnt; + int data_len; + enum dma_status status; + enum dma_transfer_direction direction; + int chan_id; + /* reg_base (direction + chan_id) */ + int reg_offs; +}; + +struct aml_dma_dev { + struct dma_device dma_device; + void __iomem *base; + struct regmap *regmap; + struct clk *clk; + int irq; + struct platform_device *pdev; + struct aml_dma_chan *aml_rch[MAX_CHAN_ID]; + struct aml_dma_chan *aml_wch[MAX_CHAN_ID]; + unsigned int chan_nr; + unsigned int chan_used; + struct aml_dma_chan aml_chans[]__counted_by(chan_ns); +}; + +static struct aml_dma_chan *to_aml_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct aml_dma_chan, chan); +} + +static dma_cookie_t aml_dma_tx_submit(struct dma_async_tx_descriptor *tx) +{ + return dma_cookie_assign(tx); +} + +static int aml_dma_alloc_chan_resources(struct dma_chan *chan) +{ + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; + + aml_chan->sg_link = dma_alloc_coherent(aml_dma->dma_device.dev, + sizeof(struct aml_dma_sg_link) * DMA_MAX_LINK, + &aml_chan->sg_link_phys, GFP_KERNEL); + if (!aml_chan->sg_link) + return -ENOMEM; + + /* offset is the same RCH_CFG and WCH_CFG */ + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_CLEAR, CFG_CLEAR); + aml_chan->status = DMA_COMPLETE; + dma_async_tx_descriptor_init(&aml_chan->desc, chan); + aml_chan->desc.tx_submit = aml_dma_tx_submit; + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_CLEAR, 0); + + return 0; +} + +static void aml_dma_free_chan_resources(struct dma_chan *chan) +{ + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; + + aml_chan->status = DMA_COMPLETE; + dma_free_coherent(aml_dma->dma_device.dev, + sizeof(struct aml_dma_sg_link) * DMA_MAX_LINK, + aml_chan->sg_link, aml_chan->sg_link_phys); +} + +/* DMA transfer state update how many data reside it */ +static enum dma_status aml_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; + u32 residue, done; + + regmap_read(aml_dma->regmap, aml_chan->reg_offs + RCH_RD_LEN, &done); + residue = aml_chan->data_len - done; + dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie, + residue); + + return aml_chan->status; +} + +static struct dma_async_tx_descriptor *aml_dma_prep_slave_sg + (struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; + struct aml_dma_sg_link *sg_link; + struct scatterlist *sg; + int idx = 0; + u32 reg, chan_id; + u32 i; + + if (aml_chan->direction != direction) { + dev_err(aml_dma->dma_device.dev, "direction not support\n"); + return NULL; + } + + switch (aml_chan->status) { + case DMA_IN_PROGRESS: + /* support multiple prep before pending */ + idx = aml_chan->sg_link_cnt; + + break; + case DMA_COMPLETE: + aml_chan->data_len = 0; + chan_id = aml_chan->chan_id; + reg = (direction == DMA_DEV_TO_MEM) ? WCH_INT_MASK : RCH_INT_MASK; + regmap_update_bits(aml_dma->regmap, reg, BIT(chan_id), BIT(chan_id)); + + break; + default: + dev_err(aml_dma->dma_device.dev, "status error\n"); + return NULL; + } + + if (sg_len + idx > DMA_MAX_LINK) { + dev_err(aml_dma->dma_device.dev, + "maximum number of sg exceeded: %d > %d\n", + sg_len, DMA_MAX_LINK); + aml_chan->status = DMA_ERROR; + return NULL; + } + + aml_chan->status = DMA_IN_PROGRESS; + + for_each_sg(sgl, sg, sg_len, i) { + if (sg_dma_len(sg) > SG_MAX_LEN) { + dev_err(aml_dma->dma_device.dev, + "maximum bytes exceeded: %d > %d\n", + sg_dma_len(sg), SG_MAX_LEN); + aml_chan->status = DMA_ERROR; + return NULL; + } + sg_link = &aml_chan->sg_link[idx++]; + /* set dma address and len to sglink*/ + sg_link->address = sg->dma_address; + sg_link->ctl = FIELD_PREP(LINK_LEN, sg_dma_len(sg)); + + aml_chan->data_len += sg_dma_len(sg); + } + aml_chan->sg_link_cnt = idx; + + return &aml_chan->desc; +} + +static int aml_dma_pause_chan(struct dma_chan *chan) +{ + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; + + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_PAUSE, CFG_PAUSE); + aml_chan->status = DMA_PAUSED; + + return 0; +} + +static int aml_dma_resume_chan(struct dma_chan *chan) +{ + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; + + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_PAUSE, 0); + aml_chan->status = DMA_IN_PROGRESS; + + return 0; +} + +static int aml_dma_terminate_all(struct dma_chan *chan) +{ + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; + int chan_id = aml_chan->chan_id; + + aml_dma_pause_chan(chan); + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_CLEAR, CFG_CLEAR); + + if (aml_chan->direction == DMA_MEM_TO_DEV) + regmap_update_bits(aml_dma->regmap, RCH_INT_MASK, BIT(chan_id), BIT(chan_id)); + else if (aml_chan->direction == DMA_DEV_TO_MEM) + regmap_update_bits(aml_dma->regmap, WCH_INT_MASK, BIT(chan_id), BIT(chan_id)); + + aml_chan->status = DMA_COMPLETE; + + return 0; +} + +static void aml_dma_enable_chan(struct dma_chan *chan) +{ + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; + struct aml_dma_sg_link *sg_link; + int chan_id = aml_chan->chan_id; + int idx = aml_chan->sg_link_cnt - 1; + + /* the last sg set eoc flag */ + sg_link = &aml_chan->sg_link[idx]; + sg_link->ctl |= LINK_EOC; + dma_wmb(); + if (aml_chan->direction == DMA_MEM_TO_DEV) { + regmap_write(aml_dma->regmap, aml_chan->reg_offs + RCH_ADDR, + aml_chan->sg_link_phys); + regmap_write(aml_dma->regmap, aml_chan->reg_offs + RCH_LEN, aml_chan->data_len); + regmap_update_bits(aml_dma->regmap, RCH_INT_MASK, BIT(chan_id), 0); + /* for rch (tx) need set cfg 0 to trigger start */ + regmap_write(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, 0); + } else if (aml_chan->direction == DMA_DEV_TO_MEM) { + regmap_write(aml_dma->regmap, aml_chan->reg_offs + WCH_ADDR, + aml_chan->sg_link_phys); + regmap_write(aml_dma->regmap, aml_chan->reg_offs + WCH_LEN, aml_chan->data_len); + regmap_update_bits(aml_dma->regmap, WCH_INT_MASK, BIT(chan_id), 0); + } +} + +static irqreturn_t aml_dma_interrupt_handler(int irq, void *dev_id) +{ + struct aml_dma_dev *aml_dma = dev_id; + struct aml_dma_chan *aml_chan; + u32 done, eoc_done, err, err_l, end; + int i = 0; + + /* deal with rch normal complete and error */ + regmap_read(aml_dma->regmap, RCH_DONE, &done); + regmap_read(aml_dma->regmap, RCH_ERR, &err); + regmap_read(aml_dma->regmap, RCH_LEN_ERR, &err_l); + err = err | err_l; + + done = done | err; + + i = 0; + while (done) { + if (done & 1) { + aml_chan = aml_dma->aml_rch[i]; + regmap_write(aml_dma->regmap, CLEAR_RCH, BIT(aml_chan->chan_id)); + if (!aml_chan) { + dev_err(aml_dma->dma_device.dev, "idx %d rch not initialized\n", i); + continue; + } + aml_chan->status = (err & (1 << i)) ? DMA_ERROR : DMA_COMPLETE; + /* Make sure to use this for initialization */ + if (aml_chan->desc.cookie >= DMA_MIN_COOKIE) + dma_cookie_complete(&aml_chan->desc); + dmaengine_desc_get_callback_invoke(&aml_chan->desc, NULL); + } + i++; + done = done >> 1; + } + + /* deal with wch normal complete and error */ + regmap_read(aml_dma->regmap, DMA_BATCH_END, &end); + if (end) + regmap_write(aml_dma->regmap, CLEAR_W_BATCH, end); + + regmap_read(aml_dma->regmap, WCH_DONE, &done); + regmap_read(aml_dma->regmap, WCH_EOC_DONE, &eoc_done); + done = done | eoc_done; + + regmap_read(aml_dma->regmap, WCH_ERR, &err); + regmap_read(aml_dma->regmap, WDMA_RESP_ERR, &err_l); + err = err | err_l; + + done = done | err; + i = 0; + while (done) { + if (done & 1) { + aml_chan = aml_dma->aml_wch[i]; + regmap_write(aml_dma->regmap, CLEAR_WCH, BIT(aml_chan->chan_id)); + if (!aml_chan) { + dev_err(aml_dma->dma_device.dev, "idx %d wch not initialized\n", i); + continue; + } + aml_chan->status = (err & (1 << i)) ? DMA_ERROR : DMA_COMPLETE; + if (aml_chan->desc.cookie >= DMA_MIN_COOKIE) + dma_cookie_complete(&aml_chan->desc); + dmaengine_desc_get_callback_invoke(&aml_chan->desc, NULL); + } + i++; + done = done >> 1; + } + + return IRQ_HANDLED; +} + +static struct dma_chan *aml_of_dma_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) +{ + struct aml_dma_dev *aml_dma = (struct aml_dma_dev *)ofdma->of_dma_data; + struct aml_dma_chan *aml_chan = NULL; + u32 type; + u32 phy_chan_id; + + if (dma_spec->args_count != 2) + return NULL; + + type = dma_spec->args[0]; + phy_chan_id = dma_spec->args[1]; + + if (phy_chan_id >= MAX_CHAN_ID) + return NULL; + + if (type == 0) { + aml_chan = aml_dma->aml_rch[phy_chan_id]; + if (!aml_chan) { + if (aml_dma->chan_used >= aml_dma->chan_nr) { + dev_err(aml_dma->dma_device.dev, "some dma clients err used\n"); + return NULL; + } + aml_chan = &aml_dma->aml_chans[aml_dma->chan_used]; + aml_dma->chan_used++; + aml_chan->direction = DMA_MEM_TO_DEV; + aml_chan->chan_id = phy_chan_id; + aml_chan->reg_offs = RCH_REG_BASE + 0x40 * aml_chan->chan_id; + aml_dma->aml_rch[phy_chan_id] = aml_chan; + } + } else if (type == 1) { + aml_chan = aml_dma->aml_wch[phy_chan_id]; + if (!aml_chan) { + if (aml_dma->chan_used >= aml_dma->chan_nr) { + dev_err(aml_dma->dma_device.dev, "some dma clients err used\n"); + return NULL; + } + aml_chan = &aml_dma->aml_chans[aml_dma->chan_used]; + aml_dma->chan_used++; + aml_chan->direction = DMA_DEV_TO_MEM; + aml_chan->chan_id = phy_chan_id; + aml_chan->reg_offs = WCH_REG_BASE + 0x40 * aml_chan->chan_id; + aml_dma->aml_wch[phy_chan_id] = aml_chan; + } + } else { + dev_err(aml_dma->dma_device.dev, "type %d not supported\n", type); + return NULL; + } + + return dma_get_slave_channel(&aml_chan->chan); +} + +static int aml_dma_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct dma_device *dma_dev; + struct aml_dma_dev *aml_dma; + int ret, i, len; + u32 chan_nr; + + const struct regmap_config aml_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x3000, + }; + + ret = of_property_read_u32(np, "dma-channels", &chan_nr); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to read dma-channels\n"); + + len = sizeof(*aml_dma) + sizeof(struct aml_dma_chan) * chan_nr; + aml_dma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!aml_dma) + return -ENOMEM; + + aml_dma->chan_nr = chan_nr; + + aml_dma->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(aml_dma->base)) + return PTR_ERR(aml_dma->base); + + aml_dma->regmap = devm_regmap_init_mmio(&pdev->dev, aml_dma->base, + &aml_regmap_config); + if (IS_ERR_OR_NULL(aml_dma->regmap)) + return PTR_ERR(aml_dma->regmap); + + aml_dma->clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(aml_dma->clk)) + return PTR_ERR(aml_dma->clk); + + aml_dma->irq = platform_get_irq(pdev, 0); + + aml_dma->pdev = pdev; + aml_dma->dma_device.dev = &pdev->dev; + + dma_dev = &aml_dma->dma_device; + INIT_LIST_HEAD(&dma_dev->channels); + + /* Initialize channel parameters */ + for (i = 0; i < chan_nr; i++) { + struct aml_dma_chan *aml_chan = &aml_dma->aml_chans[i]; + + aml_chan->aml_dma = aml_dma; + aml_chan->chan.device = &aml_dma->dma_device; + dma_cookie_init(&aml_chan->chan); + + /* Add the channel to aml_chan list */ + list_add_tail(&aml_chan->chan.device_node, + &aml_dma->dma_device.channels); + } + aml_dma->chan_used = 0; + + dma_set_max_seg_size(dma_dev->dev, SG_MAX_LEN); + + dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); + dma_dev->device_alloc_chan_resources = aml_dma_alloc_chan_resources; + dma_dev->device_free_chan_resources = aml_dma_free_chan_resources; + dma_dev->device_tx_status = aml_dma_tx_status; + dma_dev->device_prep_slave_sg = aml_dma_prep_slave_sg; + + dma_dev->device_pause = aml_dma_pause_chan; + dma_dev->device_resume = aml_dma_resume_chan; + dma_dev->device_terminate_all = aml_dma_terminate_all; + dma_dev->device_issue_pending = aml_dma_enable_chan; + /* PIO 4 bytes and I2C 1 byte */ + dma_dev->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES | DMA_SLAVE_BUSWIDTH_1_BYTE); + dma_dev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + + ret = dmaenginem_async_device_register(dma_dev); + if (ret) + return ret; + + ret = of_dma_controller_register(np, aml_of_dma_xlate, aml_dma); + if (ret) + return ret; + + regmap_write(aml_dma->regmap, RCH_INT_MASK, 0xffffffff); + regmap_write(aml_dma->regmap, WCH_INT_MASK, 0xffffffff); + + ret = devm_request_irq(&pdev->dev, aml_dma->irq, aml_dma_interrupt_handler, + IRQF_SHARED, dev_name(&pdev->dev), aml_dma); + if (ret) + return ret; + + dev_info(aml_dma->dma_device.dev, "initialized\n"); + + return 0; +} + +static const struct of_device_id aml_dma_ids[] = { + { .compatible = "amlogic,general-dma", }, + {}, +}; +MODULE_DEVICE_TABLE(of, aml_dma_ids); + +static struct platform_driver aml_dma_driver = { + .probe = aml_dma_probe, + .driver = { + .name = "aml-dma", + .of_match_table = aml_dma_ids, + }, +}; + +module_platform_driver(aml_dma_driver); + +MODULE_DESCRIPTION("GENERAL DMA driver for Amlogic"); +MODULE_AUTHOR("Xianwei Zhao <xianwei.zhao@amlogic.com>"); +MODULE_ALIAS("platform:aml-dma"); +MODULE_LICENSE("GPL"); -- 2.52.0 _______________________________________________ linux-amlogic mailing list linux-amlogic@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-amlogic ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/3] dma: amlogic: Add general DMA driver for SoCs 2025-12-16 8:03 ` [PATCH 2/3] dma: amlogic: Add general DMA driver for SoCs Xianwei Zhao via B4 Relay @ 2025-12-16 8:46 ` Krzysztof Kozlowski 2025-12-19 2:04 ` Xianwei Zhao 2025-12-16 9:09 ` neil.armstrong 2025-12-20 18:21 ` kernel test robot 2 siblings, 1 reply; 11+ messages in thread From: Krzysztof Kozlowski @ 2025-12-16 8:46 UTC (permalink / raw) To: xianwei.zhao, Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Kees Cook, Gustavo A. R. Silva Cc: linux-amlogic, dmaengine, devicetree, linux-kernel, linux-hardening On 16/12/2025 09:03, Xianwei Zhao via B4 Relay wrote: > From: Xianwei Zhao <xianwei.zhao@amlogic.com> > > Amlogic SoCs include a general-purpose DMA controller that can be used > by multiple peripherals, such as I2C PIO and I3C. Each peripheral group > is associated with a dedicated DMA channel in hardware. > > Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com> > --- > drivers/dma/Kconfig | 8 + > drivers/dma/Makefile | 1 + > drivers/dma/amlogic-dma.c | 567 ++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 576 insertions(+) > > diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig > index 8bb0a119ecd4..fc7f70e22c22 100644 > --- a/drivers/dma/Kconfig > +++ b/drivers/dma/Kconfig > @@ -85,6 +85,14 @@ config AMCC_PPC440SPE_ADMA > help > Enable support for the AMCC PPC440SPe RAID engines. > > +config AMLOGIC_DMA > + tristate "Amlogic genneral DMA support" > + depends on ARCH_MESON Missing compile test. > + select DMA_ENGINE > + select REGMAP_MMIO > + help > + Enable support for the Amlogic general DMA engines. > + > + > + ret = of_dma_controller_register(np, aml_of_dma_xlate, aml_dma); > + if (ret) > + return ret; > + > + regmap_write(aml_dma->regmap, RCH_INT_MASK, 0xffffffff); > + regmap_write(aml_dma->regmap, WCH_INT_MASK, 0xffffffff); > + > + ret = devm_request_irq(&pdev->dev, aml_dma->irq, aml_dma_interrupt_handler, > + IRQF_SHARED, dev_name(&pdev->dev), aml_dma); > + if (ret) > + return ret; > + > + dev_info(aml_dma->dma_device.dev, "initialized\n"); Useless message, drop. This does not look like useful printk message. Drivers should be silent on success: https://elixir.bootlin.com/linux/v6.15-rc7/source/Documentation/process/coding-style.rst#L913 https://elixir.bootlin.com/linux/v6.15-rc7/source/Documentation/process/debugging/driver_development_debugging_guide.rst#L79 > + > + return 0; > +} > + > +static const struct of_device_id aml_dma_ids[] = { > + { .compatible = "amlogic,general-dma", }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, aml_dma_ids); > + > +static struct platform_driver aml_dma_driver = { > + .probe = aml_dma_probe, > + .driver = { > + .name = "aml-dma", > + .of_match_table = aml_dma_ids, > + }, > +}; > + > +module_platform_driver(aml_dma_driver); > + > +MODULE_DESCRIPTION("GENERAL DMA driver for Amlogic"); > +MODULE_AUTHOR("Xianwei Zhao <xianwei.zhao@amlogic.com>"); > +MODULE_ALIAS("platform:aml-dma"); > +MODULE_LICENSE("GPL"); > Best regards, Krzysztof _______________________________________________ linux-amlogic mailing list linux-amlogic@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-amlogic ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/3] dma: amlogic: Add general DMA driver for SoCs 2025-12-16 8:46 ` Krzysztof Kozlowski @ 2025-12-19 2:04 ` Xianwei Zhao 0 siblings, 0 replies; 11+ messages in thread From: Xianwei Zhao @ 2025-12-19 2:04 UTC (permalink / raw) To: Krzysztof Kozlowski, Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Kees Cook, Gustavo A. R. Silva Cc: linux-amlogic, dmaengine, devicetree, linux-kernel, linux-hardening Hi Krzysztof, Thanks for your review. On 2025/12/16 16:46, Krzysztof Kozlowski wrote: > [ EXTERNAL EMAIL ] > > On 16/12/2025 09:03, Xianwei Zhao via B4 Relay wrote: >> From: Xianwei Zhao <xianwei.zhao@amlogic.com> >> >> Amlogic SoCs include a general-purpose DMA controller that can be used >> by multiple peripherals, such as I2C PIO and I3C. Each peripheral group >> is associated with a dedicated DMA channel in hardware. >> >> Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com> >> --- >> drivers/dma/Kconfig | 8 + >> drivers/dma/Makefile | 1 + >> drivers/dma/amlogic-dma.c | 567 ++++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 576 insertions(+) >> >> diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig >> index 8bb0a119ecd4..fc7f70e22c22 100644 >> --- a/drivers/dma/Kconfig >> +++ b/drivers/dma/Kconfig >> @@ -85,6 +85,14 @@ config AMCC_PPC440SPE_ADMA >> help >> Enable support for the AMCC PPC440SPe RAID engines. >> >> +config AMLOGIC_DMA >> + tristate "Amlogic genneral DMA support" >> + depends on ARCH_MESON > > Missing compile test. > Will Add it. depends on ARCH_MESON || COMPILE_TEST >> + select DMA_ENGINE >> + select REGMAP_MMIO >> + help >> + Enable support for the Amlogic general DMA engines. >> + > > > >> + >> + ret = of_dma_controller_register(np, aml_of_dma_xlate, aml_dma); >> + if (ret) >> + return ret; >> + >> + regmap_write(aml_dma->regmap, RCH_INT_MASK, 0xffffffff); >> + regmap_write(aml_dma->regmap, WCH_INT_MASK, 0xffffffff); >> + >> + ret = devm_request_irq(&pdev->dev, aml_dma->irq, aml_dma_interrupt_handler, >> + IRQF_SHARED, dev_name(&pdev->dev), aml_dma); >> + if (ret) >> + return ret; >> + >> + dev_info(aml_dma->dma_device.dev, "initialized\n"); > > Useless message, drop. This does not look like useful printk message. > Drivers should be silent on success: > https://elixir.bootlin.com/linux/v6.15-rc7/source/Documentation/process/coding-style.rst#L913 > https://elixir.bootlin.com/linux/v6.15-rc7/source/Documentation/process/debugging/driver_development_debugging_guide.rst#L79 > Will drop it. >> + >> + return 0; >> +} >> + >> +static const struct of_device_id aml_dma_ids[] = { >> + { .compatible = "amlogic,general-dma", }, >> + {}, >> +}; >> +MODULE_DEVICE_TABLE(of, aml_dma_ids); >> + >> +static struct platform_driver aml_dma_driver = { >> + .probe = aml_dma_probe, >> + .driver = { >> + .name = "aml-dma", >> + .of_match_table = aml_dma_ids, >> + }, >> +}; >> + >> +module_platform_driver(aml_dma_driver); >> + >> +MODULE_DESCRIPTION("GENERAL DMA driver for Amlogic"); >> +MODULE_AUTHOR("Xianwei Zhao <xianwei.zhao@amlogic.com>"); >> +MODULE_ALIAS("platform:aml-dma"); >> +MODULE_LICENSE("GPL"); >> > > > Best regards, > Krzysztof _______________________________________________ linux-amlogic mailing list linux-amlogic@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-amlogic ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/3] dma: amlogic: Add general DMA driver for SoCs 2025-12-16 8:03 ` [PATCH 2/3] dma: amlogic: Add general DMA driver for SoCs Xianwei Zhao via B4 Relay 2025-12-16 8:46 ` Krzysztof Kozlowski @ 2025-12-16 9:09 ` neil.armstrong 2025-12-19 2:35 ` Xianwei Zhao 2025-12-20 18:21 ` kernel test robot 2 siblings, 1 reply; 11+ messages in thread From: neil.armstrong @ 2025-12-16 9:09 UTC (permalink / raw) To: xianwei.zhao, Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Kees Cook, Gustavo A. R. Silva Cc: linux-amlogic, dmaengine, devicetree, linux-kernel, linux-hardening Hi, On 12/16/25 09:03, Xianwei Zhao via B4 Relay wrote: > From: Xianwei Zhao <xianwei.zhao@amlogic.com> > > Amlogic SoCs include a general-purpose DMA controller that can be used > by multiple peripherals, such as I2C PIO and I3C. Each peripheral group > is associated with a dedicated DMA channel in hardware. PLease add which SoC is concerned, in commoit message and in Kconfig help. > > Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com> > --- > drivers/dma/Kconfig | 8 + > drivers/dma/Makefile | 1 + > drivers/dma/amlogic-dma.c | 567 ++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 576 insertions(+) > > diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig > index 8bb0a119ecd4..fc7f70e22c22 100644 > --- a/drivers/dma/Kconfig > +++ b/drivers/dma/Kconfig > @@ -85,6 +85,14 @@ config AMCC_PPC440SPE_ADMA > help > Enable support for the AMCC PPC440SPe RAID engines. > > +config AMLOGIC_DMA > + tristate "Amlogic genneral DMA support" > + depends on ARCH_MESON > + select DMA_ENGINE > + select REGMAP_MMIO > + help > + Enable support for the Amlogic general DMA engines. > + > config APPLE_ADMAC > tristate "Apple ADMAC support" > depends on ARCH_APPLE || COMPILE_TEST > diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile > index a54d7688392b..fc28dade5b69 100644 > --- a/drivers/dma/Makefile > +++ b/drivers/dma/Makefile > @@ -16,6 +16,7 @@ obj-$(CONFIG_DMATEST) += dmatest.o > obj-$(CONFIG_ALTERA_MSGDMA) += altera-msgdma.o > obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o > obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/ > +obj-$(CONFIG_AMLOGIC_DMA) += amlogic-dma.o > obj-$(CONFIG_APPLE_ADMAC) += apple-admac.o > obj-$(CONFIG_ARM_DMA350) += arm-dma350.o > obj-$(CONFIG_AT_HDMAC) += at_hdmac.o > diff --git a/drivers/dma/amlogic-dma.c b/drivers/dma/amlogic-dma.c > new file mode 100644 > index 000000000000..40099002d558 > --- /dev/null > +++ b/drivers/dma/amlogic-dma.c > @@ -0,0 +1,567 @@ > +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) > +/* > + * Copyright (C) 2025 Amlogic, Inc. All rights reserved > + * Author: Xianwei Zhao <xianwei.zhao@amlogic.com> > + */ > + > +#include <linux/init.h> > +#include <linux/bitfield.h> > +#include <linux/types.h> > +#include <linux/mm.h> > +#include <linux/interrupt.h> > +#include <linux/clk.h> > +#include <linux/device.h> > +#include <linux/platform_device.h> > +#include <linux/dma-mapping.h> > +#include <linux/slab.h> > +#include <linux/dmaengine.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/of_dma.h> > +#include <linux/list.h> > +#include <linux/regmap.h> > +#include <asm/irq.h> > +#include "dmaengine.h" > + > +#define RCH_REG_BASE 0x0 > +#define WCH_REG_BASE 0x2000 > +/* > + * Each rch (read from memory) REG offset Rch_offset 0x0 each channel total 0x40 > + * rch addr = DMA_base + Rch_offset+ chan_id * 0x40 + reg_offset > + */ > +#define RCH_READY 0x0 > +#define RCH_STATUS 0x4 > +#define RCH_CFG 0x8 > +#define CFG_CLEAR BIT(25) > +#define CFG_PAUSE BIT(26) > +#define CFG_ENABLE BIT(27) > +#define CFG_DONE BIT(28) > +#define RCH_ADDR 0xc > +#define RCH_LEN 0x10 > +#define RCH_RD_LEN 0x14 > +#define RCH_PRT 0x18 > +#define RCH_SYCN_STAT 0x1c > +#define RCH_ADDR_LOW 0x20 > +#define RCH_ADDR_HIGH 0x24 > +/* if work on 64, it work with RCH_PRT */ > +#define RCH_PTR_HIGH 0x28 > + > +/* > + * Each wch (write to memory) REG offset Wch_offset 0x2000 each channel total 0x40 > + * wch addr = DMA_base + Wch_offset+ chan_id * 0x40 + reg_offset > + */ > +#define WCH_READY 0x0 > +#define WCH_TOTAL_LEN 0x4 > +#define WCH_CFG 0x8 > +#define WCH_ADDR 0xc > +#define WCH_LEN 0x10 > +#define WCH_RD_LEN 0x14 > +#define WCH_PRT 0x18 > +#define WCH_CMD_CNT 0x1c > +#define WCH_ADDR_LOW 0x20 > +#define WCH_ADDR_HIGH 0x24 > +/* if work on 64, it work with RCH_PRT */ > +#define WCH_PTR_HIGH 0x28 > + > +/* DMA controller reg */ > +#define RCH_INT_MASK 0x1000 > +#define WCH_INT_MASK 0x1004 > +#define CLEAR_W_BATCH 0x1014 > +#define CLEAR_RCH 0x1024 > +#define CLEAR_WCH 0x1028 > +#define RCH_ACTIVE 0x1038 > +#define WCH_ACTIVE 0x103C > +#define RCH_DONE 0x104C > +#define WCH_DONE 0x1050 > +#define RCH_ERR 0x1060 > +#define RCH_LEN_ERR 0x1064 > +#define WCH_ERR 0x1068 > +#define DMA_BATCH_END 0x1078 > +#define WCH_EOC_DONE 0x1088 > +#define WDMA_RESP_ERR 0x1098 > +#define UPT_PKT_SYNC 0x10A8 > +#define RCHN_CFG 0x10AC > +#define WCHN_CFG 0x10B0 > +#define MEM_PD_CFG 0x10B4 > +#define MEM_BUS_CFG 0x10B8 > +#define DMA_GMV_CFG 0x10BC > +#define DMA_GMR_CFG 0x10C0 > + > +#define DMA_MAX_LINK 8 > +#define MAX_CHAN_ID 32 > +#define SG_MAX_LEN ((1 << 27) - 1) > + > +struct aml_dma_sg_link { > +#define LINK_LEN GENMASK(26, 0) > +#define LINK_IRQ BIT(27) > +#define LINK_EOC BIT(28) > +#define LINK_LOOP BIT(29) > +#define LINK_ERR BIT(30) > +#define LINK_OWNER BIT(31) > + u32 ctl; > + u64 address; > + u32 revered; > +} __packed; PLease make sure __packed is needed > + > +struct aml_dma_chan { > + struct dma_chan chan; > + struct dma_async_tx_descriptor desc; > + struct aml_dma_dev *aml_dma; > + struct aml_dma_sg_link *sg_link; > + dma_addr_t sg_link_phys; > + int sg_link_cnt; > + int data_len; > + enum dma_status status; > + enum dma_transfer_direction direction; > + int chan_id; > + /* reg_base (direction + chan_id) */ > + int reg_offs; > +}; > + > +struct aml_dma_dev { > + struct dma_device dma_device; > + void __iomem *base; > + struct regmap *regmap; > + struct clk *clk; > + int irq; > + struct platform_device *pdev; > + struct aml_dma_chan *aml_rch[MAX_CHAN_ID]; > + struct aml_dma_chan *aml_wch[MAX_CHAN_ID]; > + unsigned int chan_nr; > + unsigned int chan_used; > + struct aml_dma_chan aml_chans[]__counted_by(chan_ns); > +}; > + > +static struct aml_dma_chan *to_aml_dma_chan(struct dma_chan *chan) > +{ > + return container_of(chan, struct aml_dma_chan, chan); > +} > + > +static dma_cookie_t aml_dma_tx_submit(struct dma_async_tx_descriptor *tx) > +{ > + return dma_cookie_assign(tx); > +} > + > +static int aml_dma_alloc_chan_resources(struct dma_chan *chan) > +{ > + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); > + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; > + > + aml_chan->sg_link = dma_alloc_coherent(aml_dma->dma_device.dev, > + sizeof(struct aml_dma_sg_link) * DMA_MAX_LINK, > + &aml_chan->sg_link_phys, GFP_KERNEL); > + if (!aml_chan->sg_link) > + return -ENOMEM; > + > + /* offset is the same RCH_CFG and WCH_CFG */ > + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_CLEAR, CFG_CLEAR); > + aml_chan->status = DMA_COMPLETE; > + dma_async_tx_descriptor_init(&aml_chan->desc, chan); > + aml_chan->desc.tx_submit = aml_dma_tx_submit; > + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_CLEAR, 0); > + > + return 0; > +} > + > +static void aml_dma_free_chan_resources(struct dma_chan *chan) > +{ > + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); > + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; > + > + aml_chan->status = DMA_COMPLETE; > + dma_free_coherent(aml_dma->dma_device.dev, > + sizeof(struct aml_dma_sg_link) * DMA_MAX_LINK, > + aml_chan->sg_link, aml_chan->sg_link_phys); > +} > + > +/* DMA transfer state update how many data reside it */ > +static enum dma_status aml_dma_tx_status(struct dma_chan *chan, > + dma_cookie_t cookie, > + struct dma_tx_state *txstate) > +{ > + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); > + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; > + u32 residue, done; > + > + regmap_read(aml_dma->regmap, aml_chan->reg_offs + RCH_RD_LEN, &done); > + residue = aml_chan->data_len - done; > + dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie, > + residue); > + > + return aml_chan->status; > +} > + > +static struct dma_async_tx_descriptor *aml_dma_prep_slave_sg > + (struct dma_chan *chan, struct scatterlist *sgl, > + unsigned int sg_len, enum dma_transfer_direction direction, > + unsigned long flags, void *context) > +{ > + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); > + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; > + struct aml_dma_sg_link *sg_link; > + struct scatterlist *sg; > + int idx = 0; > + u32 reg, chan_id; > + u32 i; > + > + if (aml_chan->direction != direction) { > + dev_err(aml_dma->dma_device.dev, "direction not support\n"); > + return NULL; > + } > + > + switch (aml_chan->status) { > + case DMA_IN_PROGRESS: > + /* support multiple prep before pending */ > + idx = aml_chan->sg_link_cnt; > + > + break; > + case DMA_COMPLETE: > + aml_chan->data_len = 0; > + chan_id = aml_chan->chan_id; > + reg = (direction == DMA_DEV_TO_MEM) ? WCH_INT_MASK : RCH_INT_MASK; > + regmap_update_bits(aml_dma->regmap, reg, BIT(chan_id), BIT(chan_id)); > + > + break; > + default: > + dev_err(aml_dma->dma_device.dev, "status error\n"); > + return NULL; > + } > + > + if (sg_len + idx > DMA_MAX_LINK) { > + dev_err(aml_dma->dma_device.dev, > + "maximum number of sg exceeded: %d > %d\n", > + sg_len, DMA_MAX_LINK); > + aml_chan->status = DMA_ERROR; > + return NULL; > + } > + > + aml_chan->status = DMA_IN_PROGRESS; > + > + for_each_sg(sgl, sg, sg_len, i) { > + if (sg_dma_len(sg) > SG_MAX_LEN) { > + dev_err(aml_dma->dma_device.dev, > + "maximum bytes exceeded: %d > %d\n", > + sg_dma_len(sg), SG_MAX_LEN); > + aml_chan->status = DMA_ERROR; > + return NULL; > + } > + sg_link = &aml_chan->sg_link[idx++]; > + /* set dma address and len to sglink*/ > + sg_link->address = sg->dma_address; > + sg_link->ctl = FIELD_PREP(LINK_LEN, sg_dma_len(sg)); > + > + aml_chan->data_len += sg_dma_len(sg); > + } > + aml_chan->sg_link_cnt = idx; > + > + return &aml_chan->desc; > +} > + > +static int aml_dma_pause_chan(struct dma_chan *chan) > +{ > + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); > + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; > + > + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_PAUSE, CFG_PAUSE); > + aml_chan->status = DMA_PAUSED; > + > + return 0; > +} > + > +static int aml_dma_resume_chan(struct dma_chan *chan) > +{ > + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); > + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; > + > + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_PAUSE, 0); > + aml_chan->status = DMA_IN_PROGRESS; > + > + return 0; > +} > + > +static int aml_dma_terminate_all(struct dma_chan *chan) > +{ > + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); > + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; > + int chan_id = aml_chan->chan_id; > + > + aml_dma_pause_chan(chan); > + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, CFG_CLEAR, CFG_CLEAR); > + > + if (aml_chan->direction == DMA_MEM_TO_DEV) > + regmap_update_bits(aml_dma->regmap, RCH_INT_MASK, BIT(chan_id), BIT(chan_id)); > + else if (aml_chan->direction == DMA_DEV_TO_MEM) > + regmap_update_bits(aml_dma->regmap, WCH_INT_MASK, BIT(chan_id), BIT(chan_id)); > + > + aml_chan->status = DMA_COMPLETE; > + > + return 0; > +} > + > +static void aml_dma_enable_chan(struct dma_chan *chan) > +{ > + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); > + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; > + struct aml_dma_sg_link *sg_link; > + int chan_id = aml_chan->chan_id; > + int idx = aml_chan->sg_link_cnt - 1; > + > + /* the last sg set eoc flag */ > + sg_link = &aml_chan->sg_link[idx]; > + sg_link->ctl |= LINK_EOC; > + dma_wmb(); > + if (aml_chan->direction == DMA_MEM_TO_DEV) { > + regmap_write(aml_dma->regmap, aml_chan->reg_offs + RCH_ADDR, > + aml_chan->sg_link_phys); > + regmap_write(aml_dma->regmap, aml_chan->reg_offs + RCH_LEN, aml_chan->data_len); > + regmap_update_bits(aml_dma->regmap, RCH_INT_MASK, BIT(chan_id), 0); > + /* for rch (tx) need set cfg 0 to trigger start */ > + regmap_write(aml_dma->regmap, aml_chan->reg_offs + RCH_CFG, 0); > + } else if (aml_chan->direction == DMA_DEV_TO_MEM) { > + regmap_write(aml_dma->regmap, aml_chan->reg_offs + WCH_ADDR, > + aml_chan->sg_link_phys); > + regmap_write(aml_dma->regmap, aml_chan->reg_offs + WCH_LEN, aml_chan->data_len); > + regmap_update_bits(aml_dma->regmap, WCH_INT_MASK, BIT(chan_id), 0); > + } > +} > + > +static irqreturn_t aml_dma_interrupt_handler(int irq, void *dev_id) > +{ > + struct aml_dma_dev *aml_dma = dev_id; > + struct aml_dma_chan *aml_chan; > + u32 done, eoc_done, err, err_l, end; > + int i = 0; > + > + /* deal with rch normal complete and error */ > + regmap_read(aml_dma->regmap, RCH_DONE, &done); > + regmap_read(aml_dma->regmap, RCH_ERR, &err); > + regmap_read(aml_dma->regmap, RCH_LEN_ERR, &err_l); > + err = err | err_l; > + > + done = done | err; > + > + i = 0; > + while (done) { > + if (done & 1) { Use BIT(0) > + aml_chan = aml_dma->aml_rch[i]; > + regmap_write(aml_dma->regmap, CLEAR_RCH, BIT(aml_chan->chan_id)); > + if (!aml_chan) { > + dev_err(aml_dma->dma_device.dev, "idx %d rch not initialized\n", i); > + continue; > + } > + aml_chan->status = (err & (1 << i)) ? DMA_ERROR : DMA_COMPLETE; > + /* Make sure to use this for initialization */ > + if (aml_chan->desc.cookie >= DMA_MIN_COOKIE) > + dma_cookie_complete(&aml_chan->desc); > + dmaengine_desc_get_callback_invoke(&aml_chan->desc, NULL); > + } > + i++; > + done = done >> 1; > + } I would rather iterate on bits or loop with ffs(): for (i = ffs(done); i; i = ffs(done)) { aml_chan = aml_dma->aml_rch[i - 1]; ... } > + > + /* deal with wch normal complete and error */ > + regmap_read(aml_dma->regmap, DMA_BATCH_END, &end); > + if (end) > + regmap_write(aml_dma->regmap, CLEAR_W_BATCH, end); > + > + regmap_read(aml_dma->regmap, WCH_DONE, &done); > + regmap_read(aml_dma->regmap, WCH_EOC_DONE, &eoc_done); > + done = done | eoc_done; > + > + regmap_read(aml_dma->regmap, WCH_ERR, &err); > + regmap_read(aml_dma->regmap, WDMA_RESP_ERR, &err_l); > + err = err | err_l; > + > + done = done | err; > + i = 0; > + while (done) { > + if (done & 1) { > + aml_chan = aml_dma->aml_wch[i]; > + regmap_write(aml_dma->regmap, CLEAR_WCH, BIT(aml_chan->chan_id)); > + if (!aml_chan) { > + dev_err(aml_dma->dma_device.dev, "idx %d wch not initialized\n", i); > + continue; > + } > + aml_chan->status = (err & (1 << i)) ? DMA_ERROR : DMA_COMPLETE; > + if (aml_chan->desc.cookie >= DMA_MIN_COOKIE) > + dma_cookie_complete(&aml_chan->desc); > + dmaengine_desc_get_callback_invoke(&aml_chan->desc, NULL); > + } > + i++; > + done = done >> 1; > + } > + > + return IRQ_HANDLED; > +} > + > +static struct dma_chan *aml_of_dma_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) > +{ > + struct aml_dma_dev *aml_dma = (struct aml_dma_dev *)ofdma->of_dma_data; > + struct aml_dma_chan *aml_chan = NULL; > + u32 type; > + u32 phy_chan_id; > + > + if (dma_spec->args_count != 2) > + return NULL; > + > + type = dma_spec->args[0]; > + phy_chan_id = dma_spec->args[1]; > + > + if (phy_chan_id >= MAX_CHAN_ID) > + return NULL; > + > + if (type == 0) { Add or use a define for type > + aml_chan = aml_dma->aml_rch[phy_chan_id]; > + if (!aml_chan) { > + if (aml_dma->chan_used >= aml_dma->chan_nr) { > + dev_err(aml_dma->dma_device.dev, "some dma clients err used\n"); > + return NULL; > + } > + aml_chan = &aml_dma->aml_chans[aml_dma->chan_used]; > + aml_dma->chan_used++; > + aml_chan->direction = DMA_MEM_TO_DEV; > + aml_chan->chan_id = phy_chan_id; > + aml_chan->reg_offs = RCH_REG_BASE + 0x40 * aml_chan->chan_id; > + aml_dma->aml_rch[phy_chan_id] = aml_chan; > + } > + } else if (type == 1) { > + aml_chan = aml_dma->aml_wch[phy_chan_id]; > + if (!aml_chan) { > + if (aml_dma->chan_used >= aml_dma->chan_nr) { > + dev_err(aml_dma->dma_device.dev, "some dma clients err used\n"); > + return NULL; > + } > + aml_chan = &aml_dma->aml_chans[aml_dma->chan_used]; > + aml_dma->chan_used++; > + aml_chan->direction = DMA_DEV_TO_MEM; > + aml_chan->chan_id = phy_chan_id; > + aml_chan->reg_offs = WCH_REG_BASE + 0x40 * aml_chan->chan_id; > + aml_dma->aml_wch[phy_chan_id] = aml_chan; > + } > + } else { > + dev_err(aml_dma->dma_device.dev, "type %d not supported\n", type); > + return NULL; > + } > + > + return dma_get_slave_channel(&aml_chan->chan); > +} > + > +static int aml_dma_probe(struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + struct dma_device *dma_dev; > + struct aml_dma_dev *aml_dma; > + int ret, i, len; > + u32 chan_nr; > + > + const struct regmap_config aml_regmap_config = { > + .reg_bits = 32, > + .val_bits = 32, > + .reg_stride = 4, > + .max_register = 0x3000, > + }; Please move out of function > + > + ret = of_property_read_u32(np, "dma-channels", &chan_nr); > + if (ret) > + return dev_err_probe(&pdev->dev, ret, "failed to read dma-channels\n"); > + > + len = sizeof(*aml_dma) + sizeof(struct aml_dma_chan) * chan_nr; > + aml_dma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); > + if (!aml_dma) > + return -ENOMEM; > + > + aml_dma->chan_nr = chan_nr; > + > + aml_dma->base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(aml_dma->base)) > + return PTR_ERR(aml_dma->base); > + > + aml_dma->regmap = devm_regmap_init_mmio(&pdev->dev, aml_dma->base, > + &aml_regmap_config); > + if (IS_ERR_OR_NULL(aml_dma->regmap)) > + return PTR_ERR(aml_dma->regmap); > + > + aml_dma->clk = devm_clk_get_enabled(&pdev->dev, NULL); > + if (IS_ERR(aml_dma->clk)) > + return PTR_ERR(aml_dma->clk); > + > + aml_dma->irq = platform_get_irq(pdev, 0); > + > + aml_dma->pdev = pdev; > + aml_dma->dma_device.dev = &pdev->dev; > + > + dma_dev = &aml_dma->dma_device; > + INIT_LIST_HEAD(&dma_dev->channels); > + > + /* Initialize channel parameters */ > + for (i = 0; i < chan_nr; i++) { > + struct aml_dma_chan *aml_chan = &aml_dma->aml_chans[i]; > + > + aml_chan->aml_dma = aml_dma; > + aml_chan->chan.device = &aml_dma->dma_device; > + dma_cookie_init(&aml_chan->chan); > + > + /* Add the channel to aml_chan list */ > + list_add_tail(&aml_chan->chan.device_node, > + &aml_dma->dma_device.channels); > + } > + aml_dma->chan_used = 0; > + > + dma_set_max_seg_size(dma_dev->dev, SG_MAX_LEN); > + > + dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); > + dma_dev->device_alloc_chan_resources = aml_dma_alloc_chan_resources; > + dma_dev->device_free_chan_resources = aml_dma_free_chan_resources; > + dma_dev->device_tx_status = aml_dma_tx_status; > + dma_dev->device_prep_slave_sg = aml_dma_prep_slave_sg; > + > + dma_dev->device_pause = aml_dma_pause_chan; > + dma_dev->device_resume = aml_dma_resume_chan; > + dma_dev->device_terminate_all = aml_dma_terminate_all; > + dma_dev->device_issue_pending = aml_dma_enable_chan; > + /* PIO 4 bytes and I2C 1 byte */ > + dma_dev->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES | DMA_SLAVE_BUSWIDTH_1_BYTE); > + dma_dev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); > + dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; > + > + ret = dmaenginem_async_device_register(dma_dev); > + if (ret) > + return ret; > + > + ret = of_dma_controller_register(np, aml_of_dma_xlate, aml_dma); > + if (ret) > + return ret; > + > + regmap_write(aml_dma->regmap, RCH_INT_MASK, 0xffffffff); > + regmap_write(aml_dma->regmap, WCH_INT_MASK, 0xffffffff); > + > + ret = devm_request_irq(&pdev->dev, aml_dma->irq, aml_dma_interrupt_handler, > + IRQF_SHARED, dev_name(&pdev->dev), aml_dma); > + if (ret) > + return ret; > + > + dev_info(aml_dma->dma_device.dev, "initialized\n"); > + > + return 0; > +} > + > +static const struct of_device_id aml_dma_ids[] = { > + { .compatible = "amlogic,general-dma", }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, aml_dma_ids); > + > +static struct platform_driver aml_dma_driver = { > + .probe = aml_dma_probe, > + .driver = { > + .name = "aml-dma", > + .of_match_table = aml_dma_ids, > + }, > +}; > + > +module_platform_driver(aml_dma_driver); > + > +MODULE_DESCRIPTION("GENERAL DMA driver for Amlogic"); > +MODULE_AUTHOR("Xianwei Zhao <xianwei.zhao@amlogic.com>"); > +MODULE_ALIAS("platform:aml-dma"); Unneeded, please drop > +MODULE_LICENSE("GPL"); > Neil _______________________________________________ linux-amlogic mailing list linux-amlogic@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-amlogic ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/3] dma: amlogic: Add general DMA driver for SoCs 2025-12-16 9:09 ` neil.armstrong @ 2025-12-19 2:35 ` Xianwei Zhao 0 siblings, 0 replies; 11+ messages in thread From: Xianwei Zhao @ 2025-12-19 2:35 UTC (permalink / raw) To: Neil Armstrong, Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Kees Cook, Gustavo A. R. Silva Cc: linux-amlogic, dmaengine, devicetree, linux-kernel, linux-hardening Hi Neil, Thanks for your reply. On 2025/12/16 17:09, neil.armstrong@linaro.org wrote: > [ EXTERNAL EMAIL ] > > Hi, > > On 12/16/25 09:03, Xianwei Zhao via B4 Relay wrote: >> From: Xianwei Zhao <xianwei.zhao@amlogic.com> >> >> Amlogic SoCs include a general-purpose DMA controller that can be used >> by multiple peripherals, such as I2C PIO and I3C. Each peripheral group >> is associated with a dedicated DMA channel in hardware. > > PLease add which SoC is concerned, in commoit message and in Kconfig help. > Will add SoC name for support. >> >> Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com> >> --- >> drivers/dma/Kconfig | 8 + >> drivers/dma/Makefile | 1 + >> drivers/dma/amlogic-dma.c | 567 >> ++++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 576 insertions(+) >> >> diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig >> index 8bb0a119ecd4..fc7f70e22c22 100644 >> --- a/drivers/dma/Kconfig >> +++ b/drivers/dma/Kconfig >> @@ -85,6 +85,14 @@ config AMCC_PPC440SPE_ADMA >> help >> Enable support for the AMCC PPC440SPe RAID engines. >> >> +config AMLOGIC_DMA >> + tristate "Amlogic genneral DMA support" >> + depends on ARCH_MESON >> + select DMA_ENGINE >> + select REGMAP_MMIO >> + help >> + Enable support for the Amlogic general DMA engines. >> + >> config APPLE_ADMAC >> tristate "Apple ADMAC support" >> depends on ARCH_APPLE || COMPILE_TEST >> diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile >> index a54d7688392b..fc28dade5b69 100644 >> --- a/drivers/dma/Makefile >> +++ b/drivers/dma/Makefile >> @@ -16,6 +16,7 @@ obj-$(CONFIG_DMATEST) += dmatest.o >> obj-$(CONFIG_ALTERA_MSGDMA) += altera-msgdma.o >> obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o >> obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/ >> +obj-$(CONFIG_AMLOGIC_DMA) += amlogic-dma.o >> obj-$(CONFIG_APPLE_ADMAC) += apple-admac.o >> obj-$(CONFIG_ARM_DMA350) += arm-dma350.o >> obj-$(CONFIG_AT_HDMAC) += at_hdmac.o >> diff --git a/drivers/dma/amlogic-dma.c b/drivers/dma/amlogic-dma.c >> new file mode 100644 >> index 000000000000..40099002d558 >> --- /dev/null >> +++ b/drivers/dma/amlogic-dma.c >> @@ -0,0 +1,567 @@ >> +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) >> +/* >> + * Copyright (C) 2025 Amlogic, Inc. All rights reserved >> + * Author: Xianwei Zhao <xianwei.zhao@amlogic.com> >> + */ >> + >> +#include <linux/init.h> >> +#include <linux/bitfield.h> >> +#include <linux/types.h> >> +#include <linux/mm.h> >> +#include <linux/interrupt.h> >> +#include <linux/clk.h> >> +#include <linux/device.h> >> +#include <linux/platform_device.h> >> +#include <linux/dma-mapping.h> >> +#include <linux/slab.h> >> +#include <linux/dmaengine.h> >> +#include <linux/module.h> >> +#include <linux/of.h> >> +#include <linux/of_dma.h> >> +#include <linux/list.h> >> +#include <linux/regmap.h> >> +#include <asm/irq.h> >> +#include "dmaengine.h" >> + >> +#define RCH_REG_BASE 0x0 >> +#define WCH_REG_BASE 0x2000 >> +/* >> + * Each rch (read from memory) REG offset Rch_offset 0x0 each >> channel total 0x40 >> + * rch addr = DMA_base + Rch_offset+ chan_id * 0x40 + reg_offset >> + */ >> +#define RCH_READY 0x0 >> +#define RCH_STATUS 0x4 >> +#define RCH_CFG 0x8 >> +#define CFG_CLEAR BIT(25) >> +#define CFG_PAUSE BIT(26) >> +#define CFG_ENABLE BIT(27) >> +#define CFG_DONE BIT(28) >> +#define RCH_ADDR 0xc >> +#define RCH_LEN 0x10 >> +#define RCH_RD_LEN 0x14 >> +#define RCH_PRT 0x18 >> +#define RCH_SYCN_STAT 0x1c >> +#define RCH_ADDR_LOW 0x20 >> +#define RCH_ADDR_HIGH 0x24 >> +/* if work on 64, it work with RCH_PRT */ >> +#define RCH_PTR_HIGH 0x28 >> + >> +/* >> + * Each wch (write to memory) REG offset Wch_offset 0x2000 each >> channel total 0x40 >> + * wch addr = DMA_base + Wch_offset+ chan_id * 0x40 + reg_offset >> + */ >> +#define WCH_READY 0x0 >> +#define WCH_TOTAL_LEN 0x4 >> +#define WCH_CFG 0x8 >> +#define WCH_ADDR 0xc >> +#define WCH_LEN 0x10 >> +#define WCH_RD_LEN 0x14 >> +#define WCH_PRT 0x18 >> +#define WCH_CMD_CNT 0x1c >> +#define WCH_ADDR_LOW 0x20 >> +#define WCH_ADDR_HIGH 0x24 >> +/* if work on 64, it work with RCH_PRT */ >> +#define WCH_PTR_HIGH 0x28 >> + >> +/* DMA controller reg */ >> +#define RCH_INT_MASK 0x1000 >> +#define WCH_INT_MASK 0x1004 >> +#define CLEAR_W_BATCH 0x1014 >> +#define CLEAR_RCH 0x1024 >> +#define CLEAR_WCH 0x1028 >> +#define RCH_ACTIVE 0x1038 >> +#define WCH_ACTIVE 0x103C >> +#define RCH_DONE 0x104C >> +#define WCH_DONE 0x1050 >> +#define RCH_ERR 0x1060 >> +#define RCH_LEN_ERR 0x1064 >> +#define WCH_ERR 0x1068 >> +#define DMA_BATCH_END 0x1078 >> +#define WCH_EOC_DONE 0x1088 >> +#define WDMA_RESP_ERR 0x1098 >> +#define UPT_PKT_SYNC 0x10A8 >> +#define RCHN_CFG 0x10AC >> +#define WCHN_CFG 0x10B0 >> +#define MEM_PD_CFG 0x10B4 >> +#define MEM_BUS_CFG 0x10B8 >> +#define DMA_GMV_CFG 0x10BC >> +#define DMA_GMR_CFG 0x10C0 >> + >> +#define DMA_MAX_LINK 8 >> +#define MAX_CHAN_ID 32 >> +#define SG_MAX_LEN ((1 << 27) - 1) >> + >> +struct aml_dma_sg_link { >> +#define LINK_LEN GENMASK(26, 0) >> +#define LINK_IRQ BIT(27) >> +#define LINK_EOC BIT(28) >> +#define LINK_LOOP BIT(29) >> +#define LINK_ERR BIT(30) >> +#define LINK_OWNER BIT(31) >> + u32 ctl; >> + u64 address; >> + u32 revered; >> +} __packed; > > PLease make sure __packed is needed > Yes. This is a hardware requirement. >> + >> +struct aml_dma_chan { >> + struct dma_chan chan; >> + struct dma_async_tx_descriptor desc; >> + struct aml_dma_dev *aml_dma; >> + struct aml_dma_sg_link *sg_link; >> + dma_addr_t sg_link_phys; >> + int sg_link_cnt; >> + int data_len; >> + enum dma_status status; >> + enum dma_transfer_direction direction; >> + int chan_id; >> + /* reg_base (direction + chan_id) */ >> + int reg_offs; >> +}; >> + >> +struct aml_dma_dev { >> + struct dma_device dma_device; >> + void __iomem *base; >> + struct regmap *regmap; >> + struct clk *clk; >> + int irq; >> + struct platform_device *pdev; >> + struct aml_dma_chan *aml_rch[MAX_CHAN_ID]; >> + struct aml_dma_chan *aml_wch[MAX_CHAN_ID]; >> + unsigned int chan_nr; >> + unsigned int chan_used; >> + struct aml_dma_chan aml_chans[]__counted_by(chan_ns); >> +}; >> + >> +static struct aml_dma_chan *to_aml_dma_chan(struct dma_chan *chan) >> +{ >> + return container_of(chan, struct aml_dma_chan, chan); >> +} >> + >> +static dma_cookie_t aml_dma_tx_submit(struct dma_async_tx_descriptor >> *tx) >> +{ >> + return dma_cookie_assign(tx); >> +} >> + >> +static int aml_dma_alloc_chan_resources(struct dma_chan *chan) >> +{ >> + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); >> + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; >> + >> + aml_chan->sg_link = dma_alloc_coherent(aml_dma->dma_device.dev, >> + sizeof(struct >> aml_dma_sg_link) * DMA_MAX_LINK, >> + &aml_chan->sg_link_phys, >> GFP_KERNEL); >> + if (!aml_chan->sg_link) >> + return -ENOMEM; >> + >> + /* offset is the same RCH_CFG and WCH_CFG */ >> + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + >> RCH_CFG, CFG_CLEAR, CFG_CLEAR); >> + aml_chan->status = DMA_COMPLETE; >> + dma_async_tx_descriptor_init(&aml_chan->desc, chan); >> + aml_chan->desc.tx_submit = aml_dma_tx_submit; >> + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + >> RCH_CFG, CFG_CLEAR, 0); >> + >> + return 0; >> +} >> + >> +static void aml_dma_free_chan_resources(struct dma_chan *chan) >> +{ >> + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); >> + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; >> + >> + aml_chan->status = DMA_COMPLETE; >> + dma_free_coherent(aml_dma->dma_device.dev, >> + sizeof(struct aml_dma_sg_link) * DMA_MAX_LINK, >> + aml_chan->sg_link, aml_chan->sg_link_phys); >> +} >> + >> +/* DMA transfer state update how many data reside it */ >> +static enum dma_status aml_dma_tx_status(struct dma_chan *chan, >> + dma_cookie_t cookie, >> + struct dma_tx_state *txstate) >> +{ >> + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); >> + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; >> + u32 residue, done; >> + >> + regmap_read(aml_dma->regmap, aml_chan->reg_offs + RCH_RD_LEN, >> &done); >> + residue = aml_chan->data_len - done; >> + dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie, >> + residue); >> + >> + return aml_chan->status; >> +} >> + >> +static struct dma_async_tx_descriptor *aml_dma_prep_slave_sg >> + (struct dma_chan *chan, struct scatterlist *sgl, >> + unsigned int sg_len, enum dma_transfer_direction direction, >> + unsigned long flags, void *context) >> +{ >> + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); >> + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; >> + struct aml_dma_sg_link *sg_link; >> + struct scatterlist *sg; >> + int idx = 0; >> + u32 reg, chan_id; >> + u32 i; >> + >> + if (aml_chan->direction != direction) { >> + dev_err(aml_dma->dma_device.dev, "direction not >> support\n"); >> + return NULL; >> + } >> + >> + switch (aml_chan->status) { >> + case DMA_IN_PROGRESS: >> + /* support multiple prep before pending */ >> + idx = aml_chan->sg_link_cnt; >> + >> + break; >> + case DMA_COMPLETE: >> + aml_chan->data_len = 0; >> + chan_id = aml_chan->chan_id; >> + reg = (direction == DMA_DEV_TO_MEM) ? WCH_INT_MASK : >> RCH_INT_MASK; >> + regmap_update_bits(aml_dma->regmap, reg, BIT(chan_id), >> BIT(chan_id)); >> + >> + break; >> + default: >> + dev_err(aml_dma->dma_device.dev, "status error\n"); >> + return NULL; >> + } >> + >> + if (sg_len + idx > DMA_MAX_LINK) { >> + dev_err(aml_dma->dma_device.dev, >> + "maximum number of sg exceeded: %d > %d\n", >> + sg_len, DMA_MAX_LINK); >> + aml_chan->status = DMA_ERROR; >> + return NULL; >> + } >> + >> + aml_chan->status = DMA_IN_PROGRESS; >> + >> + for_each_sg(sgl, sg, sg_len, i) { >> + if (sg_dma_len(sg) > SG_MAX_LEN) { >> + dev_err(aml_dma->dma_device.dev, >> + "maximum bytes exceeded: %d > %d\n", >> + sg_dma_len(sg), SG_MAX_LEN); >> + aml_chan->status = DMA_ERROR; >> + return NULL; >> + } >> + sg_link = &aml_chan->sg_link[idx++]; >> + /* set dma address and len to sglink*/ >> + sg_link->address = sg->dma_address; >> + sg_link->ctl = FIELD_PREP(LINK_LEN, sg_dma_len(sg)); >> + >> + aml_chan->data_len += sg_dma_len(sg); >> + } >> + aml_chan->sg_link_cnt = idx; >> + >> + return &aml_chan->desc; >> +} >> + >> +static int aml_dma_pause_chan(struct dma_chan *chan) >> +{ >> + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); >> + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; >> + >> + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + >> RCH_CFG, CFG_PAUSE, CFG_PAUSE); >> + aml_chan->status = DMA_PAUSED; >> + >> + return 0; >> +} >> + >> +static int aml_dma_resume_chan(struct dma_chan *chan) >> +{ >> + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); >> + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; >> + >> + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + >> RCH_CFG, CFG_PAUSE, 0); >> + aml_chan->status = DMA_IN_PROGRESS; >> + >> + return 0; >> +} >> + >> +static int aml_dma_terminate_all(struct dma_chan *chan) >> +{ >> + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); >> + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; >> + int chan_id = aml_chan->chan_id; >> + >> + aml_dma_pause_chan(chan); >> + regmap_update_bits(aml_dma->regmap, aml_chan->reg_offs + >> RCH_CFG, CFG_CLEAR, CFG_CLEAR); >> + >> + if (aml_chan->direction == DMA_MEM_TO_DEV) >> + regmap_update_bits(aml_dma->regmap, RCH_INT_MASK, >> BIT(chan_id), BIT(chan_id)); >> + else if (aml_chan->direction == DMA_DEV_TO_MEM) >> + regmap_update_bits(aml_dma->regmap, WCH_INT_MASK, >> BIT(chan_id), BIT(chan_id)); >> + >> + aml_chan->status = DMA_COMPLETE; >> + >> + return 0; >> +} >> + >> +static void aml_dma_enable_chan(struct dma_chan *chan) >> +{ >> + struct aml_dma_chan *aml_chan = to_aml_dma_chan(chan); >> + struct aml_dma_dev *aml_dma = aml_chan->aml_dma; >> + struct aml_dma_sg_link *sg_link; >> + int chan_id = aml_chan->chan_id; >> + int idx = aml_chan->sg_link_cnt - 1; >> + >> + /* the last sg set eoc flag */ >> + sg_link = &aml_chan->sg_link[idx]; >> + sg_link->ctl |= LINK_EOC; >> + dma_wmb(); >> + if (aml_chan->direction == DMA_MEM_TO_DEV) { >> + regmap_write(aml_dma->regmap, aml_chan->reg_offs + >> RCH_ADDR, >> + aml_chan->sg_link_phys); >> + regmap_write(aml_dma->regmap, aml_chan->reg_offs + >> RCH_LEN, aml_chan->data_len); >> + regmap_update_bits(aml_dma->regmap, RCH_INT_MASK, >> BIT(chan_id), 0); >> + /* for rch (tx) need set cfg 0 to trigger start */ >> + regmap_write(aml_dma->regmap, aml_chan->reg_offs + >> RCH_CFG, 0); >> + } else if (aml_chan->direction == DMA_DEV_TO_MEM) { >> + regmap_write(aml_dma->regmap, aml_chan->reg_offs + >> WCH_ADDR, >> + aml_chan->sg_link_phys); >> + regmap_write(aml_dma->regmap, aml_chan->reg_offs + >> WCH_LEN, aml_chan->data_len); >> + regmap_update_bits(aml_dma->regmap, WCH_INT_MASK, >> BIT(chan_id), 0); >> + } >> +} >> + >> +static irqreturn_t aml_dma_interrupt_handler(int irq, void *dev_id) >> +{ >> + struct aml_dma_dev *aml_dma = dev_id; >> + struct aml_dma_chan *aml_chan; >> + u32 done, eoc_done, err, err_l, end; >> + int i = 0; >> + >> + /* deal with rch normal complete and error */ >> + regmap_read(aml_dma->regmap, RCH_DONE, &done); >> + regmap_read(aml_dma->regmap, RCH_ERR, &err); >> + regmap_read(aml_dma->regmap, RCH_LEN_ERR, &err_l); >> + err = err | err_l; >> + >> + done = done | err; >> + >> + i = 0; >> + while (done) { >> + if (done & 1) { > > Use BIT(0) Will do. > >> + aml_chan = aml_dma->aml_rch[i]; >> + regmap_write(aml_dma->regmap, CLEAR_RCH, >> BIT(aml_chan->chan_id)); >> + if (!aml_chan) { >> + dev_err(aml_dma->dma_device.dev, "idx %d >> rch not initialized\n", i); >> + continue; >> + } >> + aml_chan->status = (err & (1 << i)) ? DMA_ERROR >> : DMA_COMPLETE; >> + /* Make sure to use this for initialization */ >> + if (aml_chan->desc.cookie >= DMA_MIN_COOKIE) >> + dma_cookie_complete(&aml_chan->desc); >> + >> dmaengine_desc_get_callback_invoke(&aml_chan->desc, NULL); >> + } >> + i++; >> + done = done >> 1; >> + } > > I would rather iterate on bits or loop with ffs(): > > for (i = ffs(done); i; i = ffs(done)) { > aml_chan = aml_dma->aml_rch[i - 1]; > ... > } > Will do. for (; done; ) { i = ffs(done)) aml_chan = aml_dma->aml_rch[i - 1]; ... done &=~BIT(i) } >> + >> + /* deal with wch normal complete and error */ >> + regmap_read(aml_dma->regmap, DMA_BATCH_END, &end); >> + if (end) >> + regmap_write(aml_dma->regmap, CLEAR_W_BATCH, end); >> + >> + regmap_read(aml_dma->regmap, WCH_DONE, &done); >> + regmap_read(aml_dma->regmap, WCH_EOC_DONE, &eoc_done); >> + done = done | eoc_done; >> + >> + regmap_read(aml_dma->regmap, WCH_ERR, &err); >> + regmap_read(aml_dma->regmap, WDMA_RESP_ERR, &err_l); >> + err = err | err_l; >> + >> + done = done | err; >> + i = 0; >> + while (done) { >> + if (done & 1) { >> + aml_chan = aml_dma->aml_wch[i]; >> + regmap_write(aml_dma->regmap, CLEAR_WCH, >> BIT(aml_chan->chan_id)); >> + if (!aml_chan) { >> + dev_err(aml_dma->dma_device.dev, "idx %d >> wch not initialized\n", i); >> + continue; >> + } >> + aml_chan->status = (err & (1 << i)) ? DMA_ERROR >> : DMA_COMPLETE; >> + if (aml_chan->desc.cookie >= DMA_MIN_COOKIE) >> + dma_cookie_complete(&aml_chan->desc); >> + >> dmaengine_desc_get_callback_invoke(&aml_chan->desc, NULL); >> + } >> + i++; >> + done = done >> 1; >> + } >> + >> + return IRQ_HANDLED; >> +} >> + >> +static struct dma_chan *aml_of_dma_xlate(struct of_phandle_args >> *dma_spec, struct of_dma *ofdma) >> +{ >> + struct aml_dma_dev *aml_dma = (struct aml_dma_dev >> *)ofdma->of_dma_data; >> + struct aml_dma_chan *aml_chan = NULL; >> + u32 type; >> + u32 phy_chan_id; >> + >> + if (dma_spec->args_count != 2) >> + return NULL; >> + >> + type = dma_spec->args[0]; >> + phy_chan_id = dma_spec->args[1]; >> + >> + if (phy_chan_id >= MAX_CHAN_ID) >> + return NULL; >> + >> + if (type == 0) { > > Add or use a define for type > Will do. >> + aml_chan = aml_dma->aml_rch[phy_chan_id]; >> + if (!aml_chan) { >> + if (aml_dma->chan_used >= aml_dma->chan_nr) { >> + dev_err(aml_dma->dma_device.dev, "some >> dma clients err used\n"); >> + return NULL; >> + } >> + aml_chan = &aml_dma->aml_chans[aml_dma->chan_used]; >> + aml_dma->chan_used++; >> + aml_chan->direction = DMA_MEM_TO_DEV; >> + aml_chan->chan_id = phy_chan_id; >> + aml_chan->reg_offs = RCH_REG_BASE + 0x40 * >> aml_chan->chan_id; >> + aml_dma->aml_rch[phy_chan_id] = aml_chan; >> + } >> + } else if (type == 1) { >> + aml_chan = aml_dma->aml_wch[phy_chan_id]; >> + if (!aml_chan) { >> + if (aml_dma->chan_used >= aml_dma->chan_nr) { >> + dev_err(aml_dma->dma_device.dev, "some >> dma clients err used\n"); >> + return NULL; >> + } >> + aml_chan = &aml_dma->aml_chans[aml_dma->chan_used]; >> + aml_dma->chan_used++; >> + aml_chan->direction = DMA_DEV_TO_MEM; >> + aml_chan->chan_id = phy_chan_id; >> + aml_chan->reg_offs = WCH_REG_BASE + 0x40 * >> aml_chan->chan_id; >> + aml_dma->aml_wch[phy_chan_id] = aml_chan; >> + } >> + } else { >> + dev_err(aml_dma->dma_device.dev, "type %d not >> supported\n", type); >> + return NULL; >> + } >> + >> + return dma_get_slave_channel(&aml_chan->chan); >> +} >> + >> +static int aml_dma_probe(struct platform_device *pdev) >> +{ >> + struct device_node *np = pdev->dev.of_node; >> + struct dma_device *dma_dev; >> + struct aml_dma_dev *aml_dma; >> + int ret, i, len; >> + u32 chan_nr; >> + >> + const struct regmap_config aml_regmap_config = { >> + .reg_bits = 32, >> + .val_bits = 32, >> + .reg_stride = 4, >> + .max_register = 0x3000, >> + }; > > Please move out of function > Using local variables reduces memory usage, and this pattern is already being adopted in other modules as well. >> + >> + ret = of_property_read_u32(np, "dma-channels", &chan_nr); >> + if (ret) >> + return dev_err_probe(&pdev->dev, ret, "failed to read >> dma-channels\n"); >> + >> + len = sizeof(*aml_dma) + sizeof(struct aml_dma_chan) * chan_nr; >> + aml_dma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); >> + if (!aml_dma) >> + return -ENOMEM; >> + >> + aml_dma->chan_nr = chan_nr; >> + >> + aml_dma->base = devm_platform_ioremap_resource(pdev, 0); >> + if (IS_ERR(aml_dma->base)) >> + return PTR_ERR(aml_dma->base); >> + >> + aml_dma->regmap = devm_regmap_init_mmio(&pdev->dev, aml_dma->base, >> + &aml_regmap_config); >> + if (IS_ERR_OR_NULL(aml_dma->regmap)) >> + return PTR_ERR(aml_dma->regmap); >> + >> + aml_dma->clk = devm_clk_get_enabled(&pdev->dev, NULL); >> + if (IS_ERR(aml_dma->clk)) >> + return PTR_ERR(aml_dma->clk); >> + >> + aml_dma->irq = platform_get_irq(pdev, 0); >> + >> + aml_dma->pdev = pdev; >> + aml_dma->dma_device.dev = &pdev->dev; >> + >> + dma_dev = &aml_dma->dma_device; >> + INIT_LIST_HEAD(&dma_dev->channels); >> + >> + /* Initialize channel parameters */ >> + for (i = 0; i < chan_nr; i++) { >> + struct aml_dma_chan *aml_chan = &aml_dma->aml_chans[i]; >> + >> + aml_chan->aml_dma = aml_dma; >> + aml_chan->chan.device = &aml_dma->dma_device; >> + dma_cookie_init(&aml_chan->chan); >> + >> + /* Add the channel to aml_chan list */ >> + list_add_tail(&aml_chan->chan.device_node, >> + &aml_dma->dma_device.channels); >> + } >> + aml_dma->chan_used = 0; >> + >> + dma_set_max_seg_size(dma_dev->dev, SG_MAX_LEN); >> + >> + dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); >> + dma_dev->device_alloc_chan_resources = >> aml_dma_alloc_chan_resources; >> + dma_dev->device_free_chan_resources = aml_dma_free_chan_resources; >> + dma_dev->device_tx_status = aml_dma_tx_status; >> + dma_dev->device_prep_slave_sg = aml_dma_prep_slave_sg; >> + >> + dma_dev->device_pause = aml_dma_pause_chan; >> + dma_dev->device_resume = aml_dma_resume_chan; >> + dma_dev->device_terminate_all = aml_dma_terminate_all; >> + dma_dev->device_issue_pending = aml_dma_enable_chan; >> + /* PIO 4 bytes and I2C 1 byte */ >> + dma_dev->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES | >> DMA_SLAVE_BUSWIDTH_1_BYTE); >> + dma_dev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); >> + dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; >> + >> + ret = dmaenginem_async_device_register(dma_dev); >> + if (ret) >> + return ret; >> + >> + ret = of_dma_controller_register(np, aml_of_dma_xlate, aml_dma); >> + if (ret) >> + return ret; >> + >> + regmap_write(aml_dma->regmap, RCH_INT_MASK, 0xffffffff); >> + regmap_write(aml_dma->regmap, WCH_INT_MASK, 0xffffffff); >> + >> + ret = devm_request_irq(&pdev->dev, aml_dma->irq, >> aml_dma_interrupt_handler, >> + IRQF_SHARED, dev_name(&pdev->dev), aml_dma); >> + if (ret) >> + return ret; >> + >> + dev_info(aml_dma->dma_device.dev, "initialized\n"); >> + >> + return 0; >> +} >> + >> +static const struct of_device_id aml_dma_ids[] = { >> + { .compatible = "amlogic,general-dma", }, >> + {}, >> +}; >> +MODULE_DEVICE_TABLE(of, aml_dma_ids); >> + >> +static struct platform_driver aml_dma_driver = { >> + .probe = aml_dma_probe, >> + .driver = { >> + .name = "aml-dma", >> + .of_match_table = aml_dma_ids, >> + }, >> +}; >> + >> +module_platform_driver(aml_dma_driver); >> + >> +MODULE_DESCRIPTION("GENERAL DMA driver for Amlogic"); >> +MODULE_AUTHOR("Xianwei Zhao <xianwei.zhao@amlogic.com>"); >> +MODULE_ALIAS("platform:aml-dma"); > > Unneeded, please drop > Will drop. >> +MODULE_LICENSE("GPL"); >> > > Neil > _______________________________________________ linux-amlogic mailing list linux-amlogic@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-amlogic ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/3] dma: amlogic: Add general DMA driver for SoCs 2025-12-16 8:03 ` [PATCH 2/3] dma: amlogic: Add general DMA driver for SoCs Xianwei Zhao via B4 Relay 2025-12-16 8:46 ` Krzysztof Kozlowski 2025-12-16 9:09 ` neil.armstrong @ 2025-12-20 18:21 ` kernel test robot 2 siblings, 0 replies; 11+ messages in thread From: kernel test robot @ 2025-12-20 18:21 UTC (permalink / raw) To: Xianwei Zhao via B4 Relay, Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Kees Cook, Gustavo A. R. Silva Cc: oe-kbuild-all, linux-amlogic, dmaengine, devicetree, linux-kernel, linux-hardening, Xianwei Zhao Hi Xianwei, kernel test robot noticed the following build errors: [auto build test ERROR on 8f0b4cce4481fb22653697cced8d0d04027cb1e8] url: https://github.com/intel-lab-lkp/linux/commits/Xianwei-Zhao-via-B4-Relay/dt-bindings-dma-Add-Amlogic-general-DMA/20251216-163238 base: 8f0b4cce4481fb22653697cced8d0d04027cb1e8 patch link: https://lore.kernel.org/r/20251216-amlogic-dma-v1-2-e289e57e96a7%40amlogic.com patch subject: [PATCH 2/3] dma: amlogic: Add general DMA driver for SoCs config: arm-allyesconfig (https://download.01.org/0day-ci/archive/20251221/202512210111.1UZxHivt-lkp@intel.com/config) compiler: arm-linux-gnueabi-gcc (GCC) 15.1.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251221/202512210111.1UZxHivt-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202512210111.1UZxHivt-lkp@intel.com/ All errors (new ones prefixed by >>): >> drivers/dma/amlogic-dma.c:132:41: error: argument 'chan_ns' to the 'counted_by' attribute is not a field declaration in the same structure as 'aml_chans' 132 | struct aml_dma_chan aml_chans[]__counted_by(chan_ns); | ^~~~~~~~~ vim +132 drivers/dma/amlogic-dma.c 120 121 struct aml_dma_dev { 122 struct dma_device dma_device; 123 void __iomem *base; 124 struct regmap *regmap; 125 struct clk *clk; 126 int irq; 127 struct platform_device *pdev; 128 struct aml_dma_chan *aml_rch[MAX_CHAN_ID]; 129 struct aml_dma_chan *aml_wch[MAX_CHAN_ID]; 130 unsigned int chan_nr; 131 unsigned int chan_used; > 132 struct aml_dma_chan aml_chans[]__counted_by(chan_ns); 133 }; 134 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki _______________________________________________ linux-amlogic mailing list linux-amlogic@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-amlogic ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 3/3] MAINTAINERS: Add an entry for Amlogic DMA driver 2025-12-16 8:03 [PATCH 0/3] Add Amlogic general DMA Xianwei Zhao via B4 Relay 2025-12-16 8:03 ` [PATCH 1/3] dt-bindings: dma: " Xianwei Zhao via B4 Relay 2025-12-16 8:03 ` [PATCH 2/3] dma: amlogic: Add general DMA driver for SoCs Xianwei Zhao via B4 Relay @ 2025-12-16 8:03 ` Xianwei Zhao via B4 Relay 2 siblings, 0 replies; 11+ messages in thread From: Xianwei Zhao via B4 Relay @ 2025-12-16 8:03 UTC (permalink / raw) To: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Kees Cook, Gustavo A. R. Silva Cc: linux-amlogic, dmaengine, devicetree, linux-kernel, linux-hardening, Xianwei Zhao From: Xianwei Zhao <xianwei.zhao@amlogic.com> Add Amlogic DMA controller entry to MAINTAINERS to clarify the maintainers. Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com> --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5b11839cba9d..f522a34c780c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1305,6 +1305,13 @@ F: Documentation/devicetree/bindings/perf/amlogic,g12-ddr-pmu.yaml F: drivers/perf/amlogic/ F: include/soc/amlogic/ +AMLOGIC DMA DRIVER +M: Xianwei Zhao <xianwei.zhao@amlogic.com> +L: linux-amlogic@lists.infradead.org +S: Maintained +F: Documentation/devicetree/bindings/dma/amlogic,general-dma.yaml +F: drivers/dma/amlogic-dma.c + AMLOGIC ISP DRIVER M: Keke Li <keke.li@amlogic.com> L: linux-media@vger.kernel.org -- 2.52.0 _______________________________________________ linux-amlogic mailing list linux-amlogic@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-amlogic ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-12-20 18:23 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2025-12-16 8:03 [PATCH 0/3] Add Amlogic general DMA Xianwei Zhao via B4 Relay 2025-12-16 8:03 ` [PATCH 1/3] dt-bindings: dma: " Xianwei Zhao via B4 Relay 2025-12-16 8:45 ` Krzysztof Kozlowski 2025-12-19 1:58 ` Xianwei Zhao 2025-12-16 8:03 ` [PATCH 2/3] dma: amlogic: Add general DMA driver for SoCs Xianwei Zhao via B4 Relay 2025-12-16 8:46 ` Krzysztof Kozlowski 2025-12-19 2:04 ` Xianwei Zhao 2025-12-16 9:09 ` neil.armstrong 2025-12-19 2:35 ` Xianwei Zhao 2025-12-20 18:21 ` kernel test robot 2025-12-16 8:03 ` [PATCH 3/3] MAINTAINERS: Add an entry for Amlogic DMA driver Xianwei Zhao via B4 Relay
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
Powered by JetHome