[linux-yocto] [PATCH 17/39] dma: lsi-dma32: Cleanup DMA driver

Charlie Paul cpaul.windriver at gmail.com
Tue Apr 22 13:59:29 PDT 2014


From: Anders Berg <anders.berg at lsi.com>

Use the devm_* helpers to avoid messy error recovery and make use of the
virt-dma interface to avoid duplicated code.

Signed-off-by: Anders Berg <anders.berg at lsi.com>
---
 drivers/dma/Kconfig     |    1 +
 drivers/dma/lsi-dma32.c |  527 ++++++++++++++++-------------------------------
 drivers/dma/lsi-dma32.h |   41 ++--
 3 files changed, 200 insertions(+), 369 deletions(-)

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 2ad1215..03eb991 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -91,6 +91,7 @@ config AT_HDMAC
 config LSI_DMA
 	tristate "LSI General Purpose DMA support"
 	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
 	select ASYNC_TX_ENABLE_CHANNEL_SWITCH
 	help
 	  Enable support for the LSI General Purpose DMA controller found
diff --git a/drivers/dma/lsi-dma32.c b/drivers/dma/lsi-dma32.c
index b16df68..0c679bf 100644
--- a/drivers/dma/lsi-dma32.c
+++ b/drivers/dma/lsi-dma32.c
@@ -23,10 +23,10 @@
 #include <linux/stat.h>
 #include <linux/module.h>
 #include <linux/init.h>
-#include <linux/kref.h>
 #include <linux/mm.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
+#include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/of_platform.h>
 #include <linux/of_address.h>
@@ -36,11 +36,9 @@
 #include <asm/page.h>
 #include <linux/bitops.h>
 #include <linux/atomic.h>
+#include "virt-dma.h"
 #include "lsi-dma32.h"
 
-#define rd32(_addr)         readl((_addr))
-#define wr32(_value, _addr) writel((_value), (_addr))
-
 #ifdef DEBUG
 #define engine_dbg(engine, fmt, ...) \
 	do { \
@@ -54,7 +52,7 @@
 		struct gpdma_channel *_c = dmac; \
 		(void)_c; \
 		pr_debug("dma0ch%d: [%s] " fmt, \
-			dmac->channel, __func__, ##__VA_ARGS__); \
+			dmac->id, __func__, ##__VA_ARGS__); \
 	} while (0)
 #else
 #define engine_dbg(engine, fmt, ...) do {} while (0)
@@ -62,29 +60,26 @@
 #endif
 
 
-static dma_cookie_t gpdma_tx_submit(struct dma_async_tx_descriptor *txd);
-
-
 static void reset_channel(struct gpdma_channel *dmac)
 {
 	const int WAIT = 1024;
 	int i;
 
 	/* Pause channel */
-	wr32(DMA_STATUS_CH_PAUS_WR_EN | DMA_STATUS_CH_PAUSE,
+	writel(DMA_STATUS_CH_PAUS_WR_EN | DMA_STATUS_CH_PAUSE,
 	     dmac->base+DMA_STATUS);
 	wmb();
 
 	/* Disable channel */
-	wr32(0, dmac->base+DMA_CHANNEL_CONFIG);
-	for (i = 0; rd32(dmac->base+DMA_CHANNEL_CONFIG) && i < WAIT; i++)
+	writel(0, dmac->base+DMA_CHANNEL_CONFIG);
+	for (i = 0; readl(dmac->base+DMA_CHANNEL_CONFIG) && i < WAIT; i++)
 		cpu_relax();
 	if (i == WAIT)
 		ch_dbg(dmac, "Failed to DISABLE channel\n");
 
 	/* Clear FIFO */
-	wr32(DMA_CONFIG_CLEAR_FIFO, dmac->base+DMA_CHANNEL_CONFIG);
-	for (i = 0; rd32(dmac->base+DMA_CHANNEL_CONFIG) && i < WAIT; i++)
+	writel(DMA_CONFIG_CLEAR_FIFO, dmac->base+DMA_CHANNEL_CONFIG);
+	for (i = 0; readl(dmac->base+DMA_CHANNEL_CONFIG) && i < WAIT; i++)
 		cpu_relax();
 	if (i == WAIT)
 		ch_dbg(dmac, "Failed to clear FIFO\n");
@@ -100,7 +95,7 @@ static void soft_reset(struct gpdma_engine *engine)
 		reset_channel(&engine->channel[i]);
 
 	/* Reset GPDMA by writing Magic Number to reset reg */
-	wr32(GPDMA_MAGIC, engine->gbase + SOFT_RESET);
+	writel(GPDMA_MAGIC, engine->gbase + SOFT_RESET);
 	wmb();
 
 	cfg = (engine->pool.phys & 0xfff00000) | GEN_CONFIG_EXT_MEM;
@@ -110,7 +105,7 @@ static void soft_reset(struct gpdma_engine *engine)
 			cfg |= GEN_CONFIG_INT_EDGE(i);
 		engine_dbg(engine, "Using edge-triggered interrupts\n");
 	}
-	wr32(cfg, engine->gbase + GEN_CONFIG);
+	writel(cfg, engine->gbase + GEN_CONFIG);
 	engine_dbg(engine, "engine->desc.phys & 0xfff00000 == %llx\n",
 		   (engine->pool.phys & 0xfff00000));
 
@@ -164,28 +159,30 @@ static struct gpdma_desc *get_descriptor(struct gpdma_engine *engine)
 	unsigned long flags;
 	struct gpdma_desc *desc = NULL;
 
-	raw_spin_lock_irqsave(&engine->lock, flags);
+	spin_lock_irqsave(&engine->lock, flags);
 	if (engine->pool.next < GPDMA_MAX_DESCRIPTORS)
 		desc = engine->pool.free[engine->pool.next++];
-	raw_spin_unlock_irqrestore(&engine->lock, flags);
+	spin_unlock_irqrestore(&engine->lock, flags);
+	desc->engine = engine;
 
 	return desc;
 }
 
-static void
-free_descriptor(struct gpdma_engine *engine, struct gpdma_desc *desc)
+static void free_descriptor(struct virt_dma_desc *vd)
 {
+	struct gpdma_desc *desc = to_gpdma_desc(vd);
+	struct gpdma_engine *engine = desc->engine;
 	unsigned long flags;
 
-	raw_spin_lock_irqsave(&engine->lock, flags);
+	spin_lock_irqsave(&engine->lock, flags);
 	BUG_ON(engine->pool.next == 0);
 	engine->pool.free[--engine->pool.next] = desc;
-	raw_spin_unlock_irqrestore(&engine->lock, flags);
+	spin_unlock_irqrestore(&engine->lock, flags);
 }
 
 static int segment_match(struct gpdma_engine *engine, struct gpdma_desc *desc)
 {
-	unsigned int gpreg_dma = rd32(engine->gpreg);
+	unsigned int gpreg_dma = readl(engine->gpreg);
 	unsigned int seg_src = (gpreg_dma >> 0) & 0x3f;
 	unsigned int seg_dst = (gpreg_dma >> 8) & 0x3f;
 
@@ -193,17 +190,23 @@ static int segment_match(struct gpdma_engine *engine, struct gpdma_desc *desc)
 		seg_dst == ((desc->dst >> 32) & 0x3f));
 }
 
-static int gpdma_start_job(struct gpdma_channel *dmac)
+static void gpdma_start(struct gpdma_channel *dmac)
 {
-	void __iomem      *base = BASE(dmac);
-	struct gpdma_desc *desc;
-	struct descriptor *d;
-	phys_addr_t        paddr;
+	struct virt_dma_desc *vdesc;
+	struct gpdma_desc    *desc;
+	phys_addr_t           paddr;
 
-	desc = list_first_entry(&dmac->waiting, struct gpdma_desc, node);
-	d = &desc->hw;
-	paddr = virt_to_phys(d);
-	WARN_ON(paddr & 0xf);
+	vdesc = vchan_next_desc(&dmac->vc);
+	if (!vdesc) {
+		clear_bit(dmac->id, &dmac->engine->ch_busy);
+		dmac->active = NULL;
+		return;
+	}
+
+	/* Remove from list and mark as active */
+	list_del(&vdesc->node);
+	desc = to_gpdma_desc(vdesc);
+	dmac->active = desc;
 
 	if (!(dmac->engine->chip->flags & LSIDMA_SEG_REGS)) {
 		/*
@@ -211,118 +214,37 @@ static int gpdma_start_job(struct gpdma_channel *dmac)
 		 * running descriptor on any other channel.
 		 */
 		if (dmac->engine->ch_busy && !segment_match(dmac->engine, desc))
-			return -EBUSY;
+			return;
 	}
 
-	/* Remove from 'waiting' list and mark as active */
-	list_del(&desc->node);
-	desc->dma_status = DMA_IN_PROGRESS;
-	dmac->active = desc;
-	set_bit(dmac->channel, &dmac->engine->ch_busy);
-	ch_dbg(dmac, "Load desc va=%p pa=%llx\n", d, paddr);
-
+	paddr = virt_to_phys(&desc->hw);
+	WARN_ON(paddr & 0xf);
 	if (dmac->engine->chip->flags & LSIDMA_NEXT_FULL) {
 		/* Physical address of descriptor to load */
-		wr32((u32)paddr | 0x8, base+DMA_NXT_DESCR);
+		writel((u32)paddr | 0x8, dmac->base + DMA_NXT_DESCR);
 	} else {
-		wr32((u32)paddr & 0xfffff, base+DMA_NXT_DESCR);
+		writel((u32)paddr & 0xfffff, dmac->base + DMA_NXT_DESCR);
 	}
 
 	if (dmac->engine->chip->flags & LSIDMA_SEG_REGS) {
 		/* Segment bits [39..32] of descriptor, src and dst addresses */
-		wr32(paddr >> 32, base+DMA_DESCR_ADDR_SEG);
-		wr32(desc->src >> 32, base+DMA_SRC_ADDR_SEG);
-		wr32(desc->dst >> 32, base+DMA_DST_ADDR_SEG);
+		writel(paddr >> 32, dmac->base + DMA_DESCR_ADDR_SEG);
+		writel(desc->src >> 32, dmac->base + DMA_SRC_ADDR_SEG);
+		writel(desc->dst >> 32, dmac->base + DMA_DST_ADDR_SEG);
 	} else {
 		unsigned int seg_src = (desc->src >> 32) & 0x3f;
 		unsigned int seg_dst = (desc->dst >> 32) & 0x3f;
-		wr32((seg_dst << 8) | seg_src, dmac->engine->gpreg);
+		writel((seg_dst << 8) | seg_src, dmac->engine->gpreg);
 	}
 	wmb();
-	wr32(DMA_CONFIG_DSC_LOAD, base+DMA_CHANNEL_CONFIG);
-
-	return 0;
-}
-
-static inline void
-gpdma_job_complete(struct gpdma_channel *dmac, struct gpdma_desc *desc)
-{
-	dmac->last_completed = desc->txd.cookie;
-
-	if (desc->txd.callback)
-		desc->txd.callback(desc->txd.callback_param);
-	ch_dbg(dmac, "cookie %d status %d\n",
-		desc->txd.cookie, desc->dma_status);
-	free_descriptor(dmac->engine, desc);
-	clear_bit(dmac->channel, &dmac->engine->ch_busy);
-}
-
-static void flush_channel(struct gpdma_channel *dmac)
-{
-	struct gpdma_desc *desc, *tmp;
-
-	ch_dbg(dmac, "active=%p\n", dmac->active);
-	reset_channel(dmac);
-	if (dmac->active) {
-		gpdma_job_complete(dmac, dmac->active);
-		dmac->active = NULL;
-	}
-	list_for_each_entry_safe(desc, tmp, &dmac->waiting, node) {
-		ch_dbg(dmac, "del wating %p\n", desc);
-		list_del(&desc->node);
-		gpdma_job_complete(dmac, desc);
-	}
-}
-
-static inline void gpdma_sched_job_handler(struct gpdma_engine *engine)
-{
-	if (likely(test_bit(GPDMA_INIT, &engine->state)))
-		tasklet_schedule(&engine->job_task);
-}
-
-static void job_tasklet(unsigned long data)
-{
-	struct gpdma_engine *engine = (struct gpdma_engine *)data;
-	unsigned long        flags;
-	int i;
-
-	/* Handle completed jobs */
-	for (i = 0; i < engine->chip->num_channels; i++) {
-		struct gpdma_channel *dmac = &engine->channel[i];
-		struct gpdma_desc    *desc = dmac->active;
-
-		desc = dmac->active;
-		if (desc != NULL) {
-			/* Check if job completed */
-			if (desc->dma_status != DMA_IN_PROGRESS) {
-				dmac->active = NULL;
-				gpdma_job_complete(dmac, desc);
-			}
-		}
-	}
-
-	/* Start new jobs */
-	for (i = 0; i < engine->chip->num_channels; i++) {
-		struct gpdma_channel *dmac = &engine->channel[i];
-
-		raw_spin_lock_irqsave(&dmac->lock, flags);
-
-		if (dmac->active == NULL && !list_empty(&dmac->waiting)) {
-			/* Start next job */
-			if (gpdma_start_job(dmac) != 0) {
-				raw_spin_unlock_irqrestore(&dmac->lock, flags);
-				break;
-			}
-		}
-
-		raw_spin_unlock_irqrestore(&dmac->lock, flags);
-	}
+	writel(DMA_CONFIG_DSC_LOAD, dmac->base + DMA_CHANNEL_CONFIG);
+	set_bit(dmac->id, &dmac->engine->ch_busy);
 }
 
 static irqreturn_t gpdma_isr_err(int irqno, void *_engine)
 {
 	struct gpdma_engine *engine = _engine;
-	u32 status = rd32(engine->gbase + GEN_STAT);
+	u32 status = readl(engine->gbase + GEN_STAT);
 	u32 ch = (status & GEN_STAT_CH0_ERROR) ? 0 : 1;
 	struct gpdma_channel *dmac = &engine->channel[ch];
 
@@ -330,10 +252,10 @@ static irqreturn_t gpdma_isr_err(int irqno, void *_engine)
 		return IRQ_NONE;
 
 	/* Read the channel status bits and dump the error */
-	status = rd32(dmac->base + DMA_STATUS);
-	pr_err("dma: channel%d error %08x\n", dmac->channel, status);
+	status = readl(dmac->base + DMA_STATUS);
+	pr_err("dma: channel%d error %08x\n", dmac->id, status);
 	/* Clear the error indication */
-	wr32(DMA_STATUS_ERROR, dmac->base+DMA_STATUS);
+	writel(DMA_STATUS_ERROR, dmac->base+DMA_STATUS);
 
 	return IRQ_HANDLED;
 }
@@ -345,11 +267,9 @@ static irqreturn_t gpdma_isr(int irqno, void *_dmac)
 	u32                  status;
 	u32	             error;
 
-	WARN_ON(desc == NULL);
-
-	status = rd32(dmac->base+DMA_STATUS);
+	status = readl(dmac->base+DMA_STATUS);
 	error = status & DMA_STATUS_ERROR;
-	wr32(DMA_STATUS_CLEAR, dmac->base+DMA_STATUS);
+	writel(DMA_STATUS_CLEAR, dmac->base+DMA_STATUS);
 
 	ch_dbg(dmac, "irq%u channel status %08x, error %08x\n",
 		irqno, status, error);
@@ -360,25 +280,49 @@ static irqreturn_t gpdma_isr(int irqno, void *_dmac)
 		if (error & DMA_STATUS_UNALIGNED_ERR) {
 			dev_warn(dmac->engine->dev,
 				 "Unaligned transaction on ch%d (status=%#x)\n",
-				 dmac->channel, status);
+				 dmac->id, status);
 			reset_channel(dmac);
 		} else {
 			dev_warn(dmac->engine->dev,
 				 "DMA transaction error on ch%d (status=%#x)\n",
-				 dmac->channel, status);
+				 dmac->id, status);
 		}
 	}
 
-	dmac->active->dma_status = (error ? DMA_ERROR : DMA_SUCCESS);
+	BUG_ON(desc == NULL);
 
-	wr32(0, dmac->base+DMA_CHANNEL_CONFIG);
-	wr32(DMA_CONFIG_CLEAR_FIFO, dmac->base+DMA_CHANNEL_CONFIG);
-
-	gpdma_sched_job_handler(dmac->engine);
+	spin_lock(&dmac->vc.lock);
+	vchan_cookie_complete(&desc->vdesc);
+	dmac->active = NULL;
+	if (vchan_next_desc(&dmac->vc)) {
+		gpdma_start(dmac);
+	} else {
+		/* Stop channel */
+		writel(0, dmac->base + DMA_CHANNEL_CONFIG);
+		writel(DMA_CONFIG_CLEAR_FIFO, dmac->base + DMA_CHANNEL_CONFIG);
+		clear_bit(dmac->id, &dmac->engine->ch_busy);
+	}
+	spin_unlock(&dmac->vc.lock);
 
 	return IRQ_HANDLED;
 }
 
+static void flush_channel(struct gpdma_channel *dmac)
+{
+	unsigned long flags;
+	LIST_HEAD(head);
+
+	spin_lock_irqsave(&dmac->vc.lock, flags);
+	reset_channel(dmac);
+	if (dmac->active) {
+		free_descriptor(&dmac->active->vdesc);
+		dmac->active = NULL;
+	}
+	vchan_get_all_descriptors(&dmac->vc, &head);
+	spin_unlock_irqrestore(&dmac->vc.lock, flags);
+	vchan_dma_desc_free_list(&dmac->vc, &head);
+}
+
 /*
  * Perform soft reset procedure on DMA Engine.  Needed occasionally to work
  * around nasty bug ACP3400 sRIO HW.
@@ -394,41 +338,37 @@ reset_engine(struct device *dev,
 	if (!engine)
 		return -EINVAL;
 
-	/* Disable interrupts and acquire each channel lock */
-	for (i = 0; i < engine->chip->num_channels; i++)
-		disable_irq(engine->channel[i].irq);
-
-	/* Disable tasklet (synchronized) */
-	tasklet_disable(&engine->job_task);
+	/* Disable interrupts and tasklet and acquire each channel lock */
+	for (i = 0; i < engine->chip->num_channels; i++) {
+		struct gpdma_channel *dmac = &engine->channel[i];
+		disable_irq(dmac->irq);
+		spin_lock(&dmac->vc.lock);
+		tasklet_disable(&dmac->vc.task);
+	}
 
 	soft_reset(engine);
 
-	/* Re-queue any active jobs */
 	for (i = 0; i < engine->chip->num_channels; i++) {
 		struct gpdma_channel *dmac = &engine->channel[i];
-		struct gpdma_desc  *active;
-		raw_spin_lock(&engine->channel[i].lock);
-		active = dmac->active;
-		if (active && active->dma_status == DMA_IN_PROGRESS) {
-			/* Restart active job after soft reset */
-			list_add(&active->node, &dmac->waiting);
+		tasklet_enable(&dmac->vc.task);
+		enable_irq(dmac->irq);
+		/* Restart any active jobs */
+		if (dmac->active) {
+			struct gpdma_desc *active = dmac->active;
 			dmac->active = NULL;
+			list_add(&active->vdesc.node, &dmac->vc.desc_submitted);
+			if (vchan_issue_pending(&dmac->vc))
+				gpdma_start(dmac);
 		}
-		raw_spin_unlock(&engine->channel[i].lock);
+		spin_unlock(&dmac->vc.lock);
 	}
 
-	tasklet_enable(&engine->job_task);
-
-	for (i = 0; i < engine->chip->num_channels; i++)
-		enable_irq(engine->channel[i].irq);
-
-	gpdma_sched_job_handler(engine);
-
 	return count;
 }
 
 static DEVICE_ATTR(soft_reset, S_IWUSR, NULL, reset_engine);
 
+
 /*
  *===========================================================================
  *
@@ -445,7 +385,7 @@ static DEVICE_ATTR(soft_reset, S_IWUSR, NULL, reset_engine);
  */
 static int gpdma_alloc_chan_resources(struct dma_chan *chan)
 {
-	struct gpdma_channel *dmac = dchan_to_gchan(chan);
+	struct gpdma_channel *dmac = to_gpdma_chan(chan);
 
 	(void) dmac;
 	return 1;
@@ -457,7 +397,7 @@ static int gpdma_alloc_chan_resources(struct dma_chan *chan)
  */
 static void gpdma_free_chan_resources(struct dma_chan *chan)
 {
-	struct gpdma_channel *dmac = dchan_to_gchan(chan);
+	struct gpdma_channel *dmac = to_gpdma_chan(chan);
 
 	(void) dmac;
 }
@@ -468,12 +408,12 @@ static void gpdma_free_chan_resources(struct dma_chan *chan)
  */
 static struct dma_async_tx_descriptor *
 gpdma_prep_memcpy(struct dma_chan *chan,
-		 dma_addr_t dst,
-		 dma_addr_t src,
-		 size_t size,
-		 unsigned long dma_flags)
+		  dma_addr_t dst,
+		  dma_addr_t src,
+		  size_t size,
+		  unsigned long dma_flags)
 {
-	struct gpdma_channel *dmac = dchan_to_gchan(chan);
+	struct gpdma_channel *dmac = to_gpdma_chan(chan);
 	struct gpdma_desc *desc;
 	u16 rot_len, x_count, src_size, access_size;
 
@@ -532,44 +472,10 @@ gpdma_prep_memcpy(struct dma_chan *chan,
 	desc->hw.dst_addr      = cpu_to_le32(dst & 0xffffffff);
 
 	/* Setup sw descriptor */
-	INIT_LIST_HEAD(&desc->node);
 	desc->src        = src;
 	desc->dst        = dst;
-	desc->dma_status = DMA_IN_PROGRESS;
-	dma_async_tx_descriptor_init(&desc->txd, chan);
-	desc->txd.tx_submit = gpdma_tx_submit;
-	desc->txd.flags     = DMA_CTRL_ACK;
-
-	return &desc->txd;
-}
-
-/**
- * gpdma_tx_submit
- *
- */
-static dma_cookie_t gpdma_tx_submit(struct dma_async_tx_descriptor *txd)
-{
-	struct gpdma_channel *dmac = dchan_to_gchan(txd->chan);
-	struct gpdma_desc    *desc = txd_to_desc(txd);
-	unsigned long         flags;
-
-	raw_spin_lock_irqsave(&dmac->lock, flags);
-
-	/* Update last used cookie */
-	if (++dmac->chan.cookie < 0)
-		dmac->chan.cookie = DMA_MIN_COOKIE;
-
-	txd->cookie = dmac->chan.cookie;
-	list_add_tail(&desc->node, &dmac->waiting);
 
-	raw_spin_unlock_irqrestore(&dmac->lock, flags);
-
-	ch_dbg(dmac, "cookie=%d last_completed=%d\n",
-		txd->cookie, dmac->last_completed);
-
-	gpdma_sched_job_handler(dmac->engine);
-
-	return txd->cookie;
+	return vchan_tx_prep(&dmac->vc, &desc->vdesc, DMA_CTRL_ACK);
 }
 
 /**
@@ -578,9 +484,13 @@ static dma_cookie_t gpdma_tx_submit(struct dma_async_tx_descriptor *txd)
  */
 static void gpdma_issue_pending(struct dma_chan *chan)
 {
-	struct gpdma_channel *dmac = dchan_to_gchan(chan);
+	struct gpdma_channel *dmac = to_gpdma_chan(chan);
+	unsigned long flags;
 
-	gpdma_sched_job_handler(dmac->engine);
+	spin_lock_irqsave(&dmac->vc.lock, flags);
+	if (vchan_issue_pending(&dmac->vc) && !dmac->active)
+		gpdma_start(dmac);
+	spin_unlock_irqrestore(&dmac->vc.lock, flags);
 }
 
 /**
@@ -593,13 +503,7 @@ static enum dma_status gpdma_tx_status(struct dma_chan *chan,
 				       dma_cookie_t cookie,
 				       struct dma_tx_state *txstate)
 {
-	struct gpdma_channel *dmac = dchan_to_gchan(chan);
-	enum dma_status ret = 0;
-
-	ret = dma_async_is_complete(cookie, dmac->last_completed, chan->cookie);
-	dma_set_tx_state(txstate, dmac->last_completed, chan->cookie, 0);
-
-	return ret;
+	return dma_cookie_status(chan, cookie, txstate);
 }
 
 
@@ -608,20 +512,18 @@ static enum dma_status gpdma_tx_status(struct dma_chan *chan,
  * returns zero or error code.
  *
  */
-static int gpdma_device_control(struct dma_chan *dchan,
+static int gpdma_device_control(struct dma_chan *chan,
 				enum dma_ctrl_cmd cmd,
 				unsigned long arg)
 {
-	struct gpdma_channel *dmac = dchan_to_gchan(dchan);
+	struct gpdma_channel *dmac = to_gpdma_chan(chan);
 
 	if (!dmac)
 		return -EINVAL;
 
 	switch (cmd) {
 	case DMA_TERMINATE_ALL:
-		tasklet_disable(&dmac->engine->job_task);
 		flush_channel(dmac);
-		tasklet_enable(&dmac->engine->job_task);
 		break;
 
 	default:
@@ -631,88 +533,28 @@ static int gpdma_device_control(struct dma_chan *dchan,
 	return 0;
 }
 
-static void gpdma_engine_release(struct kref *kref)
-{
-	struct gpdma_engine *engine =
-		container_of(kref, struct gpdma_engine, kref);
-
-	kfree(engine);
-}
-
-/**
- * gpdma_of_remove
- *
- *
- */
-static int gpdma_of_remove(struct platform_device *op)
-{
-	struct gpdma_engine *engine = dev_get_drvdata(&op->dev);
-	int i;
-
-	dev_dbg(&op->dev, "%s\n", __func__);
-
-	if (engine) {
-		if (test_and_clear_bit(GPDMA_INIT, &engine->state)) {
-			device_remove_file(&op->dev, &dev_attr_soft_reset);
-			dma_async_device_unregister(&engine->dma_device);
-		}
-
-		tasklet_disable(&engine->job_task);
-		tasklet_kill(&engine->job_task);
-
-		if (engine->err_irq)
-			free_irq(engine->err_irq, engine);
-
-		for (i = 0; i < engine->chip->num_channels; i++) {
-			struct gpdma_channel *dmac = &engine->channel[i];
-
-			if (dmac->irq)
-				free_irq(dmac->irq, dmac);
-		}
-
-		free_desc_table(engine);
-		iounmap(engine->iobase);
-		kref_put(&engine->kref, gpdma_engine_release);
-		dev_set_drvdata(&op->dev, NULL);
-	}
-
-	return 0;
-}
-
-static int
-setup_channel(struct gpdma_engine *engine, struct device_node *child, int id)
+static int setup_channel(struct gpdma_channel *dmac, struct device_node *child)
 {
-	struct gpdma_channel *dmac;
-	int rc = -ENODEV;
+	struct gpdma_engine *engine = dmac->engine;
+	int rc;
 
-	if (id >= engine->chip->num_channels) {
-		dev_dbg(engine->dev, "Too many channels (%d)\n", id);
-		goto err_init;
-	}
-
-	dmac = &engine->channel[id];
-	dmac->channel = id;
-	dmac->engine = engine;
-	raw_spin_lock_init(&dmac->lock);
-	INIT_LIST_HEAD(&dmac->waiting);
-	dmac->chan.device = &engine->dma_device;
-
-	dmac->base = engine->iobase + id*engine->chip->chregs_offset;
-	dev_dbg(engine->dev, "channel%d base @ %p\n", id, dmac->base);
+	dmac->base = engine->iobase + dmac->id * engine->chip->chregs_offset;
+	dev_dbg(engine->dev, "channel%d base @ %p\n", dmac->id, dmac->base);
 
 	/* Find the IRQ line, if it exists in the device tree */
 	dmac->irq = irq_of_parse_and_map(child, 0);
-	dev_dbg(engine->dev, "channel %d, irq %d\n", id, dmac->irq);
-	rc = request_irq(dmac->irq, gpdma_isr, IRQF_SHARED, "lsi-dma", dmac);
+	dev_dbg(engine->dev, "channel %d, irq %d\n", dmac->id, dmac->irq);
+	rc = devm_request_irq(engine->dev, dmac->irq, gpdma_isr, 0,
+			      "lsi-dma", dmac);
 	if (rc) {
 		dev_err(engine->dev, "failed to request_irq, error = %d\n", rc);
-		goto err_init;
+		return rc;
 	}
-	/* Add the channel to the DMAC list */
-	list_add_tail(&dmac->chan.device_node, &engine->dma_device.channels);
+	/* Initialize the virt-channel */
+	dmac->vc.desc_free = free_descriptor;
+	vchan_init(&dmac->vc, &engine->dma_device);
 
-err_init:
-	return rc;
+	return 0;
 }
 
 static struct lsidma_hw lsi_dma32 = {
@@ -750,16 +592,12 @@ static const struct of_device_id gpdma_of_ids[] = {
 	{ }
 };
 
-/**
- * gpdma_of_probe
- *
- *
- */
 static int gpdma_of_probe(struct platform_device *op)
 {
 	struct gpdma_engine *engine;
 	struct dma_device   *dma;
 	struct device_node *child;
+	struct resource *res;
 	const struct of_device_id *match;
 	int rc = -ENOMEM;
 	int id = 0;
@@ -768,14 +606,11 @@ static int gpdma_of_probe(struct platform_device *op)
 	if (!match)
 		return -EINVAL;
 
-	engine = kzalloc(sizeof(*engine), GFP_KERNEL);
+	engine = devm_kzalloc(&op->dev, sizeof(*engine), GFP_KERNEL);
 	if (!engine)
 		return -ENOMEM;
 
-	dev_set_drvdata(&op->dev, engine);
-
-	kref_init(&engine->kref);
-	raw_spin_lock_init(&engine->lock);
+	spin_lock_init(&engine->lock);
 	engine->dev = &op->dev;
 	engine->chip = (struct lsidma_hw *)match->data;
 
@@ -794,50 +629,57 @@ static int gpdma_of_probe(struct platform_device *op)
 	dma->device_control = gpdma_device_control;
 	INIT_LIST_HEAD(&dma->channels);
 
-	/*
-	 * Map device I/O memory
+	/* Map device I/O memory
 	 */
-	engine->iobase = of_iomap(op->dev.of_node, 0);
-	if (!engine->iobase) {
-		rc = -EINVAL;
-		goto err_init;
-	}
+	res = platform_get_resource(op, IORESOURCE_MEM, 0);
+	engine->iobase = devm_ioremap_resource(&op->dev, res);
+	if (IS_ERR(engine->iobase))
+		return PTR_ERR(engine->iobase);
 	dev_dbg(&op->dev, "mapped base @ %p\n", engine->iobase);
 
-	engine->err_irq = irq_of_parse_and_map(op->dev.of_node, 1);
+	res = platform_get_resource(op, IORESOURCE_MEM, 1);
+	if (res) {
+		engine->gpreg = devm_ioremap_nocache(&op->dev,
+						     res->start,
+						     resource_size(res));
+		if (IS_ERR(engine->gpreg))
+			return PTR_ERR(engine->gpreg);
+		dev_dbg(&op->dev, "mapped gpreg @ %p\n", engine->gpreg);
+	}
+
+	engine->err_irq = platform_get_irq(op, 1);
 	if (engine->err_irq) {
-		rc = request_irq(engine->err_irq, gpdma_isr_err,
-				 IRQF_SHARED, "lsi-dma-err", engine);
+		rc = devm_request_irq(&op->dev, engine->err_irq,
+				      gpdma_isr_err, 0, "lsi-dma-err", engine);
 		if (rc) {
-			dev_err(engine->dev,
-				"failed to request irq%d\n",
+			dev_err(engine->dev, "failed to request irq%d\n",
 				engine->err_irq);
 			engine->err_irq = 0;
 		}
 	}
 
-	if (!(engine->chip->flags & LSIDMA_SEG_REGS)) {
-		struct device_node *gp_node;
-		gp_node = of_find_compatible_node(NULL, NULL, "lsi,gpreg");
-		if (!gp_node) {
-			dev_err(engine->dev, "FDT is missing node 'gpreg'\n");
-			rc = -EINVAL;
-			goto err_init;
-		}
-		engine->gpreg = of_iomap(gp_node, 0);
-	}
-
 	/* General registes at device specific offset */
 	engine->gbase = engine->iobase + engine->chip->genregs_offset;
 
-	if (alloc_desc_table(engine))
-		goto err_init;
+	rc = alloc_desc_table(engine);
+	if (rc)
+		return rc;
 
 	/* Setup channels */
 	for_each_child_of_node(op->dev.of_node, child) {
-		rc = setup_channel(engine, child, id++);
-		if (rc != 0)
-			goto err_init;
+		struct gpdma_channel *dmac = &engine->channel[id];
+
+		if (id >= engine->chip->num_channels) {
+			dev_dbg(engine->dev, "Too many channels (%d)\n", id);
+			return -ENODEV;
+		}
+
+		dmac->id = id;
+		dmac->engine = engine;
+		rc = setup_channel(dmac, child);
+		if (rc)
+			return rc;
+		++id;
 	}
 
 	soft_reset(engine);
@@ -845,19 +687,27 @@ static int gpdma_of_probe(struct platform_device *op)
 	rc = dma_async_device_register(&engine->dma_device);
 	if (rc) {
 		dev_err(engine->dev, "unable to register\n");
-		goto err_init;
+		return rc;
 	}
-	tasklet_init(&engine->job_task, job_tasklet, (unsigned long)engine);
-	device_create_file(&op->dev, &dev_attr_soft_reset);
 
-	set_bit(GPDMA_INIT, &engine->state);
+	device_create_file(&op->dev, &dev_attr_soft_reset);
+	dev_set_drvdata(&op->dev, engine);
 
 	return 0;
+}
 
-err_init:
-	gpdma_of_remove(op);
+static int gpdma_of_remove(struct platform_device *op)
+{
+	struct gpdma_engine *engine = dev_get_drvdata(&op->dev);
+
+	dev_dbg(&op->dev, "%s\n", __func__);
+
+	device_remove_file(&op->dev, &dev_attr_soft_reset);
+	dma_async_device_unregister(&engine->dma_device);
+	free_desc_table(engine);
 	dev_set_drvdata(&op->dev, NULL);
-	return rc;
+
+	return 0;
 }
 
 static struct platform_driver gpdma_of_driver = {
@@ -870,18 +720,7 @@ static struct platform_driver gpdma_of_driver = {
 	.remove = gpdma_of_remove,
 };
 
-static __init int gpdma_init(void)
-{
-	return platform_driver_register(&gpdma_of_driver);
-}
-
-static void __exit gpdma_exit(void)
-{
-	platform_driver_unregister(&gpdma_of_driver);
-}
-
-subsys_initcall(gpdma_init);
-module_exit(gpdma_exit);
+module_platform_driver(gpdma_of_driver);
 
 MODULE_DESCRIPTION("LSI DMA driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/dma/lsi-dma32.h b/drivers/dma/lsi-dma32.h
index 5f10198..b4248cd 100644
--- a/drivers/dma/lsi-dma32.h
+++ b/drivers/dma/lsi-dma32.h
@@ -15,9 +15,11 @@
 
 #include <linux/dmaengine.h>
 #include <linux/dma-mapping.h>
+#include <linux/dma-mapping.h>
 #include <linux/device.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
+#include "virt-dma.h"
 
 #define MAX_GPDMA_CHANNELS      4
 #define GPDMA_MAX_DESCRIPTORS  128
@@ -151,14 +153,16 @@ struct gpdma_engine;
 
 struct gpdma_desc {
 	struct descriptor               hw;
-	struct list_head                node;
 	dma_addr_t                      src;
 	dma_addr_t                      dst;
-	enum dma_status                 dma_status;
-	struct dma_async_tx_descriptor	txd;
+	struct gpdma_engine            *engine;
+	struct virt_dma_desc	        vdesc;
 } __aligned(32);
 
-#define txd_to_desc(n) container_of(n, struct gpdma_desc, txd)
+static struct gpdma_desc *to_gpdma_desc(struct virt_dma_desc *vdesc)
+{
+	return container_of(vdesc, struct gpdma_desc, vdesc);
+}
 
 struct gpdma_channel {
 	/* Back reference to DMA engine */
@@ -166,22 +170,19 @@ struct gpdma_channel {
 	/* Channel registers */
 	void __iomem			*base;
 	/* Channel id */
-	int			        channel;
+	int			        id;
 	/* IRQ number as passed to request_irq() */
 	int				irq;
-	/* Cookie for last completed transaction */
-	dma_cookie_t			last_completed;
-	/* List of prep'ed descriptors */
-	struct list_head                waiting;
 	/* Currently running descriptor */
 	struct gpdma_desc               *active;
-	/* Lock for accessing 'waiting' and 'active' */
-	raw_spinlock_t			lock;
 	/* Channel parameters (DMA engine framework) */
-	struct dma_chan			chan;
+	struct virt_dma_chan		vc;
 };
 
-#define dchan_to_gchan(n) container_of(n, struct gpdma_channel, chan)
+static inline struct gpdma_channel *to_gpdma_chan(struct dma_chan *chan)
+{
+	return container_of(chan, struct gpdma_channel, vc.chan);
+}
 
 struct lsidma_hw {
 	unsigned int num_channels;
@@ -194,20 +195,16 @@ struct lsidma_hw {
 };
 
 struct gpdma_engine {
-	unsigned long                   state;
-#define GPDMA_INIT      0U
-	struct kref                     kref;
 	struct device			*dev;
 	struct lsidma_hw		*chip;
 	struct gpdma_channel		channel[MAX_GPDMA_CHANNELS];
-	/* Bit mask where bit[n] == 1 if channel busy */
+	/** Bit mask where bit[n] == 1 if channel busy */
 	unsigned long                   ch_busy;
-	struct tasklet_struct           job_task;
 	int                             err_irq;
 	void __iomem			*iobase;
 	void __iomem			*gbase;
 	void __iomem			*gpreg;
-	raw_spinlock_t			lock;
+	spinlock_t			lock;
 	struct {
 		u32                     order;
 		dma_addr_t              phys;
@@ -220,10 +217,4 @@ struct gpdma_engine {
 
 #define desc_to_engine(n) container_of(n, struct gpdma_engine, desc)
 
-
-static inline void __iomem *BASE(struct gpdma_channel *dmac)
-{
-	return dmac->base;
-}
-
 #endif
-- 
1.7.9.5



More information about the linux-yocto mailing list