All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 0/8] DMA engine conversion for omap_hsmmc
@ 2012-04-18 10:09 ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 10:09 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap, linux-mmc

This series is the preliminary conversion of OMAPs private DMA API to
DMAengine.  It focuses on the OMAP HSMMC driver, as I believe that is
the only DMA-capable driver which is used on the platforms I have
available (3430LDP and 4430SDP).

For the OMAP DMAengine driver, there's a few short-comings:

1. pause/resume support is not implemented; it's not clear whether the
   OMAP hardware is capable of supporting this sanely.

2. status function does not return DMA residue.

3. DMAengine holds on to physical DMA channels while the driver maintains
   a reference to the virtual DMA engine channel.  This is not an issue
   with drivers; drivers should be able to hold a DMA channel for their
   lifetime.  DMAengine code could be improved to dynamically allocate
   physical channels, but this should happen when more drivers have been
   converted.

I took the approach with omap_hsmmc to add DMAengine support to the driver
along side the existing DMA support, so that I had a stage where I could
switch between the two implementations for comparisons - and also to allow
me to run with read using DMAengine support, and write using the private
API.  This allowed a progressive switch over to using DMAengine, and
allowed debugging without destroying the contents of the MMC card.

A point about DMAengine and DMA API for anyone who contemplates converting
other drivers: the struct device to be used for mapping/unmapping/coherent
memory is the DMAengine's device structure (channel->device->dev) not the
peripheral (eg, MMC) device structure.  It's the DMAengine which is
performing the DMA to memory, not the peripheral device.  The peripheral
device is merely receiving the data from the DMAengine, and does not care
where that data has come from.  So, dma masks for peripheral devices are
meaningless (and wrong.)

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

* [RFC 0/8] DMA engine conversion for omap_hsmmc
@ 2012-04-18 10:09 ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 10:09 UTC (permalink / raw)
  To: linux-arm-kernel

This series is the preliminary conversion of OMAPs private DMA API to
DMAengine.  It focuses on the OMAP HSMMC driver, as I believe that is
the only DMA-capable driver which is used on the platforms I have
available (3430LDP and 4430SDP).

For the OMAP DMAengine driver, there's a few short-comings:

1. pause/resume support is not implemented; it's not clear whether the
   OMAP hardware is capable of supporting this sanely.

2. status function does not return DMA residue.

3. DMAengine holds on to physical DMA channels while the driver maintains
   a reference to the virtual DMA engine channel.  This is not an issue
   with drivers; drivers should be able to hold a DMA channel for their
   lifetime.  DMAengine code could be improved to dynamically allocate
   physical channels, but this should happen when more drivers have been
   converted.

I took the approach with omap_hsmmc to add DMAengine support to the driver
along side the existing DMA support, so that I had a stage where I could
switch between the two implementations for comparisons - and also to allow
me to run with read using DMAengine support, and write using the private
API.  This allowed a progressive switch over to using DMAengine, and
allowed debugging without destroying the contents of the MMC card.

A point about DMAengine and DMA API for anyone who contemplates converting
other drivers: the struct device to be used for mapping/unmapping/coherent
memory is the DMAengine's device structure (channel->device->dev) not the
peripheral (eg, MMC) device structure.  It's the DMAengine which is
performing the DMA to memory, not the peripheral device.  The peripheral
device is merely receiving the data from the DMAengine, and does not care
where that data has come from.  So, dma masks for peripheral devices are
meaningless (and wrong.)

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

* [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
  2012-04-18 10:09 ` Russell King - ARM Linux
@ 2012-04-18 10:10   ` Russell King
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:10 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap, linux-mmc; +Cc: Tony Lindgren

Using coherent DMA memory with the OMAP DMA engine results in
unpredictable behaviour due to memory ordering issues; as things stand,
there is no guarantee that data written to coherent DMA memory will be
visible to the DMA hardware.

This is because the OMAP dma_write() accessor contains no barriers,
necessary on ARMv6 and above.  The effect of this can be seen in comments
in the OMAP serial driver, which incorrectly talks about cache flushing
for the coherent DMA stuff.

Rather than adding barriers to the accessors, add it in the DMA support
code just before we enable DMA, and just after we disable DMA.  This
avoids having barriers for every DMA register access.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 arch/arm/plat-omap/dma.c |   14 ++++++++++++++
 1 files changed, 14 insertions(+), 0 deletions(-)

diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c
index ecdb3da..c58d896 100644
--- a/arch/arm/plat-omap/dma.c
+++ b/arch/arm/plat-omap/dma.c
@@ -916,6 +916,13 @@ void omap_start_dma(int lch)
 			l |= OMAP_DMA_CCR_BUFFERING_DISABLE;
 	l |= OMAP_DMA_CCR_EN;
 
+	/*
+	 * As dma_write() uses IO accessors which are weakly ordered, there
+	 * is no guarantee that data in coherent DMA memory will be visible
+	 * to the DMA device.  Add a memory barrier here to ensure that any
+	 * such data is visible prior to enabling DMA.
+	 */
+	mb();
 	p->dma_write(l, CCR, lch);
 
 	dma_chan[lch].flags |= OMAP_DMA_ACTIVE;
@@ -965,6 +972,13 @@ void omap_stop_dma(int lch)
 		p->dma_write(l, CCR, lch);
 	}
 
+	/*
+	 * Ensure that data transferred by DMA is visible to any access
+	 * after DMA has been disabled.  This is important for coherent
+	 * DMA regions.
+	 */
+	mb();
+
 	if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) {
 		int next_lch, cur_lch = lch;
 		char dma_chan_link_map[dma_lch_count];
-- 
1.7.4.4


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

* [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
@ 2012-04-18 10:10   ` Russell King
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:10 UTC (permalink / raw)
  To: linux-arm-kernel

Using coherent DMA memory with the OMAP DMA engine results in
unpredictable behaviour due to memory ordering issues; as things stand,
there is no guarantee that data written to coherent DMA memory will be
visible to the DMA hardware.

This is because the OMAP dma_write() accessor contains no barriers,
necessary on ARMv6 and above.  The effect of this can be seen in comments
in the OMAP serial driver, which incorrectly talks about cache flushing
for the coherent DMA stuff.

Rather than adding barriers to the accessors, add it in the DMA support
code just before we enable DMA, and just after we disable DMA.  This
avoids having barriers for every DMA register access.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 arch/arm/plat-omap/dma.c |   14 ++++++++++++++
 1 files changed, 14 insertions(+), 0 deletions(-)

diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c
index ecdb3da..c58d896 100644
--- a/arch/arm/plat-omap/dma.c
+++ b/arch/arm/plat-omap/dma.c
@@ -916,6 +916,13 @@ void omap_start_dma(int lch)
 			l |= OMAP_DMA_CCR_BUFFERING_DISABLE;
 	l |= OMAP_DMA_CCR_EN;
 
+	/*
+	 * As dma_write() uses IO accessors which are weakly ordered, there
+	 * is no guarantee that data in coherent DMA memory will be visible
+	 * to the DMA device.  Add a memory barrier here to ensure that any
+	 * such data is visible prior to enabling DMA.
+	 */
+	mb();
 	p->dma_write(l, CCR, lch);
 
 	dma_chan[lch].flags |= OMAP_DMA_ACTIVE;
@@ -965,6 +972,13 @@ void omap_stop_dma(int lch)
 		p->dma_write(l, CCR, lch);
 	}
 
+	/*
+	 * Ensure that data transferred by DMA is visible to any access
+	 * after DMA has been disabled.  This is important for coherent
+	 * DMA regions.
+	 */
+	mb();
+
 	if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) {
 		int next_lch, cur_lch = lch;
 		char dma_chan_link_map[dma_lch_count];
-- 
1.7.4.4

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

* [PATCH 2/8] dmaengine: amba-pl08x: ensure physical channels are properly held
  2012-04-18 10:09 ` Russell King - ARM Linux
@ 2012-04-18 10:10   ` Russell King
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:10 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap, linux-mmc; +Cc: Vinod Koul, Dan Williams

Ensure that physical channels are held while there are descriptors for
them to process.  This is needed when we split the pending queue into
separate pending and issued queues.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/dma/amba-pl08x.c |   10 ++++++----
 1 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
index c301a8e..2e74cca 100644
--- a/drivers/dma/amba-pl08x.c
+++ b/drivers/dma/amba-pl08x.c
@@ -855,6 +855,8 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
 	struct pl08x_phy_chan *ch;
 	int ret;
 
+	plchan->phychan_hold++;
+
 	/* Check if we already have a channel */
 	if (plchan->phychan) {
 		ch = plchan->phychan;
@@ -863,6 +865,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
 
 	ch = pl08x_get_phy_channel(pl08x, plchan);
 	if (!ch) {
+		plchan->phychan_hold--;
 		/* No physical channel available, cope with it */
 		dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name);
 		return -EBUSY;
@@ -876,6 +879,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
 	if (plchan->slave && pl08x->pd->get_signal) {
 		ret = pl08x->pd->get_signal(plchan);
 		if (ret < 0) {
+			plchan->phychan_hold--;
 			dev_dbg(&pl08x->adev->dev,
 				"unable to use physical channel %d for transfer on %s due to platform restrictions\n",
 				ch->id, plchan->name);
@@ -899,8 +903,6 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
 	else if (txd->direction == DMA_DEV_TO_MEM)
 		txd->ccfg |= ch->signal << PL080_CONFIG_SRC_SEL_SHIFT;
 
-	plchan->phychan_hold++;
-
 	return 0;
 }
 
@@ -938,8 +940,6 @@ static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx)
 		/* Do this memcpy whenever there is a channel ready */
 		plchan->state = PL08X_CHAN_WAITING;
 		plchan->waiting = txd;
-	} else {
-		plchan->phychan_hold--;
 	}
 
 	spin_unlock_irqrestore(&plchan->lock, flags);
@@ -1428,6 +1428,7 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 			 * Mark physical channel as free and free any slave
 			 * signal
 			 */
+			plchan->phychan_hold = 0;
 			release_phy_channel(plchan);
 		}
 		/* Dequeue jobs and free LLIs */
@@ -1529,6 +1530,7 @@ static void pl08x_tasklet(unsigned long data)
 	if (txd) {
 		/* Update last completed */
 		dma_cookie_complete(&txd->tx);
+		plchan->phychan_hold--;
 	}
 
 	/* If a new descriptor is queued, set it up plchan->at is NULL here */
-- 
1.7.4.4


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

* [PATCH 2/8] dmaengine: amba-pl08x: ensure physical channels are properly held
@ 2012-04-18 10:10   ` Russell King
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:10 UTC (permalink / raw)
  To: linux-arm-kernel

Ensure that physical channels are held while there are descriptors for
them to process.  This is needed when we split the pending queue into
separate pending and issued queues.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/dma/amba-pl08x.c |   10 ++++++----
 1 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
index c301a8e..2e74cca 100644
--- a/drivers/dma/amba-pl08x.c
+++ b/drivers/dma/amba-pl08x.c
@@ -855,6 +855,8 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
 	struct pl08x_phy_chan *ch;
 	int ret;
 
+	plchan->phychan_hold++;
+
 	/* Check if we already have a channel */
 	if (plchan->phychan) {
 		ch = plchan->phychan;
@@ -863,6 +865,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
 
 	ch = pl08x_get_phy_channel(pl08x, plchan);
 	if (!ch) {
+		plchan->phychan_hold--;
 		/* No physical channel available, cope with it */
 		dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name);
 		return -EBUSY;
@@ -876,6 +879,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
 	if (plchan->slave && pl08x->pd->get_signal) {
 		ret = pl08x->pd->get_signal(plchan);
 		if (ret < 0) {
+			plchan->phychan_hold--;
 			dev_dbg(&pl08x->adev->dev,
 				"unable to use physical channel %d for transfer on %s due to platform restrictions\n",
 				ch->id, plchan->name);
@@ -899,8 +903,6 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
 	else if (txd->direction == DMA_DEV_TO_MEM)
 		txd->ccfg |= ch->signal << PL080_CONFIG_SRC_SEL_SHIFT;
 
-	plchan->phychan_hold++;
-
 	return 0;
 }
 
@@ -938,8 +940,6 @@ static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx)
 		/* Do this memcpy whenever there is a channel ready */
 		plchan->state = PL08X_CHAN_WAITING;
 		plchan->waiting = txd;
-	} else {
-		plchan->phychan_hold--;
 	}
 
 	spin_unlock_irqrestore(&plchan->lock, flags);
@@ -1428,6 +1428,7 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 			 * Mark physical channel as free and free any slave
 			 * signal
 			 */
+			plchan->phychan_hold = 0;
 			release_phy_channel(plchan);
 		}
 		/* Dequeue jobs and free LLIs */
@@ -1529,6 +1530,7 @@ static void pl08x_tasklet(unsigned long data)
 	if (txd) {
 		/* Update last completed */
 		dma_cookie_complete(&txd->tx);
+		plchan->phychan_hold--;
 	}
 
 	/* If a new descriptor is queued, set it up plchan->at is NULL here */
-- 
1.7.4.4

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

* [PATCH 3/8] dmaengine: split out virtual channel DMA support from sa11x0 driver
  2012-04-18 10:09 ` Russell King - ARM Linux
@ 2012-04-18 10:11   ` Russell King
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap, linux-mmc; +Cc: Vinod Koul, Dan Williams

Split the virtual slave channel DMA support from the sa11x0 driver so
this code can be shared with other slave DMA engine drivers.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/dma/Kconfig      |    4 +
 drivers/dma/Makefile     |    1 +
 drivers/dma/sa11x0-dma.c |  249 ++++++++++++++-------------------------------
 drivers/dma/virt-dma.c   |   99 ++++++++++++++++++
 drivers/dma/virt-dma.h   |  138 +++++++++++++++++++++++++
 5 files changed, 320 insertions(+), 171 deletions(-)
 create mode 100644 drivers/dma/virt-dma.c
 create mode 100644 drivers/dma/virt-dma.h

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index cf9da36..5828ac4 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -255,6 +255,7 @@ config DMA_SA11X0
 	tristate "SA-11x0 DMA support"
 	depends on ARCH_SA1100
 	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
 	help
 	  Support the DMA engine found on Intel StrongARM SA-1100 and
 	  SA-1110 SoCs.  This DMA engine can only be used with on-chip
@@ -263,6 +264,9 @@ config DMA_SA11X0
 config DMA_ENGINE
 	bool
 
+config DMA_VIRTUAL_CHANNELS
+	tristate
+
 comment "DMA Clients"
 	depends on DMA_ENGINE
 
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 86b795b..fc05f7d 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -2,6 +2,7 @@ ccflags-$(CONFIG_DMADEVICES_DEBUG)  := -DDEBUG
 ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG
 
 obj-$(CONFIG_DMA_ENGINE) += dmaengine.o
+obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o
 obj-$(CONFIG_NET_DMA) += iovlock.o
 obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o
 obj-$(CONFIG_DMATEST) += dmatest.o
diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c
index ec78cce..5f1d2e6 100644
--- a/drivers/dma/sa11x0-dma.c
+++ b/drivers/dma/sa11x0-dma.c
@@ -21,6 +21,8 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 
+#include "virt-dma.h"
+
 #define NR_PHY_CHAN	6
 #define DMA_ALIGN	3
 #define DMA_MAX_SIZE	0x1fff
@@ -72,12 +74,11 @@ struct sa11x0_dma_sg {
 };
 
 struct sa11x0_dma_desc {
-	struct dma_async_tx_descriptor tx;
+	struct virt_dma_desc	vd;
+
 	u32			ddar;
 	size_t			size;
 
-	/* maybe protected by c->lock */
-	struct list_head	node;
 	unsigned		sglen;
 	struct sa11x0_dma_sg	sg[0];
 };
@@ -85,15 +86,11 @@ struct sa11x0_dma_desc {
 struct sa11x0_dma_phy;
 
 struct sa11x0_dma_chan {
-	struct dma_chan		chan;
-	spinlock_t		lock;
-	dma_cookie_t		lc;
+	struct virt_dma_chan	vc;
 
-	/* protected by c->lock */
+	/* protected by c->vc.lock */
 	struct sa11x0_dma_phy	*phy;
 	enum dma_status		status;
-	struct list_head	desc_submitted;
-	struct list_head	desc_issued;
 
 	/* protected by d->lock */
 	struct list_head	node;
@@ -109,7 +106,7 @@ struct sa11x0_dma_phy {
 
 	struct sa11x0_dma_chan	*vchan;
 
-	/* Protected by c->lock */
+	/* Protected by c->vc.lock */
 	unsigned		sg_load;
 	struct sa11x0_dma_desc	*txd_load;
 	unsigned		sg_done;
@@ -127,13 +124,12 @@ struct sa11x0_dma_dev {
 	spinlock_t		lock;
 	struct tasklet_struct	task;
 	struct list_head	chan_pending;
-	struct list_head	desc_complete;
 	struct sa11x0_dma_phy	phy[NR_PHY_CHAN];
 };
 
 static struct sa11x0_dma_chan *to_sa11x0_dma_chan(struct dma_chan *chan)
 {
-	return container_of(chan, struct sa11x0_dma_chan, chan);
+	return container_of(chan, struct sa11x0_dma_chan, vc.chan);
 }
 
 static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev)
@@ -141,27 +137,26 @@ static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev)
 	return container_of(dmadev, struct sa11x0_dma_dev, slave);
 }
 
-static struct sa11x0_dma_desc *to_sa11x0_dma_tx(struct dma_async_tx_descriptor *tx)
+static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c)
 {
-	return container_of(tx, struct sa11x0_dma_desc, tx);
+	struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
+
+	return vd ? container_of(vd, struct sa11x0_dma_desc, vd) : NULL;
 }
 
-static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c)
+static void sa11x0_dma_free_desc(struct virt_dma_desc *vd)
 {
-	if (list_empty(&c->desc_issued))
-		return NULL;
-
-	return list_first_entry(&c->desc_issued, struct sa11x0_dma_desc, node);
+	kfree(container_of(vd, struct sa11x0_dma_desc, vd));
 }
 
 static void sa11x0_dma_start_desc(struct sa11x0_dma_phy *p, struct sa11x0_dma_desc *txd)
 {
-	list_del(&txd->node);
+	list_del(&txd->vd.node);
 	p->txd_load = txd;
 	p->sg_load = 0;
 
 	dev_vdbg(p->dev->slave.dev, "pchan %u: txd %p[%x]: starting: DDAR:%x\n",
-		p->num, txd, txd->tx.cookie, txd->ddar);
+		p->num, &txd->vd, txd->vd.tx.cookie, txd->ddar);
 }
 
 static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p,
@@ -229,21 +224,13 @@ static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p,
 	struct sa11x0_dma_desc *txd = p->txd_done;
 
 	if (++p->sg_done == txd->sglen) {
-		struct sa11x0_dma_dev *d = p->dev;
-
-		dev_vdbg(d->slave.dev, "pchan %u: txd %p[%x]: completed\n",
-			p->num, p->txd_done, p->txd_done->tx.cookie);
-
-		c->lc = txd->tx.cookie;
-
-		spin_lock(&d->lock);
-		list_add_tail(&txd->node, &d->desc_complete);
-		spin_unlock(&d->lock);
+		vchan_cookie_complete(&txd->vd);
 
 		p->sg_done = 0;
 		p->txd_done = p->txd_load;
 
-		tasklet_schedule(&d->task);
+		if (!p->txd_done)
+			tasklet_schedule(&p->dev->task);
 	}
 
 	sa11x0_dma_start_sg(p, c);
@@ -280,7 +267,7 @@ static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id)
 	if (c) {
 		unsigned long flags;
 
-		spin_lock_irqsave(&c->lock, flags);
+		spin_lock_irqsave(&c->vc.lock, flags);
 		/*
 		 * Now that we're holding the lock, check that the vchan
 		 * really is associated with this pchan before touching the
@@ -294,7 +281,7 @@ static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id)
 			if (dcsr & DCSR_DONEB)
 				sa11x0_dma_complete(p, c);
 		}
-		spin_unlock_irqrestore(&c->lock, flags);
+		spin_unlock_irqrestore(&c->vc.lock, flags);
 	}
 
 	return IRQ_HANDLED;
@@ -332,28 +319,15 @@ static void sa11x0_dma_tasklet(unsigned long arg)
 	struct sa11x0_dma_dev *d = (struct sa11x0_dma_dev *)arg;
 	struct sa11x0_dma_phy *p;
 	struct sa11x0_dma_chan *c;
-	struct sa11x0_dma_desc *txd, *txn;
-	LIST_HEAD(head);
 	unsigned pch, pch_alloc = 0;
 
 	dev_dbg(d->slave.dev, "tasklet enter\n");
 
-	/* Get the completed tx descriptors */
-	spin_lock_irq(&d->lock);
-	list_splice_init(&d->desc_complete, &head);
-	spin_unlock_irq(&d->lock);
-
-	list_for_each_entry(txd, &head, node) {
-		c = to_sa11x0_dma_chan(txd->tx.chan);
-
-		dev_dbg(d->slave.dev, "vchan %p: txd %p[%x] completed\n",
-			c, txd, txd->tx.cookie);
-
-		spin_lock_irq(&c->lock);
+	list_for_each_entry(c, &d->slave.channels, vc.chan.device_node) {
+		spin_lock_irq(&c->vc.lock);
 		p = c->phy;
-		if (p) {
-			if (!p->txd_done)
-				sa11x0_dma_start_txd(c);
+		if (p && !p->txd_done) {
+			sa11x0_dma_start_txd(c);
 			if (!p->txd_done) {
 				/* No current txd associated with this channel */
 				dev_dbg(d->slave.dev, "pchan %u: free\n", p->num);
@@ -363,7 +337,7 @@ static void sa11x0_dma_tasklet(unsigned long arg)
 				p->vchan = NULL;
 			}
 		}
-		spin_unlock_irq(&c->lock);
+		spin_unlock_irq(&c->vc.lock);
 	}
 
 	spin_lock_irq(&d->lock);
@@ -380,7 +354,7 @@ static void sa11x0_dma_tasklet(unsigned long arg)
 			/* Mark this channel allocated */
 			p->vchan = c;
 
-			dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, c);
+			dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, &c->vc);
 		}
 	}
 	spin_unlock_irq(&d->lock);
@@ -390,42 +364,18 @@ static void sa11x0_dma_tasklet(unsigned long arg)
 			p = &d->phy[pch];
 			c = p->vchan;
 
-			spin_lock_irq(&c->lock);
+			spin_lock_irq(&c->vc.lock);
 			c->phy = p;
 
 			sa11x0_dma_start_txd(c);
-			spin_unlock_irq(&c->lock);
+			spin_unlock_irq(&c->vc.lock);
 		}
 	}
 
-	/* Now free the completed tx descriptor, and call their callbacks */
-	list_for_each_entry_safe(txd, txn, &head, node) {
-		dma_async_tx_callback callback = txd->tx.callback;
-		void *callback_param = txd->tx.callback_param;
-
-		dev_dbg(d->slave.dev, "txd %p[%x]: callback and free\n",
-			txd, txd->tx.cookie);
-
-		kfree(txd);
-
-		if (callback)
-			callback(callback_param);
-	}
-
 	dev_dbg(d->slave.dev, "tasklet exit\n");
 }
 
 
-static void sa11x0_dma_desc_free(struct sa11x0_dma_dev *d, struct list_head *head)
-{
-	struct sa11x0_dma_desc *txd, *txn;
-
-	list_for_each_entry_safe(txd, txn, head, node) {
-		dev_dbg(d->slave.dev, "txd %p: freeing\n", txd);
-		kfree(txd);
-	}
-}
-
 static int sa11x0_dma_alloc_chan_resources(struct dma_chan *chan)
 {
 	return 0;
@@ -436,18 +386,12 @@ static void sa11x0_dma_free_chan_resources(struct dma_chan *chan)
 	struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
 	struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
 	unsigned long flags;
-	LIST_HEAD(head);
 
-	spin_lock_irqsave(&c->lock, flags);
-	spin_lock(&d->lock);
+	spin_lock_irqsave(&d->lock, flags);
 	list_del_init(&c->node);
-	spin_unlock(&d->lock);
-
-	list_splice_tail_init(&c->desc_submitted, &head);
-	list_splice_tail_init(&c->desc_issued, &head);
-	spin_unlock_irqrestore(&c->lock, flags);
+	spin_unlock_irqrestore(&d->lock, flags);
 
-	sa11x0_dma_desc_free(d, &head);
+	vchan_free_chan_resources(&c->vc);
 }
 
 static dma_addr_t sa11x0_dma_pos(struct sa11x0_dma_phy *p)
@@ -473,21 +417,15 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan,
 	struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
 	struct sa11x0_dma_phy *p;
 	struct sa11x0_dma_desc *txd;
-	dma_cookie_t last_used, last_complete;
 	unsigned long flags;
 	enum dma_status ret;
 	size_t bytes = 0;
 
-	last_used = c->chan.cookie;
-	last_complete = c->lc;
-
-	ret = dma_async_is_complete(cookie, last_complete, last_used);
-	if (ret == DMA_SUCCESS) {
-		dma_set_tx_state(state, last_complete, last_used, 0);
+	ret = dma_cookie_status(&c->vc.chan, cookie, state);
+	if (ret == DMA_SUCCESS)
 		return ret;
-	}
 
-	spin_lock_irqsave(&c->lock, flags);
+	spin_lock_irqsave(&c->vc.lock, flags);
 	p = c->phy;
 	ret = c->status;
 	if (p) {
@@ -524,12 +462,13 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan,
 		if (txd != p->txd_load && p->txd_load)
 			bytes += p->txd_load->size;
 	}
-	list_for_each_entry(txd, &c->desc_issued, node) {
+	list_for_each_entry(txd, &c->vc.desc_issued, vd.node) {
 		bytes += txd->size;
 	}
-	spin_unlock_irqrestore(&c->lock, flags);
+	spin_unlock_irqrestore(&c->vc.lock, flags);
 
-	dma_set_tx_state(state, last_complete, last_used, bytes);
+	if (state)
+		state->residue = bytes;
 
 	dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", bytes);
 
@@ -547,40 +486,20 @@ static void sa11x0_dma_issue_pending(struct dma_chan *chan)
 	struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
 	unsigned long flags;
 
-	spin_lock_irqsave(&c->lock, flags);
-	list_splice_tail_init(&c->desc_submitted, &c->desc_issued);
-	if (!list_empty(&c->desc_issued)) {
-		spin_lock(&d->lock);
-		if (!c->phy && list_empty(&c->node)) {
-			list_add_tail(&c->node, &d->chan_pending);
-			tasklet_schedule(&d->task);
-			dev_dbg(d->slave.dev, "vchan %p: issued\n", c);
+	spin_lock_irqsave(&c->vc.lock, flags);
+	if (vchan_issue_pending(&c->vc)) {
+		if (!c->phy) {
+			spin_lock(&d->lock);
+			if (list_empty(&c->node)) {
+				list_add_tail(&c->node, &d->chan_pending);
+				tasklet_schedule(&d->task);
+				dev_dbg(d->slave.dev, "vchan %p: issued\n", &c->vc);
+			}
+			spin_unlock(&d->lock);
 		}
-		spin_unlock(&d->lock);
 	} else
-		dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", c);
-	spin_unlock_irqrestore(&c->lock, flags);
-}
-
-static dma_cookie_t sa11x0_dma_tx_submit(struct dma_async_tx_descriptor *tx)
-{
-	struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(tx->chan);
-	struct sa11x0_dma_desc *txd = to_sa11x0_dma_tx(tx);
-	unsigned long flags;
-
-	spin_lock_irqsave(&c->lock, flags);
-	c->chan.cookie += 1;
-	if (c->chan.cookie < 0)
-		c->chan.cookie = 1;
-	txd->tx.cookie = c->chan.cookie;
-
-	list_add_tail(&txd->node, &c->desc_submitted);
-	spin_unlock_irqrestore(&c->lock, flags);
-
-	dev_dbg(tx->chan->device->dev, "vchan %p: txd %p[%x]: submitted\n",
-		c, txd, txd->tx.cookie);
-
-	return txd->tx.cookie;
+		dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", &c->vc);
+	spin_unlock_irqrestore(&c->vc.lock, flags);
 }
 
 static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
@@ -596,7 +515,7 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
 	/* SA11x0 channels can only operate in their native direction */
 	if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) {
 		dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n",
-			c, c->ddar, dir);
+			&c->vc, c->ddar, dir);
 		return NULL;
 	}
 
@@ -612,14 +531,14 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
 			j += DIV_ROUND_UP(len, DMA_MAX_SIZE & ~DMA_ALIGN) - 1;
 		if (addr & DMA_ALIGN) {
 			dev_dbg(chan->device->dev, "vchan %p: bad buffer alignment: %08x\n",
-				c, addr);
+				&c->vc, addr);
 			return NULL;
 		}
 	}
 
 	txd = kzalloc(sizeof(*txd) + j * sizeof(txd->sg[0]), GFP_ATOMIC);
 	if (!txd) {
-		dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", c);
+		dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", &c->vc);
 		return NULL;
 	}
 
@@ -655,17 +574,14 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
 		} while (len);
 	}
 
-	dma_async_tx_descriptor_init(&txd->tx, &c->chan);
-	txd->tx.flags = flags;
-	txd->tx.tx_submit = sa11x0_dma_tx_submit;
 	txd->ddar = c->ddar;
 	txd->size = size;
 	txd->sglen = j;
 
 	dev_dbg(chan->device->dev, "vchan %p: txd %p: size %u nr %u\n",
-		c, txd, txd->size, txd->sglen);
+		&c->vc, &txd->vd, txd->size, txd->sglen);
 
-	return &txd->tx;
+	return vchan_tx_prep(&c->vc, &txd->vd, flags);
 }
 
 static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg)
@@ -695,8 +611,8 @@ static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_c
 	if (maxburst == 8)
 		ddar |= DDAR_BS;
 
-	dev_dbg(c->chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n",
-		c, addr, width, maxburst);
+	dev_dbg(c->vc.chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n",
+		&c->vc, addr, width, maxburst);
 
 	c->ddar = ddar | (addr & 0xf0000000) | (addr & 0x003ffffc) << 6;
 
@@ -718,16 +634,13 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 		return sa11x0_dma_slave_config(c, (struct dma_slave_config *)arg);
 
 	case DMA_TERMINATE_ALL:
-		dev_dbg(d->slave.dev, "vchan %p: terminate all\n", c);
+		dev_dbg(d->slave.dev, "vchan %p: terminate all\n", &c->vc);
 		/* Clear the tx descriptor lists */
-		spin_lock_irqsave(&c->lock, flags);
-		list_splice_tail_init(&c->desc_submitted, &head);
-		list_splice_tail_init(&c->desc_issued, &head);
+		spin_lock_irqsave(&c->vc.lock, flags);
+		vchan_get_all_descriptors(&c->vc, &head);
 
 		p = c->phy;
 		if (p) {
-			struct sa11x0_dma_desc *txd, *txn;
-
 			dev_dbg(d->slave.dev, "pchan %u: terminating\n", p->num);
 			/* vchan is assigned to a pchan - stop the channel */
 			writel(DCSR_RUN | DCSR_IE |
@@ -735,17 +648,13 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 				DCSR_STRTB | DCSR_DONEB,
 				p->base + DMA_DCSR_C);
 
-			list_for_each_entry_safe(txd, txn, &d->desc_complete, node)
-				if (txd->tx.chan == &c->chan)
-					list_move(&txd->node, &head);
-
 			if (p->txd_load) {
 				if (p->txd_load != p->txd_done)
-					list_add_tail(&p->txd_load->node, &head);
+					list_add_tail(&p->txd_load->vd.node, &head);
 				p->txd_load = NULL;
 			}
 			if (p->txd_done) {
-				list_add_tail(&p->txd_done->node, &head);
+				list_add_tail(&p->txd_done->vd.node, &head);
 				p->txd_done = NULL;
 			}
 			c->phy = NULL;
@@ -754,14 +663,14 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 			spin_unlock(&d->lock);
 			tasklet_schedule(&d->task);
 		}
-		spin_unlock_irqrestore(&c->lock, flags);
-		sa11x0_dma_desc_free(d, &head);
+		spin_unlock_irqrestore(&c->vc.lock, flags);
+		vchan_dma_desc_free_list(&c->vc, &head);
 		ret = 0;
 		break;
 
 	case DMA_PAUSE:
-		dev_dbg(d->slave.dev, "vchan %p: pause\n", c);
-		spin_lock_irqsave(&c->lock, flags);
+		dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc);
+		spin_lock_irqsave(&c->vc.lock, flags);
 		if (c->status == DMA_IN_PROGRESS) {
 			c->status = DMA_PAUSED;
 
@@ -774,26 +683,26 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 				spin_unlock(&d->lock);
 			}
 		}
-		spin_unlock_irqrestore(&c->lock, flags);
+		spin_unlock_irqrestore(&c->vc.lock, flags);
 		ret = 0;
 		break;
 
 	case DMA_RESUME:
-		dev_dbg(d->slave.dev, "vchan %p: resume\n", c);
-		spin_lock_irqsave(&c->lock, flags);
+		dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc);
+		spin_lock_irqsave(&c->vc.lock, flags);
 		if (c->status == DMA_PAUSED) {
 			c->status = DMA_IN_PROGRESS;
 
 			p = c->phy;
 			if (p) {
 				writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_S);
-			} else if (!list_empty(&c->desc_issued)) {
+			} else if (!list_empty(&c->vc.desc_issued)) {
 				spin_lock(&d->lock);
 				list_add_tail(&c->node, &d->chan_pending);
 				spin_unlock(&d->lock);
 			}
 		}
-		spin_unlock_irqrestore(&c->lock, flags);
+		spin_unlock_irqrestore(&c->vc.lock, flags);
 		ret = 0;
 		break;
 
@@ -853,15 +762,13 @@ static int __devinit sa11x0_dma_init_dmadev(struct dma_device *dmadev,
 			return -ENOMEM;
 		}
 
-		c->chan.device = dmadev;
 		c->status = DMA_IN_PROGRESS;
 		c->ddar = chan_desc[i].ddar;
 		c->name = chan_desc[i].name;
-		spin_lock_init(&c->lock);
-		INIT_LIST_HEAD(&c->desc_submitted);
-		INIT_LIST_HEAD(&c->desc_issued);
 		INIT_LIST_HEAD(&c->node);
-		list_add_tail(&c->chan.device_node, &dmadev->channels);
+
+		c->vc.desc_free = sa11x0_dma_free_desc;
+		vchan_init(&c->vc, dmadev);
 	}
 
 	return dma_async_device_register(dmadev);
@@ -890,8 +797,9 @@ static void sa11x0_dma_free_channels(struct dma_device *dmadev)
 {
 	struct sa11x0_dma_chan *c, *cn;
 
-	list_for_each_entry_safe(c, cn, &dmadev->channels, chan.device_node) {
-		list_del(&c->chan.device_node);
+	list_for_each_entry_safe(c, cn, &dmadev->channels, vc.chan.device_node) {
+		list_del(&c->vc.chan.device_node);
+		tasklet_kill(&c->vc.task);
 		kfree(c);
 	}
 }
@@ -915,7 +823,6 @@ static int __devinit sa11x0_dma_probe(struct platform_device *pdev)
 
 	spin_lock_init(&d->lock);
 	INIT_LIST_HEAD(&d->chan_pending);
-	INIT_LIST_HEAD(&d->desc_complete);
 
 	d->base = ioremap(res->start, resource_size(res));
 	if (!d->base) {
diff --git a/drivers/dma/virt-dma.c b/drivers/dma/virt-dma.c
new file mode 100644
index 0000000..bd85b05
--- /dev/null
+++ b/drivers/dma/virt-dma.c
@@ -0,0 +1,99 @@
+/*
+ * Virtual DMA channel support for DMAengine
+ *
+ * Copyright (C) 2012 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+
+static struct virt_dma_desc *to_virt_desc(struct dma_async_tx_descriptor *tx)
+{
+	return container_of(tx, struct virt_dma_desc, tx);
+}
+
+dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+	struct virt_dma_chan *vc = to_virt_chan(tx->chan);
+	struct virt_dma_desc *vd = to_virt_desc(tx);
+	unsigned long flags;
+	dma_cookie_t cookie;
+
+	spin_lock_irqsave(&vc->lock, flags);
+	cookie = dma_cookie_assign(tx);
+
+	list_add_tail(&vd->node, &vc->desc_submitted);
+	spin_unlock_irqrestore(&vc->lock, flags);
+
+	dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted\n",
+		vc, vd, cookie);
+
+	return cookie;
+}
+EXPORT_SYMBOL_GPL(vchan_tx_submit);
+
+/*
+ * This tasklet handles the completion of a DMA descriptor by
+ * calling its callback and freeing it.
+ */
+static void vchan_complete(unsigned long arg)
+{
+	struct virt_dma_chan *vc = (struct virt_dma_chan *)arg;
+	LIST_HEAD(head);
+
+	spin_lock_irq(&vc->lock);
+	list_splice_tail_init(&vc->desc_completed, &head);
+	spin_unlock_irq(&vc->lock);
+
+	while (!list_empty(&head)) {
+		struct virt_dma_desc *vd = list_first_entry(&head,
+				struct virt_dma_desc, node);
+		dma_async_tx_callback cb = vd->tx.callback;
+		void *cb_data = vd->tx.callback_param;
+
+		list_del(&vd->node);
+
+		vc->desc_free(vd);
+
+		if (cb)
+			cb(cb_data);
+	}
+}
+
+void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head)
+{
+	while (!list_empty(head)) {
+		struct virt_dma_desc *vd = list_first_entry(head,
+			struct virt_dma_desc, node);
+		list_del(&vd->node);
+		dev_dbg(vc->chan.device->dev, "txd %p: freeing\n", vd);
+		vc->desc_free(vd);
+	}
+}
+EXPORT_SYMBOL_GPL(vchan_dma_desc_free_list);
+
+void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
+{
+	dma_cookie_init(&vc->chan);
+
+	spin_lock_init(&vc->lock);
+	INIT_LIST_HEAD(&vc->desc_submitted);
+	INIT_LIST_HEAD(&vc->desc_issued);
+	INIT_LIST_HEAD(&vc->desc_completed);
+
+	tasklet_init(&vc->task, vchan_complete, (unsigned long)vc);
+
+	vc->chan.device = dmadev;
+	list_add_tail(&vc->chan.device_node, &dmadev->channels);
+}
+EXPORT_SYMBOL_GPL(vchan_init);
+
+MODULE_AUTHOR("Russell King");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/virt-dma.h b/drivers/dma/virt-dma.h
new file mode 100644
index 0000000..825bb96
--- /dev/null
+++ b/drivers/dma/virt-dma.h
@@ -0,0 +1,138 @@
+/*
+ * Virtual DMA channel support for DMAengine
+ *
+ * Copyright (C) 2012 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef VIRT_DMA_H
+#define VIRT_DMA_H
+
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+
+#include "dmaengine.h"
+
+struct virt_dma_desc {
+	struct dma_async_tx_descriptor tx;
+	/* protected by vc.lock */
+	struct list_head node;
+};
+
+struct virt_dma_chan {
+	struct dma_chan	chan;
+	struct tasklet_struct task;
+	void (*desc_free)(struct virt_dma_desc *);
+
+	spinlock_t lock;
+
+	/* protected by vc.lock */
+	struct list_head desc_submitted;
+	struct list_head desc_issued;
+	struct list_head desc_completed;
+};
+
+static inline struct virt_dma_chan *to_virt_chan(struct dma_chan *chan)
+{
+	return container_of(chan, struct virt_dma_chan, chan);
+}
+
+void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head);
+
+void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev);
+
+/**
+ * vchan_tx_prep - prepare a descriptor
+ * vc: virtual channel allocating this descriptor
+ * vd: virtual descriptor to prepare
+ * tx_flags: flags argument passed in to prepare function
+ */
+static inline struct dma_async_tx_descriptor *vchan_tx_prep(struct virt_dma_chan *vc,
+	struct virt_dma_desc *vd, unsigned long tx_flags)
+{
+	extern dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *);
+
+	dma_async_tx_descriptor_init(&vd->tx, &vc->chan);
+	vd->tx.flags = tx_flags;
+	vd->tx.tx_submit = vchan_tx_submit;
+
+	return &vd->tx;
+}
+
+/**
+ * vchan_issue_pending - move submitted descriptors to issued list
+ * vc: virtual channel to update
+ *
+ * vc.lock must be held by caller
+ */
+static inline bool vchan_issue_pending(struct virt_dma_chan *vc)
+{
+	list_splice_tail_init(&vc->desc_submitted, &vc->desc_issued);
+	return !list_empty(&vc->desc_issued);
+}
+
+/**
+ * vchan_cookie_complete - report completion of a descriptor
+ * vd: virtual descriptor to update
+ *
+ * vc.lock must be held by caller
+ */
+static inline void vchan_cookie_complete(struct virt_dma_desc *vd)
+{
+	struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);
+
+	dma_cookie_complete(&vd->tx);
+	dev_vdbg(vc->chan.device->dev, "txd %p[%x]: marked complete\n",
+		vd, vd->tx.cookie);
+	list_add_tail(&vd->node, &vc->desc_completed);
+
+	tasklet_schedule(&vc->task);
+}
+
+/**
+ * vchan_next_desc - peek at the next descriptor to be processed
+ * vc: virtual channel to obtain descriptor from
+ *
+ * vc.lock must be held by caller
+ */
+static inline struct virt_dma_desc *vchan_next_desc(struct virt_dma_chan *vc)
+{
+	if (list_empty(&vc->desc_issued))
+		return NULL;
+
+	return list_first_entry(&vc->desc_issued, struct virt_dma_desc, node);
+}
+
+/**
+ * vchan_get_all_descriptors - obtain all submitted and issued descriptors
+ * vc: virtual channel to get descriptors from
+ * head: list of descriptors found
+ *
+ * vc.lock must be held by caller
+ *
+ * Removes all submitted and issued descriptors from internal lists, and
+ * provides a list of all descriptors found
+ */
+static inline void vchan_get_all_descriptors(struct virt_dma_chan *vc,
+	struct list_head *head)
+{
+	list_splice_tail_init(&vc->desc_submitted, head);
+	list_splice_tail_init(&vc->desc_issued, head);
+	list_splice_tail_init(&vc->desc_completed, head);
+}
+
+static inline void vchan_free_chan_resources(struct virt_dma_chan *vc)
+{
+	unsigned long flags;
+	LIST_HEAD(head);
+
+	spin_lock_irqsave(&vc->lock, flags);
+	vchan_get_all_descriptors(vc, &head);
+	spin_unlock_irqrestore(&vc->lock, flags);
+
+	vchan_dma_desc_free_list(vc, &head);
+}
+
+#endif
-- 
1.7.4.4


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

* [PATCH 3/8] dmaengine: split out virtual channel DMA support from sa11x0 driver
@ 2012-04-18 10:11   ` Russell King
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:11 UTC (permalink / raw)
  To: linux-arm-kernel

Split the virtual slave channel DMA support from the sa11x0 driver so
this code can be shared with other slave DMA engine drivers.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/dma/Kconfig      |    4 +
 drivers/dma/Makefile     |    1 +
 drivers/dma/sa11x0-dma.c |  249 ++++++++++++++-------------------------------
 drivers/dma/virt-dma.c   |   99 ++++++++++++++++++
 drivers/dma/virt-dma.h   |  138 +++++++++++++++++++++++++
 5 files changed, 320 insertions(+), 171 deletions(-)
 create mode 100644 drivers/dma/virt-dma.c
 create mode 100644 drivers/dma/virt-dma.h

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index cf9da36..5828ac4 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -255,6 +255,7 @@ config DMA_SA11X0
 	tristate "SA-11x0 DMA support"
 	depends on ARCH_SA1100
 	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
 	help
 	  Support the DMA engine found on Intel StrongARM SA-1100 and
 	  SA-1110 SoCs.  This DMA engine can only be used with on-chip
@@ -263,6 +264,9 @@ config DMA_SA11X0
 config DMA_ENGINE
 	bool
 
+config DMA_VIRTUAL_CHANNELS
+	tristate
+
 comment "DMA Clients"
 	depends on DMA_ENGINE
 
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 86b795b..fc05f7d 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -2,6 +2,7 @@ ccflags-$(CONFIG_DMADEVICES_DEBUG)  := -DDEBUG
 ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG
 
 obj-$(CONFIG_DMA_ENGINE) += dmaengine.o
+obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o
 obj-$(CONFIG_NET_DMA) += iovlock.o
 obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o
 obj-$(CONFIG_DMATEST) += dmatest.o
diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c
index ec78cce..5f1d2e6 100644
--- a/drivers/dma/sa11x0-dma.c
+++ b/drivers/dma/sa11x0-dma.c
@@ -21,6 +21,8 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 
+#include "virt-dma.h"
+
 #define NR_PHY_CHAN	6
 #define DMA_ALIGN	3
 #define DMA_MAX_SIZE	0x1fff
@@ -72,12 +74,11 @@ struct sa11x0_dma_sg {
 };
 
 struct sa11x0_dma_desc {
-	struct dma_async_tx_descriptor tx;
+	struct virt_dma_desc	vd;
+
 	u32			ddar;
 	size_t			size;
 
-	/* maybe protected by c->lock */
-	struct list_head	node;
 	unsigned		sglen;
 	struct sa11x0_dma_sg	sg[0];
 };
@@ -85,15 +86,11 @@ struct sa11x0_dma_desc {
 struct sa11x0_dma_phy;
 
 struct sa11x0_dma_chan {
-	struct dma_chan		chan;
-	spinlock_t		lock;
-	dma_cookie_t		lc;
+	struct virt_dma_chan	vc;
 
-	/* protected by c->lock */
+	/* protected by c->vc.lock */
 	struct sa11x0_dma_phy	*phy;
 	enum dma_status		status;
-	struct list_head	desc_submitted;
-	struct list_head	desc_issued;
 
 	/* protected by d->lock */
 	struct list_head	node;
@@ -109,7 +106,7 @@ struct sa11x0_dma_phy {
 
 	struct sa11x0_dma_chan	*vchan;
 
-	/* Protected by c->lock */
+	/* Protected by c->vc.lock */
 	unsigned		sg_load;
 	struct sa11x0_dma_desc	*txd_load;
 	unsigned		sg_done;
@@ -127,13 +124,12 @@ struct sa11x0_dma_dev {
 	spinlock_t		lock;
 	struct tasklet_struct	task;
 	struct list_head	chan_pending;
-	struct list_head	desc_complete;
 	struct sa11x0_dma_phy	phy[NR_PHY_CHAN];
 };
 
 static struct sa11x0_dma_chan *to_sa11x0_dma_chan(struct dma_chan *chan)
 {
-	return container_of(chan, struct sa11x0_dma_chan, chan);
+	return container_of(chan, struct sa11x0_dma_chan, vc.chan);
 }
 
 static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev)
@@ -141,27 +137,26 @@ static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev)
 	return container_of(dmadev, struct sa11x0_dma_dev, slave);
 }
 
-static struct sa11x0_dma_desc *to_sa11x0_dma_tx(struct dma_async_tx_descriptor *tx)
+static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c)
 {
-	return container_of(tx, struct sa11x0_dma_desc, tx);
+	struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
+
+	return vd ? container_of(vd, struct sa11x0_dma_desc, vd) : NULL;
 }
 
-static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c)
+static void sa11x0_dma_free_desc(struct virt_dma_desc *vd)
 {
-	if (list_empty(&c->desc_issued))
-		return NULL;
-
-	return list_first_entry(&c->desc_issued, struct sa11x0_dma_desc, node);
+	kfree(container_of(vd, struct sa11x0_dma_desc, vd));
 }
 
 static void sa11x0_dma_start_desc(struct sa11x0_dma_phy *p, struct sa11x0_dma_desc *txd)
 {
-	list_del(&txd->node);
+	list_del(&txd->vd.node);
 	p->txd_load = txd;
 	p->sg_load = 0;
 
 	dev_vdbg(p->dev->slave.dev, "pchan %u: txd %p[%x]: starting: DDAR:%x\n",
-		p->num, txd, txd->tx.cookie, txd->ddar);
+		p->num, &txd->vd, txd->vd.tx.cookie, txd->ddar);
 }
 
 static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p,
@@ -229,21 +224,13 @@ static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p,
 	struct sa11x0_dma_desc *txd = p->txd_done;
 
 	if (++p->sg_done == txd->sglen) {
-		struct sa11x0_dma_dev *d = p->dev;
-
-		dev_vdbg(d->slave.dev, "pchan %u: txd %p[%x]: completed\n",
-			p->num, p->txd_done, p->txd_done->tx.cookie);
-
-		c->lc = txd->tx.cookie;
-
-		spin_lock(&d->lock);
-		list_add_tail(&txd->node, &d->desc_complete);
-		spin_unlock(&d->lock);
+		vchan_cookie_complete(&txd->vd);
 
 		p->sg_done = 0;
 		p->txd_done = p->txd_load;
 
-		tasklet_schedule(&d->task);
+		if (!p->txd_done)
+			tasklet_schedule(&p->dev->task);
 	}
 
 	sa11x0_dma_start_sg(p, c);
@@ -280,7 +267,7 @@ static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id)
 	if (c) {
 		unsigned long flags;
 
-		spin_lock_irqsave(&c->lock, flags);
+		spin_lock_irqsave(&c->vc.lock, flags);
 		/*
 		 * Now that we're holding the lock, check that the vchan
 		 * really is associated with this pchan before touching the
@@ -294,7 +281,7 @@ static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id)
 			if (dcsr & DCSR_DONEB)
 				sa11x0_dma_complete(p, c);
 		}
-		spin_unlock_irqrestore(&c->lock, flags);
+		spin_unlock_irqrestore(&c->vc.lock, flags);
 	}
 
 	return IRQ_HANDLED;
@@ -332,28 +319,15 @@ static void sa11x0_dma_tasklet(unsigned long arg)
 	struct sa11x0_dma_dev *d = (struct sa11x0_dma_dev *)arg;
 	struct sa11x0_dma_phy *p;
 	struct sa11x0_dma_chan *c;
-	struct sa11x0_dma_desc *txd, *txn;
-	LIST_HEAD(head);
 	unsigned pch, pch_alloc = 0;
 
 	dev_dbg(d->slave.dev, "tasklet enter\n");
 
-	/* Get the completed tx descriptors */
-	spin_lock_irq(&d->lock);
-	list_splice_init(&d->desc_complete, &head);
-	spin_unlock_irq(&d->lock);
-
-	list_for_each_entry(txd, &head, node) {
-		c = to_sa11x0_dma_chan(txd->tx.chan);
-
-		dev_dbg(d->slave.dev, "vchan %p: txd %p[%x] completed\n",
-			c, txd, txd->tx.cookie);
-
-		spin_lock_irq(&c->lock);
+	list_for_each_entry(c, &d->slave.channels, vc.chan.device_node) {
+		spin_lock_irq(&c->vc.lock);
 		p = c->phy;
-		if (p) {
-			if (!p->txd_done)
-				sa11x0_dma_start_txd(c);
+		if (p && !p->txd_done) {
+			sa11x0_dma_start_txd(c);
 			if (!p->txd_done) {
 				/* No current txd associated with this channel */
 				dev_dbg(d->slave.dev, "pchan %u: free\n", p->num);
@@ -363,7 +337,7 @@ static void sa11x0_dma_tasklet(unsigned long arg)
 				p->vchan = NULL;
 			}
 		}
-		spin_unlock_irq(&c->lock);
+		spin_unlock_irq(&c->vc.lock);
 	}
 
 	spin_lock_irq(&d->lock);
@@ -380,7 +354,7 @@ static void sa11x0_dma_tasklet(unsigned long arg)
 			/* Mark this channel allocated */
 			p->vchan = c;
 
-			dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, c);
+			dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, &c->vc);
 		}
 	}
 	spin_unlock_irq(&d->lock);
@@ -390,42 +364,18 @@ static void sa11x0_dma_tasklet(unsigned long arg)
 			p = &d->phy[pch];
 			c = p->vchan;
 
-			spin_lock_irq(&c->lock);
+			spin_lock_irq(&c->vc.lock);
 			c->phy = p;
 
 			sa11x0_dma_start_txd(c);
-			spin_unlock_irq(&c->lock);
+			spin_unlock_irq(&c->vc.lock);
 		}
 	}
 
-	/* Now free the completed tx descriptor, and call their callbacks */
-	list_for_each_entry_safe(txd, txn, &head, node) {
-		dma_async_tx_callback callback = txd->tx.callback;
-		void *callback_param = txd->tx.callback_param;
-
-		dev_dbg(d->slave.dev, "txd %p[%x]: callback and free\n",
-			txd, txd->tx.cookie);
-
-		kfree(txd);
-
-		if (callback)
-			callback(callback_param);
-	}
-
 	dev_dbg(d->slave.dev, "tasklet exit\n");
 }
 
 
-static void sa11x0_dma_desc_free(struct sa11x0_dma_dev *d, struct list_head *head)
-{
-	struct sa11x0_dma_desc *txd, *txn;
-
-	list_for_each_entry_safe(txd, txn, head, node) {
-		dev_dbg(d->slave.dev, "txd %p: freeing\n", txd);
-		kfree(txd);
-	}
-}
-
 static int sa11x0_dma_alloc_chan_resources(struct dma_chan *chan)
 {
 	return 0;
@@ -436,18 +386,12 @@ static void sa11x0_dma_free_chan_resources(struct dma_chan *chan)
 	struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
 	struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
 	unsigned long flags;
-	LIST_HEAD(head);
 
-	spin_lock_irqsave(&c->lock, flags);
-	spin_lock(&d->lock);
+	spin_lock_irqsave(&d->lock, flags);
 	list_del_init(&c->node);
-	spin_unlock(&d->lock);
-
-	list_splice_tail_init(&c->desc_submitted, &head);
-	list_splice_tail_init(&c->desc_issued, &head);
-	spin_unlock_irqrestore(&c->lock, flags);
+	spin_unlock_irqrestore(&d->lock, flags);
 
-	sa11x0_dma_desc_free(d, &head);
+	vchan_free_chan_resources(&c->vc);
 }
 
 static dma_addr_t sa11x0_dma_pos(struct sa11x0_dma_phy *p)
@@ -473,21 +417,15 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan,
 	struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
 	struct sa11x0_dma_phy *p;
 	struct sa11x0_dma_desc *txd;
-	dma_cookie_t last_used, last_complete;
 	unsigned long flags;
 	enum dma_status ret;
 	size_t bytes = 0;
 
-	last_used = c->chan.cookie;
-	last_complete = c->lc;
-
-	ret = dma_async_is_complete(cookie, last_complete, last_used);
-	if (ret == DMA_SUCCESS) {
-		dma_set_tx_state(state, last_complete, last_used, 0);
+	ret = dma_cookie_status(&c->vc.chan, cookie, state);
+	if (ret == DMA_SUCCESS)
 		return ret;
-	}
 
-	spin_lock_irqsave(&c->lock, flags);
+	spin_lock_irqsave(&c->vc.lock, flags);
 	p = c->phy;
 	ret = c->status;
 	if (p) {
@@ -524,12 +462,13 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan,
 		if (txd != p->txd_load && p->txd_load)
 			bytes += p->txd_load->size;
 	}
-	list_for_each_entry(txd, &c->desc_issued, node) {
+	list_for_each_entry(txd, &c->vc.desc_issued, vd.node) {
 		bytes += txd->size;
 	}
-	spin_unlock_irqrestore(&c->lock, flags);
+	spin_unlock_irqrestore(&c->vc.lock, flags);
 
-	dma_set_tx_state(state, last_complete, last_used, bytes);
+	if (state)
+		state->residue = bytes;
 
 	dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", bytes);
 
@@ -547,40 +486,20 @@ static void sa11x0_dma_issue_pending(struct dma_chan *chan)
 	struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
 	unsigned long flags;
 
-	spin_lock_irqsave(&c->lock, flags);
-	list_splice_tail_init(&c->desc_submitted, &c->desc_issued);
-	if (!list_empty(&c->desc_issued)) {
-		spin_lock(&d->lock);
-		if (!c->phy && list_empty(&c->node)) {
-			list_add_tail(&c->node, &d->chan_pending);
-			tasklet_schedule(&d->task);
-			dev_dbg(d->slave.dev, "vchan %p: issued\n", c);
+	spin_lock_irqsave(&c->vc.lock, flags);
+	if (vchan_issue_pending(&c->vc)) {
+		if (!c->phy) {
+			spin_lock(&d->lock);
+			if (list_empty(&c->node)) {
+				list_add_tail(&c->node, &d->chan_pending);
+				tasklet_schedule(&d->task);
+				dev_dbg(d->slave.dev, "vchan %p: issued\n", &c->vc);
+			}
+			spin_unlock(&d->lock);
 		}
-		spin_unlock(&d->lock);
 	} else
-		dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", c);
-	spin_unlock_irqrestore(&c->lock, flags);
-}
-
-static dma_cookie_t sa11x0_dma_tx_submit(struct dma_async_tx_descriptor *tx)
-{
-	struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(tx->chan);
-	struct sa11x0_dma_desc *txd = to_sa11x0_dma_tx(tx);
-	unsigned long flags;
-
-	spin_lock_irqsave(&c->lock, flags);
-	c->chan.cookie += 1;
-	if (c->chan.cookie < 0)
-		c->chan.cookie = 1;
-	txd->tx.cookie = c->chan.cookie;
-
-	list_add_tail(&txd->node, &c->desc_submitted);
-	spin_unlock_irqrestore(&c->lock, flags);
-
-	dev_dbg(tx->chan->device->dev, "vchan %p: txd %p[%x]: submitted\n",
-		c, txd, txd->tx.cookie);
-
-	return txd->tx.cookie;
+		dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", &c->vc);
+	spin_unlock_irqrestore(&c->vc.lock, flags);
 }
 
 static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
@@ -596,7 +515,7 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
 	/* SA11x0 channels can only operate in their native direction */
 	if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) {
 		dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n",
-			c, c->ddar, dir);
+			&c->vc, c->ddar, dir);
 		return NULL;
 	}
 
@@ -612,14 +531,14 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
 			j += DIV_ROUND_UP(len, DMA_MAX_SIZE & ~DMA_ALIGN) - 1;
 		if (addr & DMA_ALIGN) {
 			dev_dbg(chan->device->dev, "vchan %p: bad buffer alignment: %08x\n",
-				c, addr);
+				&c->vc, addr);
 			return NULL;
 		}
 	}
 
 	txd = kzalloc(sizeof(*txd) + j * sizeof(txd->sg[0]), GFP_ATOMIC);
 	if (!txd) {
-		dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", c);
+		dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", &c->vc);
 		return NULL;
 	}
 
@@ -655,17 +574,14 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
 		} while (len);
 	}
 
-	dma_async_tx_descriptor_init(&txd->tx, &c->chan);
-	txd->tx.flags = flags;
-	txd->tx.tx_submit = sa11x0_dma_tx_submit;
 	txd->ddar = c->ddar;
 	txd->size = size;
 	txd->sglen = j;
 
 	dev_dbg(chan->device->dev, "vchan %p: txd %p: size %u nr %u\n",
-		c, txd, txd->size, txd->sglen);
+		&c->vc, &txd->vd, txd->size, txd->sglen);
 
-	return &txd->tx;
+	return vchan_tx_prep(&c->vc, &txd->vd, flags);
 }
 
 static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg)
@@ -695,8 +611,8 @@ static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_c
 	if (maxburst == 8)
 		ddar |= DDAR_BS;
 
-	dev_dbg(c->chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n",
-		c, addr, width, maxburst);
+	dev_dbg(c->vc.chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n",
+		&c->vc, addr, width, maxburst);
 
 	c->ddar = ddar | (addr & 0xf0000000) | (addr & 0x003ffffc) << 6;
 
@@ -718,16 +634,13 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 		return sa11x0_dma_slave_config(c, (struct dma_slave_config *)arg);
 
 	case DMA_TERMINATE_ALL:
-		dev_dbg(d->slave.dev, "vchan %p: terminate all\n", c);
+		dev_dbg(d->slave.dev, "vchan %p: terminate all\n", &c->vc);
 		/* Clear the tx descriptor lists */
-		spin_lock_irqsave(&c->lock, flags);
-		list_splice_tail_init(&c->desc_submitted, &head);
-		list_splice_tail_init(&c->desc_issued, &head);
+		spin_lock_irqsave(&c->vc.lock, flags);
+		vchan_get_all_descriptors(&c->vc, &head);
 
 		p = c->phy;
 		if (p) {
-			struct sa11x0_dma_desc *txd, *txn;
-
 			dev_dbg(d->slave.dev, "pchan %u: terminating\n", p->num);
 			/* vchan is assigned to a pchan - stop the channel */
 			writel(DCSR_RUN | DCSR_IE |
@@ -735,17 +648,13 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 				DCSR_STRTB | DCSR_DONEB,
 				p->base + DMA_DCSR_C);
 
-			list_for_each_entry_safe(txd, txn, &d->desc_complete, node)
-				if (txd->tx.chan == &c->chan)
-					list_move(&txd->node, &head);
-
 			if (p->txd_load) {
 				if (p->txd_load != p->txd_done)
-					list_add_tail(&p->txd_load->node, &head);
+					list_add_tail(&p->txd_load->vd.node, &head);
 				p->txd_load = NULL;
 			}
 			if (p->txd_done) {
-				list_add_tail(&p->txd_done->node, &head);
+				list_add_tail(&p->txd_done->vd.node, &head);
 				p->txd_done = NULL;
 			}
 			c->phy = NULL;
@@ -754,14 +663,14 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 			spin_unlock(&d->lock);
 			tasklet_schedule(&d->task);
 		}
-		spin_unlock_irqrestore(&c->lock, flags);
-		sa11x0_dma_desc_free(d, &head);
+		spin_unlock_irqrestore(&c->vc.lock, flags);
+		vchan_dma_desc_free_list(&c->vc, &head);
 		ret = 0;
 		break;
 
 	case DMA_PAUSE:
-		dev_dbg(d->slave.dev, "vchan %p: pause\n", c);
-		spin_lock_irqsave(&c->lock, flags);
+		dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc);
+		spin_lock_irqsave(&c->vc.lock, flags);
 		if (c->status == DMA_IN_PROGRESS) {
 			c->status = DMA_PAUSED;
 
@@ -774,26 +683,26 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 				spin_unlock(&d->lock);
 			}
 		}
-		spin_unlock_irqrestore(&c->lock, flags);
+		spin_unlock_irqrestore(&c->vc.lock, flags);
 		ret = 0;
 		break;
 
 	case DMA_RESUME:
-		dev_dbg(d->slave.dev, "vchan %p: resume\n", c);
-		spin_lock_irqsave(&c->lock, flags);
+		dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc);
+		spin_lock_irqsave(&c->vc.lock, flags);
 		if (c->status == DMA_PAUSED) {
 			c->status = DMA_IN_PROGRESS;
 
 			p = c->phy;
 			if (p) {
 				writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_S);
-			} else if (!list_empty(&c->desc_issued)) {
+			} else if (!list_empty(&c->vc.desc_issued)) {
 				spin_lock(&d->lock);
 				list_add_tail(&c->node, &d->chan_pending);
 				spin_unlock(&d->lock);
 			}
 		}
-		spin_unlock_irqrestore(&c->lock, flags);
+		spin_unlock_irqrestore(&c->vc.lock, flags);
 		ret = 0;
 		break;
 
@@ -853,15 +762,13 @@ static int __devinit sa11x0_dma_init_dmadev(struct dma_device *dmadev,
 			return -ENOMEM;
 		}
 
-		c->chan.device = dmadev;
 		c->status = DMA_IN_PROGRESS;
 		c->ddar = chan_desc[i].ddar;
 		c->name = chan_desc[i].name;
-		spin_lock_init(&c->lock);
-		INIT_LIST_HEAD(&c->desc_submitted);
-		INIT_LIST_HEAD(&c->desc_issued);
 		INIT_LIST_HEAD(&c->node);
-		list_add_tail(&c->chan.device_node, &dmadev->channels);
+
+		c->vc.desc_free = sa11x0_dma_free_desc;
+		vchan_init(&c->vc, dmadev);
 	}
 
 	return dma_async_device_register(dmadev);
@@ -890,8 +797,9 @@ static void sa11x0_dma_free_channels(struct dma_device *dmadev)
 {
 	struct sa11x0_dma_chan *c, *cn;
 
-	list_for_each_entry_safe(c, cn, &dmadev->channels, chan.device_node) {
-		list_del(&c->chan.device_node);
+	list_for_each_entry_safe(c, cn, &dmadev->channels, vc.chan.device_node) {
+		list_del(&c->vc.chan.device_node);
+		tasklet_kill(&c->vc.task);
 		kfree(c);
 	}
 }
@@ -915,7 +823,6 @@ static int __devinit sa11x0_dma_probe(struct platform_device *pdev)
 
 	spin_lock_init(&d->lock);
 	INIT_LIST_HEAD(&d->chan_pending);
-	INIT_LIST_HEAD(&d->desc_complete);
 
 	d->base = ioremap(res->start, resource_size(res));
 	if (!d->base) {
diff --git a/drivers/dma/virt-dma.c b/drivers/dma/virt-dma.c
new file mode 100644
index 0000000..bd85b05
--- /dev/null
+++ b/drivers/dma/virt-dma.c
@@ -0,0 +1,99 @@
+/*
+ * Virtual DMA channel support for DMAengine
+ *
+ * Copyright (C) 2012 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+
+static struct virt_dma_desc *to_virt_desc(struct dma_async_tx_descriptor *tx)
+{
+	return container_of(tx, struct virt_dma_desc, tx);
+}
+
+dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+	struct virt_dma_chan *vc = to_virt_chan(tx->chan);
+	struct virt_dma_desc *vd = to_virt_desc(tx);
+	unsigned long flags;
+	dma_cookie_t cookie;
+
+	spin_lock_irqsave(&vc->lock, flags);
+	cookie = dma_cookie_assign(tx);
+
+	list_add_tail(&vd->node, &vc->desc_submitted);
+	spin_unlock_irqrestore(&vc->lock, flags);
+
+	dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted\n",
+		vc, vd, cookie);
+
+	return cookie;
+}
+EXPORT_SYMBOL_GPL(vchan_tx_submit);
+
+/*
+ * This tasklet handles the completion of a DMA descriptor by
+ * calling its callback and freeing it.
+ */
+static void vchan_complete(unsigned long arg)
+{
+	struct virt_dma_chan *vc = (struct virt_dma_chan *)arg;
+	LIST_HEAD(head);
+
+	spin_lock_irq(&vc->lock);
+	list_splice_tail_init(&vc->desc_completed, &head);
+	spin_unlock_irq(&vc->lock);
+
+	while (!list_empty(&head)) {
+		struct virt_dma_desc *vd = list_first_entry(&head,
+				struct virt_dma_desc, node);
+		dma_async_tx_callback cb = vd->tx.callback;
+		void *cb_data = vd->tx.callback_param;
+
+		list_del(&vd->node);
+
+		vc->desc_free(vd);
+
+		if (cb)
+			cb(cb_data);
+	}
+}
+
+void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head)
+{
+	while (!list_empty(head)) {
+		struct virt_dma_desc *vd = list_first_entry(head,
+			struct virt_dma_desc, node);
+		list_del(&vd->node);
+		dev_dbg(vc->chan.device->dev, "txd %p: freeing\n", vd);
+		vc->desc_free(vd);
+	}
+}
+EXPORT_SYMBOL_GPL(vchan_dma_desc_free_list);
+
+void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
+{
+	dma_cookie_init(&vc->chan);
+
+	spin_lock_init(&vc->lock);
+	INIT_LIST_HEAD(&vc->desc_submitted);
+	INIT_LIST_HEAD(&vc->desc_issued);
+	INIT_LIST_HEAD(&vc->desc_completed);
+
+	tasklet_init(&vc->task, vchan_complete, (unsigned long)vc);
+
+	vc->chan.device = dmadev;
+	list_add_tail(&vc->chan.device_node, &dmadev->channels);
+}
+EXPORT_SYMBOL_GPL(vchan_init);
+
+MODULE_AUTHOR("Russell King");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/virt-dma.h b/drivers/dma/virt-dma.h
new file mode 100644
index 0000000..825bb96
--- /dev/null
+++ b/drivers/dma/virt-dma.h
@@ -0,0 +1,138 @@
+/*
+ * Virtual DMA channel support for DMAengine
+ *
+ * Copyright (C) 2012 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef VIRT_DMA_H
+#define VIRT_DMA_H
+
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+
+#include "dmaengine.h"
+
+struct virt_dma_desc {
+	struct dma_async_tx_descriptor tx;
+	/* protected by vc.lock */
+	struct list_head node;
+};
+
+struct virt_dma_chan {
+	struct dma_chan	chan;
+	struct tasklet_struct task;
+	void (*desc_free)(struct virt_dma_desc *);
+
+	spinlock_t lock;
+
+	/* protected by vc.lock */
+	struct list_head desc_submitted;
+	struct list_head desc_issued;
+	struct list_head desc_completed;
+};
+
+static inline struct virt_dma_chan *to_virt_chan(struct dma_chan *chan)
+{
+	return container_of(chan, struct virt_dma_chan, chan);
+}
+
+void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head);
+
+void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev);
+
+/**
+ * vchan_tx_prep - prepare a descriptor
+ * vc: virtual channel allocating this descriptor
+ * vd: virtual descriptor to prepare
+ * tx_flags: flags argument passed in to prepare function
+ */
+static inline struct dma_async_tx_descriptor *vchan_tx_prep(struct virt_dma_chan *vc,
+	struct virt_dma_desc *vd, unsigned long tx_flags)
+{
+	extern dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *);
+
+	dma_async_tx_descriptor_init(&vd->tx, &vc->chan);
+	vd->tx.flags = tx_flags;
+	vd->tx.tx_submit = vchan_tx_submit;
+
+	return &vd->tx;
+}
+
+/**
+ * vchan_issue_pending - move submitted descriptors to issued list
+ * vc: virtual channel to update
+ *
+ * vc.lock must be held by caller
+ */
+static inline bool vchan_issue_pending(struct virt_dma_chan *vc)
+{
+	list_splice_tail_init(&vc->desc_submitted, &vc->desc_issued);
+	return !list_empty(&vc->desc_issued);
+}
+
+/**
+ * vchan_cookie_complete - report completion of a descriptor
+ * vd: virtual descriptor to update
+ *
+ * vc.lock must be held by caller
+ */
+static inline void vchan_cookie_complete(struct virt_dma_desc *vd)
+{
+	struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);
+
+	dma_cookie_complete(&vd->tx);
+	dev_vdbg(vc->chan.device->dev, "txd %p[%x]: marked complete\n",
+		vd, vd->tx.cookie);
+	list_add_tail(&vd->node, &vc->desc_completed);
+
+	tasklet_schedule(&vc->task);
+}
+
+/**
+ * vchan_next_desc - peek at the next descriptor to be processed
+ * vc: virtual channel to obtain descriptor from
+ *
+ * vc.lock must be held by caller
+ */
+static inline struct virt_dma_desc *vchan_next_desc(struct virt_dma_chan *vc)
+{
+	if (list_empty(&vc->desc_issued))
+		return NULL;
+
+	return list_first_entry(&vc->desc_issued, struct virt_dma_desc, node);
+}
+
+/**
+ * vchan_get_all_descriptors - obtain all submitted and issued descriptors
+ * vc: virtual channel to get descriptors from
+ * head: list of descriptors found
+ *
+ * vc.lock must be held by caller
+ *
+ * Removes all submitted and issued descriptors from internal lists, and
+ * provides a list of all descriptors found
+ */
+static inline void vchan_get_all_descriptors(struct virt_dma_chan *vc,
+	struct list_head *head)
+{
+	list_splice_tail_init(&vc->desc_submitted, head);
+	list_splice_tail_init(&vc->desc_issued, head);
+	list_splice_tail_init(&vc->desc_completed, head);
+}
+
+static inline void vchan_free_chan_resources(struct virt_dma_chan *vc)
+{
+	unsigned long flags;
+	LIST_HEAD(head);
+
+	spin_lock_irqsave(&vc->lock, flags);
+	vchan_get_all_descriptors(vc, &head);
+	spin_unlock_irqrestore(&vc->lock, flags);
+
+	vchan_dma_desc_free_list(vc, &head);
+}
+
+#endif
-- 
1.7.4.4

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

* [PATCH 4/8] dmaengine: add OMAP DMA engine driver
  2012-04-18 10:09 ` Russell King - ARM Linux
@ 2012-04-18 10:11   ` Russell King
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap, linux-mmc; +Cc: Vinod Koul, Dan Williams

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/dma/Kconfig    |    6 +
 drivers/dma/Makefile   |    1 +
 drivers/dma/omap-dma.c |  519 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 526 insertions(+), 0 deletions(-)
 create mode 100644 drivers/dma/omap-dma.c

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 5828ac4..792b486 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -261,6 +261,12 @@ config DMA_SA11X0
 	  SA-1110 SoCs.  This DMA engine can only be used with on-chip
 	  devices.
 
+config DMA_OMAP
+	tristate "OMAP DMA support"
+	depends on ARCH_OMAP
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+
 config DMA_ENGINE
 	bool
 
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index fc05f7d..ddc291a 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_PCH_DMA) += pch_dma.o
 obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
 obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
 obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
+obj-$(CONFIG_DMA_OMAP) += omap-dma.o
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
new file mode 100644
index 0000000..5b12f74
--- /dev/null
+++ b/drivers/dma/omap-dma.c
@@ -0,0 +1,519 @@
+/*
+ * OMAP DMAengine support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+#include <plat/dma.h>
+
+struct omap_dmadev {
+	struct dma_device ddev;
+	spinlock_t lock;
+	struct tasklet_struct task;
+	struct list_head pending;
+};
+
+struct omap_chan {
+	struct virt_dma_chan vc;
+	struct list_head node;
+
+	struct dma_slave_config	cfg;
+	unsigned dma_sig;
+
+	int dma_ch;
+	struct omap_desc *desc;
+	unsigned sgidx;
+};
+
+struct omap_sg {
+	dma_addr_t addr;
+	uint32_t en;		/* number of elements (24-bit) */
+	uint32_t fn;		/* number of frames (16-bit) */
+};
+
+struct omap_desc {
+	struct virt_dma_desc vd;
+	enum dma_transfer_direction dir;
+	dma_addr_t dev_addr;
+
+	uint8_t es;		/* element size */
+	uint8_t sync_mode;	/* OMAP_DMA_SYNC_xxx */
+	uint8_t sync_type;	/* OMAP_DMA_xxx_SYNC* */
+
+	unsigned sglen;
+	struct omap_sg sg[0];
+};
+
+static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d)
+{
+	return container_of(d, struct omap_dmadev, ddev);
+}
+
+static inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c)
+{
+	return container_of(c, struct omap_chan, vc.chan);
+}
+
+static inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t)
+{
+	return container_of(t, struct omap_desc, vd.tx);
+}
+
+static void omap_dma_desc_free(struct virt_dma_desc *vd)
+{
+	kfree(container_of(vd, struct omap_desc, vd));
+}
+
+static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
+	unsigned idx)
+{
+	struct omap_sg *sg = d->sg + idx;
+
+	if (d->dir == DMA_DEV_TO_MEM)
+		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+				sg->addr, 0, 0);
+	else
+		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+				sg->addr, 0, 0);
+
+	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
+		d->sync_mode, c->dma_sig, d->sync_type);
+
+	omap_start_dma(c->dma_ch);
+}
+
+static void omap_dma_start_desc(struct omap_chan *c)
+{
+	struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
+	struct omap_desc *d;
+
+	if (!vd) {
+		c->desc = NULL;
+		return;
+	}
+
+	list_del(&vd->node);
+
+	c->desc = d = to_omap_dma_desc(&vd->tx);
+	c->sgidx = 0;
+
+	if (d->dir == DMA_DEV_TO_MEM)
+		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+			d->dev_addr, 0, 0);
+	else
+		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+			d->dev_addr, 0, 0);
+
+	omap_dma_start_sg(c, d, 0);
+}
+
+static void omap_dma_callback(int ch, u16 status, void *data)
+{
+	struct omap_chan *c = data;
+	struct omap_desc *d;
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	d = c->desc;
+	if (!d)
+		return;
+
+	if (++c->sgidx < d->sglen) {
+		omap_dma_start_sg(c, d, c->sgidx);
+	} else {
+		omap_dma_start_desc(c);
+		vchan_cookie_complete(&d->vd);
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+/*
+ * This callback schedules all pending channels.  We could be more
+ * clever here by postponing allocation of the real DMA channels to
+ * this point, and freeing them when our virtual channel becomes idle.
+ *
+ * We would then need to deal with 'all channels in-use'
+ */
+static void omap_dma_sched(unsigned long data)
+{
+	struct omap_dmadev *d = (struct omap_dmadev *)data;
+	LIST_HEAD(head);
+
+	spin_lock_irq(&d->lock);
+	list_splice_tail_init(&d->pending, &head);
+	spin_unlock_irq(&d->lock);
+
+	while (!list_empty(&head)) {
+		struct omap_chan *c = list_first_entry(&head,
+			struct omap_chan, node);
+
+		spin_lock_irq(&c->vc.lock);
+		list_del_init(&c->node);
+		omap_dma_start_desc(c);
+		spin_unlock_irq(&c->vc.lock);
+	}
+}
+
+static int omap_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+
+	dev_info(c->vc.chan.device->dev, "allocating channel for %u\n", c->dma_sig);
+
+	return omap_request_dma(c->dma_sig, "DMA engine",
+		omap_dma_callback, c, &c->dma_ch);
+}
+
+static void omap_dma_free_chan_resources(struct dma_chan *chan)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+
+	vchan_free_chan_resources(&c->vc);
+	omap_free_dma(c->dma_ch);
+
+	dev_info(c->vc.chan.device->dev, "freeing channel for %u\n", c->dma_sig);
+}
+
+static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
+	dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+	/*
+	 * FIXME: do we need to return pending bytes?
+	 * We have no users of that info at the moment...
+	 */
+	return dma_cookie_status(chan, cookie, txstate);
+}
+
+static void omap_dma_issue_pending(struct dma_chan *chan)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	if (vchan_issue_pending(&c->vc) && !c->desc) {
+		struct omap_dmadev *d = to_omap_dma_dev(chan->device);
+		spin_lock(&d->lock);
+		if (list_empty(&c->node))
+			list_add_tail(&c->node, &d->pending);
+		spin_unlock(&d->lock);
+		tasklet_schedule(&d->task);
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
+	struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen,
+	enum dma_transfer_direction dir, unsigned long tx_flags, void *context)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+	enum dma_slave_buswidth dev_width;
+	struct scatterlist *sgent;
+	struct omap_desc *d;
+	dma_addr_t dev_addr;
+	unsigned i, j = 0, es, es_bytes, en, frame_bytes, sync_type;
+	u32 burst;
+
+	if (dir == DMA_DEV_TO_MEM) {
+		dev_addr = c->cfg.src_addr;
+		dev_width = c->cfg.src_addr_width;
+		burst = c->cfg.src_maxburst;
+		sync_type = OMAP_DMA_SRC_SYNC;
+	} else if (dir == DMA_MEM_TO_DEV) {
+		dev_addr = c->cfg.dst_addr;
+		dev_width = c->cfg.dst_addr_width;
+		burst = c->cfg.dst_maxburst;
+		sync_type = OMAP_DMA_DST_SYNC;
+	} else {
+		dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
+		return NULL;
+	}
+
+	/* Bus width translates to the element size (ES) */
+	switch (dev_width) {
+	case DMA_SLAVE_BUSWIDTH_1_BYTE:
+		es = OMAP_DMA_DATA_TYPE_S8;
+		es_bytes = 1;
+		break;
+	case DMA_SLAVE_BUSWIDTH_2_BYTES:
+		es = OMAP_DMA_DATA_TYPE_S16;
+		es_bytes = 2;
+		break;
+	case DMA_SLAVE_BUSWIDTH_4_BYTES:
+		es = OMAP_DMA_DATA_TYPE_S32;
+		es_bytes = 4;
+		break;
+	default: /* not reached */
+		return NULL;
+	}
+
+	/* Now allocate and setup the descriptor. */
+	d = kzalloc(sizeof(*d) + sglen * sizeof(d->sg[0]), GFP_ATOMIC);
+	if (!d)
+		return NULL;
+
+	d->dir = dir;
+	d->dev_addr = dev_addr;
+	d->es = es;
+	d->sync_mode = OMAP_DMA_SYNC_FRAME;
+	d->sync_type = sync_type;
+
+	/*
+	 * Build our scatterlist entries: each contains the address,
+	 * the number of elements (EN) in each frame, and the number of
+	 * frames (FN).  Number of bytes for this entry = ES * EN * FN.
+	 *
+	 * Burst size translates to number of elements with frame sync.
+	 * Note: DMA engine defines burst to be the number of dev-width
+	 * transfers.
+	 */
+	en = burst;
+	frame_bytes = es_bytes * en;
+	for_each_sg(sgl, sgent, sglen, i) {
+		d->sg[j].addr = sg_dma_address(sgent);
+		d->sg[j].en = en;
+		d->sg[j].fn = sg_dma_len(sgent) / frame_bytes;
+		j++;
+	}
+
+	d->sglen = j;
+
+	return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
+}
+
+static int omap_dma_slave_config(struct omap_chan *c, struct dma_slave_config *cfg)
+{
+	if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
+	    cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
+		return -EINVAL;
+
+	memcpy(&c->cfg, cfg, sizeof(c->cfg));
+
+	return 0;
+}
+
+static int omap_dma_terminate_all(struct omap_chan *c)
+{
+	struct omap_dmadev *d = to_omap_dma_dev(c->vc.chan.device);
+	unsigned long flags;
+	LIST_HEAD(head);
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+
+	/* Prevent this channel being scheduled */
+	spin_lock(&d->lock);
+	list_del_init(&c->node);
+	spin_unlock(&d->lock);
+
+	/*
+	 * Stop DMA activity: we assume the callback will not be called
+	 * after omap_stop_dma() returns (even if it does, it will see
+	 * c->desc is NULL and exit.)
+	 */
+	if (c->desc) {
+		c->desc = NULL;
+		omap_stop_dma(c->dma_ch);
+	}
+
+	vchan_get_all_descriptors(&c->vc, &head);
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+	vchan_dma_desc_free_list(&c->vc, &head);
+
+	return 0;
+}
+
+static int omap_dma_pause(struct omap_chan *c)
+{
+	/* FIXME: not supported by platform private API */
+	return -EINVAL;
+}
+
+static int omap_dma_resume(struct omap_chan *c)
+{
+	/* FIXME: not supported by platform private API */
+	return -EINVAL;
+}
+
+static int omap_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+	unsigned long arg)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+	int ret;
+
+	switch (cmd) {
+	case DMA_SLAVE_CONFIG:
+		ret = omap_dma_slave_config(c, (struct dma_slave_config *)arg);
+		break;
+
+	case DMA_TERMINATE_ALL:
+		ret = omap_dma_terminate_all(c);
+		break;
+
+	case DMA_PAUSE:
+		ret = omap_dma_pause(c);
+		break;
+
+	case DMA_RESUME:
+		ret = omap_dma_resume(c);
+		break;
+
+	default:
+		ret = -ENXIO;
+		break;
+	}
+
+	return ret;
+}
+
+static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig)
+{
+	struct omap_chan *c;
+
+	c = kzalloc(sizeof(*c), GFP_KERNEL);
+	if (!c)
+		return -ENOMEM;
+
+	c->dma_sig = dma_sig;
+	c->vc.desc_free = omap_dma_desc_free;
+	vchan_init(&c->vc, &od->ddev);
+	INIT_LIST_HEAD(&c->node);
+
+	od->ddev.chancnt++;
+
+	return 0;
+}
+
+static void omap_dma_free(struct omap_dmadev *od)
+{
+	tasklet_kill(&od->task);
+	while (!list_empty(&od->ddev.channels)) {
+		struct omap_chan *c = list_first_entry(&od->ddev.channels,
+			struct omap_chan, vc.chan.device_node);
+
+		list_del(&c->vc.chan.device_node);
+		tasklet_kill(&c->vc.task);
+		kfree(c);
+	}
+	kfree(od);
+}
+
+static int omap_dma_probe(struct platform_device *pdev)
+{
+	struct omap_dmadev *od;
+	int rc, i;
+
+	od = kzalloc(sizeof(*od), GFP_KERNEL);
+	if (!od)
+		return -ENOMEM;
+
+	dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
+	od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources;
+	od->ddev.device_free_chan_resources = omap_dma_free_chan_resources;
+	od->ddev.device_tx_status = omap_dma_tx_status;
+	od->ddev.device_issue_pending = omap_dma_issue_pending;
+	od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg;
+	od->ddev.device_control = omap_dma_control;
+	od->ddev.dev = &pdev->dev;
+	INIT_LIST_HEAD(&od->ddev.channels);
+	INIT_LIST_HEAD(&od->pending);
+	spin_lock_init(&od->lock);
+
+	tasklet_init(&od->task, omap_dma_sched, (unsigned long)od);
+
+	for (i = 0; i < 127; i++) {
+		rc = omap_dma_chan_init(od, i);
+		if (rc) {
+			omap_dma_free(od);
+			return rc;
+		}
+	}
+
+	rc = dma_async_device_register(&od->ddev);
+	if (rc) {
+		pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
+			rc);
+		omap_dma_free(od);
+	} else {
+		platform_set_drvdata(pdev, od);
+	}
+
+	dev_info(&pdev->dev, "OMAP DMA engine driver\n");
+
+	return rc;
+}
+
+static int omap_dma_remove(struct platform_device *pdev)
+{
+	struct omap_dmadev *od = platform_get_drvdata(pdev);
+
+	dma_async_device_unregister(&od->ddev);
+	omap_dma_free(od);
+
+	return 0;
+}
+
+static struct platform_driver omap_dma_driver = {
+	.probe	= omap_dma_probe,
+	.remove	= omap_dma_remove,
+	.driver = {
+		.name = "omap-dma-engine",
+		.owner = THIS_MODULE,
+	},
+};
+
+bool omap_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+	if (chan->device->dev->driver == &omap_dma_driver.driver) {
+		struct omap_chan *c = to_omap_dma_chan(chan);
+		unsigned req = *(unsigned *)param;
+
+		return req == c->dma_sig;
+	}
+	return false;
+}
+EXPORT_SYMBOL_GPL(omap_dma_filter_fn);
+
+static struct platform_device *pdev;
+
+static const struct platform_device_info omap_dma_dev_info = {
+	.name = "omap-dma-engine",
+	.id = -1,
+	.dma_mask = DMA_BIT_MASK(32),
+};
+
+static int omap_dma_init(void)
+{
+	int rc = platform_driver_register(&omap_dma_driver);
+
+	if (rc == 0) {
+		pdev = platform_device_register_full(&omap_dma_dev_info);
+		if (IS_ERR(pdev)) {
+			platform_driver_unregister(&omap_dma_driver);
+			rc = PTR_ERR(pdev);
+		}
+	}
+	return rc;
+}
+subsys_initcall(omap_dma_init);
+
+static void __exit omap_dma_exit(void)
+{
+	platform_device_unregister(pdev);
+	platform_driver_unregister(&omap_dma_driver);
+}
+module_exit(omap_dma_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_LICENSE("GPL");
-- 
1.7.4.4


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

* [PATCH 4/8] dmaengine: add OMAP DMA engine driver
@ 2012-04-18 10:11   ` Russell King
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:11 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/dma/Kconfig    |    6 +
 drivers/dma/Makefile   |    1 +
 drivers/dma/omap-dma.c |  519 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 526 insertions(+), 0 deletions(-)
 create mode 100644 drivers/dma/omap-dma.c

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 5828ac4..792b486 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -261,6 +261,12 @@ config DMA_SA11X0
 	  SA-1110 SoCs.  This DMA engine can only be used with on-chip
 	  devices.
 
+config DMA_OMAP
+	tristate "OMAP DMA support"
+	depends on ARCH_OMAP
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+
 config DMA_ENGINE
 	bool
 
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index fc05f7d..ddc291a 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_PCH_DMA) += pch_dma.o
 obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
 obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
 obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
+obj-$(CONFIG_DMA_OMAP) += omap-dma.o
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
new file mode 100644
index 0000000..5b12f74
--- /dev/null
+++ b/drivers/dma/omap-dma.c
@@ -0,0 +1,519 @@
+/*
+ * OMAP DMAengine support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+#include <plat/dma.h>
+
+struct omap_dmadev {
+	struct dma_device ddev;
+	spinlock_t lock;
+	struct tasklet_struct task;
+	struct list_head pending;
+};
+
+struct omap_chan {
+	struct virt_dma_chan vc;
+	struct list_head node;
+
+	struct dma_slave_config	cfg;
+	unsigned dma_sig;
+
+	int dma_ch;
+	struct omap_desc *desc;
+	unsigned sgidx;
+};
+
+struct omap_sg {
+	dma_addr_t addr;
+	uint32_t en;		/* number of elements (24-bit) */
+	uint32_t fn;		/* number of frames (16-bit) */
+};
+
+struct omap_desc {
+	struct virt_dma_desc vd;
+	enum dma_transfer_direction dir;
+	dma_addr_t dev_addr;
+
+	uint8_t es;		/* element size */
+	uint8_t sync_mode;	/* OMAP_DMA_SYNC_xxx */
+	uint8_t sync_type;	/* OMAP_DMA_xxx_SYNC* */
+
+	unsigned sglen;
+	struct omap_sg sg[0];
+};
+
+static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d)
+{
+	return container_of(d, struct omap_dmadev, ddev);
+}
+
+static inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c)
+{
+	return container_of(c, struct omap_chan, vc.chan);
+}
+
+static inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t)
+{
+	return container_of(t, struct omap_desc, vd.tx);
+}
+
+static void omap_dma_desc_free(struct virt_dma_desc *vd)
+{
+	kfree(container_of(vd, struct omap_desc, vd));
+}
+
+static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
+	unsigned idx)
+{
+	struct omap_sg *sg = d->sg + idx;
+
+	if (d->dir == DMA_DEV_TO_MEM)
+		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+				sg->addr, 0, 0);
+	else
+		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+				sg->addr, 0, 0);
+
+	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
+		d->sync_mode, c->dma_sig, d->sync_type);
+
+	omap_start_dma(c->dma_ch);
+}
+
+static void omap_dma_start_desc(struct omap_chan *c)
+{
+	struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
+	struct omap_desc *d;
+
+	if (!vd) {
+		c->desc = NULL;
+		return;
+	}
+
+	list_del(&vd->node);
+
+	c->desc = d = to_omap_dma_desc(&vd->tx);
+	c->sgidx = 0;
+
+	if (d->dir == DMA_DEV_TO_MEM)
+		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+			d->dev_addr, 0, 0);
+	else
+		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+			d->dev_addr, 0, 0);
+
+	omap_dma_start_sg(c, d, 0);
+}
+
+static void omap_dma_callback(int ch, u16 status, void *data)
+{
+	struct omap_chan *c = data;
+	struct omap_desc *d;
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	d = c->desc;
+	if (!d)
+		return;
+
+	if (++c->sgidx < d->sglen) {
+		omap_dma_start_sg(c, d, c->sgidx);
+	} else {
+		omap_dma_start_desc(c);
+		vchan_cookie_complete(&d->vd);
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+/*
+ * This callback schedules all pending channels.  We could be more
+ * clever here by postponing allocation of the real DMA channels to
+ * this point, and freeing them when our virtual channel becomes idle.
+ *
+ * We would then need to deal with 'all channels in-use'
+ */
+static void omap_dma_sched(unsigned long data)
+{
+	struct omap_dmadev *d = (struct omap_dmadev *)data;
+	LIST_HEAD(head);
+
+	spin_lock_irq(&d->lock);
+	list_splice_tail_init(&d->pending, &head);
+	spin_unlock_irq(&d->lock);
+
+	while (!list_empty(&head)) {
+		struct omap_chan *c = list_first_entry(&head,
+			struct omap_chan, node);
+
+		spin_lock_irq(&c->vc.lock);
+		list_del_init(&c->node);
+		omap_dma_start_desc(c);
+		spin_unlock_irq(&c->vc.lock);
+	}
+}
+
+static int omap_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+
+	dev_info(c->vc.chan.device->dev, "allocating channel for %u\n", c->dma_sig);
+
+	return omap_request_dma(c->dma_sig, "DMA engine",
+		omap_dma_callback, c, &c->dma_ch);
+}
+
+static void omap_dma_free_chan_resources(struct dma_chan *chan)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+
+	vchan_free_chan_resources(&c->vc);
+	omap_free_dma(c->dma_ch);
+
+	dev_info(c->vc.chan.device->dev, "freeing channel for %u\n", c->dma_sig);
+}
+
+static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
+	dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+	/*
+	 * FIXME: do we need to return pending bytes?
+	 * We have no users of that info at the moment...
+	 */
+	return dma_cookie_status(chan, cookie, txstate);
+}
+
+static void omap_dma_issue_pending(struct dma_chan *chan)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	if (vchan_issue_pending(&c->vc) && !c->desc) {
+		struct omap_dmadev *d = to_omap_dma_dev(chan->device);
+		spin_lock(&d->lock);
+		if (list_empty(&c->node))
+			list_add_tail(&c->node, &d->pending);
+		spin_unlock(&d->lock);
+		tasklet_schedule(&d->task);
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
+	struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen,
+	enum dma_transfer_direction dir, unsigned long tx_flags, void *context)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+	enum dma_slave_buswidth dev_width;
+	struct scatterlist *sgent;
+	struct omap_desc *d;
+	dma_addr_t dev_addr;
+	unsigned i, j = 0, es, es_bytes, en, frame_bytes, sync_type;
+	u32 burst;
+
+	if (dir == DMA_DEV_TO_MEM) {
+		dev_addr = c->cfg.src_addr;
+		dev_width = c->cfg.src_addr_width;
+		burst = c->cfg.src_maxburst;
+		sync_type = OMAP_DMA_SRC_SYNC;
+	} else if (dir == DMA_MEM_TO_DEV) {
+		dev_addr = c->cfg.dst_addr;
+		dev_width = c->cfg.dst_addr_width;
+		burst = c->cfg.dst_maxburst;
+		sync_type = OMAP_DMA_DST_SYNC;
+	} else {
+		dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
+		return NULL;
+	}
+
+	/* Bus width translates to the element size (ES) */
+	switch (dev_width) {
+	case DMA_SLAVE_BUSWIDTH_1_BYTE:
+		es = OMAP_DMA_DATA_TYPE_S8;
+		es_bytes = 1;
+		break;
+	case DMA_SLAVE_BUSWIDTH_2_BYTES:
+		es = OMAP_DMA_DATA_TYPE_S16;
+		es_bytes = 2;
+		break;
+	case DMA_SLAVE_BUSWIDTH_4_BYTES:
+		es = OMAP_DMA_DATA_TYPE_S32;
+		es_bytes = 4;
+		break;
+	default: /* not reached */
+		return NULL;
+	}
+
+	/* Now allocate and setup the descriptor. */
+	d = kzalloc(sizeof(*d) + sglen * sizeof(d->sg[0]), GFP_ATOMIC);
+	if (!d)
+		return NULL;
+
+	d->dir = dir;
+	d->dev_addr = dev_addr;
+	d->es = es;
+	d->sync_mode = OMAP_DMA_SYNC_FRAME;
+	d->sync_type = sync_type;
+
+	/*
+	 * Build our scatterlist entries: each contains the address,
+	 * the number of elements (EN) in each frame, and the number of
+	 * frames (FN).  Number of bytes for this entry = ES * EN * FN.
+	 *
+	 * Burst size translates to number of elements with frame sync.
+	 * Note: DMA engine defines burst to be the number of dev-width
+	 * transfers.
+	 */
+	en = burst;
+	frame_bytes = es_bytes * en;
+	for_each_sg(sgl, sgent, sglen, i) {
+		d->sg[j].addr = sg_dma_address(sgent);
+		d->sg[j].en = en;
+		d->sg[j].fn = sg_dma_len(sgent) / frame_bytes;
+		j++;
+	}
+
+	d->sglen = j;
+
+	return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
+}
+
+static int omap_dma_slave_config(struct omap_chan *c, struct dma_slave_config *cfg)
+{
+	if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
+	    cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
+		return -EINVAL;
+
+	memcpy(&c->cfg, cfg, sizeof(c->cfg));
+
+	return 0;
+}
+
+static int omap_dma_terminate_all(struct omap_chan *c)
+{
+	struct omap_dmadev *d = to_omap_dma_dev(c->vc.chan.device);
+	unsigned long flags;
+	LIST_HEAD(head);
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+
+	/* Prevent this channel being scheduled */
+	spin_lock(&d->lock);
+	list_del_init(&c->node);
+	spin_unlock(&d->lock);
+
+	/*
+	 * Stop DMA activity: we assume the callback will not be called
+	 * after omap_stop_dma() returns (even if it does, it will see
+	 * c->desc is NULL and exit.)
+	 */
+	if (c->desc) {
+		c->desc = NULL;
+		omap_stop_dma(c->dma_ch);
+	}
+
+	vchan_get_all_descriptors(&c->vc, &head);
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+	vchan_dma_desc_free_list(&c->vc, &head);
+
+	return 0;
+}
+
+static int omap_dma_pause(struct omap_chan *c)
+{
+	/* FIXME: not supported by platform private API */
+	return -EINVAL;
+}
+
+static int omap_dma_resume(struct omap_chan *c)
+{
+	/* FIXME: not supported by platform private API */
+	return -EINVAL;
+}
+
+static int omap_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+	unsigned long arg)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+	int ret;
+
+	switch (cmd) {
+	case DMA_SLAVE_CONFIG:
+		ret = omap_dma_slave_config(c, (struct dma_slave_config *)arg);
+		break;
+
+	case DMA_TERMINATE_ALL:
+		ret = omap_dma_terminate_all(c);
+		break;
+
+	case DMA_PAUSE:
+		ret = omap_dma_pause(c);
+		break;
+
+	case DMA_RESUME:
+		ret = omap_dma_resume(c);
+		break;
+
+	default:
+		ret = -ENXIO;
+		break;
+	}
+
+	return ret;
+}
+
+static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig)
+{
+	struct omap_chan *c;
+
+	c = kzalloc(sizeof(*c), GFP_KERNEL);
+	if (!c)
+		return -ENOMEM;
+
+	c->dma_sig = dma_sig;
+	c->vc.desc_free = omap_dma_desc_free;
+	vchan_init(&c->vc, &od->ddev);
+	INIT_LIST_HEAD(&c->node);
+
+	od->ddev.chancnt++;
+
+	return 0;
+}
+
+static void omap_dma_free(struct omap_dmadev *od)
+{
+	tasklet_kill(&od->task);
+	while (!list_empty(&od->ddev.channels)) {
+		struct omap_chan *c = list_first_entry(&od->ddev.channels,
+			struct omap_chan, vc.chan.device_node);
+
+		list_del(&c->vc.chan.device_node);
+		tasklet_kill(&c->vc.task);
+		kfree(c);
+	}
+	kfree(od);
+}
+
+static int omap_dma_probe(struct platform_device *pdev)
+{
+	struct omap_dmadev *od;
+	int rc, i;
+
+	od = kzalloc(sizeof(*od), GFP_KERNEL);
+	if (!od)
+		return -ENOMEM;
+
+	dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
+	od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources;
+	od->ddev.device_free_chan_resources = omap_dma_free_chan_resources;
+	od->ddev.device_tx_status = omap_dma_tx_status;
+	od->ddev.device_issue_pending = omap_dma_issue_pending;
+	od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg;
+	od->ddev.device_control = omap_dma_control;
+	od->ddev.dev = &pdev->dev;
+	INIT_LIST_HEAD(&od->ddev.channels);
+	INIT_LIST_HEAD(&od->pending);
+	spin_lock_init(&od->lock);
+
+	tasklet_init(&od->task, omap_dma_sched, (unsigned long)od);
+
+	for (i = 0; i < 127; i++) {
+		rc = omap_dma_chan_init(od, i);
+		if (rc) {
+			omap_dma_free(od);
+			return rc;
+		}
+	}
+
+	rc = dma_async_device_register(&od->ddev);
+	if (rc) {
+		pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
+			rc);
+		omap_dma_free(od);
+	} else {
+		platform_set_drvdata(pdev, od);
+	}
+
+	dev_info(&pdev->dev, "OMAP DMA engine driver\n");
+
+	return rc;
+}
+
+static int omap_dma_remove(struct platform_device *pdev)
+{
+	struct omap_dmadev *od = platform_get_drvdata(pdev);
+
+	dma_async_device_unregister(&od->ddev);
+	omap_dma_free(od);
+
+	return 0;
+}
+
+static struct platform_driver omap_dma_driver = {
+	.probe	= omap_dma_probe,
+	.remove	= omap_dma_remove,
+	.driver = {
+		.name = "omap-dma-engine",
+		.owner = THIS_MODULE,
+	},
+};
+
+bool omap_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+	if (chan->device->dev->driver == &omap_dma_driver.driver) {
+		struct omap_chan *c = to_omap_dma_chan(chan);
+		unsigned req = *(unsigned *)param;
+
+		return req == c->dma_sig;
+	}
+	return false;
+}
+EXPORT_SYMBOL_GPL(omap_dma_filter_fn);
+
+static struct platform_device *pdev;
+
+static const struct platform_device_info omap_dma_dev_info = {
+	.name = "omap-dma-engine",
+	.id = -1,
+	.dma_mask = DMA_BIT_MASK(32),
+};
+
+static int omap_dma_init(void)
+{
+	int rc = platform_driver_register(&omap_dma_driver);
+
+	if (rc == 0) {
+		pdev = platform_device_register_full(&omap_dma_dev_info);
+		if (IS_ERR(pdev)) {
+			platform_driver_unregister(&omap_dma_driver);
+			rc = PTR_ERR(pdev);
+		}
+	}
+	return rc;
+}
+subsys_initcall(omap_dma_init);
+
+static void __exit omap_dma_exit(void)
+{
+	platform_device_unregister(pdev);
+	platform_driver_unregister(&omap_dma_driver);
+}
+module_exit(omap_dma_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_LICENSE("GPL");
-- 
1.7.4.4

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

* [PATCH 5/8] mmc: omap_hsmmc: release correct resource
  2012-04-18 10:09 ` Russell King - ARM Linux
@ 2012-04-18 10:11   ` Russell King
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap, linux-mmc; +Cc: Chris Ball

res can be one of several resources, as this variable is re-used several
times during probe.  This can cause the wrong resource parameters to be
passed to release_mem_region().

Get the original memory resource before calling release_mem_region().

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/mmc/host/omap_hsmmc.c |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 5c2b1c1..ac26f81a 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -2030,7 +2030,9 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 err_alloc:
 	omap_hsmmc_gpio_free(pdata);
 err:
-	release_mem_region(res->start, resource_size(res));
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res)
+		release_mem_region(res->start, resource_size(res));
 	return ret;
 }
 
-- 
1.7.4.4


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

* [PATCH 5/8] mmc: omap_hsmmc: release correct resource
@ 2012-04-18 10:11   ` Russell King
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:11 UTC (permalink / raw)
  To: linux-arm-kernel

res can be one of several resources, as this variable is re-used several
times during probe.  This can cause the wrong resource parameters to be
passed to release_mem_region().

Get the original memory resource before calling release_mem_region().

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/mmc/host/omap_hsmmc.c |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 5c2b1c1..ac26f81a 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -2030,7 +2030,9 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 err_alloc:
 	omap_hsmmc_gpio_free(pdata);
 err:
-	release_mem_region(res->start, resource_size(res));
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res)
+		release_mem_region(res->start, resource_size(res));
 	return ret;
 }
 
-- 
1.7.4.4

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

* [PATCH 6/8] mmc: omap_hsmmc: add DMA engine support
  2012-04-18 10:09 ` Russell King - ARM Linux
@ 2012-04-18 10:12   ` Russell King
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap, linux-mmc; +Cc: Chris Ball

Add DMA engine support to the OMAP HSMMC driver.  This supplements the
private DMA API implementation contained within this driver, and the
driver can be switched at build time between using DMA engine and the
private DMA API.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/mmc/host/omap_hsmmc.c |  192 +++++++++++++++++++++++++++++++++++------
 1 files changed, 165 insertions(+), 27 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index ac26f81a..6c09a80 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -19,6 +19,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/debugfs.h>
+#include <linux/dmaengine.h>
 #include <linux/seq_file.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
@@ -164,7 +165,9 @@ struct omap_hsmmc_host {
 	u32			bytesleft;
 	int			suspended;
 	int			irq;
-	int			use_dma, dma_ch;
+	int			use_dma, dma_ch, dma2;
+	struct dma_chan		*tx_chan;
+	struct dma_chan		*rx_chan;
 	int			dma_line_tx, dma_line_rx;
 	int			slot_id;
 	int			got_dbclk;
@@ -793,18 +796,25 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data)
 		return DMA_FROM_DEVICE;
 }
 
+static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host,
+	struct mmc_data *data)
+{
+	return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
+}
+
 static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
 {
-	int dma_ch;
+	int dma_ch, dma2;
 
 	spin_lock(&host->irq_lock);
 	host->req_in_progress = 0;
 	dma_ch = host->dma_ch;
+	dma2 = host->dma2;
 	spin_unlock(&host->irq_lock);
 
 	omap_hsmmc_disable_irq(host);
 	/* Do not complete the request if DMA is still in progress */
-	if (mrq->data && host->use_dma && dma_ch != -1)
+	if (mrq->data && host->use_dma && (dma_ch != -1 || dma2 != -1))
 		return;
 	host->mrq = NULL;
 	mmc_request_done(host->mmc, mrq);
@@ -873,15 +883,27 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
  */
 static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
 {
-	int dma_ch;
+	int dma_ch, dma2;
 
 	host->data->error = errno;
 
 	spin_lock(&host->irq_lock);
 	dma_ch = host->dma_ch;
 	host->dma_ch = -1;
+	dma2 = host->dma2;
+	host->dma2 = -1;
 	spin_unlock(&host->irq_lock);
 
+	if (host->use_dma && dma2 != -1) {
+		struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data);
+
+		dmaengine_terminate_all(chan);
+		dma_unmap_sg(chan->device->dev,
+			host->data->sg, host->data->sg_len,
+			omap_hsmmc_get_dma_dir(host, host->data));
+
+		host->data->host_cookie = 0;
+	}
 	if (host->use_dma && dma_ch != -1) {
 		dma_unmap_sg(mmc_dev(host->mmc), host->data->sg,
 			host->data->sg_len,
@@ -1277,9 +1299,43 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
 	}
 }
 
+static void omap_hsmmc_dma_callback(void *param)
+{
+	struct omap_hsmmc_host *host = param;
+	struct dma_chan *chan;
+	struct mmc_data *data;
+	int req_in_progress;
+
+	spin_lock_irq(&host->irq_lock);
+	if (host->dma2 < 0) {
+		spin_unlock_irq(&host->irq_lock);
+		return;
+	}
+
+	data = host->mrq->data;
+	chan = omap_hsmmc_get_dma_chan(host, data);
+	if (!data->host_cookie)
+		dma_unmap_sg(chan->device->dev,
+			     data->sg, data->sg_len,
+			     omap_hsmmc_get_dma_dir(host, data));
+
+	req_in_progress = host->req_in_progress;
+	host->dma2 = -1;
+	spin_unlock_irq(&host->irq_lock);
+
+	/* If DMA has finished after TC, complete the request */
+	if (!req_in_progress) {
+		struct mmc_request *mrq = host->mrq;
+
+		host->mrq = NULL;
+		mmc_request_done(host->mmc, mrq);
+	}
+}
+
 static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
 				       struct mmc_data *data,
-				       struct omap_hsmmc_next *next)
+				       struct omap_hsmmc_next *next,
+				       struct device *dev)
 {
 	int dma_len;
 
@@ -1294,8 +1350,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
 	/* Check if next job is already prepared */
 	if (next ||
 	    (!next && data->host_cookie != host->next_data.cookie)) {
-		dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
-				     data->sg_len,
+		dma_len = dma_map_sg(dev, data->sg, data->sg_len,
 				     omap_hsmmc_get_dma_dir(host, data));
 
 	} else {
@@ -1324,6 +1379,7 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
 {
 	int dma_ch = 0, ret = 0, i;
 	struct mmc_data *data = req->data;
+	struct dma_chan *chan;
 
 	/* Sanity check: all the SG entries must be aligned by block size. */
 	for (i = 0; i < data->sg_len; i++) {
@@ -1339,24 +1395,66 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
 		 */
 		return -EINVAL;
 
-	BUG_ON(host->dma_ch != -1);
+	BUG_ON(host->dma_ch != -1 || host->dma2 != -1);
 
-	ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data),
-			       "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
-	if (ret != 0) {
-		dev_err(mmc_dev(host->mmc),
-			"%s: omap_request_dma() failed with %d\n",
-			mmc_hostname(host->mmc), ret);
-		return ret;
-	}
-	ret = omap_hsmmc_pre_dma_transfer(host, data, NULL);
-	if (ret)
-		return ret;
+	chan = omap_hsmmc_get_dma_chan(host, data);
+	if (!chan) {
+		ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data),
+				       "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
+		if (ret != 0) {
+			dev_err(mmc_dev(host->mmc),
+				"%s: omap_request_dma() failed with %d\n",
+				mmc_hostname(host->mmc), ret);
+			return ret;
+		}
+		ret = omap_hsmmc_pre_dma_transfer(host, data, NULL,
+						  mmc_dev(host->mmc));
+		if (ret)
+			return ret;
+
+		host->dma_ch = dma_ch;
+		host->dma_sg_idx = 0;
+
+		omap_hsmmc_config_dma_params(host, data, data->sg);
+	} else {
+		struct dma_slave_config cfg;
+		struct dma_async_tx_descriptor *tx;
+
+		cfg.src_addr = host->mapbase + OMAP_HSMMC_DATA;
+		cfg.dst_addr = host->mapbase + OMAP_HSMMC_DATA;
+		cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		cfg.src_maxburst = data->blksz / 4;
+		cfg.dst_maxburst = data->blksz / 4;
+
+		ret = dmaengine_slave_config(chan, &cfg);
+		if (ret)
+			return ret;
+
+		ret = omap_hsmmc_pre_dma_transfer(host, data, NULL,
+						  chan->device->dev);
+		if (ret)
+			return ret;
+
+		tx = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len,
+			data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+		if (!tx) {
+			dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n");
+			/* FIXME: cleanup */
+			return -1;
+		}
 
-	host->dma_ch = dma_ch;
-	host->dma_sg_idx = 0;
+		tx->callback = omap_hsmmc_dma_callback;
+		tx->callback_param = host;
 
-	omap_hsmmc_config_dma_params(host, data, data->sg);
+		/* Does not fail */
+		dmaengine_submit(tx);
+
+		host->dma2 = 1;
+
+		dma_async_issue_pending(chan);
+	}
 
 	return 0;
 }
@@ -1439,9 +1537,12 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
 	struct mmc_data *data = mrq->data;
 
 	if (host->use_dma) {
+		struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data);
+		struct device *dev = c ? c->device->dev : mmc_dev(mmc);
+
 		if (data->host_cookie)
-			dma_unmap_sg(mmc_dev(host->mmc), data->sg,
-				     data->sg_len,
+			dma_unmap_sg(dev,
+				     data->sg, data->sg_len,
 				     omap_hsmmc_get_dma_dir(host, data));
 		data->host_cookie = 0;
 	}
@@ -1457,10 +1558,14 @@ static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
 		return ;
 	}
 
-	if (host->use_dma)
+	if (host->use_dma) {
+		struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data);
+		struct device *dev = c ? c->device->dev : mmc_dev(mmc);
+
 		if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
-						&host->next_data))
+						&host->next_data, dev))
 			mrq->data->host_cookie = 0;
+	}
 }
 
 /*
@@ -1472,7 +1577,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
 	int err;
 
 	BUG_ON(host->req_in_progress);
-	BUG_ON(host->dma_ch != -1);
+	BUG_ON(host->dma_ch != -1 || host->dma2 != -1);
 	if (host->protect_card) {
 		if (host->reqs_blocked < 3) {
 			/*
@@ -1839,6 +1944,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 	host->use_dma	= 1;
 	host->dev->dma_mask = &pdata->dma_mask;
 	host->dma_ch	= -1;
+	host->dma2	= -1;
 	host->irq	= irq;
 	host->slot_id	= 0;
 	host->mapbase	= res->start + pdata->reg_offset;
@@ -1939,6 +2045,29 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 	}
 	host->dma_line_rx = res->start;
 
+	{
+		dma_cap_mask_t mask;
+		unsigned sig;
+		extern bool omap_dma_filter_fn(struct dma_chan *chan, void *param);
+
+		dma_cap_zero(mask);
+		dma_cap_set(DMA_SLAVE, mask);
+#if 1
+		sig = host->dma_line_rx;
+		host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+		if (!host->rx_chan) {
+			dev_warn(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", sig);
+		}
+#endif
+#if 1
+		sig = host->dma_line_tx;
+		host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+		if (!host->tx_chan) {
+			dev_warn(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", sig);
+		}
+#endif
+	}
+
 	/* Request IRQ for MMC operations */
 	ret = request_irq(host->irq, omap_hsmmc_irq, 0,
 			mmc_hostname(mmc), host);
@@ -2016,6 +2145,10 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 err_irq_cd_init:
 	free_irq(host->irq, host);
 err_irq:
+	if (host->tx_chan)
+		dma_release_channel(host->tx_chan);
+	if (host->rx_chan)
+		dma_release_channel(host->rx_chan);
 	pm_runtime_put_sync(host->dev);
 	pm_runtime_disable(host->dev);
 	clk_put(host->fclk);
@@ -2051,6 +2184,11 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
 	if (mmc_slot(host).card_detect_irq)
 		free_irq(mmc_slot(host).card_detect_irq, host);
 
+	if (host->tx_chan)
+		dma_release_channel(host->tx_chan);
+	if (host->rx_chan)
+		dma_release_channel(host->rx_chan);
+
 	pm_runtime_put_sync(host->dev);
 	pm_runtime_disable(host->dev);
 	clk_put(host->fclk);
-- 
1.7.4.4


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

* [PATCH 6/8] mmc: omap_hsmmc: add DMA engine support
@ 2012-04-18 10:12   ` Russell King
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:12 UTC (permalink / raw)
  To: linux-arm-kernel

Add DMA engine support to the OMAP HSMMC driver.  This supplements the
private DMA API implementation contained within this driver, and the
driver can be switched at build time between using DMA engine and the
private DMA API.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/mmc/host/omap_hsmmc.c |  192 +++++++++++++++++++++++++++++++++++------
 1 files changed, 165 insertions(+), 27 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index ac26f81a..6c09a80 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -19,6 +19,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/debugfs.h>
+#include <linux/dmaengine.h>
 #include <linux/seq_file.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
@@ -164,7 +165,9 @@ struct omap_hsmmc_host {
 	u32			bytesleft;
 	int			suspended;
 	int			irq;
-	int			use_dma, dma_ch;
+	int			use_dma, dma_ch, dma2;
+	struct dma_chan		*tx_chan;
+	struct dma_chan		*rx_chan;
 	int			dma_line_tx, dma_line_rx;
 	int			slot_id;
 	int			got_dbclk;
@@ -793,18 +796,25 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data)
 		return DMA_FROM_DEVICE;
 }
 
+static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host,
+	struct mmc_data *data)
+{
+	return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
+}
+
 static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
 {
-	int dma_ch;
+	int dma_ch, dma2;
 
 	spin_lock(&host->irq_lock);
 	host->req_in_progress = 0;
 	dma_ch = host->dma_ch;
+	dma2 = host->dma2;
 	spin_unlock(&host->irq_lock);
 
 	omap_hsmmc_disable_irq(host);
 	/* Do not complete the request if DMA is still in progress */
-	if (mrq->data && host->use_dma && dma_ch != -1)
+	if (mrq->data && host->use_dma && (dma_ch != -1 || dma2 != -1))
 		return;
 	host->mrq = NULL;
 	mmc_request_done(host->mmc, mrq);
@@ -873,15 +883,27 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
  */
 static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
 {
-	int dma_ch;
+	int dma_ch, dma2;
 
 	host->data->error = errno;
 
 	spin_lock(&host->irq_lock);
 	dma_ch = host->dma_ch;
 	host->dma_ch = -1;
+	dma2 = host->dma2;
+	host->dma2 = -1;
 	spin_unlock(&host->irq_lock);
 
+	if (host->use_dma && dma2 != -1) {
+		struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data);
+
+		dmaengine_terminate_all(chan);
+		dma_unmap_sg(chan->device->dev,
+			host->data->sg, host->data->sg_len,
+			omap_hsmmc_get_dma_dir(host, host->data));
+
+		host->data->host_cookie = 0;
+	}
 	if (host->use_dma && dma_ch != -1) {
 		dma_unmap_sg(mmc_dev(host->mmc), host->data->sg,
 			host->data->sg_len,
@@ -1277,9 +1299,43 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
 	}
 }
 
+static void omap_hsmmc_dma_callback(void *param)
+{
+	struct omap_hsmmc_host *host = param;
+	struct dma_chan *chan;
+	struct mmc_data *data;
+	int req_in_progress;
+
+	spin_lock_irq(&host->irq_lock);
+	if (host->dma2 < 0) {
+		spin_unlock_irq(&host->irq_lock);
+		return;
+	}
+
+	data = host->mrq->data;
+	chan = omap_hsmmc_get_dma_chan(host, data);
+	if (!data->host_cookie)
+		dma_unmap_sg(chan->device->dev,
+			     data->sg, data->sg_len,
+			     omap_hsmmc_get_dma_dir(host, data));
+
+	req_in_progress = host->req_in_progress;
+	host->dma2 = -1;
+	spin_unlock_irq(&host->irq_lock);
+
+	/* If DMA has finished after TC, complete the request */
+	if (!req_in_progress) {
+		struct mmc_request *mrq = host->mrq;
+
+		host->mrq = NULL;
+		mmc_request_done(host->mmc, mrq);
+	}
+}
+
 static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
 				       struct mmc_data *data,
-				       struct omap_hsmmc_next *next)
+				       struct omap_hsmmc_next *next,
+				       struct device *dev)
 {
 	int dma_len;
 
@@ -1294,8 +1350,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
 	/* Check if next job is already prepared */
 	if (next ||
 	    (!next && data->host_cookie != host->next_data.cookie)) {
-		dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
-				     data->sg_len,
+		dma_len = dma_map_sg(dev, data->sg, data->sg_len,
 				     omap_hsmmc_get_dma_dir(host, data));
 
 	} else {
@@ -1324,6 +1379,7 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
 {
 	int dma_ch = 0, ret = 0, i;
 	struct mmc_data *data = req->data;
+	struct dma_chan *chan;
 
 	/* Sanity check: all the SG entries must be aligned by block size. */
 	for (i = 0; i < data->sg_len; i++) {
@@ -1339,24 +1395,66 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
 		 */
 		return -EINVAL;
 
-	BUG_ON(host->dma_ch != -1);
+	BUG_ON(host->dma_ch != -1 || host->dma2 != -1);
 
-	ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data),
-			       "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
-	if (ret != 0) {
-		dev_err(mmc_dev(host->mmc),
-			"%s: omap_request_dma() failed with %d\n",
-			mmc_hostname(host->mmc), ret);
-		return ret;
-	}
-	ret = omap_hsmmc_pre_dma_transfer(host, data, NULL);
-	if (ret)
-		return ret;
+	chan = omap_hsmmc_get_dma_chan(host, data);
+	if (!chan) {
+		ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data),
+				       "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
+		if (ret != 0) {
+			dev_err(mmc_dev(host->mmc),
+				"%s: omap_request_dma() failed with %d\n",
+				mmc_hostname(host->mmc), ret);
+			return ret;
+		}
+		ret = omap_hsmmc_pre_dma_transfer(host, data, NULL,
+						  mmc_dev(host->mmc));
+		if (ret)
+			return ret;
+
+		host->dma_ch = dma_ch;
+		host->dma_sg_idx = 0;
+
+		omap_hsmmc_config_dma_params(host, data, data->sg);
+	} else {
+		struct dma_slave_config cfg;
+		struct dma_async_tx_descriptor *tx;
+
+		cfg.src_addr = host->mapbase + OMAP_HSMMC_DATA;
+		cfg.dst_addr = host->mapbase + OMAP_HSMMC_DATA;
+		cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		cfg.src_maxburst = data->blksz / 4;
+		cfg.dst_maxburst = data->blksz / 4;
+
+		ret = dmaengine_slave_config(chan, &cfg);
+		if (ret)
+			return ret;
+
+		ret = omap_hsmmc_pre_dma_transfer(host, data, NULL,
+						  chan->device->dev);
+		if (ret)
+			return ret;
+
+		tx = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len,
+			data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+		if (!tx) {
+			dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n");
+			/* FIXME: cleanup */
+			return -1;
+		}
 
-	host->dma_ch = dma_ch;
-	host->dma_sg_idx = 0;
+		tx->callback = omap_hsmmc_dma_callback;
+		tx->callback_param = host;
 
-	omap_hsmmc_config_dma_params(host, data, data->sg);
+		/* Does not fail */
+		dmaengine_submit(tx);
+
+		host->dma2 = 1;
+
+		dma_async_issue_pending(chan);
+	}
 
 	return 0;
 }
@@ -1439,9 +1537,12 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
 	struct mmc_data *data = mrq->data;
 
 	if (host->use_dma) {
+		struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data);
+		struct device *dev = c ? c->device->dev : mmc_dev(mmc);
+
 		if (data->host_cookie)
-			dma_unmap_sg(mmc_dev(host->mmc), data->sg,
-				     data->sg_len,
+			dma_unmap_sg(dev,
+				     data->sg, data->sg_len,
 				     omap_hsmmc_get_dma_dir(host, data));
 		data->host_cookie = 0;
 	}
@@ -1457,10 +1558,14 @@ static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
 		return ;
 	}
 
-	if (host->use_dma)
+	if (host->use_dma) {
+		struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data);
+		struct device *dev = c ? c->device->dev : mmc_dev(mmc);
+
 		if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
-						&host->next_data))
+						&host->next_data, dev))
 			mrq->data->host_cookie = 0;
+	}
 }
 
 /*
@@ -1472,7 +1577,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
 	int err;
 
 	BUG_ON(host->req_in_progress);
-	BUG_ON(host->dma_ch != -1);
+	BUG_ON(host->dma_ch != -1 || host->dma2 != -1);
 	if (host->protect_card) {
 		if (host->reqs_blocked < 3) {
 			/*
@@ -1839,6 +1944,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 	host->use_dma	= 1;
 	host->dev->dma_mask = &pdata->dma_mask;
 	host->dma_ch	= -1;
+	host->dma2	= -1;
 	host->irq	= irq;
 	host->slot_id	= 0;
 	host->mapbase	= res->start + pdata->reg_offset;
@@ -1939,6 +2045,29 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 	}
 	host->dma_line_rx = res->start;
 
+	{
+		dma_cap_mask_t mask;
+		unsigned sig;
+		extern bool omap_dma_filter_fn(struct dma_chan *chan, void *param);
+
+		dma_cap_zero(mask);
+		dma_cap_set(DMA_SLAVE, mask);
+#if 1
+		sig = host->dma_line_rx;
+		host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+		if (!host->rx_chan) {
+			dev_warn(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", sig);
+		}
+#endif
+#if 1
+		sig = host->dma_line_tx;
+		host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+		if (!host->tx_chan) {
+			dev_warn(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", sig);
+		}
+#endif
+	}
+
 	/* Request IRQ for MMC operations */
 	ret = request_irq(host->irq, omap_hsmmc_irq, 0,
 			mmc_hostname(mmc), host);
@@ -2016,6 +2145,10 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 err_irq_cd_init:
 	free_irq(host->irq, host);
 err_irq:
+	if (host->tx_chan)
+		dma_release_channel(host->tx_chan);
+	if (host->rx_chan)
+		dma_release_channel(host->rx_chan);
 	pm_runtime_put_sync(host->dev);
 	pm_runtime_disable(host->dev);
 	clk_put(host->fclk);
@@ -2051,6 +2184,11 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
 	if (mmc_slot(host).card_detect_irq)
 		free_irq(mmc_slot(host).card_detect_irq, host);
 
+	if (host->tx_chan)
+		dma_release_channel(host->tx_chan);
+	if (host->rx_chan)
+		dma_release_channel(host->rx_chan);
+
 	pm_runtime_put_sync(host->dev);
 	pm_runtime_disable(host->dev);
 	clk_put(host->fclk);
-- 
1.7.4.4

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

* [PATCH 7/8] mmc: omap_hsmmc: remove private DMA API implementation
  2012-04-18 10:09 ` Russell King - ARM Linux
@ 2012-04-18 10:12   ` Russell King
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap, linux-mmc; +Cc: Chris Ball

Remove the private DMA API implementation from omap_hsmmc, making it
use entirely the DMA engine API.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/mmc/host/omap_hsmmc.c |  264 ++++++++++-------------------------------
 1 files changed, 64 insertions(+), 200 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 6c09a80..fa85efe 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -38,7 +38,6 @@
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
 #include <linux/pm_runtime.h>
-#include <plat/dma.h>
 #include <mach/hardware.h>
 #include <plat/board.h>
 #include <plat/mmc.h>
@@ -165,10 +164,9 @@ struct omap_hsmmc_host {
 	u32			bytesleft;
 	int			suspended;
 	int			irq;
-	int			use_dma, dma_ch, dma2;
+	int			use_dma, dma_ch;
 	struct dma_chan		*tx_chan;
 	struct dma_chan		*rx_chan;
-	int			dma_line_tx, dma_line_rx;
 	int			slot_id;
 	int			got_dbclk;
 	int			response_busy;
@@ -804,17 +802,16 @@ static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host,
 
 static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
 {
-	int dma_ch, dma2;
+	int dma_ch;
 
 	spin_lock(&host->irq_lock);
 	host->req_in_progress = 0;
 	dma_ch = host->dma_ch;
-	dma2 = host->dma2;
 	spin_unlock(&host->irq_lock);
 
 	omap_hsmmc_disable_irq(host);
 	/* Do not complete the request if DMA is still in progress */
-	if (mrq->data && host->use_dma && (dma_ch != -1 || dma2 != -1))
+	if (mrq->data && host->use_dma && dma_ch != -1)
 		return;
 	host->mrq = NULL;
 	mmc_request_done(host->mmc, mrq);
@@ -883,18 +880,16 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
  */
 static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
 {
-	int dma_ch, dma2;
+	int dma_ch;
 
 	host->data->error = errno;
 
 	spin_lock(&host->irq_lock);
 	dma_ch = host->dma_ch;
 	host->dma_ch = -1;
-	dma2 = host->dma2;
-	host->dma2 = -1;
 	spin_unlock(&host->irq_lock);
 
-	if (host->use_dma && dma2 != -1) {
+	if (host->use_dma && dma_ch != -1) {
 		struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data);
 
 		dmaengine_terminate_all(chan);
@@ -904,13 +899,6 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
 
 		host->data->host_cookie = 0;
 	}
-	if (host->use_dma && dma_ch != -1) {
-		dma_unmap_sg(mmc_dev(host->mmc), host->data->sg,
-			host->data->sg_len,
-			omap_hsmmc_get_dma_dir(host, host->data));
-		omap_free_dma(dma_ch);
-		host->data->host_cookie = 0;
-	}
 	host->data = NULL;
 }
 
@@ -1206,99 +1194,6 @@ static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static int omap_hsmmc_get_dma_sync_dev(struct omap_hsmmc_host *host,
-				     struct mmc_data *data)
-{
-	int sync_dev;
-
-	if (data->flags & MMC_DATA_WRITE)
-		sync_dev = host->dma_line_tx;
-	else
-		sync_dev = host->dma_line_rx;
-	return sync_dev;
-}
-
-static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host,
-				       struct mmc_data *data,
-				       struct scatterlist *sgl)
-{
-	int blksz, nblk, dma_ch;
-
-	dma_ch = host->dma_ch;
-	if (data->flags & MMC_DATA_WRITE) {
-		omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
-			(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
-		omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
-			sg_dma_address(sgl), 0, 0);
-	} else {
-		omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
-			(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
-		omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
-			sg_dma_address(sgl), 0, 0);
-	}
-
-	blksz = host->data->blksz;
-	nblk = sg_dma_len(sgl) / blksz;
-
-	omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
-			blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
-			omap_hsmmc_get_dma_sync_dev(host, data),
-			!(data->flags & MMC_DATA_WRITE));
-
-	omap_start_dma(dma_ch);
-}
-
-/*
- * DMA call back function
- */
-static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
-{
-	struct omap_hsmmc_host *host = cb_data;
-	struct mmc_data *data;
-	int dma_ch, req_in_progress;
-
-	if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
-		dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
-			ch_status);
-		return;
-	}
-
-	spin_lock(&host->irq_lock);
-	if (host->dma_ch < 0) {
-		spin_unlock(&host->irq_lock);
-		return;
-	}
-
-	data = host->mrq->data;
-	host->dma_sg_idx++;
-	if (host->dma_sg_idx < host->dma_len) {
-		/* Fire up the next transfer. */
-		omap_hsmmc_config_dma_params(host, data,
-					   data->sg + host->dma_sg_idx);
-		spin_unlock(&host->irq_lock);
-		return;
-	}
-
-	if (!data->host_cookie)
-		dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
-			     omap_hsmmc_get_dma_dir(host, data));
-
-	req_in_progress = host->req_in_progress;
-	dma_ch = host->dma_ch;
-	host->dma_ch = -1;
-	spin_unlock(&host->irq_lock);
-
-	omap_free_dma(dma_ch);
-
-	/* If DMA has finished after TC, complete the request */
-	if (!req_in_progress) {
-		struct mmc_request *mrq = host->mrq;
-
-		host->mrq = NULL;
-		mmc_request_done(host->mmc, mrq);
-	}
-}
-
 static void omap_hsmmc_dma_callback(void *param)
 {
 	struct omap_hsmmc_host *host = param;
@@ -1307,7 +1202,7 @@ static void omap_hsmmc_dma_callback(void *param)
 	int req_in_progress;
 
 	spin_lock_irq(&host->irq_lock);
-	if (host->dma2 < 0) {
+	if (host->dma_ch < 0) {
 		spin_unlock_irq(&host->irq_lock);
 		return;
 	}
@@ -1320,7 +1215,7 @@ static void omap_hsmmc_dma_callback(void *param)
 			     omap_hsmmc_get_dma_dir(host, data));
 
 	req_in_progress = host->req_in_progress;
-	host->dma2 = -1;
+	host->dma_ch = -1;
 	spin_unlock_irq(&host->irq_lock);
 
 	/* If DMA has finished after TC, complete the request */
@@ -1335,7 +1230,7 @@ static void omap_hsmmc_dma_callback(void *param)
 static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
 				       struct mmc_data *data,
 				       struct omap_hsmmc_next *next,
-				       struct device *dev)
+				       struct dma_chan *chan)
 {
 	int dma_len;
 
@@ -1350,7 +1245,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
 	/* Check if next job is already prepared */
 	if (next ||
 	    (!next && data->host_cookie != host->next_data.cookie)) {
-		dma_len = dma_map_sg(dev, data->sg, data->sg_len,
+		dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
 				     omap_hsmmc_get_dma_dir(host, data));
 
 	} else {
@@ -1377,7 +1272,9 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
 static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
 					struct mmc_request *req)
 {
-	int dma_ch = 0, ret = 0, i;
+	struct dma_slave_config cfg;
+	struct dma_async_tx_descriptor *tx;
+	int ret = 0, i;
 	struct mmc_data *data = req->data;
 	struct dma_chan *chan;
 
@@ -1395,66 +1292,43 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
 		 */
 		return -EINVAL;
 
-	BUG_ON(host->dma_ch != -1 || host->dma2 != -1);
+	BUG_ON(host->dma_ch != -1);
 
 	chan = omap_hsmmc_get_dma_chan(host, data);
-	if (!chan) {
-		ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data),
-				       "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
-		if (ret != 0) {
-			dev_err(mmc_dev(host->mmc),
-				"%s: omap_request_dma() failed with %d\n",
-				mmc_hostname(host->mmc), ret);
-			return ret;
-		}
-		ret = omap_hsmmc_pre_dma_transfer(host, data, NULL,
-						  mmc_dev(host->mmc));
-		if (ret)
-			return ret;
-
-		host->dma_ch = dma_ch;
-		host->dma_sg_idx = 0;
 
-		omap_hsmmc_config_dma_params(host, data, data->sg);
-	} else {
-		struct dma_slave_config cfg;
-		struct dma_async_tx_descriptor *tx;
+	cfg.src_addr = host->mapbase + OMAP_HSMMC_DATA;
+	cfg.dst_addr = host->mapbase + OMAP_HSMMC_DATA;
+	cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	cfg.src_maxburst = data->blksz / 4;
+	cfg.dst_maxburst = data->blksz / 4;
 
-		cfg.src_addr = host->mapbase + OMAP_HSMMC_DATA;
-		cfg.dst_addr = host->mapbase + OMAP_HSMMC_DATA;
-		cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-		cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-		cfg.src_maxburst = data->blksz / 4;
-		cfg.dst_maxburst = data->blksz / 4;
-
-		ret = dmaengine_slave_config(chan, &cfg);
-		if (ret)
-			return ret;
+	ret = dmaengine_slave_config(chan, &cfg);
+	if (ret)
+		return ret;
 
-		ret = omap_hsmmc_pre_dma_transfer(host, data, NULL,
-						  chan->device->dev);
-		if (ret)
-			return ret;
+	ret = omap_hsmmc_pre_dma_transfer(host, data, NULL, chan);
+	if (ret)
+		return ret;
 
-		tx = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len,
-			data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
-			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
-		if (!tx) {
-			dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n");
-			/* FIXME: cleanup */
-			return -1;
-		}
+	tx = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len,
+		data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+		DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!tx) {
+		dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n");
+		/* FIXME: cleanup */
+		return -1;
+	}
 
-		tx->callback = omap_hsmmc_dma_callback;
-		tx->callback_param = host;
+	tx->callback = omap_hsmmc_dma_callback;
+	tx->callback_param = host;
 
-		/* Does not fail */
-		dmaengine_submit(tx);
+	/* Does not fail */
+	dmaengine_submit(tx);
 
-		host->dma2 = 1;
+	host->dma_ch = 1;
 
-		dma_async_issue_pending(chan);
-	}
+	dma_async_issue_pending(chan);
 
 	return 0;
 }
@@ -1536,14 +1410,11 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
 	struct omap_hsmmc_host *host = mmc_priv(mmc);
 	struct mmc_data *data = mrq->data;
 
-	if (host->use_dma) {
+	if (host->use_dma && data->host_cookie) {
 		struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data);
-		struct device *dev = c ? c->device->dev : mmc_dev(mmc);
 
-		if (data->host_cookie)
-			dma_unmap_sg(dev,
-				     data->sg, data->sg_len,
-				     omap_hsmmc_get_dma_dir(host, data));
+		dma_unmap_sg(c->device->dev, data->sg, data->sg_len,
+			     omap_hsmmc_get_dma_dir(host, data));
 		data->host_cookie = 0;
 	}
 }
@@ -1560,10 +1431,9 @@ static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
 
 	if (host->use_dma) {
 		struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data);
-		struct device *dev = c ? c->device->dev : mmc_dev(mmc);
 
 		if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
-						&host->next_data, dev))
+						&host->next_data, c))
 			mrq->data->host_cookie = 0;
 	}
 }
@@ -1577,7 +1447,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
 	int err;
 
 	BUG_ON(host->req_in_progress);
-	BUG_ON(host->dma_ch != -1 || host->dma2 != -1);
+	BUG_ON(host->dma_ch != -1);
 	if (host->protect_card) {
 		if (host->reqs_blocked < 3) {
 			/*
@@ -1890,6 +1760,8 @@ static inline struct omap_mmc_platform_data
 }
 #endif
 
+extern bool omap_dma_filter_fn(struct dma_chan *chan, void *param);
+
 static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 {
 	struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
@@ -1898,6 +1770,8 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 	struct resource *res;
 	int ret, irq;
 	const struct of_device_id *match;
+	dma_cap_mask_t mask;
+	unsigned tx_req, rx_req;
 
 	match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
 	if (match) {
@@ -1942,9 +1816,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 	host->pdata	= pdata;
 	host->dev	= &pdev->dev;
 	host->use_dma	= 1;
-	host->dev->dma_mask = &pdata->dma_mask;
 	host->dma_ch	= -1;
-	host->dma2	= -1;
 	host->irq	= irq;
 	host->slot_id	= 0;
 	host->mapbase	= res->start + pdata->reg_offset;
@@ -2036,36 +1908,28 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 		dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n");
 		goto err_irq;
 	}
-	host->dma_line_tx = res->start;
+	tx_req = res->start;
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
 	if (!res) {
 		dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n");
 		goto err_irq;
 	}
-	host->dma_line_rx = res->start;
+	rx_req = res->start;
 
-	{
-		dma_cap_mask_t mask;
-		unsigned sig;
-		extern bool omap_dma_filter_fn(struct dma_chan *chan, void *param);
-
-		dma_cap_zero(mask);
-		dma_cap_set(DMA_SLAVE, mask);
-#if 1
-		sig = host->dma_line_rx;
-		host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &sig);
-		if (!host->rx_chan) {
-			dev_warn(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", sig);
-		}
-#endif
-#if 1
-		sig = host->dma_line_tx;
-		host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &sig);
-		if (!host->tx_chan) {
-			dev_warn(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", sig);
-		}
-#endif
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &rx_req);
+	if (!host->rx_chan) {
+		dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
+		goto err_irq;
+	}
+
+	host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &tx_req);
+	if (!host->tx_chan) {
+		dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req);
+		goto err_irq;
 	}
 
 	/* Request IRQ for MMC operations */
-- 
1.7.4.4


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

* [PATCH 7/8] mmc: omap_hsmmc: remove private DMA API implementation
@ 2012-04-18 10:12   ` Russell King
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:12 UTC (permalink / raw)
  To: linux-arm-kernel

Remove the private DMA API implementation from omap_hsmmc, making it
use entirely the DMA engine API.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/mmc/host/omap_hsmmc.c |  264 ++++++++++-------------------------------
 1 files changed, 64 insertions(+), 200 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 6c09a80..fa85efe 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -38,7 +38,6 @@
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
 #include <linux/pm_runtime.h>
-#include <plat/dma.h>
 #include <mach/hardware.h>
 #include <plat/board.h>
 #include <plat/mmc.h>
@@ -165,10 +164,9 @@ struct omap_hsmmc_host {
 	u32			bytesleft;
 	int			suspended;
 	int			irq;
-	int			use_dma, dma_ch, dma2;
+	int			use_dma, dma_ch;
 	struct dma_chan		*tx_chan;
 	struct dma_chan		*rx_chan;
-	int			dma_line_tx, dma_line_rx;
 	int			slot_id;
 	int			got_dbclk;
 	int			response_busy;
@@ -804,17 +802,16 @@ static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host,
 
 static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
 {
-	int dma_ch, dma2;
+	int dma_ch;
 
 	spin_lock(&host->irq_lock);
 	host->req_in_progress = 0;
 	dma_ch = host->dma_ch;
-	dma2 = host->dma2;
 	spin_unlock(&host->irq_lock);
 
 	omap_hsmmc_disable_irq(host);
 	/* Do not complete the request if DMA is still in progress */
-	if (mrq->data && host->use_dma && (dma_ch != -1 || dma2 != -1))
+	if (mrq->data && host->use_dma && dma_ch != -1)
 		return;
 	host->mrq = NULL;
 	mmc_request_done(host->mmc, mrq);
@@ -883,18 +880,16 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
  */
 static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
 {
-	int dma_ch, dma2;
+	int dma_ch;
 
 	host->data->error = errno;
 
 	spin_lock(&host->irq_lock);
 	dma_ch = host->dma_ch;
 	host->dma_ch = -1;
-	dma2 = host->dma2;
-	host->dma2 = -1;
 	spin_unlock(&host->irq_lock);
 
-	if (host->use_dma && dma2 != -1) {
+	if (host->use_dma && dma_ch != -1) {
 		struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data);
 
 		dmaengine_terminate_all(chan);
@@ -904,13 +899,6 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
 
 		host->data->host_cookie = 0;
 	}
-	if (host->use_dma && dma_ch != -1) {
-		dma_unmap_sg(mmc_dev(host->mmc), host->data->sg,
-			host->data->sg_len,
-			omap_hsmmc_get_dma_dir(host, host->data));
-		omap_free_dma(dma_ch);
-		host->data->host_cookie = 0;
-	}
 	host->data = NULL;
 }
 
@@ -1206,99 +1194,6 @@ static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static int omap_hsmmc_get_dma_sync_dev(struct omap_hsmmc_host *host,
-				     struct mmc_data *data)
-{
-	int sync_dev;
-
-	if (data->flags & MMC_DATA_WRITE)
-		sync_dev = host->dma_line_tx;
-	else
-		sync_dev = host->dma_line_rx;
-	return sync_dev;
-}
-
-static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host,
-				       struct mmc_data *data,
-				       struct scatterlist *sgl)
-{
-	int blksz, nblk, dma_ch;
-
-	dma_ch = host->dma_ch;
-	if (data->flags & MMC_DATA_WRITE) {
-		omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
-			(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
-		omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
-			sg_dma_address(sgl), 0, 0);
-	} else {
-		omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
-			(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
-		omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
-			sg_dma_address(sgl), 0, 0);
-	}
-
-	blksz = host->data->blksz;
-	nblk = sg_dma_len(sgl) / blksz;
-
-	omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
-			blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
-			omap_hsmmc_get_dma_sync_dev(host, data),
-			!(data->flags & MMC_DATA_WRITE));
-
-	omap_start_dma(dma_ch);
-}
-
-/*
- * DMA call back function
- */
-static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
-{
-	struct omap_hsmmc_host *host = cb_data;
-	struct mmc_data *data;
-	int dma_ch, req_in_progress;
-
-	if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
-		dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
-			ch_status);
-		return;
-	}
-
-	spin_lock(&host->irq_lock);
-	if (host->dma_ch < 0) {
-		spin_unlock(&host->irq_lock);
-		return;
-	}
-
-	data = host->mrq->data;
-	host->dma_sg_idx++;
-	if (host->dma_sg_idx < host->dma_len) {
-		/* Fire up the next transfer. */
-		omap_hsmmc_config_dma_params(host, data,
-					   data->sg + host->dma_sg_idx);
-		spin_unlock(&host->irq_lock);
-		return;
-	}
-
-	if (!data->host_cookie)
-		dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
-			     omap_hsmmc_get_dma_dir(host, data));
-
-	req_in_progress = host->req_in_progress;
-	dma_ch = host->dma_ch;
-	host->dma_ch = -1;
-	spin_unlock(&host->irq_lock);
-
-	omap_free_dma(dma_ch);
-
-	/* If DMA has finished after TC, complete the request */
-	if (!req_in_progress) {
-		struct mmc_request *mrq = host->mrq;
-
-		host->mrq = NULL;
-		mmc_request_done(host->mmc, mrq);
-	}
-}
-
 static void omap_hsmmc_dma_callback(void *param)
 {
 	struct omap_hsmmc_host *host = param;
@@ -1307,7 +1202,7 @@ static void omap_hsmmc_dma_callback(void *param)
 	int req_in_progress;
 
 	spin_lock_irq(&host->irq_lock);
-	if (host->dma2 < 0) {
+	if (host->dma_ch < 0) {
 		spin_unlock_irq(&host->irq_lock);
 		return;
 	}
@@ -1320,7 +1215,7 @@ static void omap_hsmmc_dma_callback(void *param)
 			     omap_hsmmc_get_dma_dir(host, data));
 
 	req_in_progress = host->req_in_progress;
-	host->dma2 = -1;
+	host->dma_ch = -1;
 	spin_unlock_irq(&host->irq_lock);
 
 	/* If DMA has finished after TC, complete the request */
@@ -1335,7 +1230,7 @@ static void omap_hsmmc_dma_callback(void *param)
 static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
 				       struct mmc_data *data,
 				       struct omap_hsmmc_next *next,
-				       struct device *dev)
+				       struct dma_chan *chan)
 {
 	int dma_len;
 
@@ -1350,7 +1245,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
 	/* Check if next job is already prepared */
 	if (next ||
 	    (!next && data->host_cookie != host->next_data.cookie)) {
-		dma_len = dma_map_sg(dev, data->sg, data->sg_len,
+		dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
 				     omap_hsmmc_get_dma_dir(host, data));
 
 	} else {
@@ -1377,7 +1272,9 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
 static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
 					struct mmc_request *req)
 {
-	int dma_ch = 0, ret = 0, i;
+	struct dma_slave_config cfg;
+	struct dma_async_tx_descriptor *tx;
+	int ret = 0, i;
 	struct mmc_data *data = req->data;
 	struct dma_chan *chan;
 
@@ -1395,66 +1292,43 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
 		 */
 		return -EINVAL;
 
-	BUG_ON(host->dma_ch != -1 || host->dma2 != -1);
+	BUG_ON(host->dma_ch != -1);
 
 	chan = omap_hsmmc_get_dma_chan(host, data);
-	if (!chan) {
-		ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data),
-				       "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
-		if (ret != 0) {
-			dev_err(mmc_dev(host->mmc),
-				"%s: omap_request_dma() failed with %d\n",
-				mmc_hostname(host->mmc), ret);
-			return ret;
-		}
-		ret = omap_hsmmc_pre_dma_transfer(host, data, NULL,
-						  mmc_dev(host->mmc));
-		if (ret)
-			return ret;
-
-		host->dma_ch = dma_ch;
-		host->dma_sg_idx = 0;
 
-		omap_hsmmc_config_dma_params(host, data, data->sg);
-	} else {
-		struct dma_slave_config cfg;
-		struct dma_async_tx_descriptor *tx;
+	cfg.src_addr = host->mapbase + OMAP_HSMMC_DATA;
+	cfg.dst_addr = host->mapbase + OMAP_HSMMC_DATA;
+	cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	cfg.src_maxburst = data->blksz / 4;
+	cfg.dst_maxburst = data->blksz / 4;
 
-		cfg.src_addr = host->mapbase + OMAP_HSMMC_DATA;
-		cfg.dst_addr = host->mapbase + OMAP_HSMMC_DATA;
-		cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-		cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-		cfg.src_maxburst = data->blksz / 4;
-		cfg.dst_maxburst = data->blksz / 4;
-
-		ret = dmaengine_slave_config(chan, &cfg);
-		if (ret)
-			return ret;
+	ret = dmaengine_slave_config(chan, &cfg);
+	if (ret)
+		return ret;
 
-		ret = omap_hsmmc_pre_dma_transfer(host, data, NULL,
-						  chan->device->dev);
-		if (ret)
-			return ret;
+	ret = omap_hsmmc_pre_dma_transfer(host, data, NULL, chan);
+	if (ret)
+		return ret;
 
-		tx = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len,
-			data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
-			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
-		if (!tx) {
-			dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n");
-			/* FIXME: cleanup */
-			return -1;
-		}
+	tx = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len,
+		data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+		DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!tx) {
+		dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n");
+		/* FIXME: cleanup */
+		return -1;
+	}
 
-		tx->callback = omap_hsmmc_dma_callback;
-		tx->callback_param = host;
+	tx->callback = omap_hsmmc_dma_callback;
+	tx->callback_param = host;
 
-		/* Does not fail */
-		dmaengine_submit(tx);
+	/* Does not fail */
+	dmaengine_submit(tx);
 
-		host->dma2 = 1;
+	host->dma_ch = 1;
 
-		dma_async_issue_pending(chan);
-	}
+	dma_async_issue_pending(chan);
 
 	return 0;
 }
@@ -1536,14 +1410,11 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
 	struct omap_hsmmc_host *host = mmc_priv(mmc);
 	struct mmc_data *data = mrq->data;
 
-	if (host->use_dma) {
+	if (host->use_dma && data->host_cookie) {
 		struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data);
-		struct device *dev = c ? c->device->dev : mmc_dev(mmc);
 
-		if (data->host_cookie)
-			dma_unmap_sg(dev,
-				     data->sg, data->sg_len,
-				     omap_hsmmc_get_dma_dir(host, data));
+		dma_unmap_sg(c->device->dev, data->sg, data->sg_len,
+			     omap_hsmmc_get_dma_dir(host, data));
 		data->host_cookie = 0;
 	}
 }
@@ -1560,10 +1431,9 @@ static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
 
 	if (host->use_dma) {
 		struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data);
-		struct device *dev = c ? c->device->dev : mmc_dev(mmc);
 
 		if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
-						&host->next_data, dev))
+						&host->next_data, c))
 			mrq->data->host_cookie = 0;
 	}
 }
@@ -1577,7 +1447,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
 	int err;
 
 	BUG_ON(host->req_in_progress);
-	BUG_ON(host->dma_ch != -1 || host->dma2 != -1);
+	BUG_ON(host->dma_ch != -1);
 	if (host->protect_card) {
 		if (host->reqs_blocked < 3) {
 			/*
@@ -1890,6 +1760,8 @@ static inline struct omap_mmc_platform_data
 }
 #endif
 
+extern bool omap_dma_filter_fn(struct dma_chan *chan, void *param);
+
 static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 {
 	struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
@@ -1898,6 +1770,8 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 	struct resource *res;
 	int ret, irq;
 	const struct of_device_id *match;
+	dma_cap_mask_t mask;
+	unsigned tx_req, rx_req;
 
 	match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
 	if (match) {
@@ -1942,9 +1816,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 	host->pdata	= pdata;
 	host->dev	= &pdev->dev;
 	host->use_dma	= 1;
-	host->dev->dma_mask = &pdata->dma_mask;
 	host->dma_ch	= -1;
-	host->dma2	= -1;
 	host->irq	= irq;
 	host->slot_id	= 0;
 	host->mapbase	= res->start + pdata->reg_offset;
@@ -2036,36 +1908,28 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 		dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n");
 		goto err_irq;
 	}
-	host->dma_line_tx = res->start;
+	tx_req = res->start;
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
 	if (!res) {
 		dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n");
 		goto err_irq;
 	}
-	host->dma_line_rx = res->start;
+	rx_req = res->start;
 
-	{
-		dma_cap_mask_t mask;
-		unsigned sig;
-		extern bool omap_dma_filter_fn(struct dma_chan *chan, void *param);
-
-		dma_cap_zero(mask);
-		dma_cap_set(DMA_SLAVE, mask);
-#if 1
-		sig = host->dma_line_rx;
-		host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &sig);
-		if (!host->rx_chan) {
-			dev_warn(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", sig);
-		}
-#endif
-#if 1
-		sig = host->dma_line_tx;
-		host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &sig);
-		if (!host->tx_chan) {
-			dev_warn(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", sig);
-		}
-#endif
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &rx_req);
+	if (!host->rx_chan) {
+		dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
+		goto err_irq;
+	}
+
+	host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &tx_req);
+	if (!host->tx_chan) {
+		dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req);
+		goto err_irq;
 	}
 
 	/* Request IRQ for MMC operations */
-- 
1.7.4.4

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 10:09 ` Russell King - ARM Linux
@ 2012-04-18 10:12   ` Russell King
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap, linux-mmc; +Cc: Tony Lindgren

DMAengine uses the DMA engine device structure when mapping/unmapping
memory for DMA, so the MMC devices do not need their DMA masks
initialized (this reflects hardware: the MMC device is not the device
doing DMA.)

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 arch/arm/mach-omap1/board-h2-mmc.c    |    1 -
 arch/arm/mach-omap1/board-h3-mmc.c    |    1 -
 arch/arm/mach-omap1/board-nokia770.c  |    1 -
 arch/arm/mach-omap2/board-n8x0.c      |    1 -
 arch/arm/mach-omap2/hsmmc.c           |    1 -
 arch/arm/plat-omap/include/plat/mmc.h |    2 --
 6 files changed, 0 insertions(+), 7 deletions(-)

diff --git a/arch/arm/mach-omap1/board-h2-mmc.c b/arch/arm/mach-omap1/board-h2-mmc.c
index da0e37d..e1362ce 100644
--- a/arch/arm/mach-omap1/board-h2-mmc.c
+++ b/arch/arm/mach-omap1/board-h2-mmc.c
@@ -54,7 +54,6 @@ static struct omap_mmc_platform_data mmc1_data = {
 	.nr_slots                       = 1,
 	.init				= mmc_late_init,
 	.cleanup			= mmc_cleanup,
-	.dma_mask			= 0xffffffff,
 	.slots[0]       = {
 		.set_power              = mmc_set_power,
 		.ocr_mask               = MMC_VDD_32_33 | MMC_VDD_33_34,
diff --git a/arch/arm/mach-omap1/board-h3-mmc.c b/arch/arm/mach-omap1/board-h3-mmc.c
index f8242aa..c74daac 100644
--- a/arch/arm/mach-omap1/board-h3-mmc.c
+++ b/arch/arm/mach-omap1/board-h3-mmc.c
@@ -36,7 +36,6 @@ static int mmc_set_power(struct device *dev, int slot, int power_on,
  */
 static struct omap_mmc_platform_data mmc1_data = {
 	.nr_slots                       = 1,
-	.dma_mask			= 0xffffffff,
 	.slots[0]       = {
 		.set_power              = mmc_set_power,
 		.ocr_mask               = MMC_VDD_32_33 | MMC_VDD_33_34,
diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c
index d21dcc2..cac9954 100644
--- a/arch/arm/mach-omap1/board-nokia770.c
+++ b/arch/arm/mach-omap1/board-nokia770.c
@@ -185,7 +185,6 @@ static int nokia770_mmc_get_cover_state(struct device *dev, int slot)
 
 static struct omap_mmc_platform_data nokia770_mmc2_data = {
 	.nr_slots                       = 1,
-	.dma_mask			= 0xffffffff,
 	.max_freq                       = 12000000,
 	.slots[0]       = {
 		.set_power		= nokia770_mmc_set_power,
diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c
index 518091c..1003748 100644
--- a/arch/arm/mach-omap2/board-n8x0.c
+++ b/arch/arm/mach-omap2/board-n8x0.c
@@ -470,7 +470,6 @@ static struct omap_mmc_platform_data mmc1_data = {
 	.cleanup			= n8x0_mmc_cleanup,
 	.shutdown			= n8x0_mmc_shutdown,
 	.max_freq			= 24000000,
-	.dma_mask			= 0xffffffff,
 	.slots[0] = {
 		.wires			= 4,
 		.set_power		= n8x0_mmc_set_power,
diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c
index b0268ea..41e5277 100644
--- a/arch/arm/mach-omap2/hsmmc.c
+++ b/arch/arm/mach-omap2/hsmmc.c
@@ -315,7 +315,6 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
 	mmc->slots[0].caps = c->caps;
 	mmc->slots[0].pm_caps = c->pm_caps;
 	mmc->slots[0].internal_clock = !c->ext_clock;
-	mmc->dma_mask = 0xffffffff;
 	mmc->max_freq = c->max_freq;
 	if (cpu_is_omap44xx())
 		mmc->reg_offset = OMAP4_MMC_REG_OFFSET;
diff --git a/arch/arm/plat-omap/include/plat/mmc.h b/arch/arm/plat-omap/include/plat/mmc.h
index 7a38750..9fe87a7 100644
--- a/arch/arm/plat-omap/include/plat/mmc.h
+++ b/arch/arm/plat-omap/include/plat/mmc.h
@@ -80,8 +80,6 @@ struct omap_mmc_platform_data {
 	/* Return context loss count due to PM states changing */
 	int (*get_context_loss_count)(struct device *dev);
 
-	u64 dma_mask;
-
 	/* Integrating attributes from the omap_hwmod layer */
 	u8 controller_flags;
 
-- 
1.7.4.4


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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-18 10:12   ` Russell King
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King @ 2012-04-18 10:12 UTC (permalink / raw)
  To: linux-arm-kernel

DMAengine uses the DMA engine device structure when mapping/unmapping
memory for DMA, so the MMC devices do not need their DMA masks
initialized (this reflects hardware: the MMC device is not the device
doing DMA.)

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 arch/arm/mach-omap1/board-h2-mmc.c    |    1 -
 arch/arm/mach-omap1/board-h3-mmc.c    |    1 -
 arch/arm/mach-omap1/board-nokia770.c  |    1 -
 arch/arm/mach-omap2/board-n8x0.c      |    1 -
 arch/arm/mach-omap2/hsmmc.c           |    1 -
 arch/arm/plat-omap/include/plat/mmc.h |    2 --
 6 files changed, 0 insertions(+), 7 deletions(-)

diff --git a/arch/arm/mach-omap1/board-h2-mmc.c b/arch/arm/mach-omap1/board-h2-mmc.c
index da0e37d..e1362ce 100644
--- a/arch/arm/mach-omap1/board-h2-mmc.c
+++ b/arch/arm/mach-omap1/board-h2-mmc.c
@@ -54,7 +54,6 @@ static struct omap_mmc_platform_data mmc1_data = {
 	.nr_slots                       = 1,
 	.init				= mmc_late_init,
 	.cleanup			= mmc_cleanup,
-	.dma_mask			= 0xffffffff,
 	.slots[0]       = {
 		.set_power              = mmc_set_power,
 		.ocr_mask               = MMC_VDD_32_33 | MMC_VDD_33_34,
diff --git a/arch/arm/mach-omap1/board-h3-mmc.c b/arch/arm/mach-omap1/board-h3-mmc.c
index f8242aa..c74daac 100644
--- a/arch/arm/mach-omap1/board-h3-mmc.c
+++ b/arch/arm/mach-omap1/board-h3-mmc.c
@@ -36,7 +36,6 @@ static int mmc_set_power(struct device *dev, int slot, int power_on,
  */
 static struct omap_mmc_platform_data mmc1_data = {
 	.nr_slots                       = 1,
-	.dma_mask			= 0xffffffff,
 	.slots[0]       = {
 		.set_power              = mmc_set_power,
 		.ocr_mask               = MMC_VDD_32_33 | MMC_VDD_33_34,
diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c
index d21dcc2..cac9954 100644
--- a/arch/arm/mach-omap1/board-nokia770.c
+++ b/arch/arm/mach-omap1/board-nokia770.c
@@ -185,7 +185,6 @@ static int nokia770_mmc_get_cover_state(struct device *dev, int slot)
 
 static struct omap_mmc_platform_data nokia770_mmc2_data = {
 	.nr_slots                       = 1,
-	.dma_mask			= 0xffffffff,
 	.max_freq                       = 12000000,
 	.slots[0]       = {
 		.set_power		= nokia770_mmc_set_power,
diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c
index 518091c..1003748 100644
--- a/arch/arm/mach-omap2/board-n8x0.c
+++ b/arch/arm/mach-omap2/board-n8x0.c
@@ -470,7 +470,6 @@ static struct omap_mmc_platform_data mmc1_data = {
 	.cleanup			= n8x0_mmc_cleanup,
 	.shutdown			= n8x0_mmc_shutdown,
 	.max_freq			= 24000000,
-	.dma_mask			= 0xffffffff,
 	.slots[0] = {
 		.wires			= 4,
 		.set_power		= n8x0_mmc_set_power,
diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c
index b0268ea..41e5277 100644
--- a/arch/arm/mach-omap2/hsmmc.c
+++ b/arch/arm/mach-omap2/hsmmc.c
@@ -315,7 +315,6 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
 	mmc->slots[0].caps = c->caps;
 	mmc->slots[0].pm_caps = c->pm_caps;
 	mmc->slots[0].internal_clock = !c->ext_clock;
-	mmc->dma_mask = 0xffffffff;
 	mmc->max_freq = c->max_freq;
 	if (cpu_is_omap44xx())
 		mmc->reg_offset = OMAP4_MMC_REG_OFFSET;
diff --git a/arch/arm/plat-omap/include/plat/mmc.h b/arch/arm/plat-omap/include/plat/mmc.h
index 7a38750..9fe87a7 100644
--- a/arch/arm/plat-omap/include/plat/mmc.h
+++ b/arch/arm/plat-omap/include/plat/mmc.h
@@ -80,8 +80,6 @@ struct omap_mmc_platform_data {
 	/* Return context loss count due to PM states changing */
 	int (*get_context_loss_count)(struct device *dev);
 
-	u64 dma_mask;
-
 	/* Integrating attributes from the omap_hwmod layer */
 	u8 controller_flags;
 
-- 
1.7.4.4

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

* Re: [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
  2012-04-18 10:10   ` Russell King
@ 2012-04-18 10:15     ` Felipe Balbi
  -1 siblings, 0 replies; 120+ messages in thread
From: Felipe Balbi @ 2012-04-18 10:15 UTC (permalink / raw)
  To: Russell King; +Cc: linux-arm-kernel, linux-omap, linux-mmc, Tony Lindgren

[-- Attachment #1: Type: text/plain, Size: 894 bytes --]

On Wed, Apr 18, 2012 at 11:10:35AM +0100, Russell King wrote:
> Using coherent DMA memory with the OMAP DMA engine results in
> unpredictable behaviour due to memory ordering issues; as things stand,
> there is no guarantee that data written to coherent DMA memory will be
> visible to the DMA hardware.
> 
> This is because the OMAP dma_write() accessor contains no barriers,
> necessary on ARMv6 and above.  The effect of this can be seen in comments
> in the OMAP serial driver, which incorrectly talks about cache flushing
> for the coherent DMA stuff.
> 
> Rather than adding barriers to the accessors, add it in the DMA support
> code just before we enable DMA, and just after we disable DMA.  This
> avoids having barriers for every DMA register access.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

cool, should this go to stable too ?

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

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

* [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
@ 2012-04-18 10:15     ` Felipe Balbi
  0 siblings, 0 replies; 120+ messages in thread
From: Felipe Balbi @ 2012-04-18 10:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 11:10:35AM +0100, Russell King wrote:
> Using coherent DMA memory with the OMAP DMA engine results in
> unpredictable behaviour due to memory ordering issues; as things stand,
> there is no guarantee that data written to coherent DMA memory will be
> visible to the DMA hardware.
> 
> This is because the OMAP dma_write() accessor contains no barriers,
> necessary on ARMv6 and above.  The effect of this can be seen in comments
> in the OMAP serial driver, which incorrectly talks about cache flushing
> for the coherent DMA stuff.
> 
> Rather than adding barriers to the accessors, add it in the DMA support
> code just before we enable DMA, and just after we disable DMA.  This
> avoids having barriers for every DMA register access.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

cool, should this go to stable too ?

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120418/e0c8a6c0/attachment.sig>

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

* Re: [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
  2012-04-18 10:15     ` Felipe Balbi
@ 2012-04-18 10:17       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 10:17 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: linux-arm-kernel, linux-omap, linux-mmc, Tony Lindgren

On Wed, Apr 18, 2012 at 01:15:20PM +0300, Felipe Balbi wrote:
> On Wed, Apr 18, 2012 at 11:10:35AM +0100, Russell King wrote:
> > Using coherent DMA memory with the OMAP DMA engine results in
> > unpredictable behaviour due to memory ordering issues; as things stand,
> > there is no guarantee that data written to coherent DMA memory will be
> > visible to the DMA hardware.
> > 
> > This is because the OMAP dma_write() accessor contains no barriers,
> > necessary on ARMv6 and above.  The effect of this can be seen in comments
> > in the OMAP serial driver, which incorrectly talks about cache flushing
> > for the coherent DMA stuff.
> > 
> > Rather than adding barriers to the accessors, add it in the DMA support
> > code just before we enable DMA, and just after we disable DMA.  This
> > avoids having barriers for every DMA register access.
> > 
> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> 
> cool, should this go to stable too ?

Has anyone seen a problem (other than me when trying to get DMA engine
working with omap-serial transmit paths) ?

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

* [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
@ 2012-04-18 10:17       ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 10:17 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 01:15:20PM +0300, Felipe Balbi wrote:
> On Wed, Apr 18, 2012 at 11:10:35AM +0100, Russell King wrote:
> > Using coherent DMA memory with the OMAP DMA engine results in
> > unpredictable behaviour due to memory ordering issues; as things stand,
> > there is no guarantee that data written to coherent DMA memory will be
> > visible to the DMA hardware.
> > 
> > This is because the OMAP dma_write() accessor contains no barriers,
> > necessary on ARMv6 and above.  The effect of this can be seen in comments
> > in the OMAP serial driver, which incorrectly talks about cache flushing
> > for the coherent DMA stuff.
> > 
> > Rather than adding barriers to the accessors, add it in the DMA support
> > code just before we enable DMA, and just after we disable DMA.  This
> > avoids having barriers for every DMA register access.
> > 
> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> 
> cool, should this go to stable too ?

Has anyone seen a problem (other than me when trying to get DMA engine
working with omap-serial transmit paths) ?

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

* Re: [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
  2012-04-18 10:17       ` Russell King - ARM Linux
@ 2012-04-18 10:18         ` Felipe Balbi
  -1 siblings, 0 replies; 120+ messages in thread
From: Felipe Balbi @ 2012-04-18 10:18 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Felipe Balbi, linux-arm-kernel, linux-omap, linux-mmc, Tony Lindgren

[-- Attachment #1: Type: text/plain, Size: 1298 bytes --]

On Wed, Apr 18, 2012 at 11:17:33AM +0100, Russell King - ARM Linux wrote:
> On Wed, Apr 18, 2012 at 01:15:20PM +0300, Felipe Balbi wrote:
> > On Wed, Apr 18, 2012 at 11:10:35AM +0100, Russell King wrote:
> > > Using coherent DMA memory with the OMAP DMA engine results in
> > > unpredictable behaviour due to memory ordering issues; as things stand,
> > > there is no guarantee that data written to coherent DMA memory will be
> > > visible to the DMA hardware.
> > > 
> > > This is because the OMAP dma_write() accessor contains no barriers,
> > > necessary on ARMv6 and above.  The effect of this can be seen in comments
> > > in the OMAP serial driver, which incorrectly talks about cache flushing
> > > for the coherent DMA stuff.
> > > 
> > > Rather than adding barriers to the accessors, add it in the DMA support
> > > code just before we enable DMA, and just after we disable DMA.  This
> > > avoids having barriers for every DMA register access.
> > > 
> > > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > 
> > cool, should this go to stable too ?
> 
> Has anyone seen a problem (other than me when trying to get DMA engine
> working with omap-serial transmit paths) ?

probably not since omap-serial isn't using DMA anyway. Nevermind

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

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

* [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
@ 2012-04-18 10:18         ` Felipe Balbi
  0 siblings, 0 replies; 120+ messages in thread
From: Felipe Balbi @ 2012-04-18 10:18 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 11:17:33AM +0100, Russell King - ARM Linux wrote:
> On Wed, Apr 18, 2012 at 01:15:20PM +0300, Felipe Balbi wrote:
> > On Wed, Apr 18, 2012 at 11:10:35AM +0100, Russell King wrote:
> > > Using coherent DMA memory with the OMAP DMA engine results in
> > > unpredictable behaviour due to memory ordering issues; as things stand,
> > > there is no guarantee that data written to coherent DMA memory will be
> > > visible to the DMA hardware.
> > > 
> > > This is because the OMAP dma_write() accessor contains no barriers,
> > > necessary on ARMv6 and above.  The effect of this can be seen in comments
> > > in the OMAP serial driver, which incorrectly talks about cache flushing
> > > for the coherent DMA stuff.
> > > 
> > > Rather than adding barriers to the accessors, add it in the DMA support
> > > code just before we enable DMA, and just after we disable DMA.  This
> > > avoids having barriers for every DMA register access.
> > > 
> > > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > 
> > cool, should this go to stable too ?
> 
> Has anyone seen a problem (other than me when trying to get DMA engine
> working with omap-serial transmit paths) ?

probably not since omap-serial isn't using DMA anyway. Nevermind

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120418/1da5c157/attachment.sig>

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

* Re: [PATCH 2/8] dmaengine: amba-pl08x: ensure physical channels are properly held
  2012-04-18 10:10   ` Russell King
@ 2012-04-18 10:19     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 10:19 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap, linux-mmc; +Cc: Vinod Koul, Dan Williams

Oops, this patch wasn't supposed to be part of this set...

On Wed, Apr 18, 2012 at 11:10:55AM +0100, Russell King wrote:
> Ensure that physical channels are held while there are descriptors for
> them to process.  This is needed when we split the pending queue into
> separate pending and issued queues.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/dma/amba-pl08x.c |   10 ++++++----
>  1 files changed, 6 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
> index c301a8e..2e74cca 100644
> --- a/drivers/dma/amba-pl08x.c
> +++ b/drivers/dma/amba-pl08x.c
> @@ -855,6 +855,8 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
>  	struct pl08x_phy_chan *ch;
>  	int ret;
>  
> +	plchan->phychan_hold++;
> +
>  	/* Check if we already have a channel */
>  	if (plchan->phychan) {
>  		ch = plchan->phychan;
> @@ -863,6 +865,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
>  
>  	ch = pl08x_get_phy_channel(pl08x, plchan);
>  	if (!ch) {
> +		plchan->phychan_hold--;
>  		/* No physical channel available, cope with it */
>  		dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name);
>  		return -EBUSY;
> @@ -876,6 +879,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
>  	if (plchan->slave && pl08x->pd->get_signal) {
>  		ret = pl08x->pd->get_signal(plchan);
>  		if (ret < 0) {
> +			plchan->phychan_hold--;
>  			dev_dbg(&pl08x->adev->dev,
>  				"unable to use physical channel %d for transfer on %s due to platform restrictions\n",
>  				ch->id, plchan->name);
> @@ -899,8 +903,6 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
>  	else if (txd->direction == DMA_DEV_TO_MEM)
>  		txd->ccfg |= ch->signal << PL080_CONFIG_SRC_SEL_SHIFT;
>  
> -	plchan->phychan_hold++;
> -
>  	return 0;
>  }
>  
> @@ -938,8 +940,6 @@ static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx)
>  		/* Do this memcpy whenever there is a channel ready */
>  		plchan->state = PL08X_CHAN_WAITING;
>  		plchan->waiting = txd;
> -	} else {
> -		plchan->phychan_hold--;
>  	}
>  
>  	spin_unlock_irqrestore(&plchan->lock, flags);
> @@ -1428,6 +1428,7 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
>  			 * Mark physical channel as free and free any slave
>  			 * signal
>  			 */
> +			plchan->phychan_hold = 0;
>  			release_phy_channel(plchan);
>  		}
>  		/* Dequeue jobs and free LLIs */
> @@ -1529,6 +1530,7 @@ static void pl08x_tasklet(unsigned long data)
>  	if (txd) {
>  		/* Update last completed */
>  		dma_cookie_complete(&txd->tx);
> +		plchan->phychan_hold--;
>  	}
>  
>  	/* If a new descriptor is queued, set it up plchan->at is NULL here */
> -- 
> 1.7.4.4
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 2/8] dmaengine: amba-pl08x: ensure physical channels are properly held
@ 2012-04-18 10:19     ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 10:19 UTC (permalink / raw)
  To: linux-arm-kernel

Oops, this patch wasn't supposed to be part of this set...

On Wed, Apr 18, 2012 at 11:10:55AM +0100, Russell King wrote:
> Ensure that physical channels are held while there are descriptors for
> them to process.  This is needed when we split the pending queue into
> separate pending and issued queues.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  drivers/dma/amba-pl08x.c |   10 ++++++----
>  1 files changed, 6 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
> index c301a8e..2e74cca 100644
> --- a/drivers/dma/amba-pl08x.c
> +++ b/drivers/dma/amba-pl08x.c
> @@ -855,6 +855,8 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
>  	struct pl08x_phy_chan *ch;
>  	int ret;
>  
> +	plchan->phychan_hold++;
> +
>  	/* Check if we already have a channel */
>  	if (plchan->phychan) {
>  		ch = plchan->phychan;
> @@ -863,6 +865,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
>  
>  	ch = pl08x_get_phy_channel(pl08x, plchan);
>  	if (!ch) {
> +		plchan->phychan_hold--;
>  		/* No physical channel available, cope with it */
>  		dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name);
>  		return -EBUSY;
> @@ -876,6 +879,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
>  	if (plchan->slave && pl08x->pd->get_signal) {
>  		ret = pl08x->pd->get_signal(plchan);
>  		if (ret < 0) {
> +			plchan->phychan_hold--;
>  			dev_dbg(&pl08x->adev->dev,
>  				"unable to use physical channel %d for transfer on %s due to platform restrictions\n",
>  				ch->id, plchan->name);
> @@ -899,8 +903,6 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan,
>  	else if (txd->direction == DMA_DEV_TO_MEM)
>  		txd->ccfg |= ch->signal << PL080_CONFIG_SRC_SEL_SHIFT;
>  
> -	plchan->phychan_hold++;
> -
>  	return 0;
>  }
>  
> @@ -938,8 +940,6 @@ static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx)
>  		/* Do this memcpy whenever there is a channel ready */
>  		plchan->state = PL08X_CHAN_WAITING;
>  		plchan->waiting = txd;
> -	} else {
> -		plchan->phychan_hold--;
>  	}
>  
>  	spin_unlock_irqrestore(&plchan->lock, flags);
> @@ -1428,6 +1428,7 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
>  			 * Mark physical channel as free and free any slave
>  			 * signal
>  			 */
> +			plchan->phychan_hold = 0;
>  			release_phy_channel(plchan);
>  		}
>  		/* Dequeue jobs and free LLIs */
> @@ -1529,6 +1530,7 @@ static void pl08x_tasklet(unsigned long data)
>  	if (txd) {
>  		/* Update last completed */
>  		dma_cookie_complete(&txd->tx);
> +		plchan->phychan_hold--;
>  	}
>  
>  	/* If a new descriptor is queued, set it up plchan->at is NULL here */
> -- 
> 1.7.4.4
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
  2012-04-18 10:18         ` Felipe Balbi
@ 2012-04-18 10:26           ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 10:26 UTC (permalink / raw)
  To: Felipe Balbi; +Cc: linux-arm-kernel, linux-omap, linux-mmc, Tony Lindgren

On Wed, Apr 18, 2012 at 01:18:55PM +0300, Felipe Balbi wrote:
> On Wed, Apr 18, 2012 at 11:17:33AM +0100, Russell King - ARM Linux wrote:
> > Has anyone seen a problem (other than me when trying to get DMA engine
> > working with omap-serial transmit paths) ?
> 
> probably not since omap-serial isn't using DMA anyway. Nevermind

... which is a good thing because the driver is rather broken for DMA
support (take a look at what the set_termios() method does, and consider
what happens if that function is called when there's DMA activity
outstanding.)

I've come to the conclusion that it's not worth the effort to try to
convert omap-serial to DMAengine support.  Firstly, DMA support requires
hardware assisted flow control to work - which it currently doesn't for
many many reasons.  Not only because of wrong defines, but also because
the programming examples in the OMAP4430 TRM is wrong.  You'd do much
better going to the TI16C752B data sheet and seeing how the original IP
was described.  Why TI didn't include a copy of that in the TRM...

Secondly, the problem I mention above wrt set_termios(), with the flow
control fixed, results in a nice race condition causing the TX DMA to
write into registers other than the FIFO data register, corrupting the
UART configuration and halting system boot.

(I have a series of patches to fix the flow control issues, and this
requires some work outside of the omap-serial driver to achieve that.
I'll post those patches when they're fully ready.  At the moment, it's
probably better to rip out all the hardware assisted flow control code
from omap-serial so that we fall back on the standard software driven
flow control.)

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

* [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
@ 2012-04-18 10:26           ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 10:26 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 01:18:55PM +0300, Felipe Balbi wrote:
> On Wed, Apr 18, 2012 at 11:17:33AM +0100, Russell King - ARM Linux wrote:
> > Has anyone seen a problem (other than me when trying to get DMA engine
> > working with omap-serial transmit paths) ?
> 
> probably not since omap-serial isn't using DMA anyway. Nevermind

... which is a good thing because the driver is rather broken for DMA
support (take a look at what the set_termios() method does, and consider
what happens if that function is called when there's DMA activity
outstanding.)

I've come to the conclusion that it's not worth the effort to try to
convert omap-serial to DMAengine support.  Firstly, DMA support requires
hardware assisted flow control to work - which it currently doesn't for
many many reasons.  Not only because of wrong defines, but also because
the programming examples in the OMAP4430 TRM is wrong.  You'd do much
better going to the TI16C752B data sheet and seeing how the original IP
was described.  Why TI didn't include a copy of that in the TRM...

Secondly, the problem I mention above wrt set_termios(), with the flow
control fixed, results in a nice race condition causing the TX DMA to
write into registers other than the FIFO data register, corrupting the
UART configuration and halting system boot.

(I have a series of patches to fix the flow control issues, and this
requires some work outside of the omap-serial driver to achieve that.
I'll post those patches when they're fully ready.  At the moment, it's
probably better to rip out all the hardware assisted flow control code
from omap-serial so that we fall back on the standard software driven
flow control.)

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 10:12   ` Russell King
@ 2012-04-18 15:23     ` T Krishnamoorthy, Balaji
  -1 siblings, 0 replies; 120+ messages in thread
From: T Krishnamoorthy, Balaji @ 2012-04-18 15:23 UTC (permalink / raw)
  To: Russell King; +Cc: linux-arm-kernel, linux-omap, linux-mmc, Tony Lindgren

On Wed, Apr 18, 2012 at 3:42 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> DMAengine uses the DMA engine device structure when mapping/unmapping
> memory for DMA, so the MMC devices do not need their DMA masks
> initialized (this reflects hardware: the MMC device is not the device
> doing DMA.)
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
>  arch/arm/mach-omap1/board-h2-mmc.c    |    1 -
>  arch/arm/mach-omap1/board-h3-mmc.c    |    1 -
>  arch/arm/mach-omap1/board-nokia770.c  |    1 -
>  arch/arm/mach-omap2/board-n8x0.c      |    1 -
>  arch/arm/mach-omap2/hsmmc.c           |    1 -
>  arch/arm/plat-omap/include/plat/mmc.h |    2 --
Hi,

drivers/mmc/host/omap.c is also using dma_mask should that also be removed

>  6 files changed, 0 insertions(+), 7 deletions(-)
>
> diff --git a/arch/arm/mach-omap1/board-h2-mmc.c b/arch/arm/mach-omap1/board-h2-mmc.c
> index da0e37d..e1362ce 100644
> --- a/arch/arm/mach-omap1/board-h2-mmc.c
> +++ b/arch/arm/mach-omap1/board-h2-mmc.c
> @@ -54,7 +54,6 @@ static struct omap_mmc_platform_data mmc1_data = {
>        .nr_slots                       = 1,
>        .init                           = mmc_late_init,
>        .cleanup                        = mmc_cleanup,
> -       .dma_mask                       = 0xffffffff,
>        .slots[0]       = {
>                .set_power              = mmc_set_power,
>                .ocr_mask               = MMC_VDD_32_33 | MMC_VDD_33_34,
> diff --git a/arch/arm/mach-omap1/board-h3-mmc.c b/arch/arm/mach-omap1/board-h3-mmc.c
> index f8242aa..c74daac 100644
> --- a/arch/arm/mach-omap1/board-h3-mmc.c
> +++ b/arch/arm/mach-omap1/board-h3-mmc.c
> @@ -36,7 +36,6 @@ static int mmc_set_power(struct device *dev, int slot, int power_on,
>  */
>  static struct omap_mmc_platform_data mmc1_data = {
>        .nr_slots                       = 1,
> -       .dma_mask                       = 0xffffffff,
>        .slots[0]       = {
>                .set_power              = mmc_set_power,
>                .ocr_mask               = MMC_VDD_32_33 | MMC_VDD_33_34,
> diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c
> index d21dcc2..cac9954 100644
> --- a/arch/arm/mach-omap1/board-nokia770.c
> +++ b/arch/arm/mach-omap1/board-nokia770.c
> @@ -185,7 +185,6 @@ static int nokia770_mmc_get_cover_state(struct device *dev, int slot)
>
>  static struct omap_mmc_platform_data nokia770_mmc2_data = {
>        .nr_slots                       = 1,
> -       .dma_mask                       = 0xffffffff,
>        .max_freq                       = 12000000,
>        .slots[0]       = {
>                .set_power              = nokia770_mmc_set_power,
> diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c
> index 518091c..1003748 100644
> --- a/arch/arm/mach-omap2/board-n8x0.c
> +++ b/arch/arm/mach-omap2/board-n8x0.c
> @@ -470,7 +470,6 @@ static struct omap_mmc_platform_data mmc1_data = {
>        .cleanup                        = n8x0_mmc_cleanup,
>        .shutdown                       = n8x0_mmc_shutdown,
>        .max_freq                       = 24000000,
> -       .dma_mask                       = 0xffffffff,
>        .slots[0] = {
>                .wires                  = 4,
>                .set_power              = n8x0_mmc_set_power,
> diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c
> index b0268ea..41e5277 100644
> --- a/arch/arm/mach-omap2/hsmmc.c
> +++ b/arch/arm/mach-omap2/hsmmc.c
> @@ -315,7 +315,6 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
>        mmc->slots[0].caps = c->caps;
>        mmc->slots[0].pm_caps = c->pm_caps;
>        mmc->slots[0].internal_clock = !c->ext_clock;
> -       mmc->dma_mask = 0xffffffff;
>        mmc->max_freq = c->max_freq;
>        if (cpu_is_omap44xx())
>                mmc->reg_offset = OMAP4_MMC_REG_OFFSET;
> diff --git a/arch/arm/plat-omap/include/plat/mmc.h b/arch/arm/plat-omap/include/plat/mmc.h
> index 7a38750..9fe87a7 100644
> --- a/arch/arm/plat-omap/include/plat/mmc.h
> +++ b/arch/arm/plat-omap/include/plat/mmc.h
> @@ -80,8 +80,6 @@ struct omap_mmc_platform_data {
>        /* Return context loss count due to PM states changing */
>        int (*get_context_loss_count)(struct device *dev);
>
> -       u64 dma_mask;
> -
>        /* Integrating attributes from the omap_hwmod layer */
>        u8 controller_flags;
>
> --
> 1.7.4.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-18 15:23     ` T Krishnamoorthy, Balaji
  0 siblings, 0 replies; 120+ messages in thread
From: T Krishnamoorthy, Balaji @ 2012-04-18 15:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 3:42 PM, Russell King
<rmk+kernel@arm.linux.org.uk> wrote:
> DMAengine uses the DMA engine device structure when mapping/unmapping
> memory for DMA, so the MMC devices do not need their DMA masks
> initialized (this reflects hardware: the MMC device is not the device
> doing DMA.)
>
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> ---
> ?arch/arm/mach-omap1/board-h2-mmc.c ? ?| ? ?1 -
> ?arch/arm/mach-omap1/board-h3-mmc.c ? ?| ? ?1 -
> ?arch/arm/mach-omap1/board-nokia770.c ?| ? ?1 -
> ?arch/arm/mach-omap2/board-n8x0.c ? ? ?| ? ?1 -
> ?arch/arm/mach-omap2/hsmmc.c ? ? ? ? ? | ? ?1 -
> ?arch/arm/plat-omap/include/plat/mmc.h | ? ?2 --
Hi,

drivers/mmc/host/omap.c is also using dma_mask should that also be removed

> ?6 files changed, 0 insertions(+), 7 deletions(-)
>
> diff --git a/arch/arm/mach-omap1/board-h2-mmc.c b/arch/arm/mach-omap1/board-h2-mmc.c
> index da0e37d..e1362ce 100644
> --- a/arch/arm/mach-omap1/board-h2-mmc.c
> +++ b/arch/arm/mach-omap1/board-h2-mmc.c
> @@ -54,7 +54,6 @@ static struct omap_mmc_platform_data mmc1_data = {
> ? ? ? ?.nr_slots ? ? ? ? ? ? ? ? ? ? ? = 1,
> ? ? ? ?.init ? ? ? ? ? ? ? ? ? ? ? ? ? = mmc_late_init,
> ? ? ? ?.cleanup ? ? ? ? ? ? ? ? ? ? ? ?= mmc_cleanup,
> - ? ? ? .dma_mask ? ? ? ? ? ? ? ? ? ? ? = 0xffffffff,
> ? ? ? ?.slots[0] ? ? ? = {
> ? ? ? ? ? ? ? ?.set_power ? ? ? ? ? ? ?= mmc_set_power,
> ? ? ? ? ? ? ? ?.ocr_mask ? ? ? ? ? ? ? = MMC_VDD_32_33 | MMC_VDD_33_34,
> diff --git a/arch/arm/mach-omap1/board-h3-mmc.c b/arch/arm/mach-omap1/board-h3-mmc.c
> index f8242aa..c74daac 100644
> --- a/arch/arm/mach-omap1/board-h3-mmc.c
> +++ b/arch/arm/mach-omap1/board-h3-mmc.c
> @@ -36,7 +36,6 @@ static int mmc_set_power(struct device *dev, int slot, int power_on,
> ?*/
> ?static struct omap_mmc_platform_data mmc1_data = {
> ? ? ? ?.nr_slots ? ? ? ? ? ? ? ? ? ? ? = 1,
> - ? ? ? .dma_mask ? ? ? ? ? ? ? ? ? ? ? = 0xffffffff,
> ? ? ? ?.slots[0] ? ? ? = {
> ? ? ? ? ? ? ? ?.set_power ? ? ? ? ? ? ?= mmc_set_power,
> ? ? ? ? ? ? ? ?.ocr_mask ? ? ? ? ? ? ? = MMC_VDD_32_33 | MMC_VDD_33_34,
> diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c
> index d21dcc2..cac9954 100644
> --- a/arch/arm/mach-omap1/board-nokia770.c
> +++ b/arch/arm/mach-omap1/board-nokia770.c
> @@ -185,7 +185,6 @@ static int nokia770_mmc_get_cover_state(struct device *dev, int slot)
>
> ?static struct omap_mmc_platform_data nokia770_mmc2_data = {
> ? ? ? ?.nr_slots ? ? ? ? ? ? ? ? ? ? ? = 1,
> - ? ? ? .dma_mask ? ? ? ? ? ? ? ? ? ? ? = 0xffffffff,
> ? ? ? ?.max_freq ? ? ? ? ? ? ? ? ? ? ? = 12000000,
> ? ? ? ?.slots[0] ? ? ? = {
> ? ? ? ? ? ? ? ?.set_power ? ? ? ? ? ? ?= nokia770_mmc_set_power,
> diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c
> index 518091c..1003748 100644
> --- a/arch/arm/mach-omap2/board-n8x0.c
> +++ b/arch/arm/mach-omap2/board-n8x0.c
> @@ -470,7 +470,6 @@ static struct omap_mmc_platform_data mmc1_data = {
> ? ? ? ?.cleanup ? ? ? ? ? ? ? ? ? ? ? ?= n8x0_mmc_cleanup,
> ? ? ? ?.shutdown ? ? ? ? ? ? ? ? ? ? ? = n8x0_mmc_shutdown,
> ? ? ? ?.max_freq ? ? ? ? ? ? ? ? ? ? ? = 24000000,
> - ? ? ? .dma_mask ? ? ? ? ? ? ? ? ? ? ? = 0xffffffff,
> ? ? ? ?.slots[0] = {
> ? ? ? ? ? ? ? ?.wires ? ? ? ? ? ? ? ? ?= 4,
> ? ? ? ? ? ? ? ?.set_power ? ? ? ? ? ? ?= n8x0_mmc_set_power,
> diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c
> index b0268ea..41e5277 100644
> --- a/arch/arm/mach-omap2/hsmmc.c
> +++ b/arch/arm/mach-omap2/hsmmc.c
> @@ -315,7 +315,6 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
> ? ? ? ?mmc->slots[0].caps = c->caps;
> ? ? ? ?mmc->slots[0].pm_caps = c->pm_caps;
> ? ? ? ?mmc->slots[0].internal_clock = !c->ext_clock;
> - ? ? ? mmc->dma_mask = 0xffffffff;
> ? ? ? ?mmc->max_freq = c->max_freq;
> ? ? ? ?if (cpu_is_omap44xx())
> ? ? ? ? ? ? ? ?mmc->reg_offset = OMAP4_MMC_REG_OFFSET;
> diff --git a/arch/arm/plat-omap/include/plat/mmc.h b/arch/arm/plat-omap/include/plat/mmc.h
> index 7a38750..9fe87a7 100644
> --- a/arch/arm/plat-omap/include/plat/mmc.h
> +++ b/arch/arm/plat-omap/include/plat/mmc.h
> @@ -80,8 +80,6 @@ struct omap_mmc_platform_data {
> ? ? ? ?/* Return context loss count due to PM states changing */
> ? ? ? ?int (*get_context_loss_count)(struct device *dev);
>
> - ? ? ? u64 dma_mask;
> -
> ? ? ? ?/* Integrating attributes from the omap_hwmod layer */
> ? ? ? ?u8 controller_flags;
>
> --
> 1.7.4.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 15:23     ` T Krishnamoorthy, Balaji
@ 2012-04-18 15:29       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 15:29 UTC (permalink / raw)
  To: T Krishnamoorthy, Balaji
  Cc: linux-arm-kernel, linux-omap, linux-mmc, Tony Lindgren

On Wed, Apr 18, 2012 at 08:53:32PM +0530, T Krishnamoorthy, Balaji wrote:
> Hi,
> 
> drivers/mmc/host/omap.c is also using dma_mask should that also be removed

Does this driver make use of this platform data?

If so, it needs converting to DMA engine _before_ this patch (which is
one reason why its a good idea to do these changes as separate patches...
as I've done.)  It means stuff like this can be slotted in as necessary
in the patch order.

I'm told that driver is OMAP2 only - I don't have any OMAP2 platforms to
be able to test it on (I only have OMAP3 and OMAP4.)

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-18 15:29       ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 15:29 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 08:53:32PM +0530, T Krishnamoorthy, Balaji wrote:
> Hi,
> 
> drivers/mmc/host/omap.c is also using dma_mask should that also be removed

Does this driver make use of this platform data?

If so, it needs converting to DMA engine _before_ this patch (which is
one reason why its a good idea to do these changes as separate patches...
as I've done.)  It means stuff like this can be slotted in as necessary
in the patch order.

I'm told that driver is OMAP2 only - I don't have any OMAP2 platforms to
be able to test it on (I only have OMAP3 and OMAP4.)

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 15:29       ` Russell King - ARM Linux
@ 2012-04-18 15:35         ` T Krishnamoorthy, Balaji
  -1 siblings, 0 replies; 120+ messages in thread
From: T Krishnamoorthy, Balaji @ 2012-04-18 15:35 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-arm-kernel, linux-omap, linux-mmc, Tony Lindgren

On Wed, Apr 18, 2012 at 8:59 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Wed, Apr 18, 2012 at 08:53:32PM +0530, T Krishnamoorthy, Balaji wrote:
>> Hi,
>>
>> drivers/mmc/host/omap.c is also using dma_mask should that also be removed
>
> Does this driver make use of this platform data?
>
> If so, it needs converting to DMA engine _before_ this patch (which is
> one reason why its a good idea to do these changes as separate patches...
> as I've done.)  It means stuff like this can be slotted in as necessary
> in the patch order.
>
> I'm told that driver is OMAP2 only - I don't have any OMAP2 platforms to
> be able to test it on (I only have OMAP3 and OMAP4.)

Yes, this driver is for omap2420 and omap1 devices.
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-18 15:35         ` T Krishnamoorthy, Balaji
  0 siblings, 0 replies; 120+ messages in thread
From: T Krishnamoorthy, Balaji @ 2012-04-18 15:35 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 8:59 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Wed, Apr 18, 2012 at 08:53:32PM +0530, T Krishnamoorthy, Balaji wrote:
>> Hi,
>>
>> drivers/mmc/host/omap.c is also using dma_mask should that also be removed
>
> Does this driver make use of this platform data?
>
> If so, it needs converting to DMA engine _before_ this patch (which is
> one reason why its a good idea to do these changes as separate patches...
> as I've done.) ?It means stuff like this can be slotted in as necessary
> in the patch order.
>
> I'm told that driver is OMAP2 only - I don't have any OMAP2 platforms to
> be able to test it on (I only have OMAP3 and OMAP4.)

Yes, this driver is for omap2420 and omap1 devices.

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

* Re: [PATCH 6/8] mmc: omap_hsmmc: add DMA engine support
  2012-04-18 10:12   ` Russell King
@ 2012-04-18 18:11     ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-18 18:11 UTC (permalink / raw)
  To: Russell King; +Cc: linux-arm-kernel, linux-omap, linux-mmc, Chris Ball

* Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:15]:
> Add DMA engine support to the OMAP HSMMC driver.  This supplements the
> private DMA API implementation contained within this driver, and the
> driver can be switched at build time between using DMA engine and the
> private DMA API.

Glad to see this happening!

No luck here though, after a quick test I'm getting the following with MMC
not working:

zoom3
[    2.277160] omap_hsmmc omap_hsmmc.1: unable to obtain RX DMA engine channel 48
[    2.284729] omap_hsmmc omap_hsmmc.2: unable to obtain RX DMA engine channel 78
[    2.292999] omap_hsmmc omap_hsmmc.0: unable to obtain RX DMA engine channel 62
blaze
[    1.681610] omap_hsmmc omap_hsmmc.1: unable to obtain RX DMA engine channel 48
[    1.689331] omap_hsmmc omap_hsmmc.0: unable to obtain RX DMA engine channel 62
[    1.697174] omap_hsmmc omap_hsmmc.4: unable to obtain RX DMA engine channel 60
n900
[    2.492248] omap_hsmmc omap_hsmmc.0: unable to obtain RX DMA engine channel 62
[    2.500488] omap_hsmmc omap_hsmmc.1: unable to obtain RX DMA engine channel 48

And leaving out patch 7/8 produces similar errors for both rx and tx, but MMC
works without DMA:

n900
[    2.478363] omap_hsmmc omap_hsmmc.0: unable to obtain RX DMA engine channel 62
[    2.486267] omap_hsmmc omap_hsmmc.0: unable to obtain TX DMA engine channel 61
[    2.718322] omap_hsmmc omap_hsmmc.1: unable to obtain RX DMA engine channel 48
[    2.726135] omap_hsmmc omap_hsmmc.1: unable to obtain TX DMA engine channel 47


Regards,

Tony

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

* [PATCH 6/8] mmc: omap_hsmmc: add DMA engine support
@ 2012-04-18 18:11     ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-18 18:11 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:15]:
> Add DMA engine support to the OMAP HSMMC driver.  This supplements the
> private DMA API implementation contained within this driver, and the
> driver can be switched at build time between using DMA engine and the
> private DMA API.

Glad to see this happening!

No luck here though, after a quick test I'm getting the following with MMC
not working:

zoom3
[    2.277160] omap_hsmmc omap_hsmmc.1: unable to obtain RX DMA engine channel 48
[    2.284729] omap_hsmmc omap_hsmmc.2: unable to obtain RX DMA engine channel 78
[    2.292999] omap_hsmmc omap_hsmmc.0: unable to obtain RX DMA engine channel 62
blaze
[    1.681610] omap_hsmmc omap_hsmmc.1: unable to obtain RX DMA engine channel 48
[    1.689331] omap_hsmmc omap_hsmmc.0: unable to obtain RX DMA engine channel 62
[    1.697174] omap_hsmmc omap_hsmmc.4: unable to obtain RX DMA engine channel 60
n900
[    2.492248] omap_hsmmc omap_hsmmc.0: unable to obtain RX DMA engine channel 62
[    2.500488] omap_hsmmc omap_hsmmc.1: unable to obtain RX DMA engine channel 48

And leaving out patch 7/8 produces similar errors for both rx and tx, but MMC
works without DMA:

n900
[    2.478363] omap_hsmmc omap_hsmmc.0: unable to obtain RX DMA engine channel 62
[    2.486267] omap_hsmmc omap_hsmmc.0: unable to obtain TX DMA engine channel 61
[    2.718322] omap_hsmmc omap_hsmmc.1: unable to obtain RX DMA engine channel 48
[    2.726135] omap_hsmmc omap_hsmmc.1: unable to obtain TX DMA engine channel 47


Regards,

Tony

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 15:35         ` T Krishnamoorthy, Balaji
@ 2012-04-18 18:19           ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-18 18:19 UTC (permalink / raw)
  To: T Krishnamoorthy, Balaji
  Cc: Russell King - ARM Linux, linux-arm-kernel, linux-omap, linux-mmc

* T Krishnamoorthy, Balaji <balajitk@ti.com> [120418 08:39]:
> On Wed, Apr 18, 2012 at 8:59 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Wed, Apr 18, 2012 at 08:53:32PM +0530, T Krishnamoorthy, Balaji wrote:
> >> Hi,
> >>
> >> drivers/mmc/host/omap.c is also using dma_mask should that also be removed
> >
> > Does this driver make use of this platform data?
> >
> > If so, it needs converting to DMA engine _before_ this patch (which is
> > one reason why its a good idea to do these changes as separate patches...
> > as I've done.)  It means stuff like this can be slotted in as necessary
> > in the patch order.

Can't we can just do this in drivers/mmc/host/omap.c:

- host->dev->dma_mask = &pdata->dma_mask;
+ host->dev->dma_mask = 0xffffffff;

And that way drop it from the platform data. Looks like some omap1
omap_mmc_platform_data don't have it set though, but we do have host->use_dma = 1
set anyways in drivers/mmc/host/omap.c.

Regards,

Tony
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-18 18:19           ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-18 18:19 UTC (permalink / raw)
  To: linux-arm-kernel

* T Krishnamoorthy, Balaji <balajitk@ti.com> [120418 08:39]:
> On Wed, Apr 18, 2012 at 8:59 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Wed, Apr 18, 2012 at 08:53:32PM +0530, T Krishnamoorthy, Balaji wrote:
> >> Hi,
> >>
> >> drivers/mmc/host/omap.c is also using dma_mask should that also be removed
> >
> > Does this driver make use of this platform data?
> >
> > If so, it needs converting to DMA engine _before_ this patch (which is
> > one reason why its a good idea to do these changes as separate patches...
> > as I've done.) ?It means stuff like this can be slotted in as necessary
> > in the patch order.

Can't we can just do this in drivers/mmc/host/omap.c:

- host->dev->dma_mask = &pdata->dma_mask;
+ host->dev->dma_mask = 0xffffffff;

And that way drop it from the platform data. Looks like some omap1
omap_mmc_platform_data don't have it set though, but we do have host->use_dma = 1
set anyways in drivers/mmc/host/omap.c.

Regards,

Tony

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

* Re: [PATCH 6/8] mmc: omap_hsmmc: add DMA engine support
  2012-04-18 18:11     ` Tony Lindgren
@ 2012-04-18 19:09       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 19:09 UTC (permalink / raw)
  To: Tony Lindgren; +Cc: linux-arm-kernel, linux-omap, linux-mmc, Chris Ball

On Wed, Apr 18, 2012 at 11:11:12AM -0700, Tony Lindgren wrote:
> * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:15]:
> > Add DMA engine support to the OMAP HSMMC driver.  This supplements the
> > private DMA API implementation contained within this driver, and the
> > driver can be switched at build time between using DMA engine and the
> > private DMA API.
> 
> Glad to see this happening!
> 
> No luck here though, after a quick test I'm getting the following with MMC
> not working:

Did you enable OMAP DMA engine support?

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

* [PATCH 6/8] mmc: omap_hsmmc: add DMA engine support
@ 2012-04-18 19:09       ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 19:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 11:11:12AM -0700, Tony Lindgren wrote:
> * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:15]:
> > Add DMA engine support to the OMAP HSMMC driver.  This supplements the
> > private DMA API implementation contained within this driver, and the
> > driver can be switched at build time between using DMA engine and the
> > private DMA API.
> 
> Glad to see this happening!
> 
> No luck here though, after a quick test I'm getting the following with MMC
> not working:

Did you enable OMAP DMA engine support?

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 18:19           ` Tony Lindgren
@ 2012-04-18 19:10             ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 19:10 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: T Krishnamoorthy, Balaji, linux-arm-kernel, linux-omap, linux-mmc

On Wed, Apr 18, 2012 at 11:19:18AM -0700, Tony Lindgren wrote:
> * T Krishnamoorthy, Balaji <balajitk@ti.com> [120418 08:39]:
> > On Wed, Apr 18, 2012 at 8:59 PM, Russell King - ARM Linux
> > <linux@arm.linux.org.uk> wrote:
> > > On Wed, Apr 18, 2012 at 08:53:32PM +0530, T Krishnamoorthy, Balaji wrote:
> > >> Hi,
> > >>
> > >> drivers/mmc/host/omap.c is also using dma_mask should that also be removed
> > >
> > > Does this driver make use of this platform data?
> > >
> > > If so, it needs converting to DMA engine _before_ this patch (which is
> > > one reason why its a good idea to do these changes as separate patches...
> > > as I've done.)  It means stuff like this can be slotted in as necessary
> > > in the patch order.
> 
> Can't we can just do this in drivers/mmc/host/omap.c:
> 
> - host->dev->dma_mask = &pdata->dma_mask;
> + host->dev->dma_mask = 0xffffffff;
> 
> And that way drop it from the platform data. Looks like some omap1
> omap_mmc_platform_data don't have it set though, but we do have host->use_dma = 1
> set anyways in drivers/mmc/host/omap.c.

No.  dma_mask is a pointer to u64.  You need it stored in some variable.

But, really, we shouldn't be initializing dma masks in drivers at all.
That's supposed to come from the point where devices are created - and
in the case of DMA engine stuff, its not the peripheral device structures
which are doing DMA (they're DMA-incapable) - they rely on the services
of a separate IP to perform DMA on their behalf.
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-18 19:10             ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 19:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 11:19:18AM -0700, Tony Lindgren wrote:
> * T Krishnamoorthy, Balaji <balajitk@ti.com> [120418 08:39]:
> > On Wed, Apr 18, 2012 at 8:59 PM, Russell King - ARM Linux
> > <linux@arm.linux.org.uk> wrote:
> > > On Wed, Apr 18, 2012 at 08:53:32PM +0530, T Krishnamoorthy, Balaji wrote:
> > >> Hi,
> > >>
> > >> drivers/mmc/host/omap.c is also using dma_mask should that also be removed
> > >
> > > Does this driver make use of this platform data?
> > >
> > > If so, it needs converting to DMA engine _before_ this patch (which is
> > > one reason why its a good idea to do these changes as separate patches...
> > > as I've done.) ?It means stuff like this can be slotted in as necessary
> > > in the patch order.
> 
> Can't we can just do this in drivers/mmc/host/omap.c:
> 
> - host->dev->dma_mask = &pdata->dma_mask;
> + host->dev->dma_mask = 0xffffffff;
> 
> And that way drop it from the platform data. Looks like some omap1
> omap_mmc_platform_data don't have it set though, but we do have host->use_dma = 1
> set anyways in drivers/mmc/host/omap.c.

No.  dma_mask is a pointer to u64.  You need it stored in some variable.

But, really, we shouldn't be initializing dma masks in drivers at all.
That's supposed to come from the point where devices are created - and
in the case of DMA engine stuff, its not the peripheral device structures
which are doing DMA (they're DMA-incapable) - they rely on the services
of a separate IP to perform DMA on their behalf.

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 15:35         ` T Krishnamoorthy, Balaji
@ 2012-04-18 19:42           ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 19:42 UTC (permalink / raw)
  To: T Krishnamoorthy, Balaji
  Cc: linux-arm-kernel, linux-omap, linux-mmc, Tony Lindgren

On Wed, Apr 18, 2012 at 09:05:56PM +0530, T Krishnamoorthy, Balaji wrote:
> On Wed, Apr 18, 2012 at 8:59 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Wed, Apr 18, 2012 at 08:53:32PM +0530, T Krishnamoorthy, Balaji wrote:
> >> Hi,
> >>
> >> drivers/mmc/host/omap.c is also using dma_mask should that also be removed
> >
> > Does this driver make use of this platform data?
> >
> > If so, it needs converting to DMA engine _before_ this patch (which is
> > one reason why its a good idea to do these changes as separate patches...
> > as I've done.)  It means stuff like this can be slotted in as necessary
> > in the patch order.
> >
> > I'm told that driver is OMAP2 only - I don't have any OMAP2 platforms to
> > be able to test it on (I only have OMAP3 and OMAP4.)
> 
> Yes, this driver is for omap2420 and omap1 devices.

Looking at this driver, there seems to be some interaction between how
the DMA is programmed for each part of the scatterlist, and how the
MMC interface is programmed, specifically:

        frame = data->blksz;
        if (cpu_is_omap15xx() && frame > 32)
                frame = 32;
        else if (frame > 64)
                frame = 64;
        frame >>= 1;
        if (!(data->flags & MMC_DATA_WRITE)) {
                buf = 0x800f | ((frame - 1) << 8);
        } else {
                buf = 0x0f80 | ((frame - 1) << 0);
        }
        OMAP_MMC_WRITE(host, BUF, buf);

Does this register have to be written on every scatterlist entry, or can
it be written once at the start of request processing?
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-18 19:42           ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 19:42 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 09:05:56PM +0530, T Krishnamoorthy, Balaji wrote:
> On Wed, Apr 18, 2012 at 8:59 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> > On Wed, Apr 18, 2012 at 08:53:32PM +0530, T Krishnamoorthy, Balaji wrote:
> >> Hi,
> >>
> >> drivers/mmc/host/omap.c is also using dma_mask should that also be removed
> >
> > Does this driver make use of this platform data?
> >
> > If so, it needs converting to DMA engine _before_ this patch (which is
> > one reason why its a good idea to do these changes as separate patches...
> > as I've done.) ?It means stuff like this can be slotted in as necessary
> > in the patch order.
> >
> > I'm told that driver is OMAP2 only - I don't have any OMAP2 platforms to
> > be able to test it on (I only have OMAP3 and OMAP4.)
> 
> Yes, this driver is for omap2420 and omap1 devices.

Looking at this driver, there seems to be some interaction between how
the DMA is programmed for each part of the scatterlist, and how the
MMC interface is programmed, specifically:

        frame = data->blksz;
        if (cpu_is_omap15xx() && frame > 32)
                frame = 32;
        else if (frame > 64)
                frame = 64;
        frame >>= 1;
        if (!(data->flags & MMC_DATA_WRITE)) {
                buf = 0x800f | ((frame - 1) << 8);
        } else {
                buf = 0x0f80 | ((frame - 1) << 0);
        }
        OMAP_MMC_WRITE(host, BUF, buf);

Does this register have to be written on every scatterlist entry, or can
it be written once@the start of request processing?

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

* Re: [PATCH 6/8] mmc: omap_hsmmc: add DMA engine support
  2012-04-18 19:09       ` Russell King - ARM Linux
@ 2012-04-18 19:53         ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-18 19:53 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-arm-kernel, linux-omap, linux-mmc, Chris Ball

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 12:12]:
> On Wed, Apr 18, 2012 at 11:11:12AM -0700, Tony Lindgren wrote:
> > * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:15]:
> > > Add DMA engine support to the OMAP HSMMC driver.  This supplements the
> > > private DMA API implementation contained within this driver, and the
> > > driver can be switched at build time between using DMA engine and the
> > > private DMA API.
> > 
> > Glad to see this happening!
> > 
> > No luck here though, after a quick test I'm getting the following with MMC
> > not working:
> 
> Did you enable OMAP DMA engine support?

Heh that's it :) Yup it works now after adding:

CONFIG_DMADEVICES=y
CONFIG_DMA_OMAP=y

How about just continue on PIO if DMA reques fails:

--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1921,16 +1921,12 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 	dma_cap_set(DMA_SLAVE, mask);
 
 	host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &rx_req);
-	if (!host->rx_chan) {
-		dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
-		goto err_irq;
-	}
+	if (!host->rx_chan)
+		dev_info(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
 
 	host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &tx_req);
-	if (!host->tx_chan) {
-		dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req);
-		goto err_irq;
-	}
+	if (!host->tx_chan)
+		dev_info(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req);
 
 	/* Request IRQ for MMC operations */
 	ret = request_irq(host->irq, omap_hsmmc_irq, 0,

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

* [PATCH 6/8] mmc: omap_hsmmc: add DMA engine support
@ 2012-04-18 19:53         ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-18 19:53 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 12:12]:
> On Wed, Apr 18, 2012 at 11:11:12AM -0700, Tony Lindgren wrote:
> > * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:15]:
> > > Add DMA engine support to the OMAP HSMMC driver.  This supplements the
> > > private DMA API implementation contained within this driver, and the
> > > driver can be switched at build time between using DMA engine and the
> > > private DMA API.
> > 
> > Glad to see this happening!
> > 
> > No luck here though, after a quick test I'm getting the following with MMC
> > not working:
> 
> Did you enable OMAP DMA engine support?

Heh that's it :) Yup it works now after adding:

CONFIG_DMADEVICES=y
CONFIG_DMA_OMAP=y

How about just continue on PIO if DMA reques fails:

--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1921,16 +1921,12 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 	dma_cap_set(DMA_SLAVE, mask);
 
 	host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &rx_req);
-	if (!host->rx_chan) {
-		dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
-		goto err_irq;
-	}
+	if (!host->rx_chan)
+		dev_info(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
 
 	host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &tx_req);
-	if (!host->tx_chan) {
-		dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req);
-		goto err_irq;
-	}
+	if (!host->tx_chan)
+		dev_info(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req);
 
 	/* Request IRQ for MMC operations */
 	ret = request_irq(host->irq, omap_hsmmc_irq, 0,

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 19:10             ` Russell King - ARM Linux
@ 2012-04-18 19:55               ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-18 19:55 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: T Krishnamoorthy, Balaji, linux-arm-kernel, linux-omap, linux-mmc

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 12:14]:
> On Wed, Apr 18, 2012 at 11:19:18AM -0700, Tony Lindgren wrote:
> > * T Krishnamoorthy, Balaji <balajitk@ti.com> [120418 08:39]:
> > > On Wed, Apr 18, 2012 at 8:59 PM, Russell King - ARM Linux
> > > <linux@arm.linux.org.uk> wrote:
> > > > On Wed, Apr 18, 2012 at 08:53:32PM +0530, T Krishnamoorthy, Balaji wrote:
> > > >> Hi,
> > > >>
> > > >> drivers/mmc/host/omap.c is also using dma_mask should that also be removed
> > > >
> > > > Does this driver make use of this platform data?
> > > >
> > > > If so, it needs converting to DMA engine _before_ this patch (which is
> > > > one reason why its a good idea to do these changes as separate patches...
> > > > as I've done.)  It means stuff like this can be slotted in as necessary
> > > > in the patch order.
> > 
> > Can't we can just do this in drivers/mmc/host/omap.c:
> > 
> > - host->dev->dma_mask = &pdata->dma_mask;
> > + host->dev->dma_mask = 0xffffffff;
> > 
> > And that way drop it from the platform data. Looks like some omap1
> > omap_mmc_platform_data don't have it set though, but we do have host->use_dma = 1
> > set anyways in drivers/mmc/host/omap.c.
> 
> No.  dma_mask is a pointer to u64.  You need it stored in some variable.

Oops yeah.
 
> But, really, we shouldn't be initializing dma masks in drivers at all.
> That's supposed to come from the point where devices are created - and
> in the case of DMA engine stuff, its not the peripheral device structures
> which are doing DMA (they're DMA-incapable) - they rely on the services
> of a separate IP to perform DMA on their behalf.

OK

Tony
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-18 19:55               ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-18 19:55 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 12:14]:
> On Wed, Apr 18, 2012 at 11:19:18AM -0700, Tony Lindgren wrote:
> > * T Krishnamoorthy, Balaji <balajitk@ti.com> [120418 08:39]:
> > > On Wed, Apr 18, 2012 at 8:59 PM, Russell King - ARM Linux
> > > <linux@arm.linux.org.uk> wrote:
> > > > On Wed, Apr 18, 2012 at 08:53:32PM +0530, T Krishnamoorthy, Balaji wrote:
> > > >> Hi,
> > > >>
> > > >> drivers/mmc/host/omap.c is also using dma_mask should that also be removed
> > > >
> > > > Does this driver make use of this platform data?
> > > >
> > > > If so, it needs converting to DMA engine _before_ this patch (which is
> > > > one reason why its a good idea to do these changes as separate patches...
> > > > as I've done.) ?It means stuff like this can be slotted in as necessary
> > > > in the patch order.
> > 
> > Can't we can just do this in drivers/mmc/host/omap.c:
> > 
> > - host->dev->dma_mask = &pdata->dma_mask;
> > + host->dev->dma_mask = 0xffffffff;
> > 
> > And that way drop it from the platform data. Looks like some omap1
> > omap_mmc_platform_data don't have it set though, but we do have host->use_dma = 1
> > set anyways in drivers/mmc/host/omap.c.
> 
> No.  dma_mask is a pointer to u64.  You need it stored in some variable.

Oops yeah.
 
> But, really, we shouldn't be initializing dma masks in drivers at all.
> That's supposed to come from the point where devices are created - and
> in the case of DMA engine stuff, its not the peripheral device structures
> which are doing DMA (they're DMA-incapable) - they rely on the services
> of a separate IP to perform DMA on their behalf.

OK

Tony

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 19:42           ` Russell King - ARM Linux
@ 2012-04-18 20:02             ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-18 20:02 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: T Krishnamoorthy, Balaji, linux-arm-kernel, linux-omap, linux-mmc

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 12:46]:
> On Wed, Apr 18, 2012 at 09:05:56PM +0530, T Krishnamoorthy, Balaji wrote:
> > On Wed, Apr 18, 2012 at 8:59 PM, Russell King - ARM Linux
> > <linux@arm.linux.org.uk> wrote:
> > > On Wed, Apr 18, 2012 at 08:53:32PM +0530, T Krishnamoorthy, Balaji wrote:
> > >> Hi,
> > >>
> > >> drivers/mmc/host/omap.c is also using dma_mask should that also be removed
> > >
> > > Does this driver make use of this platform data?
> > >
> > > If so, it needs converting to DMA engine _before_ this patch (which is
> > > one reason why its a good idea to do these changes as separate patches...
> > > as I've done.)  It means stuff like this can be slotted in as necessary
> > > in the patch order.
> > >
> > > I'm told that driver is OMAP2 only - I don't have any OMAP2 platforms to
> > > be able to test it on (I only have OMAP3 and OMAP4.)
> > 
> > Yes, this driver is for omap2420 and omap1 devices.
> 
> Looking at this driver, there seems to be some interaction between how
> the DMA is programmed for each part of the scatterlist, and how the
> MMC interface is programmed, specifically:
> 
>         frame = data->blksz;
>         if (cpu_is_omap15xx() && frame > 32)
>                 frame = 32;
>         else if (frame > 64)
>                 frame = 64;
>         frame >>= 1;
>         if (!(data->flags & MMC_DATA_WRITE)) {
>                 buf = 0x800f | ((frame - 1) << 8);
>         } else {
>                 buf = 0x0f80 | ((frame - 1) << 0);
>         }
>         OMAP_MMC_WRITE(host, BUF, buf);
> 
> Does this register have to be written on every scatterlist entry, or can
> it be written once at the start of request processing?

The scatterlist entries are separate DMA requests on omap1, there's no
chaining support in the DMA hardware on omap1. So yes, it's for every
scatterlist entry on omap1.

Regards,

Tony

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-18 20:02             ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-18 20:02 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 12:46]:
> On Wed, Apr 18, 2012 at 09:05:56PM +0530, T Krishnamoorthy, Balaji wrote:
> > On Wed, Apr 18, 2012 at 8:59 PM, Russell King - ARM Linux
> > <linux@arm.linux.org.uk> wrote:
> > > On Wed, Apr 18, 2012 at 08:53:32PM +0530, T Krishnamoorthy, Balaji wrote:
> > >> Hi,
> > >>
> > >> drivers/mmc/host/omap.c is also using dma_mask should that also be removed
> > >
> > > Does this driver make use of this platform data?
> > >
> > > If so, it needs converting to DMA engine _before_ this patch (which is
> > > one reason why its a good idea to do these changes as separate patches...
> > > as I've done.) ?It means stuff like this can be slotted in as necessary
> > > in the patch order.
> > >
> > > I'm told that driver is OMAP2 only - I don't have any OMAP2 platforms to
> > > be able to test it on (I only have OMAP3 and OMAP4.)
> > 
> > Yes, this driver is for omap2420 and omap1 devices.
> 
> Looking at this driver, there seems to be some interaction between how
> the DMA is programmed for each part of the scatterlist, and how the
> MMC interface is programmed, specifically:
> 
>         frame = data->blksz;
>         if (cpu_is_omap15xx() && frame > 32)
>                 frame = 32;
>         else if (frame > 64)
>                 frame = 64;
>         frame >>= 1;
>         if (!(data->flags & MMC_DATA_WRITE)) {
>                 buf = 0x800f | ((frame - 1) << 8);
>         } else {
>                 buf = 0x0f80 | ((frame - 1) << 0);
>         }
>         OMAP_MMC_WRITE(host, BUF, buf);
> 
> Does this register have to be written on every scatterlist entry, or can
> it be written once at the start of request processing?

The scatterlist entries are separate DMA requests on omap1, there's no
chaining support in the DMA hardware on omap1. So yes, it's for every
scatterlist entry on omap1.

Regards,

Tony

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 20:02             ` Tony Lindgren
@ 2012-04-18 20:24               ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 20:24 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: T Krishnamoorthy, Balaji, linux-arm-kernel, linux-omap, linux-mmc

On Wed, Apr 18, 2012 at 01:02:15PM -0700, Tony Lindgren wrote:
> The scatterlist entries are separate DMA requests on omap1, there's no
> chaining support in the DMA hardware on omap1. So yes, it's for every
> scatterlist entry on omap1.

There's no direct scatterlist support on OMAP2 either (omap_hsmmc _used_
to do each scatterlist entry separately too).  The DMA engine driver
does the same, but will walk the scatterlist internally rather than the
driver having to do it.

I'd like to have the same thing happen on OMAP1 as well (it's actually
quite simple to do) and it means that this DMA engine implementation
detail is (correctly) hidden from the drivers - provided there's no
dependencies on each individual scatterlist entry back to the peripheral
driver.

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-18 20:24               ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 20:24 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 01:02:15PM -0700, Tony Lindgren wrote:
> The scatterlist entries are separate DMA requests on omap1, there's no
> chaining support in the DMA hardware on omap1. So yes, it's for every
> scatterlist entry on omap1.

There's no direct scatterlist support on OMAP2 either (omap_hsmmc _used_
to do each scatterlist entry separately too).  The DMA engine driver
does the same, but will walk the scatterlist internally rather than the
driver having to do it.

I'd like to have the same thing happen on OMAP1 as well (it's actually
quite simple to do) and it means that this DMA engine implementation
detail is (correctly) hidden from the drivers - provided there's no
dependencies on each individual scatterlist entry back to the peripheral
driver.

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 20:24               ` Russell King - ARM Linux
@ 2012-04-18 21:01                 ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-18 21:01 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: T Krishnamoorthy, Balaji, linux-arm-kernel, linux-omap, linux-mmc

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 13:28]:
> On Wed, Apr 18, 2012 at 01:02:15PM -0700, Tony Lindgren wrote:
> > The scatterlist entries are separate DMA requests on omap1, there's no
> > chaining support in the DMA hardware on omap1. So yes, it's for every
> > scatterlist entry on omap1.
> 
> There's no direct scatterlist support on OMAP2 either (omap_hsmmc _used_
> to do each scatterlist entry separately too).  The DMA engine driver
> does the same, but will walk the scatterlist internally rather than the
> driver having to do it.

OK
 
> I'd like to have the same thing happen on OMAP1 as well (it's actually
> quite simple to do) and it means that this DMA engine implementation
> detail is (correctly) hidden from the drivers - provided there's no
> dependencies on each individual scatterlist entry back to the peripheral
> driver.

That would be nice. If the DMA engine driver can't dynamically adjust the
frame size for each SG entry, then maybe we can work around that by not doing
DMA for entries that are below the frame sizes?

Tony


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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-18 21:01                 ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-18 21:01 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 13:28]:
> On Wed, Apr 18, 2012 at 01:02:15PM -0700, Tony Lindgren wrote:
> > The scatterlist entries are separate DMA requests on omap1, there's no
> > chaining support in the DMA hardware on omap1. So yes, it's for every
> > scatterlist entry on omap1.
> 
> There's no direct scatterlist support on OMAP2 either (omap_hsmmc _used_
> to do each scatterlist entry separately too).  The DMA engine driver
> does the same, but will walk the scatterlist internally rather than the
> driver having to do it.

OK
 
> I'd like to have the same thing happen on OMAP1 as well (it's actually
> quite simple to do) and it means that this DMA engine implementation
> detail is (correctly) hidden from the drivers - provided there's no
> dependencies on each individual scatterlist entry back to the peripheral
> driver.

That would be nice. If the DMA engine driver can't dynamically adjust the
frame size for each SG entry, then maybe we can work around that by not doing
DMA for entries that are below the frame sizes?

Tony

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 21:01                 ` Tony Lindgren
@ 2012-04-18 21:16                   ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 21:16 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: T Krishnamoorthy, Balaji, linux-arm-kernel, linux-omap, linux-mmc

On Wed, Apr 18, 2012 at 02:01:44PM -0700, Tony Lindgren wrote:
> * Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 13:28]:
> > I'd like to have the same thing happen on OMAP1 as well (it's actually
> > quite simple to do) and it means that this DMA engine implementation
> > detail is (correctly) hidden from the drivers - provided there's no
> > dependencies on each individual scatterlist entry back to the peripheral
> > driver.
> 
> That would be nice. If the DMA engine driver can't dynamically adjust the
> frame size for each SG entry, then maybe we can work around that by not doing
> DMA for entries that are below the frame sizes?

I don't think it has to in this case, though I am thinking that we
may have to adjust the frame size for other peripherals.

In the case of the OMAP1 MMC driver, let me pull out that chunk of code
again:

        frame = data->blksz;
        if (cpu_is_omap15xx() && frame > 32)
                frame = 32;
        else if (frame > 64)
                frame = 64;
        frame >>= 1;
        if (!(data->flags & MMC_DATA_WRITE)) {
                buf = 0x800f | ((frame - 1) << 8);
        } else {
                buf = 0x0f80 | ((frame - 1) << 0);
        }
        OMAP_MMC_WRITE(host, BUF, buf);

Nothing there depends on the size of an individual scatterlist entry -
the dependencies for the frame size are the smaller of the FIFO size
and the data block size.  I believe we can cope with that quite well on
a per-transfer basis, and I _think_ we can get away with writing this
BUF register just once per transfer (similar to what happens for PIO
transfers.)

However, this is going to be rather hit and miss for me - all I can do
is create a patch and check that it builds.  I have no way (that I know
of) to test this change as I don't have any OMAP1 nor OMAP2 hardware.

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-18 21:16                   ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 21:16 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 02:01:44PM -0700, Tony Lindgren wrote:
> * Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 13:28]:
> > I'd like to have the same thing happen on OMAP1 as well (it's actually
> > quite simple to do) and it means that this DMA engine implementation
> > detail is (correctly) hidden from the drivers - provided there's no
> > dependencies on each individual scatterlist entry back to the peripheral
> > driver.
> 
> That would be nice. If the DMA engine driver can't dynamically adjust the
> frame size for each SG entry, then maybe we can work around that by not doing
> DMA for entries that are below the frame sizes?

I don't think it has to in this case, though I am thinking that we
may have to adjust the frame size for other peripherals.

In the case of the OMAP1 MMC driver, let me pull out that chunk of code
again:

        frame = data->blksz;
        if (cpu_is_omap15xx() && frame > 32)
                frame = 32;
        else if (frame > 64)
                frame = 64;
        frame >>= 1;
        if (!(data->flags & MMC_DATA_WRITE)) {
                buf = 0x800f | ((frame - 1) << 8);
        } else {
                buf = 0x0f80 | ((frame - 1) << 0);
        }
        OMAP_MMC_WRITE(host, BUF, buf);

Nothing there depends on the size of an individual scatterlist entry -
the dependencies for the frame size are the smaller of the FIFO size
and the data block size.  I believe we can cope with that quite well on
a per-transfer basis, and I _think_ we can get away with writing this
BUF register just once per transfer (similar to what happens for PIO
transfers.)

However, this is going to be rather hit and miss for me - all I can do
is create a patch and check that it builds.  I have no way (that I know
of) to test this change as I don't have any OMAP1 nor OMAP2 hardware.

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 21:16                   ` Russell King - ARM Linux
@ 2012-04-18 21:34                     ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-18 21:34 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: T Krishnamoorthy, Balaji, linux-arm-kernel, linux-omap, linux-mmc

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 14:19]:
> On Wed, Apr 18, 2012 at 02:01:44PM -0700, Tony Lindgren wrote:
> > * Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 13:28]:
> > > I'd like to have the same thing happen on OMAP1 as well (it's actually
> > > quite simple to do) and it means that this DMA engine implementation
> > > detail is (correctly) hidden from the drivers - provided there's no
> > > dependencies on each individual scatterlist entry back to the peripheral
> > > driver.
> > 
> > That would be nice. If the DMA engine driver can't dynamically adjust the
> > frame size for each SG entry, then maybe we can work around that by not doing
> > DMA for entries that are below the frame sizes?
> 
> I don't think it has to in this case, though I am thinking that we
> may have to adjust the frame size for other peripherals.
> 
> In the case of the OMAP1 MMC driver, let me pull out that chunk of code
> again:
> 
>         frame = data->blksz;
>         if (cpu_is_omap15xx() && frame > 32)
>                 frame = 32;
>         else if (frame > 64)
>                 frame = 64;
>         frame >>= 1;
>         if (!(data->flags & MMC_DATA_WRITE)) {
>                 buf = 0x800f | ((frame - 1) << 8);
>         } else {
>                 buf = 0x0f80 | ((frame - 1) << 0);
>         }
>         OMAP_MMC_WRITE(host, BUF, buf);
> 
> Nothing there depends on the size of an individual scatterlist entry -
> the dependencies for the frame size are the smaller of the FIFO size
> and the data block size.  I believe we can cope with that quite well on
> a per-transfer basis, and I _think_ we can get away with writing this
> BUF register just once per transfer (similar to what happens for PIO
> transfers.)

OK, let's see if that works :)
 
> However, this is going to be rather hit and miss for me - all I can do
> is create a patch and check that it builds.  I have no way (that I know
> of) to test this change as I don't have any OMAP1 nor OMAP2 hardware.

I can certainly test it on at least for 1710 on 770 and 2420 on N800.

Regards,

Tony

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-18 21:34                     ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-18 21:34 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 14:19]:
> On Wed, Apr 18, 2012 at 02:01:44PM -0700, Tony Lindgren wrote:
> > * Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 13:28]:
> > > I'd like to have the same thing happen on OMAP1 as well (it's actually
> > > quite simple to do) and it means that this DMA engine implementation
> > > detail is (correctly) hidden from the drivers - provided there's no
> > > dependencies on each individual scatterlist entry back to the peripheral
> > > driver.
> > 
> > That would be nice. If the DMA engine driver can't dynamically adjust the
> > frame size for each SG entry, then maybe we can work around that by not doing
> > DMA for entries that are below the frame sizes?
> 
> I don't think it has to in this case, though I am thinking that we
> may have to adjust the frame size for other peripherals.
> 
> In the case of the OMAP1 MMC driver, let me pull out that chunk of code
> again:
> 
>         frame = data->blksz;
>         if (cpu_is_omap15xx() && frame > 32)
>                 frame = 32;
>         else if (frame > 64)
>                 frame = 64;
>         frame >>= 1;
>         if (!(data->flags & MMC_DATA_WRITE)) {
>                 buf = 0x800f | ((frame - 1) << 8);
>         } else {
>                 buf = 0x0f80 | ((frame - 1) << 0);
>         }
>         OMAP_MMC_WRITE(host, BUF, buf);
> 
> Nothing there depends on the size of an individual scatterlist entry -
> the dependencies for the frame size are the smaller of the FIFO size
> and the data block size.  I believe we can cope with that quite well on
> a per-transfer basis, and I _think_ we can get away with writing this
> BUF register just once per transfer (similar to what happens for PIO
> transfers.)

OK, let's see if that works :)
 
> However, this is going to be rather hit and miss for me - all I can do
> is create a patch and check that it builds.  I have no way (that I know
> of) to test this change as I don't have any OMAP1 nor OMAP2 hardware.

I can certainly test it on at least for 1710 on 770 and 2420 on N800.

Regards,

Tony

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 21:16                   ` Russell King - ARM Linux
@ 2012-04-18 21:36                     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 21:36 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-omap, linux-mmc, T Krishnamoorthy, Balaji, linux-arm-kernel

On Wed, Apr 18, 2012 at 10:16:06PM +0100, Russell King - ARM Linux wrote:
> I don't think it has to in this case, though I am thinking that we
> may have to adjust the frame size for other peripherals.
> 
> In the case of the OMAP1 MMC driver, let me pull out that chunk of code
> again:
> 
>         frame = data->blksz;
>         if (cpu_is_omap15xx() && frame > 32)
>                 frame = 32;
>         else if (frame > 64)
>                 frame = 64;
>         frame >>= 1;
>         if (!(data->flags & MMC_DATA_WRITE)) {
>                 buf = 0x800f | ((frame - 1) << 8);
>         } else {
>                 buf = 0x0f80 | ((frame - 1) << 0);
>         }
>         OMAP_MMC_WRITE(host, BUF, buf);
> 
> Nothing there depends on the size of an individual scatterlist entry -
> the dependencies for the frame size are the smaller of the FIFO size
> and the data block size.  I believe we can cope with that quite well on
> a per-transfer basis, and I _think_ we can get away with writing this
> BUF register just once per transfer (similar to what happens for PIO
> transfers.)
> 
> However, this is going to be rather hit and miss for me - all I can do
> is create a patch and check that it builds.  I have no way (that I know
> of) to test this change as I don't have any OMAP1 nor OMAP2 hardware.

Right, so from a purely theoretical standpoint, here's the OMAP1 driver
converted, and build-tested only.  Modulo a few warnings and patch 8,
you should be able to convert between DMA engine and the original DMA
engine implementation by changing the definition at the top.

I have to stress: this is untested, it may eat your MMC/SD cards.  Test
it carefully and test it well.

diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 887c0e5..a77c72e 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -10,6 +10,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+#define USE_DMA_ENGINE
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
@@ -17,6 +18,7 @@
 #include <linux/ioport.h>
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
+#include <linux/dmaengine.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
 #include <linux/spinlock.h>
@@ -128,6 +130,10 @@ struct mmc_omap_host {
 	unsigned char		id; /* 16xx chips have 2 MMC blocks */
 	struct clk *		iclk;
 	struct clk *		fclk;
+	struct dma_chan		*dma_rx;
+	u32			dma_rx_burst;
+	struct dma_chan		*dma_tx;
+	u32			dma_tx_burst;
 	struct resource		*mem_res;
 	void __iomem		*virt_base;
 	unsigned int		phys_base;
@@ -153,12 +159,14 @@ struct mmc_omap_host {
 
 	unsigned		use_dma:1;
 	unsigned		brs_received:1, dma_done:1;
-	unsigned		dma_is_read:1;
 	unsigned		dma_in_use:1;
+#ifdef USE_DMA_PRIVATE
+	unsigned		dma_is_read:1;
 	int			dma_ch;
-	spinlock_t		dma_lock;
 	struct timer_list	dma_timer;
 	unsigned		dma_len;
+#endif
+	spinlock_t		dma_lock;
 
 	struct mmc_omap_slot    *slots[OMAP_MMC_MAX_SLOTS];
 	struct mmc_omap_slot    *current_slot;
@@ -406,18 +414,34 @@ mmc_omap_release_dma(struct mmc_omap_host *host, struct mmc_data *data,
 		     int abort)
 {
 	enum dma_data_direction dma_data_dir;
+	struct device *dev = mmc_dev(host->mmc);
+	struct dma_chan *c;
 
+#ifdef USE_DMA_PRIVATE
 	BUG_ON(host->dma_ch < 0);
 	if (data->error)
 		omap_stop_dma(host->dma_ch);
 	/* Release DMA channel lazily */
 	mod_timer(&host->dma_timer, jiffies + HZ);
-	if (data->flags & MMC_DATA_WRITE)
+#endif
+	if (data->flags & MMC_DATA_WRITE) {
 		dma_data_dir = DMA_TO_DEVICE;
-	else
+		c = host->dma_tx;
+	} else {
 		dma_data_dir = DMA_FROM_DEVICE;
-	dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
-		     dma_data_dir);
+		c = host->dma_rx;
+	}
+#ifdef USE_DMA_ENGINE
+	if (c) {
+		if (data->error) {
+			dmaengine_terminate_all(c);
+			/* Claim nothing transferred on error... */
+			data->bytes_xfered = 0;
+		}
+		dev = c->device->dev;
+	}
+#endif
+	dma_unmap_sg(dev, data->sg, host->sg_len, dma_data_dir);
 }
 
 static void mmc_omap_send_stop_work(struct work_struct *work)
@@ -524,6 +548,7 @@ mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data)
 		mmc_omap_xfer_done(host, data);
 }
 
+#ifdef USE_DMA_PRIVATE
 static void
 mmc_omap_dma_timer(unsigned long data)
 {
@@ -533,6 +558,7 @@ mmc_omap_dma_timer(unsigned long data)
 	omap_free_dma(host->dma_ch);
 	host->dma_ch = -1;
 }
+#endif
 
 static void
 mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data)
@@ -891,6 +917,19 @@ static void mmc_omap_cover_handler(unsigned long param)
 		  jiffies + msecs_to_jiffies(OMAP_MMC_COVER_POLL_DELAY));
 }
 
+#ifdef USE_DMA_ENGINE
+static void mmc_omap_dma_callback(void *priv)
+{
+	struct mmc_omap_host *host = priv;
+	struct mmc_data *data = host->data;
+
+	/* If we got to the end of DMA, assume everything went well */
+	data->bytes_xfered += data->blocks * data->blksz;
+
+	mmc_omap_dma_done(host, data);
+}
+#endif
+#ifdef USE_DMA_PRIVATE
 /* Prepare to transfer the next segment of a scatterlist */
 static void
 mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data)
@@ -1045,6 +1084,7 @@ static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data
 
 	return 0;
 }
+#endif
 
 static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req)
 {
@@ -1117,6 +1157,82 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
 	}
 
 	host->sg_idx = 0;
+#ifdef USE_DMA_ENGINE
+	if (use_dma) {
+		enum dma_data_direction dma_data_dir;
+		struct dma_async_tx_descriptor *tx;
+		struct dma_chan *c;
+		u32 burst, *bp;
+		u16 buf;
+
+		/*
+		 * FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx
+		 * and 24xx. Use 16 or 32 word frames when the
+		 * blocksize is at least that large. Blocksize is
+		 * usually 512 bytes; but not for some SD reads.
+		 */
+		burst = cpu_is_omap15xx() ? 32 : 64;
+		if (burst > data->blksz)
+			burst = data->blksz;
+
+		burst >>= 1;
+
+		if (data->flags & MMC_DATA_WRITE) {
+			c = host->dma_tx;
+			bp = &host->dma_tx_burst;
+			buf = 0x0f80 | (burst - 1) << 0;
+			dma_data_dir = DMA_TO_DEVICE;
+		} else {
+			c = host->dma_rx;
+			bp = &host->dma_rx_burst;
+			buf = 0x800f | (burst - 1) << 8;
+			dma_data_dir = DMA_FROM_DEVICE;
+		}
+
+		if (!c)
+			goto use_pio;
+
+		/* Only reconfigure if we have a different burst size */
+		if (*bp != burst) {
+			struct dma_slave_config cfg;
+
+			cfg.src_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
+			cfg.dst_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
+			cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+			cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+			cfg.src_maxburst = burst;
+			cfg.dst_maxburst = burst;
+
+			if (dmaengine_slave_config(c, &cfg))
+				goto use_pio;
+
+			*bp = burst;
+		}
+
+		host->sg_len = dma_map_sg(c->device->dev, data->sg, sg_len,
+					  dma_data_dir);
+		if (host->sg_len == 0)
+			goto use_pio;
+
+		tx = dmaengine_prep_slave_sg(c, data->sg, host->sg_len,
+			data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+		if (!tx)
+			goto use_pio;
+
+		OMAP_MMC_WRITE(host, BUF, buf);
+
+		tx->callback = mmc_omap_dma_callback;
+		tx->callback_param = host;
+		dmaengine_submit(tx);
+		host->brs_received = 0;
+		host->dma_done = 0;
+		host->dma_in_use = 1;
+		return;
+	}
+ use_pio:
+#endif
+#ifdef USE_DMA_PRIVATE
 	if (use_dma) {
 		if (mmc_omap_get_dma_channel(host, data) == 0) {
 			enum dma_data_direction dma_data_dir;
@@ -1136,6 +1252,9 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
 		} else
 			use_dma = 0;
 	}
+#else
+	use_dma = 0;
+#endif
 
 	/* Revert to PIO? */
 	if (!use_dma) {
@@ -1157,8 +1276,17 @@ static void mmc_omap_start_request(struct mmc_omap_host *host,
 	/* only touch fifo AFTER the controller readies it */
 	mmc_omap_prepare_data(host, req);
 	mmc_omap_start_command(host, req->cmd);
-	if (host->dma_in_use)
-		omap_start_dma(host->dma_ch);
+	if (host->dma_in_use) {
+		struct dma_chan *c = host->data->flags & MMC_DATA_WRITE ?
+				host->dma_tx : host->dma_rx;
+
+		if (c)
+			dma_async_issue_pending(c);
+#ifdef USE_DMA_PRIVATE
+		else
+			omap_start_dma(host->dma_ch);
+#endif
+	}
 }
 
 static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)
@@ -1395,11 +1523,15 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
 	mmc_free_host(mmc);
 }
 
+extern bool omap_dma_filter_fn(struct dma_chan *chan, void *param);
+
 static int __init mmc_omap_probe(struct platform_device *pdev)
 {
 	struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
 	struct mmc_omap_host *host = NULL;
 	struct resource *res;
+	dma_cap_mask_t mask;
+	unsigned sig;
 	int i, ret = 0;
 	int irq;
 
@@ -1439,7 +1571,9 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 	setup_timer(&host->clk_timer, mmc_omap_clk_timer, (unsigned long) host);
 
 	spin_lock_init(&host->dma_lock);
+#ifdef USE_DMA_PRIVATE
 	setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host);
+#endif
 	spin_lock_init(&host->slot_lock);
 	init_waitqueue_head(&host->slot_wq);
 
@@ -1452,8 +1586,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 	host->irq = irq;
 
 	host->use_dma = 1;
+#ifdef USE_DMA_PRIVATE
 	host->dev->dma_mask = &pdata->dma_mask;
 	host->dma_ch = -1;
+#endif
 
 	host->irq = irq;
 	host->phys_base = host->mem_res->start;
@@ -1474,9 +1610,46 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 		goto err_free_iclk;
 	}
 
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+#ifdef USE_DMA_ENGINE
+	host->dma_tx_burst = -1;
+	host->dma_rx_burst = -1;
+
+	sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
+	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+#if 0
+	if (!host->dma_tx) {
+		def_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
+			sig);
+		goto err_dma;
+	}
+#else
+	if (!host->dma_tx)
+		dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n",
+			sig);
+#endif
+#endif
+#ifdef USE_DMA_ENGINE
+	sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
+	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+#if 0
+	if (!host->dma_rx) {
+		dev_err(host->dev, "unable to obtain RX DMA engine channel %u\n",
+			sig);
+		goto err_dma;
+	}
+#else
+	if (!host->dma_rx)
+		dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n",
+			sig);
+#endif
+#endif
+
 	ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);
 	if (ret)
-		goto err_free_fclk;
+		goto err_free_dma;
 
 	if (pdata->init != NULL) {
 		ret = pdata->init(&pdev->dev);
@@ -1504,7 +1677,11 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 		pdata->cleanup(&pdev->dev);
 err_free_irq:
 	free_irq(host->irq, host);
-err_free_fclk:
+err_free_dma:
+	if (host->dma_tx)
+		dma_release_channel(host->dma_tx);
+	if (host->dma_rx)
+		dma_release_channel(host->dma_rx);
 	clk_put(host->fclk);
 err_free_iclk:
 	clk_disable(host->iclk);
@@ -1539,6 +1716,11 @@ static int mmc_omap_remove(struct platform_device *pdev)
 	clk_disable(host->iclk);
 	clk_put(host->iclk);
 
+	if (host->dma_tx)
+		dma_release_channel(host->dma_tx);
+	if (host->dma_rx)
+		dma_release_channel(host->dma_rx);
+
 	iounmap(host->virt_base);
 	release_mem_region(pdev->resource[0].start,
 			   pdev->resource[0].end - pdev->resource[0].start + 1);


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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-18 21:36                     ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-18 21:36 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 10:16:06PM +0100, Russell King - ARM Linux wrote:
> I don't think it has to in this case, though I am thinking that we
> may have to adjust the frame size for other peripherals.
> 
> In the case of the OMAP1 MMC driver, let me pull out that chunk of code
> again:
> 
>         frame = data->blksz;
>         if (cpu_is_omap15xx() && frame > 32)
>                 frame = 32;
>         else if (frame > 64)
>                 frame = 64;
>         frame >>= 1;
>         if (!(data->flags & MMC_DATA_WRITE)) {
>                 buf = 0x800f | ((frame - 1) << 8);
>         } else {
>                 buf = 0x0f80 | ((frame - 1) << 0);
>         }
>         OMAP_MMC_WRITE(host, BUF, buf);
> 
> Nothing there depends on the size of an individual scatterlist entry -
> the dependencies for the frame size are the smaller of the FIFO size
> and the data block size.  I believe we can cope with that quite well on
> a per-transfer basis, and I _think_ we can get away with writing this
> BUF register just once per transfer (similar to what happens for PIO
> transfers.)
> 
> However, this is going to be rather hit and miss for me - all I can do
> is create a patch and check that it builds.  I have no way (that I know
> of) to test this change as I don't have any OMAP1 nor OMAP2 hardware.

Right, so from a purely theoretical standpoint, here's the OMAP1 driver
converted, and build-tested only.  Modulo a few warnings and patch 8,
you should be able to convert between DMA engine and the original DMA
engine implementation by changing the definition at the top.

I have to stress: this is untested, it may eat your MMC/SD cards.  Test
it carefully and test it well.

diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 887c0e5..a77c72e 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -10,6 +10,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+#define USE_DMA_ENGINE
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
@@ -17,6 +18,7 @@
 #include <linux/ioport.h>
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
+#include <linux/dmaengine.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
 #include <linux/spinlock.h>
@@ -128,6 +130,10 @@ struct mmc_omap_host {
 	unsigned char		id; /* 16xx chips have 2 MMC blocks */
 	struct clk *		iclk;
 	struct clk *		fclk;
+	struct dma_chan		*dma_rx;
+	u32			dma_rx_burst;
+	struct dma_chan		*dma_tx;
+	u32			dma_tx_burst;
 	struct resource		*mem_res;
 	void __iomem		*virt_base;
 	unsigned int		phys_base;
@@ -153,12 +159,14 @@ struct mmc_omap_host {
 
 	unsigned		use_dma:1;
 	unsigned		brs_received:1, dma_done:1;
-	unsigned		dma_is_read:1;
 	unsigned		dma_in_use:1;
+#ifdef USE_DMA_PRIVATE
+	unsigned		dma_is_read:1;
 	int			dma_ch;
-	spinlock_t		dma_lock;
 	struct timer_list	dma_timer;
 	unsigned		dma_len;
+#endif
+	spinlock_t		dma_lock;
 
 	struct mmc_omap_slot    *slots[OMAP_MMC_MAX_SLOTS];
 	struct mmc_omap_slot    *current_slot;
@@ -406,18 +414,34 @@ mmc_omap_release_dma(struct mmc_omap_host *host, struct mmc_data *data,
 		     int abort)
 {
 	enum dma_data_direction dma_data_dir;
+	struct device *dev = mmc_dev(host->mmc);
+	struct dma_chan *c;
 
+#ifdef USE_DMA_PRIVATE
 	BUG_ON(host->dma_ch < 0);
 	if (data->error)
 		omap_stop_dma(host->dma_ch);
 	/* Release DMA channel lazily */
 	mod_timer(&host->dma_timer, jiffies + HZ);
-	if (data->flags & MMC_DATA_WRITE)
+#endif
+	if (data->flags & MMC_DATA_WRITE) {
 		dma_data_dir = DMA_TO_DEVICE;
-	else
+		c = host->dma_tx;
+	} else {
 		dma_data_dir = DMA_FROM_DEVICE;
-	dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
-		     dma_data_dir);
+		c = host->dma_rx;
+	}
+#ifdef USE_DMA_ENGINE
+	if (c) {
+		if (data->error) {
+			dmaengine_terminate_all(c);
+			/* Claim nothing transferred on error... */
+			data->bytes_xfered = 0;
+		}
+		dev = c->device->dev;
+	}
+#endif
+	dma_unmap_sg(dev, data->sg, host->sg_len, dma_data_dir);
 }
 
 static void mmc_omap_send_stop_work(struct work_struct *work)
@@ -524,6 +548,7 @@ mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data)
 		mmc_omap_xfer_done(host, data);
 }
 
+#ifdef USE_DMA_PRIVATE
 static void
 mmc_omap_dma_timer(unsigned long data)
 {
@@ -533,6 +558,7 @@ mmc_omap_dma_timer(unsigned long data)
 	omap_free_dma(host->dma_ch);
 	host->dma_ch = -1;
 }
+#endif
 
 static void
 mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data)
@@ -891,6 +917,19 @@ static void mmc_omap_cover_handler(unsigned long param)
 		  jiffies + msecs_to_jiffies(OMAP_MMC_COVER_POLL_DELAY));
 }
 
+#ifdef USE_DMA_ENGINE
+static void mmc_omap_dma_callback(void *priv)
+{
+	struct mmc_omap_host *host = priv;
+	struct mmc_data *data = host->data;
+
+	/* If we got to the end of DMA, assume everything went well */
+	data->bytes_xfered += data->blocks * data->blksz;
+
+	mmc_omap_dma_done(host, data);
+}
+#endif
+#ifdef USE_DMA_PRIVATE
 /* Prepare to transfer the next segment of a scatterlist */
 static void
 mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data)
@@ -1045,6 +1084,7 @@ static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data
 
 	return 0;
 }
+#endif
 
 static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req)
 {
@@ -1117,6 +1157,82 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
 	}
 
 	host->sg_idx = 0;
+#ifdef USE_DMA_ENGINE
+	if (use_dma) {
+		enum dma_data_direction dma_data_dir;
+		struct dma_async_tx_descriptor *tx;
+		struct dma_chan *c;
+		u32 burst, *bp;
+		u16 buf;
+
+		/*
+		 * FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx
+		 * and 24xx. Use 16 or 32 word frames when the
+		 * blocksize is at least that large. Blocksize is
+		 * usually 512 bytes; but not for some SD reads.
+		 */
+		burst = cpu_is_omap15xx() ? 32 : 64;
+		if (burst > data->blksz)
+			burst = data->blksz;
+
+		burst >>= 1;
+
+		if (data->flags & MMC_DATA_WRITE) {
+			c = host->dma_tx;
+			bp = &host->dma_tx_burst;
+			buf = 0x0f80 | (burst - 1) << 0;
+			dma_data_dir = DMA_TO_DEVICE;
+		} else {
+			c = host->dma_rx;
+			bp = &host->dma_rx_burst;
+			buf = 0x800f | (burst - 1) << 8;
+			dma_data_dir = DMA_FROM_DEVICE;
+		}
+
+		if (!c)
+			goto use_pio;
+
+		/* Only reconfigure if we have a different burst size */
+		if (*bp != burst) {
+			struct dma_slave_config cfg;
+
+			cfg.src_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
+			cfg.dst_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
+			cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+			cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+			cfg.src_maxburst = burst;
+			cfg.dst_maxburst = burst;
+
+			if (dmaengine_slave_config(c, &cfg))
+				goto use_pio;
+
+			*bp = burst;
+		}
+
+		host->sg_len = dma_map_sg(c->device->dev, data->sg, sg_len,
+					  dma_data_dir);
+		if (host->sg_len == 0)
+			goto use_pio;
+
+		tx = dmaengine_prep_slave_sg(c, data->sg, host->sg_len,
+			data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+		if (!tx)
+			goto use_pio;
+
+		OMAP_MMC_WRITE(host, BUF, buf);
+
+		tx->callback = mmc_omap_dma_callback;
+		tx->callback_param = host;
+		dmaengine_submit(tx);
+		host->brs_received = 0;
+		host->dma_done = 0;
+		host->dma_in_use = 1;
+		return;
+	}
+ use_pio:
+#endif
+#ifdef USE_DMA_PRIVATE
 	if (use_dma) {
 		if (mmc_omap_get_dma_channel(host, data) == 0) {
 			enum dma_data_direction dma_data_dir;
@@ -1136,6 +1252,9 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
 		} else
 			use_dma = 0;
 	}
+#else
+	use_dma = 0;
+#endif
 
 	/* Revert to PIO? */
 	if (!use_dma) {
@@ -1157,8 +1276,17 @@ static void mmc_omap_start_request(struct mmc_omap_host *host,
 	/* only touch fifo AFTER the controller readies it */
 	mmc_omap_prepare_data(host, req);
 	mmc_omap_start_command(host, req->cmd);
-	if (host->dma_in_use)
-		omap_start_dma(host->dma_ch);
+	if (host->dma_in_use) {
+		struct dma_chan *c = host->data->flags & MMC_DATA_WRITE ?
+				host->dma_tx : host->dma_rx;
+
+		if (c)
+			dma_async_issue_pending(c);
+#ifdef USE_DMA_PRIVATE
+		else
+			omap_start_dma(host->dma_ch);
+#endif
+	}
 }
 
 static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)
@@ -1395,11 +1523,15 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
 	mmc_free_host(mmc);
 }
 
+extern bool omap_dma_filter_fn(struct dma_chan *chan, void *param);
+
 static int __init mmc_omap_probe(struct platform_device *pdev)
 {
 	struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
 	struct mmc_omap_host *host = NULL;
 	struct resource *res;
+	dma_cap_mask_t mask;
+	unsigned sig;
 	int i, ret = 0;
 	int irq;
 
@@ -1439,7 +1571,9 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 	setup_timer(&host->clk_timer, mmc_omap_clk_timer, (unsigned long) host);
 
 	spin_lock_init(&host->dma_lock);
+#ifdef USE_DMA_PRIVATE
 	setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host);
+#endif
 	spin_lock_init(&host->slot_lock);
 	init_waitqueue_head(&host->slot_wq);
 
@@ -1452,8 +1586,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 	host->irq = irq;
 
 	host->use_dma = 1;
+#ifdef USE_DMA_PRIVATE
 	host->dev->dma_mask = &pdata->dma_mask;
 	host->dma_ch = -1;
+#endif
 
 	host->irq = irq;
 	host->phys_base = host->mem_res->start;
@@ -1474,9 +1610,46 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 		goto err_free_iclk;
 	}
 
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+#ifdef USE_DMA_ENGINE
+	host->dma_tx_burst = -1;
+	host->dma_rx_burst = -1;
+
+	sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
+	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+#if 0
+	if (!host->dma_tx) {
+		def_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
+			sig);
+		goto err_dma;
+	}
+#else
+	if (!host->dma_tx)
+		dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n",
+			sig);
+#endif
+#endif
+#ifdef USE_DMA_ENGINE
+	sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
+	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+#if 0
+	if (!host->dma_rx) {
+		dev_err(host->dev, "unable to obtain RX DMA engine channel %u\n",
+			sig);
+		goto err_dma;
+	}
+#else
+	if (!host->dma_rx)
+		dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n",
+			sig);
+#endif
+#endif
+
 	ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);
 	if (ret)
-		goto err_free_fclk;
+		goto err_free_dma;
 
 	if (pdata->init != NULL) {
 		ret = pdata->init(&pdev->dev);
@@ -1504,7 +1677,11 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 		pdata->cleanup(&pdev->dev);
 err_free_irq:
 	free_irq(host->irq, host);
-err_free_fclk:
+err_free_dma:
+	if (host->dma_tx)
+		dma_release_channel(host->dma_tx);
+	if (host->dma_rx)
+		dma_release_channel(host->dma_rx);
 	clk_put(host->fclk);
 err_free_iclk:
 	clk_disable(host->iclk);
@@ -1539,6 +1716,11 @@ static int mmc_omap_remove(struct platform_device *pdev)
 	clk_disable(host->iclk);
 	clk_put(host->iclk);
 
+	if (host->dma_tx)
+		dma_release_channel(host->dma_tx);
+	if (host->dma_rx)
+		dma_release_channel(host->dma_rx);
+
 	iounmap(host->virt_base);
 	release_mem_region(pdev->resource[0].start,
 			   pdev->resource[0].end - pdev->resource[0].start + 1);

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-18 21:36                     ` Russell King - ARM Linux
@ 2012-04-19  1:39                       ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-19  1:39 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-omap, linux-mmc, T Krishnamoorthy, Balaji, linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 14:40]:
> On Wed, Apr 18, 2012 at 10:16:06PM +0100, Russell King - ARM Linux wrote:
> > I don't think it has to in this case, though I am thinking that we
> > may have to adjust the frame size for other peripherals.
> > 
> > In the case of the OMAP1 MMC driver, let me pull out that chunk of code
> > again:
> > 
> >         frame = data->blksz;
> >         if (cpu_is_omap15xx() && frame > 32)
> >                 frame = 32;
> >         else if (frame > 64)
> >                 frame = 64;
> >         frame >>= 1;
> >         if (!(data->flags & MMC_DATA_WRITE)) {
> >                 buf = 0x800f | ((frame - 1) << 8);
> >         } else {
> >                 buf = 0x0f80 | ((frame - 1) << 0);
> >         }
> >         OMAP_MMC_WRITE(host, BUF, buf);
> > 
> > Nothing there depends on the size of an individual scatterlist entry -
> > the dependencies for the frame size are the smaller of the FIFO size
> > and the data block size.  I believe we can cope with that quite well on
> > a per-transfer basis, and I _think_ we can get away with writing this
> > BUF register just once per transfer (similar to what happens for PIO
> > transfers.)
> > 
> > However, this is going to be rather hit and miss for me - all I can do
> > is create a patch and check that it builds.  I have no way (that I know
> > of) to test this change as I don't have any OMAP1 nor OMAP2 hardware.
> 
> Right, so from a purely theoretical standpoint, here's the OMAP1 driver
> converted, and build-tested only.  Modulo a few warnings and patch 8,
> you should be able to convert between DMA engine and the original DMA
> engine implementation by changing the definition at the top.
> 
> I have to stress: this is untested, it may eat your MMC/SD cards.  Test
> it carefully and test it well.

Cool, you almost got it. Got it working for n800 and 770 with the following
patch. Only extremely light testing done now so careful with this patch too..

Had to hack in support for the src_port and dst_port that's needed for
omap_set_dma_src/dest_params on omap1. For that I just used existing
struct omap_dma_channel_params to pass src_port and dst_port to
omap_dma_filter_fn. Got any better ideas for doing that?

Regards,

Tony


diff --git a/arch/arm/plat-omap/include/plat/dma.h b/arch/arm/plat-omap/include/plat/dma.h
index dc562a5..1f65d8e 100644
--- a/arch/arm/plat-omap/include/plat/dma.h
+++ b/arch/arm/plat-omap/include/plat/dma.h
@@ -401,6 +401,8 @@ struct omap_dma_channel_params {
 	unsigned char read_prio;/* read priority */
 	unsigned char write_prio;/* write priority */
 
+	unsigned dma_sig;
+
 #ifndef CONFIG_ARCH_OMAP1
 	enum omap_dma_burst_mode burst_mode; /* Burst mode 4/8/16 words */
 #endif
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
index 5b12f74..b2f8c62 100644
--- a/drivers/dma/omap-dma.c
+++ b/drivers/dma/omap-dma.c
@@ -36,6 +36,9 @@ struct omap_chan {
 	int dma_ch;
 	struct omap_desc *desc;
 	unsigned sgidx;
+
+	uint8_t src_port;	/* OMAP_DMA_PORT_xxx, omap1 only */
+	uint8_t dst_port;	/* OMAP_DMA_PORT_xxx, omap1 only */
 };
 
 struct omap_sg {
@@ -83,10 +86,10 @@ static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
 	struct omap_sg *sg = d->sg + idx;
 
 	if (d->dir == DMA_DEV_TO_MEM)
-		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+		omap_set_dma_dest_params(c->dma_ch, c->dst_port, OMAP_DMA_AMODE_POST_INC,
 				sg->addr, 0, 0);
 	else
-		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+		omap_set_dma_src_params(c->dma_ch, c->src_port, OMAP_DMA_AMODE_POST_INC,
 				sg->addr, 0, 0);
 
 	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
@@ -111,10 +114,10 @@ static void omap_dma_start_desc(struct omap_chan *c)
 	c->sgidx = 0;
 
 	if (d->dir == DMA_DEV_TO_MEM)
-		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+		omap_set_dma_src_params(c->dma_ch, c->src_port, OMAP_DMA_AMODE_CONSTANT,
 			d->dev_addr, 0, 0);
 	else
-		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+		omap_set_dma_dest_params(c->dma_ch, c->dst_port, OMAP_DMA_AMODE_CONSTANT,
 			d->dev_addr, 0, 0);
 
 	omap_dma_start_sg(c, d, 0);
@@ -475,11 +478,17 @@ static struct platform_driver omap_dma_driver = {
 
 bool omap_dma_filter_fn(struct dma_chan *chan, void *param)
 {
+	struct omap_dma_channel_params *p = param;
+
 	if (chan->device->dev->driver == &omap_dma_driver.driver) {
 		struct omap_chan *c = to_omap_dma_chan(chan);
-		unsigned req = *(unsigned *)param;
+		unsigned req = p->dma_sig;
 
-		return req == c->dma_sig;
+		if (req == c->dma_sig) {
+			c->src_port = p->src_port;
+			c->dst_port = p->dst_port;
+			return true;
+		}
 	}
 	return false;
 }
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index a77c72e..284e602 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -1531,7 +1531,7 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 	struct mmc_omap_host *host = NULL;
 	struct resource *res;
 	dma_cap_mask_t mask;
-	unsigned sig;
+	struct omap_dma_channel_params p;
 	int i, ret = 0;
 	int irq;
 
@@ -1616,34 +1616,49 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 #ifdef USE_DMA_ENGINE
 	host->dma_tx_burst = -1;
 	host->dma_rx_burst = -1;
-
-	sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
-	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+	if (cpu_is_omap24xx()) {
+		p.dma_sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX : OMAP24XX_DMA_MMC2_TX;
+		p.src_port = 0;
+		p.dst_port = 0;
+	} else {
+		p.dma_sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
+		p.src_port = OMAP_DMA_PORT_EMIFF;
+		p.dst_port = OMAP_DMA_PORT_TIPB;
+	}
+	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &p);
 #if 0
 	if (!host->dma_tx) {
-		def_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
+		dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
 			sig);
 		goto err_dma;
 	}
 #else
 	if (!host->dma_tx)
 		dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n",
-			sig);
+			p.dma_sig);
 #endif
 #endif
 #ifdef USE_DMA_ENGINE
-	sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
-	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+	if (cpu_is_omap24xx()) {
+		p.dma_sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX :  OMAP24XX_DMA_MMC2_RX;
+		p.src_port = 0;
+		p.dst_port = 0;
+	} else {
+		p.dma_sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
+		p.src_port = OMAP_DMA_PORT_TIPB;
+		p.dst_port = OMAP_DMA_PORT_EMIFF;
+	}
+	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &p);
 #if 0
 	if (!host->dma_rx) {
 		dev_err(host->dev, "unable to obtain RX DMA engine channel %u\n",
-			sig);
+			p.dma_sig);
 		goto err_dma;
 	}
 #else
 	if (!host->dma_rx)
 		dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n",
-			sig);
+			p.dma_sig);
 #endif
 #endif
 
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index fa85efe..f373791 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -42,6 +42,7 @@
 #include <plat/board.h>
 #include <plat/mmc.h>
 #include <plat/cpu.h>
+#include <plat/dma.h>
 
 /* OMAP HSMMC Host Controller Registers */
 #define OMAP_HSMMC_SYSCONFIG	0x0010
@@ -1772,6 +1773,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 	const struct of_device_id *match;
 	dma_cap_mask_t mask;
 	unsigned tx_req, rx_req;
+	struct omap_dma_channel_params p;
 
 	match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
 	if (match) {
@@ -1920,13 +1922,17 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 	dma_cap_zero(mask);
 	dma_cap_set(DMA_SLAVE, mask);
 
-	host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &rx_req);
+	p.dma_sig = rx_req;
+	p.src_port = 0;
+	p.dst_port = 0;
+	host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &p);
 	if (!host->rx_chan) {
 		dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
 		goto err_irq;
 	}
 
-	host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &tx_req);
+	p.dma_sig = tx_req;
+	host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &p);
 	if (!host->tx_chan) {
 		dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req);
 		goto err_irq;

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-19  1:39                       ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-19  1:39 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120418 14:40]:
> On Wed, Apr 18, 2012 at 10:16:06PM +0100, Russell King - ARM Linux wrote:
> > I don't think it has to in this case, though I am thinking that we
> > may have to adjust the frame size for other peripherals.
> > 
> > In the case of the OMAP1 MMC driver, let me pull out that chunk of code
> > again:
> > 
> >         frame = data->blksz;
> >         if (cpu_is_omap15xx() && frame > 32)
> >                 frame = 32;
> >         else if (frame > 64)
> >                 frame = 64;
> >         frame >>= 1;
> >         if (!(data->flags & MMC_DATA_WRITE)) {
> >                 buf = 0x800f | ((frame - 1) << 8);
> >         } else {
> >                 buf = 0x0f80 | ((frame - 1) << 0);
> >         }
> >         OMAP_MMC_WRITE(host, BUF, buf);
> > 
> > Nothing there depends on the size of an individual scatterlist entry -
> > the dependencies for the frame size are the smaller of the FIFO size
> > and the data block size.  I believe we can cope with that quite well on
> > a per-transfer basis, and I _think_ we can get away with writing this
> > BUF register just once per transfer (similar to what happens for PIO
> > transfers.)
> > 
> > However, this is going to be rather hit and miss for me - all I can do
> > is create a patch and check that it builds.  I have no way (that I know
> > of) to test this change as I don't have any OMAP1 nor OMAP2 hardware.
> 
> Right, so from a purely theoretical standpoint, here's the OMAP1 driver
> converted, and build-tested only.  Modulo a few warnings and patch 8,
> you should be able to convert between DMA engine and the original DMA
> engine implementation by changing the definition at the top.
> 
> I have to stress: this is untested, it may eat your MMC/SD cards.  Test
> it carefully and test it well.

Cool, you almost got it. Got it working for n800 and 770 with the following
patch. Only extremely light testing done now so careful with this patch too..

Had to hack in support for the src_port and dst_port that's needed for
omap_set_dma_src/dest_params on omap1. For that I just used existing
struct omap_dma_channel_params to pass src_port and dst_port to
omap_dma_filter_fn. Got any better ideas for doing that?

Regards,

Tony


diff --git a/arch/arm/plat-omap/include/plat/dma.h b/arch/arm/plat-omap/include/plat/dma.h
index dc562a5..1f65d8e 100644
--- a/arch/arm/plat-omap/include/plat/dma.h
+++ b/arch/arm/plat-omap/include/plat/dma.h
@@ -401,6 +401,8 @@ struct omap_dma_channel_params {
 	unsigned char read_prio;/* read priority */
 	unsigned char write_prio;/* write priority */
 
+	unsigned dma_sig;
+
 #ifndef CONFIG_ARCH_OMAP1
 	enum omap_dma_burst_mode burst_mode; /* Burst mode 4/8/16 words */
 #endif
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
index 5b12f74..b2f8c62 100644
--- a/drivers/dma/omap-dma.c
+++ b/drivers/dma/omap-dma.c
@@ -36,6 +36,9 @@ struct omap_chan {
 	int dma_ch;
 	struct omap_desc *desc;
 	unsigned sgidx;
+
+	uint8_t src_port;	/* OMAP_DMA_PORT_xxx, omap1 only */
+	uint8_t dst_port;	/* OMAP_DMA_PORT_xxx, omap1 only */
 };
 
 struct omap_sg {
@@ -83,10 +86,10 @@ static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
 	struct omap_sg *sg = d->sg + idx;
 
 	if (d->dir == DMA_DEV_TO_MEM)
-		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+		omap_set_dma_dest_params(c->dma_ch, c->dst_port, OMAP_DMA_AMODE_POST_INC,
 				sg->addr, 0, 0);
 	else
-		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+		omap_set_dma_src_params(c->dma_ch, c->src_port, OMAP_DMA_AMODE_POST_INC,
 				sg->addr, 0, 0);
 
 	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
@@ -111,10 +114,10 @@ static void omap_dma_start_desc(struct omap_chan *c)
 	c->sgidx = 0;
 
 	if (d->dir == DMA_DEV_TO_MEM)
-		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+		omap_set_dma_src_params(c->dma_ch, c->src_port, OMAP_DMA_AMODE_CONSTANT,
 			d->dev_addr, 0, 0);
 	else
-		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+		omap_set_dma_dest_params(c->dma_ch, c->dst_port, OMAP_DMA_AMODE_CONSTANT,
 			d->dev_addr, 0, 0);
 
 	omap_dma_start_sg(c, d, 0);
@@ -475,11 +478,17 @@ static struct platform_driver omap_dma_driver = {
 
 bool omap_dma_filter_fn(struct dma_chan *chan, void *param)
 {
+	struct omap_dma_channel_params *p = param;
+
 	if (chan->device->dev->driver == &omap_dma_driver.driver) {
 		struct omap_chan *c = to_omap_dma_chan(chan);
-		unsigned req = *(unsigned *)param;
+		unsigned req = p->dma_sig;
 
-		return req == c->dma_sig;
+		if (req == c->dma_sig) {
+			c->src_port = p->src_port;
+			c->dst_port = p->dst_port;
+			return true;
+		}
 	}
 	return false;
 }
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index a77c72e..284e602 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -1531,7 +1531,7 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 	struct mmc_omap_host *host = NULL;
 	struct resource *res;
 	dma_cap_mask_t mask;
-	unsigned sig;
+	struct omap_dma_channel_params p;
 	int i, ret = 0;
 	int irq;
 
@@ -1616,34 +1616,49 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 #ifdef USE_DMA_ENGINE
 	host->dma_tx_burst = -1;
 	host->dma_rx_burst = -1;
-
-	sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
-	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+	if (cpu_is_omap24xx()) {
+		p.dma_sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX : OMAP24XX_DMA_MMC2_TX;
+		p.src_port = 0;
+		p.dst_port = 0;
+	} else {
+		p.dma_sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
+		p.src_port = OMAP_DMA_PORT_EMIFF;
+		p.dst_port = OMAP_DMA_PORT_TIPB;
+	}
+	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &p);
 #if 0
 	if (!host->dma_tx) {
-		def_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
+		dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
 			sig);
 		goto err_dma;
 	}
 #else
 	if (!host->dma_tx)
 		dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n",
-			sig);
+			p.dma_sig);
 #endif
 #endif
 #ifdef USE_DMA_ENGINE
-	sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
-	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+	if (cpu_is_omap24xx()) {
+		p.dma_sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX :  OMAP24XX_DMA_MMC2_RX;
+		p.src_port = 0;
+		p.dst_port = 0;
+	} else {
+		p.dma_sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
+		p.src_port = OMAP_DMA_PORT_TIPB;
+		p.dst_port = OMAP_DMA_PORT_EMIFF;
+	}
+	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &p);
 #if 0
 	if (!host->dma_rx) {
 		dev_err(host->dev, "unable to obtain RX DMA engine channel %u\n",
-			sig);
+			p.dma_sig);
 		goto err_dma;
 	}
 #else
 	if (!host->dma_rx)
 		dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n",
-			sig);
+			p.dma_sig);
 #endif
 #endif
 
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index fa85efe..f373791 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -42,6 +42,7 @@
 #include <plat/board.h>
 #include <plat/mmc.h>
 #include <plat/cpu.h>
+#include <plat/dma.h>
 
 /* OMAP HSMMC Host Controller Registers */
 #define OMAP_HSMMC_SYSCONFIG	0x0010
@@ -1772,6 +1773,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 	const struct of_device_id *match;
 	dma_cap_mask_t mask;
 	unsigned tx_req, rx_req;
+	struct omap_dma_channel_params p;
 
 	match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
 	if (match) {
@@ -1920,13 +1922,17 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
 	dma_cap_zero(mask);
 	dma_cap_set(DMA_SLAVE, mask);
 
-	host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &rx_req);
+	p.dma_sig = rx_req;
+	p.src_port = 0;
+	p.dst_port = 0;
+	host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &p);
 	if (!host->rx_chan) {
 		dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
 		goto err_irq;
 	}
 
-	host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &tx_req);
+	p.dma_sig = tx_req;
+	host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &p);
 	if (!host->tx_chan) {
 		dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req);
 		goto err_irq;

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-19  1:39                       ` Tony Lindgren
@ 2012-04-19 17:43                         ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-19 17:43 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-omap, linux-mmc, T Krishnamoorthy, Balaji, linux-arm-kernel

On Wed, Apr 18, 2012 at 06:39:14PM -0700, Tony Lindgren wrote:
> Cool, you almost got it. Got it working for n800 and 770 with the following
> patch. Only extremely light testing done now so careful with this patch too..
> 
> Had to hack in support for the src_port and dst_port that's needed for
> omap_set_dma_src/dest_params on omap1.

What's the relationship between these ports and the DMA request signal?
Is there a document which describes this?

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-19 17:43                         ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-19 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 06:39:14PM -0700, Tony Lindgren wrote:
> Cool, you almost got it. Got it working for n800 and 770 with the following
> patch. Only extremely light testing done now so careful with this patch too..
> 
> Had to hack in support for the src_port and dst_port that's needed for
> omap_set_dma_src/dest_params on omap1.

What's the relationship between these ports and the DMA request signal?
Is there a document which describes this?

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-19 17:43                         ` Russell King - ARM Linux
@ 2012-04-19 18:07                           ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-19 18:07 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-omap, linux-mmc, T Krishnamoorthy, Balaji, linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120419 10:46]:
> On Wed, Apr 18, 2012 at 06:39:14PM -0700, Tony Lindgren wrote:
> > Cool, you almost got it. Got it working for n800 and 770 with the following
> > patch. Only extremely light testing done now so careful with this patch too..
> > 
> > Had to hack in support for the src_port and dst_port that's needed for
> > omap_set_dma_src/dest_params on omap1.
> 
> What's the relationship between these ports and the DMA request signal?
> Is there a document which describes this?

It's the source and destination addresses bus port, so it's more related
to the addresses than request signal. I guess routing of the DMA request
signal is what it really does.

I'm now wondering if it might be possible to set it automatically based on
source and destination address.

There's some information in the omap5912 trm on pages 717 and 718:

http://www.ti.com/litv/pdf/spru742

It says for CSDP source port "This field identifies the port originator of
the transfer."

Regards,

Tony

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-19 18:07                           ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-19 18:07 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120419 10:46]:
> On Wed, Apr 18, 2012 at 06:39:14PM -0700, Tony Lindgren wrote:
> > Cool, you almost got it. Got it working for n800 and 770 with the following
> > patch. Only extremely light testing done now so careful with this patch too..
> > 
> > Had to hack in support for the src_port and dst_port that's needed for
> > omap_set_dma_src/dest_params on omap1.
> 
> What's the relationship between these ports and the DMA request signal?
> Is there a document which describes this?

It's the source and destination addresses bus port, so it's more related
to the addresses than request signal. I guess routing of the DMA request
signal is what it really does.

I'm now wondering if it might be possible to set it automatically based on
source and destination address.

There's some information in the omap5912 trm on pages 717 and 718:

http://www.ti.com/litv/pdf/spru742

It says for CSDP source port "This field identifies the port originator of
the transfer."

Regards,

Tony

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-19 18:07                           ` Tony Lindgren
@ 2012-04-20 15:10                             ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-20 15:10 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-omap, linux-mmc, T Krishnamoorthy, Balaji, linux-arm-kernel

On Thu, Apr 19, 2012 at 11:07:42AM -0700, Tony Lindgren wrote:
> * Russell King - ARM Linux <linux@arm.linux.org.uk> [120419 10:46]:
> > On Wed, Apr 18, 2012 at 06:39:14PM -0700, Tony Lindgren wrote:
> > > Cool, you almost got it. Got it working for n800 and 770 with the following
> > > patch. Only extremely light testing done now so careful with this patch too..
> > > 
> > > Had to hack in support for the src_port and dst_port that's needed for
> > > omap_set_dma_src/dest_params on omap1.
> > 
> > What's the relationship between these ports and the DMA request signal?
> > Is there a document which describes this?
> 
> It's the source and destination addresses bus port, so it's more related
> to the addresses than request signal. I guess routing of the DMA request
> signal is what it really does.
> 
> I'm now wondering if it might be possible to set it automatically based on
> source and destination address.
> 
> There's some information in the omap5912 trm on pages 717 and 718:
> 
> http://www.ti.com/litv/pdf/spru742
> 
> It says for CSDP source port "This field identifies the port originator of
> the transfer."

Is there a reason not to use EMIFF as for the memory port for any DMA
activity between a peripheral and system (kernel) memory?

It looks to me like TIPB is the right port to use for anything except
camera and LCD?

If that's true, we can solve the memory side of the problem very easily -
we know that prep_slave_sg() will always souce from system memory, so
the port for that will be known.

For the peripheral side of the transfer, I think we should case this on
the request signal as the combinations seem to be very limited - and it
looks like this knowledge is specific to the DMA controller rather than
the peripheral.

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-20 15:10                             ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-20 15:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Apr 19, 2012 at 11:07:42AM -0700, Tony Lindgren wrote:
> * Russell King - ARM Linux <linux@arm.linux.org.uk> [120419 10:46]:
> > On Wed, Apr 18, 2012 at 06:39:14PM -0700, Tony Lindgren wrote:
> > > Cool, you almost got it. Got it working for n800 and 770 with the following
> > > patch. Only extremely light testing done now so careful with this patch too..
> > > 
> > > Had to hack in support for the src_port and dst_port that's needed for
> > > omap_set_dma_src/dest_params on omap1.
> > 
> > What's the relationship between these ports and the DMA request signal?
> > Is there a document which describes this?
> 
> It's the source and destination addresses bus port, so it's more related
> to the addresses than request signal. I guess routing of the DMA request
> signal is what it really does.
> 
> I'm now wondering if it might be possible to set it automatically based on
> source and destination address.
> 
> There's some information in the omap5912 trm on pages 717 and 718:
> 
> http://www.ti.com/litv/pdf/spru742
> 
> It says for CSDP source port "This field identifies the port originator of
> the transfer."

Is there a reason not to use EMIFF as for the memory port for any DMA
activity between a peripheral and system (kernel) memory?

It looks to me like TIPB is the right port to use for anything except
camera and LCD?

If that's true, we can solve the memory side of the problem very easily -
we know that prep_slave_sg() will always souce from system memory, so
the port for that will be known.

For the peripheral side of the transfer, I think we should case this on
the request signal as the combinations seem to be very limited - and it
looks like this knowledge is specific to the DMA controller rather than
the peripheral.

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-20 15:10                             ` Russell King - ARM Linux
@ 2012-04-20 15:26                               ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-20 15:26 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-omap, linux-mmc, T Krishnamoorthy, Balaji, linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120420 08:15]:
> On Thu, Apr 19, 2012 at 11:07:42AM -0700, Tony Lindgren wrote:
> > * Russell King - ARM Linux <linux@arm.linux.org.uk> [120419 10:46]:
> > > On Wed, Apr 18, 2012 at 06:39:14PM -0700, Tony Lindgren wrote:
> > > > Cool, you almost got it. Got it working for n800 and 770 with the following
> > > > patch. Only extremely light testing done now so careful with this patch too..
> > > > 
> > > > Had to hack in support for the src_port and dst_port that's needed for
> > > > omap_set_dma_src/dest_params on omap1.
> > > 
> > > What's the relationship between these ports and the DMA request signal?
> > > Is there a document which describes this?
> > 
> > It's the source and destination addresses bus port, so it's more related
> > to the addresses than request signal. I guess routing of the DMA request
> > signal is what it really does.
> > 
> > I'm now wondering if it might be possible to set it automatically based on
> > source and destination address.

There's probably some latency involved that requires setting the src
and dst port earlier, so I doubt that setting it automatically based on
the src and dst address would work correctly.

> > There's some information in the omap5912 trm on pages 717 and 718:
> > 
> > http://www.ti.com/litv/pdf/spru742
> > 
> > It says for CSDP source port "This field identifies the port originator of
> > the transfer."
> 
> Is there a reason not to use EMIFF as for the memory port for any DMA
> activity between a peripheral and system (kernel) memory?

I don't think we have any device to device cases, and it's unlikely that
we'll get them either.
 
> It looks to me like TIPB is the right port to use for anything except
> camera and LCD?
>
> If that's true, we can solve the memory side of the problem very easily -
> we know that prep_slave_sg() will always souce from system memory, so
> the port for that will be known.

OK
 
> For the peripheral side of the transfer, I think we should case this on
> the request signal as the combinations seem to be very limited - and it
> looks like this knowledge is specific to the DMA controller rather than
> the peripheral.

OK sounds good to me.

Regards,

Tony

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-20 15:26                               ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-20 15:26 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120420 08:15]:
> On Thu, Apr 19, 2012 at 11:07:42AM -0700, Tony Lindgren wrote:
> > * Russell King - ARM Linux <linux@arm.linux.org.uk> [120419 10:46]:
> > > On Wed, Apr 18, 2012 at 06:39:14PM -0700, Tony Lindgren wrote:
> > > > Cool, you almost got it. Got it working for n800 and 770 with the following
> > > > patch. Only extremely light testing done now so careful with this patch too..
> > > > 
> > > > Had to hack in support for the src_port and dst_port that's needed for
> > > > omap_set_dma_src/dest_params on omap1.
> > > 
> > > What's the relationship between these ports and the DMA request signal?
> > > Is there a document which describes this?
> > 
> > It's the source and destination addresses bus port, so it's more related
> > to the addresses than request signal. I guess routing of the DMA request
> > signal is what it really does.
> > 
> > I'm now wondering if it might be possible to set it automatically based on
> > source and destination address.

There's probably some latency involved that requires setting the src
and dst port earlier, so I doubt that setting it automatically based on
the src and dst address would work correctly.

> > There's some information in the omap5912 trm on pages 717 and 718:
> > 
> > http://www.ti.com/litv/pdf/spru742
> > 
> > It says for CSDP source port "This field identifies the port originator of
> > the transfer."
> 
> Is there a reason not to use EMIFF as for the memory port for any DMA
> activity between a peripheral and system (kernel) memory?

I don't think we have any device to device cases, and it's unlikely that
we'll get them either.
 
> It looks to me like TIPB is the right port to use for anything except
> camera and LCD?
>
> If that's true, we can solve the memory side of the problem very easily -
> we know that prep_slave_sg() will always souce from system memory, so
> the port for that will be known.

OK
 
> For the peripheral side of the transfer, I think we should case this on
> the request signal as the combinations seem to be very limited - and it
> looks like this knowledge is specific to the DMA controller rather than
> the peripheral.

OK sounds good to me.

Regards,

Tony

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-20 15:26                               ` Tony Lindgren
@ 2012-04-20 15:37                                 ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-20 15:37 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-omap, linux-mmc, T Krishnamoorthy, Balaji, linux-arm-kernel

On Fri, Apr 20, 2012 at 08:26:25AM -0700, Tony Lindgren wrote:
> * Russell King - ARM Linux <linux@arm.linux.org.uk> [120420 08:15]:
> > On Thu, Apr 19, 2012 at 11:07:42AM -0700, Tony Lindgren wrote:
> > > There's some information in the omap5912 trm on pages 717 and 718:
> > > 
> > > http://www.ti.com/litv/pdf/spru742
> > > 
> > > It says for CSDP source port "This field identifies the port originator of
> > > the transfer."
> > 
> > Is there a reason not to use EMIFF as for the memory port for any DMA
> > activity between a peripheral and system (kernel) memory?
> 
> I don't think we have any device to device cases, and it's unlikely that
> we'll get them either.
>  
> > It looks to me like TIPB is the right port to use for anything except
> > camera and LCD?
> >
> > If that's true, we can solve the memory side of the problem very easily -
> > we know that prep_slave_sg() will always souce from system memory, so
> > the port for that will be known.
> 
> OK
>  
> > For the peripheral side of the transfer, I think we should case this on
> > the request signal as the combinations seem to be very limited - and it
> > looks like this knowledge is specific to the DMA controller rather than
> > the peripheral.
> 
> OK sounds good to me.

Okay, so this turns out to be really quite simple as the first step - as
below.  This makes us always use EMIFF for the memory side, and TIPB for
the peripherals, which won't be a problem until we start thinking about
camera and LCD.

I'm not going to conditionalise their use because they're merely ignored
by arch/arm/plat-omap/dma.c on non-OMAP1 targets, so having this driver
set them to zero for non-OMAP1 makes little sense (other than just making
things unnecessarily more complicated.)

So, with this patch plus my original patch to omap's mmc host driver, this
should result in something which works without all the overhead of drivers
supplying the port information.  Please confirm, and I'll merge this into
the original omap-dma.c commit.

Thanks.

 drivers/dma/omap-dma.c |   18 ++++++++++--------
 1 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
index 5b12f74..ed1b8f5 100644
--- a/drivers/dma/omap-dma.c
+++ b/drivers/dma/omap-dma.c
@@ -52,6 +52,7 @@ struct omap_desc {
 	uint8_t es;		/* element size */
 	uint8_t sync_mode;	/* OMAP_DMA_SYNC_xxx */
 	uint8_t sync_type;	/* OMAP_DMA_xxx_SYNC* */
+	uint8_t periph_port;	/* Peripheral port */
 
 	unsigned sglen;
 	struct omap_sg sg[0];
@@ -83,11 +84,11 @@ static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
 	struct omap_sg *sg = d->sg + idx;
 
 	if (d->dir == DMA_DEV_TO_MEM)
-		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
-				sg->addr, 0, 0);
+		omap_set_dma_dest_params(c->dma_ch, d->periph_port,
+			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
 	else
-		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
-				sg->addr, 0, 0);
+		omap_set_dma_src_params(c->dma_ch, d->periph_port,
+			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
 
 	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
 		d->sync_mode, c->dma_sig, d->sync_type);
@@ -111,11 +112,11 @@ static void omap_dma_start_desc(struct omap_chan *c)
 	c->sgidx = 0;
 
 	if (d->dir == DMA_DEV_TO_MEM)
-		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
-			d->dev_addr, 0, 0);
+		omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
 	else
-		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
-			d->dev_addr, 0, 0);
+		omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
 
 	omap_dma_start_sg(c, d, 0);
 }
@@ -269,6 +270,7 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
 	d->es = es;
 	d->sync_mode = OMAP_DMA_SYNC_FRAME;
 	d->sync_type = sync_type;
+	d->periph_port = OMAP_DMA_PORT_TIPB;
 
 	/*
 	 * Build our scatterlist entries: each contains the address,


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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-20 15:37                                 ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-20 15:37 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 20, 2012 at 08:26:25AM -0700, Tony Lindgren wrote:
> * Russell King - ARM Linux <linux@arm.linux.org.uk> [120420 08:15]:
> > On Thu, Apr 19, 2012 at 11:07:42AM -0700, Tony Lindgren wrote:
> > > There's some information in the omap5912 trm on pages 717 and 718:
> > > 
> > > http://www.ti.com/litv/pdf/spru742
> > > 
> > > It says for CSDP source port "This field identifies the port originator of
> > > the transfer."
> > 
> > Is there a reason not to use EMIFF as for the memory port for any DMA
> > activity between a peripheral and system (kernel) memory?
> 
> I don't think we have any device to device cases, and it's unlikely that
> we'll get them either.
>  
> > It looks to me like TIPB is the right port to use for anything except
> > camera and LCD?
> >
> > If that's true, we can solve the memory side of the problem very easily -
> > we know that prep_slave_sg() will always souce from system memory, so
> > the port for that will be known.
> 
> OK
>  
> > For the peripheral side of the transfer, I think we should case this on
> > the request signal as the combinations seem to be very limited - and it
> > looks like this knowledge is specific to the DMA controller rather than
> > the peripheral.
> 
> OK sounds good to me.

Okay, so this turns out to be really quite simple as the first step - as
below.  This makes us always use EMIFF for the memory side, and TIPB for
the peripherals, which won't be a problem until we start thinking about
camera and LCD.

I'm not going to conditionalise their use because they're merely ignored
by arch/arm/plat-omap/dma.c on non-OMAP1 targets, so having this driver
set them to zero for non-OMAP1 makes little sense (other than just making
things unnecessarily more complicated.)

So, with this patch plus my original patch to omap's mmc host driver, this
should result in something which works without all the overhead of drivers
supplying the port information.  Please confirm, and I'll merge this into
the original omap-dma.c commit.

Thanks.

 drivers/dma/omap-dma.c |   18 ++++++++++--------
 1 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
index 5b12f74..ed1b8f5 100644
--- a/drivers/dma/omap-dma.c
+++ b/drivers/dma/omap-dma.c
@@ -52,6 +52,7 @@ struct omap_desc {
 	uint8_t es;		/* element size */
 	uint8_t sync_mode;	/* OMAP_DMA_SYNC_xxx */
 	uint8_t sync_type;	/* OMAP_DMA_xxx_SYNC* */
+	uint8_t periph_port;	/* Peripheral port */
 
 	unsigned sglen;
 	struct omap_sg sg[0];
@@ -83,11 +84,11 @@ static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
 	struct omap_sg *sg = d->sg + idx;
 
 	if (d->dir == DMA_DEV_TO_MEM)
-		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
-				sg->addr, 0, 0);
+		omap_set_dma_dest_params(c->dma_ch, d->periph_port,
+			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
 	else
-		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
-				sg->addr, 0, 0);
+		omap_set_dma_src_params(c->dma_ch, d->periph_port,
+			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
 
 	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
 		d->sync_mode, c->dma_sig, d->sync_type);
@@ -111,11 +112,11 @@ static void omap_dma_start_desc(struct omap_chan *c)
 	c->sgidx = 0;
 
 	if (d->dir == DMA_DEV_TO_MEM)
-		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
-			d->dev_addr, 0, 0);
+		omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
 	else
-		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
-			d->dev_addr, 0, 0);
+		omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
 
 	omap_dma_start_sg(c, d, 0);
 }
@@ -269,6 +270,7 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
 	d->es = es;
 	d->sync_mode = OMAP_DMA_SYNC_FRAME;
 	d->sync_type = sync_type;
+	d->periph_port = OMAP_DMA_PORT_TIPB;
 
 	/*
 	 * Build our scatterlist entries: each contains the address,

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-20 15:37                                 ` Russell King - ARM Linux
@ 2012-04-20 16:43                                   ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-20 16:43 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-omap, linux-mmc, T Krishnamoorthy, Balaji, linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120420 08:41]:
> --- a/drivers/dma/omap-dma.c
> +++ b/drivers/dma/omap-dma.c
> @@ -83,11 +84,11 @@ static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
>  	struct omap_sg *sg = d->sg + idx;
>  
>  	if (d->dir == DMA_DEV_TO_MEM)
> -		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
> -				sg->addr, 0, 0);
> +		omap_set_dma_dest_params(c->dma_ch, d->periph_port,
> +			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
>  	else
> -		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
> -				sg->addr, 0, 0);
> +		omap_set_dma_src_params(c->dma_ch, d->periph_port,
> +			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
>  
>  	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
>  		d->sync_mode, c->dma_sig, d->sync_type);

These are now wrong way around, should use OMAP_DMA_PORT_EMIFF here..

> @@ -111,11 +112,11 @@ static void omap_dma_start_desc(struct omap_chan *c)
>  	c->sgidx = 0;
>  
>  	if (d->dir == DMA_DEV_TO_MEM)
> -		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
> -			d->dev_addr, 0, 0);
> +		omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
> +			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
>  	else
> -		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
> -			d->dev_addr, 0, 0);
> +		omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
> +			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
>  
>  	omap_dma_start_sg(c, d, 0);
>  }

..and then use d->periph_port here. So the following is also needed on
top of this:

--- a/drivers/dma/omap-dma.c
+++ b/drivers/dma/omap-dma.c
@@ -84,10 +84,10 @@ static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
 	struct omap_sg *sg = d->sg + idx;
 
 	if (d->dir == DMA_DEV_TO_MEM)
-		omap_set_dma_dest_params(c->dma_ch, d->periph_port,
+		omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
 			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
 	else
-		omap_set_dma_src_params(c->dma_ch, d->periph_port,
+		omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
 			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
 
 	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
@@ -112,10 +112,10 @@ static void omap_dma_start_desc(struct omap_chan *c)
 	c->sgidx = 0;
 
 	if (d->dir == DMA_DEV_TO_MEM)
-		omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+		omap_set_dma_src_params(c->dma_ch, d->periph_port,
 			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
 	else
-		omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+		omap_set_dma_dest_params(c->dma_ch, d->periph_port,
 			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
 
 	omap_dma_start_sg(c, d, 0);

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-20 16:43                                   ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-20 16:43 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120420 08:41]:
> --- a/drivers/dma/omap-dma.c
> +++ b/drivers/dma/omap-dma.c
> @@ -83,11 +84,11 @@ static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
>  	struct omap_sg *sg = d->sg + idx;
>  
>  	if (d->dir == DMA_DEV_TO_MEM)
> -		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
> -				sg->addr, 0, 0);
> +		omap_set_dma_dest_params(c->dma_ch, d->periph_port,
> +			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
>  	else
> -		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
> -				sg->addr, 0, 0);
> +		omap_set_dma_src_params(c->dma_ch, d->periph_port,
> +			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
>  
>  	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
>  		d->sync_mode, c->dma_sig, d->sync_type);

These are now wrong way around, should use OMAP_DMA_PORT_EMIFF here..

> @@ -111,11 +112,11 @@ static void omap_dma_start_desc(struct omap_chan *c)
>  	c->sgidx = 0;
>  
>  	if (d->dir == DMA_DEV_TO_MEM)
> -		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
> -			d->dev_addr, 0, 0);
> +		omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
> +			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
>  	else
> -		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
> -			d->dev_addr, 0, 0);
> +		omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
> +			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
>  
>  	omap_dma_start_sg(c, d, 0);
>  }

..and then use d->periph_port here. So the following is also needed on
top of this:

--- a/drivers/dma/omap-dma.c
+++ b/drivers/dma/omap-dma.c
@@ -84,10 +84,10 @@ static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
 	struct omap_sg *sg = d->sg + idx;
 
 	if (d->dir == DMA_DEV_TO_MEM)
-		omap_set_dma_dest_params(c->dma_ch, d->periph_port,
+		omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
 			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
 	else
-		omap_set_dma_src_params(c->dma_ch, d->periph_port,
+		omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
 			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
 
 	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
@@ -112,10 +112,10 @@ static void omap_dma_start_desc(struct omap_chan *c)
 	c->sgidx = 0;
 
 	if (d->dir == DMA_DEV_TO_MEM)
-		omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+		omap_set_dma_src_params(c->dma_ch, d->periph_port,
 			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
 	else
-		omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+		omap_set_dma_dest_params(c->dma_ch, d->periph_port,
 			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
 
 	omap_dma_start_sg(c, d, 0);

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-20 15:37                                 ` Russell King - ARM Linux
@ 2012-04-20 16:50                                   ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-20 16:50 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-omap, linux-mmc, T Krishnamoorthy, Balaji, linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120420 08:41]:
> 
> So, with this patch plus my original patch to omap's mmc host driver, this
> should result in something which works without all the overhead of drivers
> supplying the port information.  Please confirm, and I'll merge this into
> the original omap-dma.c commit.

Then something like this is still needed on top of your original mmc driver
patch for omap2420:

--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -1617,11 +1617,14 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 	host->dma_tx_burst = -1;
 	host->dma_rx_burst = -1;
 
-	sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
+	if (cpu_is_omap24xx())
+		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX :  OMAP24XX_DMA_MMC2_TX;
+	else
+		sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
 	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
 #if 0
 	if (!host->dma_tx) {
-		def_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
+		dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
 			sig);
 		goto err_dma;
 	}
@@ -1632,7 +1635,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 #endif
 #endif
 #ifdef USE_DMA_ENGINE
-	sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
+	if (cpu_is_omap24xx())
+		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX :  OMAP24XX_DMA_MMC2_RX;
+	else
+		sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
 	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
 #if 0
 	if (!host->dma_rx) {

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-20 16:50                                   ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-20 16:50 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120420 08:41]:
> 
> So, with this patch plus my original patch to omap's mmc host driver, this
> should result in something which works without all the overhead of drivers
> supplying the port information.  Please confirm, and I'll merge this into
> the original omap-dma.c commit.

Then something like this is still needed on top of your original mmc driver
patch for omap2420:

--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -1617,11 +1617,14 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 	host->dma_tx_burst = -1;
 	host->dma_rx_burst = -1;
 
-	sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
+	if (cpu_is_omap24xx())
+		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX :  OMAP24XX_DMA_MMC2_TX;
+	else
+		sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
 	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
 #if 0
 	if (!host->dma_tx) {
-		def_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
+		dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
 			sig);
 		goto err_dma;
 	}
@@ -1632,7 +1635,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
 #endif
 #endif
 #ifdef USE_DMA_ENGINE
-	sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
+	if (cpu_is_omap24xx())
+		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX :  OMAP24XX_DMA_MMC2_RX;
+	else
+		sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
 	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
 #if 0
 	if (!host->dma_rx) {

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-20 16:43                                   ` Tony Lindgren
@ 2012-04-20 22:09                                     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-20 22:09 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-omap, linux-mmc, T Krishnamoorthy, Balaji, linux-arm-kernel

On Fri, Apr 20, 2012 at 09:43:07AM -0700, Tony Lindgren wrote:
> * Russell King - ARM Linux <linux@arm.linux.org.uk> [120420 08:41]:
> > --- a/drivers/dma/omap-dma.c
> > +++ b/drivers/dma/omap-dma.c
> > @@ -83,11 +84,11 @@ static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
> >  	struct omap_sg *sg = d->sg + idx;
> >  
> >  	if (d->dir == DMA_DEV_TO_MEM)
> > -		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
> > -				sg->addr, 0, 0);
> > +		omap_set_dma_dest_params(c->dma_ch, d->periph_port,
> > +			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
> >  	else
> > -		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
> > -				sg->addr, 0, 0);
> > +		omap_set_dma_src_params(c->dma_ch, d->periph_port,
> > +			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
> >  
> >  	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
> >  		d->sync_mode, c->dma_sig, d->sync_type);
> 
> These are now wrong way around, should use OMAP_DMA_PORT_EMIFF here..

Right, sorry about that.

Okay, I'll combine all these patches into the original adding the
OMAP DMA support tonight.  So, the new OMAP DMA driver now looks
like this:

 drivers/dma/Kconfig    |    6 +
 drivers/dma/Makefile   |    1 +
 drivers/dma/omap-dma.c |  521 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 528 insertions(+), 0 deletions(-)

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 5828ac4..792b486 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -261,6 +261,12 @@ config DMA_SA11X0
 	  SA-1110 SoCs.  This DMA engine can only be used with on-chip
 	  devices.
 
+config DMA_OMAP
+	tristate "OMAP DMA support"
+	depends on ARCH_OMAP
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+
 config DMA_ENGINE
 	bool
 
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index fc05f7d..ddc291a 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_PCH_DMA) += pch_dma.o
 obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
 obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
 obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
+obj-$(CONFIG_DMA_OMAP) += omap-dma.o
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
new file mode 100644
index 0000000..04df47f
--- /dev/null
+++ b/drivers/dma/omap-dma.c
@@ -0,0 +1,521 @@
+/*
+ * OMAP DMAengine support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+#include <plat/dma.h>
+
+struct omap_dmadev {
+	struct dma_device ddev;
+	spinlock_t lock;
+	struct tasklet_struct task;
+	struct list_head pending;
+};
+
+struct omap_chan {
+	struct virt_dma_chan vc;
+	struct list_head node;
+
+	struct dma_slave_config	cfg;
+	unsigned dma_sig;
+
+	int dma_ch;
+	struct omap_desc *desc;
+	unsigned sgidx;
+};
+
+struct omap_sg {
+	dma_addr_t addr;
+	uint32_t en;		/* number of elements (24-bit) */
+	uint32_t fn;		/* number of frames (16-bit) */
+};
+
+struct omap_desc {
+	struct virt_dma_desc vd;
+	enum dma_transfer_direction dir;
+	dma_addr_t dev_addr;
+
+	uint8_t es;		/* element size */
+	uint8_t sync_mode;	/* OMAP_DMA_SYNC_xxx */
+	uint8_t sync_type;	/* OMAP_DMA_xxx_SYNC* */
+	uint8_t periph_port;	/* Peripheral port */
+
+	unsigned sglen;
+	struct omap_sg sg[0];
+};
+
+static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d)
+{
+	return container_of(d, struct omap_dmadev, ddev);
+}
+
+static inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c)
+{
+	return container_of(c, struct omap_chan, vc.chan);
+}
+
+static inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t)
+{
+	return container_of(t, struct omap_desc, vd.tx);
+}
+
+static void omap_dma_desc_free(struct virt_dma_desc *vd)
+{
+	kfree(container_of(vd, struct omap_desc, vd));
+}
+
+static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
+	unsigned idx)
+{
+	struct omap_sg *sg = d->sg + idx;
+
+	if (d->dir == DMA_DEV_TO_MEM)
+		omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
+	else
+		omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
+
+	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
+		d->sync_mode, c->dma_sig, d->sync_type);
+
+	omap_start_dma(c->dma_ch);
+}
+
+static void omap_dma_start_desc(struct omap_chan *c)
+{
+	struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
+	struct omap_desc *d;
+
+	if (!vd) {
+		c->desc = NULL;
+		return;
+	}
+
+	list_del(&vd->node);
+
+	c->desc = d = to_omap_dma_desc(&vd->tx);
+	c->sgidx = 0;
+
+	if (d->dir == DMA_DEV_TO_MEM)
+		omap_set_dma_src_params(c->dma_ch, d->periph_port,
+			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
+	else
+		omap_set_dma_dest_params(c->dma_ch, d->periph_port,
+			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
+
+	omap_dma_start_sg(c, d, 0);
+}
+
+static void omap_dma_callback(int ch, u16 status, void *data)
+{
+	struct omap_chan *c = data;
+	struct omap_desc *d;
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	d = c->desc;
+	if (!d)
+		return;
+
+	if (++c->sgidx < d->sglen) {
+		omap_dma_start_sg(c, d, c->sgidx);
+	} else {
+		omap_dma_start_desc(c);
+		vchan_cookie_complete(&d->vd);
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+/*
+ * This callback schedules all pending channels.  We could be more
+ * clever here by postponing allocation of the real DMA channels to
+ * this point, and freeing them when our virtual channel becomes idle.
+ *
+ * We would then need to deal with 'all channels in-use'
+ */
+static void omap_dma_sched(unsigned long data)
+{
+	struct omap_dmadev *d = (struct omap_dmadev *)data;
+	LIST_HEAD(head);
+
+	spin_lock_irq(&d->lock);
+	list_splice_tail_init(&d->pending, &head);
+	spin_unlock_irq(&d->lock);
+
+	while (!list_empty(&head)) {
+		struct omap_chan *c = list_first_entry(&head,
+			struct omap_chan, node);
+
+		spin_lock_irq(&c->vc.lock);
+		list_del_init(&c->node);
+		omap_dma_start_desc(c);
+		spin_unlock_irq(&c->vc.lock);
+	}
+}
+
+static int omap_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+
+	dev_info(c->vc.chan.device->dev, "allocating channel for %u\n", c->dma_sig);
+
+	return omap_request_dma(c->dma_sig, "DMA engine",
+		omap_dma_callback, c, &c->dma_ch);
+}
+
+static void omap_dma_free_chan_resources(struct dma_chan *chan)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+
+	vchan_free_chan_resources(&c->vc);
+	omap_free_dma(c->dma_ch);
+
+	dev_info(c->vc.chan.device->dev, "freeing channel for %u\n", c->dma_sig);
+}
+
+static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
+	dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+	/*
+	 * FIXME: do we need to return pending bytes?
+	 * We have no users of that info at the moment...
+	 */
+	return dma_cookie_status(chan, cookie, txstate);
+}
+
+static void omap_dma_issue_pending(struct dma_chan *chan)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	if (vchan_issue_pending(&c->vc) && !c->desc) {
+		struct omap_dmadev *d = to_omap_dma_dev(chan->device);
+		spin_lock(&d->lock);
+		if (list_empty(&c->node))
+			list_add_tail(&c->node, &d->pending);
+		spin_unlock(&d->lock);
+		tasklet_schedule(&d->task);
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
+	struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen,
+	enum dma_transfer_direction dir, unsigned long tx_flags, void *context)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+	enum dma_slave_buswidth dev_width;
+	struct scatterlist *sgent;
+	struct omap_desc *d;
+	dma_addr_t dev_addr;
+	unsigned i, j = 0, es, es_bytes, en, frame_bytes, sync_type;
+	u32 burst;
+
+	if (dir == DMA_DEV_TO_MEM) {
+		dev_addr = c->cfg.src_addr;
+		dev_width = c->cfg.src_addr_width;
+		burst = c->cfg.src_maxburst;
+		sync_type = OMAP_DMA_SRC_SYNC;
+	} else if (dir == DMA_MEM_TO_DEV) {
+		dev_addr = c->cfg.dst_addr;
+		dev_width = c->cfg.dst_addr_width;
+		burst = c->cfg.dst_maxburst;
+		sync_type = OMAP_DMA_DST_SYNC;
+	} else {
+		dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
+		return NULL;
+	}
+
+	/* Bus width translates to the element size (ES) */
+	switch (dev_width) {
+	case DMA_SLAVE_BUSWIDTH_1_BYTE:
+		es = OMAP_DMA_DATA_TYPE_S8;
+		es_bytes = 1;
+		break;
+	case DMA_SLAVE_BUSWIDTH_2_BYTES:
+		es = OMAP_DMA_DATA_TYPE_S16;
+		es_bytes = 2;
+		break;
+	case DMA_SLAVE_BUSWIDTH_4_BYTES:
+		es = OMAP_DMA_DATA_TYPE_S32;
+		es_bytes = 4;
+		break;
+	default: /* not reached */
+		return NULL;
+	}
+
+	/* Now allocate and setup the descriptor. */
+	d = kzalloc(sizeof(*d) + sglen * sizeof(d->sg[0]), GFP_ATOMIC);
+	if (!d)
+		return NULL;
+
+	d->dir = dir;
+	d->dev_addr = dev_addr;
+	d->es = es;
+	d->sync_mode = OMAP_DMA_SYNC_FRAME;
+	d->sync_type = sync_type;
+	d->periph_port = OMAP_DMA_PORT_TIPB;
+
+	/*
+	 * Build our scatterlist entries: each contains the address,
+	 * the number of elements (EN) in each frame, and the number of
+	 * frames (FN).  Number of bytes for this entry = ES * EN * FN.
+	 *
+	 * Burst size translates to number of elements with frame sync.
+	 * Note: DMA engine defines burst to be the number of dev-width
+	 * transfers.
+	 */
+	en = burst;
+	frame_bytes = es_bytes * en;
+	for_each_sg(sgl, sgent, sglen, i) {
+		d->sg[j].addr = sg_dma_address(sgent);
+		d->sg[j].en = en;
+		d->sg[j].fn = sg_dma_len(sgent) / frame_bytes;
+		j++;
+	}
+
+	d->sglen = j;
+
+	return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
+}
+
+static int omap_dma_slave_config(struct omap_chan *c, struct dma_slave_config *cfg)
+{
+	if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
+	    cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
+		return -EINVAL;
+
+	memcpy(&c->cfg, cfg, sizeof(c->cfg));
+
+	return 0;
+}
+
+static int omap_dma_terminate_all(struct omap_chan *c)
+{
+	struct omap_dmadev *d = to_omap_dma_dev(c->vc.chan.device);
+	unsigned long flags;
+	LIST_HEAD(head);
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+
+	/* Prevent this channel being scheduled */
+	spin_lock(&d->lock);
+	list_del_init(&c->node);
+	spin_unlock(&d->lock);
+
+	/*
+	 * Stop DMA activity: we assume the callback will not be called
+	 * after omap_stop_dma() returns (even if it does, it will see
+	 * c->desc is NULL and exit.)
+	 */
+	if (c->desc) {
+		c->desc = NULL;
+		omap_stop_dma(c->dma_ch);
+	}
+
+	vchan_get_all_descriptors(&c->vc, &head);
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+	vchan_dma_desc_free_list(&c->vc, &head);
+
+	return 0;
+}
+
+static int omap_dma_pause(struct omap_chan *c)
+{
+	/* FIXME: not supported by platform private API */
+	return -EINVAL;
+}
+
+static int omap_dma_resume(struct omap_chan *c)
+{
+	/* FIXME: not supported by platform private API */
+	return -EINVAL;
+}
+
+static int omap_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+	unsigned long arg)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+	int ret;
+
+	switch (cmd) {
+	case DMA_SLAVE_CONFIG:
+		ret = omap_dma_slave_config(c, (struct dma_slave_config *)arg);
+		break;
+
+	case DMA_TERMINATE_ALL:
+		ret = omap_dma_terminate_all(c);
+		break;
+
+	case DMA_PAUSE:
+		ret = omap_dma_pause(c);
+		break;
+
+	case DMA_RESUME:
+		ret = omap_dma_resume(c);
+		break;
+
+	default:
+		ret = -ENXIO;
+		break;
+	}
+
+	return ret;
+}
+
+static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig)
+{
+	struct omap_chan *c;
+
+	c = kzalloc(sizeof(*c), GFP_KERNEL);
+	if (!c)
+		return -ENOMEM;
+
+	c->dma_sig = dma_sig;
+	c->vc.desc_free = omap_dma_desc_free;
+	vchan_init(&c->vc, &od->ddev);
+	INIT_LIST_HEAD(&c->node);
+
+	od->ddev.chancnt++;
+
+	return 0;
+}
+
+static void omap_dma_free(struct omap_dmadev *od)
+{
+	tasklet_kill(&od->task);
+	while (!list_empty(&od->ddev.channels)) {
+		struct omap_chan *c = list_first_entry(&od->ddev.channels,
+			struct omap_chan, vc.chan.device_node);
+
+		list_del(&c->vc.chan.device_node);
+		tasklet_kill(&c->vc.task);
+		kfree(c);
+	}
+	kfree(od);
+}
+
+static int omap_dma_probe(struct platform_device *pdev)
+{
+	struct omap_dmadev *od;
+	int rc, i;
+
+	od = kzalloc(sizeof(*od), GFP_KERNEL);
+	if (!od)
+		return -ENOMEM;
+
+	dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
+	od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources;
+	od->ddev.device_free_chan_resources = omap_dma_free_chan_resources;
+	od->ddev.device_tx_status = omap_dma_tx_status;
+	od->ddev.device_issue_pending = omap_dma_issue_pending;
+	od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg;
+	od->ddev.device_control = omap_dma_control;
+	od->ddev.dev = &pdev->dev;
+	INIT_LIST_HEAD(&od->ddev.channels);
+	INIT_LIST_HEAD(&od->pending);
+	spin_lock_init(&od->lock);
+
+	tasklet_init(&od->task, omap_dma_sched, (unsigned long)od);
+
+	for (i = 0; i < 127; i++) {
+		rc = omap_dma_chan_init(od, i);
+		if (rc) {
+			omap_dma_free(od);
+			return rc;
+		}
+	}
+
+	rc = dma_async_device_register(&od->ddev);
+	if (rc) {
+		pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
+			rc);
+		omap_dma_free(od);
+	} else {
+		platform_set_drvdata(pdev, od);
+	}
+
+	dev_info(&pdev->dev, "OMAP DMA engine driver\n");
+
+	return rc;
+}
+
+static int omap_dma_remove(struct platform_device *pdev)
+{
+	struct omap_dmadev *od = platform_get_drvdata(pdev);
+
+	dma_async_device_unregister(&od->ddev);
+	omap_dma_free(od);
+
+	return 0;
+}
+
+static struct platform_driver omap_dma_driver = {
+	.probe	= omap_dma_probe,
+	.remove	= omap_dma_remove,
+	.driver = {
+		.name = "omap-dma-engine",
+		.owner = THIS_MODULE,
+	},
+};
+
+bool omap_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+	if (chan->device->dev->driver == &omap_dma_driver.driver) {
+		struct omap_chan *c = to_omap_dma_chan(chan);
+		unsigned req = *(unsigned *)param;
+
+		return req == c->dma_sig;
+	}
+	return false;
+}
+EXPORT_SYMBOL_GPL(omap_dma_filter_fn);
+
+static struct platform_device *pdev;
+
+static const struct platform_device_info omap_dma_dev_info = {
+	.name = "omap-dma-engine",
+	.id = -1,
+	.dma_mask = DMA_BIT_MASK(32),
+};
+
+static int omap_dma_init(void)
+{
+	int rc = platform_driver_register(&omap_dma_driver);
+
+	if (rc == 0) {
+		pdev = platform_device_register_full(&omap_dma_dev_info);
+		if (IS_ERR(pdev)) {
+			platform_driver_unregister(&omap_dma_driver);
+			rc = PTR_ERR(pdev);
+		}
+	}
+	return rc;
+}
+subsys_initcall(omap_dma_init);
+
+static void __exit omap_dma_exit(void)
+{
+	platform_device_unregister(pdev);
+	platform_driver_unregister(&omap_dma_driver);
+}
+module_exit(omap_dma_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_LICENSE("GPL");


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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-20 22:09                                     ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-20 22:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 20, 2012 at 09:43:07AM -0700, Tony Lindgren wrote:
> * Russell King - ARM Linux <linux@arm.linux.org.uk> [120420 08:41]:
> > --- a/drivers/dma/omap-dma.c
> > +++ b/drivers/dma/omap-dma.c
> > @@ -83,11 +84,11 @@ static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
> >  	struct omap_sg *sg = d->sg + idx;
> >  
> >  	if (d->dir == DMA_DEV_TO_MEM)
> > -		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
> > -				sg->addr, 0, 0);
> > +		omap_set_dma_dest_params(c->dma_ch, d->periph_port,
> > +			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
> >  	else
> > -		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
> > -				sg->addr, 0, 0);
> > +		omap_set_dma_src_params(c->dma_ch, d->periph_port,
> > +			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
> >  
> >  	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
> >  		d->sync_mode, c->dma_sig, d->sync_type);
> 
> These are now wrong way around, should use OMAP_DMA_PORT_EMIFF here..

Right, sorry about that.

Okay, I'll combine all these patches into the original adding the
OMAP DMA support tonight.  So, the new OMAP DMA driver now looks
like this:

 drivers/dma/Kconfig    |    6 +
 drivers/dma/Makefile   |    1 +
 drivers/dma/omap-dma.c |  521 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 528 insertions(+), 0 deletions(-)

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 5828ac4..792b486 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -261,6 +261,12 @@ config DMA_SA11X0
 	  SA-1110 SoCs.  This DMA engine can only be used with on-chip
 	  devices.
 
+config DMA_OMAP
+	tristate "OMAP DMA support"
+	depends on ARCH_OMAP
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+
 config DMA_ENGINE
 	bool
 
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index fc05f7d..ddc291a 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_PCH_DMA) += pch_dma.o
 obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
 obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
 obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
+obj-$(CONFIG_DMA_OMAP) += omap-dma.o
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
new file mode 100644
index 0000000..04df47f
--- /dev/null
+++ b/drivers/dma/omap-dma.c
@@ -0,0 +1,521 @@
+/*
+ * OMAP DMAengine support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+#include <plat/dma.h>
+
+struct omap_dmadev {
+	struct dma_device ddev;
+	spinlock_t lock;
+	struct tasklet_struct task;
+	struct list_head pending;
+};
+
+struct omap_chan {
+	struct virt_dma_chan vc;
+	struct list_head node;
+
+	struct dma_slave_config	cfg;
+	unsigned dma_sig;
+
+	int dma_ch;
+	struct omap_desc *desc;
+	unsigned sgidx;
+};
+
+struct omap_sg {
+	dma_addr_t addr;
+	uint32_t en;		/* number of elements (24-bit) */
+	uint32_t fn;		/* number of frames (16-bit) */
+};
+
+struct omap_desc {
+	struct virt_dma_desc vd;
+	enum dma_transfer_direction dir;
+	dma_addr_t dev_addr;
+
+	uint8_t es;		/* element size */
+	uint8_t sync_mode;	/* OMAP_DMA_SYNC_xxx */
+	uint8_t sync_type;	/* OMAP_DMA_xxx_SYNC* */
+	uint8_t periph_port;	/* Peripheral port */
+
+	unsigned sglen;
+	struct omap_sg sg[0];
+};
+
+static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d)
+{
+	return container_of(d, struct omap_dmadev, ddev);
+}
+
+static inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c)
+{
+	return container_of(c, struct omap_chan, vc.chan);
+}
+
+static inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t)
+{
+	return container_of(t, struct omap_desc, vd.tx);
+}
+
+static void omap_dma_desc_free(struct virt_dma_desc *vd)
+{
+	kfree(container_of(vd, struct omap_desc, vd));
+}
+
+static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
+	unsigned idx)
+{
+	struct omap_sg *sg = d->sg + idx;
+
+	if (d->dir == DMA_DEV_TO_MEM)
+		omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
+	else
+		omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
+
+	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
+		d->sync_mode, c->dma_sig, d->sync_type);
+
+	omap_start_dma(c->dma_ch);
+}
+
+static void omap_dma_start_desc(struct omap_chan *c)
+{
+	struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
+	struct omap_desc *d;
+
+	if (!vd) {
+		c->desc = NULL;
+		return;
+	}
+
+	list_del(&vd->node);
+
+	c->desc = d = to_omap_dma_desc(&vd->tx);
+	c->sgidx = 0;
+
+	if (d->dir == DMA_DEV_TO_MEM)
+		omap_set_dma_src_params(c->dma_ch, d->periph_port,
+			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
+	else
+		omap_set_dma_dest_params(c->dma_ch, d->periph_port,
+			OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, 0);
+
+	omap_dma_start_sg(c, d, 0);
+}
+
+static void omap_dma_callback(int ch, u16 status, void *data)
+{
+	struct omap_chan *c = data;
+	struct omap_desc *d;
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	d = c->desc;
+	if (!d)
+		return;
+
+	if (++c->sgidx < d->sglen) {
+		omap_dma_start_sg(c, d, c->sgidx);
+	} else {
+		omap_dma_start_desc(c);
+		vchan_cookie_complete(&d->vd);
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+/*
+ * This callback schedules all pending channels.  We could be more
+ * clever here by postponing allocation of the real DMA channels to
+ * this point, and freeing them when our virtual channel becomes idle.
+ *
+ * We would then need to deal with 'all channels in-use'
+ */
+static void omap_dma_sched(unsigned long data)
+{
+	struct omap_dmadev *d = (struct omap_dmadev *)data;
+	LIST_HEAD(head);
+
+	spin_lock_irq(&d->lock);
+	list_splice_tail_init(&d->pending, &head);
+	spin_unlock_irq(&d->lock);
+
+	while (!list_empty(&head)) {
+		struct omap_chan *c = list_first_entry(&head,
+			struct omap_chan, node);
+
+		spin_lock_irq(&c->vc.lock);
+		list_del_init(&c->node);
+		omap_dma_start_desc(c);
+		spin_unlock_irq(&c->vc.lock);
+	}
+}
+
+static int omap_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+
+	dev_info(c->vc.chan.device->dev, "allocating channel for %u\n", c->dma_sig);
+
+	return omap_request_dma(c->dma_sig, "DMA engine",
+		omap_dma_callback, c, &c->dma_ch);
+}
+
+static void omap_dma_free_chan_resources(struct dma_chan *chan)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+
+	vchan_free_chan_resources(&c->vc);
+	omap_free_dma(c->dma_ch);
+
+	dev_info(c->vc.chan.device->dev, "freeing channel for %u\n", c->dma_sig);
+}
+
+static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
+	dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+	/*
+	 * FIXME: do we need to return pending bytes?
+	 * We have no users of that info at the moment...
+	 */
+	return dma_cookie_status(chan, cookie, txstate);
+}
+
+static void omap_dma_issue_pending(struct dma_chan *chan)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+	unsigned long flags;
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+	if (vchan_issue_pending(&c->vc) && !c->desc) {
+		struct omap_dmadev *d = to_omap_dma_dev(chan->device);
+		spin_lock(&d->lock);
+		if (list_empty(&c->node))
+			list_add_tail(&c->node, &d->pending);
+		spin_unlock(&d->lock);
+		tasklet_schedule(&d->task);
+	}
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
+	struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen,
+	enum dma_transfer_direction dir, unsigned long tx_flags, void *context)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+	enum dma_slave_buswidth dev_width;
+	struct scatterlist *sgent;
+	struct omap_desc *d;
+	dma_addr_t dev_addr;
+	unsigned i, j = 0, es, es_bytes, en, frame_bytes, sync_type;
+	u32 burst;
+
+	if (dir == DMA_DEV_TO_MEM) {
+		dev_addr = c->cfg.src_addr;
+		dev_width = c->cfg.src_addr_width;
+		burst = c->cfg.src_maxburst;
+		sync_type = OMAP_DMA_SRC_SYNC;
+	} else if (dir == DMA_MEM_TO_DEV) {
+		dev_addr = c->cfg.dst_addr;
+		dev_width = c->cfg.dst_addr_width;
+		burst = c->cfg.dst_maxburst;
+		sync_type = OMAP_DMA_DST_SYNC;
+	} else {
+		dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
+		return NULL;
+	}
+
+	/* Bus width translates to the element size (ES) */
+	switch (dev_width) {
+	case DMA_SLAVE_BUSWIDTH_1_BYTE:
+		es = OMAP_DMA_DATA_TYPE_S8;
+		es_bytes = 1;
+		break;
+	case DMA_SLAVE_BUSWIDTH_2_BYTES:
+		es = OMAP_DMA_DATA_TYPE_S16;
+		es_bytes = 2;
+		break;
+	case DMA_SLAVE_BUSWIDTH_4_BYTES:
+		es = OMAP_DMA_DATA_TYPE_S32;
+		es_bytes = 4;
+		break;
+	default: /* not reached */
+		return NULL;
+	}
+
+	/* Now allocate and setup the descriptor. */
+	d = kzalloc(sizeof(*d) + sglen * sizeof(d->sg[0]), GFP_ATOMIC);
+	if (!d)
+		return NULL;
+
+	d->dir = dir;
+	d->dev_addr = dev_addr;
+	d->es = es;
+	d->sync_mode = OMAP_DMA_SYNC_FRAME;
+	d->sync_type = sync_type;
+	d->periph_port = OMAP_DMA_PORT_TIPB;
+
+	/*
+	 * Build our scatterlist entries: each contains the address,
+	 * the number of elements (EN) in each frame, and the number of
+	 * frames (FN).  Number of bytes for this entry = ES * EN * FN.
+	 *
+	 * Burst size translates to number of elements with frame sync.
+	 * Note: DMA engine defines burst to be the number of dev-width
+	 * transfers.
+	 */
+	en = burst;
+	frame_bytes = es_bytes * en;
+	for_each_sg(sgl, sgent, sglen, i) {
+		d->sg[j].addr = sg_dma_address(sgent);
+		d->sg[j].en = en;
+		d->sg[j].fn = sg_dma_len(sgent) / frame_bytes;
+		j++;
+	}
+
+	d->sglen = j;
+
+	return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
+}
+
+static int omap_dma_slave_config(struct omap_chan *c, struct dma_slave_config *cfg)
+{
+	if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
+	    cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
+		return -EINVAL;
+
+	memcpy(&c->cfg, cfg, sizeof(c->cfg));
+
+	return 0;
+}
+
+static int omap_dma_terminate_all(struct omap_chan *c)
+{
+	struct omap_dmadev *d = to_omap_dma_dev(c->vc.chan.device);
+	unsigned long flags;
+	LIST_HEAD(head);
+
+	spin_lock_irqsave(&c->vc.lock, flags);
+
+	/* Prevent this channel being scheduled */
+	spin_lock(&d->lock);
+	list_del_init(&c->node);
+	spin_unlock(&d->lock);
+
+	/*
+	 * Stop DMA activity: we assume the callback will not be called
+	 * after omap_stop_dma() returns (even if it does, it will see
+	 * c->desc is NULL and exit.)
+	 */
+	if (c->desc) {
+		c->desc = NULL;
+		omap_stop_dma(c->dma_ch);
+	}
+
+	vchan_get_all_descriptors(&c->vc, &head);
+	spin_unlock_irqrestore(&c->vc.lock, flags);
+	vchan_dma_desc_free_list(&c->vc, &head);
+
+	return 0;
+}
+
+static int omap_dma_pause(struct omap_chan *c)
+{
+	/* FIXME: not supported by platform private API */
+	return -EINVAL;
+}
+
+static int omap_dma_resume(struct omap_chan *c)
+{
+	/* FIXME: not supported by platform private API */
+	return -EINVAL;
+}
+
+static int omap_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+	unsigned long arg)
+{
+	struct omap_chan *c = to_omap_dma_chan(chan);
+	int ret;
+
+	switch (cmd) {
+	case DMA_SLAVE_CONFIG:
+		ret = omap_dma_slave_config(c, (struct dma_slave_config *)arg);
+		break;
+
+	case DMA_TERMINATE_ALL:
+		ret = omap_dma_terminate_all(c);
+		break;
+
+	case DMA_PAUSE:
+		ret = omap_dma_pause(c);
+		break;
+
+	case DMA_RESUME:
+		ret = omap_dma_resume(c);
+		break;
+
+	default:
+		ret = -ENXIO;
+		break;
+	}
+
+	return ret;
+}
+
+static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig)
+{
+	struct omap_chan *c;
+
+	c = kzalloc(sizeof(*c), GFP_KERNEL);
+	if (!c)
+		return -ENOMEM;
+
+	c->dma_sig = dma_sig;
+	c->vc.desc_free = omap_dma_desc_free;
+	vchan_init(&c->vc, &od->ddev);
+	INIT_LIST_HEAD(&c->node);
+
+	od->ddev.chancnt++;
+
+	return 0;
+}
+
+static void omap_dma_free(struct omap_dmadev *od)
+{
+	tasklet_kill(&od->task);
+	while (!list_empty(&od->ddev.channels)) {
+		struct omap_chan *c = list_first_entry(&od->ddev.channels,
+			struct omap_chan, vc.chan.device_node);
+
+		list_del(&c->vc.chan.device_node);
+		tasklet_kill(&c->vc.task);
+		kfree(c);
+	}
+	kfree(od);
+}
+
+static int omap_dma_probe(struct platform_device *pdev)
+{
+	struct omap_dmadev *od;
+	int rc, i;
+
+	od = kzalloc(sizeof(*od), GFP_KERNEL);
+	if (!od)
+		return -ENOMEM;
+
+	dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
+	od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources;
+	od->ddev.device_free_chan_resources = omap_dma_free_chan_resources;
+	od->ddev.device_tx_status = omap_dma_tx_status;
+	od->ddev.device_issue_pending = omap_dma_issue_pending;
+	od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg;
+	od->ddev.device_control = omap_dma_control;
+	od->ddev.dev = &pdev->dev;
+	INIT_LIST_HEAD(&od->ddev.channels);
+	INIT_LIST_HEAD(&od->pending);
+	spin_lock_init(&od->lock);
+
+	tasklet_init(&od->task, omap_dma_sched, (unsigned long)od);
+
+	for (i = 0; i < 127; i++) {
+		rc = omap_dma_chan_init(od, i);
+		if (rc) {
+			omap_dma_free(od);
+			return rc;
+		}
+	}
+
+	rc = dma_async_device_register(&od->ddev);
+	if (rc) {
+		pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
+			rc);
+		omap_dma_free(od);
+	} else {
+		platform_set_drvdata(pdev, od);
+	}
+
+	dev_info(&pdev->dev, "OMAP DMA engine driver\n");
+
+	return rc;
+}
+
+static int omap_dma_remove(struct platform_device *pdev)
+{
+	struct omap_dmadev *od = platform_get_drvdata(pdev);
+
+	dma_async_device_unregister(&od->ddev);
+	omap_dma_free(od);
+
+	return 0;
+}
+
+static struct platform_driver omap_dma_driver = {
+	.probe	= omap_dma_probe,
+	.remove	= omap_dma_remove,
+	.driver = {
+		.name = "omap-dma-engine",
+		.owner = THIS_MODULE,
+	},
+};
+
+bool omap_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+	if (chan->device->dev->driver == &omap_dma_driver.driver) {
+		struct omap_chan *c = to_omap_dma_chan(chan);
+		unsigned req = *(unsigned *)param;
+
+		return req == c->dma_sig;
+	}
+	return false;
+}
+EXPORT_SYMBOL_GPL(omap_dma_filter_fn);
+
+static struct platform_device *pdev;
+
+static const struct platform_device_info omap_dma_dev_info = {
+	.name = "omap-dma-engine",
+	.id = -1,
+	.dma_mask = DMA_BIT_MASK(32),
+};
+
+static int omap_dma_init(void)
+{
+	int rc = platform_driver_register(&omap_dma_driver);
+
+	if (rc == 0) {
+		pdev = platform_device_register_full(&omap_dma_dev_info);
+		if (IS_ERR(pdev)) {
+			platform_driver_unregister(&omap_dma_driver);
+			rc = PTR_ERR(pdev);
+		}
+	}
+	return rc;
+}
+subsys_initcall(omap_dma_init);
+
+static void __exit omap_dma_exit(void)
+{
+	platform_device_unregister(pdev);
+	platform_driver_unregister(&omap_dma_driver);
+}
+module_exit(omap_dma_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_LICENSE("GPL");

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-20 22:09                                     ` Russell King - ARM Linux
@ 2012-04-20 22:21                                       ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-20 22:21 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-omap, linux-mmc, T Krishnamoorthy, Balaji, linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120420 15:13]:
> On Fri, Apr 20, 2012 at 09:43:07AM -0700, Tony Lindgren wrote:
> > * Russell King - ARM Linux <linux@arm.linux.org.uk> [120420 08:41]:
> > > --- a/drivers/dma/omap-dma.c
> > > +++ b/drivers/dma/omap-dma.c
> > > @@ -83,11 +84,11 @@ static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
> > >  	struct omap_sg *sg = d->sg + idx;
> > >  
> > >  	if (d->dir == DMA_DEV_TO_MEM)
> > > -		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
> > > -				sg->addr, 0, 0);
> > > +		omap_set_dma_dest_params(c->dma_ch, d->periph_port,
> > > +			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
> > >  	else
> > > -		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
> > > -				sg->addr, 0, 0);
> > > +		omap_set_dma_src_params(c->dma_ch, d->periph_port,
> > > +			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
> > >  
> > >  	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
> > >  		d->sync_mode, c->dma_sig, d->sync_type);
> > 
> > These are now wrong way around, should use OMAP_DMA_PORT_EMIFF here..
> 
> Right, sorry about that.
> 
> Okay, I'll combine all these patches into the original adding the
> OMAP DMA support tonight.  So, the new OMAP DMA driver now looks
> like this:

Thanks that works. Tried it on 700, n800, n900, 2430sdp, zoom3 and blaze:

Tested-by: Tony Lindgren <tony@atomide.com>

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-20 22:21                                       ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-20 22:21 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120420 15:13]:
> On Fri, Apr 20, 2012 at 09:43:07AM -0700, Tony Lindgren wrote:
> > * Russell King - ARM Linux <linux@arm.linux.org.uk> [120420 08:41]:
> > > --- a/drivers/dma/omap-dma.c
> > > +++ b/drivers/dma/omap-dma.c
> > > @@ -83,11 +84,11 @@ static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
> > >  	struct omap_sg *sg = d->sg + idx;
> > >  
> > >  	if (d->dir == DMA_DEV_TO_MEM)
> > > -		omap_set_dma_dest_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
> > > -				sg->addr, 0, 0);
> > > +		omap_set_dma_dest_params(c->dma_ch, d->periph_port,
> > > +			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
> > >  	else
> > > -		omap_set_dma_src_params(c->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
> > > -				sg->addr, 0, 0);
> > > +		omap_set_dma_src_params(c->dma_ch, d->periph_port,
> > > +			OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
> > >  
> > >  	omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
> > >  		d->sync_mode, c->dma_sig, d->sync_type);
> > 
> > These are now wrong way around, should use OMAP_DMA_PORT_EMIFF here..
> 
> Right, sorry about that.
> 
> Okay, I'll combine all these patches into the original adding the
> OMAP DMA support tonight.  So, the new OMAP DMA driver now looks
> like this:

Thanks that works. Tried it on 700, n800, n900, 2430sdp, zoom3 and blaze:

Tested-by: Tony Lindgren <tony@atomide.com>

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

* Re: [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
  2012-04-18 10:10   ` Russell King
@ 2012-04-20 22:22     ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-20 22:22 UTC (permalink / raw)
  To: Russell King; +Cc: linux-arm-kernel, linux-omap, linux-mmc

* Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:14]:
> Using coherent DMA memory with the OMAP DMA engine results in
> unpredictable behaviour due to memory ordering issues; as things stand,
> there is no guarantee that data written to coherent DMA memory will be
> visible to the DMA hardware.
> 
> This is because the OMAP dma_write() accessor contains no barriers,
> necessary on ARMv6 and above.  The effect of this can be seen in comments
> in the OMAP serial driver, which incorrectly talks about cache flushing
> for the coherent DMA stuff.
> 
> Rather than adding barriers to the accessors, add it in the DMA support
> code just before we enable DMA, and just after we disable DMA.  This
> avoids having barriers for every DMA register access.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Acked-by: Tony Lindgren <tony@atomide.com>

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

* [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
@ 2012-04-20 22:22     ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-20 22:22 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:14]:
> Using coherent DMA memory with the OMAP DMA engine results in
> unpredictable behaviour due to memory ordering issues; as things stand,
> there is no guarantee that data written to coherent DMA memory will be
> visible to the DMA hardware.
> 
> This is because the OMAP dma_write() accessor contains no barriers,
> necessary on ARMv6 and above.  The effect of this can be seen in comments
> in the OMAP serial driver, which incorrectly talks about cache flushing
> for the coherent DMA stuff.
> 
> Rather than adding barriers to the accessors, add it in the DMA support
> code just before we enable DMA, and just after we disable DMA.  This
> avoids having barriers for every DMA register access.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Acked-by: Tony Lindgren <tony@atomide.com>

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

* Re: [PATCH 5/8] mmc: omap_hsmmc: release correct resource
  2012-04-18 10:11   ` Russell King
@ 2012-04-20 22:23     ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-20 22:23 UTC (permalink / raw)
  To: Russell King; +Cc: linux-arm-kernel, linux-omap, linux-mmc, Chris Ball

* Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:15]:
> res can be one of several resources, as this variable is re-used several
> times during probe.  This can cause the wrong resource parameters to be
> passed to release_mem_region().
> 
> Get the original memory resource before calling release_mem_region().
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Acked-by: Tony Lindgren <tony@atomide.com>

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

* [PATCH 5/8] mmc: omap_hsmmc: release correct resource
@ 2012-04-20 22:23     ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-20 22:23 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:15]:
> res can be one of several resources, as this variable is re-used several
> times during probe.  This can cause the wrong resource parameters to be
> passed to release_mem_region().
> 
> Get the original memory resource before calling release_mem_region().
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Acked-by: Tony Lindgren <tony@atomide.com>

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

* Re: [PATCH 5/8] mmc: omap_hsmmc: release correct resource
  2012-04-20 22:23     ` Tony Lindgren
@ 2012-04-20 22:59       ` Chris Ball
  -1 siblings, 0 replies; 120+ messages in thread
From: Chris Ball @ 2012-04-20 22:59 UTC (permalink / raw)
  To: Tony Lindgren; +Cc: Russell King, linux-arm-kernel, linux-omap, linux-mmc

Hi,

On Fri, Apr 20 2012, Tony Lindgren wrote:
> * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:15]:
>> res can be one of several resources, as this variable is re-used several
>> times during probe.  This can cause the wrong resource parameters to be
>> passed to release_mem_region().
>> 
>> Get the original memory resource before calling release_mem_region().
>> 
>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>
> Acked-by: Tony Lindgren <tony@atomide.com>

Thanks, pushed to mmc-next for 3.4.

- Chris.
-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

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

* [PATCH 5/8] mmc: omap_hsmmc: release correct resource
@ 2012-04-20 22:59       ` Chris Ball
  0 siblings, 0 replies; 120+ messages in thread
From: Chris Ball @ 2012-04-20 22:59 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Fri, Apr 20 2012, Tony Lindgren wrote:
> * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:15]:
>> res can be one of several resources, as this variable is re-used several
>> times during probe.  This can cause the wrong resource parameters to be
>> passed to release_mem_region().
>> 
>> Get the original memory resource before calling release_mem_region().
>> 
>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>
> Acked-by: Tony Lindgren <tony@atomide.com>

Thanks, pushed to mmc-next for 3.4.

- Chris.
-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

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

* Re: [PATCH 5/8] mmc: omap_hsmmc: release correct resource
  2012-04-20 22:59       ` Chris Ball
@ 2012-04-21  2:35         ` Chris Ball
  -1 siblings, 0 replies; 120+ messages in thread
From: Chris Ball @ 2012-04-21  2:35 UTC (permalink / raw)
  To: Tony Lindgren; +Cc: Russell King, linux-arm-kernel, linux-omap, linux-mmc

Hi,

On Fri, Apr 20 2012, Chris Ball wrote:
> On Fri, Apr 20 2012, Tony Lindgren wrote:
>> * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:15]:
>>> res can be one of several resources, as this variable is re-used several
>>> times during probe.  This can cause the wrong resource parameters to be
>>> passed to release_mem_region().
>>> 
>>> Get the original memory resource before calling release_mem_region().
>>> 
>>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>>
>> Acked-by: Tony Lindgren <tony@atomide.com>
>
> Thanks, pushed to mmc-next for 3.4.

(Actually, that's premature of me, sorry -- just noticed that this
is part of an RFC patchset.  I'll wait for the rest of the patches.)

- Chris.
-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

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

* [PATCH 5/8] mmc: omap_hsmmc: release correct resource
@ 2012-04-21  2:35         ` Chris Ball
  0 siblings, 0 replies; 120+ messages in thread
From: Chris Ball @ 2012-04-21  2:35 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Fri, Apr 20 2012, Chris Ball wrote:
> On Fri, Apr 20 2012, Tony Lindgren wrote:
>> * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:15]:
>>> res can be one of several resources, as this variable is re-used several
>>> times during probe.  This can cause the wrong resource parameters to be
>>> passed to release_mem_region().
>>> 
>>> Get the original memory resource before calling release_mem_region().
>>> 
>>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>>
>> Acked-by: Tony Lindgren <tony@atomide.com>
>
> Thanks, pushed to mmc-next for 3.4.

(Actually, that's premature of me, sorry -- just noticed that this
is part of an RFC patchset.  I'll wait for the rest of the patches.)

- Chris.
-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

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

* Re: [PATCH 5/8] mmc: omap_hsmmc: release correct resource
  2012-04-21  2:35         ` Chris Ball
@ 2012-04-21  9:48           ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-21  9:48 UTC (permalink / raw)
  To: Chris Ball; +Cc: Tony Lindgren, linux-arm-kernel, linux-omap, linux-mmc

On Fri, Apr 20, 2012 at 10:35:59PM -0400, Chris Ball wrote:
> Hi,
> 
> On Fri, Apr 20 2012, Chris Ball wrote:
> > On Fri, Apr 20 2012, Tony Lindgren wrote:
> >> * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:15]:
> >>> res can be one of several resources, as this variable is re-used several
> >>> times during probe.  This can cause the wrong resource parameters to be
> >>> passed to release_mem_region().
> >>> 
> >>> Get the original memory resource before calling release_mem_region().
> >>> 
> >>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> >>
> >> Acked-by: Tony Lindgren <tony@atomide.com>
> >
> > Thanks, pushed to mmc-next for 3.4.
> 
> (Actually, that's premature of me, sorry -- just noticed that this
> is part of an RFC patchset.  I'll wait for the rest of the patches.)

It was previously posted as an entirely separate patch - the reason its
part of this RFC is because I needed the patch while developing this
series.  So please go ahead and take _this_ patch.

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

* [PATCH 5/8] mmc: omap_hsmmc: release correct resource
@ 2012-04-21  9:48           ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-21  9:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 20, 2012 at 10:35:59PM -0400, Chris Ball wrote:
> Hi,
> 
> On Fri, Apr 20 2012, Chris Ball wrote:
> > On Fri, Apr 20 2012, Tony Lindgren wrote:
> >> * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:15]:
> >>> res can be one of several resources, as this variable is re-used several
> >>> times during probe.  This can cause the wrong resource parameters to be
> >>> passed to release_mem_region().
> >>> 
> >>> Get the original memory resource before calling release_mem_region().
> >>> 
> >>> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> >>
> >> Acked-by: Tony Lindgren <tony@atomide.com>
> >
> > Thanks, pushed to mmc-next for 3.4.
> 
> (Actually, that's premature of me, sorry -- just noticed that this
> is part of an RFC patchset.  I'll wait for the rest of the patches.)

It was previously posted as an entirely separate patch - the reason its
part of this RFC is because I needed the patch while developing this
series.  So please go ahead and take _this_ patch.

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

* Re: [PATCH 5/8] mmc: omap_hsmmc: release correct resource
  2012-04-21  9:48           ` Russell King - ARM Linux
@ 2012-04-22 15:20             ` Chris Ball
  -1 siblings, 0 replies; 120+ messages in thread
From: Chris Ball @ 2012-04-22 15:20 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Tony Lindgren, linux-arm-kernel, linux-omap, linux-mmc

Hi,

On Sat, Apr 21 2012, Russell King - ARM Linux wrote:
>> > Thanks, pushed to mmc-next for 3.4.
>> 
>> (Actually, that's premature of me, sorry -- just noticed that this
>> is part of an RFC patchset.  I'll wait for the rest of the patches.)
>
> It was previously posted as an entirely separate patch - the reason its
> part of this RFC is because I needed the patch while developing this
> series.  So please go ahead and take _this_ patch.

Okay, thanks.  It's back in mmc-next.

- Chris.
-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

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

* [PATCH 5/8] mmc: omap_hsmmc: release correct resource
@ 2012-04-22 15:20             ` Chris Ball
  0 siblings, 0 replies; 120+ messages in thread
From: Chris Ball @ 2012-04-22 15:20 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Sat, Apr 21 2012, Russell King - ARM Linux wrote:
>> > Thanks, pushed to mmc-next for 3.4.
>> 
>> (Actually, that's premature of me, sorry -- just noticed that this
>> is part of an RFC patchset.  I'll wait for the rest of the patches.)
>
> It was previously posted as an entirely separate patch - the reason its
> part of this RFC is because I needed the patch while developing this
> series.  So please go ahead and take _this_ patch.

Okay, thanks.  It's back in mmc-next.

- Chris.
-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

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

* Re: [RFC 0/8] DMA engine conversion
  2012-04-18 10:09 ` Russell King - ARM Linux
@ 2012-04-23 11:46   ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-23 11:46 UTC (permalink / raw)
  To: linux-arm-kernel, linux-omap, linux-mmc

Here's a patch which converts the SPI driver to use dma engine support.
It's more or less a mechanical replacement in this driver.  Tested on
OMAP4430SDP ethernet (which basically means that udhcpc works.)

One thing to note: the old DMA code programmed the DMA hardware for an
element count of N and frame size of 1, with element sync.  The DMA
engine code will program the DMA hardware for an element count of 1 and
a frame size of N, with frame sync.  This appears to work - but at the
moment I've not looked to see if there's any implications of this.

You can enable/disable the DMA engine code for the TX or RX side by
tweaking the two definitions at the top of the file if you want to do
comparisons.

Comments?  If none, I'll see about committing this into the patch set,
adding the removal of the private DMA code, and sending it out more
"officially" with the next round of RFC for this set.

Modulo the element vs frame sync issue, I think folk can see that the
conversion process is fairly straight forward - it's more or less:
1. add struct dma_chan * pointer to the private structure.
2. add allocation/freeing functions for the DMA engine channels.
3. convert the callbacks to match the DMA engine callback prototype.
4. add code to set the DMA engine channel configuration appropriately
   (avoiding any unnecessary calls) via dmaengine_slave_config().
5. add code to queue the transfer via dmaengine_prep_slave_sg() and
   dmaengine_submit().
6. omap_start_dma() becomes dma_async_issue_pending().

 drivers/spi/spi-omap2-mcspi.c |  186 +++++++++++++++++++++++++++++++++-------
 1 files changed, 153 insertions(+), 33 deletions(-)

diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index bb9274c..a77af0b 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -20,6 +20,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  *
  */
+#define USE_DMA_ENGINE_RX
+#define USE_DMA_ENGINE_TX
 
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -28,6 +30,7 @@
 #include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
 #include <linux/platform_device.h>
 #include <linux/err.h>
 #include <linux/clk.h>
@@ -95,6 +98,8 @@
 
 /* We have 2 DMA channels per CS, one for RX and one for TX */
 struct omap2_mcspi_dma {
+	struct dma_chan *dma_tx;
+	struct dma_chan *dma_rx;
 	int dma_tx_channel;
 	int dma_rx_channel;
 
@@ -290,6 +295,30 @@ static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
 	return 0;
 }
 
+static void omap2_mcspi_rx_callback(void *data)
+{
+	struct spi_device *spi = data;
+	struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master);
+	struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+
+	complete(&mcspi_dma->dma_rx_completion);
+
+	/* We must disable the DMA RX request */
+	omap2_mcspi_set_dma_req(spi, 1, 0);
+}
+
+static void omap2_mcspi_tx_callback(void *data)
+{
+	struct spi_device *spi = data;
+	struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master);
+	struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+
+	complete(&mcspi_dma->dma_tx_completion);
+
+	/* We must disable the DMA TX request */
+	omap2_mcspi_set_dma_req(spi, 0, 0);
+}
+
 static unsigned
 omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 {
@@ -304,6 +333,9 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 	u8			* rx;
 	const u8		* tx;
 	void __iomem		*chstat_reg;
+	struct dma_slave_config	cfg;
+	enum dma_slave_buswidth width;
+	unsigned es;
 
 	mcspi = spi_master_get_devdata(spi->master);
 	mcspi_dma = &mcspi->dma_channels[spi->chip_select];
@@ -311,6 +343,71 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 
 	chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
 
+	if (cs->word_len <= 8) {
+		width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+		es = 1;
+	} else if (cs->word_len <= 16) {
+		width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+		es = 2;
+	} else {
+		width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		es = 4;
+	}
+
+	memset(&cfg, 0, sizeof(cfg));
+	cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0;
+	cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0;
+	cfg.src_addr_width = width;
+	cfg.dst_addr_width = width;
+	cfg.src_maxburst = 1;
+	cfg.dst_maxburst = 1;
+
+	if (xfer->tx_buf && mcspi_dma->dma_tx) {
+		struct dma_async_tx_descriptor *tx;
+		struct scatterlist sg;
+
+		dmaengine_slave_config(mcspi_dma->dma_tx, &cfg);
+
+		sg_init_table(&sg, 1);
+		sg_dma_address(&sg) = xfer->tx_dma;
+		sg_dma_len(&sg) = xfer->len;
+
+		tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1,
+			DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+		if (tx) {
+			tx->callback = omap2_mcspi_tx_callback;
+			tx->callback_param = spi;
+			dmaengine_submit(tx);
+		} else {
+			/* FIXME: fall back to PIO? */
+		}
+	}
+
+	if (xfer->rx_buf && mcspi_dma->dma_rx) {
+		struct dma_async_tx_descriptor *tx;
+		struct scatterlist sg;
+		size_t len = xfer->len - es;
+
+		dmaengine_slave_config(mcspi_dma->dma_rx, &cfg);
+
+		if (l & OMAP2_MCSPI_CHCONF_TURBO)
+			len -= es;
+
+		sg_init_table(&sg, 1);
+		sg_dma_address(&sg) = xfer->rx_dma;
+		sg_dma_len(&sg) = len;
+
+		tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1,
+			DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+		if (tx) {
+			tx->callback = omap2_mcspi_rx_callback;
+			tx->callback_param = spi;
+			dmaengine_submit(tx);
+		} else {
+			/* FIXME: fall back to PIO? */
+		}
+	}
+
 	count = xfer->len;
 	c = count;
 	word_len = cs->word_len;
@@ -332,7 +429,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 		element_count = count >> 2;
 	}
 
-	if (tx != NULL) {
+	if (tx != NULL && mcspi_dma->dma_tx_channel != -1) {
 		omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel,
 				data_type, element_count, 1,
 				OMAP_DMA_SYNC_ELEMENT,
@@ -347,7 +444,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 				xfer->tx_dma, 0, 0);
 	}
 
-	if (rx != NULL) {
+	if (rx != NULL && mcspi_dma->dma_rx_channel != -1) {
 		elements = element_count - 1;
 		if (l & OMAP2_MCSPI_CHCONF_TURBO)
 			elements--;
@@ -367,12 +464,18 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 	}
 
 	if (tx != NULL) {
-		omap_start_dma(mcspi_dma->dma_tx_channel);
+		if (mcspi_dma->dma_tx)
+			dma_async_issue_pending(mcspi_dma->dma_tx);
+		else
+			omap_start_dma(mcspi_dma->dma_tx_channel);
 		omap2_mcspi_set_dma_req(spi, 0, 1);
 	}
 
 	if (rx != NULL) {
-		omap_start_dma(mcspi_dma->dma_rx_channel);
+		if (mcspi_dma->dma_rx)
+			dma_async_issue_pending(mcspi_dma->dma_rx);
+		else
+			omap_start_dma(mcspi_dma->dma_rx_channel);
 		omap2_mcspi_set_dma_req(spi, 1, 1);
 	}
 
@@ -396,7 +499,10 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 		dma_unmap_single(&spi->dev, xfer->rx_dma, count, DMA_FROM_DEVICE);
 		omap2_mcspi_set_enable(spi, 0);
 
+		elements = element_count - 1;
+
 		if (l & OMAP2_MCSPI_CHCONF_TURBO) {
+			elements--;
 
 			if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
 				   & OMAP2_MCSPI_CHSTAT_RXS)) {
@@ -715,50 +821,58 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
 
 static void omap2_mcspi_dma_rx_callback(int lch, u16 ch_status, void *data)
 {
-	struct spi_device	*spi = data;
-	struct omap2_mcspi	*mcspi;
-	struct omap2_mcspi_dma	*mcspi_dma;
-
-	mcspi = spi_master_get_devdata(spi->master);
-	mcspi_dma = &(mcspi->dma_channels[spi->chip_select]);
-
-	complete(&mcspi_dma->dma_rx_completion);
-
-	/* We must disable the DMA RX request */
-	omap2_mcspi_set_dma_req(spi, 1, 0);
+	omap2_mcspi_rx_callback(data);
 }
 
 static void omap2_mcspi_dma_tx_callback(int lch, u16 ch_status, void *data)
 {
-	struct spi_device	*spi = data;
-	struct omap2_mcspi	*mcspi;
-	struct omap2_mcspi_dma	*mcspi_dma;
-
-	mcspi = spi_master_get_devdata(spi->master);
-	mcspi_dma = &(mcspi->dma_channels[spi->chip_select]);
-
-	complete(&mcspi_dma->dma_tx_completion);
-
-	/* We must disable the DMA TX request */
-	omap2_mcspi_set_dma_req(spi, 0, 0);
+	omap2_mcspi_tx_callback(data);
 }
 
+extern bool omap_dma_filter_fn(struct dma_chan *chan, void *param);
+
 static int omap2_mcspi_request_dma(struct spi_device *spi)
 {
 	struct spi_master	*master = spi->master;
 	struct omap2_mcspi	*mcspi;
 	struct omap2_mcspi_dma	*mcspi_dma;
+	dma_cap_mask_t mask;
+	unsigned sig;
 
 	mcspi = spi_master_get_devdata(master);
 	mcspi_dma = mcspi->dma_channels + spi->chip_select;
 
+	init_completion(&mcspi_dma->dma_rx_completion);
+	init_completion(&mcspi_dma->dma_tx_completion);
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+#ifdef USE_DMA_ENGINE_RX
+	sig = mcspi_dma->dma_rx_sync_dev;
+	mcspi_dma->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+	if (!mcspi_dma->dma_rx) {
+		dev_err(&spi->dev, "no RX DMA engine channel for McSPI\n");
+		return -EAGAIN;
+	}
+#else
 	if (omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX",
 			omap2_mcspi_dma_rx_callback, spi,
 			&mcspi_dma->dma_rx_channel)) {
 		dev_err(&spi->dev, "no RX DMA channel for McSPI\n");
 		return -EAGAIN;
 	}
+#endif
 
+#ifdef USE_DMA_ENGINE_TX
+	sig = mcspi_dma->dma_tx_sync_dev;
+	mcspi_dma->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+	if (!mcspi_dma->dma_tx) {
+		dev_err(&spi->dev, "no TX DMA engine channel for McSPI\n");
+		dma_release_channel(mcspi_dma->dma_rx);
+		mcspi_dma->dma_rx = NULL;
+		return -EAGAIN;
+	}
+#else
 	if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX",
 			omap2_mcspi_dma_tx_callback, spi,
 			&mcspi_dma->dma_tx_channel)) {
@@ -767,9 +881,7 @@ static int omap2_mcspi_request_dma(struct spi_device *spi)
 		dev_err(&spi->dev, "no TX DMA channel for McSPI\n");
 		return -EAGAIN;
 	}
-
-	init_completion(&mcspi_dma->dma_rx_completion);
-	init_completion(&mcspi_dma->dma_tx_completion);
+#endif
 
 	return 0;
 }
@@ -803,8 +915,8 @@ static int omap2_mcspi_setup(struct spi_device *spi)
 			&omap2_mcspi_ctx[mcspi->master->bus_num - 1].cs);
 	}
 
-	if (mcspi_dma->dma_rx_channel == -1
-			|| mcspi_dma->dma_tx_channel == -1) {
+	if ((!mcspi_dma->dma_rx && mcspi_dma->dma_rx_channel == -1) ||
+	    (!mcspi_dma->dma_tx && mcspi_dma->dma_tx_channel == -1)) {
 		ret = omap2_mcspi_request_dma(spi);
 		if (ret < 0)
 			return ret;
@@ -839,6 +951,14 @@ static void omap2_mcspi_cleanup(struct spi_device *spi)
 	if (spi->chip_select < spi->master->num_chipselect) {
 		mcspi_dma = &mcspi->dma_channels[spi->chip_select];
 
+		if (mcspi_dma->dma_rx) {
+			dma_release_channel(mcspi_dma->dma_rx);
+			mcspi_dma->dma_rx = NULL;
+		}
+		if (mcspi_dma->dma_tx) {
+			dma_release_channel(mcspi_dma->dma_tx);
+			mcspi_dma->dma_tx = NULL;
+		}
 		if (mcspi_dma->dma_rx_channel != -1) {
 			omap_free_dma(mcspi_dma->dma_rx_channel);
 			mcspi_dma->dma_rx_channel = -1;

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

* [RFC 0/8] DMA engine conversion
@ 2012-04-23 11:46   ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-23 11:46 UTC (permalink / raw)
  To: linux-arm-kernel

Here's a patch which converts the SPI driver to use dma engine support.
It's more or less a mechanical replacement in this driver.  Tested on
OMAP4430SDP ethernet (which basically means that udhcpc works.)

One thing to note: the old DMA code programmed the DMA hardware for an
element count of N and frame size of 1, with element sync.  The DMA
engine code will program the DMA hardware for an element count of 1 and
a frame size of N, with frame sync.  This appears to work - but at the
moment I've not looked to see if there's any implications of this.

You can enable/disable the DMA engine code for the TX or RX side by
tweaking the two definitions at the top of the file if you want to do
comparisons.

Comments?  If none, I'll see about committing this into the patch set,
adding the removal of the private DMA code, and sending it out more
"officially" with the next round of RFC for this set.

Modulo the element vs frame sync issue, I think folk can see that the
conversion process is fairly straight forward - it's more or less:
1. add struct dma_chan * pointer to the private structure.
2. add allocation/freeing functions for the DMA engine channels.
3. convert the callbacks to match the DMA engine callback prototype.
4. add code to set the DMA engine channel configuration appropriately
   (avoiding any unnecessary calls) via dmaengine_slave_config().
5. add code to queue the transfer via dmaengine_prep_slave_sg() and
   dmaengine_submit().
6. omap_start_dma() becomes dma_async_issue_pending().

 drivers/spi/spi-omap2-mcspi.c |  186 +++++++++++++++++++++++++++++++++-------
 1 files changed, 153 insertions(+), 33 deletions(-)

diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index bb9274c..a77af0b 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -20,6 +20,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  *
  */
+#define USE_DMA_ENGINE_RX
+#define USE_DMA_ENGINE_TX
 
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -28,6 +30,7 @@
 #include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
 #include <linux/platform_device.h>
 #include <linux/err.h>
 #include <linux/clk.h>
@@ -95,6 +98,8 @@
 
 /* We have 2 DMA channels per CS, one for RX and one for TX */
 struct omap2_mcspi_dma {
+	struct dma_chan *dma_tx;
+	struct dma_chan *dma_rx;
 	int dma_tx_channel;
 	int dma_rx_channel;
 
@@ -290,6 +295,30 @@ static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
 	return 0;
 }
 
+static void omap2_mcspi_rx_callback(void *data)
+{
+	struct spi_device *spi = data;
+	struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master);
+	struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+
+	complete(&mcspi_dma->dma_rx_completion);
+
+	/* We must disable the DMA RX request */
+	omap2_mcspi_set_dma_req(spi, 1, 0);
+}
+
+static void omap2_mcspi_tx_callback(void *data)
+{
+	struct spi_device *spi = data;
+	struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master);
+	struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+
+	complete(&mcspi_dma->dma_tx_completion);
+
+	/* We must disable the DMA TX request */
+	omap2_mcspi_set_dma_req(spi, 0, 0);
+}
+
 static unsigned
 omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 {
@@ -304,6 +333,9 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 	u8			* rx;
 	const u8		* tx;
 	void __iomem		*chstat_reg;
+	struct dma_slave_config	cfg;
+	enum dma_slave_buswidth width;
+	unsigned es;
 
 	mcspi = spi_master_get_devdata(spi->master);
 	mcspi_dma = &mcspi->dma_channels[spi->chip_select];
@@ -311,6 +343,71 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 
 	chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
 
+	if (cs->word_len <= 8) {
+		width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+		es = 1;
+	} else if (cs->word_len <= 16) {
+		width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+		es = 2;
+	} else {
+		width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		es = 4;
+	}
+
+	memset(&cfg, 0, sizeof(cfg));
+	cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0;
+	cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0;
+	cfg.src_addr_width = width;
+	cfg.dst_addr_width = width;
+	cfg.src_maxburst = 1;
+	cfg.dst_maxburst = 1;
+
+	if (xfer->tx_buf && mcspi_dma->dma_tx) {
+		struct dma_async_tx_descriptor *tx;
+		struct scatterlist sg;
+
+		dmaengine_slave_config(mcspi_dma->dma_tx, &cfg);
+
+		sg_init_table(&sg, 1);
+		sg_dma_address(&sg) = xfer->tx_dma;
+		sg_dma_len(&sg) = xfer->len;
+
+		tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1,
+			DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+		if (tx) {
+			tx->callback = omap2_mcspi_tx_callback;
+			tx->callback_param = spi;
+			dmaengine_submit(tx);
+		} else {
+			/* FIXME: fall back to PIO? */
+		}
+	}
+
+	if (xfer->rx_buf && mcspi_dma->dma_rx) {
+		struct dma_async_tx_descriptor *tx;
+		struct scatterlist sg;
+		size_t len = xfer->len - es;
+
+		dmaengine_slave_config(mcspi_dma->dma_rx, &cfg);
+
+		if (l & OMAP2_MCSPI_CHCONF_TURBO)
+			len -= es;
+
+		sg_init_table(&sg, 1);
+		sg_dma_address(&sg) = xfer->rx_dma;
+		sg_dma_len(&sg) = len;
+
+		tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1,
+			DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+		if (tx) {
+			tx->callback = omap2_mcspi_rx_callback;
+			tx->callback_param = spi;
+			dmaengine_submit(tx);
+		} else {
+			/* FIXME: fall back to PIO? */
+		}
+	}
+
 	count = xfer->len;
 	c = count;
 	word_len = cs->word_len;
@@ -332,7 +429,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 		element_count = count >> 2;
 	}
 
-	if (tx != NULL) {
+	if (tx != NULL && mcspi_dma->dma_tx_channel != -1) {
 		omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel,
 				data_type, element_count, 1,
 				OMAP_DMA_SYNC_ELEMENT,
@@ -347,7 +444,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 				xfer->tx_dma, 0, 0);
 	}
 
-	if (rx != NULL) {
+	if (rx != NULL && mcspi_dma->dma_rx_channel != -1) {
 		elements = element_count - 1;
 		if (l & OMAP2_MCSPI_CHCONF_TURBO)
 			elements--;
@@ -367,12 +464,18 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 	}
 
 	if (tx != NULL) {
-		omap_start_dma(mcspi_dma->dma_tx_channel);
+		if (mcspi_dma->dma_tx)
+			dma_async_issue_pending(mcspi_dma->dma_tx);
+		else
+			omap_start_dma(mcspi_dma->dma_tx_channel);
 		omap2_mcspi_set_dma_req(spi, 0, 1);
 	}
 
 	if (rx != NULL) {
-		omap_start_dma(mcspi_dma->dma_rx_channel);
+		if (mcspi_dma->dma_rx)
+			dma_async_issue_pending(mcspi_dma->dma_rx);
+		else
+			omap_start_dma(mcspi_dma->dma_rx_channel);
 		omap2_mcspi_set_dma_req(spi, 1, 1);
 	}
 
@@ -396,7 +499,10 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 		dma_unmap_single(&spi->dev, xfer->rx_dma, count, DMA_FROM_DEVICE);
 		omap2_mcspi_set_enable(spi, 0);
 
+		elements = element_count - 1;
+
 		if (l & OMAP2_MCSPI_CHCONF_TURBO) {
+			elements--;
 
 			if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
 				   & OMAP2_MCSPI_CHSTAT_RXS)) {
@@ -715,50 +821,58 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
 
 static void omap2_mcspi_dma_rx_callback(int lch, u16 ch_status, void *data)
 {
-	struct spi_device	*spi = data;
-	struct omap2_mcspi	*mcspi;
-	struct omap2_mcspi_dma	*mcspi_dma;
-
-	mcspi = spi_master_get_devdata(spi->master);
-	mcspi_dma = &(mcspi->dma_channels[spi->chip_select]);
-
-	complete(&mcspi_dma->dma_rx_completion);
-
-	/* We must disable the DMA RX request */
-	omap2_mcspi_set_dma_req(spi, 1, 0);
+	omap2_mcspi_rx_callback(data);
 }
 
 static void omap2_mcspi_dma_tx_callback(int lch, u16 ch_status, void *data)
 {
-	struct spi_device	*spi = data;
-	struct omap2_mcspi	*mcspi;
-	struct omap2_mcspi_dma	*mcspi_dma;
-
-	mcspi = spi_master_get_devdata(spi->master);
-	mcspi_dma = &(mcspi->dma_channels[spi->chip_select]);
-
-	complete(&mcspi_dma->dma_tx_completion);
-
-	/* We must disable the DMA TX request */
-	omap2_mcspi_set_dma_req(spi, 0, 0);
+	omap2_mcspi_tx_callback(data);
 }
 
+extern bool omap_dma_filter_fn(struct dma_chan *chan, void *param);
+
 static int omap2_mcspi_request_dma(struct spi_device *spi)
 {
 	struct spi_master	*master = spi->master;
 	struct omap2_mcspi	*mcspi;
 	struct omap2_mcspi_dma	*mcspi_dma;
+	dma_cap_mask_t mask;
+	unsigned sig;
 
 	mcspi = spi_master_get_devdata(master);
 	mcspi_dma = mcspi->dma_channels + spi->chip_select;
 
+	init_completion(&mcspi_dma->dma_rx_completion);
+	init_completion(&mcspi_dma->dma_tx_completion);
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+#ifdef USE_DMA_ENGINE_RX
+	sig = mcspi_dma->dma_rx_sync_dev;
+	mcspi_dma->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+	if (!mcspi_dma->dma_rx) {
+		dev_err(&spi->dev, "no RX DMA engine channel for McSPI\n");
+		return -EAGAIN;
+	}
+#else
 	if (omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX",
 			omap2_mcspi_dma_rx_callback, spi,
 			&mcspi_dma->dma_rx_channel)) {
 		dev_err(&spi->dev, "no RX DMA channel for McSPI\n");
 		return -EAGAIN;
 	}
+#endif
 
+#ifdef USE_DMA_ENGINE_TX
+	sig = mcspi_dma->dma_tx_sync_dev;
+	mcspi_dma->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+	if (!mcspi_dma->dma_tx) {
+		dev_err(&spi->dev, "no TX DMA engine channel for McSPI\n");
+		dma_release_channel(mcspi_dma->dma_rx);
+		mcspi_dma->dma_rx = NULL;
+		return -EAGAIN;
+	}
+#else
 	if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX",
 			omap2_mcspi_dma_tx_callback, spi,
 			&mcspi_dma->dma_tx_channel)) {
@@ -767,9 +881,7 @@ static int omap2_mcspi_request_dma(struct spi_device *spi)
 		dev_err(&spi->dev, "no TX DMA channel for McSPI\n");
 		return -EAGAIN;
 	}
-
-	init_completion(&mcspi_dma->dma_rx_completion);
-	init_completion(&mcspi_dma->dma_tx_completion);
+#endif
 
 	return 0;
 }
@@ -803,8 +915,8 @@ static int omap2_mcspi_setup(struct spi_device *spi)
 			&omap2_mcspi_ctx[mcspi->master->bus_num - 1].cs);
 	}
 
-	if (mcspi_dma->dma_rx_channel == -1
-			|| mcspi_dma->dma_tx_channel == -1) {
+	if ((!mcspi_dma->dma_rx && mcspi_dma->dma_rx_channel == -1) ||
+	    (!mcspi_dma->dma_tx && mcspi_dma->dma_tx_channel == -1)) {
 		ret = omap2_mcspi_request_dma(spi);
 		if (ret < 0)
 			return ret;
@@ -839,6 +951,14 @@ static void omap2_mcspi_cleanup(struct spi_device *spi)
 	if (spi->chip_select < spi->master->num_chipselect) {
 		mcspi_dma = &mcspi->dma_channels[spi->chip_select];
 
+		if (mcspi_dma->dma_rx) {
+			dma_release_channel(mcspi_dma->dma_rx);
+			mcspi_dma->dma_rx = NULL;
+		}
+		if (mcspi_dma->dma_tx) {
+			dma_release_channel(mcspi_dma->dma_tx);
+			mcspi_dma->dma_tx = NULL;
+		}
 		if (mcspi_dma->dma_rx_channel != -1) {
 			omap_free_dma(mcspi_dma->dma_rx_channel);
 			mcspi_dma->dma_rx_channel = -1;

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

* Re: [RFC 0/8] DMA engine conversion
  2012-04-23 11:46   ` Russell King - ARM Linux
@ 2012-04-23 12:32     ` Shilimkar, Santosh
  -1 siblings, 0 replies; 120+ messages in thread
From: Shilimkar, Santosh @ 2012-04-23 12:32 UTC (permalink / raw)
  To: Russell King - ARM Linux; +Cc: linux-arm-kernel, linux-omap, linux-mmc

On Mon, Apr 23, 2012 at 5:16 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> Here's a patch which converts the SPI driver to use dma engine support.
> It's more or less a mechanical replacement in this driver.  Tested on
> OMAP4430SDP ethernet (which basically means that udhcpc works.)
>
> One thing to note: the old DMA code programmed the DMA hardware for an
> element count of N and frame size of 1, with element sync.  The DMA
> engine code will program the DMA hardware for an element count of 1 and
> a frame size of N, with frame sync.  This appears to work - but at the
> moment I've not looked to see if there's any implications of this.
>
> You can enable/disable the DMA engine code for the TX or RX side by
> tweaking the two definitions at the top of the file if you want to do
> comparisons.
>
> Comments?  If none, I'll see about committing this into the patch set,
> adding the removal of the private DMA code, and sending it out more
> "officially" with the next round of RFC for this set.
>
Element sync vs Frame sync change seems to be fine thought it's
bit puzzling that why it was not that way first place.

> Modulo the element vs frame sync issue, I think folk can see that the
> conversion process is fairly straight forward - it's more or less:
> 1. add struct dma_chan * pointer to the private structure.
> 2. add allocation/freeing functions for the DMA engine channels.
> 3. convert the callbacks to match the DMA engine callback prototype.
> 4. add code to set the DMA engine channel configuration appropriately
>   (avoiding any unnecessary calls) via dmaengine_slave_config().
> 5. add code to queue the transfer via dmaengine_prep_slave_sg() and
>   dmaengine_submit().
> 6. omap_start_dma() becomes dma_async_issue_pending().
>
>  drivers/spi/spi-omap2-mcspi.c |  186 +++++++++++++++++++++++++++++++++-------
>  1 files changed, 153 insertions(+), 33 deletions(-)

The patch seems to be fine to me.

Am looping Shubro in case he has any comment on "why element sync was used ?"
and also to test the patch with more peripherals connected over SPI.

Regards
Santosh
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [RFC 0/8] DMA engine conversion
@ 2012-04-23 12:32     ` Shilimkar, Santosh
  0 siblings, 0 replies; 120+ messages in thread
From: Shilimkar, Santosh @ 2012-04-23 12:32 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Apr 23, 2012 at 5:16 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> Here's a patch which converts the SPI driver to use dma engine support.
> It's more or less a mechanical replacement in this driver. ?Tested on
> OMAP4430SDP ethernet (which basically means that udhcpc works.)
>
> One thing to note: the old DMA code programmed the DMA hardware for an
> element count of N and frame size of 1, with element sync. ?The DMA
> engine code will program the DMA hardware for an element count of 1 and
> a frame size of N, with frame sync. ?This appears to work - but at the
> moment I've not looked to see if there's any implications of this.
>
> You can enable/disable the DMA engine code for the TX or RX side by
> tweaking the two definitions at the top of the file if you want to do
> comparisons.
>
> Comments? ?If none, I'll see about committing this into the patch set,
> adding the removal of the private DMA code, and sending it out more
> "officially" with the next round of RFC for this set.
>
Element sync vs Frame sync change seems to be fine thought it's
bit puzzling that why it was not that way first place.

> Modulo the element vs frame sync issue, I think folk can see that the
> conversion process is fairly straight forward - it's more or less:
> 1. add struct dma_chan * pointer to the private structure.
> 2. add allocation/freeing functions for the DMA engine channels.
> 3. convert the callbacks to match the DMA engine callback prototype.
> 4. add code to set the DMA engine channel configuration appropriately
> ? (avoiding any unnecessary calls) via dmaengine_slave_config().
> 5. add code to queue the transfer via dmaengine_prep_slave_sg() and
> ? dmaengine_submit().
> 6. omap_start_dma() becomes dma_async_issue_pending().
>
> ?drivers/spi/spi-omap2-mcspi.c | ?186 +++++++++++++++++++++++++++++++++-------
> ?1 files changed, 153 insertions(+), 33 deletions(-)

The patch seems to be fine to me.

Am looping Shubro in case he has any comment on "why element sync was used ?"
and also to test the patch with more peripherals connected over SPI.

Regards
Santosh

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-20 16:50                                   ` Tony Lindgren
@ 2012-04-23 14:14                                     ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-23 14:14 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-omap, linux-mmc, T Krishnamoorthy, Balaji, linux-arm-kernel

On Fri, Apr 20, 2012 at 09:50:00AM -0700, Tony Lindgren wrote:
> Then something like this is still needed on top of your original mmc driver
> patch for omap2420:
> 
> --- a/drivers/mmc/host/omap.c
> +++ b/drivers/mmc/host/omap.c
> @@ -1617,11 +1617,14 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
>  	host->dma_tx_burst = -1;
>  	host->dma_rx_burst = -1;
>  
> -	sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
> +	if (cpu_is_omap24xx())
> +		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX :  OMAP24XX_DMA_MMC2_TX;
> +	else
> +		sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
>  	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
>  #if 0
>  	if (!host->dma_tx) {
> -		def_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
> +		dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
>  			sig);
>  		goto err_dma;
>  	}
> @@ -1632,7 +1635,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
>  #endif
>  #endif
>  #ifdef USE_DMA_ENGINE
> -	sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
> +	if (cpu_is_omap24xx())
> +		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX :  OMAP24XX_DMA_MMC2_RX;
> +	else
> +		sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
>  	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
>  #if 0
>  	if (!host->dma_rx) {

Is there any chance this DMA signal information could be moved into
platform resources, like omap_hsmmc has done?  I could then get rid
of the plat/dma.h dependence from this driver.

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-23 14:14                                     ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-23 14:14 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 20, 2012 at 09:50:00AM -0700, Tony Lindgren wrote:
> Then something like this is still needed on top of your original mmc driver
> patch for omap2420:
> 
> --- a/drivers/mmc/host/omap.c
> +++ b/drivers/mmc/host/omap.c
> @@ -1617,11 +1617,14 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
>  	host->dma_tx_burst = -1;
>  	host->dma_rx_burst = -1;
>  
> -	sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
> +	if (cpu_is_omap24xx())
> +		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX :  OMAP24XX_DMA_MMC2_TX;
> +	else
> +		sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
>  	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
>  #if 0
>  	if (!host->dma_tx) {
> -		def_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
> +		dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
>  			sig);
>  		goto err_dma;
>  	}
> @@ -1632,7 +1635,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
>  #endif
>  #endif
>  #ifdef USE_DMA_ENGINE
> -	sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
> +	if (cpu_is_omap24xx())
> +		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX :  OMAP24XX_DMA_MMC2_RX;
> +	else
> +		sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
>  	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
>  #if 0
>  	if (!host->dma_rx) {

Is there any chance this DMA signal information could be moved into
platform resources, like omap_hsmmc has done?  I could then get rid
of the plat/dma.h dependence from this driver.

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

* Re: [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
  2012-04-20 22:22     ` Tony Lindgren
@ 2012-04-23 14:19       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-23 14:19 UTC (permalink / raw)
  To: Tony Lindgren; +Cc: linux-arm-kernel, linux-omap, linux-mmc

On Fri, Apr 20, 2012 at 03:22:33PM -0700, Tony Lindgren wrote:
> * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:14]:
> > Using coherent DMA memory with the OMAP DMA engine results in
> > unpredictable behaviour due to memory ordering issues; as things stand,
> > there is no guarantee that data written to coherent DMA memory will be
> > visible to the DMA hardware.
> > 
> > This is because the OMAP dma_write() accessor contains no barriers,
> > necessary on ARMv6 and above.  The effect of this can be seen in comments
> > in the OMAP serial driver, which incorrectly talks about cache flushing
> > for the coherent DMA stuff.
> > 
> > Rather than adding barriers to the accessors, add it in the DMA support
> > code just before we enable DMA, and just after we disable DMA.  This
> > avoids having barriers for every DMA register access.
> > 
> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> 
> Acked-by: Tony Lindgren <tony@atomide.com>

Would you like me to push this upstream on my next -rc push (with a
stable tag?)

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

* [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
@ 2012-04-23 14:19       ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-23 14:19 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 20, 2012 at 03:22:33PM -0700, Tony Lindgren wrote:
> * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:14]:
> > Using coherent DMA memory with the OMAP DMA engine results in
> > unpredictable behaviour due to memory ordering issues; as things stand,
> > there is no guarantee that data written to coherent DMA memory will be
> > visible to the DMA hardware.
> > 
> > This is because the OMAP dma_write() accessor contains no barriers,
> > necessary on ARMv6 and above.  The effect of this can be seen in comments
> > in the OMAP serial driver, which incorrectly talks about cache flushing
> > for the coherent DMA stuff.
> > 
> > Rather than adding barriers to the accessors, add it in the DMA support
> > code just before we enable DMA, and just after we disable DMA.  This
> > avoids having barriers for every DMA register access.
> > 
> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> 
> Acked-by: Tony Lindgren <tony@atomide.com>

Would you like me to push this upstream on my next -rc push (with a
stable tag?)

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

* Re: [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
  2012-04-23 14:19       ` Russell King - ARM Linux
@ 2012-04-23 14:27         ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-23 14:27 UTC (permalink / raw)
  To: Russell King - ARM Linux; +Cc: linux-arm-kernel, linux-omap, linux-mmc

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120423 07:22]:
> On Fri, Apr 20, 2012 at 03:22:33PM -0700, Tony Lindgren wrote:
> > * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:14]:
> > > Using coherent DMA memory with the OMAP DMA engine results in
> > > unpredictable behaviour due to memory ordering issues; as things stand,
> > > there is no guarantee that data written to coherent DMA memory will be
> > > visible to the DMA hardware.
> > > 
> > > This is because the OMAP dma_write() accessor contains no barriers,
> > > necessary on ARMv6 and above.  The effect of this can be seen in comments
> > > in the OMAP serial driver, which incorrectly talks about cache flushing
> > > for the coherent DMA stuff.
> > > 
> > > Rather than adding barriers to the accessors, add it in the DMA support
> > > code just before we enable DMA, and just after we disable DMA.  This
> > > avoids having barriers for every DMA register access.
> > > 
> > > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > 
> > Acked-by: Tony Lindgren <tony@atomide.com>
> 
> Would you like me to push this upstream on my next -rc push (with a
> stable tag?)

Yes sounds good to me.

Regards,

Tony

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

* [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
@ 2012-04-23 14:27         ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-23 14:27 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120423 07:22]:
> On Fri, Apr 20, 2012 at 03:22:33PM -0700, Tony Lindgren wrote:
> > * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:14]:
> > > Using coherent DMA memory with the OMAP DMA engine results in
> > > unpredictable behaviour due to memory ordering issues; as things stand,
> > > there is no guarantee that data written to coherent DMA memory will be
> > > visible to the DMA hardware.
> > > 
> > > This is because the OMAP dma_write() accessor contains no barriers,
> > > necessary on ARMv6 and above.  The effect of this can be seen in comments
> > > in the OMAP serial driver, which incorrectly talks about cache flushing
> > > for the coherent DMA stuff.
> > > 
> > > Rather than adding barriers to the accessors, add it in the DMA support
> > > code just before we enable DMA, and just after we disable DMA.  This
> > > avoids having barriers for every DMA register access.
> > > 
> > > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> > 
> > Acked-by: Tony Lindgren <tony@atomide.com>
> 
> Would you like me to push this upstream on my next -rc push (with a
> stable tag?)

Yes sounds good to me.

Regards,

Tony

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-23 14:14                                     ` Russell King - ARM Linux
@ 2012-04-23 14:30                                       ` Tony Lindgren
  -1 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-23 14:30 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-omap, linux-mmc, T Krishnamoorthy, Balaji, linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120423 07:17]:
> On Fri, Apr 20, 2012 at 09:50:00AM -0700, Tony Lindgren wrote:
> > Then something like this is still needed on top of your original mmc driver
> > patch for omap2420:
> > 
> > --- a/drivers/mmc/host/omap.c
> > +++ b/drivers/mmc/host/omap.c
> > @@ -1617,11 +1617,14 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
> >  	host->dma_tx_burst = -1;
> >  	host->dma_rx_burst = -1;
> >  
> > -	sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
> > +	if (cpu_is_omap24xx())
> > +		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX :  OMAP24XX_DMA_MMC2_TX;
> > +	else
> > +		sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
> >  	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
> >  #if 0
> >  	if (!host->dma_tx) {
> > -		def_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
> > +		dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
> >  			sig);
> >  		goto err_dma;
> >  	}
> > @@ -1632,7 +1635,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
> >  #endif
> >  #endif
> >  #ifdef USE_DMA_ENGINE
> > -	sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
> > +	if (cpu_is_omap24xx())
> > +		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX :  OMAP24XX_DMA_MMC2_RX;
> > +	else
> > +		sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
> >  	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
> >  #if 0
> >  	if (!host->dma_rx) {
> 
> Is there any chance this DMA signal information could be moved into
> platform resources, like omap_hsmmc has done?  I could then get rid
> of the plat/dma.h dependence from this driver.

Sure, I'll take a look at that later on today.

Tony

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-23 14:30                                       ` Tony Lindgren
  0 siblings, 0 replies; 120+ messages in thread
From: Tony Lindgren @ 2012-04-23 14:30 UTC (permalink / raw)
  To: linux-arm-kernel

* Russell King - ARM Linux <linux@arm.linux.org.uk> [120423 07:17]:
> On Fri, Apr 20, 2012 at 09:50:00AM -0700, Tony Lindgren wrote:
> > Then something like this is still needed on top of your original mmc driver
> > patch for omap2420:
> > 
> > --- a/drivers/mmc/host/omap.c
> > +++ b/drivers/mmc/host/omap.c
> > @@ -1617,11 +1617,14 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
> >  	host->dma_tx_burst = -1;
> >  	host->dma_rx_burst = -1;
> >  
> > -	sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
> > +	if (cpu_is_omap24xx())
> > +		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX :  OMAP24XX_DMA_MMC2_TX;
> > +	else
> > +		sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
> >  	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
> >  #if 0
> >  	if (!host->dma_tx) {
> > -		def_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
> > +		dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
> >  			sig);
> >  		goto err_dma;
> >  	}
> > @@ -1632,7 +1635,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
> >  #endif
> >  #endif
> >  #ifdef USE_DMA_ENGINE
> > -	sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
> > +	if (cpu_is_omap24xx())
> > +		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX :  OMAP24XX_DMA_MMC2_RX;
> > +	else
> > +		sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
> >  	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
> >  #if 0
> >  	if (!host->dma_rx) {
> 
> Is there any chance this DMA signal information could be moved into
> platform resources, like omap_hsmmc has done?  I could then get rid
> of the plat/dma.h dependence from this driver.

Sure, I'll take a look at that later on today.

Tony

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

* Re: [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
  2012-04-23 14:30                                       ` Tony Lindgren
@ 2012-04-23 14:34                                         ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-23 14:34 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: linux-omap, linux-mmc, T Krishnamoorthy, Balaji, linux-arm-kernel

On Mon, Apr 23, 2012 at 07:30:48AM -0700, Tony Lindgren wrote:
> * Russell King - ARM Linux <linux@arm.linux.org.uk> [120423 07:17]:
> > On Fri, Apr 20, 2012 at 09:50:00AM -0700, Tony Lindgren wrote:
> > > Then something like this is still needed on top of your original mmc driver
> > > patch for omap2420:
> > > 
> > > --- a/drivers/mmc/host/omap.c
> > > +++ b/drivers/mmc/host/omap.c
> > > @@ -1617,11 +1617,14 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
> > >  	host->dma_tx_burst = -1;
> > >  	host->dma_rx_burst = -1;
> > >  
> > > -	sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
> > > +	if (cpu_is_omap24xx())
> > > +		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX :  OMAP24XX_DMA_MMC2_TX;
> > > +	else
> > > +		sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
> > >  	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
> > >  #if 0
> > >  	if (!host->dma_tx) {
> > > -		def_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
> > > +		dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
> > >  			sig);
> > >  		goto err_dma;
> > >  	}
> > > @@ -1632,7 +1635,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
> > >  #endif
> > >  #endif
> > >  #ifdef USE_DMA_ENGINE
> > > -	sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
> > > +	if (cpu_is_omap24xx())
> > > +		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX :  OMAP24XX_DMA_MMC2_RX;
> > > +	else
> > > +		sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
> > >  	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
> > >  #if 0
> > >  	if (!host->dma_rx) {
> > 
> > Is there any chance this DMA signal information could be moved into
> > platform resources, like omap_hsmmc has done?  I could then get rid
> > of the plat/dma.h dependence from this driver.
> 
> Sure, I'll take a look at that later on today.

Let me re-post the series first so you can produce patches against the
latest version of the series.  It's 11 patches, which contains the SPI
conversion.

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

* [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization
@ 2012-04-23 14:34                                         ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-23 14:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Apr 23, 2012 at 07:30:48AM -0700, Tony Lindgren wrote:
> * Russell King - ARM Linux <linux@arm.linux.org.uk> [120423 07:17]:
> > On Fri, Apr 20, 2012 at 09:50:00AM -0700, Tony Lindgren wrote:
> > > Then something like this is still needed on top of your original mmc driver
> > > patch for omap2420:
> > > 
> > > --- a/drivers/mmc/host/omap.c
> > > +++ b/drivers/mmc/host/omap.c
> > > @@ -1617,11 +1617,14 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
> > >  	host->dma_tx_burst = -1;
> > >  	host->dma_rx_burst = -1;
> > >  
> > > -	sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
> > > +	if (cpu_is_omap24xx())
> > > +		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX :  OMAP24XX_DMA_MMC2_TX;
> > > +	else
> > > +		sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
> > >  	host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
> > >  #if 0
> > >  	if (!host->dma_tx) {
> > > -		def_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
> > > +		dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
> > >  			sig);
> > >  		goto err_dma;
> > >  	}
> > > @@ -1632,7 +1635,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
> > >  #endif
> > >  #endif
> > >  #ifdef USE_DMA_ENGINE
> > > -	sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
> > > +	if (cpu_is_omap24xx())
> > > +		sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX :  OMAP24XX_DMA_MMC2_RX;
> > > +	else
> > > +		sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
> > >  	host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
> > >  #if 0
> > >  	if (!host->dma_rx) {
> > 
> > Is there any chance this DMA signal information could be moved into
> > platform resources, like omap_hsmmc has done?  I could then get rid
> > of the plat/dma.h dependence from this driver.
> 
> Sure, I'll take a look at that later on today.

Let me re-post the series first so you can produce patches against the
latest version of the series.  It's 11 patches, which contains the SPI
conversion.

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

* Re: [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
  2012-04-23 14:27         ` Tony Lindgren
@ 2012-04-23 14:35           ` Shilimkar, Santosh
  -1 siblings, 0 replies; 120+ messages in thread
From: Shilimkar, Santosh @ 2012-04-23 14:35 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: Russell King - ARM Linux, linux-arm-kernel, linux-omap, linux-mmc

On Mon, Apr 23, 2012 at 7:57 PM, Tony Lindgren <tony@atomide.com> wrote:
> * Russell King - ARM Linux <linux@arm.linux.org.uk> [120423 07:22]:
>> On Fri, Apr 20, 2012 at 03:22:33PM -0700, Tony Lindgren wrote:
>> > * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:14]:
>> > > Using coherent DMA memory with the OMAP DMA engine results in
>> > > unpredictable behaviour due to memory ordering issues; as things stand,
>> > > there is no guarantee that data written to coherent DMA memory will be
>> > > visible to the DMA hardware.
>> > >
>> > > This is because the OMAP dma_write() accessor contains no barriers,
>> > > necessary on ARMv6 and above.  The effect of this can be seen in comments
>> > > in the OMAP serial driver, which incorrectly talks about cache flushing
>> > > for the coherent DMA stuff.
>> > >
>> > > Rather than adding barriers to the accessors, add it in the DMA support
>> > > code just before we enable DMA, and just after we disable DMA.  This
>> > > avoids having barriers for every DMA register access.
>> > >
>> > > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> >
>> > Acked-by: Tony Lindgren <tony@atomide.com>
>>
>> Would you like me to push this upstream on my next -rc push (with a
>> stable tag?)
>
> Yes sounds good to me.
>
FWIW,
Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com>

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

* [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering
@ 2012-04-23 14:35           ` Shilimkar, Santosh
  0 siblings, 0 replies; 120+ messages in thread
From: Shilimkar, Santosh @ 2012-04-23 14:35 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Apr 23, 2012 at 7:57 PM, Tony Lindgren <tony@atomide.com> wrote:
> * Russell King - ARM Linux <linux@arm.linux.org.uk> [120423 07:22]:
>> On Fri, Apr 20, 2012 at 03:22:33PM -0700, Tony Lindgren wrote:
>> > * Russell King <rmk+kernel@arm.linux.org.uk> [120418 03:14]:
>> > > Using coherent DMA memory with the OMAP DMA engine results in
>> > > unpredictable behaviour due to memory ordering issues; as things stand,
>> > > there is no guarantee that data written to coherent DMA memory will be
>> > > visible to the DMA hardware.
>> > >
>> > > This is because the OMAP dma_write() accessor contains no barriers,
>> > > necessary on ARMv6 and above. ?The effect of this can be seen in comments
>> > > in the OMAP serial driver, which incorrectly talks about cache flushing
>> > > for the coherent DMA stuff.
>> > >
>> > > Rather than adding barriers to the accessors, add it in the DMA support
>> > > code just before we enable DMA, and just after we disable DMA. ?This
>> > > avoids having barriers for every DMA register access.
>> > >
>> > > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
>> >
>> > Acked-by: Tony Lindgren <tony@atomide.com>
>>
>> Would you like me to push this upstream on my next -rc push (with a
>> stable tag?)
>
> Yes sounds good to me.
>
FWIW,
Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com>

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

* Re: [RFC 0/8] DMA engine conversion
  2012-04-23 12:32     ` Shilimkar, Santosh
@ 2012-04-23 15:27       ` Shubhrajyoti
  -1 siblings, 0 replies; 120+ messages in thread
From: Shubhrajyoti @ 2012-04-23 15:27 UTC (permalink / raw)
  To: Shilimkar, Santosh
  Cc: Russell King - ARM Linux, linux-arm-kernel, linux-omap, linux-mmc

On Monday 23 April 2012 06:02 PM, Shilimkar, Santosh wrote:
>> conversion process is fairly straight forward - it's more or less:
>> > 1. add struct dma_chan * pointer to the private structure.
>> > 2. add allocation/freeing functions for the DMA engine channels.
>> > 3. convert the callbacks to match the DMA engine callback prototype.
>> > 4. add code to set the DMA engine channel configuration appropriately
>> >   (avoiding any unnecessary calls) via dmaengine_slave_config().
>> > 5. add code to queue the transfer via dmaengine_prep_slave_sg() and
>> >   dmaengine_submit().
>> > 6. omap_start_dma() becomes dma_async_issue_pending().
>> >
>> >  drivers/spi/spi-omap2-mcspi.c |  186 +++++++++++++++++++++++++++++++++-------
>> >  1 files changed, 153 insertions(+), 33 deletions(-)
> The patch seems to be fine to me.
>
> Am looping Shubro in case he has any comment on "why element sync was used ?"
> and also to test the patch with more peripherals connected over SPI.
>
> Regards
> Santosh
Tested on omap4 ethernet and omap3 touchscreen
also did a spidev test.

Tested-by: Shubhrajyoti <shubhrajyoti@ti.com <mailto:shubhrajyoti@ti.com>>

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

* [RFC 0/8] DMA engine conversion
@ 2012-04-23 15:27       ` Shubhrajyoti
  0 siblings, 0 replies; 120+ messages in thread
From: Shubhrajyoti @ 2012-04-23 15:27 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 23 April 2012 06:02 PM, Shilimkar, Santosh wrote:
>> conversion process is fairly straight forward - it's more or less:
>> > 1. add struct dma_chan * pointer to the private structure.
>> > 2. add allocation/freeing functions for the DMA engine channels.
>> > 3. convert the callbacks to match the DMA engine callback prototype.
>> > 4. add code to set the DMA engine channel configuration appropriately
>> >   (avoiding any unnecessary calls) via dmaengine_slave_config().
>> > 5. add code to queue the transfer via dmaengine_prep_slave_sg() and
>> >   dmaengine_submit().
>> > 6. omap_start_dma() becomes dma_async_issue_pending().
>> >
>> >  drivers/spi/spi-omap2-mcspi.c |  186 +++++++++++++++++++++++++++++++++-------
>> >  1 files changed, 153 insertions(+), 33 deletions(-)
> The patch seems to be fine to me.
>
> Am looping Shubro in case he has any comment on "why element sync was used ?"
> and also to test the patch with more peripherals connected over SPI.
>
> Regards
> Santosh
Tested on omap4 ethernet and omap3 touchscreen
also did a spidev test.

Tested-by: Shubhrajyoti <shubhrajyoti at ti.com <mailto:shubhrajyoti@ti.com>>

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

* Re: [PATCH 3/8] dmaengine: split out virtual channel DMA support from sa11x0 driver
  2012-04-18 10:11   ` Russell King
@ 2012-04-24 10:35     ` Laxman Dewangan
  -1 siblings, 0 replies; 120+ messages in thread
From: Laxman Dewangan @ 2012-04-24 10:35 UTC (permalink / raw)
  To: Russell King
  Cc: linux-arm-kernel, linux-omap, linux-mmc, Vinod Koul, Dan Williams

On Wednesday 18 April 2012 03:41 PM, Russell King wrote:
> +/**
> + * vchan_cookie_complete - report completion of a descriptor
> + * vd: virtual descriptor to update
> + *
> + * vc.lock must be held by caller
> + */
> +static inline void vchan_cookie_complete(struct virt_dma_desc *vd)
> +{
> +       struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);
> +
> +       dma_cookie_complete(&vd->tx);
> +       dev_vdbg(vc->chan.device->dev, "txd %p[%x]: marked complete\n",
> +               vd, vd->tx.cookie);
> +       list_add_tail(&vd->node,&vc->desc_completed);
> +
> +       tasklet_schedule(&vc->task);
> +}

For cyclic case, we will not like to call the  dma_cookie_complete() but 
want to put the desc in callback list.
So can we have one more arg on this function which byspass the call of 
dma_cookie_complete()

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

* [PATCH 3/8] dmaengine: split out virtual channel DMA support from sa11x0 driver
@ 2012-04-24 10:35     ` Laxman Dewangan
  0 siblings, 0 replies; 120+ messages in thread
From: Laxman Dewangan @ 2012-04-24 10:35 UTC (permalink / raw)
  To: linux-arm-kernel

On Wednesday 18 April 2012 03:41 PM, Russell King wrote:
> +/**
> + * vchan_cookie_complete - report completion of a descriptor
> + * vd: virtual descriptor to update
> + *
> + * vc.lock must be held by caller
> + */
> +static inline void vchan_cookie_complete(struct virt_dma_desc *vd)
> +{
> +       struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);
> +
> +       dma_cookie_complete(&vd->tx);
> +       dev_vdbg(vc->chan.device->dev, "txd %p[%x]: marked complete\n",
> +               vd, vd->tx.cookie);
> +       list_add_tail(&vd->node,&vc->desc_completed);
> +
> +       tasklet_schedule(&vc->task);
> +}

For cyclic case, we will not like to call the  dma_cookie_complete() but 
want to put the desc in callback list.
So can we have one more arg on this function which byspass the call of 
dma_cookie_complete()

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

* Re: [PATCH 3/8] dmaengine: split out virtual channel DMA support from sa11x0 driver
  2012-04-24 10:35     ` Laxman Dewangan
@ 2012-04-24 10:50       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-24 10:50 UTC (permalink / raw)
  To: Laxman Dewangan
  Cc: linux-arm-kernel, linux-omap, linux-mmc, Vinod Koul, Dan Williams

On Tue, Apr 24, 2012 at 04:05:29PM +0530, Laxman Dewangan wrote:
> On Wednesday 18 April 2012 03:41 PM, Russell King wrote:
>> +/**
>> + * vchan_cookie_complete - report completion of a descriptor
>> + * vd: virtual descriptor to update
>> + *
>> + * vc.lock must be held by caller
>> + */
>> +static inline void vchan_cookie_complete(struct virt_dma_desc *vd)
>> +{
>> +       struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);
>> +
>> +       dma_cookie_complete(&vd->tx);
>> +       dev_vdbg(vc->chan.device->dev, "txd %p[%x]: marked complete\n",
>> +               vd, vd->tx.cookie);
>> +       list_add_tail(&vd->node,&vc->desc_completed);
>> +
>> +       tasklet_schedule(&vc->task);
>> +}
>
> For cyclic case, we will not like to call the  dma_cookie_complete() but  
> want to put the desc in callback list.
> So can we have one more arg on this function which byspass the call of  
> dma_cookie_complete()

See the discussion on what's supposed to happen with cyclic transfers.
Cyclic transfers don't complete, so adding them to the completed list
and marking them complete is the wrong thing to be doing.  So arguably
calling this function is also the wrong thing to be doing because
you're not completing the transfer.

I'll be addressing the issue of cyclic transfers when I eventually get
to sorting out the OMAP ASoC driver.

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

* [PATCH 3/8] dmaengine: split out virtual channel DMA support from sa11x0 driver
@ 2012-04-24 10:50       ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-24 10:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 24, 2012 at 04:05:29PM +0530, Laxman Dewangan wrote:
> On Wednesday 18 April 2012 03:41 PM, Russell King wrote:
>> +/**
>> + * vchan_cookie_complete - report completion of a descriptor
>> + * vd: virtual descriptor to update
>> + *
>> + * vc.lock must be held by caller
>> + */
>> +static inline void vchan_cookie_complete(struct virt_dma_desc *vd)
>> +{
>> +       struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);
>> +
>> +       dma_cookie_complete(&vd->tx);
>> +       dev_vdbg(vc->chan.device->dev, "txd %p[%x]: marked complete\n",
>> +               vd, vd->tx.cookie);
>> +       list_add_tail(&vd->node,&vc->desc_completed);
>> +
>> +       tasklet_schedule(&vc->task);
>> +}
>
> For cyclic case, we will not like to call the  dma_cookie_complete() but  
> want to put the desc in callback list.
> So can we have one more arg on this function which byspass the call of  
> dma_cookie_complete()

See the discussion on what's supposed to happen with cyclic transfers.
Cyclic transfers don't complete, so adding them to the completed list
and marking them complete is the wrong thing to be doing.  So arguably
calling this function is also the wrong thing to be doing because
you're not completing the transfer.

I'll be addressing the issue of cyclic transfers when I eventually get
to sorting out the OMAP ASoC driver.

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

* Re: [PATCH 3/8] dmaengine: split out virtual channel DMA support from sa11x0 driver
  2012-04-24 10:50       ` Russell King - ARM Linux
@ 2012-04-24 10:57         ` Laxman Dewangan
  -1 siblings, 0 replies; 120+ messages in thread
From: Laxman Dewangan @ 2012-04-24 10:57 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-arm-kernel, linux-omap, linux-mmc, Vinod Koul, Dan Williams

On Tuesday 24 April 2012 04:20 PM, Russell King - ARM Linux wrote:
>
>> For cyclic case, we will not like to call the  dma_cookie_complete() but
>> want to put the desc in callback list.
>> So can we have one more arg on this function which byspass the call of
>> dma_cookie_complete()
> See the discussion on what's supposed to happen with cyclic transfers.
> Cyclic transfers don't complete, so adding them to the completed list
> and marking them complete is the wrong thing to be doing.  So arguably
> calling this function is also the wrong thing to be doing because
> you're not completing the transfer.

OK, we will not call this function but still need to call the callback. 
So do you suggest to call callback directly from dma driver rather than 
the virt_chan?
> I'll be addressing the issue of cyclic transfers when I eventually get
> to sorting out the OMAP ASoC driver.

Here I am developing the dma driver for Tegra in cyclic and normal mode 
and what is your suggestion here?
Should I use your virt_chan now or I can go ahead with my first patch 
without virt_chan and once you are done with your virt_chan with all 
cyclic support then  port the tegra_dma to use virt chan and next 
enhanced patch?

In this way, my Tegra dma will be there in tree, all client will be move 
to dma engine based driver and then I will comeback to tegra_dma for 
using the virt_channel.







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

* [PATCH 3/8] dmaengine: split out virtual channel DMA support from sa11x0 driver
@ 2012-04-24 10:57         ` Laxman Dewangan
  0 siblings, 0 replies; 120+ messages in thread
From: Laxman Dewangan @ 2012-04-24 10:57 UTC (permalink / raw)
  To: linux-arm-kernel

On Tuesday 24 April 2012 04:20 PM, Russell King - ARM Linux wrote:
>
>> For cyclic case, we will not like to call the  dma_cookie_complete() but
>> want to put the desc in callback list.
>> So can we have one more arg on this function which byspass the call of
>> dma_cookie_complete()
> See the discussion on what's supposed to happen with cyclic transfers.
> Cyclic transfers don't complete, so adding them to the completed list
> and marking them complete is the wrong thing to be doing.  So arguably
> calling this function is also the wrong thing to be doing because
> you're not completing the transfer.

OK, we will not call this function but still need to call the callback. 
So do you suggest to call callback directly from dma driver rather than 
the virt_chan?
> I'll be addressing the issue of cyclic transfers when I eventually get
> to sorting out the OMAP ASoC driver.

Here I am developing the dma driver for Tegra in cyclic and normal mode 
and what is your suggestion here?
Should I use your virt_chan now or I can go ahead with my first patch 
without virt_chan and once you are done with your virt_chan with all 
cyclic support then  port the tegra_dma to use virt chan and next 
enhanced patch?

In this way, my Tegra dma will be there in tree, all client will be move 
to dma engine based driver and then I will comeback to tegra_dma for 
using the virt_channel.

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

* Re: [PATCH 2/8] dmaengine: amba-pl08x: ensure physical channels are properly held
  2012-04-18 10:19     ` Russell King - ARM Linux
@ 2012-04-27 20:38       ` Linus Walleij
  -1 siblings, 0 replies; 120+ messages in thread
From: Linus Walleij @ 2012-04-27 20:38 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: linux-arm-kernel, linux-omap, linux-mmc, Vinod Koul, Dan Williams

On Wed, Apr 18, 2012 at 12:19 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:

> Oops, this patch wasn't supposed to be part of this set...

...however it gives me the impression that you have a patch set cooking
to also switch PL08x over to the virtual channel mechanism which is
really appetizing to see :-)

Yours,
Linus Walleij

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

* [PATCH 2/8] dmaengine: amba-pl08x: ensure physical channels are properly held
@ 2012-04-27 20:38       ` Linus Walleij
  0 siblings, 0 replies; 120+ messages in thread
From: Linus Walleij @ 2012-04-27 20:38 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 18, 2012 at 12:19 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:

> Oops, this patch wasn't supposed to be part of this set...

...however it gives me the impression that you have a patch set cooking
to also switch PL08x over to the virtual channel mechanism which is
really appetizing to see :-)

Yours,
Linus Walleij

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

* Re: [PATCH 2/8] dmaengine: amba-pl08x: ensure physical channels are properly held
  2012-04-27 20:38       ` Linus Walleij
@ 2012-04-27 21:41         ` Russell King - ARM Linux
  -1 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-27 21:41 UTC (permalink / raw)
  To: Linus Walleij
  Cc: linux-arm-kernel, linux-omap, linux-mmc, Vinod Koul, Dan Williams

On Fri, Apr 27, 2012 at 10:38:08PM +0200, Linus Walleij wrote:
> On Wed, Apr 18, 2012 at 12:19 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> 
> > Oops, this patch wasn't supposed to be part of this set...
> 
> ...however it gives me the impression that you have a patch set cooking
> to also switch PL08x over to the virtual channel mechanism which is
> really appetizing to see :-)

Actually, that doesn't exist yet.

This was a patch I'd had floating around, but I don't think its needed
in any way - I mis-analysed the code and created the patch...

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

* [PATCH 2/8] dmaengine: amba-pl08x: ensure physical channels are properly held
@ 2012-04-27 21:41         ` Russell King - ARM Linux
  0 siblings, 0 replies; 120+ messages in thread
From: Russell King - ARM Linux @ 2012-04-27 21:41 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 27, 2012 at 10:38:08PM +0200, Linus Walleij wrote:
> On Wed, Apr 18, 2012 at 12:19 PM, Russell King - ARM Linux
> <linux@arm.linux.org.uk> wrote:
> 
> > Oops, this patch wasn't supposed to be part of this set...
> 
> ...however it gives me the impression that you have a patch set cooking
> to also switch PL08x over to the virtual channel mechanism which is
> really appetizing to see :-)

Actually, that doesn't exist yet.

This was a patch I'd had floating around, but I don't think its needed
in any way - I mis-analysed the code and created the patch...

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

end of thread, other threads:[~2012-04-27 21:41 UTC | newest]

Thread overview: 120+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-04-18 10:09 [RFC 0/8] DMA engine conversion for omap_hsmmc Russell King - ARM Linux
2012-04-18 10:09 ` Russell King - ARM Linux
2012-04-18 10:10 ` [PATCH 1/8] ARM: OMAP: fix DMA vs memory ordering Russell King
2012-04-18 10:10   ` Russell King
2012-04-18 10:15   ` Felipe Balbi
2012-04-18 10:15     ` Felipe Balbi
2012-04-18 10:17     ` Russell King - ARM Linux
2012-04-18 10:17       ` Russell King - ARM Linux
2012-04-18 10:18       ` Felipe Balbi
2012-04-18 10:18         ` Felipe Balbi
2012-04-18 10:26         ` Russell King - ARM Linux
2012-04-18 10:26           ` Russell King - ARM Linux
2012-04-20 22:22   ` Tony Lindgren
2012-04-20 22:22     ` Tony Lindgren
2012-04-23 14:19     ` Russell King - ARM Linux
2012-04-23 14:19       ` Russell King - ARM Linux
2012-04-23 14:27       ` Tony Lindgren
2012-04-23 14:27         ` Tony Lindgren
2012-04-23 14:35         ` Shilimkar, Santosh
2012-04-23 14:35           ` Shilimkar, Santosh
2012-04-18 10:10 ` [PATCH 2/8] dmaengine: amba-pl08x: ensure physical channels are properly held Russell King
2012-04-18 10:10   ` Russell King
2012-04-18 10:19   ` Russell King - ARM Linux
2012-04-18 10:19     ` Russell King - ARM Linux
2012-04-27 20:38     ` Linus Walleij
2012-04-27 20:38       ` Linus Walleij
2012-04-27 21:41       ` Russell King - ARM Linux
2012-04-27 21:41         ` Russell King - ARM Linux
2012-04-18 10:11 ` [PATCH 3/8] dmaengine: split out virtual channel DMA support from sa11x0 driver Russell King
2012-04-18 10:11   ` Russell King
2012-04-24 10:35   ` Laxman Dewangan
2012-04-24 10:35     ` Laxman Dewangan
2012-04-24 10:50     ` Russell King - ARM Linux
2012-04-24 10:50       ` Russell King - ARM Linux
2012-04-24 10:57       ` Laxman Dewangan
2012-04-24 10:57         ` Laxman Dewangan
2012-04-18 10:11 ` [PATCH 4/8] dmaengine: add OMAP DMA engine driver Russell King
2012-04-18 10:11   ` Russell King
2012-04-18 10:11 ` [PATCH 5/8] mmc: omap_hsmmc: release correct resource Russell King
2012-04-18 10:11   ` Russell King
2012-04-20 22:23   ` Tony Lindgren
2012-04-20 22:23     ` Tony Lindgren
2012-04-20 22:59     ` Chris Ball
2012-04-20 22:59       ` Chris Ball
2012-04-21  2:35       ` Chris Ball
2012-04-21  2:35         ` Chris Ball
2012-04-21  9:48         ` Russell King - ARM Linux
2012-04-21  9:48           ` Russell King - ARM Linux
2012-04-22 15:20           ` Chris Ball
2012-04-22 15:20             ` Chris Ball
2012-04-18 10:12 ` [PATCH 6/8] mmc: omap_hsmmc: add DMA engine support Russell King
2012-04-18 10:12   ` Russell King
2012-04-18 18:11   ` Tony Lindgren
2012-04-18 18:11     ` Tony Lindgren
2012-04-18 19:09     ` Russell King - ARM Linux
2012-04-18 19:09       ` Russell King - ARM Linux
2012-04-18 19:53       ` Tony Lindgren
2012-04-18 19:53         ` Tony Lindgren
2012-04-18 10:12 ` [PATCH 7/8] mmc: omap_hsmmc: remove private DMA API implementation Russell King
2012-04-18 10:12   ` Russell King
2012-04-18 10:12 ` [PATCH 8/8] ARM: omap_hsmmc: remove platform data dma_mask and initialization Russell King
2012-04-18 10:12   ` Russell King
2012-04-18 15:23   ` T Krishnamoorthy, Balaji
2012-04-18 15:23     ` T Krishnamoorthy, Balaji
2012-04-18 15:29     ` Russell King - ARM Linux
2012-04-18 15:29       ` Russell King - ARM Linux
2012-04-18 15:35       ` T Krishnamoorthy, Balaji
2012-04-18 15:35         ` T Krishnamoorthy, Balaji
2012-04-18 18:19         ` Tony Lindgren
2012-04-18 18:19           ` Tony Lindgren
2012-04-18 19:10           ` Russell King - ARM Linux
2012-04-18 19:10             ` Russell King - ARM Linux
2012-04-18 19:55             ` Tony Lindgren
2012-04-18 19:55               ` Tony Lindgren
2012-04-18 19:42         ` Russell King - ARM Linux
2012-04-18 19:42           ` Russell King - ARM Linux
2012-04-18 20:02           ` Tony Lindgren
2012-04-18 20:02             ` Tony Lindgren
2012-04-18 20:24             ` Russell King - ARM Linux
2012-04-18 20:24               ` Russell King - ARM Linux
2012-04-18 21:01               ` Tony Lindgren
2012-04-18 21:01                 ` Tony Lindgren
2012-04-18 21:16                 ` Russell King - ARM Linux
2012-04-18 21:16                   ` Russell King - ARM Linux
2012-04-18 21:34                   ` Tony Lindgren
2012-04-18 21:34                     ` Tony Lindgren
2012-04-18 21:36                   ` Russell King - ARM Linux
2012-04-18 21:36                     ` Russell King - ARM Linux
2012-04-19  1:39                     ` Tony Lindgren
2012-04-19  1:39                       ` Tony Lindgren
2012-04-19 17:43                       ` Russell King - ARM Linux
2012-04-19 17:43                         ` Russell King - ARM Linux
2012-04-19 18:07                         ` Tony Lindgren
2012-04-19 18:07                           ` Tony Lindgren
2012-04-20 15:10                           ` Russell King - ARM Linux
2012-04-20 15:10                             ` Russell King - ARM Linux
2012-04-20 15:26                             ` Tony Lindgren
2012-04-20 15:26                               ` Tony Lindgren
2012-04-20 15:37                               ` Russell King - ARM Linux
2012-04-20 15:37                                 ` Russell King - ARM Linux
2012-04-20 16:43                                 ` Tony Lindgren
2012-04-20 16:43                                   ` Tony Lindgren
2012-04-20 22:09                                   ` Russell King - ARM Linux
2012-04-20 22:09                                     ` Russell King - ARM Linux
2012-04-20 22:21                                     ` Tony Lindgren
2012-04-20 22:21                                       ` Tony Lindgren
2012-04-20 16:50                                 ` Tony Lindgren
2012-04-20 16:50                                   ` Tony Lindgren
2012-04-23 14:14                                   ` Russell King - ARM Linux
2012-04-23 14:14                                     ` Russell King - ARM Linux
2012-04-23 14:30                                     ` Tony Lindgren
2012-04-23 14:30                                       ` Tony Lindgren
2012-04-23 14:34                                       ` Russell King - ARM Linux
2012-04-23 14:34                                         ` Russell King - ARM Linux
2012-04-23 11:46 ` [RFC 0/8] DMA engine conversion Russell King - ARM Linux
2012-04-23 11:46   ` Russell King - ARM Linux
2012-04-23 12:32   ` Shilimkar, Santosh
2012-04-23 12:32     ` Shilimkar, Santosh
2012-04-23 15:27     ` Shubhrajyoti
2012-04-23 15:27       ` Shubhrajyoti

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.