[PATCH 2/8] ARM i.MX dma-mx1-mx2: use wrapper

Sascha Hauer s.hauer at pengutronix.de
Mon Aug 9 05:05:37 EDT 2010


This patch implements the new DMA API for i.MX1/21/27. The
old APIP is kept in the tree until all users have switched
to the new API.

Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 arch/arm/mach-imx/dma-v1.c                   |  142 +++++++++++++++++++++++---
 arch/arm/mach-imx/include/mach/dma-mx1-mx2.h |    2 +-
 2 files changed, 128 insertions(+), 16 deletions(-)

diff --git a/arch/arm/mach-imx/dma-v1.c b/arch/arm/mach-imx/dma-v1.c
index 3e8c47c..d141458 100644
--- a/arch/arm/mach-imx/dma-v1.c
+++ b/arch/arm/mach-imx/dma-v1.c
@@ -35,6 +35,7 @@
 #include <asm/irq.h>
 #include <mach/hardware.h>
 #include <mach/dma-v1.h>
+#include <mach/dma.h>
 
 #define DMA_DCR     0x00		/* Control Register */
 #define DMA_DISR    0x04		/* Interrupt status Register */
@@ -112,6 +113,7 @@ struct imx_dma_channel {
 	void (*irq_handler) (int, void *);
 	void (*err_handler) (int, void *, int errcode);
 	void (*prog_handler) (int, void *, struct scatterlist *);
+	void (*completion_handler) (int channel, void *data, int error);
 	void *data;
 	unsigned int dma_mode;
 	struct scatterlist *sg;
@@ -126,6 +128,11 @@ struct imx_dma_channel {
 	struct timer_list watchdog;
 
 	int hw_chaining;
+
+	dma_addr_t dev_addr;
+
+	int sgcount;
+	int sgcur;
 };
 
 static void __iomem *imx_dmav1_baseaddr;
@@ -308,6 +315,8 @@ imx_dma_setup_sg(int channel,
 	imxdma->sg = sg;
 	imxdma->dma_mode = dmamode;
 	imxdma->resbytes = dma_length;
+	imxdma->sgcount = sgcount;
+	imxdma->sgcur = 0;
 
 	if (!sg || !sgcount) {
 		printk(KERN_ERR "imxdma%d: imx_dma_setup_sg empty sg list\n",
@@ -341,8 +350,6 @@ imx_dma_setup_sg(int channel,
 		return -EINVAL;
 	}
 
-	imx_dma_sg_next(channel, sg);
-
 	return 0;
 }
 EXPORT_SYMBOL(imx_dma_setup_sg);
@@ -470,6 +477,9 @@ void imx_dma_enable(int channel)
 
 	local_irq_save(flags);
 
+	if (imxdma->sg)
+		imx_dma_sg_next(channel, imxdma->sg + imxdma->sgcur);
+
 	imx_dmav1_writel(1 << channel, DMA_DISR);
 	imx_dmav1_writel(imx_dmav1_readl(DMA_DIMR) & ~(1 << channel), DMA_DIMR);
 	imx_dmav1_writel(imx_dmav1_readl(DMA_CCR(channel)) | CCR_CEN |
@@ -478,10 +488,14 @@ void imx_dma_enable(int channel)
 #ifdef CONFIG_ARCH_MX2
 	if ((cpu_is_mx21() || cpu_is_mx27()) &&
 			imxdma->sg && imx_dma_hw_chain(imxdma)) {
-		imxdma->sg = sg_next(imxdma->sg);
-		if (imxdma->sg) {
+		imxdma->sgcur++;
+
+		if (imxdma->resbytes == IMX_DMA_LENGTH_LOOP)
+			imxdma->sgcur %= imxdma->sgcount;
+
+		if (imxdma->sgcur < imxdma->sgcount) {
 			u32 tmp;
-			imx_dma_sg_next(channel, imxdma->sg);
+			imx_dma_sg_next(channel, imxdma->sg + imxdma->sgcur);
 			tmp = imx_dmav1_readl(DMA_CCR(channel));
 			imx_dmav1_writel(tmp | CCR_RPT | CCR_ACRPT,
 				DMA_CCR(channel));
@@ -503,7 +517,7 @@ void imx_dma_disable(int channel)
 	struct imx_dma_channel *imxdma = &imx_dma_channels[channel];
 	unsigned long flags;
 
-	pr_debug("imxdma%d: imx_dma_disable\n", channel);
+	pr_info("imxdma%d: imx_dma_disable\n", channel);
 
 	if (imx_dma_hw_chain(imxdma))
 		del_timer(&imxdma->watchdog);
@@ -522,13 +536,13 @@ EXPORT_SYMBOL(imx_dma_disable);
 static void imx_dma_watchdog(unsigned long chno)
 {
 	struct imx_dma_channel *imxdma = &imx_dma_channels[chno];
+	u32 tmp;
 
-	imx_dmav1_writel(0, DMA_CCR(chno));
-	imxdma->in_use = 0;
-	imxdma->sg = NULL;
+	mod_timer(&imxdma->watchdog, jiffies + msecs_to_jiffies(500));
 
-	if (imxdma->err_handler)
-		imxdma->err_handler(chno, imxdma->data, IMX_DMA_ERR_TIMEOUT);
+	tmp = imx_dmav1_readl(DMA_CCR(chno));
+	imx_dmav1_writel(0, DMA_CCR(chno));
+	imx_dmav1_writel(tmp, DMA_CCR(chno));
 }
 #endif
 
@@ -607,11 +621,14 @@ static void dma_irq_handle_channel(int chno)
 
 	if (imxdma->sg) {
 		u32 tmp;
-		struct scatterlist *current_sg = imxdma->sg;
-		imxdma->sg = sg_next(imxdma->sg);
+		struct scatterlist *current_sg = imxdma->sg + imxdma->sgcur;
 
-		if (imxdma->sg) {
-			imx_dma_sg_next(chno, imxdma->sg);
+		imxdma->sgcur++;
+		if (imxdma->resbytes == IMX_DMA_LENGTH_LOOP)
+			imxdma->sgcur %= imxdma->sgcount;
+
+		if (imxdma->sgcur < imxdma->sgcount) {
+			imx_dma_sg_next(chno, imxdma->sg + imxdma->sgcur);
 
 			tmp = imx_dmav1_readl(DMA_CCR(chno));
 
@@ -798,6 +815,99 @@ int imx_dma_request_by_prio(const char *name, enum imx_dma_prio prio)
 }
 EXPORT_SYMBOL(imx_dma_request_by_prio);
 
+static void __irq_handler(int channel, void *data)
+{
+	struct imx_dma_channel *imxdma = &imx_dma_channels[channel];
+
+	imxdma->completion_handler(channel, data, 0);
+}
+
+static void __prog_handler(int channel, void *data, struct scatterlist *unused)
+{
+	struct imx_dma_channel *imxdma = &imx_dma_channels[channel];
+
+	imxdma->completion_handler(channel, data, 0);
+}
+
+static int config_channel(int channel, struct imx_dma_config *cfg)
+{
+	struct imx_dma_channel *imxdma = &imx_dma_channels[channel];
+	unsigned int config_port;
+
+	imxdma->completion_handler = cfg->completion_handler;
+
+	imx_dma_config_burstlen(channel, cfg->burstlen * cfg->word_size);
+	imx_dma_setup_handlers(channel, __irq_handler,
+			NULL,
+			cfg->driver_data);
+
+	imxdma->dev_addr = cfg->dma_address;
+
+	switch (cfg->word_size) {
+	case 1:
+		config_port = IMX_DMA_MEMSIZE_8;
+		break;
+	case 2:
+		config_port = IMX_DMA_MEMSIZE_16;
+		break;
+	case 4:
+		config_port = IMX_DMA_MEMSIZE_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	config_port |= IMX_DMA_TYPE_FIFO;
+
+	imx_dma_config_channel(channel, config_port,
+			IMX_DMA_MEMSIZE_32, cfg->dma_request, 1);
+
+	if (cfg->flags & IMX_DMA_SG_LOOP) {
+		imxdma->resbytes = IMX_DMA_LENGTH_LOOP;
+		imx_dma_setup_progression_handler(channel, __prog_handler);
+	}
+
+	return 0;
+}
+
+static int setup_single(int channel, dma_addr_t mem, int dma_length,
+		unsigned int dmamode)
+{
+	struct imx_dma_channel *imxdma = &imx_dma_channels[channel];
+
+	return imx_dma_setup_single(channel, mem,
+		     dma_length, imxdma->dev_addr,
+		     dmamode);
+}
+
+static int setup_sg(int channel, struct scatterlist *sg, unsigned int sgcount,
+			unsigned int dma_length, unsigned int dmamode)
+{
+	struct imx_dma_channel *imxdma = &imx_dma_channels[channel];
+
+	if (imxdma->resbytes == IMX_DMA_LENGTH_LOOP)
+		dma_length = IMX_DMA_LENGTH_LOOP;
+
+	return imx_dma_setup_sg(channel, sg, sgcount, dma_length,
+			imxdma->dev_addr,
+			dmamode);
+}
+
+static int request_channel(enum imx_dma_prio prio)
+{
+	return imx_dma_request_by_prio("imxdma", prio);
+}
+
+static struct imx_dma_operations imxdma_ops = {
+	.config_channel = config_channel,
+	.setup_single = setup_single,
+	.setup_sg = setup_sg,
+	.enable = imx_dma_enable,
+	.disable = imx_dma_disable,
+	.request = request_channel,
+	.free = imx_dma_free,
+};
+
 static int __init imx_dma_init(void)
 {
 	int ret = 0;
@@ -856,6 +966,8 @@ static int __init imx_dma_init(void)
 		imx_dma_channels[i].dma_num = i;
 	}
 
+	imx_dma_ops_register(&imxdma_ops);
+
 	return ret;
 }
 
diff --git a/arch/arm/mach-imx/include/mach/dma-mx1-mx2.h b/arch/arm/mach-imx/include/mach/dma-mx1-mx2.h
index df5f522..90b7971 100644
--- a/arch/arm/mach-imx/include/mach/dma-mx1-mx2.h
+++ b/arch/arm/mach-imx/include/mach/dma-mx1-mx2.h
@@ -2,7 +2,7 @@
 #define __MACH_DMA_MX1_MX2_H__
 /*
  * Don't use this header in new code, it will go away when all users are
- * converted to mach/dma-v1.h
+ * converted to mach/dma.h
  */
 
 #include <mach/dma-v1.h>
-- 
1.7.1




More information about the linux-arm-kernel mailing list