All of lore.kernel.org
 help / color / mirror / Atom feed
From: jy0922.shim@samsung.com (Joonyoung Shim)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 3/3] DMA: PL330: add PL330 DMA controller driver
Date: Wed, 16 Sep 2009 17:19:39 +0900	[thread overview]
Message-ID: <4AB09F9B.8030203@samsung.com> (raw)

The PL330 is the dma controller for the S5PC1XX arm SoC. This supports
DMA_MEMCPY and DMA_SLAVE.

The datasheet for the PL330 can find below url:
http://infocenter.arm.com/help/topic/com.arm.doc.ddi0424a/DDI0424A_dmac_pl330_r0p0_trm.pdf

Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com>
---
 drivers/dma/Kconfig      |    7 +
 drivers/dma/Makefile     |    1 +
 drivers/dma/pl330_dmac.c |  994 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/dma/pl330_dmac.h |  175 ++++++++
 4 files changed, 1177 insertions(+), 0 deletions(-)
 create mode 100644 drivers/dma/pl330_dmac.c
 create mode 100644 drivers/dma/pl330_dmac.h

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 81e1020..cbce4ed 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -97,6 +97,13 @@ config TXX9_DMAC
 	  Support the TXx9 SoC internal DMA controller.  This can be
 	  integrated in chips such as the Toshiba TX4927/38/39.
 
+config PL330_DMAC
+	bool "PrimeCell DMA Controller(PL330) support"
+	depends on ARCH_S5PC1XX
+	select DMA_ENGINE
+	help
+	  Enable support for the PrimeCell DMA Controller(PL330) support.
+
 config DMA_ENGINE
 	bool
 
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 40e1e00..ce6c232 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_DW_DMAC) += dw_dmac.o
 obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
 obj-$(CONFIG_MX3_IPU) += ipu/
 obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o
+obj-$(CONFIG_PL330_DMAC) += pl330_dmac.o
diff --git a/drivers/dma/pl330_dmac.c b/drivers/dma/pl330_dmac.c
new file mode 100644
index 0000000..4e67b09
--- /dev/null
+++ b/drivers/dma/pl330_dmac.c
@@ -0,0 +1,994 @@
+/*
+ * pl330_dmac.c  --  Driver for PL330 DMA Controller
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.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/init.h>
+#include <linux/module.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <plat/dma.h>
+
+#include "pl330_dmac.h"
+
+#define to_pl330_chan(chan)	container_of(chan, struct pl330_chan, common)
+#define to_pl330_desc(node)	container_of(node, struct pl330_desc, desc_node)
+#define tx_to_pl330_desc(tx)	container_of(tx, struct pl330_desc, async_tx)
+
+static unsigned int pl330_get_reg(struct pl330_device *pl330_dev,
+		unsigned int reg)
+{
+	void __iomem *base = pl330_dev->reg_base;
+
+	return readl(base + reg);
+}
+
+static void pl330_set_reg(struct pl330_device *pl330_dev, unsigned int reg,
+		unsigned int val)
+{
+	void __iomem *base = pl330_dev->reg_base;
+
+	writel(val, base + reg);
+}
+
+static void pl330_dump_regs(struct pl330_chan *pl330_ch)
+{
+	struct device *dev = pl330_ch->pl330_dev->common.dev;
+	unsigned int val;
+	unsigned int id = pl330_ch->id;
+
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_DS);
+	dev_dbg(dev, "PL330_DS:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_DPC);
+	dev_dbg(dev, "PL330_DPC:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_INTEN);
+	dev_dbg(dev, "PL330_INTEN:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_ES);
+	dev_dbg(dev, "PL330_ES:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_INTSTATUS);
+	dev_dbg(dev, "PL330_INTSTATUS:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_FSM);
+	dev_dbg(dev, "PL330_FSM:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_FSC);
+	dev_dbg(dev, "PL330_FSC:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_FTM);
+	dev_dbg(dev, "PL330_FTM:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_FTC(id));
+	dev_dbg(dev, "PL330_FTC(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CS(id));
+	dev_dbg(dev, "PL330_CS(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CPC(id));
+	dev_dbg(dev, "PL330_CPC(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_SA(id));
+	dev_dbg(dev, "PL330_SA(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_DA(id));
+	dev_dbg(dev, "PL330_DA(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CC(id));
+	dev_dbg(dev, "PL330_CC(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_LC0(id));
+	dev_dbg(dev, "PL330_LC0(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_LC1(id));
+	dev_dbg(dev, "PL330_LC1(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_DBGSTATUS);
+	dev_dbg(dev, "PL330_DBGSTATUS:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CR0);
+	dev_dbg(dev, "PL330_CR0:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CR1);
+	dev_dbg(dev, "PL330_CR1:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CR2);
+	dev_dbg(dev, "PL330_CR2:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CR3);
+	dev_dbg(dev, "PL330_CR3:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CR4);
+	dev_dbg(dev, "PL330_CR4:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CRDN);
+	dev_dbg(dev, "PL330_CRDN:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PERIPH_ID0);
+	dev_dbg(dev, "PL330_PERIPH_ID0:\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PERIPH_ID1);
+	dev_dbg(dev, "PL330_PERIPH_ID1:\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PERIPH_ID2);
+	dev_dbg(dev, "PL330_PERIPH_ID2:\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PERIPH_ID3);
+	dev_dbg(dev, "PL330_PERIPH_ID3:\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PCELL_ID0);
+	dev_dbg(dev, "PL330_PCELL_ID0:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PCELL_ID1);
+	dev_dbg(dev, "PL330_PCELL_ID0:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PCELL_ID2);
+	dev_dbg(dev, "PL330_PCELL_ID0:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PCELL_ID3);
+	dev_dbg(dev, "PL330_PCELL_ID0:\t\t0x%08x\n", val);
+}
+
+/* instruction set functions */
+static inline int pl330_dmaaddh(u8 *desc_pool_virt, u16 imm, bool ra)
+{
+	u8 opcode = DMAADDH | (ra << 1);
+
+	writeb(opcode, desc_pool_virt++);
+	writew(imm, desc_pool_virt);
+	return 3;
+}
+
+static inline int pl330_dmaend(u8 *desc_pool_virt)
+{
+	u8 opcode = DMAEND;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmaflushp(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMAFLUSHHP;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmald(u8 *desc_pool_virt)
+{
+	u8 opcode = DMALD;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmalds(u8 *desc_pool_virt)
+{
+	u8 opcode = DMALDS;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmaldb(u8 *desc_pool_virt)
+{
+	u8 opcode = DMALDB;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmaldps(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMALDPS;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmaldpb(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMALDPB;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmalp(u8 *desc_pool_virt, u8 iter, bool lc)
+{
+	u8 opcode = DMALP | (lc << 1);
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(iter, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmalpend(u8 *desc_pool_virt, u8 backwards_jump, bool lc)
+{
+	u8 opcode = DMALPEND | (lc << 2);
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(backwards_jump, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmalpends(u8 *desc_pool_virt, u8 backwards_jump,
+		bool lc)
+{
+	u8 opcode = DMALPENDS | (lc << 2);
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(backwards_jump, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmalpendb(u8 *desc_pool_virt, u8 backwards_jump,
+		bool lc)
+{
+	u8 opcode = DMALPENDB | (lc << 2);
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(backwards_jump, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmalpfe(u8 *desc_pool_virt, u8 backwards_jump, bool lc)
+{
+	u8 opcode = DMALPFE | (lc << 2);
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(backwards_jump, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmakill(u8 *desc_pool_virt)
+{
+	u8 opcode = DMAKILL;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmamov(u8 *desc_pool_virt, u8 rd, u32 imm)
+{
+	u8 opcode = DMAMOV;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(rd, desc_pool_virt++);
+	writel(imm, desc_pool_virt);
+	return 6;
+}
+
+static inline int pl330_dmanop(u8 *desc_pool_virt)
+{
+	u8 opcode = DMANOP;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmarmb(u8 *desc_pool_virt)
+{
+	u8 opcode = DMARMB;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmasev(u8 *desc_pool_virt, u8 event_num)
+{
+	u8 opcode = DMASEV;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(event_num << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmast(u8 *desc_pool_virt)
+{
+	u8 opcode = DMAST;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmasts(u8 *desc_pool_virt)
+{
+	u8 opcode = DMASTS;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmastb(u8 *desc_pool_virt)
+{
+	u8 opcode = DMASTB;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmastps(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMASTPS;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmastpb(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMASTPB;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmastz(u8 *desc_pool_virt)
+{
+	u8 opcode = DMASTZ;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmawfe(u8 *desc_pool_virt, u8 event_num, bool invalid)
+{
+	u8 opcode = DMAWFE;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb((event_num << 3) | (invalid << 1), desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmawfps(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMAWFPS;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmawfpb(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMAWFPB;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmawfpp(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMAWFPP;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmawmb(u8 *desc_pool_virt)
+{
+	u8 opcode = DMAWMB;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static void pl330_dmago(struct pl330_chan *pl330_ch, struct pl330_desc *desc,
+		bool ns)
+{
+	unsigned int val;
+	u8 opcode = DMAGO | (ns << 1);
+
+	val = (pl330_ch->id << 24) | (opcode << 16) | (pl330_ch->id << 8);
+	pl330_set_reg(pl330_ch->pl330_dev, PL330_DBGINST0, val);
+
+	val = desc->async_tx.phys;
+	pl330_set_reg(pl330_ch->pl330_dev, PL330_DBGINST1, val);
+
+	pl330_set_reg(pl330_ch->pl330_dev, PL330_DBGCMD, 0);
+}
+
+static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+	struct pl330_chan *pl330_ch = to_pl330_chan(tx->chan);
+	struct pl330_desc *desc = tx_to_pl330_desc(tx);
+	unsigned long flags;
+	dma_cookie_t cookie;
+
+	spin_lock_irqsave(&pl330_ch->lock, flags);
+
+	cookie = pl330_ch->common.cookie;
+
+	if (++cookie < 0)
+		cookie = 1;
+
+	desc->async_tx.cookie = cookie;
+	pl330_ch->common.cookie = cookie;
+
+	list_add_tail(&desc->desc_node, &pl330_ch->queue_desc);
+
+	spin_unlock_irqrestore(&pl330_ch->lock, flags);
+
+	return cookie;
+}
+
+static struct pl330_desc *
+pl330_alloc_descriptor(struct pl330_chan *pl330_ch, gfp_t flags)
+{
+	struct device *dev = pl330_ch->pl330_dev->common.dev;
+	struct pl330_desc *desc;
+	dma_addr_t phys;
+
+	desc = kzalloc(sizeof(*desc), flags);
+	if (!desc)
+		return NULL;
+
+	desc->desc_pool_virt = dma_alloc_coherent(dev, PL330_POOL_SIZE, &phys,
+			flags);
+	if (!desc->desc_pool_virt) {
+		kfree(desc);
+		return NULL;
+	}
+
+	dma_async_tx_descriptor_init(&desc->async_tx, &pl330_ch->common);
+	desc->async_tx.tx_submit = pl330_tx_submit;
+	desc->async_tx.phys = phys;
+
+	return desc;
+}
+
+static struct pl330_desc *pl330_get_descriptor(struct pl330_chan *pl330_ch)
+{
+	struct device *dev = pl330_ch->pl330_dev->common.dev;
+	struct pl330_desc *desc;
+
+	if (!list_empty(&pl330_ch->free_desc)) {
+		desc = to_pl330_desc(pl330_ch->free_desc.next);
+		list_del(&desc->desc_node);
+	} else {
+		/* try to get another desc */
+		desc = pl330_alloc_descriptor(pl330_ch, GFP_ATOMIC);
+		if (!desc) {
+			dev_err(dev, "descriptor alloc failed\n");
+			return NULL;
+		}
+	}
+
+	return desc;
+}
+
+static int pl330_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct pl330_chan *pl330_ch = to_pl330_chan(chan);
+	struct device *dev = pl330_ch->pl330_dev->common.dev;
+	struct pl330_desc *desc;
+	int i;
+	LIST_HEAD(tmp_list);
+
+	/* have we already been set up? */
+	if (!list_empty(&pl330_ch->free_desc))
+		return pl330_ch->desc_num;
+
+	for (i = 0; i < PL330_DESC_NUM; i++) {
+		desc = pl330_alloc_descriptor(pl330_ch, GFP_KERNEL);
+		if (!desc) {
+			dev_err(dev, "Only %d initial descriptors\n", i);
+			break;
+		}
+		list_add_tail(&desc->desc_node, &tmp_list);
+	}
+
+	pl330_ch->completed = chan->cookie = 1;
+	pl330_ch->desc_num = i;
+	list_splice(&tmp_list, &pl330_ch->free_desc);
+
+	return pl330_ch->desc_num;
+}
+
+static void pl330_free_chan_resources(struct dma_chan *chan)
+{
+	struct pl330_chan *pl330_ch = to_pl330_chan(chan);
+	struct device *dev = pl330_ch->pl330_dev->common.dev;
+	struct pl330_desc *desc, *_desc;
+
+	/* Before freeing channel resources first check
+	 * if they have been previously allocated for this channel.
+	 */
+	if (pl330_ch->desc_num == 0)
+		return;
+
+	list_for_each_entry_safe(desc, _desc, &pl330_ch->complete_desc,
+			desc_node) {
+		list_del(&desc->desc_node);
+		dma_free_coherent(dev, PL330_POOL_SIZE, desc->desc_pool_virt,
+				desc->async_tx.phys);
+		kfree(desc);
+	}
+	list_for_each_entry_safe(desc, _desc, &pl330_ch->queue_desc,
+			desc_node) {
+		list_del(&desc->desc_node);
+		dma_free_coherent(dev, PL330_POOL_SIZE, desc->desc_pool_virt,
+				desc->async_tx.phys);
+		kfree(desc);
+	}
+	list_for_each_entry_safe(desc, _desc, &pl330_ch->free_desc,
+			desc_node) {
+		list_del(&desc->desc_node);
+		dma_free_coherent(dev, PL330_POOL_SIZE, desc->desc_pool_virt,
+				desc->async_tx.phys);
+		kfree(desc);
+	}
+}
+
+static enum dma_status pl330_is_tx_complete(struct dma_chan *chan,
+		dma_cookie_t cookie, dma_cookie_t *done, dma_cookie_t *used)
+{
+	struct pl330_chan *pl330_ch = to_pl330_chan(chan);
+	dma_cookie_t last_used;
+	dma_cookie_t last_complete;
+	int ret;
+
+	last_complete = pl330_ch->completed;
+	last_used = chan->cookie;
+
+	ret = dma_async_is_complete(cookie, last_complete, last_used);
+
+	return ret;
+}
+
+static void pl330_issue_pending(struct dma_chan *chan)
+{
+	struct pl330_chan *pl330_ch = to_pl330_chan(chan);
+	struct pl330_desc *desc;
+	unsigned int val;
+
+	if (!list_empty(&pl330_ch->queue_desc)) {
+		val = pl330_get_reg(pl330_ch->pl330_dev, PL330_DBGSTATUS);
+		if (val == PL330_DBG_BUSY)
+			return;
+
+		desc = to_pl330_desc(pl330_ch->queue_desc.next);
+		list_move_tail(&desc->desc_node, &pl330_ch->complete_desc);
+
+		pl330_dmago(pl330_ch, desc, NS_NONSECURE);
+	}
+}
+
+static unsigned int pl330_make_instructions(struct pl330_chan *pl330_ch,
+		struct pl330_desc *desc, dma_addr_t dest, dma_addr_t src,
+		size_t len, unsigned int inst_size,
+		enum dma_data_direction direction)
+{
+	struct device *dev = pl330_ch->pl330_dev->common.dev;
+	void *buf = desc->desc_pool_virt;
+	u32 control = *(u32 *)&pl330_ch->pl330_reg_cc;
+	unsigned int loop_size = 0;
+	unsigned int loop_size_rest = 0;
+	unsigned int loop_count0 = 0;
+	unsigned int loop_count1 = 0;
+	unsigned int loop_count0_rest = 0;
+	unsigned int loop_start0 = 0;
+	unsigned int loop_start1 = 0;
+
+	dev_dbg(dev, "desc_pool_phys: 0x%x\n", desc->async_tx.phys);
+	dev_dbg(dev, "control: 0x%x\n", control);
+	dev_dbg(dev, "dest: 0x%x\n", dest);
+	dev_dbg(dev, "src: 0x%x\n", src);
+	dev_dbg(dev, "len: 0x%x\n", len);
+
+	/* calculate loop count */
+	loop_size = (pl330_ch->pl330_reg_cc.src_burst_len + 1) *
+		(1 << pl330_ch->pl330_reg_cc.src_burst_size);
+	dev_dbg(dev, "loop_size: 0x%x\n", loop_size);
+
+	loop_count0 = (len / loop_size) - 1;
+	loop_size_rest = len % loop_size;
+	dev_dbg(dev, "loop_count0: 0x%x\n", loop_count0);
+	dev_dbg(dev, "loop_size_rest: 0x%x\n", loop_size_rest);
+
+	if (loop_count0 >= PL330_MAX_LOOPS) {
+		loop_count1 = (loop_count0 / PL330_MAX_LOOPS) - 1;
+		loop_count0_rest = (loop_count0 % PL330_MAX_LOOPS) + 1;
+		loop_count0 = PL330_MAX_LOOPS - 1;
+		dev_dbg(dev, "loop_count0: 0x%x\n", loop_count0);
+		dev_dbg(dev, "loop_count0_rest: 0x%x\n", loop_count0_rest);
+		dev_dbg(dev, "loop_count1: 0x%x\n", loop_count1);
+
+		if (loop_count1 >= PL330_MAX_LOOPS)
+			dev_dbg(dev, "loop_count1 overflow\n");
+	}
+
+	/* write instruction sets on buffer */
+	inst_size += pl330_dmamov(buf + inst_size, RD_DAR, dest);
+	inst_size += pl330_dmamov(buf + inst_size, RD_SAR, src);
+	inst_size += pl330_dmamov(buf + inst_size, RD_CCR, control);
+
+	if (loop_count1) {
+		inst_size += pl330_dmalp(buf + inst_size, loop_count1, LC_1);
+		loop_start1 = inst_size;
+	}
+
+	if (loop_count0) {
+		inst_size += pl330_dmalp(buf + inst_size, loop_count0, LC_0);
+		loop_start0 = inst_size;
+	}
+
+	if (direction == DMA_TO_DEVICE) {
+		struct pl330_dma_slave *dma_slave = pl330_ch->common.private;
+		u8 periph = dma_slave->peri_num;
+		inst_size += pl330_dmawfps(buf + inst_size, periph);
+		inst_size += pl330_dmald(buf + inst_size);
+		inst_size += pl330_dmastps(buf + inst_size, periph);
+		inst_size += pl330_dmaflushp(buf + inst_size, periph);
+	} else if (direction == DMA_FROM_DEVICE) {
+		struct pl330_dma_slave *dma_slave = pl330_ch->common.private;
+		u8 periph = dma_slave->peri_num;
+		inst_size += pl330_dmawfps(buf + inst_size, periph);
+		inst_size += pl330_dmaldps(buf + inst_size, periph);
+		inst_size += pl330_dmast(buf + inst_size);
+		inst_size += pl330_dmaflushp(buf + inst_size, periph);
+	} else {
+		inst_size += pl330_dmald(buf + inst_size);
+		inst_size += pl330_dmarmb(buf + inst_size);
+		inst_size += pl330_dmast(buf + inst_size);
+		inst_size += pl330_dmawmb(buf + inst_size);
+	}
+
+	if (loop_count0) {
+		dev_dbg(dev, "inst_size - loop_start0: 0x%x\n",
+				inst_size - loop_start0);
+		inst_size += pl330_dmalpend(buf + inst_size,
+				inst_size - loop_start0, LC_0);
+	}
+
+	if (loop_count1) {
+		dev_dbg(dev, "inst_size - loop_start1: 0x%x\n",
+				inst_size - loop_start1);
+		inst_size += pl330_dmalpend(buf + inst_size,
+				inst_size - loop_start1, LC_1);
+	}
+
+	if (loop_count0_rest) {
+		inst_size += pl330_dmalp(buf + inst_size, loop_count0_rest - 1,
+				LC_0);
+		loop_start0 = inst_size;
+
+		if (direction == DMA_TO_DEVICE) {
+			struct pl330_dma_slave *dma_slave =
+				pl330_ch->common.private;
+			u8 periph = dma_slave->peri_num;
+			inst_size += pl330_dmawfps(buf + inst_size, periph);
+			inst_size += pl330_dmald(buf + inst_size);
+			inst_size += pl330_dmastps(buf + inst_size, periph);
+			inst_size += pl330_dmaflushp(buf + inst_size, periph);
+		} else if (direction == DMA_FROM_DEVICE) {
+			struct pl330_dma_slave *dma_slave =
+				pl330_ch->common.private;
+			u8 periph = dma_slave->peri_num;
+			inst_size += pl330_dmawfps(buf + inst_size, periph);
+			inst_size += pl330_dmaldps(buf + inst_size, periph);
+			inst_size += pl330_dmast(buf + inst_size);
+			inst_size += pl330_dmaflushp(buf + inst_size, periph);
+		} else {
+			inst_size += pl330_dmald(buf + inst_size);
+			inst_size += pl330_dmarmb(buf + inst_size);
+			inst_size += pl330_dmast(buf + inst_size);
+			inst_size += pl330_dmawmb(buf + inst_size);
+		}
+
+		dev_dbg(dev, "inst_size - loop_start0: 0x%x\n",
+				inst_size - loop_start0);
+		inst_size += pl330_dmalpend(buf + inst_size,
+				inst_size - loop_start0, LC_0);
+	}
+
+	inst_size += pl330_dmasev(buf + inst_size, pl330_ch->id);
+	inst_size += pl330_dmaend(buf + inst_size);
+
+	if (loop_size_rest)
+		dev_dbg(dev, "TODO\n");
+
+	return inst_size;
+}
+
+static struct dma_async_tx_descriptor *
+pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
+		size_t len, unsigned long flags)
+{
+	struct pl330_chan *pl330_ch = to_pl330_chan(chan);
+	struct pl330_desc *desc;
+
+	if (!chan || !len)
+		return NULL;
+
+	desc = pl330_get_descriptor(pl330_ch);
+	if (!desc)
+		return NULL;
+
+	pl330_make_instructions(pl330_ch, desc, dest, src, len, 0, DMA_NONE);
+
+	desc->async_tx.flags = flags;
+
+	return &desc->async_tx;
+}
+
+static struct dma_async_tx_descriptor *
+pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+		unsigned int sg_len, enum dma_data_direction direction,
+		unsigned long flags)
+{
+	struct pl330_chan *pl330_ch = to_pl330_chan(chan);
+	struct pl330_register_cc *pl330_reg_cc = &pl330_ch->pl330_reg_cc;
+	struct pl330_dma_slave *dma_slave = chan->private;
+	struct pl330_desc *desc;
+	struct scatterlist *sg;
+	unsigned int inst_size = 0;
+	unsigned int i;
+
+	BUG_ON(!dma_slave);
+	BUG_ON(direction == DMA_BIDIRECTIONAL);
+
+	if (!dma_slave->tx_reg)
+		BUG_ON(direction == DMA_TO_DEVICE);
+
+	if (!dma_slave->rx_reg)
+		BUG_ON(direction == DMA_FROM_DEVICE);
+
+	if (unlikely(!sg_len))
+		return NULL;
+
+	desc = pl330_get_descriptor(pl330_ch);
+	if (!desc)
+		return NULL;
+
+	for_each_sg(sgl, sg, sg_len, i) {
+		dma_addr_t dest;
+		dma_addr_t src;
+		unsigned int len = sg_dma_len(sg);
+
+		if (direction == DMA_TO_DEVICE) {
+			dest = dma_slave->tx_reg;
+			src = sg_dma_address(sg);
+			pl330_reg_cc->dst_inc = 0;
+		} else {
+			dest = sg_dma_address(sg);
+			src = dma_slave->rx_reg;
+			pl330_reg_cc->src_inc = 0;
+		}
+		pl330_reg_cc->src_burst_size = dma_slave->reg_width;
+		pl330_reg_cc->dst_burst_size = dma_slave->reg_width;
+
+		inst_size = pl330_make_instructions(pl330_ch, desc, dest, src,
+				len, inst_size, direction);
+	}
+
+	desc->async_tx.flags = flags;
+
+	return &desc->async_tx;
+}
+
+static void pl330_terminate_all(struct dma_chan *chan)
+{
+	/* TODO */
+}
+
+static void pl330_xfer_complete(struct pl330_chan *pl330_ch)
+{
+	struct pl330_desc *desc;
+	dma_async_tx_callback callback;
+	void *callback_param;
+
+	/* execute next desc */
+	pl330_issue_pending(&pl330_ch->common);
+
+	if (list_empty(&pl330_ch->complete_desc))
+		return;
+
+	desc = to_pl330_desc(pl330_ch->complete_desc.next);
+	list_move_tail(&desc->desc_node, &pl330_ch->free_desc);
+
+	pl330_ch->completed = desc->async_tx.cookie;
+
+	callback = desc->async_tx.callback;
+	callback_param = desc->async_tx.callback_param;
+	if (callback)
+		callback(callback_param);
+}
+
+static void pl330_ch_tasklet(unsigned long data)
+{
+	struct pl330_chan *pl330_ch = (struct pl330_chan *)data;
+	unsigned int val;
+
+	pl330_xfer_complete(pl330_ch);
+
+	/* enable channel interrupt */
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_INTEN);
+	val |= (1 << pl330_ch->id);
+	pl330_set_reg(pl330_ch->pl330_dev, PL330_INTEN, val);
+}
+
+static irqreturn_t pl330_irq_handler(int irq, void *data)
+{
+	struct pl330_device *pl330_dev = data;
+	struct pl330_chan *pl330_ch;
+	unsigned int intstatus;
+	unsigned int inten;
+	int i;
+
+	intstatus = pl330_get_reg(pl330_dev, PL330_INTSTATUS);
+
+	if (intstatus == 0)
+		return IRQ_HANDLED;
+
+	inten = pl330_get_reg(pl330_dev, PL330_INTEN);
+	for (i = 0; i < PL330_MAX_CHANS; i++) {
+		if (intstatus & (1 << i)) {
+			pl330_ch = &pl330_dev->pl330_ch[i];
+			pl330_set_reg(pl330_dev, PL330_INTCLR, 1 << i);
+
+			/* disable channel interrupt */
+			inten &= ~(1 << i);
+			pl330_set_reg(pl330_dev, PL330_INTEN, inten);
+
+			tasklet_schedule(&pl330_ch->tasklet);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void pl330_reg_cc_init(struct pl330_register_cc *pl330_reg_cc)
+{
+	pl330_reg_cc->src_inc = 0x1;
+	pl330_reg_cc->src_burst_size = 0;
+	pl330_reg_cc->src_burst_len = 0;
+	pl330_reg_cc->src_prot_ctrl = 0x2;
+	pl330_reg_cc->src_cache_ctrl = 0;
+	pl330_reg_cc->dst_inc = 0x1;
+	pl330_reg_cc->dst_burst_size = 0;
+	pl330_reg_cc->dst_burst_len = 0;
+	pl330_reg_cc->dst_prot_ctrl = 0x2;
+	pl330_reg_cc->dst_cache_ctrl = 0;
+	pl330_reg_cc->endian_swqp_size = 0;
+}
+
+static int pl330_probe(struct platform_device *pdev)
+{
+	struct pl330_device *pl330_dev;
+	struct resource *res;
+	struct dma_device *dma_dev;
+	struct pl330_platform_data *pdata = pdev->dev.platform_data;
+	int ret;
+	int irq;
+	int i;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "platform data is required!\n");
+		return -EINVAL;
+	}
+
+	pl330_dev = devm_kzalloc(&pdev->dev, sizeof(*pl330_dev), GFP_KERNEL);
+	if (!pl330_dev)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		goto err_alloc;
+	}
+
+	pl330_dev->reg_base = devm_ioremap(&pdev->dev, res->start,
+			res->end - res->start + 1);
+	if (!pl330_dev->reg_base) {
+		ret = -EBUSY;
+		goto err_alloc;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		ret = irq;
+		goto err_remap;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, pl330_irq_handler, 0,
+			dev_name(&pdev->dev), pl330_dev);
+	if (ret)
+		goto err_remap;
+
+	dma_dev = &pl330_dev->common;
+	INIT_LIST_HEAD(&dma_dev->channels);
+
+	/* set base routines */
+	dma_dev->device_alloc_chan_resources = pl330_alloc_chan_resources;
+	dma_dev->device_free_chan_resources = pl330_free_chan_resources;
+	dma_dev->device_is_tx_complete = pl330_is_tx_complete;
+	dma_dev->device_issue_pending = pl330_issue_pending;
+	dma_dev->device_terminate_all = pl330_terminate_all;
+	dma_dev->dev = &pdev->dev;
+	dma_dev->cap_mask = pdata->cap_mask;
+
+	/* set prep routines based on capability */
+	if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask))
+		dma_dev->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
+	if (dma_has_cap(DMA_SLAVE, dma_dev->cap_mask))
+		dma_dev->device_prep_slave_sg = pl330_prep_slave_sg;
+
+	for (i = 0; i < PL330_MAX_CHANS; i++) {
+		struct pl330_chan *pl330_ch = &pl330_dev->pl330_ch[i];
+		unsigned int val;
+
+		spin_lock_init(&pl330_ch->lock);
+		pl330_ch->id = i;
+		pl330_ch->pl330_dev = pl330_dev;
+		pl330_ch->common.device = dma_dev;
+		tasklet_init(&pl330_ch->tasklet, pl330_ch_tasklet,
+				(unsigned long)pl330_ch);
+		INIT_LIST_HEAD(&pl330_ch->free_desc);
+		INIT_LIST_HEAD(&pl330_ch->queue_desc);
+		INIT_LIST_HEAD(&pl330_ch->complete_desc);
+		list_add_tail(&pl330_ch->common.device_node,
+				&dma_dev->channels);
+		dma_dev->chancnt++;
+		pl330_reg_cc_init(&pl330_ch->pl330_reg_cc);
+		val = pl330_get_reg(pl330_ch->pl330_dev, PL330_INTEN);
+		val |= (1 << pl330_ch->id);
+		pl330_set_reg(pl330_ch->pl330_dev, PL330_INTEN, val);
+		pl330_dump_regs(pl330_ch);
+	}
+
+	platform_set_drvdata(pdev, pl330_dev);
+
+	dev_info(&pdev->dev, "PL330 DMA Controller: ( %s%s)\n",
+		dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "memcpy " : "",
+		dma_has_cap(DMA_SLAVE, dma_dev->cap_mask) ? "slave " : "");
+
+	dma_async_device_register(dma_dev);
+	return 0;
+
+err_remap:
+	iounmap(pl330_dev->reg_base);
+err_alloc:
+	devm_kfree(&pdev->dev, pl330_dev);
+
+	return ret;
+}
+
+static int pl330_remove(struct platform_device *pdev)
+{
+	struct pl330_device *pl330_dev = platform_get_drvdata(pdev);
+	struct pl330_chan *pl330_ch, *_pl330_ch;
+
+	dma_async_device_unregister(&pl330_dev->common);
+
+	list_for_each_entry_safe(pl330_ch, _pl330_ch,
+			&pl330_dev->common.channels, common.device_node) {
+		list_del(&pl330_ch->common.device_node);
+		tasklet_kill(&pl330_ch->tasklet);
+	}
+
+	iounmap(pl330_dev->reg_base);
+	devm_kfree(&pdev->dev, pl330_dev);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pl330_driver = {
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "pl330",
+	},
+	.probe		= pl330_probe,
+	.remove		= pl330_remove,
+};
+
+static int __init pl330_init(void)
+{
+	return platform_driver_register(&pl330_driver);
+}
+subsys_initcall(pl330_init);
+
+static void __exit pl330_exit(void)
+{
+	platform_driver_unregister(&pl330_driver);
+
+	return;
+}
+
+module_exit(pl330_exit);
+
+MODULE_DESCRIPTION("Driver for PL330 DMA Controller");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/pl330_dmac.h b/drivers/dma/pl330_dmac.h
new file mode 100644
index 0000000..d2cbd4e
--- /dev/null
+++ b/drivers/dma/pl330_dmac.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.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.
+ *
+ */
+
+#ifndef __PL330_DMAC_H
+#define __PL330_DMAC_H
+
+#define PL330_MAX_CHANS		8
+#define PL330_MAX_LOOPS		256
+#define PL330_POOL_SIZE		SZ_256
+#define PL330_DESC_NUM		8
+
+/* registers */
+#define PL330_DS		0x00
+#define PL330_DPC		0x04
+#define PL330_INTEN		0x20			/* R/W */
+#define PL330_ES		0x24
+#define PL330_INTSTATUS		0x28
+#define PL330_INTCLR		0x2c			/* W/O */
+#define PL330_FSM		0x30
+#define PL330_FSC		0x34
+#define PL330_FTM		0x38
+#define PL330_FTC(ch)		(0x40 + (ch << 2))
+#define PL330_CS(ch)		(0x100 + (ch << 3))
+#define PL330_CPC(ch)		(0x104 + (ch << 3))
+#define PL330_SA(ch)		(0x400 + (ch << 5))
+#define PL330_DA(ch)		(0x404 + (ch << 5))
+#define PL330_CC(ch)		(0x408 + (ch << 5))
+#define PL330_LC0(ch)		(0x40c + (ch << 5))
+#define PL330_LC1(ch)		(0x410 + (ch << 5))
+#define PL330_DBGSTATUS		0xd00
+#define PL330_DBGCMD		0xd04			/* W/O */
+#define PL330_DBGINST0		0xd08			/* W/O */
+#define PL330_DBGINST1		0xd0c			/* W/O */
+#define PL330_CR0		0xe00
+#define PL330_CR1		0xe04
+#define PL330_CR2		0xe08
+#define PL330_CR3		0xe0c
+#define PL330_CR4		0xe10
+#define PL330_CRDN		0xe14
+#define PL330_PERIPH_ID0	0xfe0
+#define PL330_PERIPH_ID1	0xfe4
+#define PL330_PERIPH_ID2	0xfe8
+#define PL330_PERIPH_ID3	0xfec
+#define PL330_PCELL_ID0		0xff0
+#define PL330_PCELL_ID1		0xff4
+#define PL330_PCELL_ID2		0xff8
+#define PL330_PCELL_ID3		0xffc
+
+/* PL330_CC */
+#define PL330_SRC_INC			(1 << 0)
+#define PL330_SRC_BSIZE_1BYTE		(1 << 1)
+#define PL330_SRC_BSIZE_2BYTE		(2 << 1)
+#define PL330_SRC_BSIZE_4BYTE		(3 << 1)
+#define PL330_SRC_BSIZE_16BYTE		(4 << 1)
+#define PL330_SRC_BSIZE_32BYTE		(5 << 1)
+#define PL330_SRC_BSIZE_64BYTE		(6 << 1)
+#define PL330_SRC_BSIZE_128BYTE		(7 << 1)
+#define PL330_SRC_BLEN(n)		((n - 1) << 4)
+#define PL330_DEST_INC			(1 << 14)
+#define PL330_DEST_BSIZE_1BYTE		(1 << 15)
+#define PL330_DEST_BSIZE_2BYTE		(2 << 15)
+#define PL330_DEST_BSIZE_4BYTE		(3 << 15)
+#define PL330_DEST_BSIZE_16BYTE		(4 << 15)
+#define PL330_DEST_BSIZE_32BYTE		(5 << 15)
+#define PL330_DEST_BSIZE_64BYTE		(6 << 15)
+#define PL330_DEST_BSIZE_128BYTE	(7 << 15)
+#define PL330_DEST_BLEN(n)		((n - 18) << 4)
+
+/* PL330_DBGSTATUS */
+#define PL330_DBG_IDLE		0
+#define PL330_DBG_BUSY		1
+
+/* instruction set opcode */
+#define DMAADDH			(0x54)
+#define DMAEND			(0x00)
+#define DMAFLUSHHP		(0x35)
+#define DMAGO			(0xa0)
+#define DMALD			(0x04)
+#define DMALDS			(0x05)
+#define DMALDB			(0x07)
+#define DMALDPS			(0x25)
+#define DMALDPB			(0x27)
+#define DMALP			(0x20)
+#define DMALPEND		(0x38)
+#define DMALPENDS		(0x39)
+#define DMALPENDB		(0x3b)
+#define DMALPFE			(0x28)
+#define DMAKILL			(0x01)
+#define DMAMOV			(0xbc)
+#define DMANOP			(0xbc)
+#define DMARMB			(0x12)
+#define DMASEV			(0x34)
+#define DMAST			(0x08)
+#define DMASTS			(0x09)
+#define DMASTB			(0x0b)
+#define DMASTPS			(0x29)
+#define DMASTPB			(0x2b)
+#define DMASTZ			(0x0c)
+#define DMAWFE			(0x36)
+#define DMAWFPS			(0x30)
+#define DMAWFPB			(0x32)
+#define DMAWFPP			(0x31)
+#define DMAWMB			(0x13)
+
+/* ra DMAADDH */
+#define RA_SA			0
+#define RA_DA			1
+
+/* ns DMAGO */
+#define NS_SECURE		0
+#define NS_NONSECURE		1
+
+/* lc DMALP* */
+#define LC_0			0
+#define LC_1			1
+
+/* rd DMAMOV */
+#define RD_SAR			0
+#define RD_CCR			1
+#define RD_DAR			2
+
+/* invalid DMAWFE */
+#define INVALID_OFF		0
+#define INVALID_ON		1
+
+/* struct for PL330_CC Register */
+struct pl330_register_cc {
+	unsigned int src_inc:1;
+	unsigned int src_burst_size:3;
+	unsigned int src_burst_len:4;
+	unsigned int src_prot_ctrl:3;
+	unsigned int src_cache_ctrl:3;
+	unsigned int dst_inc:1;
+	unsigned int dst_burst_size:3;
+	unsigned int dst_burst_len:4;
+	unsigned int dst_prot_ctrl:3;
+	unsigned int dst_cache_ctrl:3;
+	unsigned int endian_swqp_size:4;
+};
+
+struct pl330_desc {
+	struct dma_async_tx_descriptor	async_tx;
+	struct list_head		desc_node;
+	void				*desc_pool_virt;
+};
+
+struct pl330_chan {
+	struct pl330_device		*pl330_dev;
+	struct pl330_register_cc	pl330_reg_cc;
+	struct dma_chan			common;
+	struct tasklet_struct		tasklet;
+	struct list_head		free_desc;
+	struct list_head		queue_desc;
+	struct list_head		complete_desc;
+	spinlock_t			lock;
+	dma_cookie_t			completed;
+	unsigned int			id;
+	unsigned int			desc_num;
+};
+
+struct pl330_device {
+	void __iomem		*reg_base;
+	struct pl330_chan	pl330_ch[PL330_MAX_CHANS];
+	struct dma_device	common;
+};
+
+#endif
-- 
1.6.0.4

WARNING: multiple messages have this Message-ID (diff)
From: Joonyoung Shim <jy0922.shim@samsung.com>
To: dan.j.williams@intel.com
Cc: linux-arm-kernel@lists.infradead.org, ben-linux@fluff.org,
	kyungmin.park@samsung.com, bhmin@samsung.com,
	linux-kernel@vger.kernel.org
Subject: [PATCH 3/3] DMA: PL330: add PL330 DMA controller driver
Date: Wed, 16 Sep 2009 17:19:39 +0900	[thread overview]
Message-ID: <4AB09F9B.8030203@samsung.com> (raw)

The PL330 is the dma controller for the S5PC1XX arm SoC. This supports
DMA_MEMCPY and DMA_SLAVE.

The datasheet for the PL330 can find below url:
http://infocenter.arm.com/help/topic/com.arm.doc.ddi0424a/DDI0424A_dmac_pl330_r0p0_trm.pdf

Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com>
---
 drivers/dma/Kconfig      |    7 +
 drivers/dma/Makefile     |    1 +
 drivers/dma/pl330_dmac.c |  994 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/dma/pl330_dmac.h |  175 ++++++++
 4 files changed, 1177 insertions(+), 0 deletions(-)
 create mode 100644 drivers/dma/pl330_dmac.c
 create mode 100644 drivers/dma/pl330_dmac.h

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 81e1020..cbce4ed 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -97,6 +97,13 @@ config TXX9_DMAC
 	  Support the TXx9 SoC internal DMA controller.  This can be
 	  integrated in chips such as the Toshiba TX4927/38/39.
 
+config PL330_DMAC
+	bool "PrimeCell DMA Controller(PL330) support"
+	depends on ARCH_S5PC1XX
+	select DMA_ENGINE
+	help
+	  Enable support for the PrimeCell DMA Controller(PL330) support.
+
 config DMA_ENGINE
 	bool
 
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 40e1e00..ce6c232 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_DW_DMAC) += dw_dmac.o
 obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
 obj-$(CONFIG_MX3_IPU) += ipu/
 obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o
+obj-$(CONFIG_PL330_DMAC) += pl330_dmac.o
diff --git a/drivers/dma/pl330_dmac.c b/drivers/dma/pl330_dmac.c
new file mode 100644
index 0000000..4e67b09
--- /dev/null
+++ b/drivers/dma/pl330_dmac.c
@@ -0,0 +1,994 @@
+/*
+ * pl330_dmac.c  --  Driver for PL330 DMA Controller
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.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/init.h>
+#include <linux/module.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <plat/dma.h>
+
+#include "pl330_dmac.h"
+
+#define to_pl330_chan(chan)	container_of(chan, struct pl330_chan, common)
+#define to_pl330_desc(node)	container_of(node, struct pl330_desc, desc_node)
+#define tx_to_pl330_desc(tx)	container_of(tx, struct pl330_desc, async_tx)
+
+static unsigned int pl330_get_reg(struct pl330_device *pl330_dev,
+		unsigned int reg)
+{
+	void __iomem *base = pl330_dev->reg_base;
+
+	return readl(base + reg);
+}
+
+static void pl330_set_reg(struct pl330_device *pl330_dev, unsigned int reg,
+		unsigned int val)
+{
+	void __iomem *base = pl330_dev->reg_base;
+
+	writel(val, base + reg);
+}
+
+static void pl330_dump_regs(struct pl330_chan *pl330_ch)
+{
+	struct device *dev = pl330_ch->pl330_dev->common.dev;
+	unsigned int val;
+	unsigned int id = pl330_ch->id;
+
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_DS);
+	dev_dbg(dev, "PL330_DS:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_DPC);
+	dev_dbg(dev, "PL330_DPC:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_INTEN);
+	dev_dbg(dev, "PL330_INTEN:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_ES);
+	dev_dbg(dev, "PL330_ES:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_INTSTATUS);
+	dev_dbg(dev, "PL330_INTSTATUS:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_FSM);
+	dev_dbg(dev, "PL330_FSM:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_FSC);
+	dev_dbg(dev, "PL330_FSC:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_FTM);
+	dev_dbg(dev, "PL330_FTM:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_FTC(id));
+	dev_dbg(dev, "PL330_FTC(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CS(id));
+	dev_dbg(dev, "PL330_CS(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CPC(id));
+	dev_dbg(dev, "PL330_CPC(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_SA(id));
+	dev_dbg(dev, "PL330_SA(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_DA(id));
+	dev_dbg(dev, "PL330_DA(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CC(id));
+	dev_dbg(dev, "PL330_CC(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_LC0(id));
+	dev_dbg(dev, "PL330_LC0(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_LC1(id));
+	dev_dbg(dev, "PL330_LC1(%d):\t\t0x%08x\n", id, val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_DBGSTATUS);
+	dev_dbg(dev, "PL330_DBGSTATUS:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CR0);
+	dev_dbg(dev, "PL330_CR0:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CR1);
+	dev_dbg(dev, "PL330_CR1:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CR2);
+	dev_dbg(dev, "PL330_CR2:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CR3);
+	dev_dbg(dev, "PL330_CR3:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CR4);
+	dev_dbg(dev, "PL330_CR4:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_CRDN);
+	dev_dbg(dev, "PL330_CRDN:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PERIPH_ID0);
+	dev_dbg(dev, "PL330_PERIPH_ID0:\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PERIPH_ID1);
+	dev_dbg(dev, "PL330_PERIPH_ID1:\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PERIPH_ID2);
+	dev_dbg(dev, "PL330_PERIPH_ID2:\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PERIPH_ID3);
+	dev_dbg(dev, "PL330_PERIPH_ID3:\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PCELL_ID0);
+	dev_dbg(dev, "PL330_PCELL_ID0:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PCELL_ID1);
+	dev_dbg(dev, "PL330_PCELL_ID0:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PCELL_ID2);
+	dev_dbg(dev, "PL330_PCELL_ID0:\t\t0x%08x\n", val);
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_PCELL_ID3);
+	dev_dbg(dev, "PL330_PCELL_ID0:\t\t0x%08x\n", val);
+}
+
+/* instruction set functions */
+static inline int pl330_dmaaddh(u8 *desc_pool_virt, u16 imm, bool ra)
+{
+	u8 opcode = DMAADDH | (ra << 1);
+
+	writeb(opcode, desc_pool_virt++);
+	writew(imm, desc_pool_virt);
+	return 3;
+}
+
+static inline int pl330_dmaend(u8 *desc_pool_virt)
+{
+	u8 opcode = DMAEND;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmaflushp(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMAFLUSHHP;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmald(u8 *desc_pool_virt)
+{
+	u8 opcode = DMALD;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmalds(u8 *desc_pool_virt)
+{
+	u8 opcode = DMALDS;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmaldb(u8 *desc_pool_virt)
+{
+	u8 opcode = DMALDB;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmaldps(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMALDPS;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmaldpb(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMALDPB;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmalp(u8 *desc_pool_virt, u8 iter, bool lc)
+{
+	u8 opcode = DMALP | (lc << 1);
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(iter, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmalpend(u8 *desc_pool_virt, u8 backwards_jump, bool lc)
+{
+	u8 opcode = DMALPEND | (lc << 2);
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(backwards_jump, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmalpends(u8 *desc_pool_virt, u8 backwards_jump,
+		bool lc)
+{
+	u8 opcode = DMALPENDS | (lc << 2);
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(backwards_jump, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmalpendb(u8 *desc_pool_virt, u8 backwards_jump,
+		bool lc)
+{
+	u8 opcode = DMALPENDB | (lc << 2);
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(backwards_jump, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmalpfe(u8 *desc_pool_virt, u8 backwards_jump, bool lc)
+{
+	u8 opcode = DMALPFE | (lc << 2);
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(backwards_jump, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmakill(u8 *desc_pool_virt)
+{
+	u8 opcode = DMAKILL;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmamov(u8 *desc_pool_virt, u8 rd, u32 imm)
+{
+	u8 opcode = DMAMOV;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(rd, desc_pool_virt++);
+	writel(imm, desc_pool_virt);
+	return 6;
+}
+
+static inline int pl330_dmanop(u8 *desc_pool_virt)
+{
+	u8 opcode = DMANOP;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmarmb(u8 *desc_pool_virt)
+{
+	u8 opcode = DMARMB;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmasev(u8 *desc_pool_virt, u8 event_num)
+{
+	u8 opcode = DMASEV;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(event_num << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmast(u8 *desc_pool_virt)
+{
+	u8 opcode = DMAST;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmasts(u8 *desc_pool_virt)
+{
+	u8 opcode = DMASTS;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmastb(u8 *desc_pool_virt)
+{
+	u8 opcode = DMASTB;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmastps(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMASTPS;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmastpb(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMASTPB;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmastz(u8 *desc_pool_virt)
+{
+	u8 opcode = DMASTZ;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static inline int pl330_dmawfe(u8 *desc_pool_virt, u8 event_num, bool invalid)
+{
+	u8 opcode = DMAWFE;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb((event_num << 3) | (invalid << 1), desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmawfps(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMAWFPS;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmawfpb(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMAWFPB;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmawfpp(u8 *desc_pool_virt, u8 periph)
+{
+	u8 opcode = DMAWFPP;
+
+	writeb(opcode, desc_pool_virt++);
+	writeb(periph << 3, desc_pool_virt);
+	return 2;
+}
+
+static inline int pl330_dmawmb(u8 *desc_pool_virt)
+{
+	u8 opcode = DMAWMB;
+
+	writeb(opcode, desc_pool_virt);
+	return 1;
+}
+
+static void pl330_dmago(struct pl330_chan *pl330_ch, struct pl330_desc *desc,
+		bool ns)
+{
+	unsigned int val;
+	u8 opcode = DMAGO | (ns << 1);
+
+	val = (pl330_ch->id << 24) | (opcode << 16) | (pl330_ch->id << 8);
+	pl330_set_reg(pl330_ch->pl330_dev, PL330_DBGINST0, val);
+
+	val = desc->async_tx.phys;
+	pl330_set_reg(pl330_ch->pl330_dev, PL330_DBGINST1, val);
+
+	pl330_set_reg(pl330_ch->pl330_dev, PL330_DBGCMD, 0);
+}
+
+static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+	struct pl330_chan *pl330_ch = to_pl330_chan(tx->chan);
+	struct pl330_desc *desc = tx_to_pl330_desc(tx);
+	unsigned long flags;
+	dma_cookie_t cookie;
+
+	spin_lock_irqsave(&pl330_ch->lock, flags);
+
+	cookie = pl330_ch->common.cookie;
+
+	if (++cookie < 0)
+		cookie = 1;
+
+	desc->async_tx.cookie = cookie;
+	pl330_ch->common.cookie = cookie;
+
+	list_add_tail(&desc->desc_node, &pl330_ch->queue_desc);
+
+	spin_unlock_irqrestore(&pl330_ch->lock, flags);
+
+	return cookie;
+}
+
+static struct pl330_desc *
+pl330_alloc_descriptor(struct pl330_chan *pl330_ch, gfp_t flags)
+{
+	struct device *dev = pl330_ch->pl330_dev->common.dev;
+	struct pl330_desc *desc;
+	dma_addr_t phys;
+
+	desc = kzalloc(sizeof(*desc), flags);
+	if (!desc)
+		return NULL;
+
+	desc->desc_pool_virt = dma_alloc_coherent(dev, PL330_POOL_SIZE, &phys,
+			flags);
+	if (!desc->desc_pool_virt) {
+		kfree(desc);
+		return NULL;
+	}
+
+	dma_async_tx_descriptor_init(&desc->async_tx, &pl330_ch->common);
+	desc->async_tx.tx_submit = pl330_tx_submit;
+	desc->async_tx.phys = phys;
+
+	return desc;
+}
+
+static struct pl330_desc *pl330_get_descriptor(struct pl330_chan *pl330_ch)
+{
+	struct device *dev = pl330_ch->pl330_dev->common.dev;
+	struct pl330_desc *desc;
+
+	if (!list_empty(&pl330_ch->free_desc)) {
+		desc = to_pl330_desc(pl330_ch->free_desc.next);
+		list_del(&desc->desc_node);
+	} else {
+		/* try to get another desc */
+		desc = pl330_alloc_descriptor(pl330_ch, GFP_ATOMIC);
+		if (!desc) {
+			dev_err(dev, "descriptor alloc failed\n");
+			return NULL;
+		}
+	}
+
+	return desc;
+}
+
+static int pl330_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct pl330_chan *pl330_ch = to_pl330_chan(chan);
+	struct device *dev = pl330_ch->pl330_dev->common.dev;
+	struct pl330_desc *desc;
+	int i;
+	LIST_HEAD(tmp_list);
+
+	/* have we already been set up? */
+	if (!list_empty(&pl330_ch->free_desc))
+		return pl330_ch->desc_num;
+
+	for (i = 0; i < PL330_DESC_NUM; i++) {
+		desc = pl330_alloc_descriptor(pl330_ch, GFP_KERNEL);
+		if (!desc) {
+			dev_err(dev, "Only %d initial descriptors\n", i);
+			break;
+		}
+		list_add_tail(&desc->desc_node, &tmp_list);
+	}
+
+	pl330_ch->completed = chan->cookie = 1;
+	pl330_ch->desc_num = i;
+	list_splice(&tmp_list, &pl330_ch->free_desc);
+
+	return pl330_ch->desc_num;
+}
+
+static void pl330_free_chan_resources(struct dma_chan *chan)
+{
+	struct pl330_chan *pl330_ch = to_pl330_chan(chan);
+	struct device *dev = pl330_ch->pl330_dev->common.dev;
+	struct pl330_desc *desc, *_desc;
+
+	/* Before freeing channel resources first check
+	 * if they have been previously allocated for this channel.
+	 */
+	if (pl330_ch->desc_num == 0)
+		return;
+
+	list_for_each_entry_safe(desc, _desc, &pl330_ch->complete_desc,
+			desc_node) {
+		list_del(&desc->desc_node);
+		dma_free_coherent(dev, PL330_POOL_SIZE, desc->desc_pool_virt,
+				desc->async_tx.phys);
+		kfree(desc);
+	}
+	list_for_each_entry_safe(desc, _desc, &pl330_ch->queue_desc,
+			desc_node) {
+		list_del(&desc->desc_node);
+		dma_free_coherent(dev, PL330_POOL_SIZE, desc->desc_pool_virt,
+				desc->async_tx.phys);
+		kfree(desc);
+	}
+	list_for_each_entry_safe(desc, _desc, &pl330_ch->free_desc,
+			desc_node) {
+		list_del(&desc->desc_node);
+		dma_free_coherent(dev, PL330_POOL_SIZE, desc->desc_pool_virt,
+				desc->async_tx.phys);
+		kfree(desc);
+	}
+}
+
+static enum dma_status pl330_is_tx_complete(struct dma_chan *chan,
+		dma_cookie_t cookie, dma_cookie_t *done, dma_cookie_t *used)
+{
+	struct pl330_chan *pl330_ch = to_pl330_chan(chan);
+	dma_cookie_t last_used;
+	dma_cookie_t last_complete;
+	int ret;
+
+	last_complete = pl330_ch->completed;
+	last_used = chan->cookie;
+
+	ret = dma_async_is_complete(cookie, last_complete, last_used);
+
+	return ret;
+}
+
+static void pl330_issue_pending(struct dma_chan *chan)
+{
+	struct pl330_chan *pl330_ch = to_pl330_chan(chan);
+	struct pl330_desc *desc;
+	unsigned int val;
+
+	if (!list_empty(&pl330_ch->queue_desc)) {
+		val = pl330_get_reg(pl330_ch->pl330_dev, PL330_DBGSTATUS);
+		if (val == PL330_DBG_BUSY)
+			return;
+
+		desc = to_pl330_desc(pl330_ch->queue_desc.next);
+		list_move_tail(&desc->desc_node, &pl330_ch->complete_desc);
+
+		pl330_dmago(pl330_ch, desc, NS_NONSECURE);
+	}
+}
+
+static unsigned int pl330_make_instructions(struct pl330_chan *pl330_ch,
+		struct pl330_desc *desc, dma_addr_t dest, dma_addr_t src,
+		size_t len, unsigned int inst_size,
+		enum dma_data_direction direction)
+{
+	struct device *dev = pl330_ch->pl330_dev->common.dev;
+	void *buf = desc->desc_pool_virt;
+	u32 control = *(u32 *)&pl330_ch->pl330_reg_cc;
+	unsigned int loop_size = 0;
+	unsigned int loop_size_rest = 0;
+	unsigned int loop_count0 = 0;
+	unsigned int loop_count1 = 0;
+	unsigned int loop_count0_rest = 0;
+	unsigned int loop_start0 = 0;
+	unsigned int loop_start1 = 0;
+
+	dev_dbg(dev, "desc_pool_phys: 0x%x\n", desc->async_tx.phys);
+	dev_dbg(dev, "control: 0x%x\n", control);
+	dev_dbg(dev, "dest: 0x%x\n", dest);
+	dev_dbg(dev, "src: 0x%x\n", src);
+	dev_dbg(dev, "len: 0x%x\n", len);
+
+	/* calculate loop count */
+	loop_size = (pl330_ch->pl330_reg_cc.src_burst_len + 1) *
+		(1 << pl330_ch->pl330_reg_cc.src_burst_size);
+	dev_dbg(dev, "loop_size: 0x%x\n", loop_size);
+
+	loop_count0 = (len / loop_size) - 1;
+	loop_size_rest = len % loop_size;
+	dev_dbg(dev, "loop_count0: 0x%x\n", loop_count0);
+	dev_dbg(dev, "loop_size_rest: 0x%x\n", loop_size_rest);
+
+	if (loop_count0 >= PL330_MAX_LOOPS) {
+		loop_count1 = (loop_count0 / PL330_MAX_LOOPS) - 1;
+		loop_count0_rest = (loop_count0 % PL330_MAX_LOOPS) + 1;
+		loop_count0 = PL330_MAX_LOOPS - 1;
+		dev_dbg(dev, "loop_count0: 0x%x\n", loop_count0);
+		dev_dbg(dev, "loop_count0_rest: 0x%x\n", loop_count0_rest);
+		dev_dbg(dev, "loop_count1: 0x%x\n", loop_count1);
+
+		if (loop_count1 >= PL330_MAX_LOOPS)
+			dev_dbg(dev, "loop_count1 overflow\n");
+	}
+
+	/* write instruction sets on buffer */
+	inst_size += pl330_dmamov(buf + inst_size, RD_DAR, dest);
+	inst_size += pl330_dmamov(buf + inst_size, RD_SAR, src);
+	inst_size += pl330_dmamov(buf + inst_size, RD_CCR, control);
+
+	if (loop_count1) {
+		inst_size += pl330_dmalp(buf + inst_size, loop_count1, LC_1);
+		loop_start1 = inst_size;
+	}
+
+	if (loop_count0) {
+		inst_size += pl330_dmalp(buf + inst_size, loop_count0, LC_0);
+		loop_start0 = inst_size;
+	}
+
+	if (direction == DMA_TO_DEVICE) {
+		struct pl330_dma_slave *dma_slave = pl330_ch->common.private;
+		u8 periph = dma_slave->peri_num;
+		inst_size += pl330_dmawfps(buf + inst_size, periph);
+		inst_size += pl330_dmald(buf + inst_size);
+		inst_size += pl330_dmastps(buf + inst_size, periph);
+		inst_size += pl330_dmaflushp(buf + inst_size, periph);
+	} else if (direction == DMA_FROM_DEVICE) {
+		struct pl330_dma_slave *dma_slave = pl330_ch->common.private;
+		u8 periph = dma_slave->peri_num;
+		inst_size += pl330_dmawfps(buf + inst_size, periph);
+		inst_size += pl330_dmaldps(buf + inst_size, periph);
+		inst_size += pl330_dmast(buf + inst_size);
+		inst_size += pl330_dmaflushp(buf + inst_size, periph);
+	} else {
+		inst_size += pl330_dmald(buf + inst_size);
+		inst_size += pl330_dmarmb(buf + inst_size);
+		inst_size += pl330_dmast(buf + inst_size);
+		inst_size += pl330_dmawmb(buf + inst_size);
+	}
+
+	if (loop_count0) {
+		dev_dbg(dev, "inst_size - loop_start0: 0x%x\n",
+				inst_size - loop_start0);
+		inst_size += pl330_dmalpend(buf + inst_size,
+				inst_size - loop_start0, LC_0);
+	}
+
+	if (loop_count1) {
+		dev_dbg(dev, "inst_size - loop_start1: 0x%x\n",
+				inst_size - loop_start1);
+		inst_size += pl330_dmalpend(buf + inst_size,
+				inst_size - loop_start1, LC_1);
+	}
+
+	if (loop_count0_rest) {
+		inst_size += pl330_dmalp(buf + inst_size, loop_count0_rest - 1,
+				LC_0);
+		loop_start0 = inst_size;
+
+		if (direction == DMA_TO_DEVICE) {
+			struct pl330_dma_slave *dma_slave =
+				pl330_ch->common.private;
+			u8 periph = dma_slave->peri_num;
+			inst_size += pl330_dmawfps(buf + inst_size, periph);
+			inst_size += pl330_dmald(buf + inst_size);
+			inst_size += pl330_dmastps(buf + inst_size, periph);
+			inst_size += pl330_dmaflushp(buf + inst_size, periph);
+		} else if (direction == DMA_FROM_DEVICE) {
+			struct pl330_dma_slave *dma_slave =
+				pl330_ch->common.private;
+			u8 periph = dma_slave->peri_num;
+			inst_size += pl330_dmawfps(buf + inst_size, periph);
+			inst_size += pl330_dmaldps(buf + inst_size, periph);
+			inst_size += pl330_dmast(buf + inst_size);
+			inst_size += pl330_dmaflushp(buf + inst_size, periph);
+		} else {
+			inst_size += pl330_dmald(buf + inst_size);
+			inst_size += pl330_dmarmb(buf + inst_size);
+			inst_size += pl330_dmast(buf + inst_size);
+			inst_size += pl330_dmawmb(buf + inst_size);
+		}
+
+		dev_dbg(dev, "inst_size - loop_start0: 0x%x\n",
+				inst_size - loop_start0);
+		inst_size += pl330_dmalpend(buf + inst_size,
+				inst_size - loop_start0, LC_0);
+	}
+
+	inst_size += pl330_dmasev(buf + inst_size, pl330_ch->id);
+	inst_size += pl330_dmaend(buf + inst_size);
+
+	if (loop_size_rest)
+		dev_dbg(dev, "TODO\n");
+
+	return inst_size;
+}
+
+static struct dma_async_tx_descriptor *
+pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
+		size_t len, unsigned long flags)
+{
+	struct pl330_chan *pl330_ch = to_pl330_chan(chan);
+	struct pl330_desc *desc;
+
+	if (!chan || !len)
+		return NULL;
+
+	desc = pl330_get_descriptor(pl330_ch);
+	if (!desc)
+		return NULL;
+
+	pl330_make_instructions(pl330_ch, desc, dest, src, len, 0, DMA_NONE);
+
+	desc->async_tx.flags = flags;
+
+	return &desc->async_tx;
+}
+
+static struct dma_async_tx_descriptor *
+pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+		unsigned int sg_len, enum dma_data_direction direction,
+		unsigned long flags)
+{
+	struct pl330_chan *pl330_ch = to_pl330_chan(chan);
+	struct pl330_register_cc *pl330_reg_cc = &pl330_ch->pl330_reg_cc;
+	struct pl330_dma_slave *dma_slave = chan->private;
+	struct pl330_desc *desc;
+	struct scatterlist *sg;
+	unsigned int inst_size = 0;
+	unsigned int i;
+
+	BUG_ON(!dma_slave);
+	BUG_ON(direction == DMA_BIDIRECTIONAL);
+
+	if (!dma_slave->tx_reg)
+		BUG_ON(direction == DMA_TO_DEVICE);
+
+	if (!dma_slave->rx_reg)
+		BUG_ON(direction == DMA_FROM_DEVICE);
+
+	if (unlikely(!sg_len))
+		return NULL;
+
+	desc = pl330_get_descriptor(pl330_ch);
+	if (!desc)
+		return NULL;
+
+	for_each_sg(sgl, sg, sg_len, i) {
+		dma_addr_t dest;
+		dma_addr_t src;
+		unsigned int len = sg_dma_len(sg);
+
+		if (direction == DMA_TO_DEVICE) {
+			dest = dma_slave->tx_reg;
+			src = sg_dma_address(sg);
+			pl330_reg_cc->dst_inc = 0;
+		} else {
+			dest = sg_dma_address(sg);
+			src = dma_slave->rx_reg;
+			pl330_reg_cc->src_inc = 0;
+		}
+		pl330_reg_cc->src_burst_size = dma_slave->reg_width;
+		pl330_reg_cc->dst_burst_size = dma_slave->reg_width;
+
+		inst_size = pl330_make_instructions(pl330_ch, desc, dest, src,
+				len, inst_size, direction);
+	}
+
+	desc->async_tx.flags = flags;
+
+	return &desc->async_tx;
+}
+
+static void pl330_terminate_all(struct dma_chan *chan)
+{
+	/* TODO */
+}
+
+static void pl330_xfer_complete(struct pl330_chan *pl330_ch)
+{
+	struct pl330_desc *desc;
+	dma_async_tx_callback callback;
+	void *callback_param;
+
+	/* execute next desc */
+	pl330_issue_pending(&pl330_ch->common);
+
+	if (list_empty(&pl330_ch->complete_desc))
+		return;
+
+	desc = to_pl330_desc(pl330_ch->complete_desc.next);
+	list_move_tail(&desc->desc_node, &pl330_ch->free_desc);
+
+	pl330_ch->completed = desc->async_tx.cookie;
+
+	callback = desc->async_tx.callback;
+	callback_param = desc->async_tx.callback_param;
+	if (callback)
+		callback(callback_param);
+}
+
+static void pl330_ch_tasklet(unsigned long data)
+{
+	struct pl330_chan *pl330_ch = (struct pl330_chan *)data;
+	unsigned int val;
+
+	pl330_xfer_complete(pl330_ch);
+
+	/* enable channel interrupt */
+	val = pl330_get_reg(pl330_ch->pl330_dev, PL330_INTEN);
+	val |= (1 << pl330_ch->id);
+	pl330_set_reg(pl330_ch->pl330_dev, PL330_INTEN, val);
+}
+
+static irqreturn_t pl330_irq_handler(int irq, void *data)
+{
+	struct pl330_device *pl330_dev = data;
+	struct pl330_chan *pl330_ch;
+	unsigned int intstatus;
+	unsigned int inten;
+	int i;
+
+	intstatus = pl330_get_reg(pl330_dev, PL330_INTSTATUS);
+
+	if (intstatus == 0)
+		return IRQ_HANDLED;
+
+	inten = pl330_get_reg(pl330_dev, PL330_INTEN);
+	for (i = 0; i < PL330_MAX_CHANS; i++) {
+		if (intstatus & (1 << i)) {
+			pl330_ch = &pl330_dev->pl330_ch[i];
+			pl330_set_reg(pl330_dev, PL330_INTCLR, 1 << i);
+
+			/* disable channel interrupt */
+			inten &= ~(1 << i);
+			pl330_set_reg(pl330_dev, PL330_INTEN, inten);
+
+			tasklet_schedule(&pl330_ch->tasklet);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void pl330_reg_cc_init(struct pl330_register_cc *pl330_reg_cc)
+{
+	pl330_reg_cc->src_inc = 0x1;
+	pl330_reg_cc->src_burst_size = 0;
+	pl330_reg_cc->src_burst_len = 0;
+	pl330_reg_cc->src_prot_ctrl = 0x2;
+	pl330_reg_cc->src_cache_ctrl = 0;
+	pl330_reg_cc->dst_inc = 0x1;
+	pl330_reg_cc->dst_burst_size = 0;
+	pl330_reg_cc->dst_burst_len = 0;
+	pl330_reg_cc->dst_prot_ctrl = 0x2;
+	pl330_reg_cc->dst_cache_ctrl = 0;
+	pl330_reg_cc->endian_swqp_size = 0;
+}
+
+static int pl330_probe(struct platform_device *pdev)
+{
+	struct pl330_device *pl330_dev;
+	struct resource *res;
+	struct dma_device *dma_dev;
+	struct pl330_platform_data *pdata = pdev->dev.platform_data;
+	int ret;
+	int irq;
+	int i;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "platform data is required!\n");
+		return -EINVAL;
+	}
+
+	pl330_dev = devm_kzalloc(&pdev->dev, sizeof(*pl330_dev), GFP_KERNEL);
+	if (!pl330_dev)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		goto err_alloc;
+	}
+
+	pl330_dev->reg_base = devm_ioremap(&pdev->dev, res->start,
+			res->end - res->start + 1);
+	if (!pl330_dev->reg_base) {
+		ret = -EBUSY;
+		goto err_alloc;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		ret = irq;
+		goto err_remap;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, pl330_irq_handler, 0,
+			dev_name(&pdev->dev), pl330_dev);
+	if (ret)
+		goto err_remap;
+
+	dma_dev = &pl330_dev->common;
+	INIT_LIST_HEAD(&dma_dev->channels);
+
+	/* set base routines */
+	dma_dev->device_alloc_chan_resources = pl330_alloc_chan_resources;
+	dma_dev->device_free_chan_resources = pl330_free_chan_resources;
+	dma_dev->device_is_tx_complete = pl330_is_tx_complete;
+	dma_dev->device_issue_pending = pl330_issue_pending;
+	dma_dev->device_terminate_all = pl330_terminate_all;
+	dma_dev->dev = &pdev->dev;
+	dma_dev->cap_mask = pdata->cap_mask;
+
+	/* set prep routines based on capability */
+	if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask))
+		dma_dev->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
+	if (dma_has_cap(DMA_SLAVE, dma_dev->cap_mask))
+		dma_dev->device_prep_slave_sg = pl330_prep_slave_sg;
+
+	for (i = 0; i < PL330_MAX_CHANS; i++) {
+		struct pl330_chan *pl330_ch = &pl330_dev->pl330_ch[i];
+		unsigned int val;
+
+		spin_lock_init(&pl330_ch->lock);
+		pl330_ch->id = i;
+		pl330_ch->pl330_dev = pl330_dev;
+		pl330_ch->common.device = dma_dev;
+		tasklet_init(&pl330_ch->tasklet, pl330_ch_tasklet,
+				(unsigned long)pl330_ch);
+		INIT_LIST_HEAD(&pl330_ch->free_desc);
+		INIT_LIST_HEAD(&pl330_ch->queue_desc);
+		INIT_LIST_HEAD(&pl330_ch->complete_desc);
+		list_add_tail(&pl330_ch->common.device_node,
+				&dma_dev->channels);
+		dma_dev->chancnt++;
+		pl330_reg_cc_init(&pl330_ch->pl330_reg_cc);
+		val = pl330_get_reg(pl330_ch->pl330_dev, PL330_INTEN);
+		val |= (1 << pl330_ch->id);
+		pl330_set_reg(pl330_ch->pl330_dev, PL330_INTEN, val);
+		pl330_dump_regs(pl330_ch);
+	}
+
+	platform_set_drvdata(pdev, pl330_dev);
+
+	dev_info(&pdev->dev, "PL330 DMA Controller: ( %s%s)\n",
+		dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "memcpy " : "",
+		dma_has_cap(DMA_SLAVE, dma_dev->cap_mask) ? "slave " : "");
+
+	dma_async_device_register(dma_dev);
+	return 0;
+
+err_remap:
+	iounmap(pl330_dev->reg_base);
+err_alloc:
+	devm_kfree(&pdev->dev, pl330_dev);
+
+	return ret;
+}
+
+static int pl330_remove(struct platform_device *pdev)
+{
+	struct pl330_device *pl330_dev = platform_get_drvdata(pdev);
+	struct pl330_chan *pl330_ch, *_pl330_ch;
+
+	dma_async_device_unregister(&pl330_dev->common);
+
+	list_for_each_entry_safe(pl330_ch, _pl330_ch,
+			&pl330_dev->common.channels, common.device_node) {
+		list_del(&pl330_ch->common.device_node);
+		tasklet_kill(&pl330_ch->tasklet);
+	}
+
+	iounmap(pl330_dev->reg_base);
+	devm_kfree(&pdev->dev, pl330_dev);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver pl330_driver = {
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "pl330",
+	},
+	.probe		= pl330_probe,
+	.remove		= pl330_remove,
+};
+
+static int __init pl330_init(void)
+{
+	return platform_driver_register(&pl330_driver);
+}
+subsys_initcall(pl330_init);
+
+static void __exit pl330_exit(void)
+{
+	platform_driver_unregister(&pl330_driver);
+
+	return;
+}
+
+module_exit(pl330_exit);
+
+MODULE_DESCRIPTION("Driver for PL330 DMA Controller");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/pl330_dmac.h b/drivers/dma/pl330_dmac.h
new file mode 100644
index 0000000..d2cbd4e
--- /dev/null
+++ b/drivers/dma/pl330_dmac.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.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.
+ *
+ */
+
+#ifndef __PL330_DMAC_H
+#define __PL330_DMAC_H
+
+#define PL330_MAX_CHANS		8
+#define PL330_MAX_LOOPS		256
+#define PL330_POOL_SIZE		SZ_256
+#define PL330_DESC_NUM		8
+
+/* registers */
+#define PL330_DS		0x00
+#define PL330_DPC		0x04
+#define PL330_INTEN		0x20			/* R/W */
+#define PL330_ES		0x24
+#define PL330_INTSTATUS		0x28
+#define PL330_INTCLR		0x2c			/* W/O */
+#define PL330_FSM		0x30
+#define PL330_FSC		0x34
+#define PL330_FTM		0x38
+#define PL330_FTC(ch)		(0x40 + (ch << 2))
+#define PL330_CS(ch)		(0x100 + (ch << 3))
+#define PL330_CPC(ch)		(0x104 + (ch << 3))
+#define PL330_SA(ch)		(0x400 + (ch << 5))
+#define PL330_DA(ch)		(0x404 + (ch << 5))
+#define PL330_CC(ch)		(0x408 + (ch << 5))
+#define PL330_LC0(ch)		(0x40c + (ch << 5))
+#define PL330_LC1(ch)		(0x410 + (ch << 5))
+#define PL330_DBGSTATUS		0xd00
+#define PL330_DBGCMD		0xd04			/* W/O */
+#define PL330_DBGINST0		0xd08			/* W/O */
+#define PL330_DBGINST1		0xd0c			/* W/O */
+#define PL330_CR0		0xe00
+#define PL330_CR1		0xe04
+#define PL330_CR2		0xe08
+#define PL330_CR3		0xe0c
+#define PL330_CR4		0xe10
+#define PL330_CRDN		0xe14
+#define PL330_PERIPH_ID0	0xfe0
+#define PL330_PERIPH_ID1	0xfe4
+#define PL330_PERIPH_ID2	0xfe8
+#define PL330_PERIPH_ID3	0xfec
+#define PL330_PCELL_ID0		0xff0
+#define PL330_PCELL_ID1		0xff4
+#define PL330_PCELL_ID2		0xff8
+#define PL330_PCELL_ID3		0xffc
+
+/* PL330_CC */
+#define PL330_SRC_INC			(1 << 0)
+#define PL330_SRC_BSIZE_1BYTE		(1 << 1)
+#define PL330_SRC_BSIZE_2BYTE		(2 << 1)
+#define PL330_SRC_BSIZE_4BYTE		(3 << 1)
+#define PL330_SRC_BSIZE_16BYTE		(4 << 1)
+#define PL330_SRC_BSIZE_32BYTE		(5 << 1)
+#define PL330_SRC_BSIZE_64BYTE		(6 << 1)
+#define PL330_SRC_BSIZE_128BYTE		(7 << 1)
+#define PL330_SRC_BLEN(n)		((n - 1) << 4)
+#define PL330_DEST_INC			(1 << 14)
+#define PL330_DEST_BSIZE_1BYTE		(1 << 15)
+#define PL330_DEST_BSIZE_2BYTE		(2 << 15)
+#define PL330_DEST_BSIZE_4BYTE		(3 << 15)
+#define PL330_DEST_BSIZE_16BYTE		(4 << 15)
+#define PL330_DEST_BSIZE_32BYTE		(5 << 15)
+#define PL330_DEST_BSIZE_64BYTE		(6 << 15)
+#define PL330_DEST_BSIZE_128BYTE	(7 << 15)
+#define PL330_DEST_BLEN(n)		((n - 18) << 4)
+
+/* PL330_DBGSTATUS */
+#define PL330_DBG_IDLE		0
+#define PL330_DBG_BUSY		1
+
+/* instruction set opcode */
+#define DMAADDH			(0x54)
+#define DMAEND			(0x00)
+#define DMAFLUSHHP		(0x35)
+#define DMAGO			(0xa0)
+#define DMALD			(0x04)
+#define DMALDS			(0x05)
+#define DMALDB			(0x07)
+#define DMALDPS			(0x25)
+#define DMALDPB			(0x27)
+#define DMALP			(0x20)
+#define DMALPEND		(0x38)
+#define DMALPENDS		(0x39)
+#define DMALPENDB		(0x3b)
+#define DMALPFE			(0x28)
+#define DMAKILL			(0x01)
+#define DMAMOV			(0xbc)
+#define DMANOP			(0xbc)
+#define DMARMB			(0x12)
+#define DMASEV			(0x34)
+#define DMAST			(0x08)
+#define DMASTS			(0x09)
+#define DMASTB			(0x0b)
+#define DMASTPS			(0x29)
+#define DMASTPB			(0x2b)
+#define DMASTZ			(0x0c)
+#define DMAWFE			(0x36)
+#define DMAWFPS			(0x30)
+#define DMAWFPB			(0x32)
+#define DMAWFPP			(0x31)
+#define DMAWMB			(0x13)
+
+/* ra DMAADDH */
+#define RA_SA			0
+#define RA_DA			1
+
+/* ns DMAGO */
+#define NS_SECURE		0
+#define NS_NONSECURE		1
+
+/* lc DMALP* */
+#define LC_0			0
+#define LC_1			1
+
+/* rd DMAMOV */
+#define RD_SAR			0
+#define RD_CCR			1
+#define RD_DAR			2
+
+/* invalid DMAWFE */
+#define INVALID_OFF		0
+#define INVALID_ON		1
+
+/* struct for PL330_CC Register */
+struct pl330_register_cc {
+	unsigned int src_inc:1;
+	unsigned int src_burst_size:3;
+	unsigned int src_burst_len:4;
+	unsigned int src_prot_ctrl:3;
+	unsigned int src_cache_ctrl:3;
+	unsigned int dst_inc:1;
+	unsigned int dst_burst_size:3;
+	unsigned int dst_burst_len:4;
+	unsigned int dst_prot_ctrl:3;
+	unsigned int dst_cache_ctrl:3;
+	unsigned int endian_swqp_size:4;
+};
+
+struct pl330_desc {
+	struct dma_async_tx_descriptor	async_tx;
+	struct list_head		desc_node;
+	void				*desc_pool_virt;
+};
+
+struct pl330_chan {
+	struct pl330_device		*pl330_dev;
+	struct pl330_register_cc	pl330_reg_cc;
+	struct dma_chan			common;
+	struct tasklet_struct		tasklet;
+	struct list_head		free_desc;
+	struct list_head		queue_desc;
+	struct list_head		complete_desc;
+	spinlock_t			lock;
+	dma_cookie_t			completed;
+	unsigned int			id;
+	unsigned int			desc_num;
+};
+
+struct pl330_device {
+	void __iomem		*reg_base;
+	struct pl330_chan	pl330_ch[PL330_MAX_CHANS];
+	struct dma_device	common;
+};
+
+#endif
-- 
1.6.0.4

             reply	other threads:[~2009-09-16  8:19 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-09-16  8:19 Joonyoung Shim [this message]
2009-09-16  8:19 ` [PATCH 3/3] DMA: PL330: add PL330 DMA controller driver Joonyoung Shim

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4AB09F9B.8030203@samsung.com \
    --to=jy0922.shim@samsung.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.