Linux Serial subsystem development
 help / color / mirror / Atom feed
* [PATCH v2 3/3] tty: serial: omap-serial: wakeup latency constraint is in microseconds, not milliseconds
From: Paul Walmsley @ 2012-01-26  2:50 UTC (permalink / raw)
  To: linux-omap, linux-serial, linux-arm-kernel
  Cc: Kevin Hilman, Greg Kroah-Hartman, Govindraj.R, Tomi Valkeinen,
	Alan Cox
In-Reply-To: <20120126024903.31613.24730.stgit@dusk>

The receive FIFO wakeup latency estimate in the omap-serial driver is
three orders of magnitude too small.  This effectively prevents the
MPU from going to a low-power state when CONFIG_CPU_IDLE=y.  This is a
major power management regression and masks some other FIFO-related
bugs in the driver.

Fix by correcting the most egregious problem in the RX wakeup latency
estimate.  There are several other flaws in the estimator; these will
be fixed by a separate patch series intended for 3.4.

The difference in low-power states with this patch can be observed via
debugfs in pm_debug/count.

This estimate does not have any effect when CONFIG_CPU_IDLE=n.

Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Kevin Hilman <khilman@ti.com>
Cc: Govindraj.R <govindraj.raja@ti.com>
Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: Alan Cox <alan@linux.intel.com>
Cc: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/tty/serial/omap-serial.c |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 11fa156..72fa783 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -740,8 +740,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
 	quot = serial_omap_get_divisor(port, baud);
 
 	/* calculate wakeup latency constraint */
-	up->calc_latency = (1000000 * up->port.fifosize) /
-				(1000 * baud / 8);
+	up->calc_latency = (USEC_PER_SEC * up->port.fifosize) / (baud / 8);
 	up->latency = up->calc_latency;
 	schedule_work(&up->qos_work);
 



^ permalink raw reply related

* Re: [PATCH 2/3] tty: serial: OMAP: block idle while the UART is transferring data in PIO mode
From: Paul Walmsley @ 2012-01-26  2:58 UTC (permalink / raw)
  To: linux-omap, linux-serial, linux-arm-kernel
  Cc: govindraj.r, Kevin Hilman, Govindraj.R, Greg Kroah-Hartman,
	Alan Cox, Tomi Valkeinen
In-Reply-To: <20120126025036.31613.67819.stgit@dusk>


Hi

The subject line on this patch should have been 

"[PATCH v2 2/3] tty: serial: OMAP: block idle while the UART is 
          transferring data in PIO mode"


- Paul

^ permalink raw reply

* Re: 3.3-rc1 console lag (was: Re: [PATCH v8 00/20] OMAP2+: UART: Runtime adaptation + cleanup)
From: Paul Walmsley @ 2012-01-26  3:00 UTC (permalink / raw)
  To: Ramirez Luna, Omar
  Cc: Tony Lindgren, Kevin Hilman, Govindraj Raja, linux-omap,
	linux-serial, linux-arm-kernel, Partha Basak, Vishwanath Sripathy,
	Rajendra Nayak, Santosh Shilimkar
In-Reply-To: <CAB-zwWg4RS4J1YCcbfMWjLSCcV=oHqk9vaUKKKr0WjnTEuOmxQ@mail.gmail.com>

On Wed, 25 Jan 2012, Ramirez Luna, Omar wrote:

> Hi,
> 
> On Fri, Nov 11, 2011 at 3:57 AM, Govindraj.R <govindraj.raja@ti.com> wrote:
> > Converting uart driver to adapt to pm runtime API's.
> > Code re-org + cleanup.
> > Moving some functionality from serial.c to omap-serial.c
> ...
> >
> > Ensure CONFIG_OMAP_PRM is set while testing irq_chaining with uart.
> > And for pm_qos usage ensure CONFIG_CPU_IDLE is selected other wise
> > console might be sluggish.
> 
> There is console lag for omap2plus_defconfig given that
> CONFIG_CPU_IDLE is not enabled. Is the intention to force CPU_IDLE
> into the defconfig or find an alternative for the new pm_qos when cpu
> idle is disabled.
> 
> Seen on beagle-xm and 3.3-rc1.

Try this

http://marc.info/?l=linux-arm-kernel&m=132754676814391&w=2


- Paul

^ permalink raw reply

* Re: patch "tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA" added to tty tree
From: Paul Walmsley @ 2012-01-26  3:02 UTC (permalink / raw)
  To: gregkh
  Cc: govindraj.r, khilman, tomi.valkeinen, linux-omap,
	linux-arm-kernel, linux-serial
In-Reply-To: <13274430881471@kroah.org>

cc lists

Hi Greg

On Tue, 24 Jan 2012, gregkh@suse.de wrote:
> 
> This is a note to let you know that I've just added the patch titled
> 
>     tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA
> 
> to my tty git tree which can be found at
>     git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
> in the tty-linus branch.

If it's not too late, I was wondering if you could drop this patch and the 
subsequent one ("tty: serial: OMAP: transmit FIFO threshold interrupts 
don't wake the"), in favor of the second version of this series that was 
just posted at

   http://marc.info/?l=linux-arm-kernel&m=132754676814391&w=2

If it is too late, we'll deal with it in 3.4.

thanks,

- Paul

^ permalink raw reply

* Re: patch "tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA" added to tty tree
From: Greg KH @ 2012-01-26  4:21 UTC (permalink / raw)
  To: Paul Walmsley
  Cc: govindraj.r, khilman, tomi.valkeinen, linux-omap,
	linux-arm-kernel, linux-serial
In-Reply-To: <alpine.DEB.2.00.1201251156361.5324@utopia.booyaka.com>

On Wed, Jan 25, 2012 at 08:02:09PM -0700, Paul Walmsley wrote:
> cc lists
> 
> Hi Greg
> 
> On Tue, 24 Jan 2012, gregkh@suse.de wrote:
> > 
> > This is a note to let you know that I've just added the patch titled
> > 
> >     tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA
> > 
> > to my tty git tree which can be found at
> >     git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
> > in the tty-linus branch.
> 
> If it's not too late, I was wondering if you could drop this patch and the 
> subsequent one ("tty: serial: OMAP: transmit FIFO threshold interrupts 
> don't wake the"), in favor of the second version of this series that was 
> just posted at
> 
>    http://marc.info/?l=linux-arm-kernel&m=132754676814391&w=2
> 
> If it is too late, we'll deal with it in 3.4.

What is wrong with the patches that I applied?  How about a fix-up patch
on top of what I have applied instead of whole new ones?

greg k-h

^ permalink raw reply

* Re: patch "tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA" added to tty tree
From: Paul Walmsley @ 2012-01-26  4:31 UTC (permalink / raw)
  To: Greg KH
  Cc: khilman, govindraj.raja, tomi.valkeinen, linux-serial, linux-omap,
	linux-arm-kernel
In-Reply-To: <20120126042155.GA3185@suse.de>

On Wed, 25 Jan 2012, Greg KH wrote:

> On Wed, Jan 25, 2012 at 08:02:09PM -0700, Paul Walmsley wrote:
> > On Tue, 24 Jan 2012, gregkh@suse.de wrote:
> > > 
> > > This is a note to let you know that I've just added the patch titled
> > > 
> > >     tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA
> > > 
> > > to my tty git tree which can be found at
> > >     git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
> > > in the tty-linus branch.
> > 
> > If it's not too late, I was wondering if you could drop this patch and the 
> > subsequent one ("tty: serial: OMAP: transmit FIFO threshold interrupts 
> > don't wake the"), in favor of the second version of this series that was 
> > just posted at
> > 
> >    http://marc.info/?l=linux-arm-kernel&m=132754676814391&w=2
> > 
> > If it is too late, we'll deal with it in 3.4.
> 
> What is wrong with the patches that I applied?

A new workaround is used that reduces the number of interrupts to normal.  
The commit messages are improved since we have a better idea of what was 
wrong.  There is also a new patch (patch 3) for a power management 
regression in the driver.
 
> How about a fix-up patch on top of what I have applied instead of whole 
> new ones?

That's fine, if that's your preference.  It will be several patches, 
though.  And about 75% of the previous series would be reverted, since a 
different workaround would be used.

Let me know if that is indeed what you'd like.

regards

- Paul

^ permalink raw reply

* [PATCH 0/7 v2] extract a simple dmaengine library from shdma.c
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: alsa-devel, linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda,
	linux-mmc, Paul Mundt, linux-serial

This is v2 of the simple DMA dmaengine library. It addresses Paul's 
comments to v1 - thanks, adds some comments.

Guennadi Liakhovetski (7):
  dmaengine: add a simple dma library
  dma: shdma: prepare for simple DMA conversion
  mmc: sh_mmcif: remove unneeded struct sh_mmcif_dma, prepare for
    simple DMA
  mmc: sh_mobile_sdhi: prepare for conversion to simple DMA
  serial: sh-sci: prepare for conversion to simple DMA
  ASoC: SIU: prepare for conversion to simple DMA
  dma: shdma: convert to the simple DMA library

 drivers/dma/Kconfig               |    4 +
 drivers/dma/Makefile              |    1 +
 drivers/dma/dma-simple.c          |  873 ++++++++++++++++++++++++++++
 drivers/dma/shdma.c               | 1138 +++++++++----------------------------
 drivers/dma/shdma.h               |   45 +-
 drivers/mmc/host/sh_mmcif.c       |   24 +-
 drivers/mmc/host/sh_mobile_sdhi.c |    8 +-
 drivers/tty/serial/sh-sci.c       |    8 +-
 include/linux/dma-simple.h        |  124 ++++
 include/linux/mmc/sh_mmcif.h      |    8 +-
 include/linux/sh_dma.h            |   39 +-
 sound/soc/sh/siu_pcm.c            |    8 +-
 12 files changed, 1334 insertions(+), 946 deletions(-)
 create mode 100644 drivers/dma/dma-simple.c
 create mode 100644 include/linux/dma-simple.h

-- 
1.7.2.5

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

^ permalink raw reply

* [PATCH 1/7 v2] dmaengine: add a simple dma library
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: alsa-devel, linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda,
	linux-mmc, Paul Mundt, linux-serial
In-Reply-To: <1327589784-4287-1-git-send-email-g.liakhovetski@gmx.de>

This patch adds a library of functions, helping to implement dmaengine
drivers for hardware, unable to handle scatter-gather lists natively.
The first version of this driver only supports memcpy and slave DMA
operation.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---

v2:

1. switch from using a tasklet to threaded IRQ, which allowed to
2. remove lock / unlock inline functions
3. remove __devinit, __devexit annotations

 drivers/dma/Kconfig        |    3 +
 drivers/dma/Makefile       |    1 +
 drivers/dma/dma-simple.c   |  873 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/dma-simple.h |  124 +++++++
 4 files changed, 1001 insertions(+), 0 deletions(-)
 create mode 100644 drivers/dma/dma-simple.c
 create mode 100644 include/linux/dma-simple.h

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index f1a2749..f7c583e 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -149,6 +149,9 @@ config TXX9_DMAC
 	  Support the TXx9 SoC internal DMA controller.  This can be
 	  integrated in chips such as the Toshiba TX4927/38/39.
 
+config DMA_SIMPLE
+	tristate
+
 config SH_DMAE
 	tristate "Renesas SuperH DMAC support"
 	depends on (SUPERH && SH_DMA) || (ARM && ARCH_SHMOBILE)
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 009a222..d63f773 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_SIMPLE) += dma-simple.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/dma-simple.c b/drivers/dma/dma-simple.c
new file mode 100644
index 0000000..49d8f7d
--- /dev/null
+++ b/drivers/dma/dma-simple.c
@@ -0,0 +1,873 @@
+/*
+ * Simple dmaengine driver library
+ *
+ * extracted from shdma.c
+ *
+ * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
+ * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.
+ * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-simple.h>
+#include <linux/dmaengine.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/* DMA descriptor control */
+enum simple_desc_status {
+	DESC_IDLE,
+	DESC_PREPARED,
+	DESC_SUBMITTED,
+	DESC_COMPLETED,	/* completed, have to call callback */
+	DESC_WAITING,	/* callback called, waiting for ack / re-submit */
+};
+
+#define NR_DESCS_PER_CHANNEL 32
+
+#define to_simple_chan(c) container_of(c, struct dma_simple_chan, dma_chan)
+#define to_simple_dev(d) container_of(d, struct dma_simple_dev, dma_dev)
+
+/*
+ * For slave DMA we assume, that there is a finite number of DMA slaves in the
+ * system, and that each such slave can only use a finite number of channels.
+ * We use slave channel IDs to make sure, that no such slave channel ID is
+ * allocated more than once.
+ */
+static unsigned int slave_num = 256;
+module_param(slave_num, uint, 0444);
+
+/* A bitmask with slave_num bits */
+static unsigned long *simple_slave_used;
+
+/* Called under spin_lock_irq(&schan->chan_lock") */
+static void simple_chan_xfer_ld_queue(struct dma_simple_chan *schan)
+{
+	struct dma_simple_dev *sdev = to_simple_dev(schan->dma_chan.device);
+	const struct dma_simple_ops *ops = sdev->ops;
+	struct dma_simple_desc *sdesc;
+
+	/* DMA work check */
+	if (ops->channel_busy(schan))
+		return;
+
+	/* Find the first not transferred descriptor */
+	list_for_each_entry(sdesc, &schan->ld_queue, node)
+		if (sdesc->mark == DESC_SUBMITTED) {
+			ops->start_xfer(schan, sdesc);
+			break;
+		}
+}
+
+static dma_cookie_t simple_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+	struct dma_simple_desc *chunk, *c, *desc =
+		container_of(tx, struct dma_simple_desc, async_tx),
+		*last = desc;
+	struct dma_simple_chan *schan = to_simple_chan(tx->chan);
+	struct dma_simple_slave *slave = tx->chan->private;
+	dma_async_tx_callback callback = tx->callback;
+	dma_cookie_t cookie;
+	bool power_up;
+
+	spin_lock_irq(&schan->chan_lock);
+
+	power_up = list_empty(&schan->ld_queue);
+
+	cookie = schan->dma_chan.cookie + 1;
+	if (cookie < 0)
+		cookie = 1;
+
+	schan->dma_chan.cookie = cookie;
+	tx->cookie = cookie;
+
+	/* Mark all chunks of this descriptor as submitted, move to the queue */
+	list_for_each_entry_safe(chunk, c, desc->node.prev, node) {
+		/*
+		 * All chunks are on the global ld_free, so, we have to find
+		 * the end of the chain ourselves
+		 */
+		if (chunk != desc && (chunk->mark == DESC_IDLE ||
+				      chunk->async_tx.cookie > 0 ||
+				      chunk->async_tx.cookie == -EBUSY ||
+				      &chunk->node == &schan->ld_free))
+			break;
+		chunk->mark = DESC_SUBMITTED;
+		/* Callback goes to the last chunk */
+		chunk->async_tx.callback = NULL;
+		chunk->cookie = cookie;
+		list_move_tail(&chunk->node, &schan->ld_queue);
+		last = chunk;
+
+		dev_dbg(schan->dev, "submit #%d@%p on %d\n",
+			tx->cookie, &last->async_tx, schan->id);
+	}
+
+	last->async_tx.callback = callback;
+	last->async_tx.callback_param = tx->callback_param;
+
+	if (power_up) {
+		int ret;
+		schan->pm_state = DMA_SIMPLE_PM_BUSY;
+
+		ret = pm_runtime_get(schan->dev);
+
+		spin_unlock_irq(&schan->chan_lock);
+		if (ret < 0)
+			dev_err(schan->dev, "%s(): GET = %d\n", __func__, ret);
+
+		pm_runtime_barrier(schan->dev);
+
+		spin_lock_irq(&schan->chan_lock);
+
+		/* Have we been reset, while waiting? */
+		if (schan->pm_state != DMA_SIMPLE_PM_ESTABLISHED) {
+			struct dma_simple_dev *sdev =
+				to_simple_dev(schan->dma_chan.device);
+			const struct dma_simple_ops *ops = sdev->ops;
+			dev_dbg(schan->dev, "Bring up channel %d\n",
+				schan->id);
+			/*
+			 * TODO: .xfer_setup() might fail on some platforms.
+			 * Make it int then, on error remove chunks from the
+			 * queue again
+			 */
+			ops->setup_xfer(schan, slave);
+
+			if (schan->pm_state == DMA_SIMPLE_PM_PENDING)
+				simple_chan_xfer_ld_queue(schan);
+			schan->pm_state = DMA_SIMPLE_PM_ESTABLISHED;
+		}
+	} else {
+		/*
+		 * Tell .device_issue_pending() not to run the queue, interrupts
+		 * will do it anyway
+		 */
+		schan->pm_state = DMA_SIMPLE_PM_PENDING;
+	}
+
+	spin_unlock_irq(&schan->chan_lock);
+
+	return cookie;
+}
+
+/* Called with desc_lock held */
+static struct dma_simple_desc *simple_get_desc(struct dma_simple_chan *schan)
+{
+	struct dma_simple_desc *sdesc;
+
+	list_for_each_entry(sdesc, &schan->ld_free, node)
+		if (sdesc->mark != DESC_PREPARED) {
+			BUG_ON(sdesc->mark != DESC_IDLE);
+			list_del(&sdesc->node);
+			return sdesc;
+		}
+
+	return NULL;
+}
+
+static int simple_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct dma_simple_chan *schan = to_simple_chan(chan);
+	struct dma_simple_dev *sdev = to_simple_dev(schan->dma_chan.device);
+	const struct dma_simple_ops *ops = sdev->ops;
+	struct dma_simple_desc *desc;
+	struct dma_simple_slave *slave = chan->private;
+	int ret, i;
+
+	/*
+	 * This relies on the guarantee from dmaengine that alloc_chan_resources
+	 * never runs concurrently with itself or free_chan_resources.
+	 */
+	if (slave) {
+		if (test_and_set_bit(slave->slave_id, simple_slave_used)) {
+			ret = -EBUSY;
+			goto etestused;
+		}
+
+		ret = ops->set_slave(schan, slave);
+		if (ret < 0)
+			goto esetslave;
+	}
+
+	schan->desc = kcalloc(NR_DESCS_PER_CHANNEL,
+			      sdev->desc_size, GFP_KERNEL);
+	if (!schan->desc) {
+		ret = -ENOMEM;
+		goto edescalloc;
+	}
+	schan->desc_num = NR_DESCS_PER_CHANNEL;
+
+	for (i = 0; i < NR_DESCS_PER_CHANNEL; i++) {
+		desc = ops->embedded_desc(schan->desc, i);
+		dma_async_tx_descriptor_init(&desc->async_tx,
+					     &schan->dma_chan);
+		desc->async_tx.tx_submit = simple_tx_submit;
+		desc->mark = DESC_IDLE;
+
+		list_add(&desc->node, &schan->ld_free);
+	}
+
+	return NR_DESCS_PER_CHANNEL;
+
+edescalloc:
+	if (slave)
+esetslave:
+		clear_bit(slave->slave_id, simple_slave_used);
+etestused:
+	chan->private = NULL;
+	return ret;
+}
+
+static dma_async_tx_callback __ld_cleanup(struct dma_simple_chan *schan, bool all)
+{
+	struct dma_simple_desc *desc, *_desc;
+	/* Is the "exposed" head of a chain acked? */
+	bool head_acked = false;
+	dma_cookie_t cookie = 0;
+	dma_async_tx_callback callback = NULL;
+	void *param = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&schan->chan_lock, flags);
+	list_for_each_entry_safe(desc, _desc, &schan->ld_queue, node) {
+		struct dma_async_tx_descriptor *tx = &desc->async_tx;
+
+		BUG_ON(tx->cookie > 0 && tx->cookie != desc->cookie);
+		BUG_ON(desc->mark != DESC_SUBMITTED &&
+		       desc->mark != DESC_COMPLETED &&
+		       desc->mark != DESC_WAITING);
+
+		/*
+		 * queue is ordered, and we use this loop to (1) clean up all
+		 * completed descriptors, and to (2) update descriptor flags of
+		 * any chunks in a (partially) completed chain
+		 */
+		if (!all && desc->mark == DESC_SUBMITTED &&
+		    desc->cookie != cookie)
+			break;
+
+		if (tx->cookie > 0)
+			cookie = tx->cookie;
+
+		if (desc->mark == DESC_COMPLETED && desc->chunks == 1) {
+			if (schan->completed_cookie != desc->cookie - 1)
+				dev_dbg(schan->dev,
+					"Completing cookie %d, expected %d\n",
+					desc->cookie,
+					schan->completed_cookie + 1);
+			schan->completed_cookie = desc->cookie;
+		}
+
+		/* Call callback on the last chunk */
+		if (desc->mark == DESC_COMPLETED && tx->callback) {
+			desc->mark = DESC_WAITING;
+			callback = tx->callback;
+			param = tx->callback_param;
+			dev_dbg(schan->dev, "descriptor #%d@%p on %d callback\n",
+				tx->cookie, tx, schan->id);
+			BUG_ON(desc->chunks != 1);
+			break;
+		}
+
+		if (tx->cookie > 0 || tx->cookie == -EBUSY) {
+			if (desc->mark == DESC_COMPLETED) {
+				BUG_ON(tx->cookie < 0);
+				desc->mark = DESC_WAITING;
+			}
+			head_acked = async_tx_test_ack(tx);
+		} else {
+			switch (desc->mark) {
+			case DESC_COMPLETED:
+				desc->mark = DESC_WAITING;
+				/* Fall through */
+			case DESC_WAITING:
+				if (head_acked)
+					async_tx_ack(&desc->async_tx);
+			}
+		}
+
+		dev_dbg(schan->dev, "descriptor %p #%d completed.\n",
+			tx, tx->cookie);
+
+		if (((desc->mark == DESC_COMPLETED ||
+		      desc->mark == DESC_WAITING) &&
+		     async_tx_test_ack(&desc->async_tx)) || all) {
+			/* Remove from ld_queue list */
+			desc->mark = DESC_IDLE;
+
+			list_move(&desc->node, &schan->ld_free);
+
+			if (list_empty(&schan->ld_queue)) {
+				dev_dbg(schan->dev, "Bring down channel %d\n", schan->id);
+				pm_runtime_put(schan->dev);
+				schan->pm_state = DMA_SIMPLE_PM_ESTABLISHED;
+			}
+		}
+	}
+
+	if (all && !callback)
+		/*
+		 * Terminating and the loop completed normally: forgive
+		 * uncompleted cookies
+		 */
+		schan->completed_cookie = schan->dma_chan.cookie;
+
+	spin_unlock_irqrestore(&schan->chan_lock, flags);
+
+	if (callback)
+		callback(param);
+
+	return callback;
+}
+
+/*
+ * simple_chan_ld_cleanup - Clean up link descriptors
+ *
+ * Clean up the ld_queue of DMA channel.
+ */
+static void simple_chan_ld_cleanup(struct dma_simple_chan *schan, bool all)
+{
+	while (__ld_cleanup(schan, all))
+		;
+}
+
+/*
+ * simple_free_chan_resources - Free all resources of the channel.
+ */
+static void simple_free_chan_resources(struct dma_chan *chan)
+{
+	struct dma_simple_chan *schan = to_simple_chan(chan);
+	struct dma_simple_dev *sdev = to_simple_dev(chan->device);
+	const struct dma_simple_ops *ops = sdev->ops;
+	LIST_HEAD(list);
+
+	/* Protect against ISR */
+	spin_lock_irq(&schan->chan_lock);
+	ops->halt_channel(schan);
+	spin_unlock_irq(&schan->chan_lock);
+
+	/* Now no new interrupts will occur */
+
+	/* Prepared and not submitted descriptors can still be on the queue */
+	if (!list_empty(&schan->ld_queue))
+		simple_chan_ld_cleanup(schan, true);
+
+	if (chan->private) {
+		/* The caller is holding dma_list_mutex */
+		struct dma_simple_slave *slave = chan->private;
+		clear_bit(slave->slave_id, simple_slave_used);
+		chan->private = NULL;
+	}
+
+	spin_lock_irq(&schan->chan_lock);
+
+	list_splice_init(&schan->ld_free, &list);
+	schan->desc_num = 0;
+
+	spin_unlock_irq(&schan->chan_lock);
+
+	kfree(schan->desc);
+}
+
+/**
+ * simple_add_desc - get, set up and return one transfer descriptor
+ * @schan:	DMA channel
+ * @flags:	DMA transfer flags
+ * @dst:	destination DMA address, incremented when direction equals
+ *		DMA_DEV_TO_MEM or DMA_MEM_TO_MEM
+ * @src:	source DMA address, incremented when direction equals
+ *		DMA_MEM_TO_DEV or DMA_MEM_TO_MEM
+ * @len:	DMA transfer length
+ * @first:	if NULL, set to the current descriptor and cookie set to -EBUSY
+ * @direction:	needed for slave DMA to decide which address to keep constant,
+ *		equals DMA_MEM_TO_MEM for MEMCPY
+ * Returns 0 or an error
+ * Locks: called with desc_lock held
+ */
+static struct dma_simple_desc *simple_add_desc(struct dma_simple_chan *schan,
+	unsigned long flags, dma_addr_t *dst, dma_addr_t *src, size_t *len,
+	struct dma_simple_desc **first, enum dma_transfer_direction direction)
+{
+	struct dma_simple_dev *sdev = to_simple_dev(schan->dma_chan.device);
+	const struct dma_simple_ops *ops = sdev->ops;
+	struct dma_simple_desc *new;
+	size_t copy_size = *len;
+
+	if (!copy_size)
+		return NULL;
+
+	/* Allocate the link descriptor from the free list */
+	new = simple_get_desc(schan);
+	if (!new) {
+		dev_err(schan->dev, "No free link descriptor available\n");
+		return NULL;
+	}
+
+	ops->desc_setup(schan, new, *src, *dst, &copy_size);
+
+	if (!*first) {
+		/* First desc */
+		new->async_tx.cookie = -EBUSY;
+		*first = new;
+	} else {
+		/* Other desc - invisible to the user */
+		new->async_tx.cookie = -EINVAL;
+	}
+
+	dev_dbg(schan->dev,
+		"chaining (%u/%u)@%x -> %x with %p, cookie %d\n",
+		copy_size, *len, *src, *dst, &new->async_tx,
+		new->async_tx.cookie);
+
+	new->mark = DESC_PREPARED;
+	new->async_tx.flags = flags;
+	new->direction = direction;
+
+	*len -= copy_size;
+	if (direction == DMA_MEM_TO_MEM || direction == DMA_MEM_TO_DEV)
+		*src += copy_size;
+	if (direction == DMA_MEM_TO_MEM || direction == DMA_DEV_TO_MEM)
+		*dst += copy_size;
+
+	return new;
+}
+
+/*
+ * simple_prep_sg - prepare transfer descriptors from an SG list
+ *
+ * Common routine for public (MEMCPY) and slave DMA. The MEMCPY case is also
+ * converted to scatter-gather to guarantee consistent locking and a correct
+ * list manipulation. For slave DMA direction carries the usual meaning, and,
+ * logically, the SG list is RAM and the addr variable contains slave address,
+ * e.g., the FIFO I/O register. For MEMCPY direction equals DMA_MEM_TO_MEM
+ * and the SG list contains only one element and points at the source buffer.
+ */
+static struct dma_async_tx_descriptor *simple_prep_sg(struct dma_simple_chan *schan,
+	struct scatterlist *sgl, unsigned int sg_len, dma_addr_t *addr,
+	enum dma_transfer_direction direction, unsigned long flags)
+{
+	struct scatterlist *sg;
+	struct dma_simple_desc *first = NULL, *new = NULL /* compiler... */;
+	LIST_HEAD(tx_list);
+	int chunks = 0;
+	unsigned long irq_flags;
+	int i;
+
+	for_each_sg(sgl, sg, sg_len, i)
+		chunks += DIV_ROUND_UP(sg_dma_len(sg), schan->max_xfer_len);
+
+	/* Have to lock the whole loop to protect against concurrent release */
+	spin_lock_irqsave(&schan->chan_lock, irq_flags);
+
+	/*
+	 * Chaining:
+	 * first descriptor is what user is dealing with in all API calls, its
+	 *	cookie is at first set to -EBUSY, at tx-submit to a positive
+	 *	number
+	 * if more than one chunk is needed further chunks have cookie = -EINVAL
+	 * the last chunk, if not equal to the first, has cookie = -ENOSPC
+	 * all chunks are linked onto the tx_list head with their .node heads
+	 *	only during this function, then they are immediately spliced
+	 *	back onto the free list in form of a chain
+	 */
+	for_each_sg(sgl, sg, sg_len, i) {
+		dma_addr_t sg_addr = sg_dma_address(sg);
+		size_t len = sg_dma_len(sg);
+
+		if (!len)
+			goto err_get_desc;
+
+		do {
+			dev_dbg(schan->dev, "Add SG #%d@%p[%d], dma %llx\n",
+				i, sg, len, (unsigned long long)sg_addr);
+
+			if (direction == DMA_DEV_TO_MEM)
+				new = simple_add_desc(schan, flags,
+						&sg_addr, addr, &len, &first,
+						direction);
+			else
+				new = simple_add_desc(schan, flags,
+						addr, &sg_addr, &len, &first,
+						direction);
+			if (!new)
+				goto err_get_desc;
+
+			new->chunks = chunks--;
+			list_add_tail(&new->node, &tx_list);
+		} while (len);
+	}
+
+	if (new != first)
+		new->async_tx.cookie = -ENOSPC;
+
+	/* Put them back on the free list, so, they don't get lost */
+	list_splice_tail(&tx_list, &schan->ld_free);
+
+	spin_unlock_irqrestore(&schan->chan_lock, irq_flags);
+
+	return &first->async_tx;
+
+err_get_desc:
+	list_for_each_entry(new, &tx_list, node)
+		new->mark = DESC_IDLE;
+	list_splice(&tx_list, &schan->ld_free);
+
+	spin_unlock_irqrestore(&schan->chan_lock, irq_flags);
+
+	return NULL;
+}
+
+static struct dma_async_tx_descriptor *simple_prep_memcpy(
+	struct dma_chan *chan, dma_addr_t dma_dest, dma_addr_t dma_src,
+	size_t len, unsigned long flags)
+{
+	struct dma_simple_chan *schan = to_simple_chan(chan);
+	struct scatterlist sg;
+
+	if (!chan || !len)
+		return NULL;
+
+	BUG_ON(!schan->desc_num);
+
+	sg_init_table(&sg, 1);
+	sg_set_page(&sg, pfn_to_page(PFN_DOWN(dma_src)), len,
+		    offset_in_page(dma_src));
+	sg_dma_address(&sg) = dma_src;
+	sg_dma_len(&sg) = len;
+
+	return simple_prep_sg(schan, &sg, 1, &dma_dest, DMA_MEM_TO_MEM, flags);
+}
+
+static struct dma_async_tx_descriptor *simple_prep_slave_sg(
+	struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
+	enum dma_transfer_direction direction, unsigned long flags)
+{
+	struct dma_simple_chan *schan = to_simple_chan(chan);
+	struct dma_simple_dev *sdev = to_simple_dev(schan->dma_chan.device);
+	const struct dma_simple_ops *ops = sdev->ops;
+	struct dma_simple_slave *slave = chan->private;
+	dma_addr_t slave_addr;
+
+	if (!chan)
+		return NULL;
+
+	BUG_ON(!schan->desc_num);
+
+	/* Someone calling slave DMA on a generic channel? */
+	if (!slave || !sg_len) {
+		dev_warn(schan->dev, "%s: bad parameter: %p, %d, %d\n",
+			 __func__, slave, sg_len, slave ? slave->slave_id : -1);
+		return NULL;
+	}
+
+	slave_addr = ops->slave_addr(schan);
+
+	return simple_prep_sg(schan, sgl, sg_len, &slave_addr,
+			      direction, flags);
+}
+
+static int simple_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+			  unsigned long arg)
+{
+	struct dma_simple_chan *schan = to_simple_chan(chan);
+	struct dma_simple_dev *sdev = to_simple_dev(chan->device);
+	const struct dma_simple_ops *ops = sdev->ops;
+	unsigned long flags;
+
+	/* Only supports DMA_TERMINATE_ALL */
+	if (cmd != DMA_TERMINATE_ALL)
+		return -ENXIO;
+
+	if (!chan)
+		return -EINVAL;
+
+	spin_lock_irqsave(&schan->chan_lock, flags);
+
+	ops->halt_channel(schan);
+
+	spin_unlock_irqrestore(&schan->chan_lock, flags);
+
+	simple_chan_ld_cleanup(schan, true);
+
+	return 0;
+}
+
+static void simple_issue_pending(struct dma_chan *chan)
+{
+	struct dma_simple_chan *schan = to_simple_chan(chan);
+
+	spin_lock_irq(&schan->chan_lock);
+	if (schan->pm_state == DMA_SIMPLE_PM_ESTABLISHED)
+		simple_chan_xfer_ld_queue(schan);
+	else
+		schan->pm_state = DMA_SIMPLE_PM_PENDING;
+	spin_unlock_irq(&schan->chan_lock);
+}
+
+static enum dma_status simple_tx_status(struct dma_chan *chan,
+					dma_cookie_t cookie,
+					struct dma_tx_state *txstate)
+{
+	struct dma_simple_chan *schan = to_simple_chan(chan);
+	dma_cookie_t last_used;
+	dma_cookie_t last_complete;
+	enum dma_status status;
+	unsigned long flags;
+
+	simple_chan_ld_cleanup(schan, false);
+
+	/* First read completed cookie to avoid a skew */
+	last_complete = schan->completed_cookie;
+	rmb();
+	last_used = chan->cookie;
+	BUG_ON(last_complete < 0);
+	dma_set_tx_state(txstate, last_complete, last_used, 0);
+
+	spin_lock_irqsave(&schan->chan_lock, flags);
+
+	status = dma_async_is_complete(cookie, last_complete, last_used);
+
+	/*
+	 * If we don't find cookie on the queue, it has been aborted and we have
+	 * to report error
+	 */
+	if (status != DMA_SUCCESS) {
+		struct dma_simple_desc *sdesc;
+		status = DMA_ERROR;
+		list_for_each_entry(sdesc, &schan->ld_queue, node)
+			if (sdesc->cookie == cookie) {
+				status = DMA_IN_PROGRESS;
+				break;
+			}
+	}
+
+	spin_unlock_irqrestore(&schan->chan_lock, flags);
+
+	return status;
+}
+
+/* Called from error IRQ or NMI */
+bool dma_simple_reset(struct dma_simple_dev *sdev)
+{
+	const struct dma_simple_ops *ops = sdev->ops;
+	struct dma_simple_chan *schan;
+	unsigned int handled = 0;
+	int i;
+
+	/* Reset all channels */
+	dma_simple_for_each_chan(schan, sdev, i) {
+		struct dma_simple_desc *sdesc;
+		LIST_HEAD(dl);
+
+		if (!schan)
+			continue;
+
+		spin_lock(&schan->chan_lock);
+
+		/* Stop the channel */
+		ops->halt_channel(schan);
+
+		list_splice_init(&schan->ld_queue, &dl);
+
+		if (!list_empty(&dl)) {
+			dev_dbg(schan->dev, "Bring down channel %d\n", schan->id);
+			pm_runtime_put(schan->dev);
+		}
+		schan->pm_state = DMA_SIMPLE_PM_ESTABLISHED;
+
+		spin_unlock(&schan->chan_lock);
+
+		/* Complete all  */
+		list_for_each_entry(sdesc, &dl, node) {
+			struct dma_async_tx_descriptor *tx = &sdesc->async_tx;
+			sdesc->mark = DESC_IDLE;
+			if (tx->callback)
+				tx->callback(tx->callback_param);
+		}
+
+		spin_lock(&schan->chan_lock);
+		list_splice(&dl, &schan->ld_free);
+		spin_unlock(&schan->chan_lock);
+
+		handled++;
+	}
+
+	return !!handled;
+}
+EXPORT_SYMBOL(dma_simple_reset);
+
+static irqreturn_t chan_irq(int irq, void *dev)
+{
+	struct dma_simple_chan *schan = dev;
+	const struct dma_simple_ops *ops =
+		to_simple_dev(schan->dma_chan.device)->ops;
+	irqreturn_t ret;
+
+	spin_lock(&schan->chan_lock);
+
+	ret = ops->chan_irq(schan, irq) ? IRQ_WAKE_THREAD : IRQ_NONE;
+
+	spin_unlock(&schan->chan_lock);
+
+	return ret;
+}
+
+static irqreturn_t chan_irqt(int irq, void *dev)
+{
+	struct dma_simple_chan *schan = dev;
+	const struct dma_simple_ops *ops =
+		to_simple_dev(schan->dma_chan.device)->ops;
+	struct dma_simple_desc *sdesc;
+
+	spin_lock_irq(&schan->chan_lock);
+	list_for_each_entry(sdesc, &schan->ld_queue, node) {
+		if (sdesc->mark == DESC_SUBMITTED &&
+		    ops->desc_completed(schan, sdesc)) {
+			dev_dbg(schan->dev, "done #%d@%p\n",
+				sdesc->async_tx.cookie, &sdesc->async_tx);
+			sdesc->mark = DESC_COMPLETED;
+			break;
+		}
+	}
+	/* Next desc */
+	simple_chan_xfer_ld_queue(schan);
+	spin_unlock_irq(&schan->chan_lock);
+
+	simple_chan_ld_cleanup(schan, false);
+
+	return IRQ_HANDLED;
+}
+
+int dma_simple_request_irq(struct dma_simple_chan *schan, int irq,
+			   unsigned long flags, const char *name)
+{
+	int ret = request_threaded_irq(irq, chan_irq, chan_irqt,
+				       flags, name, schan);
+
+	schan->irq = ret < 0 ? ret : irq;
+
+	return ret;
+}
+EXPORT_SYMBOL(dma_simple_request_irq);
+
+void dma_simple_free_irq(struct dma_simple_chan *schan)
+{
+	if (schan->irq >= 0)
+		free_irq(schan->irq, schan);
+}
+EXPORT_SYMBOL(dma_simple_free_irq);
+
+void dma_simple_chan_probe(struct dma_simple_dev *sdev,
+			   struct dma_simple_chan *schan, int id)
+{
+	schan->pm_state = DMA_SIMPLE_PM_ESTABLISHED;
+
+	/* reference struct dma_device */
+	schan->dma_chan.device = &sdev->dma_dev;
+
+	schan->dev = sdev->dma_dev.dev;
+	schan->id = id;
+
+	if (!schan->max_xfer_len)
+		schan->max_xfer_len = PAGE_SIZE;
+
+	spin_lock_init(&schan->chan_lock);
+
+	/* Init descripter manage list */
+	INIT_LIST_HEAD(&schan->ld_queue);
+	INIT_LIST_HEAD(&schan->ld_free);
+
+	/* Add the channel to DMA device channel list */
+	list_add_tail(&schan->dma_chan.device_node,
+			&sdev->dma_dev.channels);
+	sdev->schan[sdev->dma_dev.chancnt++] = schan;
+}
+EXPORT_SYMBOL(dma_simple_chan_probe);
+
+void dma_simple_chan_remove(struct dma_simple_chan *schan)
+{
+	list_del(&schan->dma_chan.device_node);
+}
+EXPORT_SYMBOL(dma_simple_chan_remove);
+
+int dma_simple_init(struct device *dev, struct dma_simple_dev *sdev,
+		    int chan_num)
+{
+	struct dma_device *dma_dev = &sdev->dma_dev;
+
+	/*
+	 * Require all call-backs for now, they can trivially be made optional
+	 * later as required
+	 */
+	if (!sdev->ops ||
+	    !sdev->desc_size ||
+	    !sdev->ops->embedded_desc ||
+	    !sdev->ops->start_xfer ||
+	    !sdev->ops->setup_xfer ||
+	    !sdev->ops->set_slave ||
+	    !sdev->ops->desc_setup ||
+	    !sdev->ops->slave_addr ||
+	    !sdev->ops->channel_busy ||
+	    !sdev->ops->halt_channel ||
+	    !sdev->ops->desc_completed)
+		return -EINVAL;
+
+	sdev->schan = kcalloc(chan_num, sizeof(*sdev->schan), GFP_KERNEL);
+	if (!sdev->schan)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&dma_dev->channels);
+
+	/* Common and MEMCPY operations */
+	dma_dev->device_alloc_chan_resources
+		= simple_alloc_chan_resources;
+	dma_dev->device_free_chan_resources = simple_free_chan_resources;
+	dma_dev->device_prep_dma_memcpy = simple_prep_memcpy;
+	dma_dev->device_tx_status = simple_tx_status;
+	dma_dev->device_issue_pending = simple_issue_pending;
+
+	/* Compulsory for DMA_SLAVE fields */
+	dma_dev->device_prep_slave_sg = simple_prep_slave_sg;
+	dma_dev->device_control = simple_control;
+
+	dma_dev->dev = dev;
+
+	return 0;
+}
+EXPORT_SYMBOL(dma_simple_init);
+
+void dma_simple_cleanup(struct dma_simple_dev *sdev)
+{
+	kfree(sdev->schan);
+}
+EXPORT_SYMBOL(dma_simple_cleanup);
+
+static int __init dma_simple_enter(void)
+{
+	simple_slave_used = kzalloc(DIV_ROUND_UP(slave_num, BITS_PER_LONG) *
+				    sizeof(long), GFP_KERNEL);
+	if (!simple_slave_used)
+		return -ENOMEM;
+	return 0;
+}
+module_init(dma_simple_enter);
+
+static void __exit dma_simple_exit(void)
+{
+	kfree(simple_slave_used);
+}
+module_exit(dma_simple_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Simple dmaengine driver library");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
diff --git a/include/linux/dma-simple.h b/include/linux/dma-simple.h
new file mode 100644
index 0000000..5336674
--- /dev/null
+++ b/include/linux/dma-simple.h
@@ -0,0 +1,124 @@
+/*
+ * Simple dmaengine driver library
+ *
+ * extracted from shdma.c and headers
+ *
+ * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
+ * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.
+ * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef DMA_SIMPLE_H
+#define DMA_SIMPLE_H
+
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/types.h>
+
+/**
+ * dma_simple_pm_state - DMA channel PM state
+ * DMA_SIMPLE_PM_ESTABLISHED:	either idle or during data transfer
+ * DMA_SIMPLE_PM_BUSY:		during the transfer preparation, when we have to
+ *				drop the lock temporarily
+ * DMA_SIMPLE_PM_PENDING:	transfers pending
+ */
+enum dma_simple_pm_state {
+	DMA_SIMPLE_PM_ESTABLISHED,
+	DMA_SIMPLE_PM_BUSY,
+	DMA_SIMPLE_PM_PENDING,
+};
+
+struct device;
+
+/*
+ * Drivers, using this library are expected to embed struct dma_simple_dev,
+ * struct dma_simple_chan, struct dma_simple_desc, and struct dma_simple_slave
+ * in their respective device, channel, descriptor and slave objects.
+ */
+
+struct dma_simple_slave {
+	unsigned int slave_id;
+};
+
+struct dma_simple_desc {
+	struct list_head node;
+	struct dma_async_tx_descriptor async_tx;
+	enum dma_transfer_direction direction;
+	dma_cookie_t cookie;
+	int chunks;
+	int mark;
+};
+
+struct dma_simple_chan {
+	dma_cookie_t completed_cookie;	/* The maximum cookie completed */
+	spinlock_t chan_lock;		/* Channel operation lock */
+	struct list_head ld_queue;	/* Link descriptors queue */
+	struct list_head ld_free;	/* Free link descriptors */
+	struct dma_chan dma_chan;	/* DMA channel */
+	struct device *dev;		/* Channel device */
+	void *desc;			/* buffer for descriptor array */
+	int desc_num;			/* desc count */
+	size_t max_xfer_len;		/* max transfer length */
+	int id;				/* Raw id of this channel */
+	int irq;			/* Channel IRQ */
+	enum dma_simple_pm_state pm_state;
+};
+
+/**
+ * struct dma_simple_ops - simple DMA driver operations
+ * desc_completed:	return true, if this is the descriptor, that just has
+ *			completed (atomic)
+ * halt_channel:	stop DMA channel operation (atomic)
+ * channel_busy:	return true, if the channel is busy (atomic)
+ * slave_addr:		return slave DMA address
+ * desc_setup:		set up the hardware specific descriptor portion (atomic)
+ * set_slave:		bind channel to a slave
+ * setup_xfer:		configure channel hardware for operation (atomic)
+ * start_xfer:		start the DMA transfer (atomic)
+ * embedded_desc:	return Nth struct dma_simple_desc pointer from the
+ *			descriptor array
+ * chan_irq:		process channel IRQ, return true if a transfer has
+ *			completed (atomic)
+ */
+struct dma_simple_ops {
+	bool (*desc_completed)(struct dma_simple_chan *, struct dma_simple_desc *);
+	void (*halt_channel)(struct dma_simple_chan *);
+	bool (*channel_busy)(struct dma_simple_chan *);
+	dma_addr_t (*slave_addr)(struct dma_simple_chan *);
+	int (*desc_setup)(struct dma_simple_chan *, struct dma_simple_desc *,
+			  dma_addr_t, dma_addr_t, size_t *);
+	int (*set_slave)(struct dma_simple_chan *, struct dma_simple_slave *);
+	void (*setup_xfer)(struct dma_simple_chan *, struct dma_simple_slave *);
+	void (*start_xfer)(struct dma_simple_chan *, struct dma_simple_desc *);
+	struct dma_simple_desc *(*embedded_desc)(void *, int);
+	bool (*chan_irq)(struct dma_simple_chan *, int);
+};
+
+struct dma_simple_dev {
+	struct dma_device dma_dev;
+	struct dma_simple_chan **schan;
+	const struct dma_simple_ops *ops;
+	size_t desc_size;
+};
+
+#define dma_simple_for_each_chan(c, d, i) for (i = 0, c = (d)->schan[0]; \
+				i < (d)->dma_dev.chancnt; c = (d)->schan[++i])
+
+int dma_simple_request_irq(struct dma_simple_chan *, int,
+			   unsigned long, const char *);
+void dma_simple_free_irq(struct dma_simple_chan *);
+bool dma_simple_reset(struct dma_simple_dev *sdev);
+void dma_simple_chan_probe(struct dma_simple_dev *sdev,
+			   struct dma_simple_chan *schan, int id);
+void dma_simple_chan_remove(struct dma_simple_chan *schan);
+int dma_simple_init(struct device *dev, struct dma_simple_dev *sdev,
+		    int chan_num);
+void dma_simple_cleanup(struct dma_simple_dev *sdev);
+
+#endif
-- 
1.7.2.5

^ permalink raw reply related

* [PATCH 2/7 v2] dma: shdma: prepare for simple DMA conversion
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda, linux-mmc,
	alsa-devel, linux-serial, Paul Mundt
In-Reply-To: <1327589784-4287-1-git-send-email-g.liakhovetski@gmx.de>

By placing an anonymous union at the top of struct sh_dmae_slave we can
transparently prepare all drivers for the upcoming simple DMA conversion.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 include/linux/sh_dma.h |    8 ++++++--
 1 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h
index 425450b..ed2aa1e 100644
--- a/include/linux/sh_dma.h
+++ b/include/linux/sh_dma.h
@@ -10,12 +10,16 @@
 #ifndef SH_DMA_H
 #define SH_DMA_H
 
-#include <linux/list.h>
+#include <linux/dma-simple.h>
 #include <linux/dmaengine.h>
+#include <linux/list.h>
 
 /* Used by slave DMA clients to request DMA to/from a specific peripheral */
 struct sh_dmae_slave {
-	unsigned int			slave_id; /* Set by the platform */
+	union {
+		unsigned int		slave_id; /* Set by the platform */
+		struct dma_simple_slave	simple_slave;
+	};
 	struct device			*dma_dev; /* Set by the platform */
 	const struct sh_dmae_slave_config	*config;  /* Set by the driver */
 };
-- 
1.7.2.5

^ permalink raw reply related

* [PATCH 3/7 v2] mmc: sh_mmcif: remove unneeded struct sh_mmcif_dma, prepare for simple DMA
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda, linux-mmc,
	alsa-devel, linux-serial, Paul Mundt
In-Reply-To: <1327589784-4287-1-git-send-email-g.liakhovetski@gmx.de>

Now that all users have been updated to use the embedded in struct
sh_mmcif_plat_data DMA slave IDs, struct sh_mmcif_dma is no longer needed
and can be removed. This also makes preparation for simple DMA conversion
easier.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/mmc/host/sh_mmcif.c  |   24 ++++++++++--------------
 include/linux/mmc/sh_mmcif.h |    8 +-------
 2 files changed, 11 insertions(+), 21 deletions(-)

diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index 2ad6c81..06a7cdc 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -384,31 +384,27 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
 	host->dma_active = false;
 
 	/* We can only either use DMA for both Tx and Rx or not use it at all */
-	if (pdata->dma) {
-		dev_warn(&host->pd->dev,
-			 "Update your platform to use embedded DMA slave IDs\n");
-		tx = &pdata->dma->chan_priv_tx;
-		rx = &pdata->dma->chan_priv_rx;
-	} else {
-		tx = &host->dma_slave_tx;
-		tx->slave_id = pdata->slave_id_tx;
-		rx = &host->dma_slave_rx;
-		rx->slave_id = pdata->slave_id_rx;
-	}
-	if (tx->slave_id > 0 && rx->slave_id > 0) {
+	tx = &host->dma_slave_tx;
+	tx->simple_slave.slave_id = pdata->slave_id_tx;
+	rx = &host->dma_slave_rx;
+	rx->simple_slave.slave_id = pdata->slave_id_rx;
+
+	if (tx->simple_slave.slave_id > 0 && rx->simple_slave.slave_id > 0) {
 		dma_cap_mask_t mask;
 
 		dma_cap_zero(mask);
 		dma_cap_set(DMA_SLAVE, mask);
 
-		host->chan_tx = dma_request_channel(mask, sh_mmcif_filter, tx);
+		host->chan_tx = dma_request_channel(mask, sh_mmcif_filter,
+						    &tx->simple_slave);
 		dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__,
 			host->chan_tx);
 
 		if (!host->chan_tx)
 			return;
 
-		host->chan_rx = dma_request_channel(mask, sh_mmcif_filter, rx);
+		host->chan_rx = dma_request_channel(mask, sh_mmcif_filter,
+						    &rx->simple_slave);
 		dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__,
 			host->chan_rx);
 
diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h
index 04ff452..b36caa9 100644
--- a/include/linux/mmc/sh_mmcif.h
+++ b/include/linux/mmc/sh_mmcif.h
@@ -32,17 +32,11 @@
  * 1111 : Peripheral clock (sup_pclk set '1')
  */
 
-struct sh_mmcif_dma {
-	struct sh_dmae_slave chan_priv_tx;
-	struct sh_dmae_slave chan_priv_rx;
-};
-
 struct sh_mmcif_plat_data {
 	void (*set_pwr)(struct platform_device *pdev, int state);
 	void (*down_pwr)(struct platform_device *pdev);
 	int (*get_cd)(struct platform_device *pdef);
-	struct sh_mmcif_dma	*dma;		/* Deprecated. Instead */
-	unsigned int		slave_id_tx;	/* use embedded slave_id_[tr]x */
+	unsigned int		slave_id_tx;	/* embedded slave_id_[tr]x */
 	unsigned int		slave_id_rx;
 	u8			sup_pclk;	/* 1 :SH7757, 0: SH7724/SH7372 */
 	unsigned long		caps;
-- 
1.7.2.5


^ permalink raw reply related

* [PATCH 4/7 v2] mmc: sh_mobile_sdhi: prepare for conversion to simple DMA
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda, linux-mmc,
	alsa-devel, linux-serial, Paul Mundt
In-Reply-To: <1327589784-4287-1-git-send-email-g.liakhovetski@gmx.de>

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/mmc/host/sh_mobile_sdhi.c |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index f91c3aa..2c3a9d3 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -130,10 +130,10 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
 		mmc_data->cd_gpio = p->cd_gpio;
 
 		if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) {
-			priv->param_tx.slave_id = p->dma_slave_tx;
-			priv->param_rx.slave_id = p->dma_slave_rx;
-			priv->dma_priv.chan_priv_tx = &priv->param_tx;
-			priv->dma_priv.chan_priv_rx = &priv->param_rx;
+			priv->param_tx.simple_slave.slave_id = p->dma_slave_tx;
+			priv->param_rx.simple_slave.slave_id = p->dma_slave_rx;
+			priv->dma_priv.chan_priv_tx = &priv->param_tx.simple_slave;
+			priv->dma_priv.chan_priv_rx = &priv->param_rx.simple_slave;
 			priv->dma_priv.alignment_shift = 1; /* 2-byte alignment */
 			mmc_data->dma = &priv->dma_priv;
 		}
-- 
1.7.2.5


^ permalink raw reply related

* [PATCH 6/7 v2] ASoC: SIU: prepare for conversion to simple DMA
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: alsa-devel, linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda,
	linux-mmc, Paul Mundt, linux-serial
In-Reply-To: <1327589784-4287-1-git-send-email-g.liakhovetski@gmx.de>

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 sound/soc/sh/siu_pcm.c |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c
index 0193e59..414bb35 100644
--- a/sound/soc/sh/siu_pcm.c
+++ b/sound/soc/sh/siu_pcm.c
@@ -330,12 +330,12 @@ static bool filter(struct dma_chan *chan, void *slave)
 {
 	struct sh_dmae_slave *param = slave;
 
-	pr_debug("%s: slave ID %d\n", __func__, param->slave_id);
+	pr_debug("%s: slave ID %d\n", __func__, param->simple_slave.slave_id);
 
 	if (unlikely(param->dma_dev != chan->device->dev))
 		return false;
 
-	chan->private = param;
+	chan->private = &param->simple_slave;
 	return true;
 }
 
@@ -360,12 +360,12 @@ static int siu_pcm_open(struct snd_pcm_substream *ss)
 	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		siu_stream = &port_info->playback;
 		param = &siu_stream->param;
-		param->slave_id = port ? pdata->dma_slave_tx_b :
+		param->simple_slave.slave_id = port ? pdata->dma_slave_tx_b :
 			pdata->dma_slave_tx_a;
 	} else {
 		siu_stream = &port_info->capture;
 		param = &siu_stream->param;
-		param->slave_id = port ? pdata->dma_slave_rx_b :
+		param->simple_slave.slave_id = port ? pdata->dma_slave_rx_b :
 			pdata->dma_slave_rx_a;
 	}
 
-- 
1.7.2.5

^ permalink raw reply related

* [PATCH 7/7 v2] dma: shdma: convert to the simple DMA library
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda, linux-mmc,
	alsa-devel, linux-serial, Paul Mundt
In-Reply-To: <1327589784-4287-1-git-send-email-g.liakhovetski@gmx.de>

The simple DMA library has originally been extracted from the shdma driver,
which now can be converted to actually use it.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---

v2: update IRQ handling to match v2 of the simple DMA library

 drivers/dma/Kconfig    |    1 +
 drivers/dma/shdma.c    | 1138 ++++++++++++------------------------------------
 drivers/dma/shdma.h    |   45 +-
 include/linux/sh_dma.h |   39 +--
 4 files changed, 308 insertions(+), 915 deletions(-)

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index f7c583e..2696552 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -157,6 +157,7 @@ config SH_DMAE
 	depends on (SUPERH && SH_DMA) || (ARM && ARCH_SHMOBILE)
 	depends on !SH_DMA_API
 	select DMA_ENGINE
+	select DMA_SIMPLE
 	help
 	  Enable support for the Renesas SuperH DMA controllers.
 
diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c
index 812fd76..08011e8 100644
--- a/drivers/dma/shdma.c
+++ b/drivers/dma/shdma.c
@@ -3,6 +3,7 @@
  *
  * base is drivers/dma/flsdma.c
  *
+ * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
  * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
  * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.
  * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
@@ -32,18 +33,12 @@
 #include <linux/rculist.h>
 #include "shdma.h"
 
-/* DMA descriptor control */
-enum sh_dmae_desc_status {
-	DESC_IDLE,
-	DESC_PREPARED,
-	DESC_SUBMITTED,
-	DESC_COMPLETED,	/* completed, have to call callback */
-	DESC_WAITING,	/* callback called, waiting for ack / re-submit */
-};
+#define SH_DMAE_DRV_NAME "sh-dma-engine"
 
-#define NR_DESCS_PER_CHANNEL 32
 /* Default MEMCPY transfer size = 2^2 = 4 bytes */
 #define LOG2_DEFAULT_XFER_SIZE	2
+#define SH_DMA_SLAVE_NUMBER 256
+#define SH_DMA_TCR_MAX (16 * 1024 * 1024 - 1)
 
 /*
  * Used for write-side mutual exclusion for the global device list,
@@ -52,18 +47,12 @@ enum sh_dmae_desc_status {
 static DEFINE_SPINLOCK(sh_dmae_lock);
 static LIST_HEAD(sh_dmae_devices);
 
-/* A bitmask with bits enough for enum sh_dmae_slave_chan_id */
-static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SH_DMA_SLAVE_NUMBER)];
-
-static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all);
-static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan);
-
 static void chclr_write(struct sh_dmae_chan *sh_dc, u32 data)
 {
 	struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
 
 	__raw_writel(data, shdev->chan_reg +
-		     shdev->pdata->channel[sh_dc->id].chclr_offset);
+		     shdev->pdata->channel[sh_dc->simple_chan.id].chclr_offset);
 }
 
 static void sh_dmae_writel(struct sh_dmae_chan *sh_dc, u32 data, u32 reg)
@@ -153,11 +142,11 @@ static int sh_dmae_rst(struct sh_dmae_device *shdev)
 	spin_unlock_irqrestore(&sh_dmae_lock, flags);
 
 	if (dmaor & (DMAOR_AE | DMAOR_NMIF)) {
-		dev_warn(shdev->common.dev, "Can't initialize DMAOR.\n");
+		dev_warn(shdev->simple_dev.dma_dev.dev, "Can't initialize DMAOR.\n");
 		return -EIO;
 	}
 	if (shdev->pdata->dmaor_init & ~dmaor)
-		dev_warn(shdev->common.dev,
+		dev_warn(shdev->simple_dev.dma_dev.dev,
 			 "DMAOR=0x%x hasn't latched the initial value 0x%x.\n",
 			 dmaor, shdev->pdata->dmaor_init);
 	return 0;
@@ -222,15 +211,6 @@ static void dmae_start(struct sh_dmae_chan *sh_chan)
 	chcr_write(sh_chan, chcr & ~CHCR_TE);
 }
 
-static void dmae_halt(struct sh_dmae_chan *sh_chan)
-{
-	struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
-	u32 chcr = chcr_read(sh_chan);
-
-	chcr &= ~(CHCR_DE | CHCR_TE | shdev->chcr_ie_bit);
-	chcr_write(sh_chan, chcr);
-}
-
 static void dmae_init(struct sh_dmae_chan *sh_chan)
 {
 	/*
@@ -259,7 +239,7 @@ static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val)
 {
 	struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
 	struct sh_dmae_pdata *pdata = shdev->pdata;
-	const struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->id];
+	const struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->simple_chan.id];
 	u16 __iomem *addr = shdev->dmars;
 	unsigned int shift = chan_pdata->dmars_bit;
 
@@ -280,720 +260,142 @@ static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val)
 	return 0;
 }
 
-static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx)
+static void sh_dmae_start_xfer(struct dma_simple_chan *schan,
+			       struct dma_simple_desc *sdesc)
 {
-	struct sh_desc *desc = tx_to_sh_desc(tx), *chunk, *last = desc, *c;
-	struct sh_dmae_chan *sh_chan = to_sh_chan(tx->chan);
-	struct sh_dmae_slave *param = tx->chan->private;
-	dma_async_tx_callback callback = tx->callback;
-	dma_cookie_t cookie;
-	bool power_up;
-
-	spin_lock_irq(&sh_chan->desc_lock);
-
-	if (list_empty(&sh_chan->ld_queue))
-		power_up = true;
-	else
-		power_up = false;
-
-	cookie = sh_chan->common.cookie;
-	cookie++;
-	if (cookie < 0)
-		cookie = 1;
-
-	sh_chan->common.cookie = cookie;
-	tx->cookie = cookie;
-
-	/* Mark all chunks of this descriptor as submitted, move to the queue */
-	list_for_each_entry_safe(chunk, c, desc->node.prev, node) {
-		/*
-		 * All chunks are on the global ld_free, so, we have to find
-		 * the end of the chain ourselves
-		 */
-		if (chunk != desc && (chunk->mark == DESC_IDLE ||
-				      chunk->async_tx.cookie > 0 ||
-				      chunk->async_tx.cookie == -EBUSY ||
-				      &chunk->node == &sh_chan->ld_free))
-			break;
-		chunk->mark = DESC_SUBMITTED;
-		/* Callback goes to the last chunk */
-		chunk->async_tx.callback = NULL;
-		chunk->cookie = cookie;
-		list_move_tail(&chunk->node, &sh_chan->ld_queue);
-		last = chunk;
-	}
-
-	last->async_tx.callback = callback;
-	last->async_tx.callback_param = tx->callback_param;
-
-	dev_dbg(sh_chan->dev, "submit #%d@%p on %d: %x[%d] -> %x\n",
-		tx->cookie, &last->async_tx, sh_chan->id,
-		desc->hw.sar, desc->hw.tcr, desc->hw.dar);
-
-	if (power_up) {
-		sh_chan->pm_state = DMAE_PM_BUSY;
-
-		pm_runtime_get(sh_chan->dev);
-
-		spin_unlock_irq(&sh_chan->desc_lock);
-
-		pm_runtime_barrier(sh_chan->dev);
-
-		spin_lock_irq(&sh_chan->desc_lock);
-
-		/* Have we been reset, while waiting? */
-		if (sh_chan->pm_state != DMAE_PM_ESTABLISHED) {
-			dev_dbg(sh_chan->dev, "Bring up channel %d\n",
-				sh_chan->id);
-			if (param) {
-				const struct sh_dmae_slave_config *cfg =
-					param->config;
-
-				dmae_set_dmars(sh_chan, cfg->mid_rid);
-				dmae_set_chcr(sh_chan, cfg->chcr);
-			} else {
-				dmae_init(sh_chan);
-			}
-
-			if (sh_chan->pm_state == DMAE_PM_PENDING)
-				sh_chan_xfer_ld_queue(sh_chan);
-			sh_chan->pm_state = DMAE_PM_ESTABLISHED;
-		}
-	} else {
-		sh_chan->pm_state = DMAE_PM_PENDING;
-	}
-
-	spin_unlock_irq(&sh_chan->desc_lock);
-
-	return cookie;
+	struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
+						    simple_chan);
+	struct sh_dmae_desc *sh_desc = container_of(sdesc,
+					struct sh_dmae_desc, simple_desc);
+	dev_dbg(sh_chan->simple_chan.dev, "Queue #%d to %d: %u@%x -> %x\n",
+		sdesc->async_tx.cookie, sh_chan->simple_chan.id,
+		sh_desc->hw.tcr, sh_desc->hw.sar, sh_desc->hw.dar);
+	/* Get the ld start address from ld_queue */
+	dmae_set_reg(sh_chan, &sh_desc->hw);
+	dmae_start(sh_chan);
 }
 
-/* Called with desc_lock held */
-static struct sh_desc *sh_dmae_get_desc(struct sh_dmae_chan *sh_chan)
+static bool sh_dmae_channel_busy(struct dma_simple_chan *schan)
 {
-	struct sh_desc *desc;
-
-	list_for_each_entry(desc, &sh_chan->ld_free, node)
-		if (desc->mark != DESC_PREPARED) {
-			BUG_ON(desc->mark != DESC_IDLE);
-			list_del(&desc->node);
-			return desc;
-		}
-
-	return NULL;
+	struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
+						    simple_chan);
+	return dmae_is_busy(sh_chan);
 }
 
-static const struct sh_dmae_slave_config *sh_dmae_find_slave(
-	struct sh_dmae_chan *sh_chan, struct sh_dmae_slave *param)
+static void sh_dmae_setup_xfer(struct dma_simple_chan *schan,
+			       struct dma_simple_slave *sslave)
 {
-	struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
-	struct sh_dmae_pdata *pdata = shdev->pdata;
-	int i;
+	struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
+						    simple_chan);
 
-	if (param->slave_id >= SH_DMA_SLAVE_NUMBER)
-		return NULL;
-
-	for (i = 0; i < pdata->slave_num; i++)
-		if (pdata->slave[i].slave_id == param->slave_id)
-			return pdata->slave + i;
-
-	return NULL;
-}
+	if (sslave) {
+		struct sh_dmae_slave *slave = container_of(sslave,
+					struct sh_dmae_slave, simple_slave);
+		const struct sh_dmae_slave_config *cfg =
+			slave->config;
 
-static int sh_dmae_alloc_chan_resources(struct dma_chan *chan)
-{
-	struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
-	struct sh_desc *desc;
-	struct sh_dmae_slave *param = chan->private;
-	int ret;
-
-	/*
-	 * This relies on the guarantee from dmaengine that alloc_chan_resources
-	 * never runs concurrently with itself or free_chan_resources.
-	 */
-	if (param) {
-		const struct sh_dmae_slave_config *cfg;
-
-		cfg = sh_dmae_find_slave(sh_chan, param);
-		if (!cfg) {
-			ret = -EINVAL;
-			goto efindslave;
-		}
-
-		if (test_and_set_bit(param->slave_id, sh_dmae_slave_used)) {
-			ret = -EBUSY;
-			goto etestused;
-		}
-
-		param->config = cfg;
-	}
-
-	while (sh_chan->descs_allocated < NR_DESCS_PER_CHANNEL) {
-		desc = kzalloc(sizeof(struct sh_desc), GFP_KERNEL);
-		if (!desc)
-			break;
-		dma_async_tx_descriptor_init(&desc->async_tx,
-					&sh_chan->common);
-		desc->async_tx.tx_submit = sh_dmae_tx_submit;
-		desc->mark = DESC_IDLE;
-
-		list_add(&desc->node, &sh_chan->ld_free);
-		sh_chan->descs_allocated++;
-	}
-
-	if (!sh_chan->descs_allocated) {
-		ret = -ENOMEM;
-		goto edescalloc;
-	}
-
-	return sh_chan->descs_allocated;
-
-edescalloc:
-	if (param)
-		clear_bit(param->slave_id, sh_dmae_slave_used);
-etestused:
-efindslave:
-	chan->private = NULL;
-	return ret;
-}
-
-/*
- * sh_dma_free_chan_resources - Free all resources of the channel.
- */
-static void sh_dmae_free_chan_resources(struct dma_chan *chan)
-{
-	struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
-	struct sh_desc *desc, *_desc;
-	LIST_HEAD(list);
-
-	/* Protect against ISR */
-	spin_lock_irq(&sh_chan->desc_lock);
-	dmae_halt(sh_chan);
-	spin_unlock_irq(&sh_chan->desc_lock);
-
-	/* Now no new interrupts will occur */
-
-	/* Prepared and not submitted descriptors can still be on the queue */
-	if (!list_empty(&sh_chan->ld_queue))
-		sh_dmae_chan_ld_cleanup(sh_chan, true);
-
-	if (chan->private) {
-		/* The caller is holding dma_list_mutex */
-		struct sh_dmae_slave *param = chan->private;
-		clear_bit(param->slave_id, sh_dmae_slave_used);
-		chan->private = NULL;
-	}
-
-	spin_lock_irq(&sh_chan->desc_lock);
-
-	list_splice_init(&sh_chan->ld_free, &list);
-	sh_chan->descs_allocated = 0;
-
-	spin_unlock_irq(&sh_chan->desc_lock);
-
-	list_for_each_entry_safe(desc, _desc, &list, node)
-		kfree(desc);
-}
-
-/**
- * sh_dmae_add_desc - get, set up and return one transfer descriptor
- * @sh_chan:	DMA channel
- * @flags:	DMA transfer flags
- * @dest:	destination DMA address, incremented when direction equals
- *		DMA_DEV_TO_MEM
- * @src:	source DMA address, incremented when direction equals
- *		DMA_MEM_TO_DEV
- * @len:	DMA transfer length
- * @first:	if NULL, set to the current descriptor and cookie set to -EBUSY
- * @direction:	needed for slave DMA to decide which address to keep constant,
- *		equals DMA_MEM_TO_MEM for MEMCPY
- * Returns 0 or an error
- * Locks: called with desc_lock held
- */
-static struct sh_desc *sh_dmae_add_desc(struct sh_dmae_chan *sh_chan,
-	unsigned long flags, dma_addr_t *dest, dma_addr_t *src, size_t *len,
-	struct sh_desc **first, enum dma_transfer_direction direction)
-{
-	struct sh_desc *new;
-	size_t copy_size;
-
-	if (!*len)
-		return NULL;
-
-	/* Allocate the link descriptor from the free list */
-	new = sh_dmae_get_desc(sh_chan);
-	if (!new) {
-		dev_err(sh_chan->dev, "No free link descriptor available\n");
-		return NULL;
-	}
-
-	copy_size = min(*len, (size_t)SH_DMA_TCR_MAX + 1);
-
-	new->hw.sar = *src;
-	new->hw.dar = *dest;
-	new->hw.tcr = copy_size;
-
-	if (!*first) {
-		/* First desc */
-		new->async_tx.cookie = -EBUSY;
-		*first = new;
+		dmae_set_dmars(sh_chan, cfg->mid_rid);
+		dmae_set_chcr(sh_chan, cfg->chcr);
 	} else {
-		/* Other desc - invisible to the user */
-		new->async_tx.cookie = -EINVAL;
+		dmae_init(sh_chan);
 	}
-
-	dev_dbg(sh_chan->dev,
-		"chaining (%u/%u)@%x -> %x with %p, cookie %d, shift %d\n",
-		copy_size, *len, *src, *dest, &new->async_tx,
-		new->async_tx.cookie, sh_chan->xmit_shift);
-
-	new->mark = DESC_PREPARED;
-	new->async_tx.flags = flags;
-	new->direction = direction;
-
-	*len -= copy_size;
-	if (direction == DMA_MEM_TO_MEM || direction == DMA_MEM_TO_DEV)
-		*src += copy_size;
-	if (direction == DMA_MEM_TO_MEM || direction == DMA_DEV_TO_MEM)
-		*dest += copy_size;
-
-	return new;
 }
 
-/*
- * sh_dmae_prep_sg - prepare transfer descriptors from an SG list
- *
- * Common routine for public (MEMCPY) and slave DMA. The MEMCPY case is also
- * converted to scatter-gather to guarantee consistent locking and a correct
- * list manipulation. For slave DMA direction carries the usual meaning, and,
- * logically, the SG list is RAM and the addr variable contains slave address,
- * e.g., the FIFO I/O register. For MEMCPY direction equals DMA_MEM_TO_MEM
- * and the SG list contains only one element and points at the source buffer.
- */
-static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_chan,
-	struct scatterlist *sgl, unsigned int sg_len, dma_addr_t *addr,
-	enum dma_transfer_direction direction, unsigned long flags)
+static const struct sh_dmae_slave_config *dmae_find_slave(
+	struct sh_dmae_chan *sh_chan, struct sh_dmae_slave *slave)
 {
-	struct scatterlist *sg;
-	struct sh_desc *first = NULL, *new = NULL /* compiler... */;
-	LIST_HEAD(tx_list);
-	int chunks = 0;
-	unsigned long irq_flags;
+	struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
+	struct sh_dmae_pdata *pdata = shdev->pdata;
+	const struct sh_dmae_slave_config *cfg;
 	int i;
 
-	if (!sg_len)
+	if (slave->simple_slave.slave_id >= SH_DMA_SLAVE_NUMBER)
 		return NULL;
 
-	for_each_sg(sgl, sg, sg_len, i)
-		chunks += (sg_dma_len(sg) + SH_DMA_TCR_MAX) /
-			(SH_DMA_TCR_MAX + 1);
-
-	/* Have to lock the whole loop to protect against concurrent release */
-	spin_lock_irqsave(&sh_chan->desc_lock, irq_flags);
-
-	/*
-	 * Chaining:
-	 * first descriptor is what user is dealing with in all API calls, its
-	 *	cookie is at first set to -EBUSY, at tx-submit to a positive
-	 *	number
-	 * if more than one chunk is needed further chunks have cookie = -EINVAL
-	 * the last chunk, if not equal to the first, has cookie = -ENOSPC
-	 * all chunks are linked onto the tx_list head with their .node heads
-	 *	only during this function, then they are immediately spliced
-	 *	back onto the free list in form of a chain
-	 */
-	for_each_sg(sgl, sg, sg_len, i) {
-		dma_addr_t sg_addr = sg_dma_address(sg);
-		size_t len = sg_dma_len(sg);
-
-		if (!len)
-			goto err_get_desc;
-
-		do {
-			dev_dbg(sh_chan->dev, "Add SG #%d@%p[%d], dma %llx\n",
-				i, sg, len, (unsigned long long)sg_addr);
-
-			if (direction == DMA_DEV_TO_MEM)
-				new = sh_dmae_add_desc(sh_chan, flags,
-						&sg_addr, addr, &len, &first,
-						direction);
-			else
-				new = sh_dmae_add_desc(sh_chan, flags,
-						addr, &sg_addr, &len, &first,
-						direction);
-			if (!new)
-				goto err_get_desc;
-
-			new->chunks = chunks--;
-			list_add_tail(&new->node, &tx_list);
-		} while (len);
-	}
-
-	if (new != first)
-		new->async_tx.cookie = -ENOSPC;
-
-	/* Put them back on the free list, so, they don't get lost */
-	list_splice_tail(&tx_list, &sh_chan->ld_free);
-
-	spin_unlock_irqrestore(&sh_chan->desc_lock, irq_flags);
-
-	return &first->async_tx;
-
-err_get_desc:
-	list_for_each_entry(new, &tx_list, node)
-		new->mark = DESC_IDLE;
-	list_splice(&tx_list, &sh_chan->ld_free);
-
-	spin_unlock_irqrestore(&sh_chan->desc_lock, irq_flags);
+	for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++)
+		if (cfg->slave_id == slave->simple_slave.slave_id)
+			return cfg;
 
 	return NULL;
 }
 
-static struct dma_async_tx_descriptor *sh_dmae_prep_memcpy(
-	struct dma_chan *chan, dma_addr_t dma_dest, dma_addr_t dma_src,
-	size_t len, unsigned long flags)
-{
-	struct sh_dmae_chan *sh_chan;
-	struct scatterlist sg;
-
-	if (!chan || !len)
-		return NULL;
-
-	sh_chan = to_sh_chan(chan);
-
-	sg_init_table(&sg, 1);
-	sg_set_page(&sg, pfn_to_page(PFN_DOWN(dma_src)), len,
-		    offset_in_page(dma_src));
-	sg_dma_address(&sg) = dma_src;
-	sg_dma_len(&sg) = len;
-
-	return sh_dmae_prep_sg(sh_chan, &sg, 1, &dma_dest, DMA_MEM_TO_MEM,
-			       flags);
-}
-
-static struct dma_async_tx_descriptor *sh_dmae_prep_slave_sg(
-	struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
-	enum dma_transfer_direction direction, unsigned long flags)
+static int sh_dmae_set_slave(struct dma_simple_chan *schan,
+			     struct dma_simple_slave *sslave)
 {
-	struct sh_dmae_slave *param;
-	struct sh_dmae_chan *sh_chan;
-	dma_addr_t slave_addr;
-
-	if (!chan)
-		return NULL;
-
-	sh_chan = to_sh_chan(chan);
-	param = chan->private;
-
-	/* Someone calling slave DMA on a public channel? */
-	if (!param || !sg_len) {
-		dev_warn(sh_chan->dev, "%s: bad parameter: %p, %d, %d\n",
-			 __func__, param, sg_len, param ? param->slave_id : -1);
-		return NULL;
-	}
-
-	slave_addr = param->config->addr;
-
-	/*
-	 * if (param != NULL), this is a successfully requested slave channel,
-	 * therefore param->config != NULL too.
-	 */
-	return sh_dmae_prep_sg(sh_chan, sgl, sg_len, &slave_addr,
-			       direction, flags);
-}
-
-static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
-			   unsigned long arg)
-{
-	struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
-	unsigned long flags;
-
-	/* Only supports DMA_TERMINATE_ALL */
-	if (cmd != DMA_TERMINATE_ALL)
-		return -ENXIO;
-
-	if (!chan)
-		return -EINVAL;
-
-	spin_lock_irqsave(&sh_chan->desc_lock, flags);
-	dmae_halt(sh_chan);
-
-	if (!list_empty(&sh_chan->ld_queue)) {
-		/* Record partial transfer */
-		struct sh_desc *desc = list_entry(sh_chan->ld_queue.next,
-						  struct sh_desc, node);
-		desc->partial = (desc->hw.tcr - sh_dmae_readl(sh_chan, TCR)) <<
-			sh_chan->xmit_shift;
-	}
-	spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
+	struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
+						    simple_chan);
+	struct sh_dmae_slave *slave = container_of(sslave, struct sh_dmae_slave,
+						   simple_slave);
+	const struct sh_dmae_slave_config *cfg = dmae_find_slave(sh_chan, slave);
+	if (!cfg)
+		return -ENODEV;
 
-	sh_dmae_chan_ld_cleanup(sh_chan, true);
+	slave->config = cfg;
 
 	return 0;
 }
 
-static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all)
+static void dmae_halt(struct sh_dmae_chan *sh_chan)
 {
-	struct sh_desc *desc, *_desc;
-	/* Is the "exposed" head of a chain acked? */
-	bool head_acked = false;
-	dma_cookie_t cookie = 0;
-	dma_async_tx_callback callback = NULL;
-	void *param = NULL;
-	unsigned long flags;
-
-	spin_lock_irqsave(&sh_chan->desc_lock, flags);
-	list_for_each_entry_safe(desc, _desc, &sh_chan->ld_queue, node) {
-		struct dma_async_tx_descriptor *tx = &desc->async_tx;
-
-		BUG_ON(tx->cookie > 0 && tx->cookie != desc->cookie);
-		BUG_ON(desc->mark != DESC_SUBMITTED &&
-		       desc->mark != DESC_COMPLETED &&
-		       desc->mark != DESC_WAITING);
-
-		/*
-		 * queue is ordered, and we use this loop to (1) clean up all
-		 * completed descriptors, and to (2) update descriptor flags of
-		 * any chunks in a (partially) completed chain
-		 */
-		if (!all && desc->mark == DESC_SUBMITTED &&
-		    desc->cookie != cookie)
-			break;
-
-		if (tx->cookie > 0)
-			cookie = tx->cookie;
-
-		if (desc->mark == DESC_COMPLETED && desc->chunks == 1) {
-			if (sh_chan->completed_cookie != desc->cookie - 1)
-				dev_dbg(sh_chan->dev,
-					"Completing cookie %d, expected %d\n",
-					desc->cookie,
-					sh_chan->completed_cookie + 1);
-			sh_chan->completed_cookie = desc->cookie;
-		}
-
-		/* Call callback on the last chunk */
-		if (desc->mark == DESC_COMPLETED && tx->callback) {
-			desc->mark = DESC_WAITING;
-			callback = tx->callback;
-			param = tx->callback_param;
-			dev_dbg(sh_chan->dev, "descriptor #%d@%p on %d callback\n",
-				tx->cookie, tx, sh_chan->id);
-			BUG_ON(desc->chunks != 1);
-			break;
-		}
-
-		if (tx->cookie > 0 || tx->cookie == -EBUSY) {
-			if (desc->mark == DESC_COMPLETED) {
-				BUG_ON(tx->cookie < 0);
-				desc->mark = DESC_WAITING;
-			}
-			head_acked = async_tx_test_ack(tx);
-		} else {
-			switch (desc->mark) {
-			case DESC_COMPLETED:
-				desc->mark = DESC_WAITING;
-				/* Fall through */
-			case DESC_WAITING:
-				if (head_acked)
-					async_tx_ack(&desc->async_tx);
-			}
-		}
-
-		dev_dbg(sh_chan->dev, "descriptor %p #%d completed.\n",
-			tx, tx->cookie);
-
-		if (((desc->mark == DESC_COMPLETED ||
-		      desc->mark == DESC_WAITING) &&
-		     async_tx_test_ack(&desc->async_tx)) || all) {
-			/* Remove from ld_queue list */
-			desc->mark = DESC_IDLE;
-
-			list_move(&desc->node, &sh_chan->ld_free);
-
-			if (list_empty(&sh_chan->ld_queue)) {
-				dev_dbg(sh_chan->dev, "Bring down channel %d\n", sh_chan->id);
-				pm_runtime_put(sh_chan->dev);
-			}
-		}
-	}
-
-	if (all && !callback)
-		/*
-		 * Terminating and the loop completed normally: forgive
-		 * uncompleted cookies
-		 */
-		sh_chan->completed_cookie = sh_chan->common.cookie;
-
-	spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
-
-	if (callback)
-		callback(param);
+	struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
+	u32 chcr = chcr_read(sh_chan);
 
-	return callback;
+	chcr &= ~(CHCR_DE | CHCR_TE | shdev->chcr_ie_bit);
+	chcr_write(sh_chan, chcr);
 }
 
-/*
- * sh_chan_ld_cleanup - Clean up link descriptors
- *
- * This function cleans up the ld_queue of DMA channel.
- */
-static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all)
+static int sh_dmae_desc_setup(struct dma_simple_chan *schan,
+			      struct dma_simple_desc *sdesc,
+			      dma_addr_t src, dma_addr_t dst, size_t *len)
 {
-	while (__ld_cleanup(sh_chan, all))
-		;
-}
+	struct sh_dmae_desc *sh_desc = container_of(sdesc,
+					struct sh_dmae_desc, simple_desc);
 
-/* Called under spin_lock_irq(&sh_chan->desc_lock) */
-static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan)
-{
-	struct sh_desc *desc;
+	if (*len > schan->max_xfer_len)
+		*len = schan->max_xfer_len;
 
-	/* DMA work check */
-	if (dmae_is_busy(sh_chan))
-		return;
-
-	/* Find the first not transferred descriptor */
-	list_for_each_entry(desc, &sh_chan->ld_queue, node)
-		if (desc->mark == DESC_SUBMITTED) {
-			dev_dbg(sh_chan->dev, "Queue #%d to %d: %u@%x -> %x\n",
-				desc->async_tx.cookie, sh_chan->id,
-				desc->hw.tcr, desc->hw.sar, desc->hw.dar);
-			/* Get the ld start address from ld_queue */
-			dmae_set_reg(sh_chan, &desc->hw);
-			dmae_start(sh_chan);
-			break;
-		}
-}
+	sh_desc->hw.sar = src;
+	sh_desc->hw.dar = dst;
+	sh_desc->hw.tcr = *len;
 
-static void sh_dmae_memcpy_issue_pending(struct dma_chan *chan)
-{
-	struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
-
-	spin_lock_irq(&sh_chan->desc_lock);
-	if (sh_chan->pm_state == DMAE_PM_ESTABLISHED)
-		sh_chan_xfer_ld_queue(sh_chan);
-	else
-		sh_chan->pm_state = DMAE_PM_PENDING;
-	spin_unlock_irq(&sh_chan->desc_lock);
+	return 0;
 }
 
-static enum dma_status sh_dmae_tx_status(struct dma_chan *chan,
-					dma_cookie_t cookie,
-					struct dma_tx_state *txstate)
+static void sh_dmae_halt(struct dma_simple_chan *schan)
 {
-	struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
-	dma_cookie_t last_used;
-	dma_cookie_t last_complete;
-	enum dma_status status;
-	unsigned long flags;
-
-	sh_dmae_chan_ld_cleanup(sh_chan, false);
-
-	/* First read completed cookie to avoid a skew */
-	last_complete = sh_chan->completed_cookie;
-	rmb();
-	last_used = chan->cookie;
-	BUG_ON(last_complete < 0);
-	dma_set_tx_state(txstate, last_complete, last_used, 0);
-
-	spin_lock_irqsave(&sh_chan->desc_lock, flags);
-
-	status = dma_async_is_complete(cookie, last_complete, last_used);
-
-	/*
-	 * If we don't find cookie on the queue, it has been aborted and we have
-	 * to report error
-	 */
-	if (status != DMA_SUCCESS) {
-		struct sh_desc *desc;
-		status = DMA_ERROR;
-		list_for_each_entry(desc, &sh_chan->ld_queue, node)
-			if (desc->cookie == cookie) {
-				status = DMA_IN_PROGRESS;
-				break;
-			}
-	}
-
-	spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
-
-	return status;
+	struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
+						    simple_chan);
+	dmae_halt(sh_chan);
 }
 
-static irqreturn_t sh_dmae_interrupt(int irq, void *data)
+static bool sh_dmae_chan_irq(struct dma_simple_chan *schan, int irq)
 {
-	irqreturn_t ret = IRQ_NONE;
-	struct sh_dmae_chan *sh_chan = data;
-	u32 chcr;
-
-	spin_lock(&sh_chan->desc_lock);
+	struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
+						    simple_chan);
 
-	chcr = chcr_read(sh_chan);
-
-	if (chcr & CHCR_TE) {
-		/* DMA stop */
-		dmae_halt(sh_chan);
-
-		ret = IRQ_HANDLED;
-		tasklet_schedule(&sh_chan->tasklet);
-	}
+	if (!(chcr_read(sh_chan) & CHCR_TE))
+		return false;
 
-	spin_unlock(&sh_chan->desc_lock);
+	/* DMA stop */
+	dmae_halt(sh_chan);
 
-	return ret;
+	return true;
 }
 
 /* Called from error IRQ or NMI */
 static bool sh_dmae_reset(struct sh_dmae_device *shdev)
 {
-	unsigned int handled = 0;
-	int i;
+	bool ret;
 
 	/* halt the dma controller */
 	sh_dmae_ctl_stop(shdev);
 
 	/* We cannot detect, which channel caused the error, have to reset all */
-	for (i = 0; i < SH_DMAC_MAX_CHANNELS; i++) {
-		struct sh_dmae_chan *sh_chan = shdev->chan[i];
-		struct sh_desc *desc;
-		LIST_HEAD(dl);
-
-		if (!sh_chan)
-			continue;
-
-		spin_lock(&sh_chan->desc_lock);
-
-		/* Stop the channel */
-		dmae_halt(sh_chan);
-
-		list_splice_init(&sh_chan->ld_queue, &dl);
-
-		if (!list_empty(&dl)) {
-			dev_dbg(sh_chan->dev, "Bring down channel %d\n", sh_chan->id);
-			pm_runtime_put(sh_chan->dev);
-		}
-		sh_chan->pm_state = DMAE_PM_ESTABLISHED;
-
-		spin_unlock(&sh_chan->desc_lock);
-
-		/* Complete all  */
-		list_for_each_entry(desc, &dl, node) {
-			struct dma_async_tx_descriptor *tx = &desc->async_tx;
-			desc->mark = DESC_IDLE;
-			if (tx->callback)
-				tx->callback(tx->callback_param);
-		}
-
-		spin_lock(&sh_chan->desc_lock);
-		list_splice(&dl, &sh_chan->ld_free);
-		spin_unlock(&sh_chan->desc_lock);
-
-		handled++;
-	}
+	ret = dma_simple_reset(&shdev->simple_dev);
 
 	sh_dmae_rst(shdev);
 
-	return !!handled;
+	return ret;
 }
 
 static irqreturn_t sh_dmae_err(int irq, void *data)
@@ -1003,35 +405,24 @@ static irqreturn_t sh_dmae_err(int irq, void *data)
 	if (!(dmaor_read(shdev) & DMAOR_AE))
 		return IRQ_NONE;
 
-	sh_dmae_reset(data);
+	sh_dmae_reset(shdev);
 	return IRQ_HANDLED;
 }
 
-static void dmae_do_tasklet(unsigned long data)
+static bool sh_dmae_desc_completed(struct dma_simple_chan *schan,
+				   struct dma_simple_desc *sdesc)
 {
-	struct sh_dmae_chan *sh_chan = (struct sh_dmae_chan *)data;
-	struct sh_desc *desc;
+	struct sh_dmae_chan *sh_chan = container_of(schan,
+					struct sh_dmae_chan, simple_chan);
+	struct sh_dmae_desc *sh_desc = container_of(sdesc,
+					struct sh_dmae_desc, simple_desc);
 	u32 sar_buf = sh_dmae_readl(sh_chan, SAR);
 	u32 dar_buf = sh_dmae_readl(sh_chan, DAR);
 
-	spin_lock_irq(&sh_chan->desc_lock);
-	list_for_each_entry(desc, &sh_chan->ld_queue, node) {
-		if (desc->mark == DESC_SUBMITTED &&
-		    ((desc->direction == DMA_DEV_TO_MEM &&
-		      (desc->hw.dar + desc->hw.tcr) == dar_buf) ||
-		     (desc->hw.sar + desc->hw.tcr) == sar_buf)) {
-			dev_dbg(sh_chan->dev, "done #%d@%p dst %u\n",
-				desc->async_tx.cookie, &desc->async_tx,
-				desc->hw.dar);
-			desc->mark = DESC_COMPLETED;
-			break;
-		}
-	}
-	/* Next desc */
-	sh_chan_xfer_ld_queue(sh_chan);
-	spin_unlock_irq(&sh_chan->desc_lock);
-
-	sh_dmae_chan_ld_cleanup(sh_chan, false);
+	return	(sdesc->direction == DMA_DEV_TO_MEM &&
+		 (sh_desc->hw.dar + sh_desc->hw.tcr) == dar_buf) ||
+		(sdesc->direction != DMA_DEV_TO_MEM &&
+		 (sh_desc->hw.sar + sh_desc->hw.tcr) == sar_buf);
 }
 
 static bool sh_dmae_nmi_notify(struct sh_dmae_device *shdev)
@@ -1085,96 +476,174 @@ static struct notifier_block sh_dmae_nmi_notifier __read_mostly = {
 static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id,
 					int irq, unsigned long flags)
 {
-	int err;
 	const struct sh_dmae_channel *chan_pdata = &shdev->pdata->channel[id];
-	struct platform_device *pdev = to_platform_device(shdev->common.dev);
-	struct sh_dmae_chan *new_sh_chan;
+	struct dma_simple_dev *sdev = &shdev->simple_dev;
+	struct platform_device *pdev = to_platform_device(sdev->dma_dev.dev);
+	struct sh_dmae_chan *sh_chan;
+	struct dma_simple_chan *schan;
+	int err;
 
-	/* alloc channel */
-	new_sh_chan = kzalloc(sizeof(struct sh_dmae_chan), GFP_KERNEL);
-	if (!new_sh_chan) {
-		dev_err(shdev->common.dev,
+	sh_chan = kzalloc(sizeof(struct sh_dmae_chan), GFP_KERNEL);
+	if (!sh_chan) {
+		dev_err(sdev->dma_dev.dev,
 			"No free memory for allocating dma channels!\n");
 		return -ENOMEM;
 	}
 
-	new_sh_chan->pm_state = DMAE_PM_ESTABLISHED;
+	schan = &sh_chan->simple_chan;
+	schan->max_xfer_len = SH_DMA_TCR_MAX + 1;
 
-	/* reference struct dma_device */
-	new_sh_chan->common.device = &shdev->common;
+	dma_simple_chan_probe(sdev, schan, id);
 
-	new_sh_chan->dev = shdev->common.dev;
-	new_sh_chan->id = id;
-	new_sh_chan->irq = irq;
-	new_sh_chan->base = shdev->chan_reg + chan_pdata->offset / sizeof(u32);
-
-	/* Init DMA tasklet */
-	tasklet_init(&new_sh_chan->tasklet, dmae_do_tasklet,
-			(unsigned long)new_sh_chan);
-
-	spin_lock_init(&new_sh_chan->desc_lock);
-
-	/* Init descripter manage list */
-	INIT_LIST_HEAD(&new_sh_chan->ld_queue);
-	INIT_LIST_HEAD(&new_sh_chan->ld_free);
-
-	/* Add the channel to DMA device channel list */
-	list_add_tail(&new_sh_chan->common.device_node,
-			&shdev->common.channels);
-	shdev->common.chancnt++;
+	sh_chan->base = shdev->chan_reg + chan_pdata->offset / sizeof(u32);
 
+	/* set up channel irq */
 	if (pdev->id >= 0)
-		snprintf(new_sh_chan->dev_id, sizeof(new_sh_chan->dev_id),
-			 "sh-dmae%d.%d", pdev->id, new_sh_chan->id);
+		snprintf(sh_chan->dev_id, sizeof(sh_chan->dev_id),
+			 "sh-dmae%d.%d", pdev->id, id);
 	else
-		snprintf(new_sh_chan->dev_id, sizeof(new_sh_chan->dev_id),
-			 "sh-dma%d", new_sh_chan->id);
+		snprintf(sh_chan->dev_id, sizeof(sh_chan->dev_id),
+			 "sh-dma%d", id);
 
-	/* set up channel irq */
-	err = request_irq(irq, &sh_dmae_interrupt, flags,
-			  new_sh_chan->dev_id, new_sh_chan);
+	err = dma_simple_request_irq(schan, irq, flags, sh_chan->dev_id);
 	if (err) {
-		dev_err(shdev->common.dev, "DMA channel %d request_irq error "
-			"with return %d\n", id, err);
+		dev_err(sdev->dma_dev.dev,
+			"DMA channel %d request_irq error %d\n",
+			id, err);
 		goto err_no_irq;
 	}
 
-	shdev->chan[id] = new_sh_chan;
+	shdev->chan[id] = sh_chan;
 	return 0;
 
 err_no_irq:
 	/* remove from dmaengine device node */
-	list_del(&new_sh_chan->common.device_node);
-	kfree(new_sh_chan);
+	dma_simple_chan_remove(schan);
+	kfree(sh_chan);
 	return err;
 }
 
 static void sh_dmae_chan_remove(struct sh_dmae_device *shdev)
 {
+	struct dma_device *dma_dev = &shdev->simple_dev.dma_dev;
+	struct dma_simple_chan *schan;
 	int i;
 
-	for (i = shdev->common.chancnt - 1 ; i >= 0 ; i--) {
-		if (shdev->chan[i]) {
-			struct sh_dmae_chan *sh_chan = shdev->chan[i];
+	dma_simple_for_each_chan(schan, &shdev->simple_dev, i) {
+		struct sh_dmae_chan *sh_chan = container_of(schan,
+					struct sh_dmae_chan, simple_chan);
+		BUG_ON(!schan);
 
-			free_irq(sh_chan->irq, sh_chan);
+		dma_simple_free_irq(&sh_chan->simple_chan);
 
-			list_del(&sh_chan->common.device_node);
-			kfree(sh_chan);
-			shdev->chan[i] = NULL;
+		dma_simple_chan_remove(schan);
+		kfree(sh_chan);
+	}
+	dma_dev->chancnt = 0;
+}
+
+static void sh_dmae_shutdown(struct platform_device *pdev)
+{
+	struct sh_dmae_device *shdev = platform_get_drvdata(pdev);
+	sh_dmae_ctl_stop(shdev);
+}
+
+static int sh_dmae_runtime_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int sh_dmae_runtime_resume(struct device *dev)
+{
+	struct sh_dmae_device *shdev = dev_get_drvdata(dev);
+
+	return sh_dmae_rst(shdev);
+}
+
+#ifdef CONFIG_PM
+static int sh_dmae_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int sh_dmae_resume(struct device *dev)
+{
+	struct sh_dmae_device *shdev = dev_get_drvdata(dev);
+	int i, ret;
+
+	ret = sh_dmae_rst(shdev);
+	if (ret < 0)
+		dev_err(dev, "Failed to reset!\n");
+
+	for (i = 0; i < shdev->pdata->channel_num; i++) {
+		struct sh_dmae_chan *sh_chan = shdev->chan[i];
+		struct sh_dmae_slave *param = sh_chan->simple_chan.dma_chan.private;
+
+		if (!sh_chan->simple_chan.desc_num)
+			continue;
+
+		if (param) {
+			const struct sh_dmae_slave_config *cfg = param->config;
+			dmae_set_dmars(sh_chan, cfg->mid_rid);
+			dmae_set_chcr(sh_chan, cfg->chcr);
+		} else {
+			dmae_init(sh_chan);
 		}
 	}
-	shdev->common.chancnt = 0;
+
+	return 0;
+}
+#else
+#define sh_dmae_suspend NULL
+#define sh_dmae_resume NULL
+#endif
+
+const struct dev_pm_ops sh_dmae_pm = {
+	.suspend		= sh_dmae_suspend,
+	.resume			= sh_dmae_resume,
+	.runtime_suspend	= sh_dmae_runtime_suspend,
+	.runtime_resume		= sh_dmae_runtime_resume,
+};
+
+static dma_addr_t sh_dmae_slave_addr(struct dma_simple_chan *schan)
+{
+	struct sh_dmae_slave *param = schan->dma_chan.private;
+
+	/*
+	 * Implicit BUG_ON(!param)
+	 * if (param != NULL), this is a successfully requested slave channel,
+	 * therefore param->config != NULL too.
+	 */
+	return param->config->addr;
+}
+
+static struct dma_simple_desc *sh_dmae_embedded_desc(void *buf, int i)
+{
+	return &((struct sh_dmae_desc *)buf)[i].simple_desc;
 }
 
-static int __init sh_dmae_probe(struct platform_device *pdev)
+static const struct dma_simple_ops sh_dmae_simple_ops = {
+	.desc_completed = sh_dmae_desc_completed,
+	.halt_channel = sh_dmae_halt,
+	.channel_busy = sh_dmae_channel_busy,
+	.slave_addr = sh_dmae_slave_addr,
+	.desc_setup = sh_dmae_desc_setup,
+	.set_slave = sh_dmae_set_slave,
+	.setup_xfer = sh_dmae_setup_xfer,
+	.start_xfer = sh_dmae_start_xfer,
+	.embedded_desc = sh_dmae_embedded_desc,
+	.chan_irq = sh_dmae_chan_irq,
+};
+
+static int __devinit sh_dmae_probe(struct platform_device *pdev)
 {
 	struct sh_dmae_pdata *pdata = pdev->dev.platform_data;
 	unsigned long irqflags = IRQF_DISABLED,
-		chan_flag[SH_DMAC_MAX_CHANNELS] = {};
-	int errirq, chan_irq[SH_DMAC_MAX_CHANNELS];
+		chan_flag[SH_DMAE_MAX_CHANNELS] = {};
+	int errirq, chan_irq[SH_DMAE_MAX_CHANNELS];
 	int err, i, irq_cnt = 0, irqres = 0, irq_cap = 0;
 	struct sh_dmae_device *shdev;
+	struct dma_device *dma_dev;
 	struct resource *chan, *dmars, *errirq_res, *chanirq_res;
 
 	/* get platform data */
@@ -1222,6 +691,8 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
 		goto ealloc;
 	}
 
+	dma_dev = &shdev->simple_dev.dma_dev;
+
 	shdev->chan_reg = ioremap(chan->start, resource_size(chan));
 	if (!shdev->chan_reg)
 		goto emapchan;
@@ -1231,8 +702,23 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
 			goto emapdmars;
 	}
 
+	if (!pdata->slave_only)
+		dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask);
+	if (pdata->slave && pdata->slave_num)
+		dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
+
+	/* Default transfer size of 32 bytes requires 32-byte alignment */
+	dma_dev->copy_align = LOG2_DEFAULT_XFER_SIZE;
+
+	shdev->simple_dev.ops = &sh_dmae_simple_ops;
+	shdev->simple_dev.desc_size = sizeof(struct sh_dmae_desc);
+	err = dma_simple_init(&pdev->dev, &shdev->simple_dev,
+			      pdata->channel_num);
+	if (err < 0)
+		goto esimple;
+
 	/* platform data */
-	shdev->pdata = pdata;
+	shdev->pdata = pdev->dev.platform_data;
 
 	if (pdata->chcr_offset)
 		shdev->chcr_offset = pdata->chcr_offset;
@@ -1246,10 +732,10 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, shdev);
 
-	shdev->common.dev = &pdev->dev;
-
 	pm_runtime_enable(&pdev->dev);
-	pm_runtime_get_sync(&pdev->dev);
+	err = pm_runtime_get_sync(&pdev->dev);
+	if (err < 0)
+		dev_err(&pdev->dev, "%s(): GET = %d\n", __func__, err);
 
 	spin_lock_irq(&sh_dmae_lock);
 	list_add_tail_rcu(&shdev->node, &sh_dmae_devices);
@@ -1260,27 +746,6 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
 	if (err)
 		goto rst_err;
 
-	INIT_LIST_HEAD(&shdev->common.channels);
-
-	if (!pdata->slave_only)
-		dma_cap_set(DMA_MEMCPY, shdev->common.cap_mask);
-	if (pdata->slave && pdata->slave_num)
-		dma_cap_set(DMA_SLAVE, shdev->common.cap_mask);
-
-	shdev->common.device_alloc_chan_resources
-		= sh_dmae_alloc_chan_resources;
-	shdev->common.device_free_chan_resources = sh_dmae_free_chan_resources;
-	shdev->common.device_prep_dma_memcpy = sh_dmae_prep_memcpy;
-	shdev->common.device_tx_status = sh_dmae_tx_status;
-	shdev->common.device_issue_pending = sh_dmae_memcpy_issue_pending;
-
-	/* Compulsory for DMA_SLAVE fields */
-	shdev->common.device_prep_slave_sg = sh_dmae_prep_slave_sg;
-	shdev->common.device_control = sh_dmae_control;
-
-	/* Default transfer size of 32 bytes requires 32-byte alignment */
-	shdev->common.copy_align = LOG2_DEFAULT_XFER_SIZE;
-
 #if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
 	chanirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
 
@@ -1312,7 +777,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
 	    !platform_get_resource(pdev, IORESOURCE_IRQ, 1)) {
 		/* Special case - all multiplexed */
 		for (; irq_cnt < pdata->channel_num; irq_cnt++) {
-			if (irq_cnt < SH_DMAC_MAX_CHANNELS) {
+			if (irq_cnt < SH_DMAE_MAX_CHANNELS) {
 				chan_irq[irq_cnt] = chanirq_res->start;
 				chan_flag[irq_cnt] = IRQF_SHARED;
 			} else {
@@ -1323,7 +788,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
 	} else {
 		do {
 			for (i = chanirq_res->start; i <= chanirq_res->end; i++) {
-				if (irq_cnt >= SH_DMAC_MAX_CHANNELS) {
+				if (irq_cnt >= SH_DMAE_MAX_CHANNELS) {
 					irq_cap = 1;
 					break;
 				}
@@ -1339,7 +804,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
 				chan_irq[irq_cnt++] = i;
 			}
 
-			if (irq_cnt >= SH_DMAC_MAX_CHANNELS)
+			if (irq_cnt >= SH_DMAE_MAX_CHANNELS)
 				break;
 
 			chanirq_res = platform_get_resource(pdev,
@@ -1357,14 +822,19 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
 	if (irq_cap)
 		dev_notice(&pdev->dev, "Attempting to register %d DMA "
 			   "channels when a maximum of %d are supported.\n",
-			   pdata->channel_num, SH_DMAC_MAX_CHANNELS);
+			   pdata->channel_num, SH_DMAE_MAX_CHANNELS);
 
 	pm_runtime_put(&pdev->dev);
 
-	dma_async_device_register(&shdev->common);
+	err = dma_async_device_register(&shdev->simple_dev.dma_dev);
+	if (err < 0)
+		goto edmadevreg;
 
 	return err;
 
+edmadevreg:
+	pm_runtime_get(&pdev->dev);
+
 chan_probe_err:
 	sh_dmae_chan_remove(shdev);
 
@@ -1380,10 +850,11 @@ rst_err:
 	pm_runtime_put(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
 
+	platform_set_drvdata(pdev, NULL);
+	dma_simple_cleanup(&shdev->simple_dev);
+esimple:
 	if (dmars)
 		iounmap(shdev->dmars);
-
-	platform_set_drvdata(pdev, NULL);
 emapdmars:
 	iounmap(shdev->chan_reg);
 	synchronize_rcu();
@@ -1398,13 +869,14 @@ ermrdmars:
 	return err;
 }
 
-static int __exit sh_dmae_remove(struct platform_device *pdev)
+static int __devexit sh_dmae_remove(struct platform_device *pdev)
 {
 	struct sh_dmae_device *shdev = platform_get_drvdata(pdev);
+	struct dma_device *dma_dev = &shdev->simple_dev.dma_dev;
 	struct resource *res;
 	int errirq = platform_get_irq(pdev, 0);
 
-	dma_async_device_unregister(&shdev->common);
+	dma_async_device_unregister(dma_dev);
 
 	if (errirq > 0)
 		free_irq(errirq, shdev);
@@ -1413,11 +885,11 @@ static int __exit sh_dmae_remove(struct platform_device *pdev)
 	list_del_rcu(&shdev->node);
 	spin_unlock_irq(&sh_dmae_lock);
 
-	/* channel data remove */
-	sh_dmae_chan_remove(shdev);
-
 	pm_runtime_disable(&pdev->dev);
 
+	sh_dmae_chan_remove(shdev);
+	dma_simple_cleanup(&shdev->simple_dev);
+
 	if (shdev->dmars)
 		iounmap(shdev->dmars);
 	iounmap(shdev->chan_reg);
@@ -1437,77 +909,14 @@ static int __exit sh_dmae_remove(struct platform_device *pdev)
 	return 0;
 }
 
-static void sh_dmae_shutdown(struct platform_device *pdev)
-{
-	struct sh_dmae_device *shdev = platform_get_drvdata(pdev);
-	sh_dmae_ctl_stop(shdev);
-}
-
-static int sh_dmae_runtime_suspend(struct device *dev)
-{
-	return 0;
-}
-
-static int sh_dmae_runtime_resume(struct device *dev)
-{
-	struct sh_dmae_device *shdev = dev_get_drvdata(dev);
-
-	return sh_dmae_rst(shdev);
-}
-
-#ifdef CONFIG_PM
-static int sh_dmae_suspend(struct device *dev)
-{
-	return 0;
-}
-
-static int sh_dmae_resume(struct device *dev)
-{
-	struct sh_dmae_device *shdev = dev_get_drvdata(dev);
-	int i, ret;
-
-	ret = sh_dmae_rst(shdev);
-	if (ret < 0)
-		dev_err(dev, "Failed to reset!\n");
-
-	for (i = 0; i < shdev->pdata->channel_num; i++) {
-		struct sh_dmae_chan *sh_chan = shdev->chan[i];
-		struct sh_dmae_slave *param = sh_chan->common.private;
-
-		if (!sh_chan->descs_allocated)
-			continue;
-
-		if (param) {
-			const struct sh_dmae_slave_config *cfg = param->config;
-			dmae_set_dmars(sh_chan, cfg->mid_rid);
-			dmae_set_chcr(sh_chan, cfg->chcr);
-		} else {
-			dmae_init(sh_chan);
-		}
-	}
-
-	return 0;
-}
-#else
-#define sh_dmae_suspend NULL
-#define sh_dmae_resume NULL
-#endif
-
-const struct dev_pm_ops sh_dmae_pm = {
-	.suspend		= sh_dmae_suspend,
-	.resume			= sh_dmae_resume,
-	.runtime_suspend	= sh_dmae_runtime_suspend,
-	.runtime_resume		= sh_dmae_runtime_resume,
-};
-
 static struct platform_driver sh_dmae_driver = {
-	.remove		= __exit_p(sh_dmae_remove),
-	.shutdown	= sh_dmae_shutdown,
-	.driver = {
+	.driver 	= {
 		.owner	= THIS_MODULE,
-		.name	= "sh-dma-engine",
 		.pm	= &sh_dmae_pm,
+		.name	= SH_DMAE_DRV_NAME,
 	},
+	.remove		= __devexit_p(sh_dmae_remove),
+	.shutdown	= sh_dmae_shutdown,
 };
 
 static int __init sh_dmae_init(void)
@@ -1531,5 +940,6 @@ module_exit(sh_dmae_exit);
 
 MODULE_AUTHOR("Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>");
 MODULE_DESCRIPTION("Renesas SH DMA Engine driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:sh-dma-engine");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1.0");
+MODULE_ALIAS("platform:" SH_DMAE_DRV_NAME);
diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h
index 2b55a27..8bffc19 100644
--- a/drivers/dma/shdma.h
+++ b/drivers/dma/shdma.h
@@ -13,43 +13,27 @@
 #ifndef __DMA_SHDMA_H
 #define __DMA_SHDMA_H
 
+#include <linux/dma-simple.h>
 #include <linux/dmaengine.h>
 #include <linux/interrupt.h>
 #include <linux/list.h>
 
-#define SH_DMAC_MAX_CHANNELS 20
-#define SH_DMA_SLAVE_NUMBER 256
-#define SH_DMA_TCR_MAX 0x00FFFFFF	/* 16MB */
+#define SH_DMAE_MAX_CHANNELS 20
+#define SH_DMAE_TCR_MAX 0x00FFFFFF	/* 16MB */
 
 struct device;
 
-enum dmae_pm_state {
-	DMAE_PM_ESTABLISHED,
-	DMAE_PM_BUSY,
-	DMAE_PM_PENDING,
-};
-
 struct sh_dmae_chan {
-	dma_cookie_t completed_cookie;	/* The maximum cookie completed */
-	spinlock_t desc_lock;		/* Descriptor operation lock */
-	struct list_head ld_queue;	/* Link descriptors queue */
-	struct list_head ld_free;	/* Link descriptors free */
-	struct dma_chan common;		/* DMA common channel */
-	struct device *dev;		/* Channel device */
-	struct tasklet_struct tasklet;	/* Tasklet */
-	int descs_allocated;		/* desc count */
+	struct dma_simple_chan simple_chan;
 	int xmit_shift;			/* log_2(bytes_per_xfer) */
-	int irq;
-	int id;				/* Raw id of this channel */
 	u32 __iomem *base;
 	char dev_id[16];		/* unique name per DMAC of channel */
 	int pm_error;
-	enum dmae_pm_state pm_state;
 };
 
 struct sh_dmae_device {
-	struct dma_device common;
-	struct sh_dmae_chan *chan[SH_DMAC_MAX_CHANNELS];
+	struct dma_simple_dev simple_dev;
+	struct sh_dmae_chan *chan[SH_DMAE_MAX_CHANNELS];
 	struct sh_dmae_pdata *pdata;
 	struct list_head node;
 	u32 __iomem *chan_reg;
@@ -58,10 +42,21 @@ struct sh_dmae_device {
 	u32 chcr_ie_bit;
 };
 
-#define to_sh_chan(chan) container_of(chan, struct sh_dmae_chan, common)
+struct sh_dmae_regs {
+	u32 sar; /* SAR / source address */
+	u32 dar; /* DAR / destination address */
+	u32 tcr; /* TCR / transfer count */
+};
+
+struct sh_dmae_desc {
+	struct sh_dmae_regs hw;
+	struct dma_simple_desc simple_desc;
+};
+
+#define to_sh_chan(chan) container_of(chan, struct sh_dmae_chan, simple_chan)
 #define to_sh_desc(lh) container_of(lh, struct sh_desc, node)
 #define tx_to_sh_desc(tx) container_of(tx, struct sh_desc, async_tx)
-#define to_sh_dev(chan) container_of(chan->common.device,\
-				     struct sh_dmae_device, common)
+#define to_sh_dev(chan) container_of(chan->simple_chan.dma_chan.device,\
+				     struct sh_dmae_device, simple_dev.dma_dev)
 
 #endif	/* __DMA_SHDMA_H */
diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h
index ed2aa1e..4f22466 100644
--- a/include/linux/sh_dma.h
+++ b/include/linux/sh_dma.h
@@ -13,34 +13,14 @@
 #include <linux/dma-simple.h>
 #include <linux/dmaengine.h>
 #include <linux/list.h>
+#include <linux/types.h>
 
-/* Used by slave DMA clients to request DMA to/from a specific peripheral */
-struct sh_dmae_slave {
-	union {
-		unsigned int		slave_id; /* Set by the platform */
-		struct dma_simple_slave	simple_slave;
-	};
-	struct device			*dma_dev; /* Set by the platform */
-	const struct sh_dmae_slave_config	*config;  /* Set by the driver */
-};
-
-struct sh_dmae_regs {
-	u32 sar; /* SAR / source address */
-	u32 dar; /* DAR / destination address */
-	u32 tcr; /* TCR / transfer count */
-};
-
-struct sh_desc {
-	struct sh_dmae_regs hw;
-	struct list_head node;
-	struct dma_async_tx_descriptor async_tx;
-	enum dma_transfer_direction direction;
-	dma_cookie_t cookie;
-	size_t partial;
-	int chunks;
-	int mark;
-};
+struct device;
 
+/*
+ * Supplied by platforms to specify, how a DMA channel has to be configured for
+ * a certain peripheral
+ */
 struct sh_dmae_slave_config {
 	unsigned int			slave_id;
 	dma_addr_t			addr;
@@ -48,6 +28,13 @@ struct sh_dmae_slave_config {
 	char				mid_rid;
 };
 
+/* Used by slave DMA clients to request DMA to/from a specific peripheral */
+struct sh_dmae_slave {
+	struct dma_simple_slave		simple_slave;	/* Set by the platform */
+	struct device			*dma_dev;	/* Set by the platform */
+	const struct sh_dmae_slave_config *config;	/* Set by the driver */
+};
+
 struct sh_dmae_channel {
 	unsigned int	offset;
 	unsigned int	dmars;
-- 
1.7.2.5


^ permalink raw reply related

* [PATCH 5/7 v2] serial: sh-sci: prepare for conversion to simple DMA
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda, linux-mmc,
	alsa-devel, linux-serial, Paul Mundt
In-Reply-To: <1327589784-4287-1-git-send-email-g.liakhovetski@gmx.de>

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
 drivers/tty/serial/sh-sci.c |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 7508579..8b99aac 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1574,9 +1574,9 @@ static bool filter(struct dma_chan *chan, void *slave)
 	struct sh_dmae_slave *param = slave;
 
 	dev_dbg(chan->device->dev, "%s: slave ID %d\n", __func__,
-		param->slave_id);
+		param->simple_slave.slave_id);
 
-	chan->private = param;
+	chan->private = &param->simple_slave;
 	return true;
 }
 
@@ -1615,7 +1615,7 @@ static void sci_request_dma(struct uart_port *port)
 	param = &s->param_tx;
 
 	/* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */
-	param->slave_id = s->cfg->dma_slave_tx;
+	param->simple_slave.slave_id = s->cfg->dma_slave_tx;
 
 	s->cookie_tx = -EINVAL;
 	chan = dma_request_channel(mask, filter, param);
@@ -1643,7 +1643,7 @@ static void sci_request_dma(struct uart_port *port)
 	param = &s->param_rx;
 
 	/* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */
-	param->slave_id = s->cfg->dma_slave_rx;
+	param->simple_slave.slave_id = s->cfg->dma_slave_rx;
 
 	chan = dma_request_channel(mask, filter, param);
 	dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan);
-- 
1.7.2.5


^ permalink raw reply related

* [PATCH 1/3] ftdi_sio: Add more identifiers
From: Alan Cox @ 2012-01-26 17:41 UTC (permalink / raw)
  To: greg, linux-serial

From: Alan Cox <alan@linux.intel.com>

0x04d8, 0x000a: Hornby Elite

Signed-off-by: Alan Cox <alan@linux.intel.com>
---

 drivers/usb/serial/ftdi_sio.c     |    1 +
 drivers/usb/serial/ftdi_sio_ids.h |    6 ++++++
 2 files changed, 7 insertions(+), 0 deletions(-)


diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 104aff5..ad654f8 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -797,6 +797,7 @@ static struct usb_device_id id_table_combined [] = {
 		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
 	{ USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID),
 		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(HORNBY_VID, HORNBY_ELITE_PID) },
 	{ USB_DEVICE(JETI_VID, JETI_SPC1201_PID) },
 	{ USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID),
 		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 09237ff..f994503 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -532,6 +532,12 @@
 #define ADI_GNICEPLUS_PID	0xF001
 
 /*
+ * Hornby Elite
+ */
+#define HORNBY_VID		0x04D8
+#define HORNBY_ELITE_PID	0x000A
+
+/*
  * RATOC REX-USB60F
  */
 #define RATOC_VENDOR_ID		0x0584


^ permalink raw reply related

* [PATCH 2/3] serial: Kill off NO_IRQ
From: Alan Cox @ 2012-01-26 17:44 UTC (permalink / raw)
  To: greg, linux-serial
In-Reply-To: <20120126174121.16858.13827.stgit@bob.linux.org.uk>

From: Alan Cox <alan@linux.intel.com>

We transform the offenders into a test of irq <= 0 which will be ok while
the ARM people get their platform sorted. Once that is done (or in a while
if they don't do it anyway) then we will change them all to !irq checks.

For arch specific drivers that are already using NO_IRQ = 0 we just test
against zero so we don't need to re-review them later.

Signed-off-by: Alan Cox <alan@linux.intel.com>
---

 drivers/tty/hvc/hvc_beat.c         |    2 +-
 drivers/tty/hvc/hvc_rtas.c         |    2 +-
 drivers/tty/hvc/hvc_udbg.c         |    2 +-
 drivers/tty/hvc/hvc_xen.c          |    2 +-
 drivers/tty/hvc/hvcs.c             |    4 ++--
 drivers/tty/hvc/hvsi.c             |    2 +-
 drivers/tty/serial/21285.c         |    4 ++--
 drivers/tty/serial/8250/8250.c     |   21 +++++++--------------
 drivers/tty/serial/8250/m32r_sio.c |   11 ++---------
 drivers/tty/serial/mpc52xx_uart.c  |    4 ++--
 drivers/tty/serial/mux.c           |    2 +-
 drivers/tty/serial/pmac_zilog.c    |    2 +-
 drivers/tty/serial/sunzilog.c      |   10 +++++-----
 drivers/tty/serial/ucc_uart.c      |    2 +-
 drivers/tty/serial/vr41xx_siu.c    |    4 ++--
 15 files changed, 30 insertions(+), 44 deletions(-)


diff --git a/drivers/tty/hvc/hvc_beat.c b/drivers/tty/hvc/hvc_beat.c
index 5fe4631..1560d23 100644
--- a/drivers/tty/hvc/hvc_beat.c
+++ b/drivers/tty/hvc/hvc_beat.c
@@ -113,7 +113,7 @@ static int __init hvc_beat_init(void)
 	if (!firmware_has_feature(FW_FEATURE_BEAT))
 		return -ENODEV;
 
-	hp = hvc_alloc(0, NO_IRQ, &hvc_beat_get_put_ops, 16);
+	hp = hvc_alloc(0, 0, &hvc_beat_get_put_ops, 16);
 	if (IS_ERR(hp))
 		return PTR_ERR(hp);
 	hvc_beat_dev = hp;
diff --git a/drivers/tty/hvc/hvc_rtas.c b/drivers/tty/hvc/hvc_rtas.c
index 61c4a61..0069bb8 100644
--- a/drivers/tty/hvc/hvc_rtas.c
+++ b/drivers/tty/hvc/hvc_rtas.c
@@ -94,7 +94,7 @@ static int __init hvc_rtas_init(void)
 
 	/* Allocate an hvc_struct for the console device we instantiated
 	 * earlier.  Save off hp so that we can return it on exit */
-	hp = hvc_alloc(hvc_rtas_cookie, NO_IRQ, &hvc_rtas_get_put_ops, 16);
+	hp = hvc_alloc(hvc_rtas_cookie, 0, &hvc_rtas_get_put_ops, 16);
 	if (IS_ERR(hp))
 		return PTR_ERR(hp);
 
diff --git a/drivers/tty/hvc/hvc_udbg.c b/drivers/tty/hvc/hvc_udbg.c
index b0957e6..4c9b13e 100644
--- a/drivers/tty/hvc/hvc_udbg.c
+++ b/drivers/tty/hvc/hvc_udbg.c
@@ -69,7 +69,7 @@ static int __init hvc_udbg_init(void)
 
 	BUG_ON(hvc_udbg_dev);
 
-	hp = hvc_alloc(0, NO_IRQ, &hvc_udbg_ops, 16);
+	hp = hvc_alloc(0, 0, &hvc_udbg_ops, 16);
 	if (IS_ERR(hp))
 		return PTR_ERR(hp);
 
diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c
index 52fdf60..a1b0a75 100644
--- a/drivers/tty/hvc/hvc_xen.c
+++ b/drivers/tty/hvc/hvc_xen.c
@@ -176,7 +176,7 @@ static int __init xen_hvc_init(void)
 		xencons_irq = bind_evtchn_to_irq(xen_start_info->console.domU.evtchn);
 	}
 	if (xencons_irq < 0)
-		xencons_irq = 0; /* NO_IRQ */
+		xencons_irq = 0;
 	else
 		irq_set_noprobe(xencons_irq);
 
diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c
index b9040be..df7e7a0 100644
--- a/drivers/tty/hvc/hvcs.c
+++ b/drivers/tty/hvc/hvcs.c
@@ -1203,7 +1203,7 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
 {
 	struct hvcs_struct *hvcsd;
 	unsigned long flags;
-	int irq = NO_IRQ;
+	int irq;
 
 	/*
 	 * Is someone trying to close the file associated with this device after
@@ -1264,7 +1264,7 @@ static void hvcs_hangup(struct tty_struct * tty)
 	struct hvcs_struct *hvcsd = tty->driver_data;
 	unsigned long flags;
 	int temp_open_count;
-	int irq = NO_IRQ;
+	int irq;
 
 	spin_lock_irqsave(&hvcsd->lock, flags);
 	/* Preserve this so that we know how many kref refs to put */
diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c
index cdfa3e0..1b5f28b 100644
--- a/drivers/tty/hvc/hvsi.c
+++ b/drivers/tty/hvc/hvsi.c
@@ -1237,7 +1237,7 @@ static int __init hvsi_console_init(void)
 		hp->state = HVSI_CLOSED;
 		hp->vtermno = *vtermno;
 		hp->virq = irq_create_mapping(NULL, irq[0]);
-		if (hp->virq == NO_IRQ) {
+		if (hp->virq == 0) {
 			printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n",
 				__func__, irq[0]);
 			continue;
diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c
index 1b37626..f899996 100644
--- a/drivers/tty/serial/21285.c
+++ b/drivers/tty/serial/21285.c
@@ -331,7 +331,7 @@ static int serial21285_verify_port(struct uart_port *port, struct serial_struct
 	int ret = 0;
 	if (ser->type != PORT_UNKNOWN && ser->type != PORT_21285)
 		ret = -EINVAL;
-	if (ser->irq != NO_IRQ)
+	if (ser->irq <= 0)
 		ret = -EINVAL;
 	if (ser->baud_base != port->uartclk / 16)
 		ret = -EINVAL;
@@ -360,7 +360,7 @@ static struct uart_ops serial21285_ops = {
 static struct uart_port serial21285_port = {
 	.mapbase	= 0x42000160,
 	.iotype		= UPIO_MEM,
-	.irq		= NO_IRQ,
+	.irq		= 0,
 	.fifosize	= 16,
 	.ops		= &serial21285_ops,
 	.flags		= UPF_BOOT_AUTOCONF,
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index 9f50c4e..b0eb961 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -86,13 +86,6 @@ static unsigned int skip_txen_test; /* force skip of txen test at init time */
 #define BOTH_EMPTY 	(UART_LSR_TEMT | UART_LSR_THRE)
 
 
-/*
- * We default to IRQ0 for the "no irq" hack.   Some
- * machine types want others as well - they're free
- * to redefine this in their header file.
- */
-#define is_real_interrupt(irq)	((irq) != 0)
-
 #ifdef CONFIG_SERIAL_8250_DETECT_IRQ
 #define CONFIG_SERIAL_DETECT_IRQ 1
 #endif
@@ -1750,7 +1743,7 @@ static void serial8250_backup_timeout(unsigned long data)
 	 * Must disable interrupts or else we risk racing with the interrupt
 	 * based handler.
 	 */
-	if (is_real_interrupt(up->port.irq)) {
+	if (up->port.irq) {
 		ier = serial_in(up, UART_IER);
 		serial_out(up, UART_IER, 0);
 	}
@@ -1775,7 +1768,7 @@ static void serial8250_backup_timeout(unsigned long data)
 	if (!(iir & UART_IIR_NO_INT))
 		serial8250_tx_chars(up);
 
-	if (is_real_interrupt(up->port.irq))
+	if (up->port.irq)
 		serial_out(up, UART_IER, ier);
 
 	spin_unlock_irqrestore(&up->port.lock, flags);
@@ -2028,7 +2021,7 @@ static int serial8250_startup(struct uart_port *port)
 		serial_outp(up, UART_LCR, 0);
 	}
 
-	if (is_real_interrupt(up->port.irq)) {
+	if (up->port.irq) {
 		unsigned char iir1;
 		/*
 		 * Test for UARTs that do not reassert THRE when the
@@ -2083,7 +2076,7 @@ static int serial8250_startup(struct uart_port *port)
 	 * hardware interrupt, we use a timer-based system.  The original
 	 * driver used to do this with IRQ0.
 	 */
-	if (!is_real_interrupt(up->port.irq)) {
+	if (!up->port.irq) {
 		up->timer.data = (unsigned long)up;
 		mod_timer(&up->timer, jiffies + uart_poll_timeout(port));
 	} else {
@@ -2099,13 +2092,13 @@ static int serial8250_startup(struct uart_port *port)
 
 	spin_lock_irqsave(&up->port.lock, flags);
 	if (up->port.flags & UPF_FOURPORT) {
-		if (!is_real_interrupt(up->port.irq))
+		if (!up->port.irq)
 			up->port.mctrl |= TIOCM_OUT1;
 	} else
 		/*
 		 * Most PC uarts need OUT2 raised to enable interrupts.
 		 */
-		if (is_real_interrupt(up->port.irq))
+		if (up->port.irq)
 			up->port.mctrl |= TIOCM_OUT2;
 
 	serial8250_set_mctrl(&up->port, up->port.mctrl);
@@ -2223,7 +2216,7 @@ static void serial8250_shutdown(struct uart_port *port)
 
 	del_timer_sync(&up->timer);
 	up->timer.function = serial8250_timeout;
-	if (is_real_interrupt(up->port.irq))
+	if (up->port.irq)
 		serial_unlink_irq_chain(up);
 }
 
diff --git a/drivers/tty/serial/8250/m32r_sio.c b/drivers/tty/serial/8250/m32r_sio.c
index 94a6792..e465dda 100644
--- a/drivers/tty/serial/8250/m32r_sio.c
+++ b/drivers/tty/serial/8250/m32r_sio.c
@@ -70,13 +70,6 @@
 
 #define PASS_LIMIT	256
 
-/*
- * We default to IRQ0 for the "no irq" hack.   Some
- * machine types want others as well - they're free
- * to redefine this in their header file.
- */
-#define is_real_interrupt(irq)	((irq) != 0)
-
 #define BASE_BAUD	115200
 
 /* Standard COM flags */
@@ -640,7 +633,7 @@ static int m32r_sio_startup(struct uart_port *port)
 	 * hardware interrupt, we use a timer-based system.  The original
 	 * driver used to do this with IRQ0.
 	 */
-	if (!is_real_interrupt(up->port.irq)) {
+	if (!up->port.irq) {
 		unsigned int timeout = up->port.timeout;
 
 		timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
@@ -687,7 +680,7 @@ static void m32r_sio_shutdown(struct uart_port *port)
 
 	sio_init();
 
-	if (!is_real_interrupt(up->port.irq))
+	if (!up->port.irq)
 		del_timer_sync(&up->timer);
 	else
 		serial_unlink_irq_chain(up);
diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c
index 1093a88..e9a770d 100644
--- a/drivers/tty/serial/mpc52xx_uart.c
+++ b/drivers/tty/serial/mpc52xx_uart.c
@@ -507,7 +507,7 @@ static int __init mpc512x_psc_fifoc_init(void)
 
 	psc_fifoc_irq = irq_of_parse_and_map(np, 0);
 	of_node_put(np);
-	if (psc_fifoc_irq == NO_IRQ) {
+	if (psc_fifoc_irq == 0) {
 		pr_err("%s: Can't get FIFOC irq\n", __func__);
 		iounmap(psc_fifoc);
 		return -ENODEV;
@@ -1354,7 +1354,7 @@ static int __devinit mpc52xx_uart_of_probe(struct platform_device *op)
 	}
 
 	psc_ops->get_irq(port, op->dev.of_node);
-	if (port->irq == NO_IRQ) {
+	if (port->irq == 0) {
 		dev_dbg(&op->dev, "Could not get irq\n");
 		return -EINVAL;
 	}
diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c
index 06f6aef..c61d950 100644
--- a/drivers/tty/serial/mux.c
+++ b/drivers/tty/serial/mux.c
@@ -499,7 +499,7 @@ static int __init mux_probe(struct parisc_device *dev)
 		port->membase	= ioremap_nocache(port->mapbase, MUX_LINE_OFFSET);
 		port->iotype	= UPIO_MEM;
 		port->type	= PORT_MUX;
-		port->irq	= NO_IRQ;
+		port->irq	= 0;
 		port->uartclk	= 0;
 		port->fifosize	= MUX_FIFO_SIZE;
 		port->ops	= &mux_pops;
diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c
index e9c2dfe4..08ebe90 100644
--- a/drivers/tty/serial/pmac_zilog.c
+++ b/drivers/tty/serial/pmac_zilog.c
@@ -1506,7 +1506,7 @@ no_dma:
 	 * fixed up interrupt info, but we use the device-tree directly
 	 * here due to early probing so we need the fixup too.
 	 */
-	if (uap->port.irq == NO_IRQ &&
+	if (uap->port.irq == 0 &&
 	    np->parent && np->parent->parent &&
 	    of_device_is_compatible(np->parent->parent, "gatwick")) {
 		/* IRQs on gatwick are offset by 64 */
diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c
index 8e916e7..5a47d1b 100644
--- a/drivers/tty/serial/sunzilog.c
+++ b/drivers/tty/serial/sunzilog.c
@@ -1397,7 +1397,7 @@ static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up)
 #endif
 }
 
-static int zilog_irq = -1;
+static int zilog_irq;
 
 static int __devinit zs_probe(struct platform_device *op)
 {
@@ -1425,7 +1425,7 @@ static int __devinit zs_probe(struct platform_device *op)
 
 	rp = sunzilog_chip_regs[inst];
 
-	if (zilog_irq == -1)
+	if (!zilog_irq)
 		zilog_irq = op->archdata.irqs[0];
 
 	up = &sunzilog_port_table[inst * 2];
@@ -1580,7 +1580,7 @@ static int __init sunzilog_init(void)
 	if (err)
 		goto out_unregister_uart;
 
-	if (zilog_irq != -1) {
+	if (!zilog_irq) {
 		struct uart_sunzilog_port *up = sunzilog_irq_chain;
 		err = request_irq(zilog_irq, sunzilog_interrupt, IRQF_SHARED,
 				  "zs", sunzilog_irq_chain);
@@ -1621,7 +1621,7 @@ static void __exit sunzilog_exit(void)
 {
 	platform_driver_unregister(&zs_driver);
 
-	if (zilog_irq != -1) {
+	if (!zilog_irq) {
 		struct uart_sunzilog_port *up = sunzilog_irq_chain;
 
 		/* Disable Interrupts */
@@ -1637,7 +1637,7 @@ static void __exit sunzilog_exit(void)
 		}
 
 		free_irq(zilog_irq, sunzilog_irq_chain);
-		zilog_irq = -1;
+		zilog_irq = 0;
 	}
 
 	if (sunzilog_reg.nr) {
diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
index 2ebe606..f99b0c9 100644
--- a/drivers/tty/serial/ucc_uart.c
+++ b/drivers/tty/serial/ucc_uart.c
@@ -1360,7 +1360,7 @@ static int ucc_uart_probe(struct platform_device *ofdev)
 	}
 
 	qe_port->port.irq = irq_of_parse_and_map(np, 0);
-	if (qe_port->port.irq == NO_IRQ) {
+	if (qe_port->port.irq == 0) {
 		dev_err(&ofdev->dev, "could not map IRQ for UCC%u\n",
 		       qe_port->ucc_num + 1);
 		ret = -EINVAL;
diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c
index 83148e7..cf0d948 100644
--- a/drivers/tty/serial/vr41xx_siu.c
+++ b/drivers/tty/serial/vr41xx_siu.c
@@ -61,7 +61,7 @@
 static struct uart_port siu_uart_ports[SIU_PORTS_MAX] = {
 	[0 ... SIU_PORTS_MAX-1] = {
 		.lock	= __SPIN_LOCK_UNLOCKED(siu_uart_ports->lock),
-		.irq	= -1,
+		.irq	= 0,
 	},
 };
 
@@ -171,7 +171,7 @@ static inline unsigned int siu_check_type(struct uart_port *port)
 {
 	if (port->line == 0)
 		return PORT_VR41XX_SIU;
-	if (port->line == 1 && port->irq != -1)
+	if (port->line == 1 && port->irq)
 		return PORT_VR41XX_DSIU;
 
 	return PORT_UNKNOWN;


^ permalink raw reply related

* [PATCH 3/3] serial: Kill off Moorestown code
From: Alan Cox @ 2012-01-26 17:45 UTC (permalink / raw)
  To: greg, linux-serial
In-Reply-To: <20120126174121.16858.13827.stgit@bob.linux.org.uk>

From: Alan Cox <alan@linux.intel.com>

All production devices operate in the Oaktrail configuration with legacy PC
elements present and an ACPI BIOS. Continue stripping out the Moorestown
elements from the tree leaving Medfield.

Signed-off-by: Alan Cox <alan@linux.intel.com>
---

 drivers/tty/serial/Kconfig        |    9 -
 drivers/tty/serial/Makefile       |    1 
 drivers/tty/serial/max3107-aava.c |  344 -------------------------------------
 3 files changed, 0 insertions(+), 354 deletions(-)
 delete mode 100644 drivers/tty/serial/max3107-aava.c


diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 0bff238..2de9924 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -264,15 +264,6 @@ config SERIAL_MAX3107
 	help
 	  MAX3107 chip support
 
-config SERIAL_MAX3107_AAVA
-	tristate "MAX3107 AAVA platform support"
-	depends on X86_MRST && SERIAL_MAX3107 && GPIOLIB
-	select SERIAL_CORE
-	help
-	  Support for the MAX3107 chip configuration found on the AAVA
-	  platform. Includes the extra initialisation and GPIO support
-	  neded for this device.
-
 config SERIAL_DZ
 	bool "DECstation DZ serial driver"
 	depends on MACH_DECSTATION && 32BIT
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index a6d1ac0..fef32e1 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -29,7 +29,6 @@ obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o
 obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o
 obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
 obj-$(CONFIG_SERIAL_MAX3107) += max3107.o
-obj-$(CONFIG_SERIAL_MAX3107_AAVA) += max3107-aava.o
 obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
 obj-$(CONFIG_SERIAL_MUX) += mux.o
 obj-$(CONFIG_SERIAL_68328) += 68328serial.o
diff --git a/drivers/tty/serial/max3107-aava.c b/drivers/tty/serial/max3107-aava.c
deleted file mode 100644
index aae772a..0000000
--- a/drivers/tty/serial/max3107-aava.c
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- *  max3107.c - spi uart protocol driver for Maxim 3107
- *  Based on max3100.c
- *	by Christian Pellegrin <chripell@evolware.org>
- *  and	max3110.c
- *	by Feng Tang <feng.tang@intel.com>
- *
- *  Copyright (C) Aavamobile 2009
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- */
-
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/serial_core.h>
-#include <linux/serial.h>
-#include <linux/spi/spi.h>
-#include <linux/freezer.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <linux/sfi.h>
-#include <linux/module.h>
-#include <asm/mrst.h>
-#include "max3107.h"
-
-/* GPIO direction to input function */
-static int max3107_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
-{
-	struct max3107_port *s = container_of(chip, struct max3107_port, chip);
-	u16 buf[1];		/* Buffer for SPI transfer */
-
-	if (offset >= MAX3107_GPIO_COUNT) {
-		dev_err(&s->spi->dev, "Invalid GPIO\n");
-		return -EINVAL;
-	}
-
-	/* Read current GPIO configuration register */
-	buf[0] = MAX3107_GPIOCFG_REG;
-	/* Perform SPI transfer */
-	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
-		dev_err(&s->spi->dev, "SPI transfer GPIO read failed\n");
-		return -EIO;
-	}
-	buf[0] &= MAX3107_SPI_RX_DATA_MASK;
-
-	/* Set GPIO to input */
-	buf[0] &= ~(0x0001 << offset);
-
-	/* Write new GPIO configuration register value */
-	buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG);
-	/* Perform SPI transfer */
-	if (max3107_rw(s, (u8 *)buf, NULL, 2)) {
-		dev_err(&s->spi->dev, "SPI transfer GPIO write failed\n");
-		return -EIO;
-	}
-	return 0;
-}
-
-/* GPIO direction to output function */
-static int max3107_gpio_direction_out(struct gpio_chip *chip, unsigned offset,
-					int value)
-{
-	struct max3107_port *s = container_of(chip, struct max3107_port, chip);
-	u16 buf[2];	/* Buffer for SPI transfers */
-
-	if (offset >= MAX3107_GPIO_COUNT) {
-		dev_err(&s->spi->dev, "Invalid GPIO\n");
-		return -EINVAL;
-	}
-
-	/* Read current GPIO configuration and data registers */
-	buf[0] = MAX3107_GPIOCFG_REG;
-	buf[1] = MAX3107_GPIODATA_REG;
-	/* Perform SPI transfer */
-	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) {
-		dev_err(&s->spi->dev, "SPI transfer gpio failed\n");
-		return -EIO;
-	}
-	buf[0] &= MAX3107_SPI_RX_DATA_MASK;
-	buf[1] &= MAX3107_SPI_RX_DATA_MASK;
-
-	/* Set GPIO to output */
-	buf[0] |= (0x0001 << offset);
-	/* Set value */
-	if (value)
-		buf[1] |= (0x0001 << offset);
-	else
-		buf[1] &= ~(0x0001 << offset);
-
-	/* Write new GPIO configuration and data register values */
-	buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG);
-	buf[1] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG);
-	/* Perform SPI transfer */
-	if (max3107_rw(s, (u8 *)buf, NULL, 4)) {
-		dev_err(&s->spi->dev,
-			"SPI transfer for GPIO conf data w failed\n");
-		return -EIO;
-	}
-	return 0;
-}
-
-/* GPIO value query function */
-static int max3107_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
-	struct max3107_port *s = container_of(chip, struct max3107_port, chip);
-	u16 buf[1];	/* Buffer for SPI transfer */
-
-	if (offset >= MAX3107_GPIO_COUNT) {
-		dev_err(&s->spi->dev, "Invalid GPIO\n");
-		return -EINVAL;
-	}
-
-	/* Read current GPIO data register */
-	buf[0] = MAX3107_GPIODATA_REG;
-	/* Perform SPI transfer */
-	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
-		dev_err(&s->spi->dev, "SPI transfer GPIO data r failed\n");
-		return -EIO;
-	}
-	buf[0] &= MAX3107_SPI_RX_DATA_MASK;
-
-	/* Return value */
-	return buf[0] & (0x0001 << offset);
-}
-
-/* GPIO value set function */
-static void max3107_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
-{
-	struct max3107_port *s = container_of(chip, struct max3107_port, chip);
-	u16 buf[2];	/* Buffer for SPI transfers */
-
-	if (offset >= MAX3107_GPIO_COUNT) {
-		dev_err(&s->spi->dev, "Invalid GPIO\n");
-		return;
-	}
-
-	/* Read current GPIO configuration registers*/
-	buf[0] = MAX3107_GPIODATA_REG;
-	buf[1] = MAX3107_GPIOCFG_REG;
-	/* Perform SPI transfer */
-	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) {
-		dev_err(&s->spi->dev,
-			"SPI transfer for GPIO data and config read failed\n");
-		return;
-	}
-	buf[0] &= MAX3107_SPI_RX_DATA_MASK;
-	buf[1] &= MAX3107_SPI_RX_DATA_MASK;
-
-	if (!(buf[1] & (0x0001 << offset))) {
-		/* Configured as input, can't set value */
-		dev_warn(&s->spi->dev,
-				"Trying to set value for input GPIO\n");
-		return;
-	}
-
-	/* Set value */
-	if (value)
-		buf[0] |= (0x0001 << offset);
-	else
-		buf[0] &= ~(0x0001 << offset);
-
-	/* Write new GPIO data register value */
-	buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG);
-	/* Perform SPI transfer */
-	if (max3107_rw(s, (u8 *)buf, NULL, 2))
-		dev_err(&s->spi->dev, "SPI transfer GPIO data w failed\n");
-}
-
-/* GPIO chip data */
-static struct gpio_chip max3107_gpio_chip = {
-	.owner			= THIS_MODULE,
-	.direction_input	= max3107_gpio_direction_in,
-	.direction_output	= max3107_gpio_direction_out,
-	.get			= max3107_gpio_get,
-	.set			= max3107_gpio_set,
-	.can_sleep		= 1,
-	.base			= MAX3107_GPIO_BASE,
-	.ngpio			= MAX3107_GPIO_COUNT,
-};
-
-/**
- *	max3107_aava_reset	-	reset on AAVA systems
- *	@spi: The SPI device we are probing
- *
- *	Reset the device ready for probing.
- */
-
-static int max3107_aava_reset(struct spi_device *spi)
-{
-	/* Reset the chip */
-	if (gpio_request(MAX3107_RESET_GPIO, "max3107")) {
-		pr_err("Requesting RESET GPIO failed\n");
-		return -EIO;
-	}
-	if (gpio_direction_output(MAX3107_RESET_GPIO, 0)) {
-		pr_err("Setting RESET GPIO to 0 failed\n");
-		gpio_free(MAX3107_RESET_GPIO);
-		return -EIO;
-	}
-	msleep(MAX3107_RESET_DELAY);
-	if (gpio_direction_output(MAX3107_RESET_GPIO, 1)) {
-		pr_err("Setting RESET GPIO to 1 failed\n");
-		gpio_free(MAX3107_RESET_GPIO);
-		return -EIO;
-	}
-	gpio_free(MAX3107_RESET_GPIO);
-	msleep(MAX3107_WAKEUP_DELAY);
-	return 0;
-}
-
-static int max3107_aava_configure(struct max3107_port *s)
-{
-	int retval;
-
-	/* Initialize GPIO chip data */
-	s->chip = max3107_gpio_chip;
-	s->chip.label = s->spi->modalias;
-	s->chip.dev = &s->spi->dev;
-
-	/* Add GPIO chip */
-	retval = gpiochip_add(&s->chip);
-	if (retval) {
-		dev_err(&s->spi->dev, "Adding GPIO chip failed\n");
-		return retval;
-	}
-
-	/* Temporary fix for EV2 boot problems, set modem reset to 0 */
-	max3107_gpio_direction_out(&s->chip, 3, 0);
-	return 0;
-}
-
-#if 0
-/* This will get enabled once we have the board stuff merged for this
-   specific case */
-
-static const struct baud_table brg13_ext[] = {
-	{ 300,    MAX3107_BRG13_B300 },
-	{ 600,    MAX3107_BRG13_B600 },
-	{ 1200,   MAX3107_BRG13_B1200 },
-	{ 2400,   MAX3107_BRG13_B2400 },
-	{ 4800,   MAX3107_BRG13_B4800 },
-	{ 9600,   MAX3107_BRG13_B9600 },
-	{ 19200,  MAX3107_BRG13_B19200 },
-	{ 57600,  MAX3107_BRG13_B57600 },
-	{ 115200, MAX3107_BRG13_B115200 },
-	{ 230400, MAX3107_BRG13_B230400 },
-	{ 460800, MAX3107_BRG13_B460800 },
-	{ 921600, MAX3107_BRG13_B921600 },
-	{ 0, 0 }
-};
-
-static void max3107_aava_init(struct max3107_port *s)
-{
-	/*override for AAVA SC specific*/
-	if (mrst_platform_id() == MRST_PLATFORM_AAVA_SC) {
-		if (get_koski_build_id() <= KOSKI_EV2)
-			if (s->ext_clk) {
-				s->brg_cfg = MAX3107_BRG13_B9600;
-				s->baud_tbl = (struct baud_table *)brg13_ext;
-			}
-	}
-}
-#endif
-
-static int __devexit max3107_aava_remove(struct spi_device *spi)
-{
-	struct max3107_port *s = dev_get_drvdata(&spi->dev);
-
-	/* Remove GPIO chip */
-	if (gpiochip_remove(&s->chip))
-		dev_warn(&spi->dev, "Removing GPIO chip failed\n");
-
-	/* Then do the default remove */
-	return max3107_remove(spi);
-}
-
-/* Platform data */
-static struct max3107_plat aava_plat_data = {
-	.loopback               = 0,
-	.ext_clk                = 1,
-/*	.init			= max3107_aava_init, */
-	.configure		= max3107_aava_configure,
-	.hw_suspend		= max3107_hw_susp,
-	.polled_mode            = 0,
-	.poll_time              = 0,
-};
-
-
-static int __devinit max3107_probe_aava(struct spi_device *spi)
-{
-	int err = max3107_aava_reset(spi);
-	if (err < 0)
-		return err;
-	return max3107_probe(spi, &aava_plat_data);
-}
-
-/* Spi driver data */
-static struct spi_driver max3107_driver = {
-	.driver = {
-		.name		= "aava-max3107",
-		.owner		= THIS_MODULE,
-	},
-	.probe		= max3107_probe_aava,
-	.remove		= __devexit_p(max3107_aava_remove),
-	.suspend	= max3107_suspend,
-	.resume		= max3107_resume,
-};
-
-/* Driver init function */
-static int __init max3107_init(void)
-{
-	return spi_register_driver(&max3107_driver);
-}
-
-/* Driver exit function */
-static void __exit max3107_exit(void)
-{
-	spi_unregister_driver(&max3107_driver);
-}
-
-module_init(max3107_init);
-module_exit(max3107_exit);
-
-MODULE_DESCRIPTION("MAX3107 driver");
-MODULE_AUTHOR("Aavamobile");
-MODULE_ALIAS("spi:aava-max3107");
-MODULE_LICENSE("GPL v2");


^ permalink raw reply related

* Re: [PATCH 1/7 v2] dmaengine: add a simple dma library
From: Sascha Hauer @ 2012-01-26 17:34 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: alsa-devel, linux-sh, Vinod Koul, linux-mmc, Yoshihiro Shimoda,
	Magnus Damm, linux-kernel, Paul Mundt, linux-serial
In-Reply-To: <1327589784-4287-2-git-send-email-g.liakhovetski@gmx.de>

Hi Guennadi,

On Thu, Jan 26, 2012 at 03:56:18PM +0100, Guennadi Liakhovetski wrote:
> This patch adds a library of functions, helping to implement dmaengine
> drivers for hardware, unable to handle scatter-gather lists natively.
> The first version of this driver only supports memcpy and slave DMA
> operation.
> 
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
> 

[...]

> +
> +/*
> + * For slave DMA we assume, that there is a finite number of DMA slaves in the
> + * system, and that each such slave can only use a finite number of channels.
> + * We use slave channel IDs to make sure, that no such slave channel ID is
> + * allocated more than once.
> + */
> +static unsigned int slave_num = 256;
> +module_param(slave_num, uint, 0444);
> +
> +/* A bitmask with slave_num bits */
> +static unsigned long *simple_slave_used;

You never check that the slave ids passed into this code are
within the range of slave_num. Given that this is a user changeable
value this is a bit flawy.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply

* Re: patch "tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA" added to tty tree
From: Greg KH @ 2012-01-26 19:16 UTC (permalink / raw)
  To: Paul Walmsley
  Cc: govindraj.raja, khilman, tomi.valkeinen, linux-omap,
	linux-arm-kernel, linux-serial
In-Reply-To: <alpine.DEB.2.00.1201252124470.11174@utopia.booyaka.com>

On Wed, Jan 25, 2012 at 09:31:53PM -0700, Paul Walmsley wrote:
> On Wed, 25 Jan 2012, Greg KH wrote:
> 
> > On Wed, Jan 25, 2012 at 08:02:09PM -0700, Paul Walmsley wrote:
> > > On Tue, 24 Jan 2012, gregkh@suse.de wrote:
> > > > 
> > > > This is a note to let you know that I've just added the patch titled
> > > > 
> > > >     tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA
> > > > 
> > > > to my tty git tree which can be found at
> > > >     git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
> > > > in the tty-linus branch.
> > > 
> > > If it's not too late, I was wondering if you could drop this patch and the 
> > > subsequent one ("tty: serial: OMAP: transmit FIFO threshold interrupts 
> > > don't wake the"), in favor of the second version of this series that was 
> > > just posted at
> > > 
> > >    http://marc.info/?l=linux-arm-kernel&m=132754676814391&w=2
> > > 
> > > If it is too late, we'll deal with it in 3.4.
> > 
> > What is wrong with the patches that I applied?
> 
> A new workaround is used that reduces the number of interrupts to normal.  
> The commit messages are improved since we have a better idea of what was 
> wrong.  There is also a new patch (patch 3) for a power management 
> regression in the driver.
>  
> > How about a fix-up patch on top of what I have applied instead of whole 
> > new ones?
> 
> That's fine, if that's your preference.  It will be several patches, 
> though.  And about 75% of the previous series would be reverted, since a 
> different workaround would be used.
> 
> Let me know if that is indeed what you'd like.

Ok, I've just reverted both of these patches for now, please send new
ones when you have them.

thanks,

greg k-h

^ permalink raw reply

* Re: patch "tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA" added to tty tree
From: Paul Walmsley @ 2012-01-26 19:34 UTC (permalink / raw)
  To: Greg KH
  Cc: khilman, govindraj.raja, tomi.valkeinen, linux-serial, linux-omap,
	linux-arm-kernel
In-Reply-To: <20120126191604.GA15516@suse.de>

On Thu, 26 Jan 2012, Greg KH wrote:

> Ok, I've just reverted both of these patches for now, please send new
> ones when you have them.

Thanks.  A pull request is at the bottom of this message.  The patches 
are also available from the mailing list archives here:

http://marc.info/?l=linux-arm-kernel&m=132754676014389&w=2
http://marc.info/?l=linux-arm-kernel&m=132754677914395&w=2
http://marc.info/?l=linux-arm-kernel&m=132754676014388&w=2


- Paul

The following changes since commit dcd6c92267155e70a94b3927bce681ce74b80d1f:

  Linux 3.3-rc1 (2012-01-19 15:04:48 -0800)

are available in the git repository at:
  git://git.pwsan.com/linux-2.6 omap_serial_fixes_3.3rc

Paul Walmsley (3):
      tty: serial: OMAP: use a 1-byte RX FIFO threshold in PIO mode
      tty: serial: OMAP: block idle while the UART is transferring data in PIO mode
      tty: serial: OMAP: wakeup latency constraint is in microseconds, not milliseconds

 arch/arm/mach-omap2/serial.c     |    8 ++++----
 drivers/tty/serial/omap-serial.c |   30 +++++++++++++++++++++++++-----
 2 files changed, 29 insertions(+), 9 deletions(-)

^ permalink raw reply

* Re: [alsa-devel] [PATCH 1/7 v2] dmaengine: add a simple dma library
From: Guennadi Liakhovetski @ 2012-01-26 21:07 UTC (permalink / raw)
  To: Sascha Hauer
  Cc: linux-kernel, alsa-devel, linux-sh, Vinod Koul, Magnus Damm,
	Yoshihiro Shimoda, linux-mmc, Paul Mundt, linux-serial
In-Reply-To: <20120126173444.GM5446@pengutronix.de>

Hi Sascha

On Thu, 26 Jan 2012, Sascha Hauer wrote:

> Hi Guennadi,
> 
> On Thu, Jan 26, 2012 at 03:56:18PM +0100, Guennadi Liakhovetski wrote:
> > This patch adds a library of functions, helping to implement dmaengine
> > drivers for hardware, unable to handle scatter-gather lists natively.
> > The first version of this driver only supports memcpy and slave DMA
> > operation.
> > 
> > Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> > ---
> > 
> 
> [...]
> 
> > +
> > +/*
> > + * For slave DMA we assume, that there is a finite number of DMA slaves in the
> > + * system, and that each such slave can only use a finite number of channels.
> > + * We use slave channel IDs to make sure, that no such slave channel ID is
> > + * allocated more than once.
> > + */
> > +static unsigned int slave_num = 256;
> > +module_param(slave_num, uint, 0444);
> > +
> > +/* A bitmask with slave_num bits */
> > +static unsigned long *simple_slave_used;
> 
> You never check that the slave ids passed into this code are
> within the range of slave_num. Given that this is a user changeable
> value this is a bit flawy.

Right, never trust the user;-) I'll fix that.

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

^ permalink raw reply

* Re: 3.3-rc1 console lag (was: Re: [PATCH v8 00/20] OMAP2+: UART: Runtime adaptation + cleanup)
From: Ramirez Luna, Omar @ 2012-01-27  0:14 UTC (permalink / raw)
  To: Paul Walmsley
  Cc: Kevin Hilman, Tony Lindgren, Rajendra Nayak, Partha Basak,
	Govindraj Raja, Santosh Shilimkar, linux-serial,
	Vishwanath Sripathy, linux-omap, linux-arm-kernel
In-Reply-To: <alpine.DEB.2.00.1201252000410.11174@utopia.booyaka.com>

Hi Paul,

On Wed, Jan 25, 2012 at 9:00 PM, Paul Walmsley <paul@pwsan.com> wrote:
...
>> > Ensure CONFIG_OMAP_PRM is set while testing irq_chaining with uart.
>> > And for pm_qos usage ensure CONFIG_CPU_IDLE is selected other wise
>> > console might be sluggish.
>>
>> There is console lag for omap2plus_defconfig given that
>> CONFIG_CPU_IDLE is not enabled. Is the intention to force CPU_IDLE
>> into the defconfig or find an alternative for the new pm_qos when cpu
>> idle is disabled.
>>
>> Seen on beagle-xm and 3.3-rc1.
>
> Try this
>
> http://marc.info/?l=linux-arm-kernel&m=132754676814391&w=2

I tried the series and the console returned to normal, I can confirm
that the following patch helps:

tty: serial: OMAP: use a 1-byte RX FIFO threshold in PIO mode

Thanks,

Omar

^ permalink raw reply

* Re: [PATCH v2 3/3] tty: serial: omap-serial: wakeup latency constraint is in microseconds, not milliseconds
From: Govindraj @ 2012-01-27  7:19 UTC (permalink / raw)
  To: Paul Walmsley
  Cc: linux-omap, linux-serial, linux-arm-kernel, Kevin Hilman,
	Greg Kroah-Hartman, Govindraj.R, Tomi Valkeinen, Alan Cox
In-Reply-To: <20120126025052.31613.37828.stgit@dusk>

On Thu, Jan 26, 2012 at 8:20 AM, Paul Walmsley <paul@pwsan.com> wrote:
> The receive FIFO wakeup latency estimate in the omap-serial driver is
> three orders of magnitude too small.  This effectively prevents the
> MPU from going to a low-power state when CONFIG_CPU_IDLE=y.  This is a
> major power management regression and masks some other FIFO-related
> bugs in the driver.
>
> Fix by correcting the most egregious problem in the RX wakeup latency
> estimate.  There are several other flaws in the estimator; these will
> be fixed by a separate patch series intended for 3.4.
>
> The difference in low-power states with this patch can be observed via
> debugfs in pm_debug/count.
>
> This estimate does not have any effect when CONFIG_CPU_IDLE=n.
>
> Signed-off-by: Paul Walmsley <paul@pwsan.com>
> Cc: Kevin Hilman <khilman@ti.com>
> Cc: Govindraj.R <govindraj.raja@ti.com>
> Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
> Cc: Alan Cox <alan@linux.intel.com>
> Cc: Greg Kroah-Hartman <gregkh@suse.de>

Acked-by: Govindraj.R <govindraj.raja@ti.com>

> ---
>  drivers/tty/serial/omap-serial.c |    3 +--
>  1 files changed, 1 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
> index 11fa156..72fa783 100644
> --- a/drivers/tty/serial/omap-serial.c
> +++ b/drivers/tty/serial/omap-serial.c
> @@ -740,8 +740,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
>        quot = serial_omap_get_divisor(port, baud);
>
>        /* calculate wakeup latency constraint */
> -       up->calc_latency = (1000000 * up->port.fifosize) /
> -                               (1000 * baud / 8);
> +       up->calc_latency = (USEC_PER_SEC * up->port.fifosize) / (baud / 8);
>        up->latency = up->calc_latency;
>        schedule_work(&up->qos_work);
>
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-serial" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
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

* Re: [PATCH 2/3] tty: serial: OMAP: block idle while the UART is transferring data in PIO mode
From: Govindraj @ 2012-01-27  7:23 UTC (permalink / raw)
  To: Paul Walmsley
  Cc: linux-omap, linux-serial, linux-arm-kernel, govindraj.r,
	Kevin Hilman, Govindraj.R, Greg Kroah-Hartman, Alan Cox,
	Tomi Valkeinen
In-Reply-To: <20120126025036.31613.67819.stgit@dusk>

On Thu, Jan 26, 2012 at 8:20 AM, Paul Walmsley <paul@pwsan.com> wrote:
> Prevent OMAP UARTs from going idle while they are still transferring
> data in PIO mode.  This works around an oversight in the OMAP UART
> hardware present in OMAP34xx and earlier: an idle UART won't send a
> wakeup when the TX FIFO threshold is reached.  This causes long delays
> during data transmission when the MPU powerdomain enters a low-power
> mode.  The MPU interrupt controller is not able to respond to
> interrupts when it's in a low-power state, so the TX buffer is not
> refilled until another wakeup event occurs.
>
> This fix changes the erratum i291 DMA idle workaround.  Rather than
> toggling between force-idle and no-idle, it will toggle between
> smart-idle and no-idle.  The important part of the workaround is the
> no-idle part, so this shouldn't result in any change in behavior.
>
> This fix should work on all OMAP UARTs.  Future patches intended for
> the 3.4 merge window will make this workaround conditional on a
> "feature" flag, and will use the OMAP36xx+ TX event wakeup support.
>
> Thanks to Kevin Hilman <khilman@ti.com> for mentioning the erratum i291
> workaround, which led to the development of this approach.
>
> Signed-off-by: Paul Walmsley <paul@pwsan.com>
> Cc: Kevin Hilman <khilman@ti.com>
> Cc: Govindraj.R <govindraj.raja@ti.com>
> Cc: Greg Kroah-Hartman <gregkh@suse.de>
> Cc: Alan Cox <alan@linux.intel.com>
> Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>

Acked-by: Govindraj.R <govindraj.raja@ti.com>

> ---
>  arch/arm/mach-omap2/serial.c     |    8 ++++----
>  drivers/tty/serial/omap-serial.c |    7 +++++++
>  2 files changed, 11 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
> index 247d894..f590afc 100644
> --- a/arch/arm/mach-omap2/serial.c
> +++ b/arch/arm/mach-omap2/serial.c
> @@ -107,18 +107,18 @@ static void omap_uart_set_noidle(struct platform_device *pdev)
>        omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_NO);
>  }
>
> -static void omap_uart_set_forceidle(struct platform_device *pdev)
> +static void omap_uart_set_smartidle(struct platform_device *pdev)
>  {
>        struct omap_device *od = to_omap_device(pdev);
>
> -       omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_FORCE);
> +       omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_SMART);
>  }
>
>  #else
>  static void omap_uart_enable_wakeup(struct platform_device *pdev, bool enable)
>  {}
>  static void omap_uart_set_noidle(struct platform_device *pdev) {}
> -static void omap_uart_set_forceidle(struct platform_device *pdev) {}
> +static void omap_uart_set_smartidle(struct platform_device *pdev) {}
>  #endif /* CONFIG_PM */
>
>  #ifdef CONFIG_OMAP_MUX
> @@ -349,7 +349,7 @@ void __init omap_serial_init_port(struct omap_board_data *bdata,
>        omap_up.uartclk = OMAP24XX_BASE_BAUD * 16;
>        omap_up.flags = UPF_BOOT_AUTOCONF;
>        omap_up.get_context_loss_count = omap_pm_get_dev_context_loss_count;
> -       omap_up.set_forceidle = omap_uart_set_forceidle;
> +       omap_up.set_forceidle = omap_uart_set_smartidle;
>        omap_up.set_noidle = omap_uart_set_noidle;
>        omap_up.enable_wakeup = omap_uart_enable_wakeup;
>        omap_up.dma_rx_buf_size = info->dma_rx_buf_size;
> diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
> index c9c9ba2..11fa156 100644
> --- a/drivers/tty/serial/omap-serial.c
> +++ b/drivers/tty/serial/omap-serial.c
> @@ -136,6 +136,7 @@ static void serial_omap_enable_ms(struct uart_port *port)
>  static void serial_omap_stop_tx(struct uart_port *port)
>  {
>        struct uart_omap_port *up = (struct uart_omap_port *)port;
> +       struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
>
>        if (up->use_dma &&
>                up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) {
> @@ -158,6 +159,9 @@ static void serial_omap_stop_tx(struct uart_port *port)
>                serial_out(up, UART_IER, up->ier);
>        }
>
> +       if (!up->use_dma && pdata->set_forceidle)
> +               pdata->set_forceidle(up->pdev);
> +
>        pm_runtime_mark_last_busy(&up->pdev->dev);
>        pm_runtime_put_autosuspend(&up->pdev->dev);
>  }
> @@ -286,6 +290,7 @@ static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up)
>  static void serial_omap_start_tx(struct uart_port *port)
>  {
>        struct uart_omap_port *up = (struct uart_omap_port *)port;
> +       struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
>        struct circ_buf *xmit;
>        unsigned int start;
>        int ret = 0;
> @@ -293,6 +298,8 @@ static void serial_omap_start_tx(struct uart_port *port)
>        if (!up->use_dma) {
>                pm_runtime_get_sync(&up->pdev->dev);
>                serial_omap_enable_ier_thri(up);
> +               if (pdata->set_noidle)
> +                       pdata->set_noidle(up->pdev);
>                pm_runtime_mark_last_busy(&up->pdev->dev);
>                pm_runtime_put_autosuspend(&up->pdev->dev);
>                return;
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-serial" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
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

* Re: [PATCH 1/7 v2] dmaengine: add a simple dma library
From: Shimoda, Yoshihiro @ 2012-01-27  8:37 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-kernel, linux-sh, Vinod Koul, Magnus Damm, linux-mmc,
	alsa-devel, linux-serial, Paul Mundt
In-Reply-To: <1327589784-4287-2-git-send-email-g.liakhovetski@gmx.de>

Hello Guennadi-san,

2012/01/26 23:56, Guennadi Liakhovetski wrote:
> This patch adds a library of functions, helping to implement dmaengine
> drivers for hardware, unable to handle scatter-gather lists natively.
> The first version of this driver only supports memcpy and slave DMA
> operation.
> 
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
> 
> v2:
> 
> 1. switch from using a tasklet to threaded IRQ, which allowed to

I tested this driver on 2 environment, but it could not work correctly.
 - renesas_usbhs driver with shdma driver on the mackerel board
 - renesas_usbhs driver with experimental SUDMAC driver

In this case, should we modify the renesas_usbhs driver?
For reference, if I changed the threaded IRQ to tasklet,
the 2 environment worked correctly:
==============
diff --git a/drivers/dma/dma-simple.c b/drivers/dma/dma-simple.c
index 49d8f7d..65a5170 100644
--- a/drivers/dma/dma-simple.c
+++ b/drivers/dma/dma-simple.c
@@ -715,16 +715,18 @@ static irqreturn_t chan_irq(int irq, void *dev)

 	spin_lock(&schan->chan_lock);

-	ret = ops->chan_irq(schan, irq) ? IRQ_WAKE_THREAD : IRQ_NONE;
+	ret = ops->chan_irq(schan, irq) ? IRQ_HANDLED : IRQ_NONE;
+	if (ret == IRQ_HANDLED)
+		tasklet_schedule(&schan->tasklet);

 	spin_unlock(&schan->chan_lock);

 	return ret;
 }

-static irqreturn_t chan_irqt(int irq, void *dev)
+static void chan_irqt(unsigned long dev)
 {
-	struct dma_simple_chan *schan = dev;
+	struct dma_simple_chan *schan = (struct dma_simple_chan *)dev;
 	const struct dma_simple_ops *ops =
 		to_simple_dev(schan->dma_chan.device)->ops;
 	struct dma_simple_desc *sdesc;
@@ -744,15 +746,13 @@ static irqreturn_t chan_irqt(int irq, void *dev)
 	spin_unlock_irq(&schan->chan_lock);

 	simple_chan_ld_cleanup(schan, false);
-
-	return IRQ_HANDLED;
 }

 int dma_simple_request_irq(struct dma_simple_chan *schan, int irq,
 			   unsigned long flags, const char *name)
 {
-	int ret = request_threaded_irq(irq, chan_irq, chan_irqt,
-				       flags, name, schan);
+	int ret = request_irq(irq, chan_irq, flags, name, schan);
+	tasklet_init(&schan->tasklet, chan_irqt, (unsigned long)schan);

 	schan->irq = ret < 0 ? ret : irq;

diff --git a/include/linux/dma-simple.h b/include/linux/dma-simple.h
index 5336674..beb1489 100644
--- a/include/linux/dma-simple.h
+++ b/include/linux/dma-simple.h
@@ -68,6 +68,7 @@ struct dma_simple_chan {
 	int id;				/* Raw id of this channel */
 	int irq;			/* Channel IRQ */
 	enum dma_simple_pm_state pm_state;
+	struct tasklet_struct tasklet;
 };

 /**
==============

Best regards,
Yoshihiro Shimoda

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox