Skip to contentSkip to conversation

Add some fixes and new feature for SPRD DMA

PATCH RESEND Applied Baolin Wang 2 participants 10 messages Nov 6, 2018 – Dec 5, 2018

Subsystems (2)
lore
Share & tools
lore
#1 lore raw

PATCH RESEND 1/7 dmaengine: sprd: Remove direction usage from struct dma_slave_config

In reply to Baolin Wang

The direction field of struct dma_slave_config was marked deprecated, thus remove the usage.

---
 drivers/dma/sprd-dma.c |    3 ---
 1 file changed, 3 deletions(-)
drivers/dma/sprd-dma.c +0 -3
index 38d4e4f..c226dc93 100644
@@ -847,9 +847,6 @@ static int sprd_dma_slave_config(struct dma_chan *chan,
 	struct sprd_dma_chn *schan = to_sprd_dma_chan(chan);
 	struct dma_slave_config *slave_cfg = &schan->slave_cfg;
 
-	if (!is_slave_direction(config->direction))
-		return -EINVAL;
-
 	memcpy(slave_cfg, config, sizeof(*config));
 	return 0;
 }
#2 lore raw

PATCH RESEND 2/7 dmaengine: sprd: Get transfer residue depending on the transfer direction

In reply to Baolin Wang

From: Eric Long <[email protected]>

Add one field to save the transfer direction for struct sprd_dma_desc, which is used to get correct transfer residue depending on the transfer direction.

[Baolin Wang adds one field to present the transfer direction]
---
 drivers/dma/sprd-dma.c |   21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)
drivers/dma/sprd-dma.c +20 -1
index c226dc93..4f3587b 100644
@@ -159,6 +159,7 @@ struct sprd_dma_chn_hw {
 struct sprd_dma_desc {
 	struct virt_dma_desc	vd;
 	struct sprd_dma_chn_hw	chn_hw;
+	enum dma_transfer_direction dir;
 };
 
 /* dma channel description */
@@ -331,6 +332,17 @@ static void sprd_dma_stop_and_disable(struct sprd_dma_chn *schan)
 	sprd_dma_disable_chn(schan);
 }
 
+static unsigned long sprd_dma_get_src_addr(struct sprd_dma_chn *schan)
+{
+	unsigned long addr, addr_high;
+
+	addr = readl(schan->chn_base + SPRD_DMA_CHN_SRC_ADDR);
+	addr_high = readl(schan->chn_base + SPRD_DMA_CHN_WARP_PTR) &
+		    SPRD_DMA_HIGH_ADDR_MASK;
+
+	return addr | (addr_high << SPRD_DMA_HIGH_ADDR_OFFSET);
+}
+
 static unsigned long sprd_dma_get_dst_addr(struct sprd_dma_chn *schan)
 {
 	unsigned long addr, addr_high;
@@ -534,7 +546,12 @@ static enum dma_status sprd_dma_tx_status(struct dma_chan *chan,
 		else
 			pos = 0;
 	} else if (schan->cur_desc && schan->cur_desc->vd.tx.cookie == cookie) {
-		pos = sprd_dma_get_dst_addr(schan);
+		struct sprd_dma_desc *sdesc = to_sprd_dma_desc(vd);
+
+		if (sdesc->dir == DMA_DEV_TO_MEM)
+			pos = sprd_dma_get_dst_addr(schan);
+		else
+			pos = sprd_dma_get_src_addr(schan);
 	} else {
 		pos = 0;
 	}
@@ -804,6 +821,8 @@ static int sprd_dma_fill_linklist_desc(struct dma_chan *chan,
 	if (!sdesc)
 		return NULL;
 
+	sdesc->dir = dir;
+
 	for_each_sg(sgl, sg, sglen, i) {
 		len = sg_dma_len(sg);
 
#3 lore raw

PATCH RESEND 3/7 dmaengine: sprd: Fix the last link-list configuration

In reply to Baolin Wang

From: Eric Long <[email protected]>

We will pass sglen as 0 configure the last link-list configuration when filling the descriptor, which will cause the incorrect link-list configuration. Thus we should check if the sglen is 0 to configure the correct link-list configuration.

---
 drivers/dma/sprd-dma.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/dma/sprd-dma.c +2 -1
index 4f3587b..e6a74dc 100644
@@ -697,7 +697,8 @@ static int sprd_dma_fill_desc(struct dma_chan *chan,
 		hw->cfg |= SPRD_DMA_LINKLIST_EN;
 
 		/* link-list index */
-		temp = (sg_index + 1) % sglen;
+		temp = sglen ? (sg_index + 1) % sglen : 0;
+
 		/* Next link-list configuration's physical address offset */
 		temp = temp * sizeof(*hw) + SPRD_DMA_CHN_SRC_ADDR;
 		/*
#4 lore raw

PATCH RESEND 4/7 dmaengine: sprd: Set cur_desc as NULL when free or terminate one dma channel

In reply to Baolin Wang

From: Eric Long <[email protected]>

It will be failed to start one new transfer if the channel started one none interrupt transfer before, since we will only set the schan->cur_desc as NULL depending on the transfer interrupt now. Thus we should set schan->cur_desc as NULL when free or terminate one dma channel to avoid this issue.

---
 drivers/dma/sprd-dma.c |    1 +
 1 file changed, 1 insertion(+)
drivers/dma/sprd-dma.c +1 -0
index e6a74dc..1b39661 100644
@@ -439,6 +439,7 @@ static void sprd_dma_stop(struct sprd_dma_chn *schan)
 	sprd_dma_stop_and_disable(schan);
 	sprd_dma_unset_uid(schan);
 	sprd_dma_clear_int(schan);
+	schan->cur_desc = NULL;
 }
 
 static bool sprd_dma_check_trans_done(struct sprd_dma_desc *sdesc,
#5 lore raw

PATCH RESEND 5/7 dmaengine: sprd: Support DMA link-list cyclic callback

In reply to Baolin Wang

From: Eric Long <[email protected]>

The Spreadtrum DMA link-list mode is always one cyclic transfer, so we should clear the SPRD_DMA_LLIST_END flag for the link-list configuration. Moreover add cyclic callback support for the cyclic transfer.

---
 drivers/dma/sprd-dma.c |   25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)
drivers/dma/sprd-dma.c +14 -11
index 1b39661..cefe42f 100644
@@ -463,7 +463,7 @@ static irqreturn_t dma_irq_handle(int irq, void *dev_id)
 	struct sprd_dma_desc *sdesc;
 	enum sprd_dma_req_mode req_type;
 	enum sprd_dma_int_type int_type;
-	bool trans_done = false;
+	bool trans_done = false, cyclic = false;
 	u32 i;
 
 	while (irq_status) {
@@ -478,13 +478,19 @@ static irqreturn_t dma_irq_handle(int irq, void *dev_id)
 
 		sdesc = schan->cur_desc;
 
-		/* Check if the dma request descriptor is done. */
-		trans_done = sprd_dma_check_trans_done(sdesc, int_type,
-						       req_type);
-		if (trans_done == true) {
-			vchan_cookie_complete(&sdesc->vd);
-			schan->cur_desc = NULL;
-			sprd_dma_start(schan);
+		/* cyclic mode schedule callback */
+		cyclic = schan->linklist.phy_addr ? true : false;
+		if (cyclic == true) {
+			vchan_cyclic_callback(&sdesc->vd);
+		} else {
+			/* Check if the dma request descriptor is done. */
+			trans_done = sprd_dma_check_trans_done(sdesc, int_type,
+							       req_type);
+			if (trans_done == true) {
+				vchan_cookie_complete(&sdesc->vd);
+				schan->cur_desc = NULL;
+				sprd_dma_start(schan);
+			}
 		}
 		spin_unlock(&schan->vc.lock);
 	}
@@ -692,9 +698,6 @@ static int sprd_dma_fill_desc(struct dma_chan *chan,
 
 	/* link-list configuration */
 	if (schan->linklist.phy_addr) {
-		if (sg_index == sglen - 1)
-			hw->frg_len |= SPRD_DMA_LLIST_END;
-
 		hw->cfg |= SPRD_DMA_LINKLIST_EN;
 
 		/* link-list index */
#6 lore raw

PATCH RESEND 6/7 dmaengine: sprd: Support DMA 2-stage transfer mode

In reply to Baolin Wang

From: Eric Long <[email protected]>

The Spreadtrum DMA controller supports channel 2-stage tansfer mode, that means we can request 2 dma channels, one for source channel, and another one for destination channel. Once the source channel's transaction is done, it will trigger the destination channel's transaction automatically by hardware signal.

---
 drivers/dma/sprd-dma.c       |   98 +++++++++++++++++++++++++++++++++++++++++-
 include/linux/dma/sprd-dma.h |   62 ++++++++++++++++++++++++--
 2 files changed, 156 insertions(+), 4 deletions(-)
drivers/dma/sprd-dma.c +97 -1
index cefe42f..50d6569 100644
@@ -36,6 +36,8 @@
 #define SPRD_DMA_GLB_CHN_EN_STS		0x1c
 #define SPRD_DMA_GLB_DEBUG_STS		0x20
 #define SPRD_DMA_GLB_ARB_SEL_STS	0x24
+#define SPRD_DMA_GLB_2STAGE_GRP1	0x28
+#define SPRD_DMA_GLB_2STAGE_GRP2	0x2c
 #define SPRD_DMA_GLB_REQ_UID(uid)	(0x4 * ((uid) - 1))
 #define SPRD_DMA_GLB_REQ_UID_OFFSET	0x2000
 
@@ -57,6 +59,18 @@
 #define SPRD_DMA_CHN_SRC_BLK_STEP	0x38
 #define SPRD_DMA_CHN_DES_BLK_STEP	0x3c
 
+/* SPRD_DMA_GLB_2STAGE_GRP register definition */
+#define SPRD_DMA_GLB_2STAGE_EN		BIT(24)
+#define SPRD_DMA_GLB_CHN_INT_MASK	GENMASK(23, 20)
+#define SPRD_DMA_GLB_LIST_DONE_TRG	BIT(19)
+#define SPRD_DMA_GLB_TRANS_DONE_TRG	BIT(18)
+#define SPRD_DMA_GLB_BLOCK_DONE_TRG	BIT(17)
+#define SPRD_DMA_GLB_FRAG_DONE_TRG	BIT(16)
+#define SPRD_DMA_GLB_TRG_OFFSET		16
+#define SPRD_DMA_GLB_DEST_CHN_MASK	GENMASK(13, 8)
+#define SPRD_DMA_GLB_DEST_CHN_OFFSET	8
+#define SPRD_DMA_GLB_SRC_CHN_MASK	GENMASK(5, 0)
+
 /* SPRD_DMA_CHN_INTC register definition */
 #define SPRD_DMA_INT_MASK		GENMASK(4, 0)
 #define SPRD_DMA_INT_CLR_OFFSET		24
@@ -118,6 +132,10 @@
 #define SPRD_DMA_SRC_TRSF_STEP_OFFSET	0
 #define SPRD_DMA_TRSF_STEP_MASK		GENMASK(15, 0)
 
+/* define DMA channel mode & trigger mode mask */
+#define SPRD_DMA_CHN_MODE_MASK		GENMASK(7, 0)
+#define SPRD_DMA_TRG_MODE_MASK		GENMASK(7, 0)
+
 /* define the DMA transfer step type */
 #define SPRD_DMA_NONE_STEP		0
 #define SPRD_DMA_BYTE_STEP		1
@@ -170,6 +188,8 @@ struct sprd_dma_chn {
 	struct dma_slave_config	slave_cfg;
 	u32			chn_num;
 	u32			dev_id;
+	enum sprd_dma_chn_mode	chn_mode;
+	enum sprd_dma_trg_mode	trg_mode;
 	struct sprd_dma_desc	*cur_desc;
 };
 
@@ -206,6 +226,16 @@ static inline struct sprd_dma_desc *to_sprd_dma_desc(struct virt_dma_desc *vd)
 	return container_of(vd, struct sprd_dma_desc, vd);
 }
 
+static void sprd_dma_glb_update(struct sprd_dma_dev *sdev, u32 reg,
+				u32 mask, u32 val)
+{
+	u32 orig = readl(sdev->glb_base + reg);
+	u32 tmp;
+
+	tmp = (orig & ~mask) | val;
+	writel(tmp, sdev->glb_base + reg);
+}
+
 static void sprd_dma_chn_update(struct sprd_dma_chn *schan, u32 reg,
 				u32 mask, u32 val)
 {
@@ -389,6 +419,49 @@ static enum sprd_dma_req_mode sprd_dma_get_req_type(struct sprd_dma_chn *schan)
 	return (frag_reg >> SPRD_DMA_REQ_MODE_OFFSET) & SPRD_DMA_REQ_MODE_MASK;
 }
 
+static int sprd_dma_set_2stage_config(struct sprd_dma_chn *schan)
+{
+	struct sprd_dma_dev *sdev = to_sprd_dma_dev(&schan->vc.chan);
+	u32 val, chn = schan->chn_num + 1;
+
+	switch (schan->chn_mode) {
+	case SPRD_DMA_SRC_CHN0:
+		val = chn & SPRD_DMA_GLB_SRC_CHN_MASK;
+		val |= BIT(schan->trg_mode - 1) << SPRD_DMA_GLB_TRG_OFFSET;
+		val |= SPRD_DMA_GLB_2STAGE_EN;
+		sprd_dma_glb_update(sdev, SPRD_DMA_GLB_2STAGE_GRP1, val, val);
+		break;
+
+	case SPRD_DMA_SRC_CHN1:
+		val = chn & SPRD_DMA_GLB_SRC_CHN_MASK;
+		val |= BIT(schan->trg_mode - 1) << SPRD_DMA_GLB_TRG_OFFSET;
+		val |= SPRD_DMA_GLB_2STAGE_EN;
+		sprd_dma_glb_update(sdev, SPRD_DMA_GLB_2STAGE_GRP2, val, val);
+		break;
+
+	case SPRD_DMA_DST_CHN0:
+		val = (chn << SPRD_DMA_GLB_DEST_CHN_OFFSET) &
+			SPRD_DMA_GLB_DEST_CHN_MASK;
+		val |= SPRD_DMA_GLB_2STAGE_EN;
+		sprd_dma_glb_update(sdev, SPRD_DMA_GLB_2STAGE_GRP1, val, val);
+		break;
+
+	case SPRD_DMA_DST_CHN1:
+		val = (chn << SPRD_DMA_GLB_DEST_CHN_OFFSET) &
+			SPRD_DMA_GLB_DEST_CHN_MASK;
+		val |= SPRD_DMA_GLB_2STAGE_EN;
+		sprd_dma_glb_update(sdev, SPRD_DMA_GLB_2STAGE_GRP2, val, val);
+		break;
+
+	default:
+		dev_err(sdev->dma_dev.dev, "invalid channel mode setting %d\n",
+			schan->chn_mode);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static void sprd_dma_set_chn_config(struct sprd_dma_chn *schan,
 				    struct sprd_dma_desc *sdesc)
 {
@@ -423,6 +496,13 @@ static void sprd_dma_start(struct sprd_dma_chn *schan)
 	schan->cur_desc = to_sprd_dma_desc(vd);
 
 	/*
+	 * Set 2-stage configuration if the channel starts one 2-stage
+	 * transfer.
+	 */
+	if (schan->chn_mode && sprd_dma_set_2stage_config(schan))
+		return;
+
+	/*
 	 * Copy the DMA configuration from DMA descriptor to this hardware
 	 * channel.
 	 */
@@ -617,6 +697,7 @@ static int sprd_dma_fill_desc(struct dma_chan *chan,
 {
 	struct sprd_dma_dev *sdev = to_sprd_dma_dev(chan);
 	struct sprd_dma_chn *schan = to_sprd_dma_chan(chan);
+	enum sprd_dma_chn_mode chn_mode = schan->chn_mode;
 	u32 req_mode = (flags >> SPRD_DMA_REQ_SHIFT) & SPRD_DMA_REQ_MODE_MASK;
 	u32 int_mode = flags & SPRD_DMA_INT_MASK;
 	int src_datawidth, dst_datawidth, src_step, dst_step;
@@ -628,7 +709,16 @@ static int sprd_dma_fill_desc(struct dma_chan *chan,
 			dev_err(sdev->dma_dev.dev, "invalid source step\n");
 			return src_step;
 		}
-		dst_step = SPRD_DMA_NONE_STEP;
+
+		/*
+		 * For 2-stage transfer, destination channel step can not be 0,
+		 * since destination device is AON IRAM.
+		 */
+		if (chn_mode == SPRD_DMA_DST_CHN0 ||
+		    chn_mode == SPRD_DMA_DST_CHN1)
+			dst_step = src_step;
+		else
+			dst_step = SPRD_DMA_NONE_STEP;
 	} else {
 		dst_step = sprd_dma_get_step(slave_cfg->dst_addr_width);
 		if (dst_step < 0) {
@@ -855,6 +945,12 @@ static int sprd_dma_fill_linklist_desc(struct dma_chan *chan,
 		}
 	}
 
+	/* Set channel mode and trigger mode for 2-stage transfer */
+	schan->chn_mode =
+		(flags >> SPRD_DMA_CHN_MODE_SHIFT) & SPRD_DMA_CHN_MODE_MASK;
+	schan->trg_mode =
+		(flags >> SPRD_DMA_TRG_MODE_SHIFT) & SPRD_DMA_TRG_MODE_MASK;
+
 	ret = sprd_dma_fill_desc(chan, &sdesc->chn_hw, 0, 0, src, dst, len,
 				 dir, flags, slave_cfg);
 	if (ret) {
include/linux/dma/sprd-dma.h +59 -3

68 lines not on this page · raw message

#7 lore raw

PATCH RESEND 7/7 dmaengine: sprd: Add me as one of the module authors

In reply to Baolin Wang

From: Eric Long <[email protected]>

Add me as one of the module authors.

---
 drivers/dma/sprd-dma.c |    1 +
 1 file changed, 1 insertion(+)
drivers/dma/sprd-dma.c +1 -0
index 50d6569..e2f0167 100644
@@ -1226,4 +1226,5 @@ static int __maybe_unused sprd_dma_runtime_resume(struct device *dev)
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("DMA driver for Spreadtrum");
 MODULE_AUTHOR("Baolin Wang <[email protected]>");
+MODULE_AUTHOR("Eric Long <[email protected]>");
 MODULE_ALIAS("platform:sprd-dma");
#0 lore raw

cover Add some fixes and new feature for SPRD DMA

This patchset removes the direction usage from struct dma_slave_config, and add one new field to save the direction. It also fixes some issues for link-list transfer. Moreover this patchset adds new 2-stage transfer support for our DMA.

Baolin Wang (1):
dmaengine: sprd: Remove direction usage from struct dma_slave_config

Eric Long (6):
dmaengine: sprd: Get transfer residue depending on the transfer
direction
dmaengine: sprd: Fix the last link-list configuration
dmaengine: sprd: Set cur_desc as NULL when free or terminate one dma
channel
dmaengine: sprd: Support DMA link-list cyclic callback
dmaengine: sprd: Support DMA 2-stage transfer mode
dmaengine: sprd: Add me as one of the module authors

 drivers/dma/sprd-dma.c       |  152 +++++++++++++++++++++++++++++++++++++-----
 include/linux/dma/sprd-dma.h |   62 ++++++++++++++++-
 2 files changed, 194 insertions(+), 20 deletions(-)
Signature · 1 line
1.7.9.5
#8 lore raw

Hi Vinod,

Quoted 5 lines · Baolin Wang

This patchset removes the direction usage from struct dma_slave_config, and add one new field to save the direction. It also fixes some issues for link-list transfer. Moreover this patchset adds new 2-stage transfer support for our DMA.

Any comments for this patch set? Thanks.

Quoted 14 lines

Baolin Wang (1):
dmaengine: sprd: Remove direction usage from struct dma_slave_config

Eric Long (6):
dmaengine: sprd: Get transfer residue depending on the transfer
direction
dmaengine: sprd: Fix the last link-list configuration
dmaengine: sprd: Set cur_desc as NULL when free or terminate one dma
channel
dmaengine: sprd: Support DMA link-list cyclic callback
dmaengine: sprd: Support DMA 2-stage transfer mode
dmaengine: sprd: Add me as one of the module authors

Quoted 7 lines
 drivers/dma/sprd-dma.c       |  152 +++++++++++++++++++++++++++++++++++++-----
 include/linux/dma/sprd-dma.h |   62 ++++++++++++++++-
 2 files changed, 194 insertions(+), 20 deletions(-)

--
1.7.9.5

--
Baolin Wang
Best Regards

maintainer Applied
#9 lore raw
In reply to Baolin Wang
Quoted 4 lines · Baolin Wang

This patchset removes the direction usage from struct dma_slave_config, and add one new field to save the direction. It also fixes some issues for link-list transfer. Moreover this patchset adds new 2-stage transfer support for our DMA.

Applied, thanks

Signature · 1 line
~Vinod