From e3f9ffd4e695a76f923612599e6e80d193d2f46b Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Mon, 18 May 2026 11:32:41 +0800 Subject: [PATCH 1/5] dt-bindings: dmaengine: Add SpacemiT K3 DMA compatible string mainline inclusion from mainline-7.2-rc1 commit 55620b11186c81757b05fb8e2df9ddc7127d6fd2 category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- Add the "spacemit,k3-pdma" compatible string for the SpacemiT K3 SoC. While the K3 PDMA IP reuses most of the design found on the earlier K1 SoC, a new compatible string is required because the DRCMR (DMA Request/Command Register) base address for extended DMA request numbers (>= 64) differs from the K1 implementation. Signed-off-by: Guodong Xu Acked-by: Conor Dooley Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260518-k3-pdma-v6-1-67fdf319a8f8@linux.spacemit.com Signed-off-by: Vinod Koul Signed-off-by: Yongchao Jia --- .../bindings/dma/spacemit,k1-pdma.yaml | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml diff --git a/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml b/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml new file mode 100644 index 0000000000000..62ce6d81526b4 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.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/spacemit,k1-pdma.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SpacemiT K1 PDMA Controller + +maintainers: + - Guodong Xu + +allOf: + - $ref: dma-controller.yaml# + +properties: + compatible: + enum: + - spacemit,k1-pdma + - spacemit,k3-pdma + + reg: + maxItems: 1 + + interrupts: + description: Shared interrupt for all DMA channels + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + dma-channels: + maximum: 16 + + '#dma-cells': + const: 1 + description: + The DMA request number for the peripheral device. + +required: + - compatible + - reg + - interrupts + - clocks + - resets + - dma-channels + - '#dma-cells' + +unevaluatedProperties: false + +examples: + - | + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + dma-controller@d4000000 { + compatible = "spacemit,k1-pdma"; + reg = <0x0 0xd4000000 0x0 0x4000>; + interrupts = <72>; + clocks = <&syscon_apmu CLK_DMA>; + resets = <&syscon_apmu RESET_DMA>; + dma-channels = <16>; + #dma-cells = <1>; + }; + }; From 3a7be98d7820a75c675fead9c208162f2370f0dc Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Mon, 18 May 2026 11:32:42 +0800 Subject: [PATCH 2/5] dmaengine: mmp_pdma: refactor DRCMR access with helper function mainline inclusion from mainline-7.2-rc1 commit f46b47623e70dea8b03794a5420ffba060425e85 category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- Refactor the DRCMR macro into a helper function mmp_pdma_get_drcmr() to support variable extended DRCMR base addresses across different PDMA implementations, such as SpacemiT K3. Signed-off-by: Guodong Xu Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260518-k3-pdma-v6-2-67fdf319a8f8@linux.spacemit.com Signed-off-by: Vinod Koul Signed-off-by: Yongchao Jia --- drivers/dma/mmp_pdma.c | 173 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 167 insertions(+), 6 deletions(-) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 046358e328eba..58cd89a1c7ae5 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -46,7 +46,9 @@ #define DCSR_CMPST BIT(10) /* The Descriptor Compare Status */ #define DCSR_EORINTR BIT(9) /* The end of Receive */ -#define DRCMR(n) ((((n) < 64) ? 0x0100 : 0x1100) + (((n) & 0x3f) << 2)) +#define DRCMR_BASE 0x0100 +#define DRCMR_EXT_BASE_DEFAULT 0x1100 +#define DRCMR_REQ_LIMIT 64 #define DRCMR_MAPVLD BIT(7) /* Map Valid (read / write) */ #define DRCMR_CHLNUM 0x1f /* mask for Channel Number (read / write) */ @@ -119,6 +121,50 @@ struct mmp_pdma_phy { struct mmp_pdma_chan *vchan; }; +/** + * struct mmp_pdma_ops - Operations for the MMP PDMA controller + * + * Hardware Register Operations (read/write hardware registers): + * @write_next_addr: Function to program address of next descriptor into + * DDADR/DDADRH + * @read_src_addr: Function to read the source address from DSADR/DSADRH + * @read_dst_addr: Function to read the destination address from DTADR/DTADRH + * + * Descriptor Memory Operations (manipulate descriptor structs in memory): + * @set_desc_next_addr: Function to set next descriptor address in descriptor + * @set_desc_src_addr: Function to set the source address in descriptor + * @set_desc_dst_addr: Function to set the destination address in descriptor + * @get_desc_src_addr: Function to get the source address from descriptor + * @get_desc_dst_addr: Function to get the destination address from descriptor + * + * Controller Configuration: + * @run_bits: Control bits in DCSR register for channel start/stop + * @dma_width: DMA addressing width in bits (32 or 64). Determines the + * DMA mask capability of the controller hardware. + * @drcmr_ext_base: Base DRCMR address for extended requests + */ +struct mmp_pdma_ops { + /* Hardware Register Operations */ + void (*write_next_addr)(struct mmp_pdma_phy *phy, dma_addr_t addr); + u64 (*read_src_addr)(struct mmp_pdma_phy *phy); + u64 (*read_dst_addr)(struct mmp_pdma_phy *phy); + + /* Descriptor Memory Operations */ + void (*set_desc_next_addr)(struct mmp_pdma_desc_hw *desc, + dma_addr_t addr); + void (*set_desc_src_addr)(struct mmp_pdma_desc_hw *desc, + dma_addr_t addr); + void (*set_desc_dst_addr)(struct mmp_pdma_desc_hw *desc, + dma_addr_t addr); + u64 (*get_desc_src_addr)(const struct mmp_pdma_desc_hw *desc); + u64 (*get_desc_dst_addr)(const struct mmp_pdma_desc_hw *desc); + + /* Controller Configuration */ + u32 run_bits; + u32 dma_width; + u32 drcmr_ext_base; +}; + struct mmp_pdma_device { int dma_channels; void __iomem *base; @@ -137,9 +183,94 @@ struct mmp_pdma_device { #define to_mmp_pdma_dev(dmadev) \ container_of(dmadev, struct mmp_pdma_device, device) -static int mmp_pdma_config_write(struct dma_chan *dchan, - struct dma_slave_config *cfg, - enum dma_transfer_direction direction); +static u32 mmp_pdma_get_drcmr(struct mmp_pdma_device *pdev, u32 drcmr) +{ + if (drcmr < DRCMR_REQ_LIMIT) + return DRCMR_BASE + (drcmr << 2); + return pdev->ops->drcmr_ext_base + ((drcmr - DRCMR_REQ_LIMIT) << 2); +} + +/* For 32-bit PDMA */ +static void write_next_addr_32(struct mmp_pdma_phy *phy, dma_addr_t addr) +{ + writel(addr, phy->base + DDADR(phy->idx)); +} + +static u64 read_src_addr_32(struct mmp_pdma_phy *phy) +{ + return readl(phy->base + DSADR(phy->idx)); +} + +static u64 read_dst_addr_32(struct mmp_pdma_phy *phy) +{ + return readl(phy->base + DTADR(phy->idx)); +} + +static void set_desc_next_addr_32(struct mmp_pdma_desc_hw *desc, dma_addr_t addr) +{ + desc->ddadr = addr; +} + +static void set_desc_src_addr_32(struct mmp_pdma_desc_hw *desc, dma_addr_t addr) +{ + desc->dsadr = addr; +} + +static void set_desc_dst_addr_32(struct mmp_pdma_desc_hw *desc, dma_addr_t addr) +{ + desc->dtadr = addr; +} + +static u64 get_desc_src_addr_32(const struct mmp_pdma_desc_hw *desc) +{ + return desc->dsadr; +} + +static u64 get_desc_dst_addr_32(const struct mmp_pdma_desc_hw *desc) +{ + return desc->dtadr; +} + +/* For 64-bit PDMA */ +static void write_next_addr_64(struct mmp_pdma_phy *phy, dma_addr_t addr) +{ + writel(lower_32_bits(addr), phy->base + DDADR(phy->idx)); + writel(upper_32_bits(addr), phy->base + DDADRH(phy->idx)); +} + +static u64 read_src_addr_64(struct mmp_pdma_phy *phy) +{ + u32 low = readl(phy->base + DSADR(phy->idx)); + u32 high = readl(phy->base + DSADRH(phy->idx)); + + return ((u64)high << 32) | low; +} + +static u64 read_dst_addr_64(struct mmp_pdma_phy *phy) +{ + u32 low = readl(phy->base + DTADR(phy->idx)); + u32 high = readl(phy->base + DTADRH(phy->idx)); + + return ((u64)high << 32) | low; +} + +static void set_desc_next_addr_64(struct mmp_pdma_desc_hw *desc, dma_addr_t addr) +{ + desc->ddadr = lower_32_bits(addr); + desc->ddadrh = upper_32_bits(addr); +} + +static void set_desc_src_addr_64(struct mmp_pdma_desc_hw *desc, dma_addr_t addr) +{ + desc->dsadr = lower_32_bits(addr); + desc->dsadrh = upper_32_bits(addr); +} + +static void set_desc_dst_addr_64(struct mmp_pdma_desc_hw *desc, dma_addr_t addr) +{ + desc->dtadr = lower_32_bits(addr); + desc->dtadrh = upper_32_bits(addr); +} static void set_desc(struct mmp_pdma_phy *phy, dma_addr_t addr) { @@ -155,7 +286,9 @@ static void enable_chan(struct mmp_pdma_phy *phy) if (!phy->vchan) return; - reg = DRCMR(phy->vchan->drcmr); + pdev = to_mmp_pdma_dev(phy->vchan->chan.device); + + reg = mmp_pdma_get_drcmr(pdev, phy->vchan->drcmr); writel(DRCMR_MAPVLD | phy->idx, phy->base + reg); dalgn = readl(phy->base + DALGN); @@ -280,7 +413,7 @@ static void mmp_pdma_free_phy(struct mmp_pdma_chan *pchan) return; /* clear the channel mapping in DRCMR */ - reg = DRCMR(pchan->drcmr); + reg = mmp_pdma_get_drcmr(pdev, pchan->drcmr); writel(0, pchan->phy->base + reg); spin_lock_irqsave(&pdev->phy_lock, flags); @@ -1002,6 +1135,34 @@ static int mmp_pdma_chan_init(struct mmp_pdma_device *pdev, int idx, int irq) return 0; } +static const struct mmp_pdma_ops marvell_pdma_v1_ops = { + .write_next_addr = write_next_addr_32, + .read_src_addr = read_src_addr_32, + .read_dst_addr = read_dst_addr_32, + .set_desc_next_addr = set_desc_next_addr_32, + .set_desc_src_addr = set_desc_src_addr_32, + .set_desc_dst_addr = set_desc_dst_addr_32, + .get_desc_src_addr = get_desc_src_addr_32, + .get_desc_dst_addr = get_desc_dst_addr_32, + .run_bits = (DCSR_RUN), + .dma_width = 32, + .drcmr_ext_base = DRCMR_EXT_BASE_DEFAULT, +}; + +static const struct mmp_pdma_ops spacemit_k1_pdma_ops = { + .write_next_addr = write_next_addr_64, + .read_src_addr = read_src_addr_64, + .read_dst_addr = read_dst_addr_64, + .set_desc_next_addr = set_desc_next_addr_64, + .set_desc_src_addr = set_desc_src_addr_64, + .set_desc_dst_addr = set_desc_dst_addr_64, + .get_desc_src_addr = get_desc_src_addr_64, + .get_desc_dst_addr = get_desc_dst_addr_64, + .run_bits = (DCSR_RUN | DCSR_LPAEEN), + .dma_width = 64, + .drcmr_ext_base = DRCMR_EXT_BASE_DEFAULT, +}; + static const struct of_device_id mmp_pdma_dt_ids[] = { { .compatible = "marvell,pdma-1.0", }, {} From cb34e472b3f399a85a3a708ff8e0bef1377b05d2 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Mon, 18 May 2026 11:32:43 +0800 Subject: [PATCH 3/5] dmaengine: mmp_pdma: add SpacemiT K3 support mainline inclusion from mainline-7.2-rc1 commit 6587b8661a0b61c2f4b260bfc9f0e9ef9de0ea2e category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- SpacemiT K3 reuses most of the PDMA IP design found on K1, with one difference being the extended DRCMR base address. Add "spacemit,k3-pdma" compatible string and define a new mmp_pdma_ops for K3 PDMA. Signed-off-by: Guodong Xu Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260518-k3-pdma-v6-3-67fdf319a8f8@linux.spacemit.com Signed-off-by: Vinod Koul Signed-off-by: Yongchao Jia --- drivers/dma/mmp_pdma.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 58cd89a1c7ae5..b3aa55e62e75e 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -47,6 +47,7 @@ #define DCSR_EORINTR BIT(9) /* The end of Receive */ #define DRCMR_BASE 0x0100 +#define DRCMR_EXT_BASE_K3 0x1000 #define DRCMR_EXT_BASE_DEFAULT 0x1100 #define DRCMR_REQ_LIMIT 64 #define DRCMR_MAPVLD BIT(7) /* Map Valid (read / write) */ @@ -1163,9 +1164,33 @@ static const struct mmp_pdma_ops spacemit_k1_pdma_ops = { .drcmr_ext_base = DRCMR_EXT_BASE_DEFAULT, }; +static const struct mmp_pdma_ops spacemit_k3_pdma_ops = { + .write_next_addr = write_next_addr_64, + .read_src_addr = read_src_addr_64, + .read_dst_addr = read_dst_addr_64, + .set_desc_next_addr = set_desc_next_addr_64, + .set_desc_src_addr = set_desc_src_addr_64, + .set_desc_dst_addr = set_desc_dst_addr_64, + .get_desc_src_addr = get_desc_src_addr_64, + .get_desc_dst_addr = get_desc_dst_addr_64, + .run_bits = (DCSR_RUN | DCSR_LPAEEN | DCSR_EORIRQEN | DCSR_EORSTOPEN), + .dma_width = 64, + .drcmr_ext_base = DRCMR_EXT_BASE_K3, +}; + static const struct of_device_id mmp_pdma_dt_ids[] = { - { .compatible = "marvell,pdma-1.0", }, - {} + { + .compatible = "marvell,pdma-1.0", + .data = &marvell_pdma_v1_ops + }, { + .compatible = "spacemit,k1-pdma", + .data = &spacemit_k1_pdma_ops + }, { + .compatible = "spacemit,k3-pdma", + .data = &spacemit_k3_pdma_ops + }, { + /* sentinel */ + } }; MODULE_DEVICE_TABLE(of, mmp_pdma_dt_ids); From 4605e50434e4213af29840d696d5410853906e5b Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Thu, 2 Jul 2026 14:29:06 +0800 Subject: [PATCH 4/5] riscv: dts: spacemit: Add PDMA controller node for K3 SoC mainline inclusion from mainline-7.2-rc1 commit 3f47ca8bb3c3f4a71688451a0cb7350d3e1e1059 category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- Add the Peripheral DMA (PDMA) controller node for the SpacemiT K3 SoC. The PDMA controller provides general-purpose DMA capabilities for various peripheral devices across the system to offload CPU data transfers. Unlike the previous K1 SoC, where some DMA masters had memory addressing limitations (e.g. restricted to the 0-4GB space) requiring a dedicated dma-bus with dma-ranges to restrict memory allocations, the K3 DMA masters have full memory addressing capabilities. Therefore, the PDMA node is now instantiated directly under the main soc bus. Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260518-k3-pdma-v6-4-67fdf319a8f8@linux.spacemit.com Signed-off-by: Yixun Lan Added `interrupt-parent = <&saplic>;` to the PDMA node in k3.dtsi, because the `soc` node in our v6.6 tree does not inherit the interrupt controller context globally. Signed-off-by: Yongchao Jia --- arch/riscv/boot/dts/spacemit/k3.dtsi | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 974ea1ea6cf05..30f54d7c90728 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -553,6 +553,18 @@ #reset-cells = <1>; }; + pdma: dma-controller@d4000000 { + compatible = "spacemit,k3-pdma"; + reg = <0x0 0xd4000000 0x0 0x4000>; + clocks = <&syscon_apmu CLK_APMU_DMA>; + resets = <&syscon_apmu RESET_APMU_DMA>; + interrupt-parent = <&saplic>; + interrupts = <72 IRQ_TYPE_LEVEL_HIGH>; + dma-channels = <16>; + #dma-cells = <1>; + status = "disabled"; + }; + uart0: serial@d4017000 { compatible = "spacemit,k1-uart", "intel,xscale-uart"; @@ -912,3 +924,4 @@ }; }; }; + From 3c50a0a832178fc5f4e9159c1e066e1e8642a180 Mon Sep 17 00:00:00 2001 From: Yongchao Jia Date: Thu, 2 Jul 2026 17:42:17 +0800 Subject: [PATCH 5/5] dmaengine: mmp_pdma: Adapt SpacemiT K3 support rvck inclusion category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- Adaptation details: - Added initialization for clock (`clk`) and reset control (`rst`) in `mmp_pdma_probe()` to match the hardware requirements on v6.6. - Replaced the deprecated `of_match_device()` with `of_device_get_match_data()`. - Updated DMA mask configuration to use `dma_set_mask_and_coherent()` based on the hardware's supported dma_width, replacing legacy masks. - Removed dependencies on v7.x specific macros and header files Signed-off-by: Yongchao Jia --- drivers/dma/mmp_pdma.c | 140 ++++++++++++++++++++++++++++------------- 1 file changed, 98 insertions(+), 42 deletions(-) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index b3aa55e62e75e..9417e0dec6d3b 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -15,7 +15,8 @@ #include #include #include -#include +#include +#include #include #include @@ -24,9 +25,12 @@ #define DCSR 0x0000 #define DALGN 0x00a0 #define DINT 0x00f0 -#define DDADR 0x0200 +#define DDADR(n) (0x0200 + ((n) << 4)) #define DSADR(n) (0x0204 + ((n) << 4)) #define DTADR(n) (0x0208 + ((n) << 4)) +#define DDADRH(n) (0x0300 + ((n) << 4)) +#define DSADRH(n) (0x0304 + ((n) << 4)) +#define DTADRH(n) (0x0308 + ((n) << 4)) #define DCMD 0x020c #define DCSR_RUN BIT(31) /* Run Bit (read / write) */ @@ -43,11 +47,11 @@ #define DCSR_EORSTOPEN BIT(26) /* STOP on an EOR */ #define DCSR_SETCMPST BIT(25) /* Set Descriptor Compare Status */ #define DCSR_CLRCMPST BIT(24) /* Clear Descriptor Compare Status */ +#define DCSR_LPAEEN BIT(21) /* Long Physical Address Extension Enable */ #define DCSR_CMPST BIT(10) /* The Descriptor Compare Status */ #define DCSR_EORINTR BIT(9) /* The end of Receive */ #define DRCMR_BASE 0x0100 -#define DRCMR_EXT_BASE_K3 0x1000 #define DRCMR_EXT_BASE_DEFAULT 0x1100 #define DRCMR_REQ_LIMIT 64 #define DRCMR_MAPVLD BIT(7) /* Map Valid (read / write) */ @@ -78,6 +82,16 @@ struct mmp_pdma_desc_hw { u32 dsadr; /* DSADR value for the current transfer */ u32 dtadr; /* DTADR value for the current transfer */ u32 dcmd; /* DCMD value for the current transfer */ + /* + * The following 32-bit words are only used in the 64-bit, ie. + * LPAE (Long Physical Address Extension) mode. + * They are used to specify the high 32 bits of the descriptor's + * addresses. + */ + u32 ddadrh; /* High 32-bit of DDADR */ + u32 dsadrh; /* High 32-bit of DSADR */ + u32 dtadrh; /* High 32-bit of DTADR */ + u32 rsvd; /* reserved */ } __aligned(32); struct mmp_pdma_desc_sw { @@ -172,6 +186,7 @@ struct mmp_pdma_device { struct device *dev; struct dma_device device; struct mmp_pdma_phy *phy; + const struct mmp_pdma_ops *ops; spinlock_t phy_lock; /* protect alloc/free phy channels */ }; @@ -273,16 +288,24 @@ static void set_desc_dst_addr_64(struct mmp_pdma_desc_hw *desc, dma_addr_t addr) desc->dtadrh = upper_32_bits(addr); } -static void set_desc(struct mmp_pdma_phy *phy, dma_addr_t addr) +static u64 get_desc_src_addr_64(const struct mmp_pdma_desc_hw *desc) { - u32 reg = (phy->idx << 4) + DDADR; + return ((u64)desc->dsadrh << 32) | desc->dsadr; +} - writel(addr, phy->base + reg); +static u64 get_desc_dst_addr_64(const struct mmp_pdma_desc_hw *desc) +{ + return ((u64)desc->dtadrh << 32) | desc->dtadr; } +static int mmp_pdma_config_write(struct dma_chan *dchan, + struct dma_slave_config *cfg, + enum dma_transfer_direction direction); + static void enable_chan(struct mmp_pdma_phy *phy) { u32 reg, dalgn; + struct mmp_pdma_device *pdev; if (!phy->vchan) return; @@ -300,18 +323,29 @@ static void enable_chan(struct mmp_pdma_phy *phy) writel(dalgn, phy->base + DALGN); reg = (phy->idx << 2) + DCSR; - writel(readl(phy->base + reg) | DCSR_RUN, phy->base + reg); + writel(readl(phy->base + reg) | pdev->ops->run_bits, + phy->base + reg); } static void disable_chan(struct mmp_pdma_phy *phy) { - u32 reg; + u32 reg, dcsr; if (!phy) return; reg = (phy->idx << 2) + DCSR; - writel(readl(phy->base + reg) & ~DCSR_RUN, phy->base + reg); + dcsr = readl(phy->base + reg); + + if (phy->vchan) { + struct mmp_pdma_device *pdev; + + pdev = to_mmp_pdma_dev(phy->vchan->chan.device); + writel(dcsr & ~pdev->ops->run_bits, phy->base + reg); + } else { + /* If no vchan, just clear the RUN bit */ + writel(dcsr & ~DCSR_RUN, phy->base + reg); + } } static int clear_chan_irq(struct mmp_pdma_phy *phy) @@ -430,6 +464,7 @@ static void mmp_pdma_free_phy(struct mmp_pdma_chan *pchan) static void start_pending_queue(struct mmp_pdma_chan *chan) { struct mmp_pdma_desc_sw *desc; + struct mmp_pdma_device *pdev = to_mmp_pdma_dev(chan->chan.device); /* still in running, irq will start the pending list */ if (!chan->idle) { @@ -464,7 +499,7 @@ static void start_pending_queue(struct mmp_pdma_chan *chan) * Program the descriptor's address into the DMA controller, * then start the DMA transaction */ - set_desc(chan->phy, desc->async_tx.phys); + pdev->ops->write_next_addr(chan->phy, desc->async_tx.phys); enable_chan(chan->phy); chan->idle = false; } @@ -580,15 +615,14 @@ mmp_pdma_prep_memcpy(struct dma_chan *dchan, size_t len, unsigned long flags) { struct mmp_pdma_chan *chan; + struct mmp_pdma_device *pdev; struct mmp_pdma_desc_sw *first = NULL, *prev = NULL, *new; size_t copy = 0; - if (!dchan) - return NULL; - - if (!len) + if (!dchan || !len) return NULL; + pdev = to_mmp_pdma_dev(dchan->device); chan = to_mmp_pdma_chan(dchan); chan->byte_align = false; @@ -611,13 +645,14 @@ mmp_pdma_prep_memcpy(struct dma_chan *dchan, chan->byte_align = true; new->desc.dcmd = chan->dcmd | (DCMD_LENGTH & copy); - new->desc.dsadr = dma_src; - new->desc.dtadr = dma_dst; + pdev->ops->set_desc_src_addr(&new->desc, dma_src); + pdev->ops->set_desc_dst_addr(&new->desc, dma_dst); if (!first) first = new; else - prev->desc.ddadr = new->async_tx.phys; + pdev->ops->set_desc_next_addr(&prev->desc, + new->async_tx.phys); new->async_tx.cookie = 0; async_tx_ack(&new->async_tx); @@ -661,6 +696,7 @@ mmp_pdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl, unsigned long flags, void *context) { struct mmp_pdma_chan *chan = to_mmp_pdma_chan(dchan); + struct mmp_pdma_device *pdev = to_mmp_pdma_dev(dchan->device); struct mmp_pdma_desc_sw *first = NULL, *prev = NULL, *new = NULL; size_t len, avail; struct scatterlist *sg; @@ -692,17 +728,18 @@ mmp_pdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl, new->desc.dcmd = chan->dcmd | (DCMD_LENGTH & len); if (dir == DMA_MEM_TO_DEV) { - new->desc.dsadr = addr; + pdev->ops->set_desc_src_addr(&new->desc, addr); new->desc.dtadr = chan->dev_addr; } else { new->desc.dsadr = chan->dev_addr; - new->desc.dtadr = addr; + pdev->ops->set_desc_dst_addr(&new->desc, addr); } if (!first) first = new; else - prev->desc.ddadr = new->async_tx.phys; + pdev->ops->set_desc_next_addr(&prev->desc, + new->async_tx.phys); new->async_tx.cookie = 0; async_tx_ack(&new->async_tx); @@ -742,12 +779,15 @@ mmp_pdma_prep_dma_cyclic(struct dma_chan *dchan, unsigned long flags) { struct mmp_pdma_chan *chan; + struct mmp_pdma_device *pdev; struct mmp_pdma_desc_sw *first = NULL, *prev = NULL, *new; dma_addr_t dma_src, dma_dst; if (!dchan || !len || !period_len) return NULL; + pdev = to_mmp_pdma_dev(dchan->device); + /* the buffer length must be a multiple of period_len */ if (len % period_len != 0) return NULL; @@ -784,13 +824,14 @@ mmp_pdma_prep_dma_cyclic(struct dma_chan *dchan, new->desc.dcmd = (chan->dcmd | DCMD_ENDIRQEN | (DCMD_LENGTH & period_len)); - new->desc.dsadr = dma_src; - new->desc.dtadr = dma_dst; + pdev->ops->set_desc_src_addr(&new->desc, dma_src); + pdev->ops->set_desc_dst_addr(&new->desc, dma_dst); if (!first) first = new; else - prev->desc.ddadr = new->async_tx.phys; + pdev->ops->set_desc_next_addr(&prev->desc, + new->async_tx.phys); new->async_tx.cookie = 0; async_tx_ack(&new->async_tx); @@ -811,7 +852,7 @@ mmp_pdma_prep_dma_cyclic(struct dma_chan *dchan, first->async_tx.cookie = -EBUSY; /* make the cyclic link */ - new->desc.ddadr = first->async_tx.phys; + pdev->ops->set_desc_next_addr(&new->desc, first->async_tx.phys); chan->cyclic_first = first; return &first->async_tx; @@ -897,8 +938,10 @@ static unsigned int mmp_pdma_residue(struct mmp_pdma_chan *chan, dma_cookie_t cookie) { struct mmp_pdma_desc_sw *sw; - u32 curr, residue = 0; + struct mmp_pdma_device *pdev = to_mmp_pdma_dev(chan->chan.device); unsigned long flags; + u64 curr; + u32 residue = 0; bool passed = false; bool cyclic = chan->cyclic_first != NULL; @@ -910,19 +953,20 @@ static unsigned int mmp_pdma_residue(struct mmp_pdma_chan *chan, return 0; if (chan->dir == DMA_DEV_TO_MEM) - curr = readl(chan->phy->base + DTADR(chan->phy->idx)); + curr = pdev->ops->read_dst_addr(chan->phy); else - curr = readl(chan->phy->base + DSADR(chan->phy->idx)); + curr = pdev->ops->read_src_addr(chan->phy); spin_lock_irqsave(&chan->desc_lock, flags); list_for_each_entry(sw, &chan->chain_running, node) { - u32 start, end, len; + u64 start, end; + u32 len; if (chan->dir == DMA_DEV_TO_MEM) - start = sw->desc.dtadr; + start = pdev->ops->get_desc_dst_addr(&sw->desc); else - start = sw->desc.dsadr; + start = pdev->ops->get_desc_src_addr(&sw->desc); len = sw->desc.dcmd & DCMD_LENGTH; end = start + len; @@ -938,7 +982,7 @@ static unsigned int mmp_pdma_residue(struct mmp_pdma_chan *chan, if (passed) { residue += len; } else if (curr >= start && curr <= end) { - residue += end - curr; + residue += (u32)(end - curr); passed = true; } @@ -1072,7 +1116,7 @@ static void dma_do_tasklet(struct tasklet_struct *t) } } -static int mmp_pdma_remove(struct platform_device *op) +static void mmp_pdma_remove(struct platform_device *op) { struct mmp_pdma_device *pdev = platform_get_drvdata(op); struct mmp_pdma_phy *phy; @@ -1098,7 +1142,6 @@ static int mmp_pdma_remove(struct platform_device *op) } dma_async_device_unregister(&pdev->device); - return 0; } static int mmp_pdma_chan_init(struct mmp_pdma_device *pdev, int idx, int irq) @@ -1175,7 +1218,7 @@ static const struct mmp_pdma_ops spacemit_k3_pdma_ops = { .get_desc_dst_addr = get_desc_dst_addr_64, .run_bits = (DCSR_RUN | DCSR_LPAEEN | DCSR_EORIRQEN | DCSR_EORSTOPEN), .dma_width = 64, - .drcmr_ext_base = DRCMR_EXT_BASE_K3, + .drcmr_ext_base = DRCMR_EXT_BASE_DEFAULT, }; static const struct of_device_id mmp_pdma_dt_ids[] = { @@ -1212,8 +1255,9 @@ static struct dma_chan *mmp_pdma_dma_xlate(struct of_phandle_args *dma_spec, static int mmp_pdma_probe(struct platform_device *op) { struct mmp_pdma_device *pdev; - const struct of_device_id *of_id; struct mmp_dma_platdata *pdata = dev_get_platdata(&op->dev); + struct clk *clk; + struct reset_control *rst; int i, ret, irq = 0; int dma_channels = 0, irq_num = 0; const enum dma_slave_buswidth widths = @@ -1232,8 +1276,21 @@ static int mmp_pdma_probe(struct platform_device *op) if (IS_ERR(pdev->base)) return PTR_ERR(pdev->base); - of_id = of_match_device(mmp_pdma_dt_ids, pdev->dev); - if (of_id) { + clk = devm_clk_get_optional_enabled(pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + rst = devm_reset_control_get_optional_exclusive(pdev->dev, NULL); + if (IS_ERR(rst)) + return PTR_ERR(rst); + if (rst) + reset_control_deassert(rst); + + pdev->ops = of_device_get_match_data(&op->dev); + if (!pdev->ops) + return -ENODEV; + + if (pdev->dev->of_node) { /* Parse new and deprecated dma-channels properties */ if (of_property_read_u32(pdev->dev->of_node, "dma-channels", &dma_channels)) @@ -1294,10 +1351,9 @@ static int mmp_pdma_probe(struct platform_device *op) pdev->device.directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM); pdev->device.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; - if (pdev->dev->coherent_dma_mask) - dma_set_mask(pdev->dev, pdev->dev->coherent_dma_mask); - else - dma_set_mask(pdev->dev, DMA_BIT_MASK(64)); + /* Set DMA mask based on controller hardware capabilities */ + dma_set_mask_and_coherent(pdev->dev, + DMA_BIT_MASK(pdev->ops->dma_width)); ret = dma_async_device_register(&pdev->device); if (ret) { @@ -1333,7 +1389,7 @@ static struct platform_driver mmp_pdma_driver = { }, .id_table = mmp_pdma_id_table, .probe = mmp_pdma_probe, - .remove = mmp_pdma_remove, + .remove_new = mmp_pdma_remove, }; module_platform_driver(mmp_pdma_driver);