linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/5] ep93xx DMA patches
@ 2011-05-29 10:10 Mika Westerberg
  2011-05-29 10:10 ` [PATCH v2 1/5] dmaengine: add ep93xx DMA support Mika Westerberg
                   ` (5 more replies)
  0 siblings, 6 replies; 36+ messages in thread
From: Mika Westerberg @ 2011-05-29 10:10 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: hsweeten, rmallon, vinod.koul, dan.j.williams, lrg, broonie,
	grant.likely, Mika Westerberg, linux-kernel

This is an updated version of the ep93xx DMA patches. Previous version can be
found here: https://lkml.org/lkml/2011/5/22/86.

Changes to v1:
 * removed unnecessary header
 * corrected SPI driver documentation
 * DMA driver platform code is only compiled when CONFIG_EP93XX_DMA is
   enabled

I tried to put all the acks in place, hope I got them right.

There is a dependency between patch 1 and patch 4 which I'm not entirely sure
how to solve. Basically it is required that we have patch 1 applied before
patch 4 or merge conflict results.

Otherwise it shouldn't matter in which order the patches arrive to the
mainline.

Unless someone has a better idea, I suggest that these patches will go through
their respective subsystem trees. All the maintainers should be copied.

Thanks,
MW

Mika Westerberg (5):
  dmaengine: add ep93xx DMA support
  ep93xx: add dmaengine platform code
  ASoC: ep93xx: convert to use the DMA engine API
  ep93xx: remove the old M2P DMA code
  spi/ep93xx: add DMA support

 Documentation/spi/ep93xx_spi                   |   10 +
 arch/arm/mach-ep93xx/Makefile                  |    4 +-
 arch/arm/mach-ep93xx/core.c                    |    6 +-
 arch/arm/mach-ep93xx/dma-m2p.c                 |  411 -------
 arch/arm/mach-ep93xx/dma.c                     |  108 ++
 arch/arm/mach-ep93xx/include/mach/dma.h        |  190 ++---
 arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h |    2 +
 drivers/dma/Kconfig                            |    7 +
 drivers/dma/Makefile                           |    1 +
 drivers/dma/ep93xx_dma.c                       | 1355 ++++++++++++++++++++++++
 drivers/spi/ep93xx_spi.c                       |  303 +++++-
 sound/soc/ep93xx/ep93xx-ac97.c                 |    4 +-
 sound/soc/ep93xx/ep93xx-i2s.c                  |    4 +-
 sound/soc/ep93xx/ep93xx-pcm.c                  |  137 ++--
 14 files changed, 1930 insertions(+), 612 deletions(-)
 delete mode 100644 arch/arm/mach-ep93xx/dma-m2p.c
 create mode 100644 arch/arm/mach-ep93xx/dma.c
 create mode 100644 drivers/dma/ep93xx_dma.c

-- 
1.7.4.4


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

* [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-05-29 10:10 [PATCH v2 0/5] ep93xx DMA patches Mika Westerberg
@ 2011-05-29 10:10 ` Mika Westerberg
  2011-11-15 15:02   ` Rafal Prylowski
  2011-05-29 10:10 ` [PATCH v2 2/5] ep93xx: add dmaengine platform code Mika Westerberg
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 36+ messages in thread
From: Mika Westerberg @ 2011-05-29 10:10 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: hsweeten, rmallon, vinod.koul, dan.j.williams, lrg, broonie,
	grant.likely, Mika Westerberg, linux-kernel

The ep93xx DMA controller has 10 independent memory to peripheral (M2P)
channels, and 2 dedicated memory to memory (M2M) channels. M2M channels can
also be used by SPI and IDE to perform DMA transfers to/from their memory
mapped FIFOs.

This driver supports both M2P and M2M channels with DMA_SLAVE, DMA_CYCLIC and
DMA_MEMCPY (M2M only) capabilities.

Signed-off-by: Mika Westerberg <mika.westerberg@iki.fi>
Signed-off-by: Ryan Mallon <rmallon@gmail.com>
Acked-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Acked-by: Vinod Koul <vinod.koul@intel.com>
Cc: Dan Williams <dan.j.williams@intel.com>
---
 arch/arm/mach-ep93xx/include/mach/dma.h |   87 ++
 drivers/dma/Kconfig                     |    7 +
 drivers/dma/Makefile                    |    1 +
 drivers/dma/ep93xx_dma.c                | 1355 +++++++++++++++++++++++++++++++
 4 files changed, 1450 insertions(+), 0 deletions(-)
 create mode 100644 drivers/dma/ep93xx_dma.c

diff --git a/arch/arm/mach-ep93xx/include/mach/dma.h b/arch/arm/mach-ep93xx/include/mach/dma.h
index 5e31b2b..6e7049a 100644
--- a/arch/arm/mach-ep93xx/include/mach/dma.h
+++ b/arch/arm/mach-ep93xx/include/mach/dma.h
@@ -15,6 +15,8 @@
 
 #include <linux/list.h>
 #include <linux/types.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
 
 /**
  * struct ep93xx_dma_buffer - Information about a buffer to be transferred
@@ -146,4 +148,89 @@ void ep93xx_dma_m2p_submit_recursive(struct ep93xx_dma_m2p_client *m2p,
  */
 void ep93xx_dma_m2p_flush(struct ep93xx_dma_m2p_client *m2p);
 
+/*
+ * M2P channels.
+ *
+ * Note that these values are also directly used for setting the PPALLOC
+ * register.
+ */
+#define EP93XX_DMA_I2S1		0
+#define EP93XX_DMA_I2S2		1
+#define EP93XX_DMA_AAC1		2
+#define EP93XX_DMA_AAC2		3
+#define EP93XX_DMA_AAC3		4
+#define EP93XX_DMA_I2S3		5
+#define EP93XX_DMA_UART1	6
+#define EP93XX_DMA_UART2	7
+#define EP93XX_DMA_UART3	8
+#define EP93XX_DMA_IRDA		9
+/* M2M channels */
+#define EP93XX_DMA_SSP		10
+#define EP93XX_DMA_IDE		11
+
+/**
+ * struct ep93xx_dma_data - configuration data for the EP93xx dmaengine
+ * @port: peripheral which is requesting the channel
+ * @direction: TX/RX channel
+ * @name: optional name for the channel, this is displayed in /proc/interrupts
+ *
+ * This information is passed as private channel parameter in a filter
+ * function. Note that this is only needed for slave/cyclic channels.  For
+ * memcpy channels %NULL data should be passed.
+ */
+struct ep93xx_dma_data {
+	int				port;
+	enum dma_data_direction		direction;
+	const char			*name;
+};
+
+/**
+ * struct ep93xx_dma_chan_data - platform specific data for a DMA channel
+ * @name: name of the channel, used for getting the right clock for the channel
+ * @base: mapped registers
+ * @irq: interrupt number used by this channel
+ */
+struct ep93xx_dma_chan_data {
+	const char			*name;
+	void __iomem			*base;
+	int				irq;
+};
+
+/**
+ * struct ep93xx_dma_platform_data - platform data for the dmaengine driver
+ * @channels: array of channels which are passed to the driver
+ * @num_channels: number of channels in the array
+ *
+ * This structure is passed to the DMA engine driver via platform data. For
+ * M2P channels, contract is that even channels are for TX and odd for RX.
+ * There is no requirement for the M2M channels.
+ */
+struct ep93xx_dma_platform_data {
+	struct ep93xx_dma_chan_data	*channels;
+	size_t				num_channels;
+};
+
+static inline bool ep93xx_dma_chan_is_m2p(struct dma_chan *chan)
+{
+	return !strcmp(dev_name(chan->device->dev), "ep93xx-dma-m2p");
+}
+
+/**
+ * ep93xx_dma_chan_direction - returns direction the channel can be used
+ * @chan: channel
+ *
+ * This function can be used in filter functions to find out whether the
+ * channel supports given DMA direction. Only M2P channels have such
+ * limitation, for M2M channels the direction is configurable.
+ */
+static inline enum dma_data_direction
+ep93xx_dma_chan_direction(struct dma_chan *chan)
+{
+	if (!ep93xx_dma_chan_is_m2p(chan))
+		return DMA_NONE;
+
+	/* even channels are for TX, odd for RX */
+	return (chan->chan_id % 2 == 0) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+}
+
 #endif /* __ASM_ARCH_DMA_H */
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index a572600..614ce7b 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -235,6 +235,13 @@ config MXS_DMA
 	  Support the MXS DMA engine. This engine including APBH-DMA
 	  and APBX-DMA is integrated into Freescale i.MX23/28 chips.
 
+config EP93XX_DMA
+	bool "Cirrus Logic EP93xx DMA support"
+	depends on ARCH_EP93XX
+	select DMA_ENGINE
+	help
+	  Enable support for the Cirrus Logic EP93xx M2P/M2M DMA controller.
+
 config DMA_ENGINE
 	bool
 
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 836095a..30cf3b1 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -25,3 +25,4 @@ obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o
 obj-$(CONFIG_PL330_DMA) += pl330.o
 obj-$(CONFIG_PCH_DMA) += pch_dma.o
 obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
+obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
new file mode 100644
index 0000000..0766c1e
--- /dev/null
+++ b/drivers/dma/ep93xx_dma.c
@@ -0,0 +1,1355 @@
+/*
+ * Driver for the Cirrus Logic EP93xx DMA Controller
+ *
+ * Copyright (C) 2011 Mika Westerberg
+ *
+ * DMA M2P implementation is based on the original
+ * arch/arm/mach-ep93xx/dma-m2p.c which has following copyrights:
+ *
+ *   Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ *   Copyright (C) 2006 Applied Data Systems
+ *   Copyright (C) 2009 Ryan Mallon <rmallon@gmail.com>
+ *
+ * This driver is based on dw_dmac and amba-pl08x drivers.
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/dmaengine.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <mach/dma.h>
+
+/* M2P registers */
+#define M2P_CONTROL			0x0000
+#define M2P_CONTROL_STALLINT		BIT(0)
+#define M2P_CONTROL_NFBINT		BIT(1)
+#define M2P_CONTROL_CH_ERROR_INT	BIT(3)
+#define M2P_CONTROL_ENABLE		BIT(4)
+#define M2P_CONTROL_ICE			BIT(6)
+
+#define M2P_INTERRUPT			0x0004
+#define M2P_INTERRUPT_STALL		BIT(0)
+#define M2P_INTERRUPT_NFB		BIT(1)
+#define M2P_INTERRUPT_ERROR		BIT(3)
+
+#define M2P_PPALLOC			0x0008
+#define M2P_STATUS			0x000c
+
+#define M2P_MAXCNT0			0x0020
+#define M2P_BASE0			0x0024
+#define M2P_MAXCNT1			0x0030
+#define M2P_BASE1			0x0034
+
+#define M2P_STATE_IDLE			0
+#define M2P_STATE_STALL			1
+#define M2P_STATE_ON			2
+#define M2P_STATE_NEXT			3
+
+/* M2M registers */
+#define M2M_CONTROL			0x0000
+#define M2M_CONTROL_DONEINT		BIT(2)
+#define M2M_CONTROL_ENABLE		BIT(3)
+#define M2M_CONTROL_START		BIT(4)
+#define M2M_CONTROL_DAH			BIT(11)
+#define M2M_CONTROL_SAH			BIT(12)
+#define M2M_CONTROL_PW_SHIFT		9
+#define M2M_CONTROL_PW_8		(0 << M2M_CONTROL_PW_SHIFT)
+#define M2M_CONTROL_PW_16		(1 << M2M_CONTROL_PW_SHIFT)
+#define M2M_CONTROL_PW_32		(2 << M2M_CONTROL_PW_SHIFT)
+#define M2M_CONTROL_PW_MASK		(3 << M2M_CONTROL_PW_SHIFT)
+#define M2M_CONTROL_TM_SHIFT		13
+#define M2M_CONTROL_TM_TX		(1 << M2M_CONTROL_TM_SHIFT)
+#define M2M_CONTROL_TM_RX		(2 << M2M_CONTROL_TM_SHIFT)
+#define M2M_CONTROL_RSS_SHIFT		22
+#define M2M_CONTROL_RSS_SSPRX		(1 << M2M_CONTROL_RSS_SHIFT)
+#define M2M_CONTROL_RSS_SSPTX		(2 << M2M_CONTROL_RSS_SHIFT)
+#define M2M_CONTROL_RSS_IDE		(3 << M2M_CONTROL_RSS_SHIFT)
+#define M2M_CONTROL_NO_HDSK		BIT(24)
+#define M2M_CONTROL_PWSC_SHIFT		25
+
+#define M2M_INTERRUPT			0x0004
+#define M2M_INTERRUPT_DONEINT		BIT(1)
+
+#define M2M_BCR0			0x0010
+#define M2M_BCR1			0x0014
+#define M2M_SAR_BASE0			0x0018
+#define M2M_SAR_BASE1			0x001c
+#define M2M_DAR_BASE0			0x002c
+#define M2M_DAR_BASE1			0x0030
+
+#define DMA_MAX_CHAN_BYTES		0xffff
+#define DMA_MAX_CHAN_DESCRIPTORS	32
+
+struct ep93xx_dma_engine;
+
+/**
+ * struct ep93xx_dma_desc - EP93xx specific transaction descriptor
+ * @src_addr: source address of the transaction
+ * @dst_addr: destination address of the transaction
+ * @size: size of the transaction (in bytes)
+ * @complete: this descriptor is completed
+ * @txd: dmaengine API descriptor
+ * @tx_list: list of linked descriptors
+ * @node: link used for putting this into a channel queue
+ */
+struct ep93xx_dma_desc {
+	u32				src_addr;
+	u32				dst_addr;
+	size_t				size;
+	bool				complete;
+	struct dma_async_tx_descriptor	txd;
+	struct list_head		tx_list;
+	struct list_head		node;
+};
+
+/**
+ * struct ep93xx_dma_chan - an EP93xx DMA M2P/M2M channel
+ * @chan: dmaengine API channel
+ * @edma: pointer to to the engine device
+ * @regs: memory mapped registers
+ * @irq: interrupt number of the channel
+ * @clk: clock used by this channel
+ * @tasklet: channel specific tasklet used for callbacks
+ * @lock: lock protecting the fields following
+ * @flags: flags for the channel
+ * @buffer: which buffer to use next (0/1)
+ * @last_completed: last completed cookie value
+ * @active: flattened chain of descriptors currently being processed
+ * @queue: pending descriptors which are handled next
+ * @free_list: list of free descriptors which can be used
+ * @runtime_addr: physical address currently used as dest/src (M2M only). This
+ *                is set via %DMA_SLAVE_CONFIG before slave operation is
+ *                prepared
+ * @runtime_ctrl: M2M runtime values for the control register.
+ *
+ * As EP93xx DMA controller doesn't support real chained DMA descriptors we
+ * will have slightly different scheme here: @active points to a head of
+ * flattened DMA descriptor chain.
+ *
+ * @queue holds pending transactions. These are linked through the first
+ * descriptor in the chain. When a descriptor is moved to the @active queue,
+ * the first and chained descriptors are flattened into a single list.
+ *
+ * @chan.private holds pointer to &struct ep93xx_dma_data which contains
+ * necessary channel configuration information. For memcpy channels this must
+ * be %NULL.
+ */
+struct ep93xx_dma_chan {
+	struct dma_chan			chan;
+	const struct ep93xx_dma_engine	*edma;
+	void __iomem			*regs;
+	int				irq;
+	struct clk			*clk;
+	struct tasklet_struct		tasklet;
+	/* protects the fields following */
+	spinlock_t			lock;
+	unsigned long			flags;
+/* Channel is configured for cyclic transfers */
+#define EP93XX_DMA_IS_CYCLIC		0
+
+	int				buffer;
+	dma_cookie_t			last_completed;
+	struct list_head		active;
+	struct list_head		queue;
+	struct list_head		free_list;
+	u32				runtime_addr;
+	u32				runtime_ctrl;
+};
+
+/**
+ * struct ep93xx_dma_engine - the EP93xx DMA engine instance
+ * @dma_dev: holds the dmaengine device
+ * @m2m: is this an M2M or M2P device
+ * @hw_setup: method which sets the channel up for operation
+ * @hw_shutdown: shuts the channel down and flushes whatever is left
+ * @hw_submit: pushes active descriptor(s) to the hardware
+ * @hw_interrupt: handle the interrupt
+ * @num_channels: number of channels for this instance
+ * @channels: array of channels
+ *
+ * There is one instance of this struct for the M2P channels and one for the
+ * M2M channels. hw_xxx() methods are used to perform operations which are
+ * different on M2M and M2P channels. These methods are called with channel
+ * lock held and interrupts disabled so they cannot sleep.
+ */
+struct ep93xx_dma_engine {
+	struct dma_device	dma_dev;
+	bool			m2m;
+	int			(*hw_setup)(struct ep93xx_dma_chan *);
+	void			(*hw_shutdown)(struct ep93xx_dma_chan *);
+	void			(*hw_submit)(struct ep93xx_dma_chan *);
+	int			(*hw_interrupt)(struct ep93xx_dma_chan *);
+#define INTERRUPT_UNKNOWN	0
+#define INTERRUPT_DONE		1
+#define INTERRUPT_NEXT_BUFFER	2
+
+	size_t			num_channels;
+	struct ep93xx_dma_chan	channels[];
+};
+
+static inline struct device *chan2dev(struct ep93xx_dma_chan *edmac)
+{
+	return &edmac->chan.dev->device;
+}
+
+static struct ep93xx_dma_chan *to_ep93xx_dma_chan(struct dma_chan *chan)
+{
+	return container_of(chan, struct ep93xx_dma_chan, chan);
+}
+
+/**
+ * ep93xx_dma_set_active - set new active descriptor chain
+ * @edmac: channel
+ * @desc: head of the new active descriptor chain
+ *
+ * Sets @desc to be the head of the new active descriptor chain. This is the
+ * chain which is processed next. The active list must be empty before calling
+ * this function.
+ *
+ * Called with @edmac->lock held and interrupts disabled.
+ */
+static void ep93xx_dma_set_active(struct ep93xx_dma_chan *edmac,
+				  struct ep93xx_dma_desc *desc)
+{
+	BUG_ON(!list_empty(&edmac->active));
+
+	list_add_tail(&desc->node, &edmac->active);
+
+	/* Flatten the @desc->tx_list chain into @edmac->active list */
+	while (!list_empty(&desc->tx_list)) {
+		struct ep93xx_dma_desc *d = list_first_entry(&desc->tx_list,
+			struct ep93xx_dma_desc, node);
+
+		/*
+		 * We copy the callback parameters from the first descriptor
+		 * to all the chained descriptors. This way we can call the
+		 * callback without having to find out the first descriptor in
+		 * the chain. Useful for cyclic transfers.
+		 */
+		d->txd.callback = desc->txd.callback;
+		d->txd.callback_param = desc->txd.callback_param;
+
+		list_move_tail(&d->node, &edmac->active);
+	}
+}
+
+/* Called with @edmac->lock held and interrupts disabled */
+static struct ep93xx_dma_desc *
+ep93xx_dma_get_active(struct ep93xx_dma_chan *edmac)
+{
+	return list_first_entry(&edmac->active, struct ep93xx_dma_desc, node);
+}
+
+/**
+ * ep93xx_dma_advance_active - advances to the next active descriptor
+ * @edmac: channel
+ *
+ * Function advances active descriptor to the next in the @edmac->active and
+ * returns %true if we still have descriptors in the chain to process.
+ * Otherwise returns %false.
+ *
+ * When the channel is in cyclic mode always returns %true.
+ *
+ * Called with @edmac->lock held and interrupts disabled.
+ */
+static bool ep93xx_dma_advance_active(struct ep93xx_dma_chan *edmac)
+{
+	list_rotate_left(&edmac->active);
+
+	if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags))
+		return true;
+
+	/*
+	 * If txd.cookie is set it means that we are back in the first
+	 * descriptor in the chain and hence done with it.
+	 */
+	return !ep93xx_dma_get_active(edmac)->txd.cookie;
+}
+
+/*
+ * M2P DMA implementation
+ */
+
+static void m2p_set_control(struct ep93xx_dma_chan *edmac, u32 control)
+{
+	writel(control, edmac->regs + M2P_CONTROL);
+	/*
+	 * EP93xx User's Guide states that we must perform a dummy read after
+	 * write to the control register.
+	 */
+	readl(edmac->regs + M2P_CONTROL);
+}
+
+static int m2p_hw_setup(struct ep93xx_dma_chan *edmac)
+{
+	struct ep93xx_dma_data *data = edmac->chan.private;
+	u32 control;
+
+	writel(data->port & 0xf, edmac->regs + M2P_PPALLOC);
+
+	control = M2P_CONTROL_CH_ERROR_INT | M2P_CONTROL_ICE
+		| M2P_CONTROL_ENABLE;
+	m2p_set_control(edmac, control);
+
+	return 0;
+}
+
+static inline u32 m2p_channel_state(struct ep93xx_dma_chan *edmac)
+{
+	return (readl(edmac->regs + M2P_STATUS) >> 4) & 0x3;
+}
+
+static void m2p_hw_shutdown(struct ep93xx_dma_chan *edmac)
+{
+	u32 control;
+
+	control = readl(edmac->regs + M2P_CONTROL);
+	control &= ~(M2P_CONTROL_STALLINT | M2P_CONTROL_NFBINT);
+	m2p_set_control(edmac, control);
+
+	while (m2p_channel_state(edmac) >= M2P_STATE_ON)
+		cpu_relax();
+
+	m2p_set_control(edmac, 0);
+
+	while (m2p_channel_state(edmac) == M2P_STATE_STALL)
+		cpu_relax();
+}
+
+static void m2p_fill_desc(struct ep93xx_dma_chan *edmac)
+{
+	struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac);
+	u32 bus_addr;
+
+	if (ep93xx_dma_chan_direction(&edmac->chan) == DMA_TO_DEVICE)
+		bus_addr = desc->src_addr;
+	else
+		bus_addr = desc->dst_addr;
+
+	if (edmac->buffer == 0) {
+		writel(desc->size, edmac->regs + M2P_MAXCNT0);
+		writel(bus_addr, edmac->regs + M2P_BASE0);
+	} else {
+		writel(desc->size, edmac->regs + M2P_MAXCNT1);
+		writel(bus_addr, edmac->regs + M2P_BASE1);
+	}
+
+	edmac->buffer ^= 1;
+}
+
+static void m2p_hw_submit(struct ep93xx_dma_chan *edmac)
+{
+	u32 control = readl(edmac->regs + M2P_CONTROL);
+
+	m2p_fill_desc(edmac);
+	control |= M2P_CONTROL_STALLINT;
+
+	if (ep93xx_dma_advance_active(edmac)) {
+		m2p_fill_desc(edmac);
+		control |= M2P_CONTROL_NFBINT;
+	}
+
+	m2p_set_control(edmac, control);
+}
+
+static int m2p_hw_interrupt(struct ep93xx_dma_chan *edmac)
+{
+	u32 irq_status = readl(edmac->regs + M2P_INTERRUPT);
+	u32 control;
+
+	if (irq_status & M2P_INTERRUPT_ERROR) {
+		struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac);
+
+		/* Clear the error interrupt */
+		writel(1, edmac->regs + M2P_INTERRUPT);
+
+		/*
+		 * It seems that there is no easy way of reporting errors back
+		 * to client so we just report the error here and continue as
+		 * usual.
+		 *
+		 * Revisit this when there is a mechanism to report back the
+		 * errors.
+		 */
+		dev_err(chan2dev(edmac),
+			"DMA transfer failed! Details:\n"
+			"\tcookie	: %d\n"
+			"\tsrc_addr	: 0x%08x\n"
+			"\tdst_addr	: 0x%08x\n"
+			"\tsize		: %zu\n",
+			desc->txd.cookie, desc->src_addr, desc->dst_addr,
+			desc->size);
+	}
+
+	switch (irq_status & (M2P_INTERRUPT_STALL | M2P_INTERRUPT_NFB)) {
+	case M2P_INTERRUPT_STALL:
+		/* Disable interrupts */
+		control = readl(edmac->regs + M2P_CONTROL);
+		control &= ~(M2P_CONTROL_STALLINT | M2P_CONTROL_NFBINT);
+		m2p_set_control(edmac, control);
+
+		return INTERRUPT_DONE;
+
+	case M2P_INTERRUPT_NFB:
+		if (ep93xx_dma_advance_active(edmac))
+			m2p_fill_desc(edmac);
+
+		return INTERRUPT_NEXT_BUFFER;
+	}
+
+	return INTERRUPT_UNKNOWN;
+}
+
+/*
+ * M2M DMA implementation
+ *
+ * For the M2M transfers we don't use NFB at all. This is because it simply
+ * doesn't work well with memcpy transfers. When you submit both buffers it is
+ * extremely unlikely that you get an NFB interrupt, but it instead reports
+ * DONE interrupt and both buffers are already transferred which means that we
+ * weren't able to update the next buffer.
+ *
+ * So for now we "simulate" NFB by just submitting buffer after buffer
+ * without double buffering.
+ */
+
+static int m2m_hw_setup(struct ep93xx_dma_chan *edmac)
+{
+	const struct ep93xx_dma_data *data = edmac->chan.private;
+	u32 control = 0;
+
+	if (!data) {
+		/* This is memcpy channel, nothing to configure */
+		writel(control, edmac->regs + M2M_CONTROL);
+		return 0;
+	}
+
+	switch (data->port) {
+	case EP93XX_DMA_SSP:
+		/*
+		 * This was found via experimenting - anything less than 5
+		 * causes the channel to perform only a partial transfer which
+		 * leads to problems since we don't get DONE interrupt then.
+		 */
+		control = (5 << M2M_CONTROL_PWSC_SHIFT);
+		control |= M2M_CONTROL_NO_HDSK;
+
+		if (data->direction == DMA_TO_DEVICE) {
+			control |= M2M_CONTROL_DAH;
+			control |= M2M_CONTROL_TM_TX;
+			control |= M2M_CONTROL_RSS_SSPTX;
+		} else {
+			control |= M2M_CONTROL_SAH;
+			control |= M2M_CONTROL_TM_RX;
+			control |= M2M_CONTROL_RSS_SSPRX;
+		}
+		break;
+
+	case EP93XX_DMA_IDE:
+		/*
+		 * This IDE part is totally untested. Values below are taken
+		 * from the EP93xx Users's Guide and might not be correct.
+		 */
+		control |= M2M_CONTROL_NO_HDSK;
+		control |= M2M_CONTROL_RSS_IDE;
+		control |= M2M_CONTROL_PW_16;
+
+		if (data->direction == DMA_TO_DEVICE) {
+			/* Worst case from the UG */
+			control = (3 << M2M_CONTROL_PWSC_SHIFT);
+			control |= M2M_CONTROL_DAH;
+			control |= M2M_CONTROL_TM_TX;
+		} else {
+			control = (2 << M2M_CONTROL_PWSC_SHIFT);
+			control |= M2M_CONTROL_SAH;
+			control |= M2M_CONTROL_TM_RX;
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	writel(control, edmac->regs + M2M_CONTROL);
+	return 0;
+}
+
+static void m2m_hw_shutdown(struct ep93xx_dma_chan *edmac)
+{
+	/* Just disable the channel */
+	writel(0, edmac->regs + M2M_CONTROL);
+}
+
+static void m2m_fill_desc(struct ep93xx_dma_chan *edmac)
+{
+	struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac);
+
+	if (edmac->buffer == 0) {
+		writel(desc->src_addr, edmac->regs + M2M_SAR_BASE0);
+		writel(desc->dst_addr, edmac->regs + M2M_DAR_BASE0);
+		writel(desc->size, edmac->regs + M2M_BCR0);
+	} else {
+		writel(desc->src_addr, edmac->regs + M2M_SAR_BASE1);
+		writel(desc->dst_addr, edmac->regs + M2M_DAR_BASE1);
+		writel(desc->size, edmac->regs + M2M_BCR1);
+	}
+
+	edmac->buffer ^= 1;
+}
+
+static void m2m_hw_submit(struct ep93xx_dma_chan *edmac)
+{
+	struct ep93xx_dma_data *data = edmac->chan.private;
+	u32 control = readl(edmac->regs + M2M_CONTROL);
+
+	/*
+	 * Since we allow clients to configure PW (peripheral width) we always
+	 * clear PW bits here and then set them according what is given in
+	 * the runtime configuration.
+	 */
+	control &= ~M2M_CONTROL_PW_MASK;
+	control |= edmac->runtime_ctrl;
+
+	m2m_fill_desc(edmac);
+	control |= M2M_CONTROL_DONEINT;
+
+	/*
+	 * Now we can finally enable the channel. For M2M channel this must be
+	 * done _after_ the BCRx registers are programmed.
+	 */
+	control |= M2M_CONTROL_ENABLE;
+	writel(control, edmac->regs + M2M_CONTROL);
+
+	if (!data) {
+		/*
+		 * For memcpy channels the software trigger must be asserted
+		 * in order to start the memcpy operation.
+		 */
+		control |= M2M_CONTROL_START;
+		writel(control, edmac->regs + M2M_CONTROL);
+	}
+}
+
+static int m2m_hw_interrupt(struct ep93xx_dma_chan *edmac)
+{
+	u32 control;
+
+	if (!(readl(edmac->regs + M2M_INTERRUPT) & M2M_INTERRUPT_DONEINT))
+		return INTERRUPT_UNKNOWN;
+
+	/* Clear the DONE bit */
+	writel(0, edmac->regs + M2M_INTERRUPT);
+
+	/* Disable interrupts and the channel */
+	control = readl(edmac->regs + M2M_CONTROL);
+	control &= ~(M2M_CONTROL_DONEINT | M2M_CONTROL_ENABLE);
+	writel(control, edmac->regs + M2M_CONTROL);
+
+	/*
+	 * Since we only get DONE interrupt we have to find out ourselves
+	 * whether there still is something to process. So we try to advance
+	 * the chain an see whether it succeeds.
+	 */
+	if (ep93xx_dma_advance_active(edmac)) {
+		edmac->edma->hw_submit(edmac);
+		return INTERRUPT_NEXT_BUFFER;
+	}
+
+	return INTERRUPT_DONE;
+}
+
+/*
+ * DMA engine API implementation
+ */
+
+static struct ep93xx_dma_desc *
+ep93xx_dma_desc_get(struct ep93xx_dma_chan *edmac)
+{
+	struct ep93xx_dma_desc *desc, *_desc;
+	struct ep93xx_dma_desc *ret = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&edmac->lock, flags);
+	list_for_each_entry_safe(desc, _desc, &edmac->free_list, node) {
+		if (async_tx_test_ack(&desc->txd)) {
+			list_del_init(&desc->node);
+
+			/* Re-initialize the descriptor */
+			desc->src_addr = 0;
+			desc->dst_addr = 0;
+			desc->size = 0;
+			desc->complete = false;
+			desc->txd.cookie = 0;
+			desc->txd.callback = NULL;
+			desc->txd.callback_param = NULL;
+
+			ret = desc;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&edmac->lock, flags);
+	return ret;
+}
+
+static void ep93xx_dma_desc_put(struct ep93xx_dma_chan *edmac,
+				struct ep93xx_dma_desc *desc)
+{
+	if (desc) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&edmac->lock, flags);
+		list_splice_init(&desc->tx_list, &edmac->free_list);
+		list_add(&desc->node, &edmac->free_list);
+		spin_unlock_irqrestore(&edmac->lock, flags);
+	}
+}
+
+/**
+ * ep93xx_dma_advance_work - start processing the next pending transaction
+ * @edmac: channel
+ *
+ * If we have pending transactions queued and we are currently idling, this
+ * function takes the next queued transaction from the @edmac->queue and
+ * pushes it to the hardware for execution.
+ */
+static void ep93xx_dma_advance_work(struct ep93xx_dma_chan *edmac)
+{
+	struct ep93xx_dma_desc *new;
+	unsigned long flags;
+
+	spin_lock_irqsave(&edmac->lock, flags);
+	if (!list_empty(&edmac->active) || list_empty(&edmac->queue)) {
+		spin_unlock_irqrestore(&edmac->lock, flags);
+		return;
+	}
+
+	/* Take the next descriptor from the pending queue */
+	new = list_first_entry(&edmac->queue, struct ep93xx_dma_desc, node);
+	list_del_init(&new->node);
+
+	ep93xx_dma_set_active(edmac, new);
+
+	/* Push it to the hardware */
+	edmac->edma->hw_submit(edmac);
+	spin_unlock_irqrestore(&edmac->lock, flags);
+}
+
+static void ep93xx_dma_unmap_buffers(struct ep93xx_dma_desc *desc)
+{
+	struct device *dev = desc->txd.chan->device->dev;
+
+	if (!(desc->txd.flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
+		if (desc->txd.flags & DMA_COMPL_SRC_UNMAP_SINGLE)
+			dma_unmap_single(dev, desc->src_addr, desc->size,
+					 DMA_TO_DEVICE);
+		else
+			dma_unmap_page(dev, desc->src_addr, desc->size,
+				       DMA_TO_DEVICE);
+	}
+	if (!(desc->txd.flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
+		if (desc->txd.flags & DMA_COMPL_DEST_UNMAP_SINGLE)
+			dma_unmap_single(dev, desc->dst_addr, desc->size,
+					 DMA_FROM_DEVICE);
+		else
+			dma_unmap_page(dev, desc->dst_addr, desc->size,
+				       DMA_FROM_DEVICE);
+	}
+}
+
+static void ep93xx_dma_tasklet(unsigned long data)
+{
+	struct ep93xx_dma_chan *edmac = (struct ep93xx_dma_chan *)data;
+	struct ep93xx_dma_desc *desc, *d;
+	dma_async_tx_callback callback;
+	void *callback_param;
+	LIST_HEAD(list);
+
+	spin_lock_irq(&edmac->lock);
+	desc = ep93xx_dma_get_active(edmac);
+	if (desc->complete) {
+		edmac->last_completed = desc->txd.cookie;
+		list_splice_init(&edmac->active, &list);
+	}
+	spin_unlock_irq(&edmac->lock);
+
+	/* Pick up the next descriptor from the queue */
+	ep93xx_dma_advance_work(edmac);
+
+	callback = desc->txd.callback;
+	callback_param = desc->txd.callback_param;
+
+	/* Now we can release all the chained descriptors */
+	list_for_each_entry_safe(desc, d, &list, node) {
+		/*
+		 * For the memcpy channels the API requires us to unmap the
+		 * buffers unless requested otherwise.
+		 */
+		if (!edmac->chan.private)
+			ep93xx_dma_unmap_buffers(desc);
+
+		ep93xx_dma_desc_put(edmac, desc);
+	}
+
+	if (callback)
+		callback(callback_param);
+}
+
+static irqreturn_t ep93xx_dma_interrupt(int irq, void *dev_id)
+{
+	struct ep93xx_dma_chan *edmac = dev_id;
+	irqreturn_t ret = IRQ_HANDLED;
+
+	spin_lock(&edmac->lock);
+
+	switch (edmac->edma->hw_interrupt(edmac)) {
+	case INTERRUPT_DONE:
+		ep93xx_dma_get_active(edmac)->complete = true;
+		tasklet_schedule(&edmac->tasklet);
+		break;
+
+	case INTERRUPT_NEXT_BUFFER:
+		if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags))
+			tasklet_schedule(&edmac->tasklet);
+		break;
+
+	default:
+		dev_warn(chan2dev(edmac), "unknown interrupt!\n");
+		ret = IRQ_NONE;
+		break;
+	}
+
+	spin_unlock(&edmac->lock);
+	return ret;
+}
+
+/**
+ * ep93xx_dma_tx_submit - set the prepared descriptor(s) to be executed
+ * @tx: descriptor to be executed
+ *
+ * Function will execute given descriptor on the hardware or if the hardware
+ * is busy, queue the descriptor to be executed later on. Returns cookie which
+ * can be used to poll the status of the descriptor.
+ */
+static dma_cookie_t ep93xx_dma_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+	struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(tx->chan);
+	struct ep93xx_dma_desc *desc;
+	dma_cookie_t cookie;
+	unsigned long flags;
+
+	spin_lock_irqsave(&edmac->lock, flags);
+
+	cookie = edmac->chan.cookie;
+
+	if (++cookie < 0)
+		cookie = 1;
+
+	desc = container_of(tx, struct ep93xx_dma_desc, txd);
+
+	edmac->chan.cookie = cookie;
+	desc->txd.cookie = cookie;
+
+	/*
+	 * If nothing is currently prosessed, we push this descriptor
+	 * directly to the hardware. Otherwise we put the descriptor
+	 * to the pending queue.
+	 */
+	if (list_empty(&edmac->active)) {
+		ep93xx_dma_set_active(edmac, desc);
+		edmac->edma->hw_submit(edmac);
+	} else {
+		list_add_tail(&desc->node, &edmac->queue);
+	}
+
+	spin_unlock_irqrestore(&edmac->lock, flags);
+	return cookie;
+}
+
+/**
+ * ep93xx_dma_alloc_chan_resources - allocate resources for the channel
+ * @chan: channel to allocate resources
+ *
+ * Function allocates necessary resources for the given DMA channel and
+ * returns number of allocated descriptors for the channel. Negative errno
+ * is returned in case of failure.
+ */
+static int ep93xx_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+	struct ep93xx_dma_data *data = chan->private;
+	const char *name = dma_chan_name(chan);
+	int ret, i;
+
+	/* Sanity check the channel parameters */
+	if (!edmac->edma->m2m) {
+		if (!data)
+			return -EINVAL;
+		if (data->port < EP93XX_DMA_I2S1 ||
+		    data->port > EP93XX_DMA_IRDA)
+			return -EINVAL;
+		if (data->direction != ep93xx_dma_chan_direction(chan))
+			return -EINVAL;
+	} else {
+		if (data) {
+			switch (data->port) {
+			case EP93XX_DMA_SSP:
+			case EP93XX_DMA_IDE:
+				if (data->direction != DMA_TO_DEVICE &&
+				    data->direction != DMA_FROM_DEVICE)
+					return -EINVAL;
+				break;
+			default:
+				return -EINVAL;
+			}
+		}
+	}
+
+	if (data && data->name)
+		name = data->name;
+
+	ret = clk_enable(edmac->clk);
+	if (ret)
+		return ret;
+
+	ret = request_irq(edmac->irq, ep93xx_dma_interrupt, 0, name, edmac);
+	if (ret)
+		goto fail_clk_disable;
+
+	spin_lock_irq(&edmac->lock);
+	edmac->last_completed = 1;
+	edmac->chan.cookie = 1;
+	ret = edmac->edma->hw_setup(edmac);
+	spin_unlock_irq(&edmac->lock);
+
+	if (ret)
+		goto fail_free_irq;
+
+	for (i = 0; i < DMA_MAX_CHAN_DESCRIPTORS; i++) {
+		struct ep93xx_dma_desc *desc;
+
+		desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+		if (!desc) {
+			dev_warn(chan2dev(edmac), "not enough descriptors\n");
+			break;
+		}
+
+		INIT_LIST_HEAD(&desc->tx_list);
+
+		dma_async_tx_descriptor_init(&desc->txd, chan);
+		desc->txd.flags = DMA_CTRL_ACK;
+		desc->txd.tx_submit = ep93xx_dma_tx_submit;
+
+		ep93xx_dma_desc_put(edmac, desc);
+	}
+
+	return i;
+
+fail_free_irq:
+	free_irq(edmac->irq, edmac);
+fail_clk_disable:
+	clk_disable(edmac->clk);
+
+	return ret;
+}
+
+/**
+ * ep93xx_dma_free_chan_resources - release resources for the channel
+ * @chan: channel
+ *
+ * Function releases all the resources allocated for the given channel.
+ * The channel must be idle when this is called.
+ */
+static void ep93xx_dma_free_chan_resources(struct dma_chan *chan)
+{
+	struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+	struct ep93xx_dma_desc *desc, *d;
+	unsigned long flags;
+	LIST_HEAD(list);
+
+	BUG_ON(!list_empty(&edmac->active));
+	BUG_ON(!list_empty(&edmac->queue));
+
+	spin_lock_irqsave(&edmac->lock, flags);
+	edmac->edma->hw_shutdown(edmac);
+	edmac->runtime_addr = 0;
+	edmac->runtime_ctrl = 0;
+	edmac->buffer = 0;
+	list_splice_init(&edmac->free_list, &list);
+	spin_unlock_irqrestore(&edmac->lock, flags);
+
+	list_for_each_entry_safe(desc, d, &list, node)
+		kfree(desc);
+
+	clk_disable(edmac->clk);
+	free_irq(edmac->irq, edmac);
+}
+
+/**
+ * ep93xx_dma_prep_dma_memcpy - prepare a memcpy DMA operation
+ * @chan: channel
+ * @dest: destination bus address
+ * @src: source bus address
+ * @len: size of the transaction
+ * @flags: flags for the descriptor
+ *
+ * Returns a valid DMA descriptor or %NULL in case of failure.
+ */
+struct dma_async_tx_descriptor *
+ep93xx_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
+			   dma_addr_t src, size_t len, unsigned long flags)
+{
+	struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+	struct ep93xx_dma_desc *desc, *first;
+	size_t bytes, offset;
+
+	first = NULL;
+	for (offset = 0; offset < len; offset += bytes) {
+		desc = ep93xx_dma_desc_get(edmac);
+		if (!desc) {
+			dev_warn(chan2dev(edmac), "couln't get descriptor\n");
+			goto fail;
+		}
+
+		bytes = min_t(size_t, len - offset, DMA_MAX_CHAN_BYTES);
+
+		desc->src_addr = src + offset;
+		desc->dst_addr = dest + offset;
+		desc->size = bytes;
+
+		if (!first)
+			first = desc;
+		else
+			list_add_tail(&desc->node, &first->tx_list);
+	}
+
+	first->txd.cookie = -EBUSY;
+	first->txd.flags = flags;
+
+	return &first->txd;
+fail:
+	ep93xx_dma_desc_put(edmac, first);
+	return NULL;
+}
+
+/**
+ * ep93xx_dma_prep_slave_sg - prepare a slave DMA operation
+ * @chan: channel
+ * @sgl: list of buffers to transfer
+ * @sg_len: number of entries in @sgl
+ * @dir: direction of tha DMA transfer
+ * @flags: flags for the descriptor
+ *
+ * Returns a valid DMA descriptor or %NULL in case of failure.
+ */
+static struct dma_async_tx_descriptor *
+ep93xx_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+			 unsigned int sg_len, enum dma_data_direction dir,
+			 unsigned long flags)
+{
+	struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+	struct ep93xx_dma_desc *desc, *first;
+	struct scatterlist *sg;
+	int i;
+
+	if (!edmac->edma->m2m && dir != ep93xx_dma_chan_direction(chan)) {
+		dev_warn(chan2dev(edmac),
+			 "channel was configured with different direction\n");
+		return NULL;
+	}
+
+	if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags)) {
+		dev_warn(chan2dev(edmac),
+			 "channel is already used for cyclic transfers\n");
+		return NULL;
+	}
+
+	first = NULL;
+	for_each_sg(sgl, sg, sg_len, i) {
+		size_t sg_len = sg_dma_len(sg);
+
+		if (sg_len > DMA_MAX_CHAN_BYTES) {
+			dev_warn(chan2dev(edmac), "too big transfer size %d\n",
+				 sg_len);
+			goto fail;
+		}
+
+		desc = ep93xx_dma_desc_get(edmac);
+		if (!desc) {
+			dev_warn(chan2dev(edmac), "couln't get descriptor\n");
+			goto fail;
+		}
+
+		if (dir == DMA_TO_DEVICE) {
+			desc->src_addr = sg_dma_address(sg);
+			desc->dst_addr = edmac->runtime_addr;
+		} else {
+			desc->src_addr = edmac->runtime_addr;
+			desc->dst_addr = sg_dma_address(sg);
+		}
+		desc->size = sg_len;
+
+		if (!first)
+			first = desc;
+		else
+			list_add_tail(&desc->node, &first->tx_list);
+	}
+
+	first->txd.cookie = -EBUSY;
+	first->txd.flags = flags;
+
+	return &first->txd;
+
+fail:
+	ep93xx_dma_desc_put(edmac, first);
+	return NULL;
+}
+
+/**
+ * ep93xx_dma_prep_dma_cyclic - prepare a cyclic DMA operation
+ * @chan: channel
+ * @dma_addr: DMA mapped address of the buffer
+ * @buf_len: length of the buffer (in bytes)
+ * @period_len: lenght of a single period
+ * @dir: direction of the operation
+ *
+ * Prepares a descriptor for cyclic DMA operation. This means that once the
+ * descriptor is submitted, we will be submitting in a @period_len sized
+ * buffers and calling callback once the period has been elapsed. Transfer
+ * terminates only when client calls dmaengine_terminate_all() for this
+ * channel.
+ *
+ * Returns a valid DMA descriptor or %NULL in case of failure.
+ */
+static struct dma_async_tx_descriptor *
+ep93xx_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr,
+			   size_t buf_len, size_t period_len,
+			   enum dma_data_direction dir)
+{
+	struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+	struct ep93xx_dma_desc *desc, *first;
+	size_t offset = 0;
+
+	if (!edmac->edma->m2m && dir != ep93xx_dma_chan_direction(chan)) {
+		dev_warn(chan2dev(edmac),
+			 "channel was configured with different direction\n");
+		return NULL;
+	}
+
+	if (test_and_set_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags)) {
+		dev_warn(chan2dev(edmac),
+			 "channel is already used for cyclic transfers\n");
+		return NULL;
+	}
+
+	if (period_len > DMA_MAX_CHAN_BYTES) {
+		dev_warn(chan2dev(edmac), "too big period length %d\n",
+			 period_len);
+		return NULL;
+	}
+
+	/* Split the buffer into period size chunks */
+	first = NULL;
+	for (offset = 0; offset < buf_len; offset += period_len) {
+		desc = ep93xx_dma_desc_get(edmac);
+		if (!desc) {
+			dev_warn(chan2dev(edmac), "couln't get descriptor\n");
+			goto fail;
+		}
+
+		if (dir == DMA_TO_DEVICE) {
+			desc->src_addr = dma_addr + offset;
+			desc->dst_addr = edmac->runtime_addr;
+		} else {
+			desc->src_addr = edmac->runtime_addr;
+			desc->dst_addr = dma_addr + offset;
+		}
+
+		desc->size = period_len;
+
+		if (!first)
+			first = desc;
+		else
+			list_add_tail(&desc->node, &first->tx_list);
+	}
+
+	first->txd.cookie = -EBUSY;
+
+	return &first->txd;
+
+fail:
+	ep93xx_dma_desc_put(edmac, first);
+	return NULL;
+}
+
+/**
+ * ep93xx_dma_terminate_all - terminate all transactions
+ * @edmac: channel
+ *
+ * Stops all DMA transactions. All descriptors are put back to the
+ * @edmac->free_list and callbacks are _not_ called.
+ */
+static int ep93xx_dma_terminate_all(struct ep93xx_dma_chan *edmac)
+{
+	struct ep93xx_dma_desc *desc, *_d;
+	unsigned long flags;
+	LIST_HEAD(list);
+
+	spin_lock_irqsave(&edmac->lock, flags);
+	/* First we disable and flush the DMA channel */
+	edmac->edma->hw_shutdown(edmac);
+	clear_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags);
+	list_splice_init(&edmac->active, &list);
+	list_splice_init(&edmac->queue, &list);
+	/*
+	 * We then re-enable the channel. This way we can continue submitting
+	 * the descriptors by just calling ->hw_submit() again.
+	 */
+	edmac->edma->hw_setup(edmac);
+	spin_unlock_irqrestore(&edmac->lock, flags);
+
+	list_for_each_entry_safe(desc, _d, &list, node)
+		ep93xx_dma_desc_put(edmac, desc);
+
+	return 0;
+}
+
+static int ep93xx_dma_slave_config(struct ep93xx_dma_chan *edmac,
+				   struct dma_slave_config *config)
+{
+	enum dma_slave_buswidth width;
+	unsigned long flags;
+	u32 addr, ctrl;
+
+	if (!edmac->edma->m2m)
+		return -EINVAL;
+
+	switch (config->direction) {
+	case DMA_FROM_DEVICE:
+		width = config->src_addr_width;
+		addr = config->src_addr;
+		break;
+
+	case DMA_TO_DEVICE:
+		width = config->dst_addr_width;
+		addr = config->dst_addr;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	switch (width) {
+	case DMA_SLAVE_BUSWIDTH_1_BYTE:
+		ctrl = 0;
+		break;
+	case DMA_SLAVE_BUSWIDTH_2_BYTES:
+		ctrl = M2M_CONTROL_PW_16;
+		break;
+	case DMA_SLAVE_BUSWIDTH_4_BYTES:
+		ctrl = M2M_CONTROL_PW_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&edmac->lock, flags);
+	edmac->runtime_addr = addr;
+	edmac->runtime_ctrl = ctrl;
+	spin_unlock_irqrestore(&edmac->lock, flags);
+
+	return 0;
+}
+
+/**
+ * ep93xx_dma_control - manipulate all pending operations on a channel
+ * @chan: channel
+ * @cmd: control command to perform
+ * @arg: optional argument
+ *
+ * Controls the channel. Function returns %0 in case of success or negative
+ * error in case of failure.
+ */
+static int ep93xx_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+			      unsigned long arg)
+{
+	struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+	struct dma_slave_config *config;
+
+	switch (cmd) {
+	case DMA_TERMINATE_ALL:
+		return ep93xx_dma_terminate_all(edmac);
+
+	case DMA_SLAVE_CONFIG:
+		config = (struct dma_slave_config *)arg;
+		return ep93xx_dma_slave_config(edmac, config);
+
+	default:
+		break;
+	}
+
+	return -ENOSYS;
+}
+
+/**
+ * ep93xx_dma_tx_status - check if a transaction is completed
+ * @chan: channel
+ * @cookie: transaction specific cookie
+ * @state: state of the transaction is stored here if given
+ *
+ * This function can be used to query state of a given transaction.
+ */
+static enum dma_status ep93xx_dma_tx_status(struct dma_chan *chan,
+					    dma_cookie_t cookie,
+					    struct dma_tx_state *state)
+{
+	struct ep93xx_dma_chan *edmac = to_ep93xx_dma_chan(chan);
+	dma_cookie_t last_used, last_completed;
+	enum dma_status ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&edmac->lock, flags);
+	last_used = chan->cookie;
+	last_completed = edmac->last_completed;
+	spin_unlock_irqrestore(&edmac->lock, flags);
+
+	ret = dma_async_is_complete(cookie, last_completed, last_used);
+	dma_set_tx_state(state, last_completed, last_used, 0);
+
+	return ret;
+}
+
+/**
+ * ep93xx_dma_issue_pending - push pending transactions to the hardware
+ * @chan: channel
+ *
+ * When this function is called, all pending transactions are pushed to the
+ * hardware and executed.
+ */
+static void ep93xx_dma_issue_pending(struct dma_chan *chan)
+{
+	ep93xx_dma_advance_work(to_ep93xx_dma_chan(chan));
+}
+
+static int __init ep93xx_dma_probe(struct platform_device *pdev)
+{
+	struct ep93xx_dma_platform_data *pdata = dev_get_platdata(&pdev->dev);
+	struct ep93xx_dma_engine *edma;
+	struct dma_device *dma_dev;
+	size_t edma_size;
+	int ret, i;
+
+	edma_size = pdata->num_channels * sizeof(struct ep93xx_dma_chan);
+	edma = kzalloc(sizeof(*edma) + edma_size, GFP_KERNEL);
+	if (!edma)
+		return -ENOMEM;
+
+	dma_dev = &edma->dma_dev;
+	edma->m2m = platform_get_device_id(pdev)->driver_data;
+	edma->num_channels = pdata->num_channels;
+
+	INIT_LIST_HEAD(&dma_dev->channels);
+	for (i = 0; i < pdata->num_channels; i++) {
+		const struct ep93xx_dma_chan_data *cdata = &pdata->channels[i];
+		struct ep93xx_dma_chan *edmac = &edma->channels[i];
+
+		edmac->chan.device = dma_dev;
+		edmac->regs = cdata->base;
+		edmac->irq = cdata->irq;
+		edmac->edma = edma;
+
+		edmac->clk = clk_get(NULL, cdata->name);
+		if (IS_ERR(edmac->clk)) {
+			dev_warn(&pdev->dev, "failed to get clock for %s\n",
+				 cdata->name);
+			continue;
+		}
+
+		spin_lock_init(&edmac->lock);
+		INIT_LIST_HEAD(&edmac->active);
+		INIT_LIST_HEAD(&edmac->queue);
+		INIT_LIST_HEAD(&edmac->free_list);
+		tasklet_init(&edmac->tasklet, ep93xx_dma_tasklet,
+			     (unsigned long)edmac);
+
+		list_add_tail(&edmac->chan.device_node,
+			      &dma_dev->channels);
+	}
+
+	dma_cap_zero(dma_dev->cap_mask);
+	dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
+	dma_cap_set(DMA_CYCLIC, dma_dev->cap_mask);
+
+	dma_dev->dev = &pdev->dev;
+	dma_dev->device_alloc_chan_resources = ep93xx_dma_alloc_chan_resources;
+	dma_dev->device_free_chan_resources = ep93xx_dma_free_chan_resources;
+	dma_dev->device_prep_slave_sg = ep93xx_dma_prep_slave_sg;
+	dma_dev->device_prep_dma_cyclic = ep93xx_dma_prep_dma_cyclic;
+	dma_dev->device_control = ep93xx_dma_control;
+	dma_dev->device_issue_pending = ep93xx_dma_issue_pending;
+	dma_dev->device_tx_status = ep93xx_dma_tx_status;
+
+	dma_set_max_seg_size(dma_dev->dev, DMA_MAX_CHAN_BYTES);
+
+	if (edma->m2m) {
+		dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask);
+		dma_dev->device_prep_dma_memcpy = ep93xx_dma_prep_dma_memcpy;
+
+		edma->hw_setup = m2m_hw_setup;
+		edma->hw_shutdown = m2m_hw_shutdown;
+		edma->hw_submit = m2m_hw_submit;
+		edma->hw_interrupt = m2m_hw_interrupt;
+	} else {
+		dma_cap_set(DMA_PRIVATE, dma_dev->cap_mask);
+
+		edma->hw_setup = m2p_hw_setup;
+		edma->hw_shutdown = m2p_hw_shutdown;
+		edma->hw_submit = m2p_hw_submit;
+		edma->hw_interrupt = m2p_hw_interrupt;
+	}
+
+	ret = dma_async_device_register(dma_dev);
+	if (unlikely(ret)) {
+		for (i = 0; i < edma->num_channels; i++) {
+			struct ep93xx_dma_chan *edmac = &edma->channels[i];
+			if (!IS_ERR_OR_NULL(edmac->clk))
+				clk_put(edmac->clk);
+		}
+		kfree(edma);
+	} else {
+		dev_info(dma_dev->dev, "EP93xx M2%s DMA ready\n",
+			 edma->m2m ? "M" : "P");
+	}
+
+	return ret;
+}
+
+static struct platform_device_id ep93xx_dma_driver_ids[] = {
+	{ "ep93xx-dma-m2p", 0 },
+	{ "ep93xx-dma-m2m", 1 },
+	{ },
+};
+
+static struct platform_driver ep93xx_dma_driver = {
+	.driver		= {
+		.name	= "ep93xx-dma",
+	},
+	.id_table	= ep93xx_dma_driver_ids,
+};
+
+static int __init ep93xx_dma_module_init(void)
+{
+	return platform_driver_probe(&ep93xx_dma_driver, ep93xx_dma_probe);
+}
+subsys_initcall(ep93xx_dma_module_init);
+
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
+MODULE_DESCRIPTION("EP93xx DMA driver");
+MODULE_LICENSE("GPL");
-- 
1.7.4.4


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

* [PATCH v2 2/5] ep93xx: add dmaengine platform code
  2011-05-29 10:10 [PATCH v2 0/5] ep93xx DMA patches Mika Westerberg
  2011-05-29 10:10 ` [PATCH v2 1/5] dmaengine: add ep93xx DMA support Mika Westerberg
@ 2011-05-29 10:10 ` Mika Westerberg
  2011-05-29 10:10 ` [PATCH v2 3/5] ASoC: ep93xx: convert to use the DMA engine API Mika Westerberg
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 36+ messages in thread
From: Mika Westerberg @ 2011-05-29 10:10 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: hsweeten, rmallon, vinod.koul, dan.j.williams, lrg, broonie,
	grant.likely, Mika Westerberg, linux-kernel

Add platform support code for the new EP93xx dmaengine driver.

Signed-off-by: Mika Westerberg <mika.westerberg@iki.fi>
Signed-off-by: Ryan Mallon <rmallon@gmail.com>
Acked-by: H Hartley Sweeten <hsweeten@visionengravers.com>
---
 arch/arm/mach-ep93xx/Makefile |    2 +
 arch/arm/mach-ep93xx/dma.c    |  108 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 110 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-ep93xx/dma.c

diff --git a/arch/arm/mach-ep93xx/Makefile b/arch/arm/mach-ep93xx/Makefile
index 33ee2c8..4920f7a 100644
--- a/arch/arm/mach-ep93xx/Makefile
+++ b/arch/arm/mach-ep93xx/Makefile
@@ -6,6 +6,8 @@ obj-m			:=
 obj-n			:=
 obj-			:=
 
+obj-$(CONFIG_EP93XX_DMA)	+= dma.o
+
 obj-$(CONFIG_MACH_ADSSPHERE)	+= adssphere.o
 obj-$(CONFIG_MACH_EDB93XX)	+= edb93xx.o
 obj-$(CONFIG_MACH_GESBC9312)	+= gesbc9312.o
diff --git a/arch/arm/mach-ep93xx/dma.c b/arch/arm/mach-ep93xx/dma.c
new file mode 100644
index 0000000..5a25708
--- /dev/null
+++ b/arch/arm/mach-ep93xx/dma.c
@@ -0,0 +1,108 @@
+/*
+ * arch/arm/mach-ep93xx/dma.c
+ *
+ * Platform support code for the EP93xx dmaengine driver.
+ *
+ * Copyright (C) 2011 Mika Westerberg
+ *
+ * This work is based on the original dma-m2p implementation with
+ * following copyrights:
+ *
+ *   Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ *   Copyright (C) 2006 Applied Data Systems
+ *   Copyright (C) 2009 Ryan Mallon <rmallon@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <mach/dma.h>
+#include <mach/hardware.h>
+
+#define DMA_CHANNEL(_name, _base, _irq) \
+	{ .name = (_name), .base = (_base), .irq = (_irq) }
+
+/*
+ * DMA M2P channels.
+ *
+ * On the EP93xx chip the following peripherals my be allocated to the 10
+ * Memory to Internal Peripheral (M2P) channels (5 transmit + 5 receive).
+ *
+ *	I2S	contains 3 Tx and 3 Rx DMA Channels
+ *	AAC	contains 3 Tx and 3 Rx DMA Channels
+ *	UART1	contains 1 Tx and 1 Rx DMA Channels
+ *	UART2	contains 1 Tx and 1 Rx DMA Channels
+ *	UART3	contains 1 Tx and 1 Rx DMA Channels
+ *	IrDA	contains 1 Tx and 1 Rx DMA Channels
+ *
+ * Registers are mapped statically in ep93xx_map_io().
+ */
+static struct ep93xx_dma_chan_data ep93xx_dma_m2p_channels[] = {
+	DMA_CHANNEL("m2p0", EP93XX_DMA_BASE + 0x0000, IRQ_EP93XX_DMAM2P0),
+	DMA_CHANNEL("m2p1", EP93XX_DMA_BASE + 0x0040, IRQ_EP93XX_DMAM2P1),
+	DMA_CHANNEL("m2p2", EP93XX_DMA_BASE + 0x0080, IRQ_EP93XX_DMAM2P2),
+	DMA_CHANNEL("m2p3", EP93XX_DMA_BASE + 0x00c0, IRQ_EP93XX_DMAM2P3),
+	DMA_CHANNEL("m2p4", EP93XX_DMA_BASE + 0x0240, IRQ_EP93XX_DMAM2P4),
+	DMA_CHANNEL("m2p5", EP93XX_DMA_BASE + 0x0200, IRQ_EP93XX_DMAM2P5),
+	DMA_CHANNEL("m2p6", EP93XX_DMA_BASE + 0x02c0, IRQ_EP93XX_DMAM2P6),
+	DMA_CHANNEL("m2p7", EP93XX_DMA_BASE + 0x0280, IRQ_EP93XX_DMAM2P7),
+	DMA_CHANNEL("m2p8", EP93XX_DMA_BASE + 0x0340, IRQ_EP93XX_DMAM2P8),
+	DMA_CHANNEL("m2p9", EP93XX_DMA_BASE + 0x0300, IRQ_EP93XX_DMAM2P9),
+};
+
+static struct ep93xx_dma_platform_data ep93xx_dma_m2p_data = {
+	.channels		= ep93xx_dma_m2p_channels,
+	.num_channels		= ARRAY_SIZE(ep93xx_dma_m2p_channels),
+};
+
+static struct platform_device ep93xx_dma_m2p_device = {
+	.name			= "ep93xx-dma-m2p",
+	.id			= -1,
+	.dev			= {
+		.platform_data	= &ep93xx_dma_m2p_data,
+	},
+};
+
+/*
+ * DMA M2M channels.
+ *
+ * There are 2 M2M channels which support memcpy/memset and in addition simple
+ * hardware requests from/to SSP and IDE. We do not implement an external
+ * hardware requests.
+ *
+ * Registers are mapped statically in ep93xx_map_io().
+ */
+static struct ep93xx_dma_chan_data ep93xx_dma_m2m_channels[] = {
+	DMA_CHANNEL("m2m0", EP93XX_DMA_BASE + 0x0100, IRQ_EP93XX_DMAM2M0),
+	DMA_CHANNEL("m2m1", EP93XX_DMA_BASE + 0x0140, IRQ_EP93XX_DMAM2M1),
+};
+
+static struct ep93xx_dma_platform_data ep93xx_dma_m2m_data = {
+	.channels		= ep93xx_dma_m2m_channels,
+	.num_channels		= ARRAY_SIZE(ep93xx_dma_m2m_channels),
+};
+
+static struct platform_device ep93xx_dma_m2m_device = {
+	.name			= "ep93xx-dma-m2m",
+	.id			= -1,
+	.dev			= {
+		.platform_data	= &ep93xx_dma_m2m_data,
+	},
+};
+
+static int __init ep93xx_dma_init(void)
+{
+	platform_device_register(&ep93xx_dma_m2p_device);
+	platform_device_register(&ep93xx_dma_m2m_device);
+	return 0;
+}
+arch_initcall(ep93xx_dma_init);
-- 
1.7.4.4


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

* [PATCH v2 3/5] ASoC: ep93xx: convert to use the DMA engine API
  2011-05-29 10:10 [PATCH v2 0/5] ep93xx DMA patches Mika Westerberg
  2011-05-29 10:10 ` [PATCH v2 1/5] dmaengine: add ep93xx DMA support Mika Westerberg
  2011-05-29 10:10 ` [PATCH v2 2/5] ep93xx: add dmaengine platform code Mika Westerberg
@ 2011-05-29 10:10 ` Mika Westerberg
  2011-05-29 10:10 ` [PATCH v2 4/5] ep93xx: remove the old M2P DMA code Mika Westerberg
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 36+ messages in thread
From: Mika Westerberg @ 2011-05-29 10:10 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: hsweeten, rmallon, vinod.koul, dan.j.williams, lrg, broonie,
	grant.likely, Mika Westerberg, linux-kernel

Now that we have the EP93xx DMA engine driver in place, we convert the ASoC
drivers (I2S, AC97 and PCM) to take advantage of this new API. There are no
functional changes.

Signed-off-by: Mika Westerberg <mika.westerberg@iki.fi>
Acked-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Acked-by: Liam Girdwood <lrg@ti.com>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
 sound/soc/ep93xx/ep93xx-ac97.c |    4 +-
 sound/soc/ep93xx/ep93xx-i2s.c  |    4 +-
 sound/soc/ep93xx/ep93xx-pcm.c  |  137 ++++++++++++++++++++++-----------------
 3 files changed, 81 insertions(+), 64 deletions(-)

diff --git a/sound/soc/ep93xx/ep93xx-ac97.c b/sound/soc/ep93xx/ep93xx-ac97.c
index 104e95c..c7417c7 100644
--- a/sound/soc/ep93xx/ep93xx-ac97.c
+++ b/sound/soc/ep93xx/ep93xx-ac97.c
@@ -106,12 +106,12 @@ static struct ep93xx_ac97_info *ep93xx_ac97_info;
 
 static struct ep93xx_pcm_dma_params ep93xx_ac97_pcm_out = {
 	.name		= "ac97-pcm-out",
-	.dma_port	= EP93XX_DMA_M2P_PORT_AAC1,
+	.dma_port	= EP93XX_DMA_AAC1,
 };
 
 static struct ep93xx_pcm_dma_params ep93xx_ac97_pcm_in = {
 	.name		= "ac97-pcm-in",
-	.dma_port	= EP93XX_DMA_M2P_PORT_AAC1,
+	.dma_port	= EP93XX_DMA_AAC1,
 };
 
 static inline unsigned ep93xx_ac97_read_reg(struct ep93xx_ac97_info *info,
diff --git a/sound/soc/ep93xx/ep93xx-i2s.c b/sound/soc/ep93xx/ep93xx-i2s.c
index 042f4e9..30df425 100644
--- a/sound/soc/ep93xx/ep93xx-i2s.c
+++ b/sound/soc/ep93xx/ep93xx-i2s.c
@@ -70,11 +70,11 @@ struct ep93xx_i2s_info {
 struct ep93xx_pcm_dma_params ep93xx_i2s_dma_params[] = {
 	[SNDRV_PCM_STREAM_PLAYBACK] = {
 		.name		= "i2s-pcm-out",
-		.dma_port	= EP93XX_DMA_M2P_PORT_I2S1,
+		.dma_port	= EP93XX_DMA_I2S1,
 	},
 	[SNDRV_PCM_STREAM_CAPTURE] = {
 		.name		= "i2s-pcm-in",
-		.dma_port	= EP93XX_DMA_M2P_PORT_I2S1,
+		.dma_port	= EP93XX_DMA_I2S1,
 	},
 };
 
diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c
index a456e49..a07f99c 100644
--- a/sound/soc/ep93xx/ep93xx-pcm.c
+++ b/sound/soc/ep93xx/ep93xx-pcm.c
@@ -16,6 +16,7 @@
 #include <linux/init.h>
 #include <linux/device.h>
 #include <linux/slab.h>
+#include <linux/dmaengine.h>
 #include <linux/dma-mapping.h>
 
 #include <sound/core.h>
@@ -53,43 +54,34 @@ static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
 
 struct ep93xx_runtime_data
 {
-	struct ep93xx_dma_m2p_client	cl;
-	struct ep93xx_pcm_dma_params	*params;
 	int				pointer_bytes;
-	struct tasklet_struct		period_tasklet;
 	int				periods;
-	struct ep93xx_dma_buffer	buf[32];
+	int				period_bytes;
+	struct dma_chan			*dma_chan;
+	struct ep93xx_dma_data		dma_data;
 };
 
-static void ep93xx_pcm_period_elapsed(unsigned long data)
+static void ep93xx_pcm_dma_callback(void *data)
 {
-	struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
-	snd_pcm_period_elapsed(substream);
-}
+	struct snd_pcm_substream *substream = data;
+	struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
 
-static void ep93xx_pcm_buffer_started(void *cookie,
-				      struct ep93xx_dma_buffer *buf)
-{
+	rtd->pointer_bytes += rtd->period_bytes;
+	rtd->pointer_bytes %= rtd->period_bytes * rtd->periods;
+
+	snd_pcm_period_elapsed(substream);
 }
 
-static void ep93xx_pcm_buffer_finished(void *cookie, 
-				       struct ep93xx_dma_buffer *buf, 
-				       int bytes, int error)
+static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param)
 {
-	struct snd_pcm_substream *substream = cookie;
-	struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
-
-	if (buf == rtd->buf + rtd->periods - 1)
-		rtd->pointer_bytes = 0;
-	else
-		rtd->pointer_bytes += buf->size;
+	struct ep93xx_dma_data *data = filter_param;
 
-	if (!error) {
-		ep93xx_dma_m2p_submit_recursive(&rtd->cl, buf);
-		tasklet_schedule(&rtd->period_tasklet);
-	} else {
-		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+	if (data->direction == ep93xx_dma_chan_direction(chan)) {
+		chan->private = data;
+		return true;
 	}
+
+	return false;
 }
 
 static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
@@ -98,30 +90,38 @@ static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
 	struct snd_soc_dai *cpu_dai = soc_rtd->cpu_dai;
 	struct ep93xx_pcm_dma_params *dma_params;
 	struct ep93xx_runtime_data *rtd;    
+	dma_cap_mask_t mask;
 	int ret;
 
-	dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream);
+	ret = snd_pcm_hw_constraint_integer(substream->runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
 	snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
 
 	rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
 	if (!rtd) 
 		return -ENOMEM;
 
-	memset(&rtd->period_tasklet, 0, sizeof(rtd->period_tasklet));
-	rtd->period_tasklet.func = ep93xx_pcm_period_elapsed;
-	rtd->period_tasklet.data = (unsigned long)substream;
-
-	rtd->cl.name = dma_params->name;
-	rtd->cl.flags = dma_params->dma_port | EP93XX_DMA_M2P_IGNORE_ERROR |
-		((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
-		 EP93XX_DMA_M2P_TX : EP93XX_DMA_M2P_RX);
-	rtd->cl.cookie = substream;
-	rtd->cl.buffer_started = ep93xx_pcm_buffer_started;
-	rtd->cl.buffer_finished = ep93xx_pcm_buffer_finished;
-	ret = ep93xx_dma_m2p_client_register(&rtd->cl);
-	if (ret < 0) {
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+	dma_cap_set(DMA_CYCLIC, mask);
+
+	dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream);
+	rtd->dma_data.port = dma_params->dma_port;
+	rtd->dma_data.name = dma_params->name;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		rtd->dma_data.direction = DMA_TO_DEVICE;
+	else
+		rtd->dma_data.direction = DMA_FROM_DEVICE;
+
+	rtd->dma_chan = dma_request_channel(mask, ep93xx_pcm_dma_filter,
+					    &rtd->dma_data);
+	if (!rtd->dma_chan) {
 		kfree(rtd);
-		return ret;
+		return -EINVAL;
 	}
 	
 	substream->runtime->private_data = rtd;
@@ -132,31 +132,52 @@ static int ep93xx_pcm_close(struct snd_pcm_substream *substream)
 {
 	struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
 
-	ep93xx_dma_m2p_client_unregister(&rtd->cl);
+	dma_release_channel(rtd->dma_chan);
 	kfree(rtd);
 	return 0;
 }
 
+static int ep93xx_pcm_dma_submit(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ep93xx_runtime_data *rtd = runtime->private_data;
+	struct dma_chan *chan = rtd->dma_chan;
+	struct dma_device *dma_dev = chan->device;
+	struct dma_async_tx_descriptor *desc;
+
+	rtd->pointer_bytes = 0;
+	desc = dma_dev->device_prep_dma_cyclic(chan, runtime->dma_addr,
+					       rtd->period_bytes * rtd->periods,
+					       rtd->period_bytes,
+					       rtd->dma_data.direction);
+	if (!desc)
+		return -EINVAL;
+
+	desc->callback = ep93xx_pcm_dma_callback;
+	desc->callback_param = substream;
+
+	dmaengine_submit(desc);
+	return 0;
+}
+
+static void ep93xx_pcm_dma_flush(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ep93xx_runtime_data *rtd = runtime->private_data;
+
+	dmaengine_terminate_all(rtd->dma_chan);
+}
+
 static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
 				struct snd_pcm_hw_params *params)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct ep93xx_runtime_data *rtd = runtime->private_data;
-	size_t totsize = params_buffer_bytes(params);
-	size_t period = params_period_bytes(params);
-	int i;
 
 	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-	runtime->dma_bytes = totsize;
-
-	rtd->periods = (totsize + period - 1) / period;
-	for (i = 0; i < rtd->periods; i++) {
-		rtd->buf[i].bus_addr = runtime->dma_addr + (i * period);
-		rtd->buf[i].size = period;
-		if ((i + 1) * period > totsize)
-			rtd->buf[i].size = totsize - (i * period);
-	}
 
+	rtd->periods = params_periods(params);
+	rtd->period_bytes = params_period_bytes(params);
 	return 0;
 }
 
@@ -168,24 +189,20 @@ static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
 
 static int ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
-	struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
 	int ret;
-	int i;
 
 	ret = 0;
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		rtd->pointer_bytes = 0;
-		for (i = 0; i < rtd->periods; i++)
-			ep93xx_dma_m2p_submit(&rtd->cl, rtd->buf + i);
+		ret = ep93xx_pcm_dma_submit(substream);
 		break;
 
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		ep93xx_dma_m2p_flush(&rtd->cl);
+		ep93xx_pcm_dma_flush(substream);
 		break;
 
 	default:
-- 
1.7.4.4


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

* [PATCH v2 4/5] ep93xx: remove the old M2P DMA code
  2011-05-29 10:10 [PATCH v2 0/5] ep93xx DMA patches Mika Westerberg
                   ` (2 preceding siblings ...)
  2011-05-29 10:10 ` [PATCH v2 3/5] ASoC: ep93xx: convert to use the DMA engine API Mika Westerberg
@ 2011-05-29 10:10 ` Mika Westerberg
  2011-05-29 10:10 ` [PATCH v2 5/5] spi/ep93xx: add DMA support Mika Westerberg
  2011-06-05  8:19 ` [PATCH v2 0/5] ep93xx DMA patches Mika Westerberg
  5 siblings, 0 replies; 36+ messages in thread
From: Mika Westerberg @ 2011-05-29 10:10 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: hsweeten, rmallon, vinod.koul, dan.j.williams, lrg, broonie,
	grant.likely, Mika Westerberg, linux-kernel

Since we have converted all existing users of the old DMA API to use the DMA
engine API the old code can be dropped.

Signed-off-by: Mika Westerberg <mika.westerberg@iki.fi>
Acked-by: Ryan Mallon <rmallon@gmail.com>
Acked-by: H Hartley Sweeten <hsweeten@visionengravers.com>
---
 arch/arm/mach-ep93xx/Makefile           |    2 +-
 arch/arm/mach-ep93xx/dma-m2p.c          |  411 -------------------------------
 arch/arm/mach-ep93xx/include/mach/dma.h |  143 -----------
 3 files changed, 1 insertions(+), 555 deletions(-)
 delete mode 100644 arch/arm/mach-ep93xx/dma-m2p.c

diff --git a/arch/arm/mach-ep93xx/Makefile b/arch/arm/mach-ep93xx/Makefile
index 4920f7a..21e721a 100644
--- a/arch/arm/mach-ep93xx/Makefile
+++ b/arch/arm/mach-ep93xx/Makefile
@@ -1,7 +1,7 @@
 #
 # Makefile for the linux kernel.
 #
-obj-y			:= core.o clock.o dma-m2p.o gpio.o
+obj-y			:= core.o clock.o gpio.o
 obj-m			:=
 obj-n			:=
 obj-			:=
diff --git a/arch/arm/mach-ep93xx/dma-m2p.c b/arch/arm/mach-ep93xx/dma-m2p.c
deleted file mode 100644
index a696d35..0000000
--- a/arch/arm/mach-ep93xx/dma-m2p.c
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * arch/arm/mach-ep93xx/dma-m2p.c
- * M2P DMA handling for Cirrus EP93xx chips.
- *
- * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
- * Copyright (C) 2006 Applied Data Systems
- *
- * Copyright (C) 2009 Ryan Mallon <ryan@bluewatersys.com>
- *
- * 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.
- */
-
-/*
- * On the EP93xx chip the following peripherals my be allocated to the 10
- * Memory to Internal Peripheral (M2P) channels (5 transmit + 5 receive).
- *
- *	I2S	contains 3 Tx and 3 Rx DMA Channels
- *	AAC	contains 3 Tx and 3 Rx DMA Channels
- *	UART1	contains 1 Tx and 1 Rx DMA Channels
- *	UART2	contains 1 Tx and 1 Rx DMA Channels
- *	UART3	contains 1 Tx and 1 Rx DMA Channels
- *	IrDA	contains 1 Tx and 1 Rx DMA Channels
- *
- * SSP and IDE use the Memory to Memory (M2M) channels and are not covered
- * with this implementation.
- */
-
-#define pr_fmt(fmt) "ep93xx " KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/io.h>
-
-#include <mach/dma.h>
-#include <mach/hardware.h>
-
-#define M2P_CONTROL			0x00
-#define  M2P_CONTROL_STALL_IRQ_EN	(1 << 0)
-#define  M2P_CONTROL_NFB_IRQ_EN		(1 << 1)
-#define  M2P_CONTROL_ERROR_IRQ_EN	(1 << 3)
-#define  M2P_CONTROL_ENABLE		(1 << 4)
-#define M2P_INTERRUPT			0x04
-#define  M2P_INTERRUPT_STALL		(1 << 0)
-#define  M2P_INTERRUPT_NFB		(1 << 1)
-#define  M2P_INTERRUPT_ERROR		(1 << 3)
-#define M2P_PPALLOC			0x08
-#define M2P_STATUS			0x0c
-#define M2P_REMAIN			0x14
-#define M2P_MAXCNT0			0x20
-#define M2P_BASE0			0x24
-#define M2P_MAXCNT1			0x30
-#define M2P_BASE1			0x34
-
-#define STATE_IDLE	0	/* Channel is inactive.  */
-#define STATE_STALL	1	/* Channel is active, no buffers pending.  */
-#define STATE_ON	2	/* Channel is active, one buffer pending.  */
-#define STATE_NEXT	3	/* Channel is active, two buffers pending.  */
-
-struct m2p_channel {
-	char				*name;
-	void __iomem			*base;
-	int				irq;
-
-	struct clk			*clk;
-	spinlock_t			lock;
-
-	void				*client;
-	unsigned			next_slot:1;
-	struct ep93xx_dma_buffer	*buffer_xfer;
-	struct ep93xx_dma_buffer	*buffer_next;
-	struct list_head		buffers_pending;
-};
-
-static struct m2p_channel m2p_rx[] = {
-	{"m2p1", EP93XX_DMA_BASE + 0x0040, IRQ_EP93XX_DMAM2P1},
-	{"m2p3", EP93XX_DMA_BASE + 0x00c0, IRQ_EP93XX_DMAM2P3},
-	{"m2p5", EP93XX_DMA_BASE + 0x0200, IRQ_EP93XX_DMAM2P5},
-	{"m2p7", EP93XX_DMA_BASE + 0x0280, IRQ_EP93XX_DMAM2P7},
-	{"m2p9", EP93XX_DMA_BASE + 0x0300, IRQ_EP93XX_DMAM2P9},
-	{NULL},
-};
-
-static struct m2p_channel m2p_tx[] = {
-	{"m2p0", EP93XX_DMA_BASE + 0x0000, IRQ_EP93XX_DMAM2P0},
-	{"m2p2", EP93XX_DMA_BASE + 0x0080, IRQ_EP93XX_DMAM2P2},
-	{"m2p4", EP93XX_DMA_BASE + 0x0240, IRQ_EP93XX_DMAM2P4},
-	{"m2p6", EP93XX_DMA_BASE + 0x02c0, IRQ_EP93XX_DMAM2P6},
-	{"m2p8", EP93XX_DMA_BASE + 0x0340, IRQ_EP93XX_DMAM2P8},
-	{NULL},
-};
-
-static void feed_buf(struct m2p_channel *ch, struct ep93xx_dma_buffer *buf)
-{
-	if (ch->next_slot == 0) {
-		writel(buf->size, ch->base + M2P_MAXCNT0);
-		writel(buf->bus_addr, ch->base + M2P_BASE0);
-	} else {
-		writel(buf->size, ch->base + M2P_MAXCNT1);
-		writel(buf->bus_addr, ch->base + M2P_BASE1);
-	}
-	ch->next_slot ^= 1;
-}
-
-static void choose_buffer_xfer(struct m2p_channel *ch)
-{
-	struct ep93xx_dma_buffer *buf;
-
-	ch->buffer_xfer = NULL;
-	if (!list_empty(&ch->buffers_pending)) {
-		buf = list_entry(ch->buffers_pending.next,
-				 struct ep93xx_dma_buffer, list);
-		list_del(&buf->list);
-		feed_buf(ch, buf);
-		ch->buffer_xfer = buf;
-	}
-}
-
-static void choose_buffer_next(struct m2p_channel *ch)
-{
-	struct ep93xx_dma_buffer *buf;
-
-	ch->buffer_next = NULL;
-	if (!list_empty(&ch->buffers_pending)) {
-		buf = list_entry(ch->buffers_pending.next,
-				 struct ep93xx_dma_buffer, list);
-		list_del(&buf->list);
-		feed_buf(ch, buf);
-		ch->buffer_next = buf;
-	}
-}
-
-static inline void m2p_set_control(struct m2p_channel *ch, u32 v)
-{
-	/*
-	 * The control register must be read immediately after being written so
-	 * that the internal state machine is correctly updated. See the ep93xx
-	 * users' guide for details.
-	 */
-	writel(v, ch->base + M2P_CONTROL);
-	readl(ch->base + M2P_CONTROL);
-}
-
-static inline int m2p_channel_state(struct m2p_channel *ch)
-{
-	return (readl(ch->base + M2P_STATUS) >> 4) & 0x3;
-}
-
-static irqreturn_t m2p_irq(int irq, void *dev_id)
-{
-	struct m2p_channel *ch = dev_id;
-	struct ep93xx_dma_m2p_client *cl;
-	u32 irq_status, v;
-	int error = 0;
-
-	cl = ch->client;
-
-	spin_lock(&ch->lock);
-	irq_status = readl(ch->base + M2P_INTERRUPT);
-
-	if (irq_status & M2P_INTERRUPT_ERROR) {
-		writel(M2P_INTERRUPT_ERROR, ch->base + M2P_INTERRUPT);
-		error = 1;
-	}
-
-	if ((irq_status & (M2P_INTERRUPT_STALL | M2P_INTERRUPT_NFB)) == 0) {
-		spin_unlock(&ch->lock);
-		return IRQ_NONE;
-	}
-
-	switch (m2p_channel_state(ch)) {
-	case STATE_IDLE:
-		pr_crit("dma interrupt without a dma buffer\n");
-		BUG();
-		break;
-
-	case STATE_STALL:
-		cl->buffer_finished(cl->cookie, ch->buffer_xfer, 0, error);
-		if (ch->buffer_next != NULL) {
-			cl->buffer_finished(cl->cookie, ch->buffer_next,
-					    0, error);
-		}
-		choose_buffer_xfer(ch);
-		choose_buffer_next(ch);
-		if (ch->buffer_xfer != NULL)
-			cl->buffer_started(cl->cookie, ch->buffer_xfer);
-		break;
-
-	case STATE_ON:
-		cl->buffer_finished(cl->cookie, ch->buffer_xfer, 0, error);
-		ch->buffer_xfer = ch->buffer_next;
-		choose_buffer_next(ch);
-		cl->buffer_started(cl->cookie, ch->buffer_xfer);
-		break;
-
-	case STATE_NEXT:
-		pr_crit("dma interrupt while next\n");
-		BUG();
-		break;
-	}
-
-	v = readl(ch->base + M2P_CONTROL) & ~(M2P_CONTROL_STALL_IRQ_EN |
-					      M2P_CONTROL_NFB_IRQ_EN);
-	if (ch->buffer_xfer != NULL)
-		v |= M2P_CONTROL_STALL_IRQ_EN;
-	if (ch->buffer_next != NULL)
-		v |= M2P_CONTROL_NFB_IRQ_EN;
-	m2p_set_control(ch, v);
-
-	spin_unlock(&ch->lock);
-	return IRQ_HANDLED;
-}
-
-static struct m2p_channel *find_free_channel(struct ep93xx_dma_m2p_client *cl)
-{
-	struct m2p_channel *ch;
-	int i;
-
-	if (cl->flags & EP93XX_DMA_M2P_RX)
-		ch = m2p_rx;
-	else
-		ch = m2p_tx;
-
-	for (i = 0; ch[i].base; i++) {
-		struct ep93xx_dma_m2p_client *client;
-
-		client = ch[i].client;
-		if (client != NULL) {
-			int port;
-
-			port = cl->flags & EP93XX_DMA_M2P_PORT_MASK;
-			if (port == (client->flags &
-				     EP93XX_DMA_M2P_PORT_MASK)) {
-				pr_warning("DMA channel already used by %s\n",
-					   cl->name ? : "unknown client");
-				return ERR_PTR(-EBUSY);
-			}
-		}
-	}
-
-	for (i = 0; ch[i].base; i++) {
-		if (ch[i].client == NULL)
-			return ch + i;
-	}
-
-	pr_warning("No free DMA channel for %s\n",
-		   cl->name ? : "unknown client");
-	return ERR_PTR(-ENODEV);
-}
-
-static void channel_enable(struct m2p_channel *ch)
-{
-	struct ep93xx_dma_m2p_client *cl = ch->client;
-	u32 v;
-
-	clk_enable(ch->clk);
-
-	v = cl->flags & EP93XX_DMA_M2P_PORT_MASK;
-	writel(v, ch->base + M2P_PPALLOC);
-
-	v = cl->flags & EP93XX_DMA_M2P_ERROR_MASK;
-	v |= M2P_CONTROL_ENABLE | M2P_CONTROL_ERROR_IRQ_EN;
-	m2p_set_control(ch, v);
-}
-
-static void channel_disable(struct m2p_channel *ch)
-{
-	u32 v;
-
-	v = readl(ch->base + M2P_CONTROL);
-	v &= ~(M2P_CONTROL_STALL_IRQ_EN | M2P_CONTROL_NFB_IRQ_EN);
-	m2p_set_control(ch, v);
-
-	while (m2p_channel_state(ch) >= STATE_ON)
-		cpu_relax();
-
-	m2p_set_control(ch, 0x0);
-
-	while (m2p_channel_state(ch) == STATE_STALL)
-		cpu_relax();
-
-	clk_disable(ch->clk);
-}
-
-int ep93xx_dma_m2p_client_register(struct ep93xx_dma_m2p_client *cl)
-{
-	struct m2p_channel *ch;
-	int err;
-
-	ch = find_free_channel(cl);
-	if (IS_ERR(ch))
-		return PTR_ERR(ch);
-
-	err = request_irq(ch->irq, m2p_irq, 0, cl->name ? : "dma-m2p", ch);
-	if (err)
-		return err;
-
-	ch->client = cl;
-	ch->next_slot = 0;
-	ch->buffer_xfer = NULL;
-	ch->buffer_next = NULL;
-	INIT_LIST_HEAD(&ch->buffers_pending);
-
-	cl->channel = ch;
-
-	channel_enable(ch);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(ep93xx_dma_m2p_client_register);
-
-void ep93xx_dma_m2p_client_unregister(struct ep93xx_dma_m2p_client *cl)
-{
-	struct m2p_channel *ch = cl->channel;
-
-	channel_disable(ch);
-	free_irq(ch->irq, ch);
-	ch->client = NULL;
-}
-EXPORT_SYMBOL_GPL(ep93xx_dma_m2p_client_unregister);
-
-void ep93xx_dma_m2p_submit(struct ep93xx_dma_m2p_client *cl,
-			   struct ep93xx_dma_buffer *buf)
-{
-	struct m2p_channel *ch = cl->channel;
-	unsigned long flags;
-	u32 v;
-
-	spin_lock_irqsave(&ch->lock, flags);
-	v = readl(ch->base + M2P_CONTROL);
-	if (ch->buffer_xfer == NULL) {
-		ch->buffer_xfer = buf;
-		feed_buf(ch, buf);
-		cl->buffer_started(cl->cookie, buf);
-
-		v |= M2P_CONTROL_STALL_IRQ_EN;
-		m2p_set_control(ch, v);
-
-	} else if (ch->buffer_next == NULL) {
-		ch->buffer_next = buf;
-		feed_buf(ch, buf);
-
-		v |= M2P_CONTROL_NFB_IRQ_EN;
-		m2p_set_control(ch, v);
-	} else {
-		list_add_tail(&buf->list, &ch->buffers_pending);
-	}
-	spin_unlock_irqrestore(&ch->lock, flags);
-}
-EXPORT_SYMBOL_GPL(ep93xx_dma_m2p_submit);
-
-void ep93xx_dma_m2p_submit_recursive(struct ep93xx_dma_m2p_client *cl,
-				     struct ep93xx_dma_buffer *buf)
-{
-	struct m2p_channel *ch = cl->channel;
-
-	list_add_tail(&buf->list, &ch->buffers_pending);
-}
-EXPORT_SYMBOL_GPL(ep93xx_dma_m2p_submit_recursive);
-
-void ep93xx_dma_m2p_flush(struct ep93xx_dma_m2p_client *cl)
-{
-	struct m2p_channel *ch = cl->channel;
-
-	channel_disable(ch);
-	ch->next_slot = 0;
-	ch->buffer_xfer = NULL;
-	ch->buffer_next = NULL;
-	INIT_LIST_HEAD(&ch->buffers_pending);
-	channel_enable(ch);
-}
-EXPORT_SYMBOL_GPL(ep93xx_dma_m2p_flush);
-
-static int init_channel(struct m2p_channel *ch)
-{
-	ch->clk = clk_get(NULL, ch->name);
-	if (IS_ERR(ch->clk))
-		return PTR_ERR(ch->clk);
-
-	spin_lock_init(&ch->lock);
-	ch->client = NULL;
-
-	return 0;
-}
-
-static int __init ep93xx_dma_m2p_init(void)
-{
-	int i;
-	int ret;
-
-	for (i = 0; m2p_rx[i].base; i++) {
-		ret = init_channel(m2p_rx + i);
-		if (ret)
-			return ret;
-	}
-
-	for (i = 0; m2p_tx[i].base; i++) {
-		ret = init_channel(m2p_tx + i);
-		if (ret)
-			return ret;
-	}
-
-	pr_info("M2P DMA subsystem initialized\n");
-	return 0;
-}
-arch_initcall(ep93xx_dma_m2p_init);
diff --git a/arch/arm/mach-ep93xx/include/mach/dma.h b/arch/arm/mach-ep93xx/include/mach/dma.h
index 6e7049a..46d4d87 100644
--- a/arch/arm/mach-ep93xx/include/mach/dma.h
+++ b/arch/arm/mach-ep93xx/include/mach/dma.h
@@ -1,153 +1,10 @@
-/**
- * DOC: EP93xx DMA M2P memory to peripheral and peripheral to memory engine
- *
- * The EP93xx DMA M2P subsystem handles DMA transfers between memory and
- * peripherals. DMA M2P channels are available for audio, UARTs and IrDA.
- * See chapter 10 of the EP93xx users guide for full details on the DMA M2P
- * engine.
- *
- * See sound/soc/ep93xx/ep93xx-pcm.c for an example use of the DMA M2P code.
- *
- */
-
 #ifndef __ASM_ARCH_DMA_H
 #define __ASM_ARCH_DMA_H
 
-#include <linux/list.h>
 #include <linux/types.h>
 #include <linux/dmaengine.h>
 #include <linux/dma-mapping.h>
 
-/**
- * struct ep93xx_dma_buffer - Information about a buffer to be transferred
- * using the DMA M2P engine
- *
- * @list: Entry in DMA buffer list
- * @bus_addr: Physical address of the buffer
- * @size: Size of the buffer in bytes
- */
-struct ep93xx_dma_buffer {
-	struct list_head	list;
-	u32			bus_addr;
-	u16			size;
-};
-
-/**
- * struct ep93xx_dma_m2p_client - Information about a DMA M2P client
- *
- * @name: Unique name for this client
- * @flags: Client flags
- * @cookie: User data to pass to callback functions
- * @buffer_started: Non NULL function to call when a transfer is started.
- * 			The arguments are the user data cookie and the DMA
- *			buffer which is starting.
- * @buffer_finished: Non NULL function to call when a transfer is completed.
- *			The arguments are the user data cookie, the DMA buffer
- *			which has completed, and a boolean flag indicating if
- *			the transfer had an error.
- */
-struct ep93xx_dma_m2p_client {
-	char			*name;
-	u8			flags;
-	void			*cookie;
-	void			(*buffer_started)(void *cookie,
-					struct ep93xx_dma_buffer *buf);
-	void			(*buffer_finished)(void *cookie,
-					struct ep93xx_dma_buffer *buf,
-					int bytes, int error);
-
-	/* private: Internal use only */
-	void			*channel;
-};
-
-/* DMA M2P ports */
-#define EP93XX_DMA_M2P_PORT_I2S1	0x00
-#define EP93XX_DMA_M2P_PORT_I2S2	0x01
-#define EP93XX_DMA_M2P_PORT_AAC1	0x02
-#define EP93XX_DMA_M2P_PORT_AAC2	0x03
-#define EP93XX_DMA_M2P_PORT_AAC3	0x04
-#define EP93XX_DMA_M2P_PORT_I2S3	0x05
-#define EP93XX_DMA_M2P_PORT_UART1	0x06
-#define EP93XX_DMA_M2P_PORT_UART2	0x07
-#define EP93XX_DMA_M2P_PORT_UART3	0x08
-#define EP93XX_DMA_M2P_PORT_IRDA	0x09
-#define EP93XX_DMA_M2P_PORT_MASK	0x0f
-
-/* DMA M2P client flags */
-#define EP93XX_DMA_M2P_TX		0x00	/* Memory to peripheral */
-#define EP93XX_DMA_M2P_RX		0x10	/* Peripheral to memory */
-
-/*
- * DMA M2P client error handling flags. See the EP93xx users guide
- * documentation on the DMA M2P CONTROL register for more details
- */
-#define EP93XX_DMA_M2P_ABORT_ON_ERROR	0x20	/* Abort on peripheral error */
-#define EP93XX_DMA_M2P_IGNORE_ERROR	0x40	/* Ignore peripheral errors */
-#define EP93XX_DMA_M2P_ERROR_MASK	0x60	/* Mask of error bits */
-
-/**
- * ep93xx_dma_m2p_client_register - Register a client with the DMA M2P
- * subsystem
- *
- * @m2p: Client information to register
- * returns 0 on success
- *
- * The DMA M2P subsystem allocates a channel and an interrupt line for the DMA
- * client
- */
-int ep93xx_dma_m2p_client_register(struct ep93xx_dma_m2p_client *m2p);
-
-/**
- * ep93xx_dma_m2p_client_unregister - Unregister a client from the DMA M2P
- * subsystem
- *
- * @m2p: Client to unregister
- *
- * Any transfers currently in progress will be completed in hardware, but
- * ignored in software.
- */
-void ep93xx_dma_m2p_client_unregister(struct ep93xx_dma_m2p_client *m2p);
-
-/**
- * ep93xx_dma_m2p_submit - Submit a DMA M2P transfer
- *
- * @m2p: DMA Client to submit the transfer on
- * @buf: DMA Buffer to submit
- *
- * If the current or next transfer positions are free on the M2P client then
- * the transfer is started immediately. If not, the transfer is added to the
- * list of pending transfers. This function must not be called from the
- * buffer_finished callback for an M2P channel.
- *
- */
-void ep93xx_dma_m2p_submit(struct ep93xx_dma_m2p_client *m2p,
-			   struct ep93xx_dma_buffer *buf);
-
-/**
- * ep93xx_dma_m2p_submit_recursive - Put a DMA transfer on the pending list
- * for an M2P channel
- *
- * @m2p: DMA Client to submit the transfer on
- * @buf: DMA Buffer to submit
- *
- * This function must only be called from the buffer_finished callback for an
- * M2P channel. It is commonly used to add the next transfer in a chained list
- * of DMA transfers.
- */
-void ep93xx_dma_m2p_submit_recursive(struct ep93xx_dma_m2p_client *m2p,
-				     struct ep93xx_dma_buffer *buf);
-
-/**
- * ep93xx_dma_m2p_flush - Flush all pending transfers on a DMA M2P client
- *
- * @m2p: DMA client to flush transfers on
- *
- * Any transfers currently in progress will be completed in hardware, but
- * ignored in software.
- *
- */
-void ep93xx_dma_m2p_flush(struct ep93xx_dma_m2p_client *m2p);
-
 /*
  * M2P channels.
  *
-- 
1.7.4.4


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

* [PATCH v2 5/5] spi/ep93xx: add DMA support
  2011-05-29 10:10 [PATCH v2 0/5] ep93xx DMA patches Mika Westerberg
                   ` (3 preceding siblings ...)
  2011-05-29 10:10 ` [PATCH v2 4/5] ep93xx: remove the old M2P DMA code Mika Westerberg
@ 2011-05-29 10:10 ` Mika Westerberg
  2011-06-03 20:44   ` Grant Likely
  2011-06-05  8:19 ` [PATCH v2 0/5] ep93xx DMA patches Mika Westerberg
  5 siblings, 1 reply; 36+ messages in thread
From: Mika Westerberg @ 2011-05-29 10:10 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: hsweeten, rmallon, vinod.koul, dan.j.williams, lrg, broonie,
	grant.likely, Mika Westerberg, linux-kernel

This patch adds DMA support for the EP93xx SPI driver. By default the DMA is
not enabled but it can be enabled by setting ep93xx_spi_info.use_dma to true
in board configuration file.

Note that the SPI driver still uses PIO for small transfers (<= 8 bytes) for
performance reasons.

Signed-off-by: Mika Westerberg <mika.westerberg@iki.fi>
Acked-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Grant Likely <grant.likely@secretlab.ca>
---
 Documentation/spi/ep93xx_spi                   |   10 +
 arch/arm/mach-ep93xx/core.c                    |    6 +-
 arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h |    2 +
 drivers/spi/ep93xx_spi.c                       |  303 +++++++++++++++++++++++-
 4 files changed, 308 insertions(+), 13 deletions(-)

diff --git a/Documentation/spi/ep93xx_spi b/Documentation/spi/ep93xx_spi
index 6325f5b..d8eb01c 100644
--- a/Documentation/spi/ep93xx_spi
+++ b/Documentation/spi/ep93xx_spi
@@ -88,6 +88,16 @@ static void __init ts72xx_init_machine(void)
 			    ARRAY_SIZE(ts72xx_spi_devices));
 }
 
+The driver can use DMA for the transfers also. In this case ts72xx_spi_info
+becomes:
+
+static struct ep93xx_spi_info ts72xx_spi_info = {
+	.num_chipselect	= ARRAY_SIZE(ts72xx_spi_devices),
+	.use_dma	= true;
+};
+
+Note that CONFIG_EP93XX_DMA should be enabled as well.
+
 Thanks to
 =========
 Martin Guy, H. Hartley Sweeten and others who helped me during development of
diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c
index 8207954..cc9f1d4 100644
--- a/arch/arm/mach-ep93xx/core.c
+++ b/arch/arm/mach-ep93xx/core.c
@@ -488,11 +488,15 @@ static struct resource ep93xx_spi_resources[] = {
 	},
 };
 
+static u64 ep93xx_spi_dma_mask = DMA_BIT_MASK(32);
+
 static struct platform_device ep93xx_spi_device = {
 	.name		= "ep93xx-spi",
 	.id		= 0,
 	.dev		= {
-		.platform_data = &ep93xx_spi_master_data,
+		.platform_data		= &ep93xx_spi_master_data,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+		.dma_mask		= &ep93xx_spi_dma_mask,
 	},
 	.num_resources	= ARRAY_SIZE(ep93xx_spi_resources),
 	.resource	= ep93xx_spi_resources,
diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h b/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h
index 0a37961..9bb63ac 100644
--- a/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h
+++ b/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h
@@ -7,9 +7,11 @@ struct spi_device;
  * struct ep93xx_spi_info - EP93xx specific SPI descriptor
  * @num_chipselect: number of chip selects on this board, must be
  *                  at least one
+ * @use_dma: use DMA for the transfers
  */
 struct ep93xx_spi_info {
 	int	num_chipselect;
+	bool	use_dma;
 };
 
 /**
diff --git a/drivers/spi/ep93xx_spi.c b/drivers/spi/ep93xx_spi.c
index d357007..1cf6454 100644
--- a/drivers/spi/ep93xx_spi.c
+++ b/drivers/spi/ep93xx_spi.c
@@ -1,7 +1,7 @@
 /*
  * Driver for Cirrus Logic EP93xx SPI controller.
  *
- * Copyright (c) 2010 Mika Westerberg
+ * Copyright (C) 2010-2011 Mika Westerberg
  *
  * Explicit FIFO handling code was inspired by amba-pl022 driver.
  *
@@ -21,13 +21,16 @@
 #include <linux/err.h>
 #include <linux/delay.h>
 #include <linux/device.h>
+#include <linux/dmaengine.h>
 #include <linux/bitops.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/workqueue.h>
 #include <linux/sched.h>
+#include <linux/scatterlist.h>
 #include <linux/spi/spi.h>
 
+#include <mach/dma.h>
 #include <mach/ep93xx_spi.h>
 
 #define SSPCR0			0x0000
@@ -71,6 +74,7 @@
  * @pdev: pointer to platform device
  * @clk: clock for the controller
  * @regs_base: pointer to ioremap()'d registers
+ * @sspdr_phys: physical address of the SSPDR register
  * @irq: IRQ number used by the driver
  * @min_rate: minimum clock rate (in Hz) supported by the controller
  * @max_rate: maximum clock rate (in Hz) supported by the controller
@@ -84,6 +88,14 @@
  * @rx: current byte in transfer to receive
  * @fifo_level: how full is FIFO (%0..%SPI_FIFO_SIZE - %1). Receiving one
  *              frame decreases this level and sending one frame increases it.
+ * @dma_rx: RX DMA channel
+ * @dma_tx: TX DMA channel
+ * @dma_rx_data: RX parameters passed to the DMA engine
+ * @dma_tx_data: TX parameters passed to the DMA engine
+ * @rx_sgt: sg table for RX transfers
+ * @tx_sgt: sg table for TX transfers
+ * @zeropage: dummy page used as RX buffer when only TX buffer is passed in by
+ *            the client
  *
  * This structure holds EP93xx SPI controller specific information. When
  * @running is %true, driver accepts transfer requests from protocol drivers.
@@ -100,6 +112,7 @@ struct ep93xx_spi {
 	const struct platform_device	*pdev;
 	struct clk			*clk;
 	void __iomem			*regs_base;
+	unsigned long			sspdr_phys;
 	int				irq;
 	unsigned long			min_rate;
 	unsigned long			max_rate;
@@ -112,6 +125,13 @@ struct ep93xx_spi {
 	size_t				tx;
 	size_t				rx;
 	size_t				fifo_level;
+	struct dma_chan			*dma_rx;
+	struct dma_chan			*dma_tx;
+	struct ep93xx_dma_data		dma_rx_data;
+	struct ep93xx_dma_data		dma_tx_data;
+	struct sg_table			rx_sgt;
+	struct sg_table			tx_sgt;
+	void				*zeropage;
 };
 
 /**
@@ -496,14 +516,195 @@ static int ep93xx_spi_read_write(struct ep93xx_spi *espi)
 		espi->fifo_level++;
 	}
 
-	if (espi->rx == t->len) {
-		msg->actual_length += t->len;
+	if (espi->rx == t->len)
 		return 0;
-	}
 
 	return -EINPROGRESS;
 }
 
+static void ep93xx_spi_pio_transfer(struct ep93xx_spi *espi)
+{
+	/*
+	 * Now everything is set up for the current transfer. We prime the TX
+	 * FIFO, enable interrupts, and wait for the transfer to complete.
+	 */
+	if (ep93xx_spi_read_write(espi)) {
+		ep93xx_spi_enable_interrupts(espi);
+		wait_for_completion(&espi->wait);
+	}
+}
+
+/**
+ * ep93xx_spi_dma_prepare() - prepares a DMA transfer
+ * @espi: ep93xx SPI controller struct
+ * @dir: DMA transfer direction
+ *
+ * Function configures the DMA, maps the buffer and prepares the DMA
+ * descriptor. Returns a valid DMA descriptor in case of success and ERR_PTR
+ * in case of failure.
+ */
+static struct dma_async_tx_descriptor *
+ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir)
+{
+	struct spi_transfer *t = espi->current_msg->state;
+	struct dma_async_tx_descriptor *txd;
+	enum dma_slave_buswidth buswidth;
+	struct dma_slave_config conf;
+	struct scatterlist *sg;
+	struct sg_table *sgt;
+	struct dma_chan *chan;
+	const void *buf, *pbuf;
+	size_t len = t->len;
+	int i, ret, nents;
+
+	if (bits_per_word(espi) > 8)
+		buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+	else
+		buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
+
+	memset(&conf, 0, sizeof(conf));
+	conf.direction = dir;
+
+	if (dir == DMA_FROM_DEVICE) {
+		chan = espi->dma_rx;
+		buf = t->rx_buf;
+		sgt = &espi->rx_sgt;
+
+		conf.src_addr = espi->sspdr_phys;
+		conf.src_addr_width = buswidth;
+	} else {
+		chan = espi->dma_tx;
+		buf = t->tx_buf;
+		sgt = &espi->tx_sgt;
+
+		conf.dst_addr = espi->sspdr_phys;
+		conf.dst_addr_width = buswidth;
+	}
+
+	ret = dmaengine_slave_config(chan, &conf);
+	if (ret)
+		return ERR_PTR(ret);
+
+	/*
+	 * We need to split the transfer into PAGE_SIZE'd chunks. This is
+	 * because we are using @espi->zeropage to provide a zero RX buffer
+	 * for the TX transfers and we have only allocated one page for that.
+	 *
+	 * For performance reasons we allocate a new sg_table only when
+	 * needed. Otherwise we will re-use the current one. Eventually the
+	 * last sg_table is released in ep93xx_spi_release_dma().
+	 */
+
+	nents = DIV_ROUND_UP(len, PAGE_SIZE);
+	if (nents != sgt->nents) {
+		sg_free_table(sgt);
+
+		ret = sg_alloc_table(sgt, nents, GFP_KERNEL);
+		if (ret)
+			return ERR_PTR(ret);
+	}
+
+	pbuf = buf;
+	for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+		size_t bytes = min_t(size_t, len, PAGE_SIZE);
+
+		if (buf) {
+			sg_set_page(sg, virt_to_page(pbuf), bytes,
+				    offset_in_page(pbuf));
+		} else {
+			sg_set_page(sg, virt_to_page(espi->zeropage),
+				    bytes, 0);
+		}
+
+		pbuf += bytes;
+		len -= bytes;
+	}
+
+	if (WARN_ON(len)) {
+		dev_warn(&espi->pdev->dev, "len = %d expected 0!", len);
+		return ERR_PTR(-EINVAL);
+	}
+
+	nents = dma_map_sg(chan->device->dev, sgt->sgl, sgt->nents, dir);
+	if (!nents)
+		return ERR_PTR(-ENOMEM);
+
+	txd = chan->device->device_prep_slave_sg(chan, sgt->sgl, nents,
+						 dir, DMA_CTRL_ACK);
+	if (!txd) {
+		dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir);
+		return ERR_PTR(-ENOMEM);
+	}
+	return txd;
+}
+
+/**
+ * ep93xx_spi_dma_finish() - finishes with a DMA transfer
+ * @espi: ep93xx SPI controller struct
+ * @dir: DMA transfer direction
+ *
+ * Function finishes with the DMA transfer. After this, the DMA buffer is
+ * unmapped.
+ */
+static void ep93xx_spi_dma_finish(struct ep93xx_spi *espi,
+				  enum dma_data_direction dir)
+{
+	struct dma_chan *chan;
+	struct sg_table *sgt;
+
+	if (dir == DMA_FROM_DEVICE) {
+		chan = espi->dma_rx;
+		sgt = &espi->rx_sgt;
+	} else {
+		chan = espi->dma_tx;
+		sgt = &espi->tx_sgt;
+	}
+
+	dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir);
+}
+
+static void ep93xx_spi_dma_callback(void *callback_param)
+{
+	complete(callback_param);
+}
+
+static void ep93xx_spi_dma_transfer(struct ep93xx_spi *espi)
+{
+	struct spi_message *msg = espi->current_msg;
+	struct dma_async_tx_descriptor *rxd, *txd;
+
+	rxd = ep93xx_spi_dma_prepare(espi, DMA_FROM_DEVICE);
+	if (IS_ERR(rxd)) {
+		dev_err(&espi->pdev->dev, "DMA RX failed: %ld\n", PTR_ERR(rxd));
+		msg->status = PTR_ERR(rxd);
+		return;
+	}
+
+	txd = ep93xx_spi_dma_prepare(espi, DMA_TO_DEVICE);
+	if (IS_ERR(txd)) {
+		ep93xx_spi_dma_finish(espi, DMA_FROM_DEVICE);
+		dev_err(&espi->pdev->dev, "DMA TX failed: %ld\n", PTR_ERR(rxd));
+		msg->status = PTR_ERR(txd);
+		return;
+	}
+
+	/* We are ready when RX is done */
+	rxd->callback = ep93xx_spi_dma_callback;
+	rxd->callback_param = &espi->wait;
+
+	/* Now submit both descriptors and wait while they finish */
+	dmaengine_submit(rxd);
+	dmaengine_submit(txd);
+
+	dma_async_issue_pending(espi->dma_rx);
+	dma_async_issue_pending(espi->dma_tx);
+
+	wait_for_completion(&espi->wait);
+
+	ep93xx_spi_dma_finish(espi, DMA_TO_DEVICE);
+	ep93xx_spi_dma_finish(espi, DMA_FROM_DEVICE);
+}
+
 /**
  * ep93xx_spi_process_transfer() - processes one SPI transfer
  * @espi: ep93xx SPI controller struct
@@ -556,13 +757,14 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi,
 	espi->tx = 0;
 
 	/*
-	 * Now everything is set up for the current transfer. We prime the TX
-	 * FIFO, enable interrupts, and wait for the transfer to complete.
+	 * There is no point of setting up DMA for the transfers which will
+	 * fit into the FIFO and can be transferred with a single interrupt.
+	 * So in these cases we will be using PIO and don't bother for DMA.
 	 */
-	if (ep93xx_spi_read_write(espi)) {
-		ep93xx_spi_enable_interrupts(espi);
-		wait_for_completion(&espi->wait);
-	}
+	if (espi->dma_rx && t->len > SPI_FIFO_SIZE)
+		ep93xx_spi_dma_transfer(espi);
+	else
+		ep93xx_spi_pio_transfer(espi);
 
 	/*
 	 * In case of error during transmit, we bail out from processing
@@ -571,6 +773,8 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi,
 	if (msg->status)
 		return;
 
+	msg->actual_length += t->len;
+
 	/*
 	 * After this transfer is finished, perform any possible
 	 * post-transfer actions requested by the protocol driver.
@@ -752,6 +956,75 @@ static irqreturn_t ep93xx_spi_interrupt(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static bool ep93xx_spi_dma_filter(struct dma_chan *chan, void *filter_param)
+{
+	if (ep93xx_dma_chan_is_m2p(chan))
+		return false;
+
+	chan->private = filter_param;
+	return true;
+}
+
+static int ep93xx_spi_setup_dma(struct ep93xx_spi *espi)
+{
+	dma_cap_mask_t mask;
+	int ret;
+
+	espi->zeropage = (void *)get_zeroed_page(GFP_KERNEL);
+	if (!espi->zeropage)
+		return -ENOMEM;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	espi->dma_rx_data.port = EP93XX_DMA_SSP;
+	espi->dma_rx_data.direction = DMA_FROM_DEVICE;
+	espi->dma_rx_data.name = "ep93xx-spi-rx";
+
+	espi->dma_rx = dma_request_channel(mask, ep93xx_spi_dma_filter,
+					   &espi->dma_rx_data);
+	if (!espi->dma_rx) {
+		ret = -ENODEV;
+		goto fail_free_page;
+	}
+
+	espi->dma_tx_data.port = EP93XX_DMA_SSP;
+	espi->dma_tx_data.direction = DMA_TO_DEVICE;
+	espi->dma_tx_data.name = "ep93xx-spi-tx";
+
+	espi->dma_tx = dma_request_channel(mask, ep93xx_spi_dma_filter,
+					   &espi->dma_tx_data);
+	if (!espi->dma_tx) {
+		ret = -ENODEV;
+		goto fail_release_rx;
+	}
+
+	return 0;
+
+fail_release_rx:
+	dma_release_channel(espi->dma_rx);
+	espi->dma_rx = NULL;
+fail_free_page:
+	free_page((unsigned long)espi->zeropage);
+
+	return ret;
+}
+
+static void ep93xx_spi_release_dma(struct ep93xx_spi *espi)
+{
+	if (espi->dma_rx) {
+		dma_release_channel(espi->dma_rx);
+		sg_free_table(&espi->rx_sgt);
+	}
+	if (espi->dma_tx) {
+		dma_release_channel(espi->dma_tx);
+		sg_free_table(&espi->tx_sgt);
+	}
+
+	if (espi->zeropage)
+		free_page((unsigned long)espi->zeropage);
+}
+
 static int __init ep93xx_spi_probe(struct platform_device *pdev)
 {
 	struct spi_master *master;
@@ -818,6 +1091,7 @@ static int __init ep93xx_spi_probe(struct platform_device *pdev)
 		goto fail_put_clock;
 	}
 
+	espi->sspdr_phys = res->start + SSPDR;
 	espi->regs_base = ioremap(res->start, resource_size(res));
 	if (!espi->regs_base) {
 		dev_err(&pdev->dev, "failed to map resources\n");
@@ -832,10 +1106,13 @@ static int __init ep93xx_spi_probe(struct platform_device *pdev)
 		goto fail_unmap_regs;
 	}
 
+	if (info->use_dma && ep93xx_spi_setup_dma(espi))
+		dev_warn(&pdev->dev, "DMA setup failed. Falling back to PIO\n");
+
 	espi->wq = create_singlethread_workqueue("ep93xx_spid");
 	if (!espi->wq) {
 		dev_err(&pdev->dev, "unable to create workqueue\n");
-		goto fail_free_irq;
+		goto fail_free_dma;
 	}
 	INIT_WORK(&espi->msg_work, ep93xx_spi_work);
 	INIT_LIST_HEAD(&espi->msg_queue);
@@ -857,7 +1134,8 @@ static int __init ep93xx_spi_probe(struct platform_device *pdev)
 
 fail_free_queue:
 	destroy_workqueue(espi->wq);
-fail_free_irq:
+fail_free_dma:
+	ep93xx_spi_release_dma(espi);
 	free_irq(espi->irq, espi);
 fail_unmap_regs:
 	iounmap(espi->regs_base);
@@ -901,6 +1179,7 @@ static int __exit ep93xx_spi_remove(struct platform_device *pdev)
 	}
 	spin_unlock_irq(&espi->lock);
 
+	ep93xx_spi_release_dma(espi);
 	free_irq(espi->irq, espi);
 	iounmap(espi->regs_base);
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-- 
1.7.4.4


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

* Re: [PATCH v2 5/5] spi/ep93xx: add DMA support
  2011-05-29 10:10 ` [PATCH v2 5/5] spi/ep93xx: add DMA support Mika Westerberg
@ 2011-06-03 20:44   ` Grant Likely
  2011-06-07 17:14     ` Mika Westerberg
  0 siblings, 1 reply; 36+ messages in thread
From: Grant Likely @ 2011-06-03 20:44 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: linux-arm-kernel, hsweeten, rmallon, vinod.koul, dan.j.williams,
	lrg, broonie, linux-kernel

On Sun, May 29, 2011 at 01:10:06PM +0300, Mika Westerberg wrote:
> This patch adds DMA support for the EP93xx SPI driver. By default the DMA is
> not enabled but it can be enabled by setting ep93xx_spi_info.use_dma to true
> in board configuration file.
> 
> Note that the SPI driver still uses PIO for small transfers (<= 8 bytes) for
> performance reasons.
> 
> Signed-off-by: Mika Westerberg <mika.westerberg@iki.fi>
> Acked-by: H Hartley Sweeten <hsweeten@visionengravers.com>
> Cc: Grant Likely <grant.likely@secretlab.ca>

Acked-by: Grant Likely <grant.likely@secretlab.ca>

Since this depends on the DMA patches, it can go in via the same tree.

g.

> ---
>  Documentation/spi/ep93xx_spi                   |   10 +
>  arch/arm/mach-ep93xx/core.c                    |    6 +-
>  arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h |    2 +
>  drivers/spi/ep93xx_spi.c                       |  303 +++++++++++++++++++++++-
>  4 files changed, 308 insertions(+), 13 deletions(-)
> 
> diff --git a/Documentation/spi/ep93xx_spi b/Documentation/spi/ep93xx_spi
> index 6325f5b..d8eb01c 100644
> --- a/Documentation/spi/ep93xx_spi
> +++ b/Documentation/spi/ep93xx_spi
> @@ -88,6 +88,16 @@ static void __init ts72xx_init_machine(void)
>  			    ARRAY_SIZE(ts72xx_spi_devices));
>  }
>  
> +The driver can use DMA for the transfers also. In this case ts72xx_spi_info
> +becomes:
> +
> +static struct ep93xx_spi_info ts72xx_spi_info = {
> +	.num_chipselect	= ARRAY_SIZE(ts72xx_spi_devices),
> +	.use_dma	= true;
> +};
> +
> +Note that CONFIG_EP93XX_DMA should be enabled as well.
> +
>  Thanks to
>  =========
>  Martin Guy, H. Hartley Sweeten and others who helped me during development of
> diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c
> index 8207954..cc9f1d4 100644
> --- a/arch/arm/mach-ep93xx/core.c
> +++ b/arch/arm/mach-ep93xx/core.c
> @@ -488,11 +488,15 @@ static struct resource ep93xx_spi_resources[] = {
>  	},
>  };
>  
> +static u64 ep93xx_spi_dma_mask = DMA_BIT_MASK(32);
> +
>  static struct platform_device ep93xx_spi_device = {
>  	.name		= "ep93xx-spi",
>  	.id		= 0,
>  	.dev		= {
> -		.platform_data = &ep93xx_spi_master_data,
> +		.platform_data		= &ep93xx_spi_master_data,
> +		.coherent_dma_mask	= DMA_BIT_MASK(32),
> +		.dma_mask		= &ep93xx_spi_dma_mask,
>  	},
>  	.num_resources	= ARRAY_SIZE(ep93xx_spi_resources),
>  	.resource	= ep93xx_spi_resources,
> diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h b/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h
> index 0a37961..9bb63ac 100644
> --- a/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h
> +++ b/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h
> @@ -7,9 +7,11 @@ struct spi_device;
>   * struct ep93xx_spi_info - EP93xx specific SPI descriptor
>   * @num_chipselect: number of chip selects on this board, must be
>   *                  at least one
> + * @use_dma: use DMA for the transfers
>   */
>  struct ep93xx_spi_info {
>  	int	num_chipselect;
> +	bool	use_dma;
>  };
>  
>  /**
> diff --git a/drivers/spi/ep93xx_spi.c b/drivers/spi/ep93xx_spi.c
> index d357007..1cf6454 100644
> --- a/drivers/spi/ep93xx_spi.c
> +++ b/drivers/spi/ep93xx_spi.c
> @@ -1,7 +1,7 @@
>  /*
>   * Driver for Cirrus Logic EP93xx SPI controller.
>   *
> - * Copyright (c) 2010 Mika Westerberg
> + * Copyright (C) 2010-2011 Mika Westerberg
>   *
>   * Explicit FIFO handling code was inspired by amba-pl022 driver.
>   *
> @@ -21,13 +21,16 @@
>  #include <linux/err.h>
>  #include <linux/delay.h>
>  #include <linux/device.h>
> +#include <linux/dmaengine.h>
>  #include <linux/bitops.h>
>  #include <linux/interrupt.h>
>  #include <linux/platform_device.h>
>  #include <linux/workqueue.h>
>  #include <linux/sched.h>
> +#include <linux/scatterlist.h>
>  #include <linux/spi/spi.h>
>  
> +#include <mach/dma.h>
>  #include <mach/ep93xx_spi.h>
>  
>  #define SSPCR0			0x0000
> @@ -71,6 +74,7 @@
>   * @pdev: pointer to platform device
>   * @clk: clock for the controller
>   * @regs_base: pointer to ioremap()'d registers
> + * @sspdr_phys: physical address of the SSPDR register
>   * @irq: IRQ number used by the driver
>   * @min_rate: minimum clock rate (in Hz) supported by the controller
>   * @max_rate: maximum clock rate (in Hz) supported by the controller
> @@ -84,6 +88,14 @@
>   * @rx: current byte in transfer to receive
>   * @fifo_level: how full is FIFO (%0..%SPI_FIFO_SIZE - %1). Receiving one
>   *              frame decreases this level and sending one frame increases it.
> + * @dma_rx: RX DMA channel
> + * @dma_tx: TX DMA channel
> + * @dma_rx_data: RX parameters passed to the DMA engine
> + * @dma_tx_data: TX parameters passed to the DMA engine
> + * @rx_sgt: sg table for RX transfers
> + * @tx_sgt: sg table for TX transfers
> + * @zeropage: dummy page used as RX buffer when only TX buffer is passed in by
> + *            the client
>   *
>   * This structure holds EP93xx SPI controller specific information. When
>   * @running is %true, driver accepts transfer requests from protocol drivers.
> @@ -100,6 +112,7 @@ struct ep93xx_spi {
>  	const struct platform_device	*pdev;
>  	struct clk			*clk;
>  	void __iomem			*regs_base;
> +	unsigned long			sspdr_phys;
>  	int				irq;
>  	unsigned long			min_rate;
>  	unsigned long			max_rate;
> @@ -112,6 +125,13 @@ struct ep93xx_spi {
>  	size_t				tx;
>  	size_t				rx;
>  	size_t				fifo_level;
> +	struct dma_chan			*dma_rx;
> +	struct dma_chan			*dma_tx;
> +	struct ep93xx_dma_data		dma_rx_data;
> +	struct ep93xx_dma_data		dma_tx_data;
> +	struct sg_table			rx_sgt;
> +	struct sg_table			tx_sgt;
> +	void				*zeropage;
>  };
>  
>  /**
> @@ -496,14 +516,195 @@ static int ep93xx_spi_read_write(struct ep93xx_spi *espi)
>  		espi->fifo_level++;
>  	}
>  
> -	if (espi->rx == t->len) {
> -		msg->actual_length += t->len;
> +	if (espi->rx == t->len)
>  		return 0;
> -	}
>  
>  	return -EINPROGRESS;
>  }
>  
> +static void ep93xx_spi_pio_transfer(struct ep93xx_spi *espi)
> +{
> +	/*
> +	 * Now everything is set up for the current transfer. We prime the TX
> +	 * FIFO, enable interrupts, and wait for the transfer to complete.
> +	 */
> +	if (ep93xx_spi_read_write(espi)) {
> +		ep93xx_spi_enable_interrupts(espi);
> +		wait_for_completion(&espi->wait);
> +	}
> +}
> +
> +/**
> + * ep93xx_spi_dma_prepare() - prepares a DMA transfer
> + * @espi: ep93xx SPI controller struct
> + * @dir: DMA transfer direction
> + *
> + * Function configures the DMA, maps the buffer and prepares the DMA
> + * descriptor. Returns a valid DMA descriptor in case of success and ERR_PTR
> + * in case of failure.
> + */
> +static struct dma_async_tx_descriptor *
> +ep93xx_spi_dma_prepare(struct ep93xx_spi *espi, enum dma_data_direction dir)
> +{
> +	struct spi_transfer *t = espi->current_msg->state;
> +	struct dma_async_tx_descriptor *txd;
> +	enum dma_slave_buswidth buswidth;
> +	struct dma_slave_config conf;
> +	struct scatterlist *sg;
> +	struct sg_table *sgt;
> +	struct dma_chan *chan;
> +	const void *buf, *pbuf;
> +	size_t len = t->len;
> +	int i, ret, nents;
> +
> +	if (bits_per_word(espi) > 8)
> +		buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
> +	else
> +		buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
> +
> +	memset(&conf, 0, sizeof(conf));
> +	conf.direction = dir;
> +
> +	if (dir == DMA_FROM_DEVICE) {
> +		chan = espi->dma_rx;
> +		buf = t->rx_buf;
> +		sgt = &espi->rx_sgt;
> +
> +		conf.src_addr = espi->sspdr_phys;
> +		conf.src_addr_width = buswidth;
> +	} else {
> +		chan = espi->dma_tx;
> +		buf = t->tx_buf;
> +		sgt = &espi->tx_sgt;
> +
> +		conf.dst_addr = espi->sspdr_phys;
> +		conf.dst_addr_width = buswidth;
> +	}
> +
> +	ret = dmaengine_slave_config(chan, &conf);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
> +	/*
> +	 * We need to split the transfer into PAGE_SIZE'd chunks. This is
> +	 * because we are using @espi->zeropage to provide a zero RX buffer
> +	 * for the TX transfers and we have only allocated one page for that.
> +	 *
> +	 * For performance reasons we allocate a new sg_table only when
> +	 * needed. Otherwise we will re-use the current one. Eventually the
> +	 * last sg_table is released in ep93xx_spi_release_dma().
> +	 */
> +
> +	nents = DIV_ROUND_UP(len, PAGE_SIZE);
> +	if (nents != sgt->nents) {
> +		sg_free_table(sgt);
> +
> +		ret = sg_alloc_table(sgt, nents, GFP_KERNEL);
> +		if (ret)
> +			return ERR_PTR(ret);
> +	}
> +
> +	pbuf = buf;
> +	for_each_sg(sgt->sgl, sg, sgt->nents, i) {
> +		size_t bytes = min_t(size_t, len, PAGE_SIZE);
> +
> +		if (buf) {
> +			sg_set_page(sg, virt_to_page(pbuf), bytes,
> +				    offset_in_page(pbuf));
> +		} else {
> +			sg_set_page(sg, virt_to_page(espi->zeropage),
> +				    bytes, 0);
> +		}
> +
> +		pbuf += bytes;
> +		len -= bytes;
> +	}
> +
> +	if (WARN_ON(len)) {
> +		dev_warn(&espi->pdev->dev, "len = %d expected 0!", len);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	nents = dma_map_sg(chan->device->dev, sgt->sgl, sgt->nents, dir);
> +	if (!nents)
> +		return ERR_PTR(-ENOMEM);
> +
> +	txd = chan->device->device_prep_slave_sg(chan, sgt->sgl, nents,
> +						 dir, DMA_CTRL_ACK);
> +	if (!txd) {
> +		dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +	return txd;
> +}
> +
> +/**
> + * ep93xx_spi_dma_finish() - finishes with a DMA transfer
> + * @espi: ep93xx SPI controller struct
> + * @dir: DMA transfer direction
> + *
> + * Function finishes with the DMA transfer. After this, the DMA buffer is
> + * unmapped.
> + */
> +static void ep93xx_spi_dma_finish(struct ep93xx_spi *espi,
> +				  enum dma_data_direction dir)
> +{
> +	struct dma_chan *chan;
> +	struct sg_table *sgt;
> +
> +	if (dir == DMA_FROM_DEVICE) {
> +		chan = espi->dma_rx;
> +		sgt = &espi->rx_sgt;
> +	} else {
> +		chan = espi->dma_tx;
> +		sgt = &espi->tx_sgt;
> +	}
> +
> +	dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir);
> +}
> +
> +static void ep93xx_spi_dma_callback(void *callback_param)
> +{
> +	complete(callback_param);
> +}
> +
> +static void ep93xx_spi_dma_transfer(struct ep93xx_spi *espi)
> +{
> +	struct spi_message *msg = espi->current_msg;
> +	struct dma_async_tx_descriptor *rxd, *txd;
> +
> +	rxd = ep93xx_spi_dma_prepare(espi, DMA_FROM_DEVICE);
> +	if (IS_ERR(rxd)) {
> +		dev_err(&espi->pdev->dev, "DMA RX failed: %ld\n", PTR_ERR(rxd));
> +		msg->status = PTR_ERR(rxd);
> +		return;
> +	}
> +
> +	txd = ep93xx_spi_dma_prepare(espi, DMA_TO_DEVICE);
> +	if (IS_ERR(txd)) {
> +		ep93xx_spi_dma_finish(espi, DMA_FROM_DEVICE);
> +		dev_err(&espi->pdev->dev, "DMA TX failed: %ld\n", PTR_ERR(rxd));
> +		msg->status = PTR_ERR(txd);
> +		return;
> +	}
> +
> +	/* We are ready when RX is done */
> +	rxd->callback = ep93xx_spi_dma_callback;
> +	rxd->callback_param = &espi->wait;
> +
> +	/* Now submit both descriptors and wait while they finish */
> +	dmaengine_submit(rxd);
> +	dmaengine_submit(txd);
> +
> +	dma_async_issue_pending(espi->dma_rx);
> +	dma_async_issue_pending(espi->dma_tx);
> +
> +	wait_for_completion(&espi->wait);
> +
> +	ep93xx_spi_dma_finish(espi, DMA_TO_DEVICE);
> +	ep93xx_spi_dma_finish(espi, DMA_FROM_DEVICE);
> +}
> +
>  /**
>   * ep93xx_spi_process_transfer() - processes one SPI transfer
>   * @espi: ep93xx SPI controller struct
> @@ -556,13 +757,14 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi,
>  	espi->tx = 0;
>  
>  	/*
> -	 * Now everything is set up for the current transfer. We prime the TX
> -	 * FIFO, enable interrupts, and wait for the transfer to complete.
> +	 * There is no point of setting up DMA for the transfers which will
> +	 * fit into the FIFO and can be transferred with a single interrupt.
> +	 * So in these cases we will be using PIO and don't bother for DMA.
>  	 */
> -	if (ep93xx_spi_read_write(espi)) {
> -		ep93xx_spi_enable_interrupts(espi);
> -		wait_for_completion(&espi->wait);
> -	}
> +	if (espi->dma_rx && t->len > SPI_FIFO_SIZE)
> +		ep93xx_spi_dma_transfer(espi);
> +	else
> +		ep93xx_spi_pio_transfer(espi);
>  
>  	/*
>  	 * In case of error during transmit, we bail out from processing
> @@ -571,6 +773,8 @@ static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi,
>  	if (msg->status)
>  		return;
>  
> +	msg->actual_length += t->len;
> +
>  	/*
>  	 * After this transfer is finished, perform any possible
>  	 * post-transfer actions requested by the protocol driver.
> @@ -752,6 +956,75 @@ static irqreturn_t ep93xx_spi_interrupt(int irq, void *dev_id)
>  	return IRQ_HANDLED;
>  }
>  
> +static bool ep93xx_spi_dma_filter(struct dma_chan *chan, void *filter_param)
> +{
> +	if (ep93xx_dma_chan_is_m2p(chan))
> +		return false;
> +
> +	chan->private = filter_param;
> +	return true;
> +}
> +
> +static int ep93xx_spi_setup_dma(struct ep93xx_spi *espi)
> +{
> +	dma_cap_mask_t mask;
> +	int ret;
> +
> +	espi->zeropage = (void *)get_zeroed_page(GFP_KERNEL);
> +	if (!espi->zeropage)
> +		return -ENOMEM;
> +
> +	dma_cap_zero(mask);
> +	dma_cap_set(DMA_SLAVE, mask);
> +
> +	espi->dma_rx_data.port = EP93XX_DMA_SSP;
> +	espi->dma_rx_data.direction = DMA_FROM_DEVICE;
> +	espi->dma_rx_data.name = "ep93xx-spi-rx";
> +
> +	espi->dma_rx = dma_request_channel(mask, ep93xx_spi_dma_filter,
> +					   &espi->dma_rx_data);
> +	if (!espi->dma_rx) {
> +		ret = -ENODEV;
> +		goto fail_free_page;
> +	}
> +
> +	espi->dma_tx_data.port = EP93XX_DMA_SSP;
> +	espi->dma_tx_data.direction = DMA_TO_DEVICE;
> +	espi->dma_tx_data.name = "ep93xx-spi-tx";
> +
> +	espi->dma_tx = dma_request_channel(mask, ep93xx_spi_dma_filter,
> +					   &espi->dma_tx_data);
> +	if (!espi->dma_tx) {
> +		ret = -ENODEV;
> +		goto fail_release_rx;
> +	}
> +
> +	return 0;
> +
> +fail_release_rx:
> +	dma_release_channel(espi->dma_rx);
> +	espi->dma_rx = NULL;
> +fail_free_page:
> +	free_page((unsigned long)espi->zeropage);
> +
> +	return ret;
> +}
> +
> +static void ep93xx_spi_release_dma(struct ep93xx_spi *espi)
> +{
> +	if (espi->dma_rx) {
> +		dma_release_channel(espi->dma_rx);
> +		sg_free_table(&espi->rx_sgt);
> +	}
> +	if (espi->dma_tx) {
> +		dma_release_channel(espi->dma_tx);
> +		sg_free_table(&espi->tx_sgt);
> +	}
> +
> +	if (espi->zeropage)
> +		free_page((unsigned long)espi->zeropage);
> +}
> +
>  static int __init ep93xx_spi_probe(struct platform_device *pdev)
>  {
>  	struct spi_master *master;
> @@ -818,6 +1091,7 @@ static int __init ep93xx_spi_probe(struct platform_device *pdev)
>  		goto fail_put_clock;
>  	}
>  
> +	espi->sspdr_phys = res->start + SSPDR;
>  	espi->regs_base = ioremap(res->start, resource_size(res));
>  	if (!espi->regs_base) {
>  		dev_err(&pdev->dev, "failed to map resources\n");
> @@ -832,10 +1106,13 @@ static int __init ep93xx_spi_probe(struct platform_device *pdev)
>  		goto fail_unmap_regs;
>  	}
>  
> +	if (info->use_dma && ep93xx_spi_setup_dma(espi))
> +		dev_warn(&pdev->dev, "DMA setup failed. Falling back to PIO\n");
> +
>  	espi->wq = create_singlethread_workqueue("ep93xx_spid");
>  	if (!espi->wq) {
>  		dev_err(&pdev->dev, "unable to create workqueue\n");
> -		goto fail_free_irq;
> +		goto fail_free_dma;
>  	}
>  	INIT_WORK(&espi->msg_work, ep93xx_spi_work);
>  	INIT_LIST_HEAD(&espi->msg_queue);
> @@ -857,7 +1134,8 @@ static int __init ep93xx_spi_probe(struct platform_device *pdev)
>  
>  fail_free_queue:
>  	destroy_workqueue(espi->wq);
> -fail_free_irq:
> +fail_free_dma:
> +	ep93xx_spi_release_dma(espi);
>  	free_irq(espi->irq, espi);
>  fail_unmap_regs:
>  	iounmap(espi->regs_base);
> @@ -901,6 +1179,7 @@ static int __exit ep93xx_spi_remove(struct platform_device *pdev)
>  	}
>  	spin_unlock_irq(&espi->lock);
>  
> +	ep93xx_spi_release_dma(espi);
>  	free_irq(espi->irq, espi);
>  	iounmap(espi->regs_base);
>  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> -- 
> 1.7.4.4
> 

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

* Re: [PATCH v2 0/5] ep93xx DMA patches
  2011-05-29 10:10 [PATCH v2 0/5] ep93xx DMA patches Mika Westerberg
                   ` (4 preceding siblings ...)
  2011-05-29 10:10 ` [PATCH v2 5/5] spi/ep93xx: add DMA support Mika Westerberg
@ 2011-06-05  8:19 ` Mika Westerberg
  2011-06-06  6:39   ` Koul, Vinod
  5 siblings, 1 reply; 36+ messages in thread
From: Mika Westerberg @ 2011-06-05  8:19 UTC (permalink / raw)
  To: vinod.koul
  Cc: hsweeten, rmallon, dan.j.williams, lrg, broonie, grant.likely,
	linux-kernel, linux-arm-kernel

Vinod,

As this series has acks for all the subsystem maintainers, would it be ok for
you to take the entire series via your tree? Or do you have any idea how to
get it merged?

On Sun, May 29, 2011 at 01:10:01PM +0300, Mika Westerberg wrote:
> This is an updated version of the ep93xx DMA patches. Previous version can be
> found here: https://lkml.org/lkml/2011/5/22/86.
> 
> Changes to v1:
>  * removed unnecessary header
>  * corrected SPI driver documentation
>  * DMA driver platform code is only compiled when CONFIG_EP93XX_DMA is
>    enabled
> 
> I tried to put all the acks in place, hope I got them right.
> 
> There is a dependency between patch 1 and patch 4 which I'm not entirely sure
> how to solve. Basically it is required that we have patch 1 applied before
> patch 4 or merge conflict results.
> 
> Otherwise it shouldn't matter in which order the patches arrive to the
> mainline.
> 
> Unless someone has a better idea, I suggest that these patches will go through
> their respective subsystem trees. All the maintainers should be copied.
> 
> Thanks,
> MW
> 
> Mika Westerberg (5):
>   dmaengine: add ep93xx DMA support
>   ep93xx: add dmaengine platform code
>   ASoC: ep93xx: convert to use the DMA engine API
>   ep93xx: remove the old M2P DMA code
>   spi/ep93xx: add DMA support
> 
>  Documentation/spi/ep93xx_spi                   |   10 +
>  arch/arm/mach-ep93xx/Makefile                  |    4 +-
>  arch/arm/mach-ep93xx/core.c                    |    6 +-
>  arch/arm/mach-ep93xx/dma-m2p.c                 |  411 -------
>  arch/arm/mach-ep93xx/dma.c                     |  108 ++
>  arch/arm/mach-ep93xx/include/mach/dma.h        |  190 ++---
>  arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h |    2 +
>  drivers/dma/Kconfig                            |    7 +
>  drivers/dma/Makefile                           |    1 +
>  drivers/dma/ep93xx_dma.c                       | 1355 ++++++++++++++++++++++++
>  drivers/spi/ep93xx_spi.c                       |  303 +++++-
>  sound/soc/ep93xx/ep93xx-ac97.c                 |    4 +-
>  sound/soc/ep93xx/ep93xx-i2s.c                  |    4 +-
>  sound/soc/ep93xx/ep93xx-pcm.c                  |  137 ++--
>  14 files changed, 1930 insertions(+), 612 deletions(-)
>  delete mode 100644 arch/arm/mach-ep93xx/dma-m2p.c
>  create mode 100644 arch/arm/mach-ep93xx/dma.c
>  create mode 100644 drivers/dma/ep93xx_dma.c
> 
> -- 
> 1.7.4.4

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

* Re: [PATCH v2 0/5] ep93xx DMA patches
  2011-06-05  8:19 ` [PATCH v2 0/5] ep93xx DMA patches Mika Westerberg
@ 2011-06-06  6:39   ` Koul, Vinod
  2011-06-06 16:42     ` Mika Westerberg
  0 siblings, 1 reply; 36+ messages in thread
From: Koul, Vinod @ 2011-06-06  6:39 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: hsweeten, rmallon, dan.j.williams, lrg, broonie, grant.likely,
	linux-kernel, linux-arm-kernel

On Sun, 2011-06-05 at 11:19 +0300, Mika Westerberg wrote:
> Vinod,
> 
> As this series has acks for all the subsystem maintainers, would it be ok for
> you to take the entire series via your tree? Or do you have any idea how to
> get it merged?
Please don't top post :(

I have applied this series to next including the asoc and spi changes,
if anyone has any objections please let me know

> 
> On Sun, May 29, 2011 at 01:10:01PM +0300, Mika Westerberg wrote:
> > This is an updated version of the ep93xx DMA patches. Previous version can be
> > found here: https://lkml.org/lkml/2011/5/22/86.
> > 
> > Changes to v1:
> >  * removed unnecessary header
> >  * corrected SPI driver documentation
> >  * DMA driver platform code is only compiled when CONFIG_EP93XX_DMA is
> >    enabled
> > 
> > I tried to put all the acks in place, hope I got them right.
> > 
> > There is a dependency between patch 1 and patch 4 which I'm not entirely sure
> > how to solve. Basically it is required that we have patch 1 applied before
> > patch 4 or merge conflict results.
> > 
> > Otherwise it shouldn't matter in which order the patches arrive to the
> > mainline.
> > 
> > Unless someone has a better idea, I suggest that these patches will go through
> > their respective subsystem trees. All the maintainers should be copied.
> > 
> > Thanks,
> > MW
> > 
> > Mika Westerberg (5):
> >   dmaengine: add ep93xx DMA support
> >   ep93xx: add dmaengine platform code
> >   ASoC: ep93xx: convert to use the DMA engine API
> >   ep93xx: remove the old M2P DMA code
> >   spi/ep93xx: add DMA support
> > 
> >  Documentation/spi/ep93xx_spi                   |   10 +
> >  arch/arm/mach-ep93xx/Makefile                  |    4 +-
> >  arch/arm/mach-ep93xx/core.c                    |    6 +-
> >  arch/arm/mach-ep93xx/dma-m2p.c                 |  411 -------
> >  arch/arm/mach-ep93xx/dma.c                     |  108 ++
> >  arch/arm/mach-ep93xx/include/mach/dma.h        |  190 ++---
> >  arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h |    2 +
> >  drivers/dma/Kconfig                            |    7 +
> >  drivers/dma/Makefile                           |    1 +
> >  drivers/dma/ep93xx_dma.c                       | 1355 ++++++++++++++++++++++++
> >  drivers/spi/ep93xx_spi.c                       |  303 +++++-
> >  sound/soc/ep93xx/ep93xx-ac97.c                 |    4 +-
> >  sound/soc/ep93xx/ep93xx-i2s.c                  |    4 +-
> >  sound/soc/ep93xx/ep93xx-pcm.c                  |  137 ++--
> >  14 files changed, 1930 insertions(+), 612 deletions(-)
> >  delete mode 100644 arch/arm/mach-ep93xx/dma-m2p.c
> >  create mode 100644 arch/arm/mach-ep93xx/dma.c
> >  create mode 100644 drivers/dma/ep93xx_dma.c
> > 
> > -- 
> > 1.7.4.4
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/


-- 
~Vinod


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

* Re: [PATCH v2 0/5] ep93xx DMA patches
  2011-06-06  6:39   ` Koul, Vinod
@ 2011-06-06 16:42     ` Mika Westerberg
  0 siblings, 0 replies; 36+ messages in thread
From: Mika Westerberg @ 2011-06-06 16:42 UTC (permalink / raw)
  To: Koul, Vinod
  Cc: hsweeten, rmallon, dan.j.williams, lrg, broonie, grant.likely,
	linux-kernel, linux-arm-kernel

On Mon, Jun 06, 2011 at 12:09:40PM +0530, Koul, Vinod wrote:
> 
> I have applied this series to next including the asoc and spi changes,
> if anyone has any objections please let me know

Great, thanks!

I believe that this was the best option considering that now we don't have to
worry about dependencies.

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

* Re: [PATCH v2 5/5] spi/ep93xx: add DMA support
  2011-06-03 20:44   ` Grant Likely
@ 2011-06-07 17:14     ` Mika Westerberg
  2011-06-07 18:45       ` Grant Likely
  0 siblings, 1 reply; 36+ messages in thread
From: Mika Westerberg @ 2011-06-07 17:14 UTC (permalink / raw)
  To: Grant Likely
  Cc: linux-arm-kernel, hsweeten, rmallon, vinod.koul, dan.j.williams,
	lrg, broonie, linux-kernel

On Fri, Jun 03, 2011 at 02:44:00PM -0600, Grant Likely wrote:
> On Sun, May 29, 2011 at 01:10:06PM +0300, Mika Westerberg wrote:
> > This patch adds DMA support for the EP93xx SPI driver. By default the DMA is
> > not enabled but it can be enabled by setting ep93xx_spi_info.use_dma to true
> > in board configuration file.
> > 
> > Note that the SPI driver still uses PIO for small transfers (<= 8 bytes) for
> > performance reasons.
> > 
> > Signed-off-by: Mika Westerberg <mika.westerberg@iki.fi>
> > Acked-by: H Hartley Sweeten <hsweeten@visionengravers.com>
> > Cc: Grant Likely <grant.likely@secretlab.ca>
> 
> Acked-by: Grant Likely <grant.likely@secretlab.ca>
> 
> Since this depends on the DMA patches, it can go in via the same tree.

I just realized that once the SPI driver renaming patch (spi: reorganize
drivers) enters linux-next, this patch (which sits on Vinod's tree) will cause
a merge conflict. Any ideas how to avoid it beforehand?

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

* Re: [PATCH v2 5/5] spi/ep93xx: add DMA support
  2011-06-07 17:14     ` Mika Westerberg
@ 2011-06-07 18:45       ` Grant Likely
  2011-06-07 19:06         ` Mika Westerberg
  0 siblings, 1 reply; 36+ messages in thread
From: Grant Likely @ 2011-06-07 18:45 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: linux-arm-kernel, hsweeten, rmallon, vinod.koul, dan.j.williams,
	lrg, broonie, linux-kernel

On Tue, Jun 7, 2011 at 11:14 AM, Mika Westerberg <mika.westerberg@iki.fi> wrote:
> On Fri, Jun 03, 2011 at 02:44:00PM -0600, Grant Likely wrote:
>> On Sun, May 29, 2011 at 01:10:06PM +0300, Mika Westerberg wrote:
>> > This patch adds DMA support for the EP93xx SPI driver. By default the DMA is
>> > not enabled but it can be enabled by setting ep93xx_spi_info.use_dma to true
>> > in board configuration file.
>> >
>> > Note that the SPI driver still uses PIO for small transfers (<= 8 bytes) for
>> > performance reasons.
>> >
>> > Signed-off-by: Mika Westerberg <mika.westerberg@iki.fi>
>> > Acked-by: H Hartley Sweeten <hsweeten@visionengravers.com>
>> > Cc: Grant Likely <grant.likely@secretlab.ca>
>>
>> Acked-by: Grant Likely <grant.likely@secretlab.ca>
>>
>> Since this depends on the DMA patches, it can go in via the same tree.
>
> I just realized that once the SPI driver renaming patch (spi: reorganize
> drivers) enters linux-next, this patch (which sits on Vinod's tree) will cause
> a merge conflict. Any ideas how to avoid it beforehand?

Yes, put the whole series into a separate branch all by itself.  Merge
that branch into Vinod's tree and I'll also merge it into spi/next,
and I'll fix up the merge conflict when I do.

g.

-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

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

* Re: [PATCH v2 5/5] spi/ep93xx: add DMA support
  2011-06-07 18:45       ` Grant Likely
@ 2011-06-07 19:06         ` Mika Westerberg
  2011-06-07 19:18           ` Grant Likely
  0 siblings, 1 reply; 36+ messages in thread
From: Mika Westerberg @ 2011-06-07 19:06 UTC (permalink / raw)
  To: Grant Likely
  Cc: linux-arm-kernel, hsweeten, rmallon, vinod.koul, dan.j.williams,
	lrg, broonie, linux-kernel

On Tue, Jun 07, 2011 at 12:45:26PM -0600, Grant Likely wrote:
> On Tue, Jun 7, 2011 at 11:14 AM, Mika Westerberg <mika.westerberg@iki.fi> wrote:
> >
> > I just realized that once the SPI driver renaming patch (spi: reorganize
> > drivers) enters linux-next, this patch (which sits on Vinod's tree) will cause
> > a merge conflict. Any ideas how to avoid it beforehand?
> 
> Yes, put the whole series into a separate branch all by itself.  Merge
> that branch into Vinod's tree and I'll also merge it into spi/next,
> and I'll fix up the merge conflict when I do.

More stupid questions: Do you mean that I create a branch in my public
repository somewhere? If that is the case, then there is a problem that I
don't have any public repository and I doubt that I get such before linux-next
is rebuilt.

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

* Re: [PATCH v2 5/5] spi/ep93xx: add DMA support
  2011-06-07 19:06         ` Mika Westerberg
@ 2011-06-07 19:18           ` Grant Likely
  2011-06-07 19:29             ` Mika Westerberg
  0 siblings, 1 reply; 36+ messages in thread
From: Grant Likely @ 2011-06-07 19:18 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: linux-arm-kernel, hsweeten, rmallon, vinod.koul, dan.j.williams,
	lrg, broonie, linux-kernel

On Tue, Jun 07, 2011 at 10:06:07PM +0300, Mika Westerberg wrote:
> On Tue, Jun 07, 2011 at 12:45:26PM -0600, Grant Likely wrote:
> > On Tue, Jun 7, 2011 at 11:14 AM, Mika Westerberg <mika.westerberg@iki.fi> wrote:
> > >
> > > I just realized that once the SPI driver renaming patch (spi: reorganize
> > > drivers) enters linux-next, this patch (which sits on Vinod's tree) will cause
> > > a merge conflict. Any ideas how to avoid it beforehand?
> > 
> > Yes, put the whole series into a separate branch all by itself.  Merge
> > that branch into Vinod's tree and I'll also merge it into spi/next,
> > and I'll fix up the merge conflict when I do.
> 
> More stupid questions: Do you mean that I create a branch in my public
> repository somewhere? If that is the case, then there is a problem that I
> don't have any public repository and I doubt that I get such before linux-next
> is rebuilt.

It doesn't have to be your repo.  Vinod could do it, or I could.

g.


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

* Re: [PATCH v2 5/5] spi/ep93xx: add DMA support
  2011-06-07 19:18           ` Grant Likely
@ 2011-06-07 19:29             ` Mika Westerberg
  2011-06-07 19:40               ` Grant Likely
  0 siblings, 1 reply; 36+ messages in thread
From: Mika Westerberg @ 2011-06-07 19:29 UTC (permalink / raw)
  To: Grant Likely
  Cc: linux-arm-kernel, hsweeten, rmallon, vinod.koul, dan.j.williams,
	lrg, broonie, linux-kernel

On Tue, Jun 07, 2011 at 01:18:24PM -0600, Grant Likely wrote:
> On Tue, Jun 07, 2011 at 10:06:07PM +0300, Mika Westerberg wrote:
> > On Tue, Jun 07, 2011 at 12:45:26PM -0600, Grant Likely wrote:
> > > On Tue, Jun 7, 2011 at 11:14 AM, Mika Westerberg <mika.westerberg@iki.fi> wrote:
> > > >
> > > > I just realized that once the SPI driver renaming patch (spi: reorganize
> > > > drivers) enters linux-next, this patch (which sits on Vinod's tree) will cause
> > > > a merge conflict. Any ideas how to avoid it beforehand?
> > > 
> > > Yes, put the whole series into a separate branch all by itself.  Merge
> > > that branch into Vinod's tree and I'll also merge it into spi/next,
> > > and I'll fix up the merge conflict when I do.
> > 
> > More stupid questions: Do you mean that I create a branch in my public
> > repository somewhere? If that is the case, then there is a problem that I
> > don't have any public repository and I doubt that I get such before linux-next
> > is rebuilt.
> 
> It doesn't have to be your repo.  Vinod could do it, or I could.

Ah, I see.

Would it then be possible that you could do it? Please let me and Vinod know
what is needed from us.

And sorry for this mess.

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

* Re: [PATCH v2 5/5] spi/ep93xx: add DMA support
  2011-06-07 19:29             ` Mika Westerberg
@ 2011-06-07 19:40               ` Grant Likely
  2011-06-08  3:58                 ` Koul, Vinod
  0 siblings, 1 reply; 36+ messages in thread
From: Grant Likely @ 2011-06-07 19:40 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: linux-arm-kernel, hsweeten, rmallon, vinod.koul, dan.j.williams,
	lrg, broonie, linux-kernel

On Tue, Jun 07, 2011 at 10:29:45PM +0300, Mika Westerberg wrote:
> On Tue, Jun 07, 2011 at 01:18:24PM -0600, Grant Likely wrote:
> > On Tue, Jun 07, 2011 at 10:06:07PM +0300, Mika Westerberg wrote:
> > > On Tue, Jun 07, 2011 at 12:45:26PM -0600, Grant Likely wrote:
> > > > On Tue, Jun 7, 2011 at 11:14 AM, Mika Westerberg <mika.westerberg@iki.fi> wrote:
> > > > >
> > > > > I just realized that once the SPI driver renaming patch (spi: reorganize
> > > > > drivers) enters linux-next, this patch (which sits on Vinod's tree) will cause
> > > > > a merge conflict. Any ideas how to avoid it beforehand?
> > > > 
> > > > Yes, put the whole series into a separate branch all by itself.  Merge
> > > > that branch into Vinod's tree and I'll also merge it into spi/next,
> > > > and I'll fix up the merge conflict when I do.
> > > 
> > > More stupid questions: Do you mean that I create a branch in my public
> > > repository somewhere? If that is the case, then there is a problem that I
> > > don't have any public repository and I doubt that I get such before linux-next
> > > is rebuilt.
> > 
> > It doesn't have to be your repo.  Vinod could do it, or I could.
> 
> Ah, I see.
> 
> Would it then be possible that you could do it? Please let me and Vinod know
> what is needed from us.
> 
> And sorry for this mess.

I'll need an ack from Vinod first for the series so that I can add it
before I make the commits, but other than that I have no problem doing
so.

g.

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

* Re: [PATCH v2 5/5] spi/ep93xx: add DMA support
  2011-06-07 19:40               ` Grant Likely
@ 2011-06-08  3:58                 ` Koul, Vinod
  2011-06-08 21:53                   ` Grant Likely
  0 siblings, 1 reply; 36+ messages in thread
From: Koul, Vinod @ 2011-06-08  3:58 UTC (permalink / raw)
  To: Grant Likely
  Cc: Mika Westerberg, linux-arm-kernel, hsweeten, rmallon,
	dan.j.williams, lrg, broonie, linux-kernel

On Tue, 2011-06-07 at 13:40 -0600, Grant Likely wrote:
> On Tue, Jun 07, 2011 at 10:29:45PM +0300, Mika Westerberg wrote:
> > On Tue, Jun 07, 2011 at 01:18:24PM -0600, Grant Likely wrote:
> > > On Tue, Jun 07, 2011 at 10:06:07PM +0300, Mika Westerberg wrote:
> > > > On Tue, Jun 07, 2011 at 12:45:26PM -0600, Grant Likely wrote:
> > > > > On Tue, Jun 7, 2011 at 11:14 AM, Mika Westerberg <mika.westerberg@iki.fi> wrote:
> > > > > >
> > > > > > I just realized that once the SPI driver renaming patch (spi: reorganize
> > > > > > drivers) enters linux-next, this patch (which sits on Vinod's tree) will cause
> > > > > > a merge conflict. Any ideas how to avoid it beforehand?
> > > > > 
> > > > > Yes, put the whole series into a separate branch all by itself.  Merge
> > > > > that branch into Vinod's tree and I'll also merge it into spi/next,
> > > > > and I'll fix up the merge conflict when I do.
> > > > 
> > > > More stupid questions: Do you mean that I create a branch in my public
> > > > repository somewhere? If that is the case, then there is a problem that I
> > > > don't have any public repository and I doubt that I get such before linux-next
> > > > is rebuilt.
> > > 
> > > It doesn't have to be your repo.  Vinod could do it, or I could.
> > 
> > Ah, I see.
> > 
> > Would it then be possible that you could do it? Please let me and Vinod know
> > what is needed from us.
> > 
> > And sorry for this mess.
> 
> I'll need an ack from Vinod first for the series so that I can add it
> before I make the commits, but other than that I have no problem doing
> so.
Acked-by: Vinod Koul <vinod.koul@intel.com>

Let me know which branch you have pushed and I will merge that to my
tree


-- 
~Vinod


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

* Re: [PATCH v2 5/5] spi/ep93xx: add DMA support
  2011-06-08  3:58                 ` Koul, Vinod
@ 2011-06-08 21:53                   ` Grant Likely
  2011-06-09 16:42                     ` Koul, Vinod
  0 siblings, 1 reply; 36+ messages in thread
From: Grant Likely @ 2011-06-08 21:53 UTC (permalink / raw)
  To: Koul, Vinod
  Cc: Mika Westerberg, linux-arm-kernel, hsweeten, rmallon,
	dan.j.williams, lrg, broonie, linux-kernel

On Wed, Jun 08, 2011 at 09:28:32AM +0530, Koul, Vinod wrote:
> On Tue, 2011-06-07 at 13:40 -0600, Grant Likely wrote:
> > On Tue, Jun 07, 2011 at 10:29:45PM +0300, Mika Westerberg wrote:
> > > On Tue, Jun 07, 2011 at 01:18:24PM -0600, Grant Likely wrote:
> > > > On Tue, Jun 07, 2011 at 10:06:07PM +0300, Mika Westerberg wrote:
> > > > > On Tue, Jun 07, 2011 at 12:45:26PM -0600, Grant Likely wrote:
> > > > > > On Tue, Jun 7, 2011 at 11:14 AM, Mika Westerberg <mika.westerberg@iki.fi> wrote:
> > > > > > >
> > > > > > > I just realized that once the SPI driver renaming patch (spi: reorganize
> > > > > > > drivers) enters linux-next, this patch (which sits on Vinod's tree) will cause
> > > > > > > a merge conflict. Any ideas how to avoid it beforehand?
> > > > > > 
> > > > > > Yes, put the whole series into a separate branch all by itself.  Merge
> > > > > > that branch into Vinod's tree and I'll also merge it into spi/next,
> > > > > > and I'll fix up the merge conflict when I do.
> > > > > 
> > > > > More stupid questions: Do you mean that I create a branch in my public
> > > > > repository somewhere? If that is the case, then there is a problem that I
> > > > > don't have any public repository and I doubt that I get such before linux-next
> > > > > is rebuilt.
> > > > 
> > > > It doesn't have to be your repo.  Vinod could do it, or I could.
> > > 
> > > Ah, I see.
> > > 
> > > Would it then be possible that you could do it? Please let me and Vinod know
> > > what is needed from us.
> > > 
> > > And sorry for this mess.
> > 
> > I'll need an ack from Vinod first for the series so that I can add it
> > before I make the commits, but other than that I have no problem doing
> > so.
> Acked-by: Vinod Koul <vinod.koul@intel.com>
> 
> Let me know which branch you have pushed and I will merge that to my
> tree

Just pushed out to the branch below.  Take a look, make sure it is
correct, and if it looks good to you then go ahead and merge it, and I
ll also merge it into spi/next.

The following changes since commit 59c5f46fbe01a00eedf54a23789634438bb80603:

  Linux 3.0-rc2 (2011-06-06 18:06:33 +0900)

are available in the git repository at:
  git://git.secretlab.ca/git/linux-2.6 ep93xx-dma

Geert Uytterhoeven (1):
      spi/rtc-m41t93: Use spi_get_drvdata() for SPI devices

Govindraj.R (1):
      spi/omap2: fix uninitialized variable

Grant Likely (1):
      Merge remote-tracking branch 'origin' into spi/merge

Mika Westerberg (5):
      dmaengine: add ep93xx DMA support
      ep93xx: add dmaengine platform code
      ASoC: ep93xx: convert to use the DMA engine API
      ep93xx: remove the old M2P DMA code
      spi/ep93xx: add DMA support

 Documentation/spi/ep93xx_spi                   |   10 +
 arch/arm/mach-ep93xx/Makefile                  |    4 +-
 arch/arm/mach-ep93xx/core.c                    |    6 +-
 arch/arm/mach-ep93xx/dma-m2p.c                 |  411 -------
 arch/arm/mach-ep93xx/dma.c                     |  108 ++
 arch/arm/mach-ep93xx/include/mach/dma.h        |  190 ++---
 arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h |    2 +
 drivers/dma/Kconfig                            |    7 +
 drivers/dma/Makefile                           |    1 +
 drivers/dma/ep93xx_dma.c                       | 1355 ++++++++++++++++++++++++
 drivers/rtc/rtc-m41t93.c                       |    2 +-
 drivers/spi/ep93xx_spi.c                       |  303 +++++-
 drivers/spi/omap2_mcspi.c                      |    2 +-
 sound/soc/ep93xx/ep93xx-ac97.c                 |    4 +-
 sound/soc/ep93xx/ep93xx-i2s.c                  |    4 +-
 sound/soc/ep93xx/ep93xx-pcm.c                  |  137 ++--
 16 files changed, 1932 insertions(+), 614 deletions(-)
 delete mode 100644 arch/arm/mach-ep93xx/dma-m2p.c
 create mode 100644 arch/arm/mach-ep93xx/dma.c
 create mode 100644 drivers/dma/ep93xx_dma.c


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

* Re: [PATCH v2 5/5] spi/ep93xx: add DMA support
  2011-06-08 21:53                   ` Grant Likely
@ 2011-06-09 16:42                     ` Koul, Vinod
  2011-06-09 18:46                       ` Grant Likely
  0 siblings, 1 reply; 36+ messages in thread
From: Koul, Vinod @ 2011-06-09 16:42 UTC (permalink / raw)
  To: Grant Likely
  Cc: Mika Westerberg, linux-arm-kernel, hsweeten, rmallon,
	dan.j.williams, lrg, broonie, linux-kernel

On Wed, 2011-06-08 at 15:53 -0600, Grant Likely wrote:
> On Wed, Jun 08, 2011 at 09:28:32AM +0530, Koul, Vinod wrote:
> > On Tue, 2011-06-07 at 13:40 -0600, Grant Likely wrote:
> > > On Tue, Jun 07, 2011 at 10:29:45PM +0300, Mika Westerberg wrote:
> > > > On Tue, Jun 07, 2011 at 01:18:24PM -0600, Grant Likely wrote:
> > > > > On Tue, Jun 07, 2011 at 10:06:07PM +0300, Mika Westerberg wrote:
> > > > > > On Tue, Jun 07, 2011 at 12:45:26PM -0600, Grant Likely wrote:
> > > > > > > On Tue, Jun 7, 2011 at 11:14 AM, Mika Westerberg <mika.westerberg@iki.fi> wrote:
> > > > > > > >
> > > > > > > > I just realized that once the SPI driver renaming patch (spi: reorganize
> > > > > > > > drivers) enters linux-next, this patch (which sits on Vinod's tree) will cause
> > > > > > > > a merge conflict. Any ideas how to avoid it beforehand?
> > > > > > > 
> > > > > > > Yes, put the whole series into a separate branch all by itself.  Merge
> > > > > > > that branch into Vinod's tree and I'll also merge it into spi/next,
> > > > > > > and I'll fix up the merge conflict when I do.
> > > > > > 
> > > > > > More stupid questions: Do you mean that I create a branch in my public
> > > > > > repository somewhere? If that is the case, then there is a problem that I
> > > > > > don't have any public repository and I doubt that I get such before linux-next
> > > > > > is rebuilt.
> > > > > 
> > > > > It doesn't have to be your repo.  Vinod could do it, or I could.
> > > > 
> > > > Ah, I see.
> > > > 
> > > > Would it then be possible that you could do it? Please let me and Vinod know
> > > > what is needed from us.
> > > > 
> > > > And sorry for this mess.
> > > 
> > > I'll need an ack from Vinod first for the series so that I can add it
> > > before I make the commits, but other than that I have no problem doing
> > > so.
> > Acked-by: Vinod Koul <vinod.koul@intel.com>
> > 
> > Let me know which branch you have pushed and I will merge that to my
> > tree
> 
> Just pushed out to the branch below.  Take a look, make sure it is
> correct, and if it looks good to you then go ahead and merge it, and I
> ll also merge it into spi/next.
> 
> The following changes since commit 59c5f46fbe01a00eedf54a23789634438bb80603:
> 
>   Linux 3.0-rc2 (2011-06-06 18:06:33 +0900)
> 
> are available in the git repository at:
>   git://git.secretlab.ca/git/linux-2.6 ep93xx-dma
> 
> Geert Uytterhoeven (1):
>       spi/rtc-m41t93: Use spi_get_drvdata() for SPI devices
> 
> Govindraj.R (1):
>       spi/omap2: fix uninitialized variable
> 
> Grant Likely (1):
>       Merge remote-tracking branch 'origin' into spi/merge
> 
> Mika Westerberg (5):
>       dmaengine: add ep93xx DMA support
>       ep93xx: add dmaengine platform code
>       ASoC: ep93xx: convert to use the DMA engine API
>       ep93xx: remove the old M2P DMA code
>       spi/ep93xx: add DMA support
> 
>  Documentation/spi/ep93xx_spi                   |   10 +
>  arch/arm/mach-ep93xx/Makefile                  |    4 +-
>  arch/arm/mach-ep93xx/core.c                    |    6 +-
>  arch/arm/mach-ep93xx/dma-m2p.c                 |  411 -------
>  arch/arm/mach-ep93xx/dma.c                     |  108 ++
>  arch/arm/mach-ep93xx/include/mach/dma.h        |  190 ++---
>  arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h |    2 +
>  drivers/dma/Kconfig                            |    7 +
>  drivers/dma/Makefile                           |    1 +
>  drivers/dma/ep93xx_dma.c                       | 1355 ++++++++++++++++++++++++
>  drivers/rtc/rtc-m41t93.c                       |    2 +-
>  drivers/spi/ep93xx_spi.c                       |  303 +++++-
>  drivers/spi/omap2_mcspi.c                      |    2 +-
>  sound/soc/ep93xx/ep93xx-ac97.c                 |    4 +-
>  sound/soc/ep93xx/ep93xx-i2s.c                  |    4 +-
>  sound/soc/ep93xx/ep93xx-pcm.c                  |  137 ++--
>  16 files changed, 1932 insertions(+), 614 deletions(-)
>  delete mode 100644 arch/arm/mach-ep93xx/dma-m2p.c
>  create mode 100644 arch/arm/mach-ep93xx/dma.c
>  create mode 100644 drivers/dma/ep93xx_dma.c
Thanks, I have merged them into my slave-dma next.


-- 
~Vinod


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

* Re: [PATCH v2 5/5] spi/ep93xx: add DMA support
  2011-06-09 16:42                     ` Koul, Vinod
@ 2011-06-09 18:46                       ` Grant Likely
  2011-06-09 19:15                         ` Mika Westerberg
  0 siblings, 1 reply; 36+ messages in thread
From: Grant Likely @ 2011-06-09 18:46 UTC (permalink / raw)
  To: Koul, Vinod
  Cc: Mika Westerberg, linux-arm-kernel, hsweeten, rmallon,
	dan.j.williams, lrg, broonie, linux-kernel

On Thu, Jun 09, 2011 at 10:12:37PM +0530, Koul, Vinod wrote:
> On Wed, 2011-06-08 at 15:53 -0600, Grant Likely wrote:
> > On Wed, Jun 08, 2011 at 09:28:32AM +0530, Koul, Vinod wrote:
> > > Acked-by: Vinod Koul <vinod.koul@intel.com>
> > > 
> > > Let me know which branch you have pushed and I will merge that to my
> > > tree
> > 
> > Just pushed out to the branch below.  Take a look, make sure it is
> > correct, and if it looks good to you then go ahead and merge it, and I
> > ll also merge it into spi/next.
> > 
> > The following changes since commit 59c5f46fbe01a00eedf54a23789634438bb80603:
> > 
> >   Linux 3.0-rc2 (2011-06-06 18:06:33 +0900)
> > 
> > are available in the git repository at:
> >   git://git.secretlab.ca/git/linux-2.6 ep93xx-dma
>
> Thanks, I have merged them into my slave-dma next.

Good.  I've merged it into spi/next also.

g.


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

* Re: [PATCH v2 5/5] spi/ep93xx: add DMA support
  2011-06-09 18:46                       ` Grant Likely
@ 2011-06-09 19:15                         ` Mika Westerberg
  0 siblings, 0 replies; 36+ messages in thread
From: Mika Westerberg @ 2011-06-09 19:15 UTC (permalink / raw)
  To: Grant Likely
  Cc: Koul, Vinod, linux-arm-kernel, hsweeten, rmallon, dan.j.williams,
	lrg, broonie, linux-kernel

On Thu, Jun 09, 2011 at 12:46:14PM -0600, Grant Likely wrote:
> On Thu, Jun 09, 2011 at 10:12:37PM +0530, Koul, Vinod wrote:
> > Thanks, I have merged them into my slave-dma next.
> 
> Good.  I've merged it into spi/next also.

Thanks guys!

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

* Re: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-05-29 10:10 ` [PATCH v2 1/5] dmaengine: add ep93xx DMA support Mika Westerberg
@ 2011-11-15 15:02   ` Rafal Prylowski
  2011-11-15 17:59     ` H Hartley Sweeten
  2011-11-16  6:55     ` Mika Westerberg
  0 siblings, 2 replies; 36+ messages in thread
From: Rafal Prylowski @ 2011-11-15 15:02 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: linux-arm-kernel, rmallon, vinod.koul, broonie, linux-kernel,
	grant.likely, hsweeten, dan.j.williams, lrg

Hello.

During adaptation of my experimental ep93xx ide driver to the new dmaengine api,
I discovered two issues with ep93xx dma implementation:
1) Control register is incorrectly programmed for IDE (m2m_hw_setup),
probably copy-paste bug ;)
2) Kernel oops when trying to stop running transfers by calling
dmaengine_terminate_all(...) - caused by dereferencing empty list
in ep93xx_dma_get_active

Following is a patch, which solves these problems for me.

Regards,
Rafal Prylowski

Index: linux-2.6/drivers/dma/ep93xx_dma.c
===================================================================
--- linux-2.6.orig/drivers/dma/ep93xx_dma.c
+++ linux-2.6/drivers/dma/ep93xx_dma.c
@@ -246,6 +246,7 @@ static void ep93xx_dma_set_active(struct
 static struct ep93xx_dma_desc *
 ep93xx_dma_get_active(struct ep93xx_dma_chan *edmac)
 {
+	BUG_ON(list_empty(&edmac->active));
 	return list_first_entry(&edmac->active, struct ep93xx_dma_desc, node);
 }
 
@@ -459,9 +460,6 @@ static int m2m_hw_setup(struct ep93xx_dm
 		 * This IDE part is totally untested. Values below are taken
 		 * from the EP93xx Users's Guide and might not be correct.
 		 */
-		control |= M2M_CONTROL_NO_HDSK;
-		control |= M2M_CONTROL_RSS_IDE;
-		control |= M2M_CONTROL_PW_16;
 
 		if (data->direction == DMA_TO_DEVICE) {
 			/* Worst case from the UG */
@@ -473,6 +471,9 @@ static int m2m_hw_setup(struct ep93xx_dm
 			control |= M2M_CONTROL_SAH;
 			control |= M2M_CONTROL_TM_RX;
 		}
+		control |= M2M_CONTROL_NO_HDSK;
+		control |= M2M_CONTROL_RSS_IDE;
+		control |= M2M_CONTROL_PW_16;
 		break;
 
 	default:
@@ -668,24 +669,28 @@ static void ep93xx_dma_unmap_buffers(str
 static void ep93xx_dma_tasklet(unsigned long data)
 {
 	struct ep93xx_dma_chan *edmac = (struct ep93xx_dma_chan *)data;
-	struct ep93xx_dma_desc *desc, *d;
-	dma_async_tx_callback callback;
-	void *callback_param;
+	struct ep93xx_dma_desc *desc = NULL, *d;
+	dma_async_tx_callback callback = NULL;
+	void *callback_param = NULL;
 	LIST_HEAD(list);
 
 	spin_lock_irq(&edmac->lock);
-	desc = ep93xx_dma_get_active(edmac);
-	if (desc->complete) {
-		edmac->last_completed = desc->txd.cookie;
-		list_splice_init(&edmac->active, &list);
+	if (!list_empty(&edmac->active)) {
+		desc = ep93xx_dma_get_active(edmac);
+		if (desc->complete) {
+			edmac->last_completed = desc->txd.cookie;
+			list_splice_init(&edmac->active, &list);
+		}
 	}
 	spin_unlock_irq(&edmac->lock);
 
 	/* Pick up the next descriptor from the queue */
 	ep93xx_dma_advance_work(edmac);
 
-	callback = desc->txd.callback;
-	callback_param = desc->txd.callback_param;
+	if (desc) {
+		callback = desc->txd.callback;
+		callback_param = desc->txd.callback_param;
+	}
 
 	/* Now we can release all the chained descriptors */
 	list_for_each_entry_safe(desc, d, &list, node) {

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

* RE: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-11-15 15:02   ` Rafal Prylowski
@ 2011-11-15 17:59     ` H Hartley Sweeten
  2011-11-16  6:48       ` Mika Westerberg
  2011-11-16  9:00       ` Rafal Prylowski
  2011-11-16  6:55     ` Mika Westerberg
  1 sibling, 2 replies; 36+ messages in thread
From: H Hartley Sweeten @ 2011-11-15 17:59 UTC (permalink / raw)
  To: Rafal Prylowski, Mika Westerberg
  Cc: linux-arm-kernel, rmallon, vinod.koul, broonie, linux-kernel,
	grant.likely, dan.j.williams, lrg

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 3918 bytes --]

On Tuesday, November 15, 2011 8:02 AM, Rafal Prylowski wrote:
>
> Hello.
>
> During adaptation of my experimental ep93xx ide driver to the new dmaengine api,
> I discovered two issues with ep93xx dma implementation:

Nice to see someone is trying to get IDE support for the ep93xx into mainline!
Unfortunately none of my ep93xx hardware supports IDE... :-(

> 1) Control register is incorrectly programmed for IDE (m2m_hw_setup),
> probably copy-paste bug ;)
> 2) Kernel oops when trying to stop running transfers by calling
> dmaengine_terminate_all(...) - caused by dereferencing empty list
> in ep93xx_dma_get_active
>
> Following is a patch, which solves these problems for me.
>
> Regards,
> Rafal Prylowski
>
> Index: linux-2.6/drivers/dma/ep93xx_dma.c
> ===================================================================
> --- linux-2.6.orig/drivers/dma/ep93xx_dma.c
> +++ linux-2.6/drivers/dma/ep93xx_dma.c
> @@ -246,6 +246,7 @@ static void ep93xx_dma_set_active(struct
>  static struct ep93xx_dma_desc *
>  ep93xx_dma_get_active(struct ep93xx_dma_chan *edmac)
>  {
> +	BUG_ON(list_empty(&edmac->active));

BUG_ON is evil...

I'm not sure this is the right way to handle this.

If your doing dma the list should never be empty.  If it is I think it should
have been caught way before this.  Mika, do you have any comments?

> 	return list_first_entry(&edmac->active, struct ep93xx_dma_desc, node);
> }
> 
> @@ -459,9 +460,6 @@ static int m2m_hw_setup(struct ep93xx_dm
>  		 * This IDE part is totally untested. Values below are taken
>  		 * from the EP93xx Users's Guide and might not be correct.
>  		 */
> -		control |= M2M_CONTROL_NO_HDSK;
> -		control |= M2M_CONTROL_RSS_IDE;
> -		control |= M2M_CONTROL_PW_16;
>  
>  		if (data->direction == DMA_TO_DEVICE) {
>  			/* Worst case from the UG */
> @@ -473,6 +471,9 @@ static int m2m_hw_setup(struct ep93xx_dm
>  			control |= M2M_CONTROL_SAH;
>  			control |= M2M_CONTROL_TM_RX;
>  		}
> +		control |= M2M_CONTROL_NO_HDSK;
> +		control |= M2M_CONTROL_RSS_IDE;
> +		control |= M2M_CONTROL_PW_16;
>  		break;

This looks right.  The common OR'ed in bits should be added after setting the
direction bits.

>  	default:
> @@ -668,24 +669,28 @@ static void ep93xx_dma_unmap_buffers(str
>  static void ep93xx_dma_tasklet(unsigned long data)
>  {
>  	struct ep93xx_dma_chan *edmac = (struct ep93xx_dma_chan *)data;
> -	struct ep93xx_dma_desc *desc, *d;
> -	dma_async_tx_callback callback;
> -	void *callback_param;
> +	struct ep93xx_dma_desc *desc = NULL, *d;
> +	dma_async_tx_callback callback = NULL;
> +	void *callback_param = NULL;
>  	LIST_HEAD(list);
>  
>  	spin_lock_irq(&edmac->lock);
> -	desc = ep93xx_dma_get_active(edmac);
> -	if (desc->complete) {
> -		edmac->last_completed = desc->txd.cookie;
> -		list_splice_init(&edmac->active, &list);
> +	if (!list_empty(&edmac->active)) {
> +		desc = ep93xx_dma_get_active(edmac);
> +		if (desc->complete) {
> +			edmac->last_completed = desc->txd.cookie;
> +			list_splice_init(&edmac->active, &list);
> +		}

It looks like this might actually catch your BUG_ON issue above.

>  	}
>  	spin_unlock_irq(&edmac->lock);
>  
>  	/* Pick up the next descriptor from the queue */
>  	ep93xx_dma_advance_work(edmac);
>  
> -	callback = desc->txd.callback;
> -	callback_param = desc->txd.callback_param;
> +	if (desc) {
> +		callback = desc->txd.callback;
> +		callback_param = desc->txd.callback_param;
> +	}

These could be moved up to where 'desc' is getting set.  You have already
verified that the list is not empty and have a valid 'desc' pointer.  Set
the callback pointers there to remove this extra if (desc) test.

> 
>  	/* Now we can release all the chained descriptors */
>  	list_for_each_entry_safe(desc, d, &list, node) {

Thanks,
Hartley
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

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

* Re: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-11-15 17:59     ` H Hartley Sweeten
@ 2011-11-16  6:48       ` Mika Westerberg
  2011-11-16  9:00       ` Rafal Prylowski
  1 sibling, 0 replies; 36+ messages in thread
From: Mika Westerberg @ 2011-11-16  6:48 UTC (permalink / raw)
  To: H Hartley Sweeten
  Cc: Rafal Prylowski, linux-arm-kernel, rmallon, vinod.koul, broonie,
	linux-kernel, grant.likely, dan.j.williams, lrg

On Tue, Nov 15, 2011 at 11:59:50AM -0600, H Hartley Sweeten wrote:
> >
> > Index: linux-2.6/drivers/dma/ep93xx_dma.c
> > ===================================================================
> > --- linux-2.6.orig/drivers/dma/ep93xx_dma.c
> > +++ linux-2.6/drivers/dma/ep93xx_dma.c
> > @@ -246,6 +246,7 @@ static void ep93xx_dma_set_active(struct
> >  static struct ep93xx_dma_desc *
> >  ep93xx_dma_get_active(struct ep93xx_dma_chan *edmac)
> >  {
> > +	BUG_ON(list_empty(&edmac->active));
> 
> BUG_ON is evil...
> 
> I'm not sure this is the right way to handle this.
> 
> If your doing dma the list should never be empty.  If it is I think it should
> have been caught way before this.  Mika, do you have any comments?

I agree - the list should never be empty when this is called. The actual bug
is somewhere else.

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

* Re: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-11-15 15:02   ` Rafal Prylowski
  2011-11-15 17:59     ` H Hartley Sweeten
@ 2011-11-16  6:55     ` Mika Westerberg
  2011-11-16  8:31       ` Rafal Prylowski
  1 sibling, 1 reply; 36+ messages in thread
From: Mika Westerberg @ 2011-11-16  6:55 UTC (permalink / raw)
  To: Rafal Prylowski
  Cc: linux-arm-kernel, rmallon, vinod.koul, broonie, linux-kernel,
	grant.likely, hsweeten, dan.j.williams, lrg

On Tue, Nov 15, 2011 at 04:02:05PM +0100, Rafal Prylowski wrote:
> 
> 1) Control register is incorrectly programmed for IDE (m2m_hw_setup),
> probably copy-paste bug ;)

Yes, this is certainly a copy-paste bug or something. Thanks for fixing :)

Can you send a separate patch for this? Make sure you Cc Vinod Koul as he is
the maintainer of slave dma subsystem.

> 2) Kernel oops when trying to stop running transfers by calling
> dmaengine_terminate_all(...) - caused by dereferencing empty list
> in ep93xx_dma_get_active

Can you post that OOPS log? I would like to understand this problem a bit
better before we do any fixes for the driver.

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

* Re: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-11-16  6:55     ` Mika Westerberg
@ 2011-11-16  8:31       ` Rafal Prylowski
  2011-11-19 12:38         ` Mika Westerberg
  0 siblings, 1 reply; 36+ messages in thread
From: Rafal Prylowski @ 2011-11-16  8:31 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: linux-arm-kernel, rmallon, vinod.koul, broonie, linux-kernel,
	grant.likely, hsweeten, dan.j.williams, lrg

Hello.

W dniu 2011-11-16 07:55, Mika Westerberg pisze:
> Yes, this is certainly a copy-paste bug or something. Thanks for fixing :)
> 
> Can you send a separate patch for this? Make sure you Cc Vinod Koul as he is
> the maintainer of slave dma subsystem.

Here is a separate patch:

Index: linux-2.6/drivers/dma/ep93xx_dma.c
===================================================================
--- linux-2.6.orig/drivers/dma/ep93xx_dma.c
+++ linux-2.6/drivers/dma/ep93xx_dma.c
@@ -459,9 +459,6 @@ static int m2m_hw_setup(struct ep93xx_dm
 		 * This IDE part is totally untested. Values below are taken
 		 * from the EP93xx Users's Guide and might not be correct.
 		 */
-		control |= M2M_CONTROL_NO_HDSK;
-		control |= M2M_CONTROL_RSS_IDE;
-		control |= M2M_CONTROL_PW_16;
 
 		if (data->direction == DMA_TO_DEVICE) {
 			/* Worst case from the UG */
@@ -473,6 +470,9 @@ static int m2m_hw_setup(struct ep93xx_dm
 			control |= M2M_CONTROL_SAH;
 			control |= M2M_CONTROL_TM_RX;
 		}
+		control |= M2M_CONTROL_NO_HDSK;
+		control |= M2M_CONTROL_RSS_IDE;
+		control |= M2M_CONTROL_PW_16;
 		break;
 
 	default:

> 
>> 2) Kernel oops when trying to stop running transfers by calling
>> dmaengine_terminate_all(...) - caused by dereferencing empty list
>> in ep93xx_dma_get_active
> 
> Can you post that OOPS log? I would like to understand this problem a bit
> better before we do any fixes for the driver.
> 

OOPS log:

Linux version 3.2.0-rc1EP-1+ (prylowski@server) (gcc version 4.4.2 (GCC) ) #1008
 Wed Nov 16 08:25:45 CET 2011
CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177
CPU: VIVT data cache, VIVT instruction cache
Machine: Metasoft FonTel EP Board
Memory policy: ECC disabled, Data cache writeback
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 8128
Kernel command line: mtdparts=physmap-flash.0:128K(uboot),1152K(kernel),15104K(r
ootfs) root=/dev/mtdblock2 rootfstype=jffs2 console=ttyAM0 init=/sbin/init early
printk lpj=496128 panic=2
PID hash table entries: 128 (order: -3, 512 bytes)
Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
Memory: 32MB = 32MB total
Memory: 27452k/27452k available, 5316k reserved, 0K highmem
Virtual kernel memory layout:
    vector  : 0xffff0000 - 0xffff1000   (   4 kB)
    fixmap  : 0xfff00000 - 0xfffe0000   ( 896 kB)
    vmalloc : 0xc2800000 - 0xfe800000   ( 960 MB)
    lowmem  : 0xc0000000 - 0xc2000000   (  32 MB)
    modules : 0xbf000000 -0xc0000000   (  16 MB)
      .text : 0xc0008000 - 0xc029d000   (2644 kB)
      .init : 0xc029d000 - 0xc02b6000   ( 100 kB)
      .data : 0xc02b6000 - 0xc02d1440   ( 110 kB)
       .bss : 0xc02d1464 - 0xc02df51c   (  57 kB)
SLUB: Genslabs=13, HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
NR_IRQS:120
VIC @fefb0000: id 0x00041190, vendor 0x41
VIC @fefc0000: id 0x00041190, vendor 0x41
Console: colour dummy device 80x30
Calibrating delay loop (skipped) preset value.. 99.22 BogoMIPS (lpj=496128)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 512
CPU: Testing write buffer coherency: ok
NET: Registered protocol family 16
ep93xx clock: PLL1 running at 400 MHz, PLL2 at 192 MHz
ep93xx clock: FCLK 200 MHz, HCLK 100 MHz, PCLK 50 MHz
bio: create slab <bio-0> at 0
ep93xx-dma ep93xx-dma-m2p: EP93xx M2P DMA ready
ep93xx-dma ep93xx-dma-m2m: EP93xx M2M DMA ready
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
i2c-gpio i2c-gpio.0: using pins 49 (SDA) and 48 (SCL)
NET: Registered protocol family 2
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 1024 (order: 1, 8192 bytes)
TCP bind hash table entries: 1024 (order: 0, 4096 bytes)
TCP: Hash tables configured (established 1024 bind 1024)
TCP reno registered
UDP hashable entries: 256 (order: 0, 4096 bytes)
UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
NET: Registered protocol family 1
JFFS2 version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
msgmni has been set to 53
io scheduler noop registered
io scheduler deadline registered (default)
Serial: AMBA driver
apb:uart1: ttyAM0 at MMIO 0x808c0000 (irq = 52) is a AMBA
console [ttyAM0] enabled
apb:uart2: ttyAM1 at MMIO 0x808d0000 (irq = 54) is a AMBA
apb:uart3: ttyAM2 at MMIO 0x808e0000 (irq = 55) is a AMBA
fontel-pri fontel-pri: FonTel PRI initialized correctly.
ep93xx-ide ep93xx-ide: version 0.5
scsi0 : ep93xx-ide
ata1: PATA max UDMA/66 irq 40
physmap platform flash device: 01000000 at 60000000
physmap-flash.0: Found 1 x16 devices at 0x0 in 16-bit bank. Manufacturer ID 0x00
0001 Chip ID 0x002101
Amd/Fujitsu Extended Query Table at 0x0040
  Amd/Fujitsu Extended Query version 1.3.
number of CFI chips: 1
3 cmdlinepart partitions found on MTD device physmap-flash.0
Creating 3 MTD partitions on "physmap-flash.0":
0x000000000000-0x000000020000 : "uboot"
0x000000020000-0x000000140000 : "kernel"
0x000000140000-0x000001000000 : "rootfs"
ep93xx-spi ep93xx-spi.0: EP93xx SPI Controller at 0x808a0000 irq 53
ep93xx-eth version 0.1 loading
eth0: ep93xx on-chip ethernet, IRQ 39, 8e:42:5c:13:29:4c
ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
ep93xx-ohci ep93xx-ohci: EP93xx OHCI
ep93xx-ohci ep93xx-ohci: new USB bus registered, assigned bus number 1
ep93xx-ohci ep93xx-ohci: irq 56, io mem 0x80020000
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 3 ports detected
Initializing USB Mass Storage driver...
usbcore: registered new interface driver usb-storage
USB Mass Storage support registered.
usbcore: registered new interface driver fontel
rtc-ds1307 0-0068: rtc core: registered ds1307 as rtc0
rtc-ds1307 0-0068: 56 bytes nvram
ep93xx-rtc ep93xx-rtc: rtc core: registered ep93xx-rtc as rtc1
Software Watchdog Timer: 0.07 initialized. soft_noboot=0 soft_margin=60 sec soft
_panic=0 (nowayout= 0)
TCP cubic registered
NET: Registered protocol family 17
NET: Registered protocol family 15
ep93xx-rtc ep93xx-rtc: setting system clock to 1970-01-01 00:00:07 UTC (7)
ata1.00: ATA-8: WDC WD3200BEVE-00A0HT0, 11.01A11, max UDMA/100
ata1.00: 625142448 sectors, multi 0: LBA48
ata1.00: configured for UDMA/66
scsi 0:0:0:0: Direct-Access     ATA      WDC WD3200BEVE-0 11.0 PQ: 0 ANSI: 5
sd 0:0:0:0: [sda] 625142448 512-byte logical blocks: (320 GB/298 GiB)
sd 0:0:0:0: [sda] Write Protect is off
sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO
 or FUA
Internal error: Oops - undefined instruction: 0 [#1]
CPU: 0    Not tainted  (3.2.0-rc1EP-1+ #1008)
PC is at 0xc184c868
LR is at ep93xx_dma_tasklet+0xec/0x164
pc : [<c184c868>]    lr : [<c012b528>]    psr: 00000013
sp : c02b7e70  ip : ffffffff  fp : c02b7ea4
r10: 00000100  r9 : 80000013  r8 : c02b7e50
r7 : c02b7e70  r6 : c02b7ea4  r5 : 000000a4  r4 : c02b7e70
r3 : c02b751d  r2 : 8ae34598  r1 : c184c6e0  r0 : c02b7ea4
Flags: nzcv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment kernel
Control: c000717f  Table: c0004000  DAC: 00000017
Process swapper (pid: 0, stack limit = 0xc02b6270)
Stack: (0xc02b7e70 to 0xc02b8000)
7e60:                                     c02b7ea4 c02b7e70 c0008b64 c02bd5c4
7e80: c02d60e0 00000000 00000000 c02bd44c c02d60e0 00000100 c02b7ec4 c02b7ea8
7ea0: c001c49c c012b44c 00000018 00000001 c02d60e0 c02b6000 c02b7f04 c02b7ec8
7ec0: c001cbc0 c001c3e4 c02b7eec c02b7ed8 00000006 0000000a c02bf674 c02c458c
7ee0: 00000011 00000000 c02b7f7c c0004000 41129200 c02b0c80 c02b7f14 c02b7f08
7f00: c001cdd0 c001cb38 c02b7f34 c02b7f18 c000983c c001cd98 c0009a60 60000013
7f20: fefb0001 c02b7f7c c02b7f44 c02b7f38 c0008190 c0009810 c02b7f9c c02b7f48
7f40: c0008b64 c0008190 c02c2bf8 00000002 c02b7f90 60000013 c02b6000 c02d1504
7f60: c02baa88 c02baa80 c0004000 41129200 c02b0c80 c02b7f9c c02b7fa0 c02b7f90
7f80: c0009a54 c0009a60 60000013 ffffffff c02b7fbc c02b7fa0 c000a03c c0009a40
7fa0: c02b80b0 c02b19dc c02b19d8 c02baa80 c02b7fcc c02b7fc0 c02384e4 c0009fd4
7fc0: c02b7ff4 c02b7fd0 c029d924 c0238494 c029d49c 00000000 00000000 c02b19dc
7fe0: c0007175 c02b803c 00000000 c02b7ff8 c000803c c029d700 00000000 00000000
Backtrace:
[<c012b43c>] (ep93xx_dma_tasklet+0x0/0x164) from [<c001c49c>] (tasklet_action+0x
c8/0xdc)
[<c001c3d4>] (tasklet_action+0x0/0xdc) from [<c001cbc0>] (__do_softirq+0x98/0x15
4)
 r7:c02b6000 r6:c02d60e0 r5:00000001 r4:00000018
[<c001cb28>] (__do_softirq+0x0/0x154) from [<c001cdd0>] (irq_exit+0x48/0x50)
[<c001cd88>] (irq_exit+0x0/0x50) from [<c000983c>] (handle_IRQ+0x3c/0x8c)
[<c0009800>] (handle_IRQ+0x0/0x8c) from [<c0008190>] (asm_do_IRQ+0x10/0x14)
 r7:c02b7f7c r6:fefb0001 r5:60000013 r4:c0009a60
[<c0008180>] (asm_do_IRQ+0x0/0x14) from [<c0008b64>] (__irq_svc+0x24/0xc0)
Exception stack(0xc02b7f48 to 0xc02b7f90)
7f40:                   c02c2bf8 00000002 c02b7f90 60000013 c02b6000 c02d1504
7f60: c02baa88 c02baa80 c0004000 41129200 c02b0c80 c02b7f9c c02b7fa0 c02b7f90
7f80: c0009a54 c0009a60 60000013 ffffffff
[<c0009a30>] (default_idle+0x0/0x34) from [<c000a03c>] (cpu_idle+0x78/0xb0)
[<c0009fc4>] (cpu_idle+0x0/0xb0) from [<c02384e4>] (rest_init+0x60/0x78)
 r7:c02baa80 r6:c02b19d8 r5:c02b19dc r4:c02b80b0
[<c0238484>] (rest_init+0x0/0x78) from [<c029d924>] (start_kernel+0x234/0x278)
[<c029d6f0>] (start_kernel+0x0/0x278) from [<c000803c>] (0xc000803c)
 r5:c02b803c r4:c0007175
Code: 42555300 54535953 643d4d45 65766972 (53007372)
---[ end trace a494979f60859f42 ]---
Kernel panic - not syncing: Fatal exception in interrupt
Rebooting in 2 seconds..


I'm trying to call dmaengine_terminate_all from function assigned as libata .bmdma_stop.
Maybe this is incorrect, and this bug report is invalid...

Regards,
Rafal Prylowski.

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

* Re: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-11-15 17:59     ` H Hartley Sweeten
  2011-11-16  6:48       ` Mika Westerberg
@ 2011-11-16  9:00       ` Rafal Prylowski
  1 sibling, 0 replies; 36+ messages in thread
From: Rafal Prylowski @ 2011-11-16  9:00 UTC (permalink / raw)
  To: H Hartley Sweeten
  Cc: Mika Westerberg, linux-arm-kernel, rmallon, vinod.koul, broonie,
	linux-kernel, grant.likely, dan.j.williams, lrg

Hello.

> Nice to see someone is trying to get IDE support for the ep93xx into mainline!
> Unfortunately none of my ep93xx hardware supports IDE... :-(
> 

This driver is a result of work of several people, which I've seen on linux-ide
mailing list. I only added this dmaengine support. I really would like to
see it in mainline, but I think it's still not ready for inclusion.

>>  	default:
>> @@ -668,24 +669,28 @@ static void ep93xx_dma_unmap_buffers(str
>>  static void ep93xx_dma_tasklet(unsigned long data)
>>  {
>>  	struct ep93xx_dma_chan *edmac = (struct ep93xx_dma_chan *)data;
>> -	struct ep93xx_dma_desc *desc, *d;
>> -	dma_async_tx_callback callback;
>> -	void *callback_param;
>> +	struct ep93xx_dma_desc *desc = NULL, *d;
>> +	dma_async_tx_callback callback = NULL;
>> +	void *callback_param = NULL;
>>  	LIST_HEAD(list);
>>  
>>  	spin_lock_irq(&edmac->lock);
>> -	desc = ep93xx_dma_get_active(edmac);
>> -	if (desc->complete) {
>> -		edmac->last_completed = desc->txd.cookie;
>> -		list_splice_init(&edmac->active, &list);
>> +	if (!list_empty(&edmac->active)) {
>> +		desc = ep93xx_dma_get_active(edmac);
>> +		if (desc->complete) {
>> +			edmac->last_completed = desc->txd.cookie;
>> +			list_splice_init(&edmac->active, &list);
>> +		}
> 
> It looks like this might actually catch your BUG_ON issue above.

Yes, I only inserted BUG_ON in ep93xx_dma_get_active() to be sure, that
nowhere else in code happens similar problem.
But now I'm not so sure, that I encountered bug in ep93xx_dma.c, or
maybe I'm misusing dmaengine api (calling dmaengine_termiante_all from
invalid context?). I don't have enough knowledge to judge this.

> 
>>  	}
>>  	spin_unlock_irq(&edmac->lock);
>>  
>>  	/* Pick up the next descriptor from the queue */
>>  	ep93xx_dma_advance_work(edmac);
>>  
>> -	callback = desc->txd.callback;
>> -	callback_param = desc->txd.callback_param;
>> +	if (desc) {
>> +		callback = desc->txd.callback;
>> +		callback_param = desc->txd.callback_param;
>> +	}
> 
> These could be moved up to where 'desc' is getting set.  You have already
> verified that the list is not empty and have a valid 'desc' pointer.  Set
> the callback pointers there to remove this extra if (desc) test.
> 

Following is a patch with suggestions applied (patch addressing problem with
incorrect programming of control register is in another mail sent in reply to
Mika Westerberg).

Regards,
Rafal Prylowski.

Index: linux-2.6/drivers/dma/ep93xx_dma.c
===================================================================
--- linux-2.6.orig/drivers/dma/ep93xx_dma.c
+++ linux-2.6/drivers/dma/ep93xx_dma.c
@@ -669,24 +669,25 @@ static void ep93xx_dma_tasklet(unsigned 
 {
 	struct ep93xx_dma_chan *edmac = (struct ep93xx_dma_chan *)data;
 	struct ep93xx_dma_desc *desc, *d;
-	dma_async_tx_callback callback;
-	void *callback_param;
+	dma_async_tx_callback callback = NULL;
+	void *callback_param = NULL;
 	LIST_HEAD(list);
 
 	spin_lock_irq(&edmac->lock);
-	desc = ep93xx_dma_get_active(edmac);
-	if (desc->complete) {
-		edmac->last_completed = desc->txd.cookie;
-		list_splice_init(&edmac->active, &list);
+	if (!list_empty(&edmac->active)) {
+		desc = ep93xx_dma_get_active(edmac);
+		if (desc->complete) {
+			edmac->last_completed = desc->txd.cookie;
+			list_splice_init(&edmac->active, &list);
+		}
+		callback = desc->txd.callback;
+		callback_param = desc->txd.callback_param;
 	}
 	spin_unlock_irq(&edmac->lock);
 
 	/* Pick up the next descriptor from the queue */
 	ep93xx_dma_advance_work(edmac);
 
-	callback = desc->txd.callback;
-	callback_param = desc->txd.callback_param;
-
 	/* Now we can release all the chained descriptors */
 	list_for_each_entry_safe(desc, d, &list, node) {
 		/*


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

* Re: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-11-16  8:31       ` Rafal Prylowski
@ 2011-11-19 12:38         ` Mika Westerberg
  2011-11-21  7:44           ` Rafal Prylowski
  0 siblings, 1 reply; 36+ messages in thread
From: Mika Westerberg @ 2011-11-19 12:38 UTC (permalink / raw)
  To: Rafal Prylowski
  Cc: linux-arm-kernel, rmallon, vinod.koul, broonie, linux-kernel,
	grant.likely, hsweeten, dan.j.williams, lrg

Sorry for delay. I've been busy with other things.

On Wed, Nov 16, 2011 at 09:31:44AM +0100, Rafal Prylowski wrote:
> 
> sd 0:0:0:0: [sda] 625142448 512-byte logical blocks: (320 GB/298 GiB)
> sd 0:0:0:0: [sda] Write Protect is off
> sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO
>  or FUA
> Internal error: Oops - undefined instruction: 0 [#1]
> CPU: 0    Not tainted  (3.2.0-rc1EP-1+ #1008)
> PC is at 0xc184c868
> LR is at ep93xx_dma_tasklet+0xec/0x164
> pc : [<c184c868>]    lr : [<c012b528>]    psr: 00000013
> sp : c02b7e70  ip : ffffffff  fp : c02b7ea4
> r10: 00000100  r9 : 80000013  r8 : c02b7e50
> r7 : c02b7e70  r6 : c02b7ea4  r5 : 000000a4  r4 : c02b7e70
> r3 : c02b751d  r2 : 8ae34598  r1 : c184c6e0  r0 : c02b7ea4
> Flags: nzcv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment kernel
> Control: c000717f  Table: c0004000  DAC: 00000017
> Process swapper (pid: 0, stack limit = 0xc02b6270)
> Stack: (0xc02b7e70 to 0xc02b8000)
> 7e60:                                     c02b7ea4 c02b7e70 c0008b64 c02bd5c4
> 7e80: c02d60e0 00000000 00000000 c02bd44c c02d60e0 00000100 c02b7ec4 c02b7ea8
> 7ea0: c001c49c c012b44c 00000018 00000001 c02d60e0 c02b6000 c02b7f04 c02b7ec8
> 7ec0: c001cbc0 c001c3e4 c02b7eec c02b7ed8 00000006 0000000a c02bf674 c02c458c
> 7ee0: 00000011 00000000 c02b7f7c c0004000 41129200 c02b0c80 c02b7f14 c02b7f08
> 7f00: c001cdd0 c001cb38 c02b7f34 c02b7f18 c000983c c001cd98 c0009a60 60000013
> 7f20: fefb0001 c02b7f7c c02b7f44 c02b7f38 c0008190 c0009810 c02b7f9c c02b7f48
> 7f40: c0008b64 c0008190 c02c2bf8 00000002 c02b7f90 60000013 c02b6000 c02d1504
> 7f60: c02baa88 c02baa80 c0004000 41129200 c02b0c80 c02b7f9c c02b7fa0 c02b7f90
> 7f80: c0009a54 c0009a60 60000013 ffffffff c02b7fbc c02b7fa0 c000a03c c0009a40
> 7fa0: c02b80b0 c02b19dc c02b19d8 c02baa80 c02b7fcc c02b7fc0 c02384e4 c0009fd4
> 7fc0: c02b7ff4 c02b7fd0 c029d924 c0238494 c029d49c 00000000 00000000 c02b19dc
> 7fe0: c0007175 c02b803c 00000000 c02b7ff8 c000803c c029d700 00000000 00000000
> Backtrace:
> [<c012b43c>] (ep93xx_dma_tasklet+0x0/0x164) from [<c001c49c>] (tasklet_action+0x
> c8/0xdc)
> [<c001c3d4>] (tasklet_action+0x0/0xdc) from [<c001cbc0>] (__do_softirq+0x98/0x15
> 4)
>  r7:c02b6000 r6:c02d60e0 r5:00000001 r4:00000018
> [<c001cb28>] (__do_softirq+0x0/0x154) from [<c001cdd0>] (irq_exit+0x48/0x50)
> [<c001cd88>] (irq_exit+0x0/0x50) from [<c000983c>] (handle_IRQ+0x3c/0x8c)
> [<c0009800>] (handle_IRQ+0x0/0x8c) from [<c0008190>] (asm_do_IRQ+0x10/0x14)
>  r7:c02b7f7c r6:fefb0001 r5:60000013 r4:c0009a60
> [<c0008180>] (asm_do_IRQ+0x0/0x14) from [<c0008b64>] (__irq_svc+0x24/0xc0)
> Exception stack(0xc02b7f48 to 0xc02b7f90)
> 7f40:                   c02c2bf8 00000002 c02b7f90 60000013 c02b6000 c02d1504
> 7f60: c02baa88 c02baa80 c0004000 41129200 c02b0c80 c02b7f9c c02b7fa0 c02b7f90
> 7f80: c0009a54 c0009a60 60000013 ffffffff
> [<c0009a30>] (default_idle+0x0/0x34) from [<c000a03c>] (cpu_idle+0x78/0xb0)
> [<c0009fc4>] (cpu_idle+0x0/0xb0) from [<c02384e4>] (rest_init+0x60/0x78)
>  r7:c02baa80 r6:c02b19d8 r5:c02b19dc r4:c02b80b0
> [<c0238484>] (rest_init+0x0/0x78) from [<c029d924>] (start_kernel+0x234/0x278)
> [<c029d6f0>] (start_kernel+0x0/0x278) from [<c000803c>] (0xc000803c)
>  r5:c02b803c r4:c0007175
> Code: 42555300 54535953 643d4d45 65766972 (53007372)
> ---[ end trace a494979f60859f42 ]---

It looks like following scenario happened:

	1. DMA interrupt is asserted

	2. Interrupt is handled by ep93xx_dma_interrupt()

	3. It schedules the ep93xx_dma_tasklet() but for some reason the
	   tasklet is not immediately run when the interrupt handler returns.

	4. Your driver calls dma_terminate_all() making the active list empty
	   and disabling the channel & interrupt.

	5. ep93xx_dma_tasklet() is run and since the list is empty, it gets
	   pointer to some garbage which then OOPses as it tries to execute
	   ->complete() which points to memory containing:

	Code: 42555300 54535953 643d4d45 65766972 (53007372)

	translated to ascii:

	\0SUBSYSTEM=drivers\0S

	probably the memory was re-used by some uevent.

I wasn't able to reproduce this but can you try if following patch helps? I'm
not sure if it is correct but at least we get more information about the
problem.

diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
index 6181811..5b717da 100644
--- a/drivers/dma/ep93xx_dma.c
+++ b/drivers/dma/ep93xx_dma.c
@@ -1106,6 +1106,7 @@ static int ep93xx_dma_terminate_all(struct ep93xx_dma_chan *edmac)
 	spin_lock_irqsave(&edmac->lock, flags);
 	/* First we disable and flush the DMA channel */
 	edmac->edma->hw_shutdown(edmac);
+	tasklet_kill(&edmac->tasklet);
 	clear_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags);
 	list_splice_init(&edmac->active, &list);
 	list_splice_init(&edmac->queue, &list);

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

* Re: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-11-19 12:38         ` Mika Westerberg
@ 2011-11-21  7:44           ` Rafal Prylowski
  2011-11-21  8:01             ` Mika Westerberg
  0 siblings, 1 reply; 36+ messages in thread
From: Rafal Prylowski @ 2011-11-21  7:44 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: linux-arm-kernel, rmallon, vinod.koul, broonie, linux-kernel,
	grant.likely, hsweeten, dan.j.williams, lrg

Hello.

> I wasn't able to reproduce this but can you try if following patch helps? I'm
> not sure if it is correct but at least we get more information about the
> problem.
> 
> diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
> index 6181811..5b717da 100644
> --- a/drivers/dma/ep93xx_dma.c
> +++ b/drivers/dma/ep93xx_dma.c
> @@ -1106,6 +1106,7 @@ static int ep93xx_dma_terminate_all(struct ep93xx_dma_chan *edmac)
>  	spin_lock_irqsave(&edmac->lock, flags);
>  	/* First we disable and flush the DMA channel */
>  	edmac->edma->hw_shutdown(edmac);
> +	tasklet_kill(&edmac->tasklet);
>  	clear_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags);
>  	list_splice_init(&edmac->active, &list);
>  	list_splice_init(&edmac->queue, &list);
> 

It doesn't help:

Linux version 3.2.0-rc2EP-1+ (prylowski@server) (gcc version 4.4.2 (GCC) ) #1015
 Mon Nov 21 08:30:57 CET 2011
CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177
CPU: VIVT data cache, VIVT instruction cache
Machine: Metasoft FonTel EP Board
Memory policy: ECC disabled, Data cache writeback
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 8128
Kernel command line: mtdparts=physmap-flash.0:128K(uboot),1152K(kernel),15104K(r
ootfs) root=/dev/mtdblock2 rootfstype=jffs2 console=ttyAM0 init=/sbin/init early
printk lpj=496128 panic=2
PID hash table entries: 128 (order: -3, 512 bytes)
Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
Memory: 32MB = 32MB total
Memory: 27452k/27452k available, 5316k reserved, 0K highmem
Virtual kernel memory layout:
    vector  : 0xffff0000 - 0xffff1000   (   4 kB)
    fixmap  : 0xfff00000 - 0xfffe0000   ( 896 kB)
    vmalloc : 0xc2800000 - 0xfe800000   ( 960 MB)
    lowmem  : 0xc0000000 - 0xc2000000   (  32 MB)
    modules : 0xbf000000 - 0xc0000000   (  16 MB)
      .text : 0xc0008000 - 0xc029d000   (2644 kB)
      .init : 0xc029d000 - 0xc02b6000   ( 100 kB)
      .data : 0xc02b6000 - 0xc02d1440   ( 110 kB)
       .bss : 0xc02d1464 - 0xc02df51c   (  57 kB)
SLUB: Genslabs=13, HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
NR_IRQS:120
VIC @fefb0000: id 0x00041190, vendor 0x41
VIC @fefc0000: id 0x00041190, vendor 0x41
Console: colour dummy device 80x30
Calibrating delay loop (skipped) preset value.. 99.22 BogoMIPS (lpj=496128)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 512
CPU: Testing write buffer coherency: ok
NET: Registered protocol family 16
ep93xx clock: PLL1 running at 400 MHz, PLL2 at 192 MHz
ep93xx clock: FCLK 200 MHz, HCLK 100 MHz, PCLK 50 MHz
bio: create slab <bio-0> at 0
ep93xx-dma ep93xx-dma-m2p: EP93xx M2P DMA ready
ep93xx-dma ep93xx-dma-m2m: EP93xx M2M DMA ready
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
i2c-gpio i2c-gpio.0: using pins 49 (SDA) and 48 (SCL)
NET: Registered protocol family 2
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 1024 (order: 1, 8192 bytes)
TCP bind hash table entries: 1024 (order: 0, 4096 bytes)
TCP: Hash tables configured (established 1024 bind 1024)
TCP reno registered
UDP hash table entries: 256 (order: 0, 4096 bytes)
UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
NET: Registered protocol family 1
JFFS2 version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
msgmni has been set to 53
io scheduler noop registered
io scheduler deadline registered (default)
Serial: AMBA driver
apb:uart1: ttyAM0 at MMIO 0x808c0000 (irq = 52) is a AMBA
console [ttyAM0] enabled
apb:uart2: ttyAM1 at MMIO 0x808d0000 (irq = 54) is a AMBA
apb:uart3: ttyAM2 at MMIO 0x808e0000 (irq = 55) is a AMBA
fontel-pri fontel-pri: FonTel PRI initialized correctly.
ep93xx-ide ep93xx-ide: version 0.5
scsi0 : ep93xx-ide
ata1: PATA max UDMA/66 irq 40
physmap platform flash device: 01000000 at 60000000
physmap-flash.0: Found 1 x16 devices at 0x0 in 16-bit bank. Manufacturer ID 0x00
0001 Chip ID 0x002101
Amd/Fujitsu Extended Query Table at 0x0040
  Amd/Fujitsu Extended Query version 1.3.
number of CFI chips: 1
3 cmdlinepart partitions found on MTD device physmap-flash.0
Creating 3 MTD partitions on "physmap-flash.0":
0x000000000000-0x000000020000 : "uboot"
0x000000020000-0x000000140000 : "kernel"
0x000000140000-0x000001000000 : "rootfs"
ep93xx-spi ep93xx-spi.0: EP93xx SPI Controller at 0x808a0000 irq 53
ep93xx-eth version 0.1 loading
eth0: ep93xx on-chip ethernet, IRQ 39, aa:42:14:07:63:9d
ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
ep93xx-ohci ep93xx-ohci: EP93xx OHCI
ep93xx-ohci ep93xx-ohci: new USB bus registered, assigned bus number 1
ep93xx-ohci ep93xx-ohci: irq 56, io mem 0x80020000
ata1.00: HPA detected: current 234439535, native 234441648
ata1.00: ATA-7: ST9120822AS, 3.ALC, max UDMA/133
ata1.00: 234439535 sectors, multi 0: LBA48
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 3 ports detected
Initializing USB Mass Storage driver...
usbcore: registered new interface driver usb-storage
USB Mass Storage support registered.
ata1.00: configured for UDMA/66
scsi 0:0:0:0: Direct-Access     ATA      ST9120822AS      3.AL PQ: 0 ANSI: 5
usbcore: registered new interface driver fontel
sd 0:0:0:0: [sda] 234439535 512-byte logical blocks: (120 GB/111 GiB)
rtc-ds1307 0-0068: rtc core: registered ds1307 as rtc0
rtc-ds1307 0-0068: 56 bytes nvram
sd 0:0:0:0: [sda] Write Protect is off
sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO
 or FUA
ep93xx-rtc ep93xx-rtc: rtc core: registered ep93xx-rtc as rtc1
Software Watchdog Timer: 0.07 initialized. soft_noboot=0 soft_margin=60 sec soft
_panic=0 (nowayout= 0)
TCP cubic registered
NET: Registered protocol family 17
NET: Registered protocol family 15
ep93xx-rtc ep93xx-rtc: setting system clock to 1970-01-01 00:00:07 UTC (7)
Attempt to kill tasklet from interrupt
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = c0004000
[00000000] *pgd=00000000
Internal error: Oops: 80000005 [#1]
CPU: 0    Not tainted  (3.2.0-rc2EP-1+ #1015)
PC is at 0x0
LR is at sys_sched_yield+0x38/0x48
pc : [<00000000>]    lr : [<c0015030>]    psr: 20000093
sp : c02b7ce8  ip : c02b7cf8  fp : c02b7cf4
r10: c1918000  r9 : c19180a4  r8 : 00002118
r7 : 00000004  r6 : c02b7d20  r5 : c184d6bc  r4 : c184d6b8
r3 : c023e458  r2 : c02b7ce8  r1 : 00000001  r0 : c02bcff0
Flags: nzCv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment kernel
Control: c000717f  Table: c0004000  DAC: 00000017
Process swapper (pid: 0, stack limit = 0xc02b6270)
Stack: (0xc02b7ce8 to 0xc02b8000)
7ce0:                   c02b7d04 c02b7cf8 c023bdcc c0015008 c02b7d1c c02b7d08
7d00: c001ca04 c023bdb4 60000093 c184d680 c02b7d44 c02b7d20 c012ac8c c001c9d8
7d20: c02b7d20 c02b7d20 c02b7d44 c1914070 c19180a4 c2860000 c02b7d64 c02b7d48
7d40: c0177c50 c012ac54 c19180a4 c1918000 00000004 00000004 c02b7d8c c02b7d68
7d60: c0176a64 c0177c20 00000000 c1914190 00000000 00000001 0000000b c19180a4
7d80: c02b7dd4 c02b7d90 c0176cc0 c01769c4 00000001 40000093 00000000 00000000
7da0: c1914190 00000000 c1822000 c1912a80 00000028 00000000 00000000 00000028
7dc0: c02d8148 c02c0084 c02b7e0c c02b7dd8 c00426d0 c0176ae0 c02b7dfc c02b7de8
7de0: c0035628 c02c0084 00000028 c02b7f48 c02b7eb4 c02bd44c c02d60e0 c02b0c90
7e00: c02b7e24 c02b7e10 c004285c c004268c c02c0084 00000028 c02b7e3c c02b7e28
7e20: c0045178 c004283c c02c4578 00000028 c02b7e4c c02b7e40 c0042254 c00450fc
7e40: c02b7e6c c02b7e50 c0009838 c0042228 c001cb80 20000013 fefc0001 c02b7eb4
7e60: c02b7e7c c02b7e70 c0008190 c0009810 c02b7f04 c02b7e80 c0008b64 c0008190
7e80: 00000000 00000000 00000000 20000013 00000040 00000040 c02d60e0 c02b6000
7ea0: c02bd44c c02d60e0 c02b0c90 c02b7f04 c02b7f08 c02b7ec8 c001cdd0 c001cb80
7ec0: 20000013 ffffffff c02b7eec c02b7ed8 c004496c 0000000a c02bf674 c02c4578
7ee0: 00000011 00000000 c02b7f7c c0004000 41129200 c02b0c90 c02b7f14 c02b7f08
7f00: c001cdd0 c001cb38 c02b7f34 c02b7f18 c000983c c001cd98 c0009a60 60000013
7f20: fefb0001 c02b7f7c c02b7f44 c02b7f38 c0008190 c0009810 c02b7f9c c02b7f48
7f40: c0008b64 c0008190 c02c2bf8 00000002 c02b7f90 60000013 c02b6000 c02d1504
7f60: c02baa88 c02baa80 c0004000 41129200 c02b0c90 c02b7f9c c02b7fa0 c02b7f90
7f80: c0009a54 c0009a60 60000013 ffffffff c02b7fbc c02b7fa0 c000a03c c0009a40
7fa0: c02b80b0 c02b19ec c02b19e8 c02baa80 c02b7fcc c02b7fc0 c02384b4 c0009fd4
7fc0: c02b7ff4 c02b7fd0 c029d924 c0238464 c029d49c 00000000 00000000 c02b19ec
7fe0: c0007175 c02b803c 00000000 c02b7ff8 c000803c c029d700 00000000 00000000
Backtrace:
[<c0014ff8>] (sys_sched_yield+0x0/0x48) from [<c023bdcc>] (yield+0x28/0x2c)
[<c023bda4>] (yield+0x0/0x2c) from [<c001ca04>] (tasklet_kill+0x3c/0x9c)
[<c001c9c8>] (tasklet_kill+0x0/0x9c) from [<c012ac8c>] (ep93xx_dma_control+0x48/
0x1c0)
 r5:c184d680 r4:60000093
[<c012ac44>] (ep93xx_dma_control+0x0/0x1c0) from [<c0177c50>] (ep93xx_pata_dma_s
top+0x40/0xb8)
 r6:c2860000 r5:c19180a4 r4:c1914070
[<c0177c10>] (ep93xx_pata_dma_stop+0x0/0xb8) from [<c0176a64>] (ata_bmdma_port_i
ntr+0xb0/0x11c)
 r7:00000004 r6:00000004 r5:c1918000 r4:c19180a4
[<c01769b4>] (ata_bmdma_port_intr+0x0/0x11c) from [<c0176cc0>] (ata_bmdma_interr
upt+0x1f0/0x274)
 r9:c19180a4 r8:0000000b r7:00000001 r6:00000000 r5:c1914190
r4:00000000
[<c0176ad0>] (ata_bmdma_interrupt+0x0/0x274) from [<c00426d0>] (handle_irq_event
_percpu+0x54/0x1b0)
[<c004267c>] (handle_irq_event_percpu+0x0/0x1b0) from [<c004285c>] (handle_irq_e
vent+0x30/0x40)
[<c004282c>] (handle_irq_event+0x0/0x40) from [<c0045178>] (handle_level_irq+0x8
c/0xe8)
 r5:00000028 r4:c02c0084
[<c00450ec>] (handle_level_irq+0x0/0xe8) from [<c0042254>] (generic_handle_irq+0
x3c/0x48)
 r5:00000028 r4:c02c4578
[<c0042218>] (generic_handle_irq+0x0/0x48) from [<c0009838>] (handle_IRQ+0x38/0x
8c)
[<c0009800>] (handle_IRQ+0x0/0x8c) from [<c0008190>] (asm_do_IRQ+0x10/0x14)
 r7:c02b7eb4 r6:fefc0001 r5:20000013 r4:c001cb80
[<c0008180>] (asm_do_IRQ+0x0/0x14) from [<c0008b64>] (__irq_svc+0x24/0xc0)
Exception stack(0xc02b7e80 to 0xc02b7ec8)
7e80: 00000000 00000000 00000000 20000013 00000040 00000040 c02d60e0 c02b6000
7ea0: c02bd44c c02d60e0 c02b0c90 c02b7f04 c02b7f08 c02b7ec8 c001cdd0 c001cb80
7ec0: 20000013 ffffffff
[<c001cb28>] (__do_softirq+0x0/0x154) from [<c001cdd0>] (irq_exit+0x48/0x50)
[<c001cd88>] (irq_exit+0x0/0x50) from [<c000983c>] (handle_IRQ+0x3c/0x8c)
[<c0009800>] (handle_IRQ+0x0/0x8c) from [<c0008190>] (asm_do_IRQ+0x10/0x14)
 r7:c02b7f7c r6:fefb0001 r5:60000013 r4:c0009a60
[<c0008180>] (asm_do_IRQ+0x0/0x14) from [<c0008b64>] (__irq_svc+0x24/0xc0)
Exception stack(0xc02b7f48 to 0xc02b7f90)
7f40:                   c02c2bf8 00000002 c02b7f90 60000013 c02b6000 c02d1504
7f60: c02baa88 c02baa80 c0004000 41129200 c02b0c90 c02b7f9c c02b7fa0 c02b7f90
7f80: c0009a54 c0009a60 60000013 ffffffff
[<c0009a30>] (default_idle+0x0/0x34) from [<c000a03c>] (cpu_idle+0x78/0xb0)
[<c0009fc4>] (cpu_idle+0x0/0xb0) from [<c02384b4>] (rest_init+0x60/0x78)
 r7:c02baa80 r6:c02b19e8 r5:c02b19ec r4:c02b80b0
[<c0238454>] (rest_init+0x0/0x78) from [<c029d924>] (start_kernel+0x234/0x278)
[<c029d6f0>] (start_kernel+0x0/0x278) from [<c000803c>] (0xc000803c)
 r5:c02b803c r4:c0007175
Code: bad PC value
---[ end trace 3d4b7b18cc0879b4 ]---
Kernel panic - not syncing: Fatal exception in interrupt
Rebooting in 2 seconds..

I'm calling dma_terminate_all from interrupt. It seems that tasklet_kill is not
allowed to be called from this context.

Thanks,
Rafał Pryłowski.

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

* Re: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-11-21  7:44           ` Rafal Prylowski
@ 2011-11-21  8:01             ` Mika Westerberg
  2011-11-21  8:32               ` Vinod Koul
  2011-11-21  8:54               ` Rafal Prylowski
  0 siblings, 2 replies; 36+ messages in thread
From: Mika Westerberg @ 2011-11-21  8:01 UTC (permalink / raw)
  To: Rafal Prylowski
  Cc: linux-arm-kernel, rmallon, vinod.koul, broonie, linux-kernel,
	grant.likely, hsweeten, dan.j.williams, lrg

On Mon, Nov 21, 2011 at 08:44:07AM +0100, Rafal Prylowski wrote:
> 
> I'm calling dma_terminate_all from interrupt. It seems that tasklet_kill is not
> allowed to be called from this context.

Ah, right.

One more try - this time we set a flag which prevents the tasktlet from
referencing an empty list.

diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
index 6181811..f7244b3 100644
--- a/drivers/dma/ep93xx_dma.c
+++ b/drivers/dma/ep93xx_dma.c
@@ -155,6 +155,8 @@ struct ep93xx_dma_chan {
 	unsigned long			flags;
 /* Channel is configured for cyclic transfers */
 #define EP93XX_DMA_IS_CYCLIC		0
+/* Channel is enabled */
+#define EP93xx_DMA_IS_RUNNING		1
 
 	int				buffer;
 	dma_cookie_t			last_completed;
@@ -673,6 +675,9 @@ static void ep93xx_dma_tasklet(unsigned long data)
 	void *callback_param;
 	LIST_HEAD(list);
 
+	if (!test_bit(EP93XX_DMA_IS_RUNNING, &edmac->flags))
+		return;
+
 	spin_lock_irq(&edmac->lock);
 	desc = ep93xx_dma_get_active(edmac);
 	if (desc->complete) {
@@ -758,6 +763,8 @@ static dma_cookie_t ep93xx_dma_tx_submit(struct dma_async_tx_descriptor *tx)
 	edmac->chan.cookie = cookie;
 	desc->txd.cookie = cookie;
 
+	set_bit(EP93XX_DMA_IS_RUNNING, &edmac->flags);
+
 	/*
 	 * If nothing is currently prosessed, we push this descriptor
 	 * directly to the hardware. Otherwise we put the descriptor
@@ -1106,6 +1113,7 @@ static int ep93xx_dma_terminate_all(struct ep93xx_dma_chan *edmac)
 	spin_lock_irqsave(&edmac->lock, flags);
 	/* First we disable and flush the DMA channel */
 	edmac->edma->hw_shutdown(edmac);
+	clear_bit(EP93XX_DMA_IS_RUNNING, &edmac->flags);
 	clear_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags);
 	list_splice_init(&edmac->active, &list);
 	list_splice_init(&edmac->queue, &list);

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

* Re: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-11-21  8:01             ` Mika Westerberg
@ 2011-11-21  8:32               ` Vinod Koul
  2011-11-22  5:59                 ` Mika Westerberg
  2011-11-21  8:54               ` Rafal Prylowski
  1 sibling, 1 reply; 36+ messages in thread
From: Vinod Koul @ 2011-11-21  8:32 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Rafal Prylowski, rmallon, broonie, linux-kernel, grant.likely,
	hsweeten, dan.j.williams, lrg, linux-arm-kernel

On Mon, 2011-11-21 at 10:01 +0200, Mika Westerberg wrote:
> On Mon, Nov 21, 2011 at 08:44:07AM +0100, Rafal Prylowski wrote:
> > 
> > I'm calling dma_terminate_all from interrupt. It seems that tasklet_kill is not
> > allowed to be called from this context.
> 
> Ah, right.
> 
> One more try - this time we set a flag which prevents the tasktlet from
> referencing an empty list.
Rather than this why not actually check for list_empty and process only
when it is not empty?
Also you should check the return of the ep93xx_dma_get_active() which
should return NULL if it didn't find anything in active list
> 
> diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
> index 6181811..f7244b3 100644
> --- a/drivers/dma/ep93xx_dma.c
> +++ b/drivers/dma/ep93xx_dma.c
> @@ -155,6 +155,8 @@ struct ep93xx_dma_chan {
>  	unsigned long			flags;
>  /* Channel is configured for cyclic transfers */
>  #define EP93XX_DMA_IS_CYCLIC		0
> +/* Channel is enabled */
> +#define EP93xx_DMA_IS_RUNNING		1
>  
>  	int				buffer;
>  	dma_cookie_t			last_completed;
> @@ -673,6 +675,9 @@ static void ep93xx_dma_tasklet(unsigned long data)
>  	void *callback_param;
>  	LIST_HEAD(list);
>  
> +	if (!test_bit(EP93XX_DMA_IS_RUNNING, &edmac->flags))
> +		return;
> +
>  	spin_lock_irq(&edmac->lock);
>  	desc = ep93xx_dma_get_active(edmac);
>  	if (desc->complete) {
> @@ -758,6 +763,8 @@ static dma_cookie_t ep93xx_dma_tx_submit(struct dma_async_tx_descriptor *tx)
>  	edmac->chan.cookie = cookie;
>  	desc->txd.cookie = cookie;
>  
> +	set_bit(EP93XX_DMA_IS_RUNNING, &edmac->flags);
> +
>  	/*
>  	 * If nothing is currently prosessed, we push this descriptor
>  	 * directly to the hardware. Otherwise we put the descriptor
> @@ -1106,6 +1113,7 @@ static int ep93xx_dma_terminate_all(struct ep93xx_dma_chan *edmac)
>  	spin_lock_irqsave(&edmac->lock, flags);
>  	/* First we disable and flush the DMA channel */
>  	edmac->edma->hw_shutdown(edmac);
> +	clear_bit(EP93XX_DMA_IS_RUNNING, &edmac->flags);
>  	clear_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags);
>  	list_splice_init(&edmac->active, &list);
>  	list_splice_init(&edmac->queue, &list);
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel


-- 
~Vinod


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

* Re: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-11-21  8:01             ` Mika Westerberg
  2011-11-21  8:32               ` Vinod Koul
@ 2011-11-21  8:54               ` Rafal Prylowski
  2011-11-22  6:47                 ` Mika Westerberg
  1 sibling, 1 reply; 36+ messages in thread
From: Rafal Prylowski @ 2011-11-21  8:54 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: linux-arm-kernel, rmallon, vinod.koul, broonie, linux-kernel,
	grant.likely, hsweeten, dan.j.williams, lrg

>> I'm calling dma_terminate_all from interrupt. It seems that tasklet_kill is not
>> allowed to be called from this context.
> 
> Ah, right.
> 
> One more try - this time we set a flag which prevents the tasktlet from
> referencing an empty list.
> 
> diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
> index 6181811..f7244b3 100644
> --- a/drivers/dma/ep93xx_dma.c
> +++ b/drivers/dma/ep93xx_dma.c
> @@ -155,6 +155,8 @@ struct ep93xx_dma_chan {
>  	unsigned long			flags;
>  /* Channel is configured for cyclic transfers */
>  #define EP93XX_DMA_IS_CYCLIC		0
> +/* Channel is enabled */
> +#define EP93xx_DMA_IS_RUNNING		1
>  
>  	int				buffer;
>  	dma_cookie_t			last_completed;
> @@ -673,6 +675,9 @@ static void ep93xx_dma_tasklet(unsigned long data)
>  	void *callback_param;
>  	LIST_HEAD(list);
>  
> +	if (!test_bit(EP93XX_DMA_IS_RUNNING, &edmac->flags))
> +		return;
> +
>  	spin_lock_irq(&edmac->lock);
>  	desc = ep93xx_dma_get_active(edmac);
>  	if (desc->complete) {
> @@ -758,6 +763,8 @@ static dma_cookie_t ep93xx_dma_tx_submit(struct dma_async_tx_descriptor *tx)
>  	edmac->chan.cookie = cookie;
>  	desc->txd.cookie = cookie;
>  
> +	set_bit(EP93XX_DMA_IS_RUNNING, &edmac->flags);
> +
>  	/*
>  	 * If nothing is currently prosessed, we push this descriptor
>  	 * directly to the hardware. Otherwise we put the descriptor
> @@ -1106,6 +1113,7 @@ static int ep93xx_dma_terminate_all(struct ep93xx_dma_chan *edmac)
>  	spin_lock_irqsave(&edmac->lock, flags);
>  	/* First we disable and flush the DMA channel */
>  	edmac->edma->hw_shutdown(edmac);
> +	clear_bit(EP93XX_DMA_IS_RUNNING, &edmac->flags);
>  	clear_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags);
>  	list_splice_init(&edmac->active, &list);
>  	list_splice_init(&edmac->queue, &list);
> 

This patch works partially for me. Drive is detected correctly, I can do simple test with
hdparm, mount partitions, create file with 'touch' command, but when trying to fill
partition with data I get:

# dd if=/dev/zero of=/mnt/b/zero4 bs=4M
Internal error: Oops - undefined instruction: 0 [#1]
CPU: 0    Not tainted  (3.2.0-rc2EP-1+ #1017)
PC is at 0xc1862868
LR is at ep93xx_dma_tasklet+0xc4/0x174
pc : [<c1862868>]    lr : [<c012b498>]    psr: 00000013
sp : c10c3b60  ip : ffffffff  fp : c10c3b94
r10: 00000100  r9 : 80000013  r8 : c10c3b40
r7 : c10c3b60  r6 : c10c3b94  r5 : 00000094  r4 : c10c3b60
r3 : c10c320d  r2 : a0e96520  r1 : c1862758  r0 : c10c3b94
Flags: nzcv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: c000717f  Table: c1240000  DAC: 00000015
Process dd (pid: 619, stack limit = 0xc10c2270)
Stack: (0xc10c3b60 to 0xc10c4000)
3b60: c10c3b94 c10c3b60 00000001 c02bd5c4 c02d60e0 00000000 00000000 c02bd44c
3b80: c02d60e0 00000100 c10c3bb4 c10c3b98 c001c49c c012b3e4 00000018 00000001
3ba0: c02d60e0 c10c2000 c10c3bf4 c10c3bb8 c001cbc0 c001c3e4 c10c3bdc c10c3bc8
3bc0: 00000006 0000000a c02bf6e4 c02c4578 00000012 00000000 c10c3c6c 00000000
3be0: 00000000 00000000 c10c3c04 c10c3bf8 c001cdd0 c001cb38 c10c3c24 c10c3c08
3c00: c000983c c001cd98 c00e1160 80000013 fefb0001 c10c3c6c c10c3c34 c10c3c28
3c20: c0008190 c0009810 c10c3ccc c10c3c38 c0008b64 c0008190 c1a44058 00000003
3c40: ffffb58e ffffffc7 c156a000 c1a70060 c10c2000 c1a44000 00000000 00000000
3c60: 00000000 c10c3ccc c1a4404c c10c3c80 c0011a28 c00e1160 80000013 ffffffff
3c80: 00000000 00001000 c10c3cb4 c10c3c98 c00d9eac c00e0f88 c156a000 c1791250
3ca0: c156a000 00000000 c0242f4c c1a44600 03605000 00000000 00000000 00001000
3cc0: c10c3cec c10c3cd0 c00d9eac c00e0f88 c1791250 c02f9860 c156a000 03605000
3ce0: c10c3d2c c10c3cf0 c00d3d7c c00d9e94 00001000 00001000 c02f9860 c008df3c
3d00: c10c3d58 c0242d18 c10c2000 c1791308 00000000 c10c3d58 00001000 00204000
3d20: c10c3d9c c10c3d30 c0046f00 c00d3cbc 00001000 00001000 c02f9860 c008df3c
3d40: 00000000 00001000 c0242d18 c19e4000 03604000 00000000 c10c3f10 00000001
3d60: 00204000 001fc000 c008df3c c02f9860 00000073 c10c3ec8 c10c2000 c19e4000
3d80: 000001fe 00000000 c10c3f10 c1791308 c10c3e2c c10c3da0 c0049028 c0046e10
3da0: 03400000 00000000 c10c3ec8 00400000 00000000 c10c3dd0 c10c3dc4 00400000
3dc0: 03800000 00000000 00000001 c10c3ec8 c10c3e90 c02d8148 03400000 00000000
3de0: c10c3dec 00000001 c00443e8 00000001 c02d60e0 00000008 c02bf6e4 00400000
3e00: 00000000 c10c3e40 c17912ac c19e4000 03400000 c10c3e90 c10c3f10 00000001
3e20: c10c3e84 c10c3e30 c00492a4 c0048cf0 c10c3e3c c0045198 c0044954 00000000
3e40: 91827364 c10c3e44 c10c3e44 c10c3e4c c10c3e4c 00000000 c10c3e64 c10c3e90
3e60: c19e4000 c10c3f10 fffffdee c10c3f78 03400000 00000000 c10c3f44 c10c3e88
3e80: c0079114 c0049234 03400000 00000000 c0008190 40b90a78 00000000 00000001
3ea0: ffffffff c19e4000 00000000 00000000 00000000 00000000 c1aea000 c10c2000
3ec0: 00000000 00000000 03400000 00000000 c10c3f0c c10c3ee0 00400000 c0034478
3ee0: 00400000 c1a03658 00001000 40b95008 00001000 003ff000 c10c2000 c1818780
3f00: c19e4288 00000001 c01197e8 00000000 40796008 00400000 c01474b0 c19e4000
3f20: 00400000 40796008 c10c3f78 c0009124 c10c2000 00000000 c10c3f74 c10c3f48
3f40: c0079c7c c0079064 00000000 00000000 c0009124 c19e4000 03400000 00000000
3f60: 00000004 c0009124 c10c3fa4 c10c3f78 c0079df4 c0079bd0 03400000 00000000
3f80: 00000004 00000000 c10c2000 00400000 40796008 4004f7e0 00000000 c10c3fa8
3fa0: c0008fa0 c0079db8 00400000 40796008 00000001 40796008 00400000 000abfc4
3fc0: 00400000 40796008 4004f7e0 00000004 40796008 40796008 bedb5f34 00000000
3fe0: 00000001 bedb5ba0 0000ef78 40180dcc 60000010 00000001 00000000 00000000
Backtrace:
[<c012b3d4>] (ep93xx_dma_tasklet+0x0/0x174) from [<c001c49c>] (tasklet_action+0x
c8/0xdc)
[<c001c3d4>] (tasklet_action+0x0/0xdc) from [<c001cbc0>] (__do_softirq+0x98/0x15
4)
 r7:c10c2000 r6:c02d60e0 r5:00000001 r4:00000018
[<c001cb28>] (__do_softirq+0x0/0x154) from [<c001cdd0>] (irq_exit+0x48/0x50)
[<c001cd88>] (irq_exit+0x0/0x50) from [<c000983c>] (handle_IRQ+0x3c/0x8c)
[<c0009800>] (handle_IRQ+0x0/0x8c) from [<c0008190>] (asm_do_IRQ+0x10/0x14)
 r7:c10c3c6c r6:fefb0001 r5:80000013 r4:c00e1160
[<c0008180>] (asm_do_IRQ+0x0/0x14) from [<c0008b64>] (__irq_svc+0x24/0xc0)
Exception stack(0xc10c3c38 to 0xc10c3c80)
3c20:                                                       c1a44058 00000003
3c40: ffffb58e ffffffc7 c156a000 c1a70060 c10c2000 c1a44000 00000000 00000000
3c60: 00000000 c10c3ccc c1a4404c c10c3c80 c0011a28 c00e1160 80000013 ffffffff
[<c00e0f78>] (journal_stop+0x0/0x2bc) from [<c00d9eac>] (__ext3_journal_stop+0x2
8/0x58)
[<c00d9e84>] (__ext3_journal_stop+0x0/0x58) from [<c00d3d7c>] (ext3_writeback_wr
ite_end+0xd0/0x174)
 r7:03605000 r6:c156a000 r5:c02f9860 r4:c1791250
[<c00d3cac>] (ext3_writeback_write_end+0x0/0x174) from [<c0046f00>] (generic_fil
e_buffered_write+0x100/0x25c)
[<c0046e00>] (generic_file_buffered_write+0x0/0x25c) from [<c0049028>] (__generi
c_file_aio_write+0x348/0x544)
[<c0048ce0>] (__generic_file_aio_write+0x0/0x544) from [<c00492a4>] (generic_fil
e_aio_write+0x80/0xe8)
[<c0049224>] (generic_file_aio_write+0x0/0xe8) from [<c0079114>] (do_sync_write+
0xc0/0xf8)
[<c0079054>] (do_sync_write+0x0/0xf8) from [<c0079c7c>] (vfs_write+0xbc/0x150)
[<c0079bc0>] (vfs_write+0x0/0x150) from [<c0079df4>] (sys_write+0x4c/0x84)
 r8:c0009124 r7:00000004 r6:00000000 r5:03400000 r4:c19e4000
[<c0079da8>] (sys_write+0x0/0x84) from [<c0008fa0>] (ret_fast_syscall+0x0/0x2c)
 r6:4004f7e0 r5:40796008 r4:00400000
Code: 42555300 54535953 643d4d45 65766972 (53007372)
---[ end trace 8f90267a5628d8bf ]---
Kernel panic - not syncing: Fatal exception in interrupt

I don't get this exception when using my patch, posted in first email.

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

* Re: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-11-21  8:32               ` Vinod Koul
@ 2011-11-22  5:59                 ` Mika Westerberg
  2011-11-23 10:47                   ` Vinod Koul
  0 siblings, 1 reply; 36+ messages in thread
From: Mika Westerberg @ 2011-11-22  5:59 UTC (permalink / raw)
  To: Vinod Koul
  Cc: Rafal Prylowski, rmallon, broonie, linux-kernel, grant.likely,
	hsweeten, dan.j.williams, lrg, linux-arm-kernel

On Mon, Nov 21, 2011 at 02:02:06PM +0530, Vinod Koul wrote:
> On Mon, 2011-11-21 at 10:01 +0200, Mika Westerberg wrote:
> > On Mon, Nov 21, 2011 at 08:44:07AM +0100, Rafal Prylowski wrote:
> > > 
> > > I'm calling dma_terminate_all from interrupt. It seems that tasklet_kill is not
> > > allowed to be called from this context.
> > 
> > Ah, right.
> > 
> > One more try - this time we set a flag which prevents the tasktlet from
> > referencing an empty list.

> Rather than this why not actually check for list_empty and process only
> when it is not empty?

I was thinking that it should be an error if the active list is empty when the
tasklet is run.

> Also you should check the return of the ep93xx_dma_get_active() which
> should return NULL if it didn't find anything in active list

Right - if the list is allowed to be empty.

I'll re-check the patch from Rafal. Maybe it's better to adapt with the fact
that dma_terminate_all() can be called in any context any time and modify the
driver accordingly (e.g like you say, return NULL if the list is empty handle
it correctly).

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

* Re: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-11-21  8:54               ` Rafal Prylowski
@ 2011-11-22  6:47                 ` Mika Westerberg
  2011-11-22  8:45                   ` Rafal Prylowski
  0 siblings, 1 reply; 36+ messages in thread
From: Mika Westerberg @ 2011-11-22  6:47 UTC (permalink / raw)
  To: Rafal Prylowski
  Cc: linux-arm-kernel, rmallon, vinod.koul, broonie, linux-kernel,
	grant.likely, hsweeten, dan.j.williams, lrg

On Mon, Nov 21, 2011 at 09:54:29AM +0100, Rafal Prylowski wrote:
> 
> This patch works partially for me. Drive is detected correctly, I can do simple test with
> hdparm, mount partitions, create file with 'touch' command, but when trying to fill
> partition with data I get:
> 
> # dd if=/dev/zero of=/mnt/b/zero4 bs=4M
> Internal error: Oops - undefined instruction: 0 [#1]

Right. It still should call ep93xx_dma_advance_work(). Like the patch below.

Let me know if this works for you. Basically it's the patch from you except
that we use a flag to indicate if the channel is running or not.

I would like to keep it this way (given that it solves the crash you are
seeing, that is) to make sure that we can expect that the active list is never
empty when the channel is operating.

diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
index 6181811..2670005 100644
--- a/drivers/dma/ep93xx_dma.c
+++ b/drivers/dma/ep93xx_dma.c
@@ -155,6 +155,8 @@ struct ep93xx_dma_chan {
 	unsigned long			flags;
 /* Channel is configured for cyclic transfers */
 #define EP93XX_DMA_IS_CYCLIC		0
+/* Channel is enabled */
+#define EP93XX_DMA_IS_RUNNING		1
 
 	int				buffer;
 	dma_cookie_t			last_completed;
@@ -669,24 +671,25 @@ static void ep93xx_dma_tasklet(unsigned long data)
 {
 	struct ep93xx_dma_chan *edmac = (struct ep93xx_dma_chan *)data;
 	struct ep93xx_dma_desc *desc, *d;
-	dma_async_tx_callback callback;
-	void *callback_param;
+	dma_async_tx_callback callback = NULL;
+	void *callback_param = NULL;
 	LIST_HEAD(list);
 
 	spin_lock_irq(&edmac->lock);
-	desc = ep93xx_dma_get_active(edmac);
-	if (desc->complete) {
-		edmac->last_completed = desc->txd.cookie;
-		list_splice_init(&edmac->active, &list);
+	if (test_bit(EP93XX_DMA_IS_RUNNING, &edmac->flags)) {
+		desc = ep93xx_dma_get_active(edmac);
+		if (desc->complete) {
+			edmac->last_completed = desc->txd.cookie;
+			list_splice_init(&edmac->active, &list);
+		}
+		callback = desc->txd.callback;
+		callback_param = desc->txd.callback_param;
 	}
 	spin_unlock_irq(&edmac->lock);
 
 	/* Pick up the next descriptor from the queue */
 	ep93xx_dma_advance_work(edmac);
 
-	callback = desc->txd.callback;
-	callback_param = desc->txd.callback_param;
-
 	/* Now we can release all the chained descriptors */
 	list_for_each_entry_safe(desc, d, &list, node) {
 		/*
@@ -758,6 +761,8 @@ static dma_cookie_t ep93xx_dma_tx_submit(struct dma_async_tx_descriptor *tx)
 	edmac->chan.cookie = cookie;
 	desc->txd.cookie = cookie;
 
+	set_bit(EP93XX_DMA_IS_RUNNING, &edmac->flags);
+
 	/*
 	 * If nothing is currently prosessed, we push this descriptor
 	 * directly to the hardware. Otherwise we put the descriptor
@@ -1106,6 +1111,7 @@ static int ep93xx_dma_terminate_all(struct ep93xx_dma_chan *edmac)
 	spin_lock_irqsave(&edmac->lock, flags);
 	/* First we disable and flush the DMA channel */
 	edmac->edma->hw_shutdown(edmac);
+	clear_bit(EP93XX_DMA_IS_RUNNING, &edmac->flags);
 	clear_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags);
 	list_splice_init(&edmac->active, &list);
 	list_splice_init(&edmac->queue, &list);

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

* Re: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-11-22  6:47                 ` Mika Westerberg
@ 2011-11-22  8:45                   ` Rafal Prylowski
  0 siblings, 0 replies; 36+ messages in thread
From: Rafal Prylowski @ 2011-11-22  8:45 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: linux-arm-kernel, rmallon, vinod.koul, broonie, linux-kernel,
	grant.likely, hsweeten, dan.j.williams, lrg

On 2011-11-22 07:47, Mika Westerberg wrote:
> Let me know if this works for you. Basically it's the patch from you except
> that we use a flag to indicate if the channel is running or not.
> 

It works correctly. All of my simple hdd tests completed without any problems.
Thanks.

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

* Re: [PATCH v2 1/5] dmaengine: add ep93xx DMA support
  2011-11-22  5:59                 ` Mika Westerberg
@ 2011-11-23 10:47                   ` Vinod Koul
  0 siblings, 0 replies; 36+ messages in thread
From: Vinod Koul @ 2011-11-23 10:47 UTC (permalink / raw)
  To: Mika Westerberg
  Cc: Rafal Prylowski, rmallon, broonie, linux-kernel, grant.likely,
	hsweeten, dan.j.williams, lrg, linux-arm-kernel

On Tue, 2011-11-22 at 07:59 +0200, Mika Westerberg wrote:
> On Mon, Nov 21, 2011 at 02:02:06PM +0530, Vinod Koul wrote:
> > On Mon, 2011-11-21 at 10:01 +0200, Mika Westerberg wrote:
> > > On Mon, Nov 21, 2011 at 08:44:07AM +0100, Rafal Prylowski wrote:
> > > > 
> > > > I'm calling dma_terminate_all from interrupt. It seems that tasklet_kill is not
> > > > allowed to be called from this context.
> > > 
> > > Ah, right.
> > > 
> > > One more try - this time we set a flag which prevents the tasktlet from
> > > referencing an empty list.
> 
> > Rather than this why not actually check for list_empty and process only
> > when it is not empty?
> 
> I was thinking that it should be an error if the active list is empty when the
> tasklet is run.
well where would you return the error from tasklet?

> 
> > Also you should check the return of the ep93xx_dma_get_active() which
> > should return NULL if it didn't find anything in active list
> 
> Right - if the list is allowed to be empty.
> 
> I'll re-check the patch from Rafal. Maybe it's better to adapt with the fact
> that dma_terminate_all() can be called in any context any time and modify the
> driver accordingly (e.g like you say, return NULL if the list is empty handle
> it correctly).
Yes dma_terminate_all() is allowed to be called by the driver in any
context. It may wish to terminate at any point and this can potentially
lead to race condition when you do actual hardware right to terminate
just when hardware sent the interrupt. And your tasklet is scheduled
after the list becomes empty.
So its better approach to check for list in tasklet and return of
ep93xx_dma_get_active() for a valid descriptor


-- 
~Vinod


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

end of thread, other threads:[~2011-11-23 10:51 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-29 10:10 [PATCH v2 0/5] ep93xx DMA patches Mika Westerberg
2011-05-29 10:10 ` [PATCH v2 1/5] dmaengine: add ep93xx DMA support Mika Westerberg
2011-11-15 15:02   ` Rafal Prylowski
2011-11-15 17:59     ` H Hartley Sweeten
2011-11-16  6:48       ` Mika Westerberg
2011-11-16  9:00       ` Rafal Prylowski
2011-11-16  6:55     ` Mika Westerberg
2011-11-16  8:31       ` Rafal Prylowski
2011-11-19 12:38         ` Mika Westerberg
2011-11-21  7:44           ` Rafal Prylowski
2011-11-21  8:01             ` Mika Westerberg
2011-11-21  8:32               ` Vinod Koul
2011-11-22  5:59                 ` Mika Westerberg
2011-11-23 10:47                   ` Vinod Koul
2011-11-21  8:54               ` Rafal Prylowski
2011-11-22  6:47                 ` Mika Westerberg
2011-11-22  8:45                   ` Rafal Prylowski
2011-05-29 10:10 ` [PATCH v2 2/5] ep93xx: add dmaengine platform code Mika Westerberg
2011-05-29 10:10 ` [PATCH v2 3/5] ASoC: ep93xx: convert to use the DMA engine API Mika Westerberg
2011-05-29 10:10 ` [PATCH v2 4/5] ep93xx: remove the old M2P DMA code Mika Westerberg
2011-05-29 10:10 ` [PATCH v2 5/5] spi/ep93xx: add DMA support Mika Westerberg
2011-06-03 20:44   ` Grant Likely
2011-06-07 17:14     ` Mika Westerberg
2011-06-07 18:45       ` Grant Likely
2011-06-07 19:06         ` Mika Westerberg
2011-06-07 19:18           ` Grant Likely
2011-06-07 19:29             ` Mika Westerberg
2011-06-07 19:40               ` Grant Likely
2011-06-08  3:58                 ` Koul, Vinod
2011-06-08 21:53                   ` Grant Likely
2011-06-09 16:42                     ` Koul, Vinod
2011-06-09 18:46                       ` Grant Likely
2011-06-09 19:15                         ` Mika Westerberg
2011-06-05  8:19 ` [PATCH v2 0/5] ep93xx DMA patches Mika Westerberg
2011-06-06  6:39   ` Koul, Vinod
2011-06-06 16:42     ` Mika Westerberg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).