FW: [linux-cirrus] Fwd: Re: More about SD-Card problems

Ryan Mallon ryan at bluewatersys.com
Mon Jan 31 15:45:37 EST 2011


On 02/01/2011 05:45 AM, H Hartley Sweeten wrote:
> *From:* linux-cirrus-bounce at freelists.org
> [mailto:linux-cirrus-bounce at freelists.org] *On Behalf Of *Martin Guy
> *Sent:* Sunday, January 30, 2011 5:40 PM
> *To:* sim1 at googlegroups.com; linux-cirrus at freelists.org
> *Subject:* [linux-cirrus] Fwd: Re: More about SD-Card problems

The Cirrus mailing lists are effectively dead now. Can we please keep
EP93xx related discussions on the Linux ARM kernel list (Cc'ed).

> ---------- Messaggio inoltrato ----------
> Da: "Mika Westerberg" <mika.westerberg at iki.fi
> <mailto:mika.westerberg at iki.fi>>
> Data: 30/gen/2011 19.13
> Oggetto: Re: More about SD-Card problems
> A: "Martin Guy" <martinwguy at gmail.com <mailto:martinwguy at gmail.com>>
> 
> On Sat, Jan 29, 2011 at 01:15:24PM +0100, Martin Guy wrote:
>>
>> Just completed 3.3GB of data. Again, not a single error.
> 
> Sounds great! So basically we don't need to worry about the CRCs since it is
> extremely unlikely that we get corrupted data transfer.
> 
> ===
> 
> The M2M DMA patches are attached. Note that it is still in "hack" phase so
> error handling etc. are not finalized at all. M2M DMA currently only
> supports
> SPI but I'm going to add that memory-to-memory support and possibly IDE,
> let's
> see. It currently doesn't use double buffering but that is going to be
> added at
> some point.
> 
> I've been developing on .38-rc2 kernel but since these patches touch
> only ep93xx
> stuff I believe that they should apply pretty easily to .36.

It would be good to get these patches applied to mainline. Some comments
below. I also think we should look at integrating the EP93xx DMA into
the existing dmaengine framework if applicable. If not, is it at least
possible to reuse/combine some parts of the existing EP93xx M2P DMA code?

> Once you have applied the patches, you can enable the DMA support like:
> 
> diff --git a/arch/arm/mach-ep93xx/simone.c b/arch/arm/mach-ep93xx/simone.c
> index 0f44123..2d12f35 100644
> --- a/arch/arm/mach-ep93xx/simone.c
> +++ b/arch/arm/mach-ep93xx/simone.c
> @@ -161,6 +161,7 @@ static struct spi_board_info simone_spi_devices[]
> __initdata = {
> 
>  static struct ep93xx_spi_info simone_spi_info __initdata = {
>        .num_chipselect = ARRAY_SIZE(simone_spi_devices),
> +       .use_dma        = true,
>  };
> 
> After this, all the transfers should use DMA. I'm not sure if it is the
> best way
> since setting up the DMA channel for 1 byte transfer sounds like overkill. I
> think that we should probably use PIO for smaller transfers and DMA for the
> larger ones.
> 
> I have been testing this on Sim.One with mmc_spi and on TS-7260 attached
> to a
> SPI EEPROM (at25).
> 
> There are probably plenty of bugs lurking around so make sure that you have
> your data backed up ;-)
> 
> Regards,
> MW

> From 03681723ec6a203fcf9993fe4321bc2b065355e9 Mon Sep 17 00:00:00 2001
> From: Mika Westerberg <mika.westerberg at iki.fi>
> Date: Sun, 30 Jan 2011 11:21:34 +0200
> Subject: [PATCH 1/2] ep93xx: add memory-to-memory DMA support
> 
> This adds support for the 2 M2M DMA channels found in ep93xx chips.
> 
> Signed-off-by: Mika Westerberg <mika.westerberg at iki.fi>
> ---
>  arch/arm/mach-ep93xx/Makefile           |    2 +-
>  arch/arm/mach-ep93xx/dma-m2m.c          |  472 +++++++++++++++++++++++++++++++
>  arch/arm/mach-ep93xx/include/mach/dma.h |   57 ++++
>  3 files changed, 530 insertions(+), 1 deletions(-)
>  create mode 100644 arch/arm/mach-ep93xx/dma-m2m.c
> 
> diff --git a/arch/arm/mach-ep93xx/Makefile b/arch/arm/mach-ep93xx/Makefile
> index 33ee2c8..ea652c2 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 dma-m2p.o dma-m2m.o gpio.o
>  obj-m			:=
>  obj-n			:=
>  obj-			:=
> diff --git a/arch/arm/mach-ep93xx/dma-m2m.c b/arch/arm/mach-ep93xx/dma-m2m.c
> new file mode 100644
> index 0000000..fabbde1
> --- /dev/null
> +++ b/arch/arm/mach-ep93xx/dma-m2m.c
> @@ -0,0 +1,472 @@
> +/*
> + * arch/arm/mach-ep93xx/dma-m2m.c
> + *
> + * M2M DMA handling for Cirrus EP93xx chips.
> + *
> + * Copyright (C) 2011 Mika Westerberg
> + *
> + * Based on dma-m2p with following copyrights:
> + *   Copyright (C) 2006 Lennert Buytenhek <buytenh at wantstofly.org>
> + *   Copyright (C) 2006 Applied Data Systems
> + *   Copyright (C) 2009 Ryan Mallon <ryan at 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.
> + */
> +/*#define DEBUG*/
> +#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 M2M_CONTROL			0x0000
> +#define M2M_CONTROL_STALLINT		BIT(0)
> +#define M2M_CONTROL_SCT			BIT(1)
> +#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_TM_SHIFT		13
> +#define M2M_CONTROL_TM_MEMORY		(0 << M2M_CONTROL_TM_SHIFT)
> +#define M2M_CONTROL_TM_TX		(1 << M2M_CONTROL_TM_SHIFT)
> +#define M2M_CONTROL_TM_RX		(2 << M2M_CONTROL_TM_SHIFT)
> +#define M2M_CONTROL_NFBINT		BIT(21)
> +#define M2M_CONTROL_RSS_SHIFT		22
> +#define M2M_CONTROL_RSS_EXT_DREQ	(0 << M2M_CONTROL_RSS_SHIFT)
> +#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_STALLINT		BIT(0)
> +#define M2M_INTERRUPT_DONEINT		BIT(1)
> +#define M2M_INTERRUPT_NFBINT		BIT(2)
> +
> +#define M2M_STATUS			0x000c
> +#define M2M_STATUS_STALL		BIT(0)
> +#define M2M_STATUS_DONE			BIT(6)
> +#define M2M_STATUS_NFB			BIT(11)
> +
> +#define M2M_BCR0			0x0010
> +#define M2M_BCR1			0x0014
> +#define M2M_SAR_BASE0			0x0018
> +#define M2M_SAR_BASE1			0x001c
> +#define M2M_SAR_CURRENT0		0x0024
> +#define M2M_SAR_CURRENT1		0x0024
> +#define M2M_DAR_BASE0			0x002c
> +#define M2M_DAR_BASE1			0x0030
> +#define M2M_DAR_CURRENT0		0x0034
> +#define M2M_DAR_CURRENT1		0x003c
> +
> +/**
> + * struct m2m_channel - DMA Memory-to-memory channel information
> + */
> +struct m2m_channel {
> +	spinlock_t			lock;
> +	char				*name;
> +	void __iomem			*base;
> +	int				irq;
> +	u32				addr;
> +	struct clk			*clk;
> +	struct ep93xx_dma_m2m_client 	*client;
> +	struct ep93xx_dma_m2m_buffer	*buffer;
> +	struct list_head		buffers_pending;
> +};
> +
> +static struct m2m_channel m2m_channels[] = {
> +	{
> +		.name = "m2m0",
> +		.base = EP93XX_DMA_BASE + 0x0100,
> +		.irq 	= IRQ_EP93XX_DMAM2M0,
> +	},
> +	{
> +		.name = "m2m1",
> +		.base = EP93XX_DMA_BASE + 0x0140,
> +		.irq 	= IRQ_EP93XX_DMAM2M1,
> +	},
> +};
> +
> +#ifdef DEBUG
> +static void m2m_dump_channel(struct m2m_channel *ch, const char *msg)
> +{

This should ideally be done in debugfs. Possibly maintain statistics
about interrupts etc?

> +	int others;
> +	u32 v;
> +
> +	pr_debug("%s channel %s: <%s>\n",
> +		(ch->client->dir == DMA_TO_DEVICE) ? "TX" : "RX",
> +		ch->name, msg);
> +
> +	v = readl(ch->base + M2M_CONTROL);
> +	others = 0;
> +	pr_debug("  CONTROL     : %x [", v);
> +	if (v & M2M_CONTROL_STALLINT)
> +		pr_cont("%sSTALLINT", others++ ? "|" : "");
> +	if (v & M2M_CONTROL_SCT)
> +		pr_cont("%sSCT", others++ ? "|" : "");
> +	if (v & M2M_CONTROL_DONEINT)
> +		pr_cont("%sDONEINT", others++ ? "|" : "");
> +	if (v & M2M_CONTROL_DAH)
> +		pr_cont("%sDAH", others++ ? "|" : "");
> +	if (v & M2M_CONTROL_SAH)
> +		pr_cont("%sSAH", others++ ? "|" : "");
> +	if (v & M2M_CONTROL_NO_HDSK)
> +		pr_cont("%sNO_HDSK", others++ ? "|" : "");
> +	pr_cont("]\n");

Having others++ in each case is a bit ugly and error prone. I think it
is cleaner to do the increment once at the end of the block.

> +
> +	pr_debug("  INTERRUPT   : %x\n", readl(ch->base + M2M_INTERRUPT));
> +
> +	v = readl(ch->base + M2M_STATUS);
> +	others = 0;
> +	pr_debug("  STATUS      : %x [", v);
> +	if (v & M2M_STATUS_STALL)
> +		pr_cont("%sSTALL", others++ ? "|" : "");
> +	if (v & M2M_STATUS_DONE)
> +		pr_cont("%sDONE", others++ ? "|" : "");
> +	if (v & M2M_STATUS_NFB)
> +		pr_cont("%sNFB", others++ ? "|" : "");
> +	pr_cont("]\n");
> +
> +	pr_debug("  BCR0        : %d\n", readl(ch->base + M2M_BCR0));
> +	pr_debug("  SAR_BASE0   : %x\n", readl(ch->base + M2M_SAR_BASE0));
> +	pr_debug("  SAR_CURRENT0: %x\n", readl(ch->base + M2M_SAR_CURRENT0));
> +	pr_debug("  DAR_BASE0   : %x\n", readl(ch->base + M2M_DAR_BASE0));
> +	pr_debug("  DAR_CURRENT0: %x\n", readl(ch->base + M2M_DAR_CURRENT0));
> +}
> +#else
> +static inline void m2m_dump_channel(struct m2m_channel *ch, const char *msg)
> +{
> +}
> +#endif
> +
> +static inline void m2m_set_control(struct m2m_channel *ch, u32 v)
> +{
> +	/*
> +	 * There's a rule for M2P CONTROL register that it should be
> +	 * read immediately after being written. Altough not required

Typo 'Altough'.

> +	 * for M2M, we will do it anyway.
> +	 */
> +	writel(v, ch->base + M2M_CONTROL);
> +	readl(ch->base + M2M_CONTROL);
> +}
> +
> +static void m2m_feed_buf(struct m2m_channel *ch,
> +			 struct ep93xx_dma_m2m_buffer *buf)
> +{
> +	writel(buf->src_addr, ch->base + M2M_SAR_BASE0);
> +	writel(buf->dst_addr, ch->base + M2M_DAR_BASE0);
> +	writel(buf->size, ch->base + M2M_BCR0);
> +}
> +
> +static int m2m_channel_init(struct m2m_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 void m2m_channel_finish(struct m2m_channel *ch)
> +{
> +	if (!IS_ERR(ch->clk))
> +		clk_put(ch->clk);

Can we finish a channel without a clock? Surely we want to BUG or warn
in this case?

> +}
> +
> +static void m2m_channel_enable(struct m2m_channel *ch)
> +{
> +	u32 v = readl(ch->base + M2M_CONTROL);

Nitpick: Blank line between variable declarations and code.

> +	v |= M2M_CONTROL_ENABLE;
> +	m2m_set_control(ch, v);
> +}
> +
> +static void m2m_channel_disable(struct m2m_channel *ch)
> +{
> +	u32 v = readl(ch->base + M2M_CONTROL);
> +
> +	v &= ~(M2M_CONTROL_DONEINT | M2M_CONTROL_NFBINT);
> +	m2m_set_control(ch, v);
> +
> +	/* REVISIT should we wait here while the channel empties? */
> +
> +	v &= ~M2M_CONTROL_ENABLE;
> +	m2m_set_control(ch, v);
> +}
> +
> +static void m2m_channel_configure(struct m2m_channel *ch)
> +{
> +	struct ep93xx_dma_m2m_client *cl = ch->client;
> +	u32 control = readl(ch->base + M2M_CONTROL);
> +
> +	switch (cl->request) {
> +	case EP93XX_DMA_M2M_REQ_SSP:
> +		if (cl->dir == 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;
> +		}
> +
> +		control |= M2M_CONTROL_NO_HDSK;
> +		/*
> +		 * This is found via experimenting. Anything less than 5 causes
> +		 * the channel perform only a partial transfer which leads to
> +		 * problems since we don't get DONE int.
> +		 */
> +		control |= (5 << M2M_CONTROL_PWSC_SHIFT);
> +
> +		break;
> +
> +	default:
> +		/*
> +		 * TODO: implement rest of the M2M requests.
> +		 */
> +		BUG();

The SPI support is probably all we need for now anyway unless somebody
wants to implement DMA memcpy. We don't have an IDE driver so that
support is unneeded. I think this should just return EINVAL for the
request though rather than doing a BUG.

> +	}
> +
> +	m2m_set_control(ch, control);
> +}
> +
> +static irqreturn_t m2m_interrupt(int irq, void *dev_id)
> +{
> +	struct m2m_channel *ch = dev_id;
> +	struct ep93xx_dma_m2m_client *cl = ch->client;
> +	u32 irq_status;
> +
> +	spin_lock(&ch->lock);
> +
> +	m2m_dump_channel(ch, "interrupt");
> +
> +	irq_status = readl(ch->base + M2M_INTERRUPT);
> +	if (irq_status & M2M_INTERRUPT_DONEINT) {
> +		/* Clear the DONE interrupt */
> +		writel(0, ch->base + M2M_INTERRUPT);
> +
> +		m2m_channel_disable(ch);
> +
> +		cl->callback(cl->cookie);
> +		ch->buffer = NULL;
> +	}
> +
> +	spin_unlock(&ch->lock);
> +	return IRQ_HANDLED;

Should we return IRQ_NONE if we didn't do anything?

> +}
> +
> +int ep93xx_dma_m2m_client_register(struct ep93xx_dma_m2m_client *cl)
> +{
> +	struct m2m_channel *ch = NULL;
> +	int i, err;
> +
> +	switch (cl->request) {
> +	case EP93XX_DMA_M2M_REQ_MEMORY:
> +	case EP93XX_DMA_M2M_REQ_IDE:
> +	case EP93XX_DMA_M2M_REQ_SSP:
> +		for (i = 0; i < ARRAY_SIZE(m2m_channels); i++) {
> +			if (!m2m_channels[i].client) {
> +				ch = &m2m_channels[i];
> +				break;
> +			}
> +		}
> +		break;
> +
> +	/*
> +	 * External DREQs have predefined channels and we cannot
> +	 * configure otherwise.
> +	 */
> +	case EP93XX_DMA_M2M_REQ_EXT_DREQ0:
> +		ch = &m2m_channels[0];
> +		break;
> +
> +	case EP93XX_DMA_M2M_REQ_EXT_DREQ1:
> +		ch = &m2m_channels[1];
> +		break;
> +
> +	default:
> +		pr_err("invalid DMA channel request %d\n", cl->request);
> +		return -EINVAL;
> +	}
> +
> +	if (!ch || ch->client)
> +		return -EBUSY;
> +
> +	spin_lock_irq(&ch->lock);
> +	ch->client = cl;
> +	ch->buffer = NULL;
> +	INIT_LIST_HEAD(&ch->buffers_pending);
> +	spin_unlock_irq(&ch->lock);

Do we need to use spin_lock_irq here? We haven't yet registered an
interrupt handler for this channel.

> +
> +	cl->channel = ch;
> +
> +	err = request_irq(ch->irq, m2m_interrupt, 0, cl->name ? : "dma-m2m", ch);
> +	if (err)
> +		return err;

We should set ch->client/cl->channel to NULL here or the channel will
remain busy forever right?

> +
> +	err = clk_enable(ch->clk);
> +	if (err) {
> +		free_irq(ch->irq, ch);
> +		return err;

Same here. Set ch->client/cl->channel to NULL.

> +	}
> +
> +	m2m_set_control(ch, 0);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_client_register);
> +
> +void ep93xx_dma_m2m_client_unregister(struct ep93xx_dma_m2m_client *cl)
> +{
> +	struct m2m_channel *ch = cl->channel;
> +
> +	spin_lock_irq(&ch->lock);
> +	m2m_set_control(ch, 0);
> +	clk_disable(ch->clk);
> +	free_irq(ch->irq, ch);
> +	ch->client = NULL;
> +	spin_unlock_irq(&ch->lock);
> +
> +	cl->channel = NULL;
> +}
> +EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_client_unregister);
> +
> +int ep93xx_dma_m2m_config(struct ep93xx_dma_m2m_client *cl,
> +			  enum ep93xx_dma_m2m_parameter param,
> +			  unsigned value)
> +{
> +	struct m2m_channel *ch = cl->channel;
> +	unsigned long flags;
> +	int ret = 0;
> +	u32 control;
> +
> +	spin_lock_irqsave(&ch->lock, flags);
> +	if (ch->buffer) {
> +		spin_unlock_irqrestore(&ch->lock, flags);
> +		return -EBUSY;
> +	}
> +
> +	control = readl(ch->base + M2M_CONTROL);
> +
> +	switch (param) {
> +	case EP93XX_DMA_M2M_DAH:
> +		if (value)
> +			control |= M2M_CONTROL_DAH;
> +		else
> +			control &= ~M2M_CONTROL_DAH;
> +		break;
> +
> +	case EP93XX_DMA_M2M_SAH:
> +		if (value)
> +			control |= M2M_CONTROL_SAH;
> +		else
> +			control &= ~M2M_CONTROL_SAH;
> +		break;
> +
> +	case EP93XX_DMA_M2M_PW:
> +		if (cl->request == EP93XX_DMA_M2M_REQ_MEMORY) {
> +			ret = -EINVAL;
> +			break;
> +		}
> +
> +		control &= ~(M2M_CONTROL_PW_16 | M2M_CONTROL_PW_32);
> +
> +		switch (value) {
> +		case 8:
> +			break;
> +		case 16:
> +			control |= M2M_CONTROL_PW_16;
> +			break;
> +		case 32:
> +			control |= M2M_CONTROL_PW_32;
> +			break;
> +		default:
> +			ret = -EINVAL;
> +			break;
> +		}
> +
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	if (ret == 0)
> +		m2m_set_control(ch, control);
> +
> +	spin_unlock_irqrestore(&ch->lock, flags);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_config);
> +
> +void ep93xx_dma_m2m_submit(struct ep93xx_dma_m2m_client *cl,
> +			   struct ep93xx_dma_m2m_buffer *buf)
> +{
> +	struct m2m_channel *ch = cl->channel;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&ch->lock, flags);
> +	if (!ch->buffer) {
> +		u32 control;
> +
> +		m2m_channel_configure(ch);
> +
> +		ch->buffer = buf;
> +		m2m_feed_buf(ch, buf);
> +
> +		control = readl(ch->base + M2M_CONTROL);
> +		control |= M2M_CONTROL_DONEINT;
> +		m2m_set_control(ch, control);
> +
> +		m2m_dump_channel(ch, "submit");
> +
> +		m2m_channel_enable(ch);
> +	} else {
> +		list_add_tail(&buf->list, &ch->buffers_pending);
> +	}
> +	spin_unlock_irqrestore(&ch->lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(ep93xx_dma_m2m_submit);
> +
> +static int __init ep93xx_dma_m2m_init(void)
> +{
> +	int i, err;
> +
> +	for (i = 0; i < ARRAY_SIZE(m2m_channels); i++) {
> +		struct m2m_channel *ch = &m2m_channels[i];
> +
> +		err = m2m_channel_init(ch);
> +		if (err) {
> +			pr_err("failed to initialize channel %s\n",
> +				ch->name);
> +			goto fail;
> +		}
> +	}
> +
> +	pr_info("M2M DMA subsystem initialized\n");
> +	return 0;
> +
> +fail:
> +	for (--i; i >= 0; i--)
> +		m2m_channel_finish(&m2m_channels[i]);
> +
> +	return err;
> +}
> +arch_initcall(ep93xx_dma_m2m_init);
> diff --git a/arch/arm/mach-ep93xx/include/mach/dma.h b/arch/arm/mach-ep93xx/include/mach/dma.h
> index 5e31b2b..8e3f650 100644
> --- a/arch/arm/mach-ep93xx/include/mach/dma.h
> +++ b/arch/arm/mach-ep93xx/include/mach/dma.h
> @@ -15,6 +15,7 @@
>  
>  #include <linux/list.h>
>  #include <linux/types.h>
> +#include <linux/dma-mapping.h>
>  
>  /**
>   * struct ep93xx_dma_buffer - Information about a buffer to be transferred
> @@ -146,4 +147,60 @@ void ep93xx_dma_m2p_submit_recursive(struct ep93xx_dma_m2p_client *m2p,
>   */
>  void ep93xx_dma_m2p_flush(struct ep93xx_dma_m2p_client *m2p);
>  
> +/*
> + * M2M DMA supports five hardware requests: 2 for external
> + * peripherials that follow the handshake protocol and 3
> + * simple requests from IDE, SSPRx and SSPTx.
> + */
> +enum ep93xx_dma_m2m_request {
> +	EP93XX_DMA_M2M_REQ_MEMORY,
> +	EP93XX_DMA_M2M_REQ_IDE,
> +	EP93XX_DMA_M2M_REQ_SSP,
> +	EP93XX_DMA_M2M_REQ_EXT_DREQ0,
> +	EP93XX_DMA_M2M_REQ_EXT_DREQ1,
> +};
> +
> +/**
> + * Parameters that the client can configure.
> + */
> +enum ep93xx_dma_m2m_parameter {
> +	EP93XX_DMA_M2M_PW,
> +	EP93XX_DMA_M2M_SAH,
> +	EP93XX_DMA_M2M_DAH,
> +	EP93XX_DMA_M2M_SCT,
> +};
> +
> +struct ep93xx_dma_m2m_buffer {
> +	struct list_head	list;
> +	dma_addr_t		src_addr;
> +	dma_addr_t		dst_addr;
> +	size_t			size;
> +};
> +
> +/**
> + * struct ep93xx_dma_m2m_client - Information about a DMA M2M client
> + * @name: name for this client
> + * @request: one of the 5 requests supported by the DMA M2M
> + *           controller
> + * @dir: direction of the data flow
> + * @cookie: user data to pass to callback functions
> + * @callback: callback called when the buffer is finished
> + */
> +struct ep93xx_dma_m2m_client {
> +	const char 			*name;
> +	enum ep93xx_dma_m2m_request	request;
> +	enum dma_data_direction		dir;
> +	void 				*cookie;
> +	void				(*callback)(void *);
> +	void				*channel;
> +};
> +
> +int ep93xx_dma_m2m_client_register(struct ep93xx_dma_m2m_client *cl);
> +void ep93xx_dma_m2m_client_unregister(struct ep93xx_dma_m2m_client *cl);
> +int ep93xx_dma_m2m_config(struct ep93xx_dma_m2m_client *cl,
> +			  enum ep93xx_dma_m2m_parameter, unsigned value);
> +void ep93xx_dma_m2m_submit(struct ep93xx_dma_m2m_client *cl,
> +			   struct ep93xx_dma_m2m_buffer *buf);
> +void ep93xx_dma_m2m_flush(struct ep93xx_dma_m2m_client *cl);
> +
>  #endif /* __ASM_ARCH_DMA_H */
> -- 
> 1.7.2.3

Mostly this looks good. I would like to know if it can be integrated
with existing code though, either dmaengine or the EP93xx DMA M2P code.

Thanks,
~Ryan

-- 
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon         		5 Amuri Park, 404 Barbadoes St
ryan at bluewatersys.com         	PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com	New Zealand
Phone: +64 3 3779127		Freecall: Australia 1800 148 751
Fax:   +64 3 3779135			  USA 1800 261 2934



More information about the linux-arm-kernel mailing list