From 8327bdcc9c14366f1124ea62741a47e17706a243 Mon Sep 17 00:00:00 2001 From: Yongchao Jia Date: Wed, 22 Jul 2026 17:40:19 +0800 Subject: [PATCH 01/15] Revert "riscv: config: enable dma driver for spacemit k1" category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- This reverts commit "riscv: config: enable dma driver for spacemit k1". Following the upstream approach, both K1 and K3 will use the mmp_pdma driver, so this configuration is not required. Signed-off-by: Yongchao Jia --- arch/riscv/configs/k1_defconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/riscv/configs/k1_defconfig b/arch/riscv/configs/k1_defconfig index a027c9272a4bd..912e6800b2ad1 100644 --- a/arch/riscv/configs/k1_defconfig +++ b/arch/riscv/configs/k1_defconfig @@ -9,7 +9,6 @@ CONFIG_RESET_K1_SPACEMIT=y CONFIG_PINCTRL_SPACEMIT_K1=y CONFIG_GPIO_K1=y CONFIG_SPACEMIT_MEM_RANGE=y -CONFIG_SPACEMIT_K1_DMA=y CONFIG_I2C_SPACEMIT_K1=y CONFIG_SPI_SPACEMIT_K1=y CONFIG_SPI_SPACEMIT_K1_QSPI=y @@ -24,4 +23,3 @@ CONFIG_SPACEMIT_P1_ADC=m CONFIG_MMC_SDHCI_OF_K1=y CONFIG_NET_VENDOR_SPACEMIT=y CONFIG_K1_EMAC=m - From 03f9deb9f8e5e660a72e219c53dfa754c0969565 Mon Sep 17 00:00:00 2001 From: Yongchao Jia Date: Wed, 22 Jul 2026 17:42:11 +0800 Subject: [PATCH 02/15] Revert "driver: k1: add dma driver support for spacemit k1" category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- This reverts commit "driver: k1: add dma driver support for spacemit k1". Following the upstream approach, both K1 and K3 will use the mmp_pdma driver, no longer using the vendor driver. Signed-off-by: Yongchao Jia --- drivers/dma/Kconfig | 7 - drivers/dma/Makefile | 1 - drivers/dma/spacemit-k1-dma.c | 1515 --------------------------------- 3 files changed, 1523 deletions(-) delete mode 100644 drivers/dma/spacemit-k1-dma.c diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 28c1b1ce58ae5..e36506471a4f6 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -489,13 +489,6 @@ config OWL_DMA help Enable support for the Actions Semi Owl SoCs DMA controller. -config SPACEMIT_K1_DMA - bool "Spacemit k1 SoCs DMA support" - depends on SOC_SPACEMIT_K1 - depends on DMA_ENGINE - help - Enable support for the Spacemit k1 SoCs DMA controller. - config PCH_DMA tristate "Intel EG20T PCH / LAPIS Semicon IOH(ML7213/ML7223/ML7831) DMA" depends on PCI && (X86_32 || COMPILE_TEST) diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 2c9e8b9933754..83553a97a010e 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -66,7 +66,6 @@ obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/ obj-$(CONFIG_PXA_DMA) += pxa_dma.o obj-$(CONFIG_RENESAS_DMA) += sh/ obj-$(CONFIG_SF_PDMA) += sf-pdma/ -obj-$(CONFIG_SPACEMIT_K1_DMA) += spacemit-k1-dma.o obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o obj-$(CONFIG_STM32_DMA) += stm32-dma.o obj-$(CONFIG_STM32_DMAMUX) += stm32-dmamux.o diff --git a/drivers/dma/spacemit-k1-dma.c b/drivers/dma/spacemit-k1-dma.c deleted file mode 100644 index d730ad085e0bd..0000000000000 --- a/drivers/dma/spacemit-k1-dma.c +++ /dev/null @@ -1,1515 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * mmp dma controller driver - * Copyright 2012 Marvell International Ltd. - * Copyright (c) 2023, spacemit Corporation. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "dmaengine.h" - -#define DDADRH(n) (0x0300 + ((n) << 4)) -#define DSADRH(n) (0x0304 + ((n) << 4)) -#define DTADRH(n) (0x0308 + ((n) << 4)) -#define DCSR_LPAEEN BIT(21) -#define DRCMR_INVALID 100 -#define DCMD_BURST64 (4 << 16) - -#define DCSR 0x0000 -#define DALGN 0x00a0 -#define DINT 0x00f0 -#define DDADR 0x0200 -#define DSADR(n) (0x0204 + ((n) << 4)) -#define DTADR(n) (0x0208 + ((n) << 4)) -#define DCMD 0x020c - -#define DCSR_RUN BIT(31) /* Run Bit (read / write) */ -#define DCSR_NODESC BIT(30) /* No-Descriptor Fetch (read / write) */ -#define DCSR_STOPIRQEN BIT(29) /* Stop Interrupt Enable (read / write) */ -#define DCSR_REQPEND BIT(8) /* Request Pending (read-only) */ -#define DCSR_STOPSTATE BIT(3) /* Stop State (read-only) */ -#define DCSR_ENDINTR BIT(2) /* End Interrupt (read / write) */ -#define DCSR_STARTINTR BIT(1) /* Start Interrupt (read / write) */ -#define DCSR_BUSERR BIT(0) /* Bus Error Interrupt (read / write) */ - -#define DCSR_EORIRQEN BIT(28) /* End of Receive Interrupt Enable (R/W) */ -#define DCSR_EORJMPEN BIT(27) /* Jump to next descriptor on EOR */ -#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_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_MAPVLD BIT(7) /* Map Valid (read / write) */ -#define DRCMR_CHLNUM 0x1f /* mask for Channel Number (read / write) */ - -#define DDADR_DESCADDR 0xfffffff0 /* Address of next descriptor (mask) */ -#define DDADR_STOP BIT(0) /* Stop (read / write) */ - -#define DCMD_INCSRCADDR BIT(31) /* Source Address Increment Setting. */ -#define DCMD_INCTRGADDR BIT(30) /* Target Address Increment Setting. */ -#define DCMD_FLOWSRC BIT(29) /* Flow Control by the source. */ -#define DCMD_FLOWTRG BIT(28) /* Flow Control by the target. */ -#define DCMD_STARTIRQEN BIT(22) /* Start Interrupt Enable */ -#define DCMD_ENDIRQEN BIT(21) /* End Interrupt Enable */ -#define DCMD_ENDIAN BIT(18) /* Device Endian-ness. */ -#define DCMD_BURST8 (1 << 16) /* 8 byte burst */ -#define DCMD_BURST16 (2 << 16) /* 16 byte burst */ -#define DCMD_BURST32 (3 << 16) /* 32 byte burst */ -#define DCMD_WIDTH1 (1 << 14) /* 1 byte width */ -#define DCMD_WIDTH2 (2 << 14) /* 2 byte width (HalfWord) */ -#define DCMD_WIDTH4 (3 << 14) /* 4 byte width (Word) */ -#define DCMD_LENGTH 0x01fff /* length mask (max = 8K - 1) */ - -#define PDMA_MAX_DESC_BYTES DCMD_LENGTH -#define PDMA_RESRV_CHAN_ARGS_NUM (2) /* reserved channel arguments count in dts */ - -struct mmp_pdma_desc_hw { - u32 ddadr; /* Points to the next descriptor + flags */ - u32 dsadr; /* DSADR value for the current transfer */ - u32 dtadr; /* DTADR value for the current transfer */ - u32 dcmd; /* DCMD value for the current transfer */ - u32 ddadrh; /* Points to the next descriptor + flags */ - u32 dsadrh; /* DSADR value for the current transfer */ - u32 dtadrh; /* DTADR value for the current transfer */ - u32 rsvd; /* DCMD value for the current transfer */ -} __aligned(64); - -struct mmp_pdma_desc_sw { - struct mmp_pdma_desc_hw desc; - struct list_head node; - struct list_head tx_list; - struct dma_async_tx_descriptor async_tx; -}; - -struct mmp_pdma_phy; - -struct mmp_pdma_chan { - struct device *dev; - struct dma_chan chan; - struct dma_async_tx_descriptor desc; - struct mmp_pdma_phy *phy; - enum dma_transfer_direction dir; - struct dma_slave_config slave_config; - struct mmp_pdma_desc_sw *cyclic_first; - - /* channel's basic info */ - struct tasklet_struct tasklet; - u32 dcmd; - u32 drcmr; - u32 dev_addr; - - /* list for desc */ - spinlock_t desc_lock; /* Descriptor list lock */ - struct list_head chain_pending; /* Link descriptors queue for pending */ - struct list_head chain_running; /* Link descriptors queue for running */ - bool idle; /* channel statue machine */ - bool byte_align; - - int user_do_qos; - int qos_count; /* Per-channel qos count */ - enum dma_status status; /* channel state machine */ - u32 bytes_residue; - - struct dma_pool *desc_pool; /* Descriptors pool */ -}; - -struct mmp_pdma_phy { - int idx; - void __iomem *base; - struct mmp_pdma_chan *vchan; -}; - -struct reserved_chan { - int chan_id; - int drcmr; -}; - -struct mmp_pdma_device { - int dma_channels; - int nr_reserved_channels; - struct reserved_chan *reserved_channels; - s32 lpm_qos; - struct clk *clk; - struct reset_control *resets; - int max_burst_size; - void __iomem *base; - struct device *dev; - struct dma_device device; - struct mmp_pdma_phy *phy; - spinlock_t phy_lock; /* protect alloc/free phy channels */ -}; - -#define tx_to_mmp_pdma_desc(tx) \ - container_of(tx, struct mmp_pdma_desc_sw, async_tx) -#define to_mmp_pdma_desc(lh) \ - container_of(lh, struct mmp_pdma_desc_sw, node) -#define to_mmp_pdma_chan(dchan) \ - container_of(dchan, struct mmp_pdma_chan, chan) -#define to_mmp_pdma_dev(dmadev) \ - container_of(dmadev, struct mmp_pdma_device, device) - -static void set_desc(struct mmp_pdma_phy *phy, dma_addr_t addr) -{ - u32 ddadrh; - u32 reg = (phy->idx << 4) + DDADR; - - writel(addr & 0xffffffff, phy->base + reg); - /* config higher bits for desc address */ - ddadrh = (addr >> 32); - writel(ddadrh, phy->base + DDADRH(phy->idx)); -} - -static void enable_chan(struct mmp_pdma_phy *phy) -{ - u32 reg, dalgn; - u32 dcsr; - unsigned long flags; - struct mmp_pdma_device *pdev; - - if (phy == NULL) - return; - - if (!phy->vchan) - return; - - pdev = to_mmp_pdma_dev(phy->vchan->chan.device); - - spin_lock_irqsave(&pdev->phy_lock, flags); - - reg = DRCMR(phy->vchan->drcmr); - writel(DRCMR_MAPVLD | phy->idx, phy->base + reg); - - dalgn = readl(phy->base + DALGN); - if (phy->vchan->byte_align) - dalgn |= 1 << phy->idx; - else - dalgn &= ~(1 << phy->idx); - writel(dalgn, phy->base + DALGN); - - reg = (phy->idx << 2) + DCSR; - - dcsr = readl(phy->base + reg); - dcsr |= (DCSR_RUN | DCSR_EORIRQEN | DCSR_EORSTOPEN); - /* use long descriptor mode: set DCSR_LPAEEN bit */ - dcsr |= DCSR_LPAEEN; - writel(dcsr, phy->base + reg); - - spin_unlock_irqrestore(&pdev->phy_lock, flags); -} - -static void disable_chan(struct mmp_pdma_phy *phy) -{ - u32 reg; - u32 dcsr, cnt = 1000; - - if (!phy) - return; - - reg = (phy->idx << 2) + DCSR; - - dcsr = readl(phy->base + reg); - dcsr &= ~(DCSR_RUN | DCSR_EORIRQEN | DCSR_EORSTOPEN); - /* use long descriptor mode: set DCSR_LPAEEN bit */ - dcsr &= ~DCSR_LPAEEN; - writel(dcsr, phy->base + reg); - - /* ensure dma is stopped. */ - dcsr = readl(phy->base + reg); - while (!(dcsr & (0x1 << 3)) && --cnt) { - udelay(10); - dcsr = readl(phy->base + reg); - } - - WARN_ON(!cnt); -} - -static int clear_chan_irq(struct mmp_pdma_phy *phy) -{ - u32 dcsr; - u32 dint = readl(phy->base + DINT); - u32 reg = (phy->idx << 2) + DCSR; - - if (!(dint & BIT(phy->idx))) - return -EAGAIN; - - /* clear irq */ - dcsr = readl(phy->base + reg); - writel(dcsr, phy->base + reg); - if ((dcsr & DCSR_BUSERR) && (phy->vchan)) - dev_warn(phy->vchan->dev, "DCSR_BUSERR\n"); - - return 0; -} - -static irqreturn_t mmp_pdma_chan_handler(int irq, void *dev_id) -{ - struct mmp_pdma_phy *phy = dev_id; - struct mmp_pdma_chan *pchan = phy->vchan; - - if (clear_chan_irq(phy) != 0) - return IRQ_NONE; - - if (pchan) - tasklet_schedule(&pchan->tasklet); - - return IRQ_HANDLED; -} - -static bool is_channel_reserved(struct mmp_pdma_device *pdev, int chan_id) -{ - int i; - - for (i = 0; i < pdev->nr_reserved_channels; i++) { - if (chan_id == pdev->reserved_channels[i].chan_id) - return true; - } - - return false; -} - -static struct mmp_pdma_phy *lookup_phy_for_drcmr(struct mmp_pdma_device *pdev, int drcmr) -{ - int i; - int chan_id; - struct mmp_pdma_phy *phy; - - for (i = 0; i < pdev->nr_reserved_channels; i++) { - if (drcmr == pdev->reserved_channels[i].drcmr) { - chan_id = pdev->reserved_channels[i].chan_id; - phy = &pdev->phy[chan_id]; - return phy; - } - } - - return NULL; -} - -static irqreturn_t mmp_pdma_int_handler(int irq, void *dev_id) -{ - struct mmp_pdma_device *pdev = dev_id; - struct mmp_pdma_phy *phy; - u32 dint = readl(pdev->base + DINT); - int i, ret; - int irq_num = 0; - unsigned long flags; - - while (dint) { - i = __ffs(dint); - /* only handle interrupts belonging to pdma driver*/ - if (i >= pdev->dma_channels) - break; - - dint &= (dint - 1); - phy = &pdev->phy[i]; - spin_lock_irqsave(&pdev->phy_lock, flags); - - ret = mmp_pdma_chan_handler(irq, phy); - - spin_unlock_irqrestore(&pdev->phy_lock, flags); - if (ret == IRQ_HANDLED) - irq_num++; - } - - if (irq_num) - return IRQ_HANDLED; - - return IRQ_NONE; -} - -/* - * lookup free phy channel as descending priority - * dma channel priorities - * ch 0 - 3, 16 - 19 <--> (0) - * ch 4 - 7, 20 - 23 <--> (1) - * ch 8 - 11, 24 - 27 <--> (2) - * ch 12 - 15, 28 - 31 <--> (3) - */ -static struct mmp_pdma_phy *lookup_phy(struct mmp_pdma_chan *pchan) -{ - int prio, i; - struct mmp_pdma_device *pdev = to_mmp_pdma_dev(pchan->chan.device); - struct mmp_pdma_phy *phy, *found = NULL; - unsigned long flags; - - spin_lock_irqsave(&pdev->phy_lock, flags); - - phy = lookup_phy_for_drcmr(pdev, pchan->drcmr); - - if (phy != NULL) { - if (!phy->vchan) { - phy->vchan = pchan; - found = phy; - } - - goto out_unlock; - } - - for (prio = 0; prio <= ((pdev->dma_channels - 1) & 0xf) >> 2; prio++) { - for (i = 0; i < pdev->dma_channels; i++) { - if (prio != (i & 0xf) >> 2) - continue; - - if (is_channel_reserved(pdev, i)) - continue; - phy = &pdev->phy[i]; - if (!phy->vchan) { - phy->vchan = pchan; - found = phy; - goto out_unlock; - } - } - } - -out_unlock: - spin_unlock_irqrestore(&pdev->phy_lock, flags); - return found; -} - -static void mmp_pdma_free_phy(struct mmp_pdma_chan *pchan) -{ - struct mmp_pdma_device *pdev = to_mmp_pdma_dev(pchan->chan.device); - unsigned long flags; - u32 reg; - - if (!pchan->phy) - return; - - /* clear the channel mapping in DRCMR */ - reg = DRCMR(pchan->drcmr); - writel(0, pchan->phy->base + reg); - - spin_lock_irqsave(&pdev->phy_lock, flags); - pchan->phy->vchan = NULL; - pchan->phy = NULL; - - spin_unlock_irqrestore(&pdev->phy_lock, flags); -} - -/* - * start_pending_queue - transfer any pending transactions - * pending list ==> running list - */ -static int start_pending_queue(struct mmp_pdma_chan *chan) -{ - struct mmp_pdma_desc_sw *desc; - struct mmp_pdma_desc_sw *_desc; - - /* still in running, irq will start the pending list */ - if (chan->status == DMA_IN_PROGRESS) { - dev_dbg(chan->dev, "DMA controller still busy\n"); - return -1; - } - - if (list_empty(&chan->chain_pending)) { - /* chance to re-fetch phy channel with higher prio */ - mmp_pdma_free_phy(chan); - dev_dbg(chan->dev, "no pending list\n"); - - return -1; - } - - if (!chan->phy) { - chan->phy = lookup_phy(chan); - if (!chan->phy) { - dev_dbg(chan->dev, "no free dma channel\n"); - - return -1; - } - } - - /* - * pending -> running - * reintilize pending list - */ - list_for_each_entry_safe(desc, _desc, &chan->chain_pending, node) { - list_del(&desc->node); - list_add_tail(&desc->node, &chan->chain_running); - if (desc->desc.ddadr & DDADR_STOP) - break; - } - - desc = list_first_entry(&chan->chain_running, - struct mmp_pdma_desc_sw, node); - - /* - * Program the descriptor's address into the DMA controller, - * then start the DMA transaction - */ - set_desc(chan->phy, desc->async_tx.phys); - enable_chan(chan->phy); - chan->idle = false; - chan->status = DMA_IN_PROGRESS; - chan->bytes_residue = 0; - return 0; -} - -/* desc->tx_list ==> pending list */ -static dma_cookie_t mmp_pdma_tx_submit(struct dma_async_tx_descriptor *tx) -{ - struct mmp_pdma_chan *chan = to_mmp_pdma_chan(tx->chan); - struct mmp_pdma_desc_sw *desc = tx_to_mmp_pdma_desc(tx); - struct mmp_pdma_desc_sw *child; - unsigned long flags; - dma_cookie_t cookie = -EBUSY; - - spin_lock_irqsave(&chan->desc_lock, flags); - - list_for_each_entry(child, &desc->tx_list, node) { - cookie = dma_cookie_assign(&child->async_tx); - } - - /* softly link to pending list - desc->tx_list ==> pending list */ - list_splice_tail_init(&desc->tx_list, &chan->chain_pending); - - spin_unlock_irqrestore(&chan->desc_lock, flags); - - return cookie; -} - -static int mmp_pdma_config_write(struct dma_chan *dchan, - struct dma_slave_config *cfg, - enum dma_transfer_direction direction) -{ - struct mmp_pdma_chan *chan = to_mmp_pdma_chan(dchan); - u32 maxburst = 0, addr = 0; - enum dma_slave_buswidth width = DMA_SLAVE_BUSWIDTH_UNDEFINED; - - if (!dchan) - return -EINVAL; - - if (direction == DMA_DEV_TO_MEM) { - chan->dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC; - maxburst = cfg->src_maxburst; - width = cfg->src_addr_width; - addr = cfg->src_addr; - } else if (direction == DMA_MEM_TO_DEV) { - chan->dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; - maxburst = cfg->dst_maxburst; - width = cfg->dst_addr_width; - addr = cfg->dst_addr; - } - - if (width == DMA_SLAVE_BUSWIDTH_1_BYTE) - chan->dcmd |= DCMD_WIDTH1; - else if (width == DMA_SLAVE_BUSWIDTH_2_BYTES) - chan->dcmd |= DCMD_WIDTH2; - else if (width == DMA_SLAVE_BUSWIDTH_4_BYTES) - chan->dcmd |= DCMD_WIDTH4; - - if (maxburst == 8) - chan->dcmd |= DCMD_BURST8; - else if (maxburst == 16) - chan->dcmd |= DCMD_BURST16; - else if (maxburst == 32) - chan->dcmd |= DCMD_BURST32; - - chan->dir = direction; - chan->dev_addr = addr; - - return 0; -} - -static struct mmp_pdma_desc_sw *mmp_pdma_alloc_descriptor(struct mmp_pdma_chan *chan) -{ - struct mmp_pdma_desc_sw *desc; - dma_addr_t pdesc; - - desc = dma_pool_zalloc(chan->desc_pool, GFP_ATOMIC, &pdesc); - if (!desc) { - dev_err(chan->dev, "out of memory for link descriptor\n"); - return NULL; - } - - INIT_LIST_HEAD(&desc->tx_list); - dma_async_tx_descriptor_init(&desc->async_tx, &chan->chan); - /* each desc has submit */ - desc->async_tx.tx_submit = mmp_pdma_tx_submit; - desc->async_tx.phys = pdesc; - - return desc; -} - -/* - * mmp_pdma_alloc_chan_resources - Allocate resources for DMA channel. - * - * This function will create a dma pool for descriptor allocation. - * Request irq only when channel is requested - * Return - The number of allocated descriptors. - */ -static int mmp_pdma_alloc_chan_resources(struct dma_chan *dchan) -{ - struct mmp_pdma_chan *chan = to_mmp_pdma_chan(dchan); - - if (chan->desc_pool) - return 1; - - chan->desc_pool = dma_pool_create(dev_name(&dchan->dev->device), - chan->dev, - sizeof(struct mmp_pdma_desc_sw), - __alignof__(struct mmp_pdma_desc_sw), - 0); - if (!chan->desc_pool) { - dev_err(chan->dev, "unable to allocate descriptor pool\n"); - return -ENOMEM; - } - - chan->status = DMA_COMPLETE; - chan->dir = 0; - chan->dcmd = 0; - - mmp_pdma_free_phy(chan); - - chan->idle = true; - chan->dev_addr = 0; - return 1; -} - -static void mmp_pdma_free_desc_list(struct mmp_pdma_chan *chan, - struct list_head *list) -{ - struct mmp_pdma_desc_sw *desc, *_desc; - - list_for_each_entry_safe(desc, _desc, list, node) { - list_del(&desc->node); - dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys); - } -} - -static void mmp_pdma_free_chan_resources(struct dma_chan *dchan) -{ - struct mmp_pdma_chan *chan = to_mmp_pdma_chan(dchan); - unsigned long flags; - - /* wait until task ends if necessary */ - tasklet_kill(&chan->tasklet); - - spin_lock_irqsave(&chan->desc_lock, flags); - mmp_pdma_free_desc_list(chan, &chan->chain_pending); - mmp_pdma_free_desc_list(chan, &chan->chain_running); - - spin_unlock_irqrestore(&chan->desc_lock, flags); - - dma_pool_destroy(chan->desc_pool); - chan->desc_pool = NULL; - chan->idle = true; - chan->dev_addr = 0; - - chan->status = DMA_COMPLETE; - chan->dir = 0; - chan->dcmd = 0; - - mmp_pdma_free_phy(chan); -} - -/* - * Per-channel qos get/put function. This function ensures that pm_ - * runtime_get/put are not called multi times for one channel. - * This guarantees pm_runtime_get/put always match for the entire device. - */ -static void mmp_pdma_qos_get(struct mmp_pdma_chan *chan) -{ - unsigned long flags; - - if (chan->user_do_qos) - return; - - spin_lock_irqsave(&chan->desc_lock, flags); - if (chan->qos_count == 0) { - chan->qos_count = 1; - /* - * Safe in spin_lock because it's marked as irq safe. - * Similar case for mmp_pdma_qos_put(). - */ - pm_runtime_get_sync(chan->dev); - } - - spin_unlock_irqrestore(&chan->desc_lock, flags); -} - -static void mmp_pdma_qos_put(struct mmp_pdma_chan *chan) -{ - unsigned long flags; - - if (chan->user_do_qos) - return; - - spin_lock_irqsave(&chan->desc_lock, flags); - if (chan->qos_count == 1) { - chan->qos_count = 0; - pm_runtime_put_autosuspend(chan->dev); - } - - spin_unlock_irqrestore(&chan->desc_lock, flags); -} - -#define INVALID_BURST_SETTING -1 -#define DEFAULT_MAX_BURST_SIZE 32 - -static int get_max_burst_setting(unsigned int max_burst_size) -{ - switch (max_burst_size) { - case 8: - return DCMD_BURST8; - case 16: - return DCMD_BURST16; - case 32: - return DCMD_BURST32; - case 64: - return DCMD_BURST64; - default: - return INVALID_BURST_SETTING; - } -} - -static struct dma_async_tx_descriptor * -mmp_pdma_prep_memcpy(struct dma_chan *dchan, - dma_addr_t dma_dst, dma_addr_t dma_src, - size_t len, unsigned long flags) -{ - struct mmp_pdma_chan *chan; - struct mmp_pdma_desc_sw *first = NULL, *prev = NULL, *new; - size_t copy = 0; - struct mmp_pdma_device *dev; - int value; - - if (!dchan) - return NULL; - - if (!len) - return NULL; - - chan = to_mmp_pdma_chan(dchan); - chan->byte_align = false; - - if (!chan->dir) { - chan->dir = DMA_MEM_TO_MEM; - chan->dcmd = DCMD_INCTRGADDR | DCMD_INCSRCADDR; - dev = to_mmp_pdma_dev(dchan->device); - value = get_max_burst_setting(dev->max_burst_size); - - WARN_ON(value == INVALID_BURST_SETTING); - - chan->dcmd |= value; - } - - do { - /* Allocate the link descriptor from DMA pool */ - new = mmp_pdma_alloc_descriptor(chan); - if (!new) { - dev_err(chan->dev, "no memory for desc\n"); - goto fail; - } - - copy = min_t(size_t, len, PDMA_MAX_DESC_BYTES); - if (dma_src & 0x7 || dma_dst & 0x7) - chan->byte_align = true; - - new->desc.dcmd = chan->dcmd | (DCMD_LENGTH & copy); - - /* - * Check whether descriptor/source-addr/target-addr is in - * region higher than 4G. If so, set related higher bits to 1. - */ - if (chan->dir == DMA_MEM_TO_DEV) { - new->desc.dsadr = dma_src & 0xffffffff; - new->desc.dtadr = dma_dst; - new->desc.dsadrh = (dma_src >> 32); - new->desc.dtadrh = 0; - } else if (chan->dir == DMA_DEV_TO_MEM) { - new->desc.dsadr = dma_src; - new->desc.dtadr = dma_dst & 0xffffffff; - new->desc.dsadrh = 0; - new->desc.dtadrh = (dma_dst >> 32); - } else if (chan->dir == DMA_MEM_TO_MEM) { - new->desc.dsadr = dma_src & 0xffffffff; - new->desc.dtadr = dma_dst & 0xffffffff; - new->desc.dsadrh = (dma_src >> 32); - new->desc.dtadrh = (dma_dst >> 32); - } else { - dev_err(chan->dev, "wrong direction: 0x%x\n", chan->dir); - goto fail; - } - - if (!first) - first = new; - else { - prev->desc.ddadr = new->async_tx.phys; - prev->desc.ddadrh = (new->async_tx.phys >> 32); - } - - new->async_tx.cookie = 0; - async_tx_ack(&new->async_tx); - - prev = new; - len -= copy; - - if (chan->dir == DMA_MEM_TO_DEV) { - dma_src += copy; - } else if (chan->dir == DMA_DEV_TO_MEM) { - dma_dst += copy; - } else if (chan->dir == DMA_MEM_TO_MEM) { - dma_src += copy; - dma_dst += copy; - } - - /* Insert the link descriptor to the LD ring */ - list_add_tail(&new->node, &first->tx_list); - } while (len); - - first->async_tx.flags = flags; /* client is in control of this ack */ - first->async_tx.cookie = -EBUSY; - - /* last desc and fire IRQ */ - new->desc.ddadr = DDADR_STOP; - new->desc.dcmd |= DCMD_ENDIRQEN; - - chan->cyclic_first = NULL; - - return &first->async_tx; - -fail: - if (first) - mmp_pdma_free_desc_list(chan, &first->tx_list); - return NULL; -} - -static struct dma_async_tx_descriptor * -mmp_pdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_transfer_direction dir, - unsigned long flags, void *context) -{ - struct mmp_pdma_chan *chan = to_mmp_pdma_chan(dchan); - struct mmp_pdma_desc_sw *first = NULL, *prev = NULL, *new = NULL; - size_t len, avail; - struct scatterlist *sg; - dma_addr_t addr; - int i; - - if ((sgl == NULL) || (sg_len == 0)) - return NULL; - - chan->byte_align = true; - - mmp_pdma_config_write(dchan, &chan->slave_config, dir); - - for_each_sg(sgl, sg, sg_len, i) { - addr = sg_dma_address(sg); - avail = sg_dma_len(sgl); - - do { - len = min_t(size_t, avail, PDMA_MAX_DESC_BYTES); - if (addr & 0x7) - chan->byte_align = true; - - /* allocate and populate the descriptor */ - new = mmp_pdma_alloc_descriptor(chan); - if (!new) { - dev_err(chan->dev, "no memory for desc\n"); - goto fail; - } - - new->desc.dcmd = chan->dcmd | (DCMD_LENGTH & len); - - /* - * Check whether descriptor/source-addr/target-addr is in - * region higher than 4G. If so, set related higher bits to 1. - */ - if (dir == DMA_MEM_TO_DEV) { - new->desc.dsadr = addr & 0xffffffff; - new->desc.dtadr = chan->dev_addr; - new->desc.dsadrh = (addr >> 32); - new->desc.dtadrh = 0; - } else if (dir == DMA_DEV_TO_MEM) { - new->desc.dsadr = chan->dev_addr; - new->desc.dtadr = addr & 0xffffffff; - new->desc.dsadrh = 0; - new->desc.dtadrh = (addr >> 32); - } else { - dev_err(chan->dev, "wrong direction: 0x%x\n", chan->dir); - goto fail; - } - - if (!first) - first = new; - else { - prev->desc.ddadr = new->async_tx.phys; - prev->desc.ddadrh = (new->async_tx.phys >> 32); - } - - new->async_tx.cookie = 0; - async_tx_ack(&new->async_tx); - prev = new; - - /* Insert the link descriptor to the LD ring */ - list_add_tail(&new->node, &first->tx_list); - - /* update metadata */ - addr += len; - avail -= len; - } while (avail); - } - - first->async_tx.cookie = -EBUSY; - first->async_tx.flags = flags; - - /* last desc and fire IRQ */ - new->desc.ddadr = DDADR_STOP; - new->desc.dcmd |= DCMD_ENDIRQEN; - - chan->dir = dir; - chan->cyclic_first = NULL; - - return &first->async_tx; - -fail: - if (first) - mmp_pdma_free_desc_list(chan, &first->tx_list); - return NULL; -} - -static struct dma_async_tx_descriptor * -mmp_pdma_prep_dma_cyclic(struct dma_chan *dchan, - dma_addr_t buf_addr, size_t len, size_t period_len, - enum dma_transfer_direction direction, - unsigned long flags) -{ - struct mmp_pdma_chan *chan; - struct mmp_pdma_desc_sw *first = NULL, *prev = NULL, *new; - dma_addr_t dma_src, dma_dst; - dma_addr_t dma_srch, dma_dsth; - - if (!dchan || !len || !period_len) - return NULL; - - /* the buffer length must be a multiple of period_len */ - if (len % period_len != 0) - return NULL; - - if (period_len > PDMA_MAX_DESC_BYTES) - return NULL; - - chan = to_mmp_pdma_chan(dchan); - mmp_pdma_config_write(dchan, &chan->slave_config, direction); - - switch (direction) { - case DMA_MEM_TO_DEV: - dma_src = buf_addr & 0xffffffff; - dma_dst = chan->dev_addr; - dma_srch = (buf_addr >> 32); - dma_dsth = 0; - break; - case DMA_DEV_TO_MEM: - dma_dst = buf_addr & 0xffffffff; - dma_src = chan->dev_addr; - dma_dsth = (buf_addr >> 32); - dma_srch = 0; - break; - default: - dev_err(chan->dev, "Unsupported direction for cyclic DMA\n"); - return NULL; - } - - chan->dir = direction; - - do { - /* Allocate the link descriptor from DMA pool */ - new = mmp_pdma_alloc_descriptor(chan); - if (!new) { - dev_err(chan->dev, "no memory for desc\n"); - goto fail; - } - - new->desc.dcmd = (chan->dcmd | DCMD_ENDIRQEN | - (DCMD_LENGTH & period_len)); - new->desc.dsadr = dma_src; - new->desc.dtadr = dma_dst; - new->desc.dsadrh = dma_dsth; - new->desc.dtadrh = dma_srch; - - if (!first) - first = new; - else { - prev->desc.ddadr = new->async_tx.phys; - prev->desc.ddadrh = (new->async_tx.phys >> 32); - } - - new->async_tx.cookie = 0; - async_tx_ack(&new->async_tx); - - prev = new; - len -= period_len; - - if (chan->dir == DMA_MEM_TO_DEV) - dma_src += period_len; - else - dma_dst += period_len; - - /* Insert the link descriptor to the LD ring */ - list_add_tail(&new->node, &first->tx_list); - } while (len); - - first->async_tx.flags = flags; /* client is in control of this ack */ - first->async_tx.cookie = -EBUSY; - - /* make the cyclic link */ - new->desc.ddadr = first->async_tx.phys; - chan->cyclic_first = first; - - return &first->async_tx; - -fail: - if (first) - mmp_pdma_free_desc_list(chan, &first->tx_list); - return NULL; -} - -static int mmp_pdma_pause_chan(struct dma_chan *dchan) -{ - struct mmp_pdma_chan *chan = to_mmp_pdma_chan(dchan); - - if (!chan->phy) - return -1; - - disable_chan(chan->phy); - chan->status = DMA_PAUSED; - - return 0; -} - -static int mmp_pdma_config(struct dma_chan *dchan, - struct dma_slave_config *cfg) -{ - struct mmp_pdma_chan *chan = to_mmp_pdma_chan(dchan); - - memcpy(&chan->slave_config, cfg, sizeof(*cfg)); - return 0; -} - -static int mmp_pdma_terminate_all(struct dma_chan *dchan) -{ - struct mmp_pdma_chan *chan = to_mmp_pdma_chan(dchan); - unsigned long flags; - - if (!dchan) - return -EINVAL; - - spin_lock_irqsave(&chan->desc_lock, flags); - disable_chan(chan->phy); - chan->status = DMA_COMPLETE; - mmp_pdma_free_phy(chan); - - mmp_pdma_free_desc_list(chan, &chan->chain_pending); - mmp_pdma_free_desc_list(chan, &chan->chain_running); - chan->bytes_residue = 0; - - spin_unlock_irqrestore(&chan->desc_lock, flags); - chan->idle = true; - - mmp_pdma_qos_put(chan); - - return 0; -} - -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; - bool passed = false; - bool cyclic = chan->cyclic_first != NULL; - - /* - * If the channel does not have a phy pointer anymore, it has already - * been completed. Therefore, its residue is 0. - */ - if (!chan->phy) - return chan->bytes_residue; /* special case for EORIRQEN */ - - if (chan->dir == DMA_DEV_TO_MEM) - curr = readl(chan->phy->base + DTADR(chan->phy->idx)); - else - curr = readl(chan->phy->base + DSADR(chan->phy->idx)); - - list_for_each_entry(sw, &chan->chain_running, node) { - u32 start, end, len; - - if (chan->dir == DMA_DEV_TO_MEM) - start = sw->desc.dtadr; - else - start = sw->desc.dsadr; - - len = sw->desc.dcmd & DCMD_LENGTH; - end = start + len; - - /* - * 'passed' will be latched once we found the descriptor which - * lies inside the boundaries of the curr pointer. All - * descriptors that occur in the list _after_ we found that - * partially handled descriptor are still to be processed and - * are hence added to the residual bytes counter. - */ - if (passed) { - residue += len; - } else if (curr >= start && curr <= end) { - residue += end - curr; - passed = true; - } - - /* - * Descriptors that have the ENDIRQEN bit set mark the end of a - * transaction chain, and the cookie assigned with it has been - * returned previously from mmp_pdma_tx_submit(). - * - * In case we have multiple transactions in the running chain, - * and the cookie does not match the one the user asked us - * about, reset the state variables and start over. - * - * This logic does not apply to cyclic transactions, where all - * descriptors have the ENDIRQEN bit set, and for which we - * can't have multiple transactions on one channel anyway. - */ - if (cyclic || !(sw->desc.dcmd & DCMD_ENDIRQEN)) - continue; - - if (sw->async_tx.cookie == cookie) - return residue; - - residue = 0; - passed = false; - } - - /* We should only get here in case of cyclic transactions */ - return residue; -} - -static enum dma_status mmp_pdma_tx_status(struct dma_chan *dchan, - dma_cookie_t cookie, - struct dma_tx_state *txstate) -{ - struct mmp_pdma_chan *chan = to_mmp_pdma_chan(dchan); - enum dma_status ret; - unsigned long flags; - - spin_lock_irqsave(&chan->desc_lock, flags); - ret = dma_cookie_status(dchan, cookie, txstate); - if (likely(ret != DMA_ERROR)) - dma_set_residue(txstate, mmp_pdma_residue(chan, cookie)); - - spin_unlock_irqrestore(&chan->desc_lock, flags); - - if (ret == DMA_COMPLETE) - return ret; - else - return chan->status; -} - -/* - * mmp_pdma_issue_pending - Issue the DMA start command - * pending list ==> running list - */ -static void mmp_pdma_issue_pending(struct dma_chan *dchan) -{ - struct mmp_pdma_chan *chan = to_mmp_pdma_chan(dchan); - unsigned long flags; - int ret = 0; - - mmp_pdma_qos_get(chan); - spin_lock_irqsave(&chan->desc_lock, flags); - ret = start_pending_queue(chan); - - spin_unlock_irqrestore(&chan->desc_lock, flags); - - if (ret) - mmp_pdma_qos_put(chan); -} - -/* - * dma_do_tasklet - * Do call back - * Start pending list - */ -static void dma_do_tasklet(struct tasklet_struct *t) -{ - struct mmp_pdma_chan *chan = from_tasklet(chan, t, tasklet); - struct mmp_pdma_desc_sw *desc, *_desc; - LIST_HEAD(chain_cleanup); - unsigned long flags; - struct dmaengine_desc_callback cb; - - int ret = 0; - - /* return if this channel has been stopped */ - spin_lock_irqsave(&chan->desc_lock, flags); - if (chan->status == DMA_COMPLETE) { - spin_unlock_irqrestore(&chan->desc_lock, flags); - return; - } - spin_unlock_irqrestore(&chan->desc_lock, flags); - - if (chan->cyclic_first) { - spin_lock_irqsave(&chan->desc_lock, flags); - desc = chan->cyclic_first; - dmaengine_desc_get_callback(&desc->async_tx, &cb); - spin_unlock_irqrestore(&chan->desc_lock, flags); - - dmaengine_desc_callback_invoke(&cb, NULL); - - return; - } - - /* submit pending list; callback for each desc; free desc */ - spin_lock_irqsave(&chan->desc_lock, flags); - - /* special for the EORIRQEN case, residue is not 0 */ - list_for_each_entry(desc, &chan->chain_running, node) { - if (desc->desc.dcmd & DCMD_ENDIRQEN) { - chan->bytes_residue = - mmp_pdma_residue(chan, desc->async_tx.cookie); - break; - } - } - - list_for_each_entry_safe(desc, _desc, &chan->chain_running, node) { - /* - * move the descriptors to a temporary list so we can drop - * the lock during the entire cleanup operation - */ - list_move(&desc->node, &chain_cleanup); - - /* - * Look for the first list entry which has the ENDIRQEN flag - * set. That is the descriptor we got an interrupt for, so - * complete that transaction and its cookie. - */ - if (desc->desc.dcmd & DCMD_ENDIRQEN) { - dma_cookie_t cookie = desc->async_tx.cookie; - - dma_cookie_complete(&desc->async_tx); - dev_dbg(chan->dev, "completed_cookie=%d\n", cookie); - break; - } - } - - /* - * The hardware is idle and ready for more when the - * chain_running list is empty. - */ - chan->status = list_empty(&chan->chain_running) ? - DMA_COMPLETE : DMA_IN_PROGRESS; - - /* Start any pending transactions automatically */ - ret = start_pending_queue(chan); - - spin_unlock_irqrestore(&chan->desc_lock, flags); - - /* restart pending transactions failed, do not need qos anymore */ - if (ret) - mmp_pdma_qos_put(chan); - - /* Run the callback for each descriptor, in order */ - list_for_each_entry_safe(desc, _desc, &chain_cleanup, node) { - struct dma_async_tx_descriptor *txd = &desc->async_tx; - - /* Remove from the list of transactions */ - list_del(&desc->node); - /* Run the link descriptor callback function */ - dmaengine_desc_get_callback(txd, &cb); - dmaengine_desc_callback_invoke(&cb, NULL); - - dma_pool_free(chan->desc_pool, desc, txd->phys); - } -} - -static int mmp_pdma_chan_init(struct mmp_pdma_device *pdev, int idx) -{ - struct mmp_pdma_phy *phy = &pdev->phy[idx]; - struct mmp_pdma_chan *chan; - - chan = devm_kzalloc(pdev->dev, sizeof(*chan), GFP_KERNEL); - if (chan == NULL) - return -ENOMEM; - - phy->idx = idx; - phy->base = pdev->base; - - spin_lock_init(&chan->desc_lock); - chan->dev = pdev->dev; - chan->chan.device = &pdev->device; - tasklet_setup(&chan->tasklet, dma_do_tasklet); - INIT_LIST_HEAD(&chan->chain_pending); - INIT_LIST_HEAD(&chan->chain_running); - - chan->status = DMA_COMPLETE; - chan->bytes_residue = 0; - chan->qos_count = 0; - chan->user_do_qos = 1; - - /* register virt channel to dma engine */ - list_add_tail(&chan->chan.device_node, &pdev->device.channels); - - return 0; -} - -static const struct of_device_id mmp_pdma_dt_ids[] = { - { .compatible = "spacemit,k1-pdma", }, - {} -}; -MODULE_DEVICE_TABLE(of, mmp_pdma_dt_ids); - -static struct dma_chan *mmp_pdma_dma_xlate(struct of_phandle_args *dma_spec, - struct of_dma *ofdma) -{ - struct mmp_pdma_device *d = ofdma->of_dma_data; - struct dma_chan *chan; - - chan = dma_get_any_slave_channel(&d->device); - if (!chan) - return NULL; - - to_mmp_pdma_chan(chan)->drcmr = dma_spec->args[0]; - - return chan; -} - -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 resource *iores; - int i, ret, value; - int irq = 0, dma_channels = 0; - const enum dma_slave_buswidth widths = - DMA_SLAVE_BUSWIDTH_1_BYTE | DMA_SLAVE_BUSWIDTH_2_BYTES | - DMA_SLAVE_BUSWIDTH_4_BYTES; - - int nr_reserved_channels; - const int *list; - unsigned int max_burst_size = DEFAULT_MAX_BURST_SIZE; - - pdev = devm_kzalloc(&op->dev, sizeof(*pdev), GFP_KERNEL); - if (!pdev) - return -ENOMEM; - - pdev->dev = &op->dev; - - spin_lock_init(&pdev->phy_lock); - - iores = platform_get_resource(op, IORESOURCE_MEM, 0); - pdev->base = devm_ioremap_resource(pdev->dev, iores); - if (IS_ERR(pdev->base)) - return PTR_ERR(pdev->base); - - pdev->clk = devm_clk_get(pdev->dev, NULL); - if (IS_ERR(pdev->clk)) - return PTR_ERR(pdev->clk); - - ret = clk_prepare_enable(pdev->clk); - if (ret) - return dev_err_probe(pdev->dev, ret, "could not enable dma bus clock\n"); - - pdev->resets = devm_reset_control_get_optional(pdev->dev, NULL); - if (IS_ERR(pdev->resets)) { - ret = PTR_ERR(pdev->resets); - goto err_rst; - } - ret = reset_control_deassert(pdev->resets); - if (ret) - goto err_rst; - - of_id = of_match_device(mmp_pdma_dt_ids, pdev->dev); - - if (of_id) { - int n; - - of_property_read_u32(pdev->dev->of_node, "#dma-channels", &dma_channels); - list = of_get_property(pdev->dev->of_node, "reserved-channels", &n); - if (of_property_read_u32(pdev->dev->of_node, "max-burst-size", &max_burst_size)) { - dev_err(pdev->dev, - "No max-burst-size node in the device tree, set it to %d\n", - DEFAULT_MAX_BURST_SIZE); - max_burst_size = DEFAULT_MAX_BURST_SIZE; - } - - if (get_max_burst_setting(max_burst_size) == INVALID_BURST_SETTING) { - dev_err(pdev->dev, "Unsupported max-burst-size value %d set it to %d\n", - max_burst_size, DEFAULT_MAX_BURST_SIZE); - max_burst_size = DEFAULT_MAX_BURST_SIZE; - } - - if (list) { - nr_reserved_channels = n / (sizeof(u32) * PDMA_RESRV_CHAN_ARGS_NUM); - pdev->nr_reserved_channels = nr_reserved_channels; - pdev->reserved_channels = devm_kcalloc(pdev->dev, - nr_reserved_channels, - sizeof(struct reserved_chan), - GFP_KERNEL); - if (pdev->reserved_channels == NULL) { - ret = -ENOMEM; - goto err_out; - } - - for (i = 0; i < nr_reserved_channels; i++) { - of_property_read_u32_index(pdev->dev->of_node, - "reserved-channels", - i * PDMA_RESRV_CHAN_ARGS_NUM, - &value); - pdev->reserved_channels[i].chan_id = value; - of_property_read_u32_index(pdev->dev->of_node, - "reserved-channels", - i * PDMA_RESRV_CHAN_ARGS_NUM + 1, - &value); - pdev->reserved_channels[i].drcmr = value; - } - } - } else if (pdata && pdata->dma_channels) - dma_channels = pdata->dma_channels; - else - dma_channels = 32; - pdev->dma_channels = dma_channels; - - pdev->max_burst_size = max_burst_size; - dev_dbg(pdev->dev, "set max burst size to %d\n", max_burst_size); - - pdev->phy = devm_kcalloc(pdev->dev, dma_channels, sizeof(*pdev->phy), - GFP_KERNEL); - if (pdev->phy == NULL) { - ret = -ENOMEM; - goto err_out; - } - - INIT_LIST_HEAD(&pdev->device.channels); - - /* all chan share one irq, demux inside */ - irq = platform_get_irq(op, 0); - ret = devm_request_irq(pdev->dev, irq, mmp_pdma_int_handler, - IRQF_SHARED, "pdma", pdev); - if (ret) - goto err_out; - - for (i = 0; i < dma_channels; i++) { - ret = mmp_pdma_chan_init(pdev, i); - if (ret) - goto err_out; - } - - dma_cap_set(DMA_SLAVE, pdev->device.cap_mask); - dma_cap_set(DMA_MEMCPY, pdev->device.cap_mask); - dma_cap_set(DMA_CYCLIC, pdev->device.cap_mask); - dma_cap_set(DMA_PRIVATE, pdev->device.cap_mask); - pdev->device.dev = &op->dev; - pdev->device.device_alloc_chan_resources = mmp_pdma_alloc_chan_resources; - pdev->device.device_free_chan_resources = mmp_pdma_free_chan_resources; - pdev->device.device_tx_status = mmp_pdma_tx_status; - pdev->device.device_prep_dma_memcpy = mmp_pdma_prep_memcpy; - pdev->device.device_prep_slave_sg = mmp_pdma_prep_slave_sg; - pdev->device.device_prep_dma_cyclic = mmp_pdma_prep_dma_cyclic; - pdev->device.device_issue_pending = mmp_pdma_issue_pending; - pdev->device.device_config = mmp_pdma_config; - pdev->device.device_pause = mmp_pdma_pause_chan; - pdev->device.device_terminate_all = mmp_pdma_terminate_all; - pdev->device.copy_align = DMAENGINE_ALIGN_8_BYTES; - pdev->device.src_addr_widths = widths; - pdev->device.dst_addr_widths = widths; - pdev->device.directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM); - pdev->device.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; - - dma_set_mask(pdev->dev, DMA_BIT_MASK(64)); - - ret = dma_async_device_register(&pdev->device); - if (ret) { - dev_err(pdev->device.dev, "unable to register\n"); - goto err_out; - } - - if (op->dev.of_node) { - /* Device-tree DMA controller registration */ - ret = of_dma_controller_register(op->dev.of_node, - mmp_pdma_dma_xlate, pdev); - if (ret < 0) { - dev_err(&op->dev, "of_dma_controller_register failed\n"); - dma_async_device_unregister(&pdev->device); - goto err_out; - } - } - - platform_set_drvdata(op, pdev); - dev_dbg(pdev->device.dev, "initialized %d channels\n", dma_channels); - return 0; - -err_out: - reset_control_assert(pdev->resets); -err_rst: - clk_disable_unprepare(pdev->clk); - return ret; -} - -static int mmp_pdma_remove(struct platform_device *op) -{ - struct mmp_pdma_device *pdev = platform_get_drvdata(op); - int irq = 0; - - if (op->dev.of_node) - of_dma_controller_free(op->dev.of_node); - - irq = platform_get_irq(op, 0); - devm_free_irq(&op->dev, irq, pdev); - - dma_async_device_unregister(&pdev->device); - - reset_control_assert(pdev->resets); - clk_disable_unprepare(pdev->clk); - - kfree(pdev->reserved_channels); - platform_set_drvdata(op, NULL); - - return 0; -} - -static struct platform_driver mmp_pdma_driver = { - .driver = { - .name = "spacemit-k1-pdma", - .of_match_table = mmp_pdma_dt_ids, - }, - .probe = mmp_pdma_probe, - .remove = mmp_pdma_remove, -}; - -static int __init spacemit_k1_pdma_init(void) -{ - return platform_driver_register(&mmp_pdma_driver); -} - -static void __exit spacemit_k1_pdma_exit(void) -{ - platform_driver_unregister(&mmp_pdma_driver); -} - -subsys_initcall(spacemit_k1_pdma_init); -module_exit(spacemit_k1_pdma_exit); - -MODULE_DESCRIPTION("Spacemit K1 Peripheral DMA Controller Driver"); -MODULE_LICENSE("GPL"); From c2eeddf1a136891e5c30102ca986990e59ac3891 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Fri, 22 Aug 2025 11:06:27 +0800 Subject: [PATCH 03/15] dt-bindings: dma: Add SpacemiT K1 PDMA controller mainline inclusion from mainline-7.2-rc1 commit 39ce725e621b category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- Add device tree binding documentation for the SpacemiT K1 PDMA controller. Signed-off-by: Guodong Xu Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250822-working_dma_0701_v2-v5-1-f5c0eda734cc@riscstar.com Signed-off-by: Vinod Koul Signed-off-by: Yongchao Jia --- .../bindings/dma/spacemit,k1-pdma.yaml | 68 +++++++++++++++++++ 1 file changed, 68 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..ec06235baf5ca --- /dev/null +++ b/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml @@ -0,0 +1,68 @@ +# 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: + const: spacemit,k1-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 5ac0c5aacf7e9ba8b49224d381eaeda9364d1e8d Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Mon, 18 May 2026 11:32:41 +0800 Subject: [PATCH 04/15] dt-bindings: dmaengine: Add SpacemiT K3 DMA compatible string mainline inclusion from mainline-7.2-rc1 commit 55620b11186c 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 --- Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml b/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml index ec06235baf5ca..62ce6d81526b4 100644 --- a/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml +++ b/Documentation/devicetree/bindings/dma/spacemit,k1-pdma.yaml @@ -14,7 +14,9 @@ allOf: properties: compatible: - const: spacemit,k1-pdma + enum: + - spacemit,k1-pdma + - spacemit,k3-pdma reg: maxItems: 1 From 242cb4f35ad621e6db949df9762626c1b518f98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 19 Sep 2023 15:31:33 +0200 Subject: [PATCH 05/15] dmaengine: mmp_pdma: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mainline inclusion from mainline-7.2-rc1 commit c0f0d93fc1da category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230919133207.1400430-26-u.kleine-koenig@pengutronix.de Signed-off-by: Vinod Koul Signed-off-by: Yongchao Jia --- drivers/dma/mmp_pdma.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 046358e328eba..3d3e826093996 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -938,7 +938,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; @@ -964,7 +964,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) @@ -1147,7 +1146,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); From f72bb163d524af985a3f1615ba378b940e2b86d0 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 6 Oct 2023 16:38:35 -0500 Subject: [PATCH 06/15] dmaengine: Drop unnecessary of_match_device() calls mainline inclusion from mainline-7.2-rc1 commit c48de45d4cef category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- If probe is reached, we've already matched the device and in the case of DT matching, the struct device_node pointer will be set. Therefore, there is no need to call of_match_device() in probe. Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20231006213835.332848-1-robh@kernel.org Signed-off-by: Vinod Koul Signed-off-by: Yongchao Jia --- drivers/dma/k3dma.c | 25 ++++++++++--------------- drivers/dma/mmp_pdma.c | 5 +---- drivers/dma/pxa_dma.c | 7 ++----- drivers/dma/stm32-dma.c | 8 -------- 4 files changed, 13 insertions(+), 32 deletions(-) diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c index ecdaada951205..1d8fbb9b4adb9 100644 --- a/drivers/dma/k3dma.c +++ b/drivers/dma/k3dma.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -839,7 +838,6 @@ static int k3_dma_probe(struct platform_device *op) { const struct k3dma_soc_data *soc_data; struct k3_dma_dev *d; - const struct of_device_id *of_id; int i, ret, irq = 0; d = devm_kzalloc(&op->dev, sizeof(*d), GFP_KERNEL); @@ -854,19 +852,16 @@ static int k3_dma_probe(struct platform_device *op) if (IS_ERR(d->base)) return PTR_ERR(d->base); - of_id = of_match_device(k3_pdma_dt_ids, &op->dev); - if (of_id) { - of_property_read_u32((&op->dev)->of_node, - "dma-channels", &d->dma_channels); - of_property_read_u32((&op->dev)->of_node, - "dma-requests", &d->dma_requests); - ret = of_property_read_u32((&op->dev)->of_node, - "dma-channel-mask", &d->dma_channel_mask); - if (ret) { - dev_warn(&op->dev, - "dma-channel-mask doesn't exist, considering all as available.\n"); - d->dma_channel_mask = (u32)~0UL; - } + of_property_read_u32((&op->dev)->of_node, + "dma-channels", &d->dma_channels); + of_property_read_u32((&op->dev)->of_node, + "dma-requests", &d->dma_requests); + ret = of_property_read_u32((&op->dev)->of_node, + "dma-channel-mask", &d->dma_channel_mask); + if (ret) { + dev_warn(&op->dev, + "dma-channel-mask doesn't exist, considering all as available.\n"); + d->dma_channel_mask = (u32)~0UL; } if (!(soc_data->flags & K3_FLAG_NOCLK)) { diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 3d3e826093996..852e6714d9f28 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -1025,7 +1024,6 @@ 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); int i, ret, irq = 0; int dma_channels = 0, irq_num = 0; @@ -1045,8 +1043,7 @@ 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) { + 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)) diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c index 16d342654da2b..40506a544d475 100644 --- a/drivers/dma/pxa_dma.c +++ b/drivers/dma/pxa_dma.c @@ -15,9 +15,8 @@ #include #include #include -#include -#include #include +#include #include #include @@ -1342,7 +1341,6 @@ static int pxad_init_dmadev(struct platform_device *op, static int pxad_probe(struct platform_device *op) { struct pxad_device *pdev; - const struct of_device_id *of_id; const struct dma_slave_map *slave_map = NULL; struct mmp_dma_platdata *pdata = dev_get_platdata(&op->dev); int ret, dma_channels = 0, nb_requestors = 0, slave_map_cnt = 0; @@ -1360,8 +1358,7 @@ static int pxad_probe(struct platform_device *op) if (IS_ERR(pdev->base)) return PTR_ERR(pdev->base); - of_id = of_match_device(pxad_dt_ids, &op->dev); - if (of_id) { + if (op->dev.of_node) { /* Parse new and deprecated dma-channels properties */ if (of_property_read_u32(op->dev.of_node, "dma-channels", &dma_channels)) diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index 3882080cffa69..340422ad6d918 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -1571,17 +1570,10 @@ static int stm32_dma_probe(struct platform_device *pdev) struct stm32_dma_chan *chan; struct stm32_dma_device *dmadev; struct dma_device *dd; - const struct of_device_id *match; struct resource *res; struct reset_control *rst; int i, ret; - match = of_match_device(stm32_dma_of_match, &pdev->dev); - if (!match) { - dev_err(&pdev->dev, "Error: No device match found\n"); - return -ENODEV; - } - dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL); if (!dmadev) return -ENOMEM; From e85b900a1fc75e139d6581e4b29c55be39b492fa Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Fri, 22 Aug 2025 11:06:28 +0800 Subject: [PATCH 07/15] dmaengine: mmp_pdma: Add clock support mainline inclusion from mainline-7.2-rc1 commit e73a9a13c99c category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- Add support for retrieving and enabling an optional clock during mmp_pdma_probe(). It is optional because in Marvell devices such as "marvell,pdma-1.0" the clocks property is not a required property. But in SpacemiT K1 PDMA, "spacemit,k1-pdma" as the dt binding schema file stated, clocks is required. Signed-off-by: Guodong Xu Link: https://lore.kernel.org/r/20250822-working_dma_0701_v2-v5-2-f5c0eda734cc@riscstar.com Signed-off-by: Vinod Koul Signed-off-by: Yongchao Jia --- drivers/dma/mmp_pdma.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 852e6714d9f28..bf48789a59b27 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -1025,6 +1026,7 @@ static int mmp_pdma_probe(struct platform_device *op) { struct mmp_pdma_device *pdev; struct mmp_dma_platdata *pdata = dev_get_platdata(&op->dev); + struct clk *clk; int i, ret, irq = 0; int dma_channels = 0, irq_num = 0; const enum dma_slave_buswidth widths = @@ -1043,6 +1045,10 @@ static int mmp_pdma_probe(struct platform_device *op) if (IS_ERR(pdev->base)) return PTR_ERR(pdev->base); + clk = devm_clk_get_optional_enabled(pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + if (pdev->dev->of_node) { /* Parse new and deprecated dma-channels properties */ if (of_property_read_u32(pdev->dev->of_node, "dma-channels", From 6b04aebbb88a76b2657a0fcea188bb7c2bfe9b07 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Fri, 22 Aug 2025 11:06:29 +0800 Subject: [PATCH 08/15] dmaengine: mmp_pdma: Add reset controller support mainline inclusion from mainline-7.2-rc1 commit fc72462bc610 category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 Replace devm_reset_control_get_optional_exclusive_deasserted() with explicit devm_reset_control_get_optional_exclusive() and reset_control_deassert() calls for compatibility with Linux 6.6 kernels. -------------------------------- Add support to acquire and deassert an optional hardware reset controller during mmp_pdma_probe(). It is optional because in Marvell devices such as "marvell,pdma-1.0" the resets property is not a required property. But in SpacemiT K1 PDMA, "spacemit,k1-pdma" as the dt binding schema file stated, resets is required. Signed-off-by: Guodong Xu Link: https://lore.kernel.org/r/20250822-working_dma_0701_v2-v5-3-f5c0eda734cc@riscstar.com Signed-off-by: Vinod Koul Signed-off-by: Yongchao Jia --- drivers/dma/mmp_pdma.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index bf48789a59b27..b0b207b51dd21 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -1027,6 +1028,7 @@ static int mmp_pdma_probe(struct platform_device *op) struct mmp_pdma_device *pdev; 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 = @@ -1049,6 +1051,14 @@ static int mmp_pdma_probe(struct platform_device *op) 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); + + ret = reset_control_deassert(rst); + if (ret) + return ret; + if (pdev->dev->of_node) { /* Parse new and deprecated dma-channels properties */ if (of_property_read_u32(pdev->dev->of_node, "dma-channels", From d89ad36c7a663f47d49582b559fee73591bd40a9 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Fri, 22 Aug 2025 11:06:30 +0800 Subject: [PATCH 09/15] dmaengine: mmp_pdma: Add operations structure for controller abstraction mainline inclusion from mainline-7.2-rc1 commit 35e40bf761fc category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- Introduce mmp_pdma_ops structure to abstract 32-bit addressing operations and enable support for different controller variants. This prepares for adding 64-bit addressing support. The ops structure includes: - Hardware register operations (read/write DDADR, DSADR, DTADR) - Descriptor memory operations (manipulate descriptor structs) - Controller configuration (run bits, DMA mask) Convert existing 32-bit operations to use the new abstraction layer while maintaining backward compatibility. Cc: Dan Carpenter Signed-off-by: Guodong Xu Link: https://lore.kernel.org/r/20250822-working_dma_0701_v2-v5-4-f5c0eda734cc@riscstar.com Signed-off-by: Vinod Koul Signed-off-by: Yongchao Jia --- drivers/dma/mmp_pdma.c | 195 +++++++++++++++++++++++++++++++++-------- 1 file changed, 160 insertions(+), 35 deletions(-) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index b0b207b51dd21..297d971d9edeb 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -25,7 +25,7 @@ #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 DCMD 0x020c @@ -120,12 +120,55 @@ 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_mask: DMA addressing capability of controller. 0 to use OF/platform + * settings, or explicit mask like DMA_BIT_MASK(32/64) + */ +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; + u64 dma_mask; +}; + struct mmp_pdma_device { int dma_channels; void __iomem *base; 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 */ }; @@ -138,24 +181,61 @@ 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); +/* 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(struct mmp_pdma_phy *phy, dma_addr_t addr) +static void set_desc_dst_addr_32(struct mmp_pdma_desc_hw *desc, dma_addr_t addr) { - u32 reg = (phy->idx << 4) + DDADR; + desc->dtadr = addr; +} - writel(addr, phy->base + reg); +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; +} + +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; + pdev = to_mmp_pdma_dev(phy->vchan->chan.device); + reg = DRCMR(phy->vchan->drcmr); writel(DRCMR_MAPVLD | phy->idx, phy->base + reg); @@ -167,18 +247,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) @@ -297,6 +388,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) { @@ -331,7 +423,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; } @@ -447,15 +539,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; @@ -478,13 +569,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); @@ -528,6 +620,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; @@ -559,17 +652,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); @@ -609,12 +703,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; @@ -651,13 +748,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); @@ -678,7 +776,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; @@ -764,8 +862,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; @@ -777,19 +877,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; @@ -805,7 +906,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; } @@ -1002,9 +1103,26 @@ 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_mask = 0, /* let OF/platform set DMA mask */ +}; + 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 + }, { + /* sentinel */ + } }; MODULE_DEVICE_TABLE(of, mmp_pdma_dt_ids); @@ -1059,6 +1177,10 @@ static int mmp_pdma_probe(struct platform_device *op) if (ret) return ret; + 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", @@ -1120,7 +1242,10 @@ 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) + /* Set DMA mask based on ops->dma_mask, or OF/platform */ + if (pdev->ops->dma_mask) + dma_set_mask(pdev->dev, pdev->ops->dma_mask); + else 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)); From 52c39b2b429571f98d45cfb3e4e804fba5de47ae Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Fri, 22 Aug 2025 11:06:31 +0800 Subject: [PATCH 10/15] dmaengine: mmp_pdma: Add SpacemiT K1 PDMA support with 64-bit addressing mainline inclusion from mainline-7.2-rc1 commit 5cfe585d8624 category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- Add support for SpacemiT K1 PDMA controller which features 64-bit addressing capabilities. The SpacemiT K1 PDMA extends the descriptor format with additional 32-bit words for high address bits, enabling access to memory beyond 4GB boundaries. The new spacemit_k1_pdma_ops provides necessary 64-bit address handling functions and k1 specific controller configurations. Key changes: - Add ARCH_SPACEMIT dependency to Kconfig - Define new high 32-bit address registers (DDADRH, DSADRH, DTADRH) - Add DCSR_LPAEEN bit for Long Physical Address Extension Enable - Implement 64-bit operations for SpacemiT K1 PDMA Signed-off-by: Guodong Xu Link: https://lore.kernel.org/r/20250822-working_dma_0701_v2-v5-5-f5c0eda734cc@riscstar.com Signed-off-by: Vinod Koul Signed-off-by: Yongchao Jia --- drivers/dma/Kconfig | 2 +- drivers/dma/mmp_pdma.c | 81 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index e36506471a4f6..9518397193375 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -411,7 +411,7 @@ config MILBEAUT_XDMAC config MMP_PDMA tristate "MMP PDMA support" - depends on ARCH_MMP || ARCH_PXA || COMPILE_TEST + depends on ARCH_MMP || ARCH_PXA || ARCH_SPACEMIT || COMPILE_TEST select DMA_ENGINE help Support the MMP PDMA engine for PXA and MMP platform. diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 297d971d9edeb..e35d849c5ec38 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -28,6 +28,9 @@ #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) */ @@ -44,6 +47,7 @@ #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 */ @@ -76,6 +80,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 { @@ -222,6 +236,57 @@ 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 u64 get_desc_src_addr_64(const struct mmp_pdma_desc_hw *desc) +{ + return ((u64)desc->dsadrh << 32) | desc->dsadr; +} + +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); @@ -1116,10 +1181,26 @@ static const struct mmp_pdma_ops marvell_pdma_v1_ops = { .dma_mask = 0, /* let OF/platform set DMA mask */ }; +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_mask = DMA_BIT_MASK(64), /* force 64-bit DMA addr capability */ +}; + static const struct of_device_id mmp_pdma_dt_ids[] = { { .compatible = "marvell,pdma-1.0", .data = &marvell_pdma_v1_ops + }, { + .compatible = "spacemit,k1-pdma", + .data = &spacemit_k1_pdma_ops }, { /* sentinel */ } From 0f40905ad5a6c636688975a9c01aa264b0f2c4c3 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Thu, 18 Sep 2025 22:27:27 +0800 Subject: [PATCH 11/15] dmaengine: mmp_pdma: fix DMA mask handling mainline inclusion from mainline-7.2-rc1 commit 49400b701eca category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- The driver's existing logic for setting the DMA mask for "marvell,pdma-1.0" was flawed. It incorrectly relied on pdev->dev->coherent_dma_mask instead of declaring the hardware's fixed addressing capability. A cleaner and more correct approach is to define the mask directly based on the hardware limitations. The MMP/PXA PDMA controller is a 32-bit DMA engine. This is supported by datasheets and various dtsi files for PXA25x, PXA27x, PXA3xx, and MMP2, all of which are 32-bit systems. This patch simplifies the driver's logic by replacing the 'u64 dma_mask' field with a simpler 'u32 dma_width' to store the addressing capability in bits. The complex if/else block in probe() is then replaced with a single, clear call to dma_set_mask_and_coherent(). This sets a fixed 32-bit DMA mask for "marvell,pdma-1.0" and a 64-bit mask for "spacemit,k1-pdma," matching each device's hardware capabilities. Finally, this change also works around a specific build error encountered with clang-20 on x86_64 allyesconfig. The shift-count-overflow error is caused by a known clang compiler issue where the DMA_BIT_MASK(n) macro's ternary operator is not correctly evaluated in static initializers. By moving the macro's evaluation into the probe() function, the driver avoids this compiler bug. Fixes: 5cfe585d8624 ("dmaengine: mmp_pdma: Add SpacemiT K1 PDMA support with 64-bit addressing") Reported-by: Naresh Kamboju Closes: https://lore.kernel.org/lkml/CA+G9fYsPcMfW-e_0_TRqu4cnwqOqYF3aJOeKUYk6Z4qRStdFvg@mail.gmail.com Suggested-by: Arnd Bergmann Signed-off-by: Guodong Xu Reviewed-by: Arnd Bergmann Tested-by: Nathan Chancellor # build Tested-by: Naresh Kamboju Signed-off-by: Vinod Koul Signed-off-by: Yongchao Jia --- drivers/dma/mmp_pdma.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index e35d849c5ec38..ef9765c65d803 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -152,8 +152,8 @@ struct mmp_pdma_phy { * * Controller Configuration: * @run_bits: Control bits in DCSR register for channel start/stop - * @dma_mask: DMA addressing capability of controller. 0 to use OF/platform - * settings, or explicit mask like DMA_BIT_MASK(32/64) + * @dma_width: DMA addressing width in bits (32 or 64). Determines the + * DMA mask capability of the controller hardware. */ struct mmp_pdma_ops { /* Hardware Register Operations */ @@ -173,7 +173,7 @@ struct mmp_pdma_ops { /* Controller Configuration */ u32 run_bits; - u64 dma_mask; + u32 dma_width; }; struct mmp_pdma_device { @@ -1178,7 +1178,7 @@ static const struct mmp_pdma_ops marvell_pdma_v1_ops = { .get_desc_src_addr = get_desc_src_addr_32, .get_desc_dst_addr = get_desc_dst_addr_32, .run_bits = (DCSR_RUN), - .dma_mask = 0, /* let OF/platform set DMA mask */ + .dma_width = 32, }; static const struct mmp_pdma_ops spacemit_k1_pdma_ops = { @@ -1191,7 +1191,7 @@ static const struct mmp_pdma_ops spacemit_k1_pdma_ops = { .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_mask = DMA_BIT_MASK(64), /* force 64-bit DMA addr capability */ + .dma_width = 64, }; static const struct of_device_id mmp_pdma_dt_ids[] = { @@ -1323,13 +1323,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; - /* Set DMA mask based on ops->dma_mask, or OF/platform */ - if (pdev->ops->dma_mask) - dma_set_mask(pdev->dev, pdev->ops->dma_mask); - else 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) { From ee05f459741142fc62bfb3955341481c7571c516 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Mon, 18 May 2026 11:32:42 +0800 Subject: [PATCH 12/15] dmaengine: mmp_pdma: refactor DRCMR access with helper function mainline inclusion from mainline-7.2-rc1 commit f46b47623e70 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 | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index ef9765c65d803..77a3729085ebc 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -51,7 +51,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) */ @@ -154,6 +156,7 @@ struct mmp_pdma_phy { * @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 */ @@ -174,6 +177,7 @@ struct mmp_pdma_ops { /* Controller Configuration */ u32 run_bits; u32 dma_width; + u32 drcmr_ext_base; }; struct mmp_pdma_device { @@ -195,6 +199,13 @@ struct mmp_pdma_device { #define to_mmp_pdma_dev(dmadev) \ container_of(dmadev, struct mmp_pdma_device, device) +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) { @@ -301,7 +312,7 @@ static void enable_chan(struct mmp_pdma_phy *phy) pdev = to_mmp_pdma_dev(phy->vchan->chan.device); - reg = DRCMR(phy->vchan->drcmr); + reg = mmp_pdma_get_drcmr(pdev, phy->vchan->drcmr); writel(DRCMR_MAPVLD | phy->idx, phy->base + reg); dalgn = readl(phy->base + DALGN); @@ -437,7 +448,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); @@ -1179,6 +1190,7 @@ static const struct mmp_pdma_ops marvell_pdma_v1_ops = { .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 = { @@ -1192,6 +1204,7 @@ static const struct mmp_pdma_ops spacemit_k1_pdma_ops = { .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[] = { From 6e2dd05244bcfd137afbae61e9b603b1d859ff82 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Mon, 18 May 2026 11:32:43 +0800 Subject: [PATCH 13/15] dmaengine: mmp_pdma: add SpacemiT K3 support mainline inclusion from mainline-7.2-rc1 commit 6587b8661a0b 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 | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 77a3729085ebc..d20fd094bdfb6 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -52,6 +52,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) */ @@ -1207,6 +1208,20 @@ 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", @@ -1214,6 +1229,9 @@ static const struct of_device_id mmp_pdma_dt_ids[] = { }, { .compatible = "spacemit,k1-pdma", .data = &spacemit_k1_pdma_ops + }, { + .compatible = "spacemit,k3-pdma", + .data = &spacemit_k3_pdma_ops }, { /* sentinel */ } From f374ade6c32aea5b2b5c13d0bd0dd1df85b3926f Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Mon, 18 May 2026 11:32:44 +0800 Subject: [PATCH 14/15] riscv: dts: spacemit: Add PDMA controller node for K3 SoC mainline inclusion from mainline-7.2-rc1 commit 3f47ca8bb3c3 category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 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. -------------------------------- 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 Signed-off-by: Yongchao Jia --- arch/riscv/boot/dts/spacemit/k3.dtsi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 8726a5a6eebc4..835a80904c6bb 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"; From 4ef875df2f4b608c1ed46c4a55ed6f2a3f61474e Mon Sep 17 00:00:00 2001 From: Yongchao Jia Date: Thu, 23 Jul 2026 22:34:51 +0800 Subject: [PATCH 15/15] riscv: config: enable dma driver for spacemit k1 and k3 category: feature bugzilla: https://github.com/RVCK-Project/rvck/issues/320 -------------------------------- Enable DMA support Signed-off-by: Yongchao Jia --- arch/riscv/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig index d540b77f78fac..5277df836b4ee 100644 --- a/arch/riscv/configs/defconfig +++ b/arch/riscv/configs/defconfig @@ -2179,6 +2179,7 @@ CONFIG_RTC_DRV_POLARFIRE_SOC=m CONFIG_DMADEVICES=y CONFIG_DMA_SUN6I=m CONFIG_DW_AXI_DMAC=m +CONFIG_MMP_PDMA=m CONFIG_ASYNC_TX_DMA=y CONFIG_DMATEST=y CONFIG_UIO_CIF=m