All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] dmaengine: ingenic: add Ingenic PDMA controller support.
@ 2024-03-21  8:02 bin.yao
  2024-03-21  8:02 ` [PATCH 2/2] dt-bindings: dma: Convert ingenic-pdma doc to YAML bin.yao
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: bin.yao @ 2024-03-21  8:02 UTC (permalink / raw
  To: vkoul
  Cc: robh+dt, krzysztof.kozlowski+dt, conor+dt, dmaengine, devicetree,
	linux-kernel, 1587636487, bin.yao

From: "bin.yao" <bin.yao@ingenic.com>

This module can be found on ingenic victory soc.

Signed-off-by: bin.yao <bin.yao@ingenic.com>
---
 drivers/dma/Kconfig        |    6 +
 drivers/dma/Makefile       |    1 +
 drivers/dma/ingenic-pdma.c | 1356 ++++++++++++++++++++++++++++++++++++
 3 files changed, 1363 insertions(+)
 create mode 100644 drivers/dma/ingenic-pdma.c

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index e928f2ca0f1e..ac3b35feb784 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -179,6 +179,12 @@ config DMA_SUN6I
 	help
 	  Support for the DMA engine first found in Allwinner A31 SoCs.
 
+config DMA_INGENIC_SOC
+	tristate "Ingenic SoC DMA support"
+	select DMA_ENGINE
+	help
+	  Support the DMA engine found on Ingenic SoCs.
+
 config DW_AXI_DMAC
 	tristate "Synopsys DesignWare AXI DMA support"
 	depends on OF
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index dfd40d14e408..c3557c5aff96 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_UNIPHIER_MDMAC) += uniphier-mdmac.o
 obj-$(CONFIG_UNIPHIER_XDMAC) += uniphier-xdmac.o
 obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
 obj-$(CONFIG_ST_FDMA) += st_fdma.o
+obj-$(CONFIG_DMA_INGENIC_SOC) += ingenic-pdma.o
 obj-$(CONFIG_FSL_DPAA2_QDMA) += fsl-dpaa2-qdma/
 obj-$(CONFIG_INTEL_LDMA) += lgm/
 
diff --git a/drivers/dma/ingenic-pdma.c b/drivers/dma/ingenic-pdma.c
new file mode 100644
index 000000000000..84c6d810403c
--- /dev/null
+++ b/drivers/dma/ingenic-pdma.c
@@ -0,0 +1,1356 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024 Ingenic Semiconductor Co., Ltd.
+ * Author: bin.yao <bin.yao@ingenic.com>
+ */
+
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/of_dma.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include "dmaengine.h"
+
+#include <dt-bindings/dma/ingenic-pdma.h>
+
+#define TCSM					0x2000
+
+#define DMAC					0x1000
+#define DIRQP					0x1004
+#define DDR					0x1008
+#define DDRS					0x100C
+#define DIP					0x1010
+#define DIC					0x1014
+#define DMACP					0x101C
+#define DSIRQP					0x1020
+#define DSIRQM					0x1024
+#define DCIRQP					0x1028
+#define DCIRQM					0x102C
+
+#define DMACH_OFF				0x20
+
+#define CH_DSA					0x00
+#define CH_DTA					0x04
+#define CH_DTC					0x08
+#define CH_DRT					0x0C
+#define CH_DCS					0x10
+#define CH_DCM					0x14
+#define CH_DDA					0x18
+#define CH_DSD					0x1C
+
+/* DCS */
+#define DCS_CDOA_MSK				(0xff << DCS_CDOA_SFT)
+#define DCS_CDOA_SFT				8
+#define DCS_NDES				BIT(31)
+#define DCS_DES8				BIT(30)
+#define DCS_AR					BIT(4)
+#define DCS_TT					BIT(3)
+#define DCS_HLT					BIT(2)
+#define DCS_CTE					BIT(0)
+
+/* DTC */
+#define DTC_TC_MSK				0xffffff
+#define DTC_TC_SFT				0
+
+/* DCM */
+#define DCM_PORT_8				(0x1 << 14 | 0x1 << 12)
+#define DCM_PORT_16				(0x2 << 14 | 0x2 << 12)
+#define DCM_PORT_32				(0x0 << 14 | 0x0 << 12)
+#define DCM_RDIL_MSK				(0xf << DCM_RDIL_SFT)
+#define DCM_TSZ_MSK				(0x7 << DCM_TSZ_SFT)
+#define DCM_PORT_MSK				(0xf << 12)
+#define DCM_SAI					BIT(23)
+#define DCM_DAI					BIT(22)
+#define DCM_STDE				BIT(2)
+#define DCM_TIE					BIT(1)
+#define DCM_LINK				BIT(0)
+#define DCM_RDIL_SFT				16
+#define DCM_RDIL_MAX				9
+#define DCM_TSZ_SFT				8
+#define DCM_TSZ_AUTO				7
+
+/* DDA */
+#define PHY_TO_DESC_DOA(dma)			((((unsigned long)dma & DDA_DOA_MSK) \
+						   >> DDA_DOA_SFT) << 24)
+#define DDA_DBA_MSK				(0xfffff << DDA_DBA_SFT)
+#define DDA_DOA_MSK				(0xff << DDA_DOA_SFT)
+#define DDA_DBA_SFT				12
+#define DDA_DOA_SFT				4
+
+/* DSD */
+#define DSD_TSD_MSK				(0xffff << DSD_TSD_SFT)
+#define DSD_SSD_MSK				(0xffff << DSD_SSD_SFT)
+#define DSD_TSD_SFT				16
+#define DSD_SSD_SFT				0
+
+/* DMAC */
+#define DMAC_INTCC_MSK				(0x1f << 17)
+#define DMAC_INTCC_SFT				17
+#define DMAC_FMSC				BIT(31)
+#define DMAC_FSSI				BIT(30)
+#define DMAC_FTSSI				BIT(29)
+#define DMAC_FUART				BIT(28)
+#define DMAC_FAIC				BIT(27)
+#define DMAC_INTCE				BIT(16)
+#define DMAC_HLT				BIT(3)
+#define DMAC_AR					BIT(2)
+#define DMAC_CH01				BIT(1)
+#define DMAC_DMAE				BIT(0)
+
+#define DMA_SPECAIL_CHS				0x3
+
+/* Hardware params */
+#define HWATTR_INTC_IRQ_SUP(x)			(HWATTR_INTC_IRQ & (x))
+#define HWATTR_SPECIAL_CH01_SUP(x)		(HWATTR_SPECIAL_CH01 & (x))
+#define HWATTR_DESC_INTER_SUP(x)		(HWATTR_DESC_INTER & (x))
+#define HWATTR_INTC_IRQ				(1 << 0)
+#define HWATTR_SPECIAL_CH01			(1 << 1)
+#define HWATTR_DESC_INTER			(1 << 2)
+
+#define DMA_DCM_TSZ_TRANSFER_UINT_128_BYTE	0x6
+#define DMA_DCM_TSZ_TRANSFER_UINT_64_BYTE	0x5
+#define DMA_DCM_TSZ_TRANSFER_UINT_32_BYTE	0x4
+#define DMA_DCM_TSZ_TRANSFER_UINT_16_BYTE	0x3
+#define DMA_DCM_TSZ_TRANSFER_UINT_4_BYTE	0x0
+#define DMA_DCM_TSZ_TRANSFER_UINT_2_BYTE	0x2
+#define DMA_DCM_TSZ_TRANSFER_UINT_1_BYTE	0x1
+
+#define INGENIC_DMA_TYPE_REQ_MSK		0xff
+#define INGENIC_DMA_TYPE_CH_SFT			8
+#define INGENIC_DMA_TYPE_CH_MSK			(0xff << INGENIC_DMA_TYPE_CH_SFT)
+#define INGENIC_DMA_TYPE_CH_EN			(1 << 16)
+#define INGENIC_DMA_TYPE_PROG			(1 << 17)
+
+#define INGENIC_DMA_REQ_AUTO			0xff
+#define INGENIC_DMA_CHAN_CNT			32
+
+#define INGENIC_DMA_CH(ch)	\
+	((((ch) << INGENIC_DMA_TYPE_CH_SFT) & \
+	  INGENIC_DMA_TYPE_CH_MSK) | \
+	 INGENIC_DMA_TYPE_CH_EN)
+
+/* 8-word hardware dma descriptor */
+struct ingenic_dma_hdesc {
+	unsigned long dcm;
+	dma_addr_t dsa;
+	dma_addr_t dta;
+	unsigned long dtc;
+	unsigned long sd;
+	unsigned long drt;
+	unsigned long reserved[2];
+};
+
+/**
+ * struct ingenic_dma_sdesc - Dma software descriptor
+ * @ingenic_dma_sdesc_node: Hardware dma descriptor addr list
+ * @p_hdesc: Dma descriptor physics addr
+ * @v_hdesc: Dma descriptor virtual addr
+ * @desc: Dma async txdescriptor
+ * @in_chan: The channel where the DMA descriptor is located
+ * @attribute: Dma descriptor attribute
+ */
+struct ingenic_dma_sdesc {
+	struct list_head ingenic_dma_sdesc_node;
+	dma_addr_t p_hdesc;
+	struct ingenic_dma_hdesc *v_hdesc;
+
+	struct dma_async_tx_descriptor desc;
+	struct ingenic_dma_chan *in_chan;
+
+#define DESC_HEAD 1
+#define DESC_MIDDLE 2
+#define DESC_TAIL 4
+	unsigned int attribute;
+};
+
+/**
+ * struct ingenic_dma_chan - Dma channel
+ * @id: Channel id
+ * @slave_id: Request type of the channel
+ * @iomem: Dma channel peripheral address
+ * @engine: Dma engine
+ * @chan: Dma chan
+ * @hdesc_pool: HW descriptors pool
+ * @task: tasklet struct
+ * @slave_addr: Slave_addr
+ * @maxburst: Hardware maxburst
+ * @transfer_width: Transfer width
+ * @dcm: The DCM of HW descriptor initial value
+ * @working_sdesc: Current working dma descriptor
+ * @hdesc_lock: Lock
+ * @completion: Channel terminated completion
+ * @ingenic_dma_sdesc_list_submitted: List of all need submitted dma software descriptor
+ * @ingenic_dma_sdesc_list_issued: List of all issued dma software descriptor
+ */
+struct ingenic_dma_chan {
+	int id;
+	unsigned int slave_id;
+	void __iomem *iomem;
+	struct ingenic_dma_engine *engine;
+	struct dma_chan chan;
+	struct dma_pool *hdesc_pool;
+	struct tasklet_struct task;
+
+	dma_addr_t slave_addr;
+	unsigned int maxburst;
+	unsigned int transfer_width;
+	unsigned int dcm;
+	struct ingenic_dma_sdesc *working_sdesc;
+
+	spinlock_t hdesc_lock;
+	struct completion completion;
+	struct list_head ingenic_dma_sdesc_list_submitted;
+	struct list_head ingenic_dma_sdesc_list_issued;
+};
+
+/**
+ * struct ingenic_dma_data - Dma parameter
+ * @nb_channels: Number of DMA channels
+ * @hwattr: DMA channels attribute
+ */
+struct ingenic_dma_data {
+	unsigned int nb_channels;
+	unsigned int hwattr;
+};
+
+/**
+ * struct ingenic_dma_engine - Dma engine
+ * @dev: Device
+ * @iomem: Dma engine peripheral address
+ * @gate_clk: Clock
+ * @dma_device: Dma device
+ * @chan_reserved: Dma reserved channel
+ * @chan_programed: Dma programed channel
+ * @intc_ch: intc channel
+ * @special_ch: special channel
+ * @irq_pdma: pdma interrupt
+ * @irq_pdmad: pdma descriptor interrupt
+ * @dma_data: Dma data
+ * @ingenic_dma_chan: Dma channel pointer array
+ */
+struct ingenic_dma_engine {
+	struct device *dev;
+	void __iomem *iomem;
+	struct clk *gate_clk;
+	struct dma_device dma_device;
+
+	uint32_t chan_reserved;
+	uint32_t chan_programed;
+	uint32_t intc_ch;
+	bool special_ch;
+
+	int irq_pdma;
+	int irq_pdmad;
+
+	struct ingenic_dma_data *dma_data;
+	struct ingenic_dma_chan *chan[INGENIC_DMA_CHAN_CNT];
+};
+
+/* dma hardware request */
+unsigned long pdma_maps[INGENIC_DMA_CHAN_CNT] = {
+	INGENIC_DMA_REQ_AUTO,
+	INGENIC_DMA_REQ_AUTO,
+	INGENIC_DMA_REQ_AIC_LOOP_RX,
+	INGENIC_DMA_REQ_AIC_TX,
+	INGENIC_DMA_REQ_AIC_F_RX,
+	INGENIC_DMA_REQ_AUTO_TX,
+	INGENIC_DMA_REQ_SADC_RX,
+	INGENIC_DMA_REQ_UART5_TX,
+	INGENIC_DMA_REQ_UART5_RX,
+	INGENIC_DMA_REQ_UART4_TX,
+	INGENIC_DMA_REQ_UART4_RX,
+	INGENIC_DMA_REQ_UART3_TX,
+	INGENIC_DMA_REQ_UART3_RX,
+	INGENIC_DMA_REQ_UART2_TX,
+	INGENIC_DMA_REQ_UART2_RX,
+	INGENIC_DMA_REQ_UART1_TX,
+	INGENIC_DMA_REQ_UART1_RX,
+	INGENIC_DMA_REQ_UART0_TX,
+	INGENIC_DMA_REQ_UART0_RX,
+	INGENIC_DMA_REQ_SSI0_TX,
+	INGENIC_DMA_REQ_SSI0_RX,
+	INGENIC_DMA_REQ_SSI1_TX,
+	INGENIC_DMA_REQ_SSI1_RX,
+	INGENIC_DMA_REQ_SLV_TX,
+	INGENIC_DMA_REQ_SLV_RX,
+	INGENIC_DMA_REQ_I2C0_TX,
+	INGENIC_DMA_REQ_I2C0_RX,
+	INGENIC_DMA_REQ_I2C1_TX,
+	INGENIC_DMA_REQ_I2C1_RX,
+	INGENIC_DMA_REQ_DES_TX,
+	INGENIC_DMA_REQ_DES_RX,
+};
+
+static dma_cookie_t ingenic_dma_tx_submit(struct dma_async_tx_descriptor *tx);
+
+static struct ingenic_dma_chan *to_ingenic_dma_chan(struct dma_chan *chan)
+{
+	return container_of(chan, struct ingenic_dma_chan, chan);
+}
+
+static struct ingenic_dma_sdesc *to_ingenic_dma_sdesc(struct dma_async_tx_descriptor *desc)
+{
+	return container_of(desc, struct ingenic_dma_sdesc, desc);
+}
+
+static unsigned int ingenic_cal_max_dma_size(unsigned long val,
+					     unsigned int *shift)
+{
+	unsigned int ret;
+	int ord = ffs(val) - 1;
+
+	switch (ord) {
+	case 0:
+		ret = DMA_DCM_TSZ_TRANSFER_UINT_1_BYTE;
+		break;
+	case 1:
+		ret = DMA_DCM_TSZ_TRANSFER_UINT_2_BYTE;
+		break;
+	case 2:
+	case 3:
+		ret = DMA_DCM_TSZ_TRANSFER_UINT_4_BYTE;
+		ord = 2;
+		break;
+	case 4:
+		ret = DMA_DCM_TSZ_TRANSFER_UINT_16_BYTE;
+		break;
+	case 5:
+		ret = DMA_DCM_TSZ_TRANSFER_UINT_32_BYTE;
+		break;
+	case 6:
+		ret = DMA_DCM_TSZ_TRANSFER_UINT_64_BYTE;
+		break;
+	default:
+		ret = DMA_DCM_TSZ_TRANSFER_UINT_128_BYTE;
+		ord = 7;
+		break;
+	}
+
+	if (shift)
+		*shift = ord;
+
+	return ret;
+}
+
+static struct ingenic_dma_sdesc *
+ingenic_dma_alloc_single_sdesc(struct ingenic_dma_chan *dmac)
+{
+	struct ingenic_dma_sdesc *sdesc;
+
+	sdesc = kzalloc(sizeof(struct ingenic_dma_sdesc),
+			GFP_NOWAIT);
+	if (!sdesc)
+		return NULL;
+
+	INIT_LIST_HEAD(&sdesc->ingenic_dma_sdesc_node);
+
+	sdesc->v_hdesc = dma_pool_alloc(dmac->hdesc_pool, GFP_NOWAIT,
+					&sdesc->p_hdesc);
+	if (!sdesc->v_hdesc) {
+		dev_err(dmac->engine->dev,
+			"Couldn't allocate the hw_desc from dma_pool %p\n",
+			dmac->hdesc_pool);
+		kfree(sdesc);
+		return NULL;
+	}
+
+	return sdesc;
+}
+
+static void
+ingenic_dma_free_single_sdesc(struct ingenic_dma_chan *dmac,
+			      struct ingenic_dma_sdesc *sdesc)
+{
+	if (sdesc->v_hdesc)
+		dma_pool_free(dmac->hdesc_pool, sdesc->v_hdesc,
+			      sdesc->p_hdesc);
+
+	kfree(sdesc);
+}
+
+static unsigned int
+ingenic_fill_single_slave_hdesc(struct ingenic_dma_chan *dmac,
+				dma_addr_t addr,
+				unsigned int length,
+				enum dma_transfer_direction direction,
+				struct ingenic_dma_hdesc *desc)
+{
+	unsigned int rdil;
+
+	desc->dcm = dmac->dcm;
+	desc->drt = dmac->slave_id;
+	desc->sd = 0;
+
+	if (direction == DMA_DEV_TO_MEM) {
+		desc->dta = addr;
+		desc->dsa = dmac->slave_addr;
+		desc->dcm |= DCM_DAI;
+	} else {
+		desc->dsa = addr;
+		desc->dta = dmac->slave_addr;
+		desc->dcm |= DCM_SAI;
+	}
+
+	rdil = dmac->maxburst * dmac->transfer_width;
+	if (rdil > 4)
+		rdil = min((int)fls(rdil) + 1, (int)DCM_RDIL_MAX);
+
+	WARN_ON(length & (~DTC_TC_MSK));
+	if (WARN_ON(!IS_ALIGNED(length, dmac->transfer_width)))
+		desc->dtc = ALIGN_DOWN((length & DTC_TC_MSK), dmac->transfer_width);
+	else
+		desc->dtc = (length & DTC_TC_MSK);
+	desc->dcm |= DCM_TSZ_MSK | (rdil << DCM_RDIL_SFT);
+
+	return desc->dtc;
+}
+
+static struct dma_async_tx_descriptor *
+ingenic_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+			  unsigned int sg_len, enum dma_transfer_direction direction,
+			  unsigned long flags, void *context)
+{
+	struct ingenic_dma_chan *dmac = to_ingenic_dma_chan(chan);
+	struct ingenic_dma_sdesc *first_sdma_desc = NULL,
+				 *last_sdma_desc = NULL,
+				 *sdesc = NULL;
+	unsigned long flg;
+	int i;
+
+	if (unlikely(!sg_len))
+		return NULL;
+
+	if (unlikely(!is_slave_direction(direction))) {
+		dev_err(dmac->engine->dev,
+			"Direction %d unsupported\n", direction);
+		return NULL;
+	}
+
+	spin_lock_irqsave(&dmac->hdesc_lock, flg);
+
+	for (i = 0; i < sg_len; i++) {
+		sdesc = ingenic_dma_alloc_single_sdesc(dmac);
+		if (!sdesc) {
+			dev_warn(dmac->engine->dev,
+				 "Alloc sdesc fail, Stop subsequent transfers\n");
+			if (last_sdma_desc) {
+				last_sdma_desc->v_hdesc->dcm &= ~DCM_LINK;
+				last_sdma_desc->v_hdesc->dcm |= DCM_TIE;
+				last_sdma_desc->attribute |= DESC_TAIL;
+				goto ret;
+			} else {
+				spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+				return NULL;
+			}
+		}
+
+		ingenic_fill_single_slave_hdesc(dmac, sg_dma_address(&sgl[i]),
+						sg_dma_len(&sgl[i]), direction, sdesc->v_hdesc);
+
+		if (i == 0) {
+			first_sdma_desc = sdesc;
+			sdesc->attribute |= DESC_HEAD;
+		} else {
+			last_sdma_desc->v_hdesc->dcm |= DCM_LINK;
+			last_sdma_desc->v_hdesc->dtc |= PHY_TO_DESC_DOA(sdesc->p_hdesc);
+			sdesc->attribute |= DESC_MIDDLE;
+		}
+
+		if (i == (sg_len - 1)) {
+			if (last_sdma_desc) {
+				last_sdma_desc->v_hdesc->dcm |= DCM_LINK;
+				last_sdma_desc->v_hdesc->dtc |= PHY_TO_DESC_DOA(sdesc->p_hdesc);
+			}
+			sdesc->v_hdesc->dcm |= DCM_TIE;
+			sdesc->attribute |= DESC_TAIL;
+		}
+
+		last_sdma_desc = sdesc;
+		list_add_tail(&sdesc->ingenic_dma_sdesc_node,
+			      &dmac->ingenic_dma_sdesc_list_submitted);
+	}
+
+ret:
+	dma_async_tx_descriptor_init(&first_sdma_desc->desc, &dmac->chan);
+	first_sdma_desc->desc.flags = flags;
+	first_sdma_desc->in_chan = dmac;
+	first_sdma_desc->desc.tx_submit = ingenic_dma_tx_submit;
+
+	spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+
+	return &first_sdma_desc->desc;
+}
+
+static struct dma_async_tx_descriptor *
+ingenic_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr,
+			    size_t buf_len, size_t period_len,
+			    enum dma_transfer_direction direction,
+			    unsigned long flags)
+{
+	struct ingenic_dma_chan *dmac = to_ingenic_dma_chan(chan);
+	struct ingenic_dma_sdesc *first_sdma_desc = NULL,
+				 *last_sdma_desc = NULL,
+				 *sdesc = NULL;
+	unsigned int periods = buf_len / period_len;
+	unsigned long flg;
+	int i;
+
+	if (unlikely(!buf_len || !period_len)) {
+		dev_err(dmac->engine->dev,
+			"Invalid buffer/period len\n");
+		return NULL;
+	}
+
+	if (unlikely(!is_slave_direction(direction))) {
+		dev_err(dmac->engine->dev,
+			"Direction %d unsupported\n", direction);
+		return NULL;
+	}
+
+	spin_lock_irqsave(&dmac->hdesc_lock, flg);
+
+	for (i = 0; i < periods; i++) {
+		sdesc = ingenic_dma_alloc_single_sdesc(dmac);
+		if (!sdesc) {
+			dev_warn(dmac->engine->dev,
+				 "Alloc sdesc fail, Stop subsequent transfers\n");
+			if (last_sdma_desc) {
+				last_sdma_desc->v_hdesc->dcm &= ~DCM_LINK;
+				last_sdma_desc->v_hdesc->dcm |= DCM_TIE;
+				last_sdma_desc->attribute |= DESC_TAIL;
+				goto ret;
+			} else {
+				spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+				return NULL;
+			}
+		}
+
+		ingenic_fill_single_slave_hdesc(dmac, buf_addr + (i * period_len),
+						period_len, direction, sdesc->v_hdesc);
+
+		if (i == 0) {
+			first_sdma_desc = sdesc;
+			sdesc->attribute |= DESC_HEAD;
+		} else {
+			last_sdma_desc->v_hdesc->dcm |= DCM_LINK;
+			last_sdma_desc->v_hdesc->dtc |= PHY_TO_DESC_DOA(sdesc->p_hdesc);
+			sdesc->attribute |= DESC_MIDDLE;
+		}
+		if (i == (periods - 1)) {
+			if (last_sdma_desc) {
+				last_sdma_desc->v_hdesc->dcm |= DCM_LINK;
+				last_sdma_desc->v_hdesc->dtc |= PHY_TO_DESC_DOA(sdesc->p_hdesc);
+			}
+			sdesc->v_hdesc->dcm |= DCM_LINK;
+			sdesc->attribute |= DESC_TAIL;
+			sdesc->v_hdesc->dtc |= PHY_TO_DESC_DOA(first_sdma_desc->p_hdesc);
+		}
+
+		last_sdma_desc = sdesc;
+		list_add_tail(&sdesc->ingenic_dma_sdesc_node,
+			      &dmac->ingenic_dma_sdesc_list_submitted);
+	}
+
+ret:
+	dma_async_tx_descriptor_init(&first_sdma_desc->desc, &dmac->chan);
+	first_sdma_desc->desc.flags = flags;
+	first_sdma_desc->in_chan = dmac;
+	first_sdma_desc->desc.tx_submit = ingenic_dma_tx_submit;
+
+	spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+
+	return &first_sdma_desc->desc;
+}
+
+static void ingenic_fill_single_hdesc(dma_addr_t saddr, dma_addr_t daddr,
+				      unsigned int length,
+				      struct ingenic_dma_hdesc *desc)
+{
+	unsigned int tsz, transfer_shift, len = length;
+
+	if (length < DTC_TC_MSK) {
+		desc->dcm = DCM_DAI | DCM_SAI |
+			    (DCM_TSZ_AUTO << DCM_TSZ_SFT) |
+			    (DCM_RDIL_MAX << DCM_RDIL_SFT);
+		desc->dtc = len;
+	} else {
+		len = ALIGN_DOWN(len, sizeof(uint32_t));
+		tsz = ingenic_cal_max_dma_size(len, &transfer_shift);
+		desc->dcm = DCM_DAI | DCM_SAI | (tsz << DCM_TSZ_SFT);
+		desc->dtc = len >> transfer_shift;
+	}
+
+	desc->dsa = saddr;
+	desc->dta = daddr;
+	desc->sd = 0;
+	desc->drt = INGENIC_DMA_REQ_AUTO_TX;
+}
+
+static struct dma_async_tx_descriptor *
+ingenic_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dma_dest,
+			     dma_addr_t dma_src, size_t len, unsigned long flags)
+{
+	struct ingenic_dma_chan *dmac = to_ingenic_dma_chan(chan);
+	struct ingenic_dma_sdesc *sdesc;
+	unsigned long flg;
+
+	if (unlikely(!len))
+		return NULL;
+
+	spin_lock_irqsave(&dmac->hdesc_lock, flg);
+
+	sdesc = ingenic_dma_alloc_single_sdesc(dmac);
+	if (!sdesc) {
+		dev_err(dmac->engine->dev, "Can not alloc sdesc\n");
+		return NULL;
+	}
+
+	ingenic_fill_single_hdesc(dma_src, dma_dest, len, sdesc->v_hdesc);
+	sdesc->v_hdesc->dcm |= DCM_TIE;
+	sdesc->attribute |= DESC_HEAD | DESC_TAIL | DESC_MIDDLE;
+	list_add_tail(&sdesc->ingenic_dma_sdesc_node,
+		      &dmac->ingenic_dma_sdesc_list_submitted);
+
+	dma_async_tx_descriptor_init(&sdesc->desc, &dmac->chan);
+	sdesc->desc.flags = flags;
+	sdesc->in_chan = dmac;
+	sdesc->desc.tx_submit = ingenic_dma_tx_submit;
+
+	spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+
+	return &sdesc->desc;
+}
+
+static enum dma_status ingenic_dma_tx_status(struct dma_chan *chan,
+					     dma_cookie_t cookie,
+					     struct dma_tx_state *txstate)
+{
+	return dma_cookie_status(chan, cookie, txstate);
+}
+
+static dma_cookie_t ingenic_dma_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+	struct ingenic_dma_sdesc *sdesc = to_ingenic_dma_sdesc(tx), *tmp;
+	struct ingenic_dma_chan *dmac = sdesc->in_chan;
+	dma_cookie_t cookie;
+	unsigned long flg;
+
+	if (unlikely(!tx))
+		return -EINVAL;
+
+	spin_lock_irqsave(&dmac->hdesc_lock, flg);
+
+	if (!list_empty(&dmac->ingenic_dma_sdesc_list_submitted)) {
+		sdesc = list_first_entry(&dmac->ingenic_dma_sdesc_list_submitted,
+					 struct ingenic_dma_sdesc,
+					 ingenic_dma_sdesc_node);
+		cookie = dma_cookie_assign(&sdesc->desc);
+		if (!(sdesc->attribute & DESC_HEAD)) {
+			spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+			dev_err(dmac->engine->dev,
+				"First submitted list not DESC_HEAD\n");
+			return -EINVAL;
+		}
+	} else {
+		spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+		dev_err(dmac->engine->dev, "Submitted list empty\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * move perpare submitt dma software descriptor to issued list.
+	 */
+	while (!list_empty(&dmac->ingenic_dma_sdesc_list_submitted)) {
+		sdesc = list_first_entry(&dmac->ingenic_dma_sdesc_list_submitted,
+					 struct ingenic_dma_sdesc,
+					 ingenic_dma_sdesc_node);
+		list_del(&sdesc->ingenic_dma_sdesc_node);
+		list_add_tail(&sdesc->ingenic_dma_sdesc_node,
+			      &dmac->ingenic_dma_sdesc_list_issued);
+		if (sdesc->attribute & DESC_TAIL)
+			break;
+	}
+
+	/*
+	 * If the DMA hardware does not support the DMA descriptor linking mode,
+	 * we must rely on software to simulate the DMA descriptor linking mode.
+	 * This requires that a DMA interrupt be triggered after each DMA descriptor
+	 * completes its transmission. The software then updates the DMA descriptor
+	 * in the DMA interrupt service function.
+	 */
+	if (!HWATTR_DESC_INTER_SUP(dmac->engine->dma_data->hwattr)) {
+		if (!list_empty(&dmac->ingenic_dma_sdesc_list_issued)) {
+			list_for_each_entry_safe(sdesc, tmp,
+						 &dmac->ingenic_dma_sdesc_list_issued,
+						 ingenic_dma_sdesc_node) {
+				sdesc->v_hdesc->dcm &= ~DCM_LINK;
+				sdesc->v_hdesc->dcm |= DCM_TIE;
+			}
+			dmac->working_sdesc = list_first_entry(&dmac->ingenic_dma_sdesc_list_issued,
+							       struct ingenic_dma_sdesc,
+							       ingenic_dma_sdesc_node);
+		} else {
+			spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+			dev_err(dmac->engine->dev, "Issued list empty\n");
+			return -EINVAL;
+		}
+	}
+
+	spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+
+	return cookie;
+}
+
+static void ingenic_dma_issue_pending(struct dma_chan *chan)
+{
+	struct ingenic_dma_chan *dmac = to_ingenic_dma_chan(chan);
+	struct ingenic_dma_sdesc *sdesc;
+	unsigned long flg;
+	unsigned int dcm;
+
+	spin_lock_irqsave(&dmac->hdesc_lock, flg);
+
+	if (!list_empty(&dmac->ingenic_dma_sdesc_list_issued)) {
+		sdesc = list_first_entry(&dmac->ingenic_dma_sdesc_list_issued,
+					 struct ingenic_dma_sdesc,
+					 ingenic_dma_sdesc_node);
+
+		if (HWATTR_DESC_INTER_SUP(dmac->engine->dma_data->hwattr)) {
+			dcm = readl(dmac->iomem + CH_DCM);
+			dcm |= DCM_LINK;
+			writel(dcm, dmac->iomem + CH_DCM);
+		}
+
+		dcm = readl(dmac->iomem + CH_DCM);
+		dcm &= ~DCM_TIE;
+		writel(dcm, dmac->iomem + CH_DCM);
+
+		writel(sdesc->p_hdesc, dmac->iomem + CH_DDA);
+		writel(BIT(dmac->id), dmac->engine->iomem + DDRS);
+		writel(DCS_DES8 | DCS_CTE, dmac->iomem + CH_DCS);
+	} else
+		dev_err(dmac->engine->dev,
+			"Issued list empty, can not submmit\n");
+
+	spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+}
+
+static int ingenic_dma_terminate_all(struct dma_chan *chan)
+{
+	struct ingenic_dma_chan *dmac = to_ingenic_dma_chan(chan);
+	struct ingenic_dma_sdesc *sdesc;
+	unsigned long flg;
+	unsigned int dcm;
+
+	spin_lock_irqsave(&dmac->hdesc_lock, flg);
+
+	reinit_completion(&dmac->completion);
+
+	/*
+	 * If ingenic_dma_sdesc_list_issued list not empty, it means
+	 * that DMA has data being transmitted.
+	 */
+	if (!list_empty(&dmac->ingenic_dma_sdesc_list_issued)) {
+		list_for_each_entry(sdesc,
+				    &dmac->ingenic_dma_sdesc_list_issued,
+				    ingenic_dma_sdesc_node) {
+			sdesc->v_hdesc->dcm &= ~DCM_LINK;
+			sdesc->v_hdesc->dcm |= DCM_TIE;
+		}
+
+		if (HWATTR_DESC_INTER_SUP(dmac->engine->dma_data->hwattr)) {
+			/*
+			 * The version of controller support descriptor interrupt
+			 * can clear LINK on runtime
+			 */
+			dcm = readl(dmac->iomem + CH_DCM);
+			if (dcm & DCM_LINK) {
+				dcm &= ~DCM_LINK;
+				writel(dcm, dmac->iomem + CH_DCM);
+			}
+		}
+
+		if (readl(dmac->iomem + CH_DCS) & DCS_CTE) {
+			if (!(readl(dmac->iomem + CH_DCS) & DCS_TT))
+				wait_for_completion(&dmac->completion);
+		}
+	}
+
+	writel(0, dmac->iomem + CH_DCS);
+
+	/*
+	 * Clear and free all issued dma descriptor.
+	 */
+	while (!list_empty(&dmac->ingenic_dma_sdesc_list_issued)) {
+		sdesc = list_first_entry(&dmac->ingenic_dma_sdesc_list_issued,
+					 struct ingenic_dma_sdesc,
+					 ingenic_dma_sdesc_node);
+		list_del(&sdesc->ingenic_dma_sdesc_node);
+		ingenic_dma_free_single_sdesc(dmac, sdesc);
+	}
+
+	/*
+	 * Clear and free all submitted dma descriptor.
+	 */
+	while (!list_empty(&dmac->ingenic_dma_sdesc_list_submitted)) {
+		sdesc = list_first_entry(&dmac->ingenic_dma_sdesc_list_submitted,
+					 struct ingenic_dma_sdesc,
+					 ingenic_dma_sdesc_node);
+		list_del(&sdesc->ingenic_dma_sdesc_node);
+		ingenic_dma_free_single_sdesc(dmac, sdesc);
+	}
+
+	spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+
+	return 0;
+}
+
+static void ingenic_dma_complete(unsigned long arg)
+{
+	struct ingenic_dma_chan *dmac = (struct ingenic_dma_chan *)arg;
+	struct ingenic_dma_sdesc *sdesc, *fsdesc;
+	struct dmaengine_desc_callback cb;
+	unsigned long flg;
+
+	spin_lock_irqsave(&dmac->hdesc_lock, flg);
+
+	if (!list_empty(&dmac->ingenic_dma_sdesc_list_issued)) {
+		fsdesc = list_first_entry(&dmac->ingenic_dma_sdesc_list_issued,
+					  struct ingenic_dma_sdesc,
+					  ingenic_dma_sdesc_node);
+
+		dmaengine_desc_get_callback(&fsdesc->desc, &cb);
+		dma_cookie_complete(&fsdesc->desc);
+
+		while (!list_empty(&dmac->ingenic_dma_sdesc_list_issued)) {
+			sdesc = list_first_entry(&dmac->ingenic_dma_sdesc_list_issued,
+						 struct ingenic_dma_sdesc,
+						 ingenic_dma_sdesc_node);
+			list_del(&sdesc->ingenic_dma_sdesc_node);
+			ingenic_dma_free_single_sdesc(dmac, sdesc);
+		}
+
+		if (dmaengine_desc_callback_valid(&cb))
+			dmaengine_desc_callback_invoke(&cb, NULL);
+
+		complete(&dmac->completion);
+	} else
+		dev_err(dmac->engine->dev, "Issued list empty\n");
+
+	spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+}
+
+static void pdma_handle_chan_irq(struct ingenic_dma_engine *ingenic_dma,
+				 int ch_id)
+{
+	struct ingenic_dma_chan *dmac = ingenic_dma->chan[ch_id];
+	struct ingenic_dma_sdesc *sdesc;
+	unsigned int dcs, dcm;
+	unsigned long flg;
+
+	dcs = readl(dmac->iomem + CH_DCS);
+	writel(0, dmac->iomem + CH_DCS);
+
+	if (dcs & DCS_AR)
+		dev_warn(dmac->engine->dev,
+			 "Address error (DCS=0x%x)\n", dcs);
+
+	if (dcs & DCS_HLT)
+		dev_warn(dmac->engine->dev,
+			 "Channel halt (DCS=0x%x)\n", dcs);
+
+	if (dcs & DCS_TT) {
+		spin_lock_irqsave(&dmac->hdesc_lock, flg);
+
+		/*
+		 * If the DMA hardware does not support the DMA descriptor chaining mode,
+		 * we need to update the DMA descriptor in the interrupt service function.
+		 */
+		if ((!HWATTR_DESC_INTER_SUP(dmac->engine->dma_data->hwattr))
+		    && dmac->working_sdesc) {
+			if (!(dmac->working_sdesc->attribute & DESC_TAIL)) {
+				dmac->working_sdesc =
+					list_entry(dmac->working_sdesc->ingenic_dma_sdesc_node.next,
+						   struct ingenic_dma_sdesc,
+						   ingenic_dma_sdesc_node);
+
+				dcm = readl(dmac->iomem + CH_DCM);
+				dcm |= DCM_TIE;
+				writel(dcm, dmac->iomem + CH_DCM);
+
+				writel(dmac->working_sdesc->p_hdesc, dmac->iomem + CH_DDA);
+				writel(BIT(dmac->id), dmac->engine->iomem + DDRS);
+
+				writel(DCS_DES8 | DCS_CTE, dmac->iomem + CH_DCS);
+			} else
+				goto COMPLETE;
+		} else {
+COMPLETE:
+			if (!list_empty(&dmac->ingenic_dma_sdesc_list_issued)) {
+				sdesc = list_first_entry(&dmac->ingenic_dma_sdesc_list_issued,
+							 struct ingenic_dma_sdesc,
+							 ingenic_dma_sdesc_node);
+				tasklet_schedule(&dmac->task);
+			} else
+				dev_err(dmac->engine->dev,
+					 "No element issued but DSC_TT\n");
+		}
+
+		spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+	}
+}
+
+static irqreturn_t pdma_int_handler(int irq, void *dev)
+{
+	struct ingenic_dma_engine *ingenic_dma = (struct ingenic_dma_engine *)dev;
+	unsigned long pending, dmac;
+	int i;
+
+	pending = readl(ingenic_dma->iomem + DIRQP);
+	writel(~pending, ingenic_dma->iomem + DIRQP);
+
+	for (i = 0; i < ingenic_dma->dma_data->nb_channels ; i++) {
+		if (!(pending & (1 << i)))
+			continue;
+		pdma_handle_chan_irq(ingenic_dma, i);
+	}
+
+	dmac = readl(ingenic_dma->iomem + DMAC);
+	dmac &= ~(DMAC_HLT | DMAC_AR);
+	writel(dmac, ingenic_dma->iomem + DMAC);
+
+	return IRQ_HANDLED;
+}
+
+
+static irqreturn_t pdmad_int_handler(int irq, void *dev)
+{
+	struct ingenic_dma_engine *ingenic_dma = (struct ingenic_dma_engine *)dev;
+	struct ingenic_dma_sdesc *sdesc;
+	struct ingenic_dma_chan *dmac;
+	unsigned long pending, flg;
+	int i;
+
+	pending = readl(ingenic_dma->iomem + DIP);
+	writel(readl(ingenic_dma->iomem + DIP) & (~pending), ingenic_dma->iomem + DIC);
+
+	for (i = 0; i < ingenic_dma->dma_data->nb_channels; i++) {
+		dmac = ingenic_dma->chan[i];
+
+		if (!(pending & (1 << i)))
+			continue;
+
+		spin_lock_irqsave(&dmac->hdesc_lock, flg);
+
+		if (!list_empty(&dmac->ingenic_dma_sdesc_list_issued)) {
+			sdesc = list_first_entry(&dmac->ingenic_dma_sdesc_list_issued,
+						 struct ingenic_dma_sdesc,
+						 ingenic_dma_sdesc_node);
+			tasklet_schedule(&dmac->task);
+		} else
+			dev_err(dmac->engine->dev,
+				 "Issued list empty but dma descriptor handler happen\n");
+
+		spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int ingenic_dma_config(struct dma_chan *chan,
+			      struct dma_slave_config *config)
+{
+	struct ingenic_dma_chan *dmac = to_ingenic_dma_chan(chan);
+	enum dma_slave_buswidth transfer_width;
+
+	if (!config)
+		return -EINVAL;
+
+	switch (config->direction) {
+	case DMA_MEM_TO_DEV:
+		if (!config->dst_addr_width || !config->dst_addr)
+			return -EINVAL;
+		if (!config->dst_maxburst)
+			config->dst_maxburst = 1;
+		transfer_width = config->dst_addr_width;
+		dmac->slave_addr = config->dst_addr;
+		dmac->maxburst = config->dst_maxburst;
+		break;
+	case DMA_DEV_TO_MEM:
+		if (!config->src_addr_width || !config->src_addr)
+			return -EINVAL;
+		if (!config->src_maxburst)
+			config->src_maxburst = 1;
+		transfer_width = config->src_addr_width;
+		dmac->slave_addr = config->src_addr;
+		dmac->maxburst = config->src_maxburst;
+		break;
+	default:
+		if (!config->src_maxburst)
+			config->src_maxburst = 1;
+		transfer_width = config->src_addr_width;
+		dmac->slave_addr = config->src_addr;
+		dmac->maxburst = config->src_maxburst;
+		break;
+	}
+
+	switch (transfer_width) {
+	case DMA_SLAVE_BUSWIDTH_1_BYTE:
+		dmac->dcm = DCM_PORT_8;
+		dmac->transfer_width = 1;
+		break;
+	case DMA_SLAVE_BUSWIDTH_2_BYTES:
+		dmac->dcm = DCM_PORT_16;
+		dmac->transfer_width = 2;
+		break;
+	case DMA_SLAVE_BUSWIDTH_4_BYTES:
+		dmac->dcm = DCM_PORT_32;
+		dmac->transfer_width = 4;
+		break;
+	default:
+		dmac->dcm = DCM_PORT_8;
+		dmac->transfer_width = 1;
+		break;
+	}
+
+	return 0;
+}
+
+static int ingenic_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct ingenic_dma_chan *dmac = to_ingenic_dma_chan(chan);
+
+	if (dmac->hdesc_pool)
+		return 0;
+
+	dmac->hdesc_pool = dma_pool_create(dev_name(&chan->dev->device),
+					   chan->device->dev,
+					   sizeof(struct ingenic_dma_hdesc),
+					   __alignof__(struct ingenic_dma_hdesc), PAGE_SIZE);
+	if (!dmac->hdesc_pool) {
+		dev_err(dmac->engine->dev,
+			"Failed to allocate descriptor pool\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void ingenic_dma_free_chan_resources(struct dma_chan *chan)
+{
+	struct ingenic_dma_chan *dmac = to_ingenic_dma_chan(chan);
+
+	dma_pool_destroy(dmac->hdesc_pool);
+	dmac->hdesc_pool = NULL;
+}
+
+static bool ingenic_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+	struct ingenic_dma_chan *dmac = to_ingenic_dma_chan(chan);
+	unsigned int private = *(unsigned int *)param;
+	unsigned long type = (unsigned long)chan->private;
+	int channel = -1;
+
+	if (private & INGENIC_DMA_TYPE_CH_EN) {
+		channel = (private & INGENIC_DMA_TYPE_CH_MSK) >> INGENIC_DMA_TYPE_CH_SFT;
+		if (dmac->id == channel) {
+			dev_info(dmac->engine->dev,
+				 "Assignment channel is %d\n", channel);
+			return true;
+		}
+		return false;
+	}
+
+	if (dmac->engine->chan_reserved & BIT(dmac->id))
+		return false;
+
+	if ((private & INGENIC_DMA_TYPE_REQ_MSK) != type)
+		return false;
+
+	dmac->slave_id = private & INGENIC_DMA_TYPE_REQ_MSK;
+
+	return true;
+}
+
+static struct of_dma_filter_info of_ingenic_dma_info = {
+	.filter_fn = ingenic_dma_filter_fn,
+};
+
+static int ingenic_dma_chan_init(struct ingenic_dma_engine *dma, int id)
+{
+	struct ingenic_dma_chan *dmac = NULL;
+
+	if ((id < 0) || (id >= INGENIC_DMA_CHAN_CNT))
+		return -EINVAL;
+
+	dmac = devm_kzalloc(dma->dev, sizeof(*dmac), GFP_KERNEL);
+	if (!dmac)
+		return -ENOMEM;
+
+	dmac->id = id;
+	dmac->iomem = dma->iomem + dmac->id * DMACH_OFF;
+	dmac->engine = dma;
+
+	spin_lock_init(&dmac->hdesc_lock);
+	init_completion(&dmac->completion);
+
+	dmac->slave_id = pdma_maps[id] & INGENIC_DMA_TYPE_REQ_MSK;
+	dma->chan[id] = dmac;
+	INIT_LIST_HEAD(&dmac->ingenic_dma_sdesc_list_submitted);
+	INIT_LIST_HEAD(&dmac->ingenic_dma_sdesc_list_issued);
+
+	dma_cookie_init(&dmac->chan);
+	dmac->chan.device = &dma->dma_device;
+	dmac->working_sdesc = NULL;
+	list_add_tail(&dmac->chan.device_node, &dma->dma_device.channels);
+	tasklet_init(&dmac->task, ingenic_dma_complete, (unsigned long)dmac);
+
+	return 0;
+}
+
+static int ingenic_dma_probe(struct platform_device *pdev)
+{
+	struct ingenic_dma_engine *ingenic_dma = NULL;
+	unsigned int reg_dmac = DMAC_DMAE;
+	struct device *dev = &pdev->dev;
+	struct resource *iores;
+	int i, ret = 0;
+
+	if (!dev->of_node) {
+		dev_err(dev, "This driver must be probed from devicetree\n");
+		return -EINVAL;
+	}
+
+	ingenic_dma = devm_kzalloc(&pdev->dev, sizeof(struct ingenic_dma_engine),
+				   GFP_KERNEL);
+	if (!ingenic_dma)
+		return -ENOMEM;
+
+	ingenic_dma->dma_data = (struct ingenic_dma_data *)device_get_match_data(dev);
+	if (!ingenic_dma->dma_data)
+		return -EINVAL;
+
+	/*
+	 * Obtaining parameters from the device tree.
+	 */
+	ingenic_dma->dev = dev;
+	if (!of_property_read_u32(pdev->dev.of_node, "programed-chs",
+				  &ingenic_dma->chan_programed))
+		ingenic_dma->chan_reserved |= ingenic_dma->chan_programed;
+
+	if (HWATTR_SPECIAL_CH01_SUP(ingenic_dma->dma_data->hwattr) &&
+	    of_property_read_bool(pdev->dev.of_node, "special-chs")) {
+		ingenic_dma->chan_reserved  |= DMA_SPECAIL_CHS;
+		ingenic_dma->chan_programed |= DMA_SPECAIL_CHS;
+		ingenic_dma->special_ch = true;
+	}
+
+	ingenic_dma->intc_ch = -1;
+	if (HWATTR_INTC_IRQ_SUP(ingenic_dma->dma_data->hwattr) &&
+	    !of_property_read_u32(pdev->dev.of_node, "intc-ch",
+				  &ingenic_dma->intc_ch)) {
+
+		if (BIT(ingenic_dma->intc_ch) & ingenic_dma->chan_reserved)
+			dev_warn(ingenic_dma->dev,
+				 "intc irq channel %d is already reserved\n",
+				 ingenic_dma->intc_ch);
+
+		ingenic_dma->chan_reserved |= BIT(ingenic_dma->intc_ch);
+	}
+
+	/*
+	 * obtaining the base address of the DMA peripheral.
+	 */
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (iores) {
+		ingenic_dma->iomem = devm_ioremap_resource(&pdev->dev, iores);
+		if (IS_ERR(ingenic_dma->iomem))
+			return PTR_ERR(ingenic_dma->iomem);
+	} else {
+		dev_err(dev, "Failed to get I/O memory\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Get PDMA interrupt.
+	 */
+	ingenic_dma->irq_pdma = platform_get_irq_byname(pdev, "pdma");
+	if (ingenic_dma->irq_pdma < 0) {
+		dev_err(dev, "Unable to get pdma irq\n");
+		return ingenic_dma->irq_pdma;
+	}
+
+	ret = devm_request_irq(&pdev->dev, ingenic_dma->irq_pdma,
+			       pdma_int_handler, 0, "pdma", ingenic_dma);
+	if (ret) {
+		dev_err(dev, "Failed to request pdma irq\n");
+		return ret;
+	}
+
+	/*
+	 * Get PDMA descriptor interrupt.
+	 */
+	if (HWATTR_DESC_INTER_SUP(ingenic_dma->dma_data->hwattr)) {
+		ingenic_dma->irq_pdmad = platform_get_irq_byname(pdev, "pdmad");
+		if (ingenic_dma->irq_pdmad < 0) {
+			dev_err(&pdev->dev, "Unable to get pdmad irq\n");
+			return ingenic_dma->irq_pdmad;
+		}
+		ret = devm_request_irq(&pdev->dev, ingenic_dma->irq_pdmad,
+				       pdmad_int_handler, 0, "pdmad", ingenic_dma);
+		if (ret) {
+			dev_err(dev, "Failed to request pdmad irq\n");
+			return ret;
+		}
+	}
+
+	/*
+	 * Initialize dma channel.
+	 */
+	INIT_LIST_HEAD(&ingenic_dma->dma_device.channels);
+	for (i = 0; i < ingenic_dma->dma_data->nb_channels; i++) {
+		/*reserved one channel for intc interrupt*/
+		if (ingenic_dma->intc_ch == i)
+			continue;
+		ingenic_dma_chan_init(ingenic_dma, i);
+	}
+
+	dma_cap_set(DMA_MEMCPY, ingenic_dma->dma_device.cap_mask);
+	dma_cap_set(DMA_SLAVE, ingenic_dma->dma_device.cap_mask);
+	dma_cap_set(DMA_CYCLIC, ingenic_dma->dma_device.cap_mask);
+
+	ingenic_dma->dma_device.dev = &pdev->dev;
+	ingenic_dma->dma_device.device_alloc_chan_resources = ingenic_dma_alloc_chan_resources;
+	ingenic_dma->dma_device.device_free_chan_resources = ingenic_dma_free_chan_resources;
+	ingenic_dma->dma_device.device_tx_status = ingenic_dma_tx_status;
+	ingenic_dma->dma_device.device_prep_slave_sg = ingenic_dma_prep_slave_sg;
+	ingenic_dma->dma_device.device_prep_dma_cyclic = ingenic_dma_prep_dma_cyclic;
+	ingenic_dma->dma_device.device_prep_dma_memcpy = ingenic_dma_prep_dma_memcpy;
+	ingenic_dma->dma_device.device_config = ingenic_dma_config;
+	ingenic_dma->dma_device.device_terminate_all = ingenic_dma_terminate_all;
+	ingenic_dma->dma_device.device_issue_pending = ingenic_dma_issue_pending;
+	ingenic_dma->dma_device.copy_align = DMAENGINE_ALIGN_4_BYTES;
+	ingenic_dma->dma_device.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+						  BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+						  BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+	ingenic_dma->dma_device.dst_addr_widths = ingenic_dma->dma_device.src_addr_widths;
+	ingenic_dma->dma_device.directions = BIT(DMA_DEV_TO_MEM) |
+					     BIT(DMA_MEM_TO_DEV) |
+					     BIT(DMA_MEM_TO_MEM);
+	ingenic_dma->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
+
+	dma_set_max_seg_size(ingenic_dma->dma_device.dev, DTC_TC_MSK);
+
+	ingenic_dma->gate_clk = devm_clk_get(&pdev->dev, "gate_pdma");
+	if (IS_ERR(ingenic_dma->gate_clk))
+		return PTR_ERR(ingenic_dma->gate_clk);
+
+	ret = dma_async_device_register(&ingenic_dma->dma_device);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to register dma\n");
+		clk_disable(ingenic_dma->gate_clk);
+		return ret;
+	}
+
+	of_ingenic_dma_info.dma_cap = ingenic_dma->dma_device.cap_mask;
+	ret = of_dma_controller_register(pdev->dev.of_node,
+					 of_dma_simple_xlate,
+					 &of_ingenic_dma_info);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to register dma to device tree\n");
+		dma_async_device_unregister(&ingenic_dma->dma_device);
+		clk_disable(ingenic_dma->gate_clk);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, ingenic_dma);
+
+	clk_prepare_enable(ingenic_dma->gate_clk);
+
+	if (ingenic_dma->chan_programed)
+		writel(ingenic_dma->chan_programed, ingenic_dma->iomem + DMACP);
+	if (ingenic_dma->intc_ch >= 0)
+		reg_dmac |= DMAC_INTCE |
+			    ((ingenic_dma->intc_ch << DMAC_INTCC_SFT) & DMAC_INTCC_MSK);
+	if (ingenic_dma->special_ch)
+		reg_dmac |= DMAC_CH01;
+	writel(reg_dmac, ingenic_dma->iomem + DMAC);
+
+	return 0;
+}
+
+static int __maybe_unused ingenic_dma_suspend(struct device *dev)
+{
+	struct ingenic_dma_engine *ingenic_dma = dev_get_drvdata(dev);
+	struct ingenic_dma_chan *dmac;
+	unsigned long flg;
+	int i;
+
+	for (i = 0; i < ingenic_dma->dma_data->nb_channels; i++) {
+		dmac = ingenic_dma->chan[i];
+		spin_lock_irqsave(&dmac->hdesc_lock, flg);
+		if (!list_empty(&dmac->ingenic_dma_sdesc_list_submitted)) {
+			spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+			return -EBUSY;
+		}
+		if (!list_empty(&dmac->ingenic_dma_sdesc_list_issued)) {
+			spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+			return -EBUSY;
+		}
+		spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
+	}
+	clk_disable_unprepare(ingenic_dma->gate_clk);
+
+	return 0;
+}
+
+static int __maybe_unused ingenic_dma_resume(struct device *dev)
+{
+	struct ingenic_dma_engine *ingenic_dma = dev_get_drvdata(dev);
+
+	clk_prepare_enable(ingenic_dma->gate_clk);
+
+	return 0;
+}
+
+static const struct dev_pm_ops ingenic_dma_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(ingenic_dma_suspend, ingenic_dma_resume)
+};
+
+static const struct ingenic_dma_data t33_soc_data = {
+	.nb_channels = 32,
+};
+
+static const struct of_device_id ingenic_dma_dt_match[] = {
+	{ .compatible = "ingenic,t33-pdma",   .data = &t33_soc_data },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ingenic_dma_dt_match);
+
+static struct platform_driver ingenic_dma_driver = {
+	.probe			= ingenic_dma_probe,
+	.driver = {
+		.name		= "ingenic-dma",
+		.of_match_table = ingenic_dma_dt_match,
+		.pm		= &ingenic_dma_dev_pm_ops,
+	},
+};
+
+static int __init ingenic_dma_init(void)
+{
+	return platform_driver_register(&ingenic_dma_driver);
+}
+subsys_initcall(ingenic_dma_init);
+
+static void __exit ingenic_dma_exit(void)
+{
+	platform_driver_unregister(&ingenic_dma_driver);
+}
+module_exit(ingenic_dma_exit);
+
+MODULE_AUTHOR("bin.yao <bin.yao@ingenic.com>");
+MODULE_DESCRIPTION("Ingenic dma driver");
+MODULE_LICENSE("GPL");
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/2] dt-bindings: dma: Convert ingenic-pdma doc to YAML
  2024-03-21  8:02 [PATCH 1/2] dmaengine: ingenic: add Ingenic PDMA controller support bin.yao
@ 2024-03-21  8:02 ` bin.yao
  2024-03-21  8:30   ` Krzysztof Kozlowski
  2024-03-21  9:23   ` Rob Herring
  2024-03-21  8:30 ` [PATCH 1/2] dmaengine: ingenic: add Ingenic PDMA controller support Krzysztof Kozlowski
  2024-03-21  8:37 ` Krzysztof Kozlowski
  2 siblings, 2 replies; 6+ messages in thread
From: bin.yao @ 2024-03-21  8:02 UTC (permalink / raw
  To: vkoul
  Cc: robh+dt, krzysztof.kozlowski+dt, conor+dt, dmaengine, devicetree,
	linux-kernel, 1587636487, bin.yao

From: "bin.yao" <bin.yao@ingenic.com>

Convert the textual documentation for the Ingenic SoCs PDMA Controller
devicetree binding to YAML.

Signed-off-by: bin.yao <bin.yao@ingenic.com>
---
 .../devicetree/bindings/dma/ingenic,pdma.yaml | 67 +++++++++++++++++++
 include/dt-bindings/dma/ingenic-pdma.h        | 45 +++++++++++++
 2 files changed, 112 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/ingenic,pdma.yaml
 create mode 100644 include/dt-bindings/dma/ingenic-pdma.h

diff --git a/Documentation/devicetree/bindings/dma/ingenic,pdma.yaml b/Documentation/devicetree/bindings/dma/ingenic,pdma.yaml
new file mode 100644
index 000000000000..290dbf182a01
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/ingenic,pdma.yaml
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/ingenic,pdma.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ingenic SoCs PDMA Controller
+
+maintainers:
+  - bin.yao <bin.yao@ingenic.com>
+
+allOf:
+  - $ref: dma-controller.yaml#
+
+properties:
+  compatible:
+    enum:
+      - ingenic,t33-pdma
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  interrupts-names:
+    const: pdma
+
+  "#dma-cells":
+    const: 1
+
+  dma-channels:
+    const: 32
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    const: gate_pdma
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupts-names
+  - "#dma-cells"
+  - dma-channels
+  - clocks
+  - clock-names
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/ingenic,jz4780-cgu.h>
+    pdma:dma@13420000 {
+      compatible = "ingenic,t33-pdma";
+      reg = <0x13420000 0x10000>;
+      interrupt-parent = <&intc>;
+      interrupt-names = "pdma";
+      interrupts = <10>;
+      #dma-cells = <0x1>;
+      dma-channels = <0x20>;
+      clocks = <&cgu JZ4780_CLK_PDMA>;
+      clock-names = "gate_pdma";
+    };
+
diff --git a/include/dt-bindings/dma/ingenic-pdma.h b/include/dt-bindings/dma/ingenic-pdma.h
new file mode 100644
index 000000000000..66188d588232
--- /dev/null
+++ b/include/dt-bindings/dma/ingenic-pdma.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2024 Ingenic Semiconductor Co., Ltd.
+ * Author: bin.yao <bin.yao@ingenic.com>
+ */
+
+#ifndef __DT_BINDINGS_INGENIC_PDMA_H__
+#define __DT_BINDINGS_INGENIC_PDMA_H__
+
+/*
+ * Request type numbers for the INGENIC DMA controller.
+ */
+#define INGENIC_DMA_REQ_AIC_LOOP_RX	0x5
+#define INGENIC_DMA_REQ_AIC_TX		0x6
+#define INGENIC_DMA_REQ_AIC_F_RX	0x7
+#define INGENIC_DMA_REQ_AUTO_TX		0x8
+#define INGENIC_DMA_REQ_SADC_RX		0x9
+#define INGENIC_DMA_REQ_UART5_TX	0xa
+#define INGENIC_DMA_REQ_UART5_RX	0xb
+#define INGENIC_DMA_REQ_UART4_TX	0xc
+#define INGENIC_DMA_REQ_UART4_RX	0xd
+#define INGENIC_DMA_REQ_UART3_TX	0xe
+#define INGENIC_DMA_REQ_UART3_RX	0xf
+#define INGENIC_DMA_REQ_UART2_TX	0x10
+#define INGENIC_DMA_REQ_UART2_RX	0x11
+#define INGENIC_DMA_REQ_UART1_TX	0x12
+#define INGENIC_DMA_REQ_UART1_RX	0x13
+#define INGENIC_DMA_REQ_UART0_TX	0x14
+#define INGENIC_DMA_REQ_UART0_RX	0x15
+#define INGENIC_DMA_REQ_SSI0_TX		0x16
+#define INGENIC_DMA_REQ_SSI0_RX		0x17
+#define INGENIC_DMA_REQ_SSI1_TX		0x18
+#define INGENIC_DMA_REQ_SSI1_RX		0x19
+#define INGENIC_DMA_REQ_SLV_TX		0x1a
+#define INGENIC_DMA_REQ_SLV_RX		0x1b
+#define INGENIC_DMA_REQ_I2C0_TX		0x24
+#define INGENIC_DMA_REQ_I2C0_RX		0x25
+#define INGENIC_DMA_REQ_I2C1_TX		0x26
+#define INGENIC_DMA_REQ_I2C1_RX		0x27
+#define INGENIC_DMA_REQ_I2C2_TX		0x28
+#define INGENIC_DMA_REQ_I2C2_RX		0x29
+#define INGENIC_DMA_REQ_DES_TX		0x2e
+#define INGENIC_DMA_REQ_DES_RX		0x2f
+
+#endif /* __DT_BINDINGS_INGENIC_PDMA_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] dmaengine: ingenic: add Ingenic PDMA controller support.
  2024-03-21  8:02 [PATCH 1/2] dmaengine: ingenic: add Ingenic PDMA controller support bin.yao
  2024-03-21  8:02 ` [PATCH 2/2] dt-bindings: dma: Convert ingenic-pdma doc to YAML bin.yao
@ 2024-03-21  8:30 ` Krzysztof Kozlowski
  2024-03-21  8:37 ` Krzysztof Kozlowski
  2 siblings, 0 replies; 6+ messages in thread
From: Krzysztof Kozlowski @ 2024-03-21  8:30 UTC (permalink / raw
  To: bin.yao, vkoul
  Cc: robh+dt, krzysztof.kozlowski+dt, conor+dt, dmaengine, devicetree,
	linux-kernel, 1587636487

On 21/03/2024 09:02, bin.yao@ingenic.com wrote:
> From: "bin.yao" <bin.yao@ingenic.com>
> 
> This module can be found on ingenic victory soc.
> 
> Signed-off-by: bin.yao <bin.yao@ingenic.com>
> ---
>  drivers/dma/Kconfig        |    6 +
>  drivers/dma/Makefile       |    1 +
>  drivers/dma/ingenic-pdma.c | 1356 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 1363 insertions(+)
>  create mode 100644 drivers/dma/ingenic-pdma.c
> 

You did not even compile this code... You don't have ingenic-pdma.h
header at this point.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] dt-bindings: dma: Convert ingenic-pdma doc to YAML
  2024-03-21  8:02 ` [PATCH 2/2] dt-bindings: dma: Convert ingenic-pdma doc to YAML bin.yao
@ 2024-03-21  8:30   ` Krzysztof Kozlowski
  2024-03-21  9:23   ` Rob Herring
  1 sibling, 0 replies; 6+ messages in thread
From: Krzysztof Kozlowski @ 2024-03-21  8:30 UTC (permalink / raw
  To: bin.yao, vkoul
  Cc: robh+dt, krzysztof.kozlowski+dt, conor+dt, dmaengine, devicetree,
	linux-kernel, 1587636487

On 21/03/2024 09:02, bin.yao@ingenic.com wrote:
> From: "bin.yao" <bin.yao@ingenic.com>
> 
> Convert the textual documentation for the Ingenic SoCs PDMA Controller

I don't see any conversion here.

> devicetree binding to YAML.
> 
> Signed-off-by: bin.yao <bin.yao@ingenic.com>

Are you sure this is Latin transliteration of your name? With a dot in
between? Looks like email login...

> ---
>  .../devicetree/bindings/dma/ingenic,pdma.yaml | 67 +++++++++++++++++++
>  include/dt-bindings/dma/ingenic-pdma.h        | 45 +++++++++++++
>  2 files changed, 112 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/dma/ingenic,pdma.yaml
>  create mode 100644 include/dt-bindings/dma/ingenic-pdma.h
> 
> diff --git a/Documentation/devicetree/bindings/dma/ingenic,pdma.yaml b/Documentation/devicetree/bindings/dma/ingenic,pdma.yaml
> new file mode 100644
> index 000000000000..290dbf182a01
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/dma/ingenic,pdma.yaml


> @@ -0,0 +1,67 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/dma/ingenic,pdma.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Ingenic SoCs PDMA Controller
> +
> +maintainers:
> +  - bin.yao <bin.yao@ingenic.com>
> +

What is PDMA? Why this is not DMA? Provide description explaining this.

> +allOf:
> +  - $ref: dma-controller.yaml#
> +
> +properties:
> +  compatible:
> +    enum:
> +      - ingenic,t33-pdma

There is no such soc like t33 so far. Please point me to SoC/board/other
bindings.

> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  interrupts-names:
> +    const: pdma

Drop names, not needed.

> +
> +  "#dma-cells":
> +    const: 1
> +
> +  dma-channels:
> +    const: 32

Drop property, not needed.

> +
> +  clocks:
> +    maxItems: 1
> +
> +  clock-names:
> +    const: gate_pdma

Drop names, not needed.

> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - interrupts-names
> +  - "#dma-cells"
> +  - dma-channels
> +  - clocks
> +  - clock-names
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/clock/ingenic,jz4780-cgu.h>
> +    pdma:dma@13420000 {

Drop label.

Node names should be generic. See also an explanation and list of
examples (not exhaustive) in DT specification:
https://devicetree-specification.readthedocs.io/en/latest/chapter2-devicetree-basics.html#generic-names-recommendation

It does not look like you tested the bindings, at least after quick
look. Please run `make dt_binding_check` (see
Documentation/devicetree/bindings/writing-schema.rst for instructions).
Maybe you need to update your dtschema and yamllint.

> +      compatible = "ingenic,t33-pdma";
> +      reg = <0x13420000 0x10000>;
> +      interrupt-parent = <&intc>;
> +      interrupt-names = "pdma";
> +      interrupts = <10>;
> +      #dma-cells = <0x1>;

That's not hex!

> +      dma-channels = <0x20>;
> +      clocks = <&cgu JZ4780_CLK_PDMA>;
> +      clock-names = "gate_pdma";
> +    };
> +
> diff --git a/include/dt-bindings/dma/ingenic-pdma.h b/include/dt-bindings/dma/ingenic-pdma.h
> new file mode 100644
> index 000000000000..66188d588232
> --- /dev/null
> +++ b/include/dt-bindings/dma/ingenic-pdma.h

Same filename as binding.

> @@ -0,0 +1,45 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
> +/*
> + * Copyright (C) 2024 Ingenic Semiconductor Co., Ltd.
> + * Author: bin.yao <bin.yao@ingenic.com>
> + */
> +
> +#ifndef __DT_BINDINGS_INGENIC_PDMA_H__
> +#define __DT_BINDINGS_INGENIC_PDMA_H__
> +
> +/*
> + * Request type numbers for the INGENIC DMA controller.
> + */
> +#define INGENIC_DMA_REQ_AIC_LOOP_RX	0x5

IDs start from 0, not 5. IDs are decimal, not hex.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] dmaengine: ingenic: add Ingenic PDMA controller support.
  2024-03-21  8:02 [PATCH 1/2] dmaengine: ingenic: add Ingenic PDMA controller support bin.yao
  2024-03-21  8:02 ` [PATCH 2/2] dt-bindings: dma: Convert ingenic-pdma doc to YAML bin.yao
  2024-03-21  8:30 ` [PATCH 1/2] dmaengine: ingenic: add Ingenic PDMA controller support Krzysztof Kozlowski
@ 2024-03-21  8:37 ` Krzysztof Kozlowski
  2 siblings, 0 replies; 6+ messages in thread
From: Krzysztof Kozlowski @ 2024-03-21  8:37 UTC (permalink / raw
  To: bin.yao, vkoul
  Cc: robh+dt, krzysztof.kozlowski+dt, conor+dt, dmaengine, devicetree,
	linux-kernel, 1587636487

On 21/03/2024 09:02, bin.yao@ingenic.com wrote:
> From: "bin.yao" <bin.yao@ingenic.com>
> 
> This module can be found on ingenic victory soc.
> 
> Signed-off-by: bin.yao <bin.yao@ingenic.com>
> ---
>  drivers/dma/Kconfig        |    6 +
>  drivers/dma/Makefile       |    1 +
>  drivers/dma/ingenic-pdma.c | 1356 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 1363 insertions(+)
>  create mode 100644 drivers/dma/ingenic-pdma.c

...

> +static int ingenic_dma_chan_init(struct ingenic_dma_engine *dma, int id)
> +{
> +	struct ingenic_dma_chan *dmac = NULL;
> +
> +	if ((id < 0) || (id >= INGENIC_DMA_CHAN_CNT))
> +		return -EINVAL;
> +
> +	dmac = devm_kzalloc(dma->dev, sizeof(*dmac), GFP_KERNEL);
> +	if (!dmac)
> +		return -ENOMEM;
> +
> +	dmac->id = id;
> +	dmac->iomem = dma->iomem + dmac->id * DMACH_OFF;
> +	dmac->engine = dma;
> +
> +	spin_lock_init(&dmac->hdesc_lock);
> +	init_completion(&dmac->completion);
> +
> +	dmac->slave_id = pdma_maps[id] & INGENIC_DMA_TYPE_REQ_MSK;
> +	dma->chan[id] = dmac;
> +	INIT_LIST_HEAD(&dmac->ingenic_dma_sdesc_list_submitted);
> +	INIT_LIST_HEAD(&dmac->ingenic_dma_sdesc_list_issued);
> +
> +	dma_cookie_init(&dmac->chan);
> +	dmac->chan.device = &dma->dma_device;
> +	dmac->working_sdesc = NULL;
> +	list_add_tail(&dmac->chan.device_node, &dma->dma_device.channels);
> +	tasklet_init(&dmac->task, ingenic_dma_complete, (unsigned long)dmac);
> +
> +	return 0;
> +}
> +
> +static int ingenic_dma_probe(struct platform_device *pdev)
> +{
> +	struct ingenic_dma_engine *ingenic_dma = NULL;
> +	unsigned int reg_dmac = DMAC_DMAE;
> +	struct device *dev = &pdev->dev;
> +	struct resource *iores;
> +	int i, ret = 0;
> +
> +	if (!dev->of_node) {
> +		dev_err(dev, "This driver must be probed from devicetree\n");

This driver cannot be probed other way. Drop.

> +		return -EINVAL;
> +	}
> +
> +	ingenic_dma = devm_kzalloc(&pdev->dev, sizeof(struct ingenic_dma_engine),

ssizeof(*(

> +				   GFP_KERNEL);
> +	if (!ingenic_dma)
> +		return -ENOMEM;
> +
> +	ingenic_dma->dma_data = (struct ingenic_dma_data *)device_get_match_data(dev);

Why the cast?

> +	if (!ingenic_dma->dma_data)
> +		return -EINVAL;
> +
> +	/*
> +	 * Obtaining parameters from the device tree.
> +	 */

Drop obvious comments.

> +	ingenic_dma->dev = dev;
> +	if (!of_property_read_u32(pdev->dev.of_node, "programed-chs",

No, there is no such property. NAK.

Upstream your DTS or I will be NAKing your patches. Because otherwise
you try to sneak a lot of undocumented stuff here.

> +				  &ingenic_dma->chan_programed))
> +		ingenic_dma->chan_reserved |= ingenic_dma->chan_programed;
> +
> +	if (HWATTR_SPECIAL_CH01_SUP(ingenic_dma->dma_data->hwattr) &&
> +	    of_property_read_bool(pdev->dev.of_node, "special-chs")) {
> +		ingenic_dma->chan_reserved  |= DMA_SPECAIL_CHS;
> +		ingenic_dma->chan_programed |= DMA_SPECAIL_CHS;
> +		ingenic_dma->special_ch = true;
> +	}
> +
> +	ingenic_dma->intc_ch = -1;
> +	if (HWATTR_INTC_IRQ_SUP(ingenic_dma->dma_data->hwattr) &&
> +	    !of_property_read_u32(pdev->dev.of_node, "intc-ch",
> +				  &ingenic_dma->intc_ch)) {
> +
> +		if (BIT(ingenic_dma->intc_ch) & ingenic_dma->chan_reserved)
> +			dev_warn(ingenic_dma->dev,
> +				 "intc irq channel %d is already reserved\n",
> +				 ingenic_dma->intc_ch);
> +
> +		ingenic_dma->chan_reserved |= BIT(ingenic_dma->intc_ch);
> +	}
> +
> +	/*
> +	 * obtaining the base address of the DMA peripheral.
> +	 */

Drop obvious comments.

> +	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (iores) {
> +		ingenic_dma->iomem = devm_ioremap_resource(&pdev->dev, iores);

Combine these two, there's helper for this.

> +		if (IS_ERR(ingenic_dma->iomem))
> +			return PTR_ERR(ingenic_dma->iomem);
> +	} else {
> +		dev_err(dev, "Failed to get I/O memory\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Get PDMA interrupt.
> +	 */

Drop

> +	ingenic_dma->irq_pdma = platform_get_irq_byname(pdev, "pdma");
> +	if (ingenic_dma->irq_pdma < 0) {
> +		dev_err(dev, "Unable to get pdma irq\n");
> +		return ingenic_dma->irq_pdma;

Syntax is return dev_err_probe().

> +	}
> +
> +	ret = devm_request_irq(&pdev->dev, ingenic_dma->irq_pdma,
> +			       pdma_int_handler, 0, "pdma", ingenic_dma);
> +	if (ret) {
> +		dev_err(dev, "Failed to request pdma irq\n");
> +		return ret;
> +	}
> +
> +	/*
> +	 * Get PDMA descriptor interrupt.
> +	 */
> +	if (HWATTR_DESC_INTER_SUP(ingenic_dma->dma_data->hwattr)) {
> +		ingenic_dma->irq_pdmad = platform_get_irq_byname(pdev, "pdmad");

NAK, you must be joking. You do not have second interrupt.

You must clean your code before upstream from all downstream weirdness
and incorrectness. You cannot send different binding than driver.

What's more, I don't trust that you will send usable binding, judging by
the driver, so I expect to see upstreamed DTS.

> +		if (ingenic_dma->irq_pdmad < 0) {
> +			dev_err(&pdev->dev, "Unable to get pdmad irq\n");
> +			return ingenic_dma->irq_pdmad;
> +		}
> +		ret = devm_request_irq(&pdev->dev, ingenic_dma->irq_pdmad,
> +				       pdmad_int_handler, 0, "pdmad", ingenic_dma);
> +		if (ret) {
> +			dev_err(dev, "Failed to request pdmad irq\n");
> +			return ret;
> +		}
> +	}
> +
> +	/*
> +	 * Initialize dma channel.
> +	 */
> +	INIT_LIST_HEAD(&ingenic_dma->dma_device.channels);
> +	for (i = 0; i < ingenic_dma->dma_data->nb_channels; i++) {
> +		/*reserved one channel for intc interrupt*/
> +		if (ingenic_dma->intc_ch == i)
> +			continue;
> +		ingenic_dma_chan_init(ingenic_dma, i);
> +	}
> +
> +	dma_cap_set(DMA_MEMCPY, ingenic_dma->dma_device.cap_mask);
> +	dma_cap_set(DMA_SLAVE, ingenic_dma->dma_device.cap_mask);
> +	dma_cap_set(DMA_CYCLIC, ingenic_dma->dma_device.cap_mask);
> +
> +	ingenic_dma->dma_device.dev = &pdev->dev;
> +	ingenic_dma->dma_device.device_alloc_chan_resources = ingenic_dma_alloc_chan_resources;
> +	ingenic_dma->dma_device.device_free_chan_resources = ingenic_dma_free_chan_resources;
> +	ingenic_dma->dma_device.device_tx_status = ingenic_dma_tx_status;
> +	ingenic_dma->dma_device.device_prep_slave_sg = ingenic_dma_prep_slave_sg;
> +	ingenic_dma->dma_device.device_prep_dma_cyclic = ingenic_dma_prep_dma_cyclic;
> +	ingenic_dma->dma_device.device_prep_dma_memcpy = ingenic_dma_prep_dma_memcpy;
> +	ingenic_dma->dma_device.device_config = ingenic_dma_config;
> +	ingenic_dma->dma_device.device_terminate_all = ingenic_dma_terminate_all;
> +	ingenic_dma->dma_device.device_issue_pending = ingenic_dma_issue_pending;
> +	ingenic_dma->dma_device.copy_align = DMAENGINE_ALIGN_4_BYTES;
> +	ingenic_dma->dma_device.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> +						  BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
> +						  BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
> +	ingenic_dma->dma_device.dst_addr_widths = ingenic_dma->dma_device.src_addr_widths;
> +	ingenic_dma->dma_device.directions = BIT(DMA_DEV_TO_MEM) |
> +					     BIT(DMA_MEM_TO_DEV) |
> +					     BIT(DMA_MEM_TO_MEM);
> +	ingenic_dma->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
> +
> +	dma_set_max_seg_size(ingenic_dma->dma_device.dev, DTC_TC_MSK);
> +
> +	ingenic_dma->gate_clk = devm_clk_get(&pdev->dev, "gate_pdma");
> +	if (IS_ERR(ingenic_dma->gate_clk))
> +		return PTR_ERR(ingenic_dma->gate_clk);
> +
> +	ret = dma_async_device_register(&ingenic_dma->dma_device);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register dma\n");
> +		clk_disable(ingenic_dma->gate_clk);
> +		return ret;
> +	}
> +
> +	of_ingenic_dma_info.dma_cap = ingenic_dma->dma_device.cap_mask;
> +	ret = of_dma_controller_register(pdev->dev.of_node,
> +					 of_dma_simple_xlate,
> +					 &of_ingenic_dma_info);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register dma to device tree\n");
> +		dma_async_device_unregister(&ingenic_dma->dma_device);
> +		clk_disable(ingenic_dma->gate_clk);
> +		return ret;
> +	}
> +
> +	platform_set_drvdata(pdev, ingenic_dma);
> +
> +	clk_prepare_enable(ingenic_dma->gate_clk);
> +
> +	if (ingenic_dma->chan_programed)
> +		writel(ingenic_dma->chan_programed, ingenic_dma->iomem + DMACP);
> +	if (ingenic_dma->intc_ch >= 0)
> +		reg_dmac |= DMAC_INTCE |
> +			    ((ingenic_dma->intc_ch << DMAC_INTCC_SFT) & DMAC_INTCC_MSK);
> +	if (ingenic_dma->special_ch)
> +		reg_dmac |= DMAC_CH01;
> +	writel(reg_dmac, ingenic_dma->iomem + DMAC);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused ingenic_dma_suspend(struct device *dev)
> +{
> +	struct ingenic_dma_engine *ingenic_dma = dev_get_drvdata(dev);
> +	struct ingenic_dma_chan *dmac;
> +	unsigned long flg;
> +	int i;
> +
> +	for (i = 0; i < ingenic_dma->dma_data->nb_channels; i++) {
> +		dmac = ingenic_dma->chan[i];
> +		spin_lock_irqsave(&dmac->hdesc_lock, flg);
> +		if (!list_empty(&dmac->ingenic_dma_sdesc_list_submitted)) {
> +			spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
> +			return -EBUSY;
> +		}
> +		if (!list_empty(&dmac->ingenic_dma_sdesc_list_issued)) {
> +			spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
> +			return -EBUSY;
> +		}
> +		spin_unlock_irqrestore(&dmac->hdesc_lock, flg);
> +	}
> +	clk_disable_unprepare(ingenic_dma->gate_clk);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused ingenic_dma_resume(struct device *dev)
> +{
> +	struct ingenic_dma_engine *ingenic_dma = dev_get_drvdata(dev);
> +
> +	clk_prepare_enable(ingenic_dma->gate_clk);
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops ingenic_dma_dev_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(ingenic_dma_suspend, ingenic_dma_resume)
> +};
> +
> +static const struct ingenic_dma_data t33_soc_data = {
> +	.nb_channels = 32,
> +};
> +
> +static const struct of_device_id ingenic_dma_dt_match[] = {
> +	{ .compatible = "ingenic,t33-pdma",   .data = &t33_soc_data },

Don't merge lines. Open existing code and look how it is done there.

> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, ingenic_dma_dt_match);
> +
> +static struct platform_driver ingenic_dma_driver = {
> +	.probe			= ingenic_dma_probe,
> +	.driver = {
> +		.name		= "ingenic-dma",
> +		.of_match_table = ingenic_dma_dt_match,
> +		.pm		= &ingenic_dma_dev_pm_ops,
> +	},
> +};
> +
> +static int __init ingenic_dma_init(void)
> +{
> +	return platform_driver_register(&ingenic_dma_driver);
> +}
> +subsys_initcall(ingenic_dma_init);
> +
> +static void __exit ingenic_dma_exit(void)
> +{
> +	platform_driver_unregister(&ingenic_dma_driver);
> +}
> +module_exit(ingenic_dma_exit);
> +
> +MODULE_AUTHOR("bin.yao <bin.yao@ingenic.com>");
> +MODULE_DESCRIPTION("Ingenic dma driver");

Why do you add second Ingenic dma driver? That's duplication.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] dt-bindings: dma: Convert ingenic-pdma doc to YAML
  2024-03-21  8:02 ` [PATCH 2/2] dt-bindings: dma: Convert ingenic-pdma doc to YAML bin.yao
  2024-03-21  8:30   ` Krzysztof Kozlowski
@ 2024-03-21  9:23   ` Rob Herring
  1 sibling, 0 replies; 6+ messages in thread
From: Rob Herring @ 2024-03-21  9:23 UTC (permalink / raw
  To: bin.yao
  Cc: krzysztof.kozlowski+dt, devicetree, dmaengine, conor+dt, robh+dt,
	linux-kernel, vkoul, 1587636487


On Thu, 21 Mar 2024 16:02:28 +0800, bin.yao@ingenic.com wrote:
> From: "bin.yao" <bin.yao@ingenic.com>
> 
> Convert the textual documentation for the Ingenic SoCs PDMA Controller
> devicetree binding to YAML.
> 
> Signed-off-by: bin.yao <bin.yao@ingenic.com>
> ---
>  .../devicetree/bindings/dma/ingenic,pdma.yaml | 67 +++++++++++++++++++
>  include/dt-bindings/dma/ingenic-pdma.h        | 45 +++++++++++++
>  2 files changed, 112 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/dma/ingenic,pdma.yaml
>  create mode 100644 include/dt-bindings/dma/ingenic-pdma.h
> 

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/dma/ingenic,pdma.example.dtb: dma@13420000: $nodename:0: 'dma@13420000' does not match '^dma-controller(@.*)?$'
	from schema $id: http://devicetree.org/schemas/dma/ingenic,pdma.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/dma/ingenic,pdma.example.dtb: dma@13420000: 'interrupts-names' is a required property
	from schema $id: http://devicetree.org/schemas/dma/ingenic,pdma.yaml#
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/dma/ingenic,pdma.example.dtb: dma@13420000: Unevaluated properties are not allowed ('interrupt-names' was unexpected)
	from schema $id: http://devicetree.org/schemas/dma/ingenic,pdma.yaml#

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20240321080228.24147-2-bin.yao@ingenic.com

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-03-21  9:23 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-03-21  8:02 [PATCH 1/2] dmaengine: ingenic: add Ingenic PDMA controller support bin.yao
2024-03-21  8:02 ` [PATCH 2/2] dt-bindings: dma: Convert ingenic-pdma doc to YAML bin.yao
2024-03-21  8:30   ` Krzysztof Kozlowski
2024-03-21  9:23   ` Rob Herring
2024-03-21  8:30 ` [PATCH 1/2] dmaengine: ingenic: add Ingenic PDMA controller support Krzysztof Kozlowski
2024-03-21  8:37 ` Krzysztof Kozlowski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.