Add dmaengine driver for MOXA ART SoCs.

Signed-off-by: Jonas Jensen <jonas.jen...@gmail.com>
---

Notes:
    Preemptively submitting a new version that has the previously
    mentioned two cell xlate.
    
    Changes since v5:
    
    1. add line request number and use two cell xlate
    
    device tree bindings document:
    2. update description, describe the two cells of #dma-cells
    
    Applies to next-20130802

 .../devicetree/bindings/dma/moxa,moxart-dma.txt    |  21 +
 drivers/dma/Kconfig                                |   7 +
 drivers/dma/Makefile                               |   1 +
 drivers/dma/moxart-dma.c                           | 610 +++++++++++++++++++++
 4 files changed, 639 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/moxa,moxart-dma.txt
 create mode 100644 drivers/dma/moxart-dma.c

diff --git a/Documentation/devicetree/bindings/dma/moxa,moxart-dma.txt 
b/Documentation/devicetree/bindings/dma/moxa,moxart-dma.txt
new file mode 100644
index 0000000..dc2b686
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/moxa,moxart-dma.txt
@@ -0,0 +1,21 @@
+MOXA ART DMA Controller
+
+See dma.txt first
+
+Required properties:
+
+- compatible : Must be "moxa,moxart-dma"
+- reg :                Should contain registers location and length
+- interrupts : Should contain the interrupt number
+- #dma-cells : Should be 2
+               cell index 0: channel number between 0-3
+               cell index 1: line request number
+
+Example:
+
+       dma: dma@90500000 {
+               compatible = "moxa,moxart-dma";
+               reg = <0x90500000 0x1000>;
+               interrupts = <24 0>;
+               #dma-cells = <1>;
+       };
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 6825957..56c3aaa 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -300,6 +300,13 @@ config DMA_JZ4740
        select DMA_ENGINE
        select DMA_VIRTUAL_CHANNELS
 
+config MOXART_DMA
+       tristate "MOXART DMA support"
+       depends on ARCH_MOXART
+       select DMA_ENGINE
+       help
+         Enable support for the MOXA ART SoC DMA controller.
+
 config DMA_ENGINE
        bool
 
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 5e0f2ef..470c11b 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -39,3 +39,4 @@ obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
 obj-$(CONFIG_DMA_OMAP) += omap-dma.o
 obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
 obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
+obj-$(CONFIG_MOXART_DMA) += moxart-dma.o
diff --git a/drivers/dma/moxart-dma.c b/drivers/dma/moxart-dma.c
new file mode 100644
index 0000000..3ed270f
--- /dev/null
+++ b/drivers/dma/moxart-dma.c
@@ -0,0 +1,610 @@
+/*
+ * MOXA ART SoCs DMA Engine support.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <jonas.jen...@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_dma.h>
+
+#include <asm/cacheflush.h>
+
+#include "dmaengine.h"
+
+#define APB_DMA_MAX_CHANNEL                    4
+
+#define APB_DMA_ENABLE                         0x1
+#define APB_DMA_FIN_INT_STS                    0x2
+#define APB_DMA_FIN_INT_EN                     0x4
+#define APB_DMA_BURST_MODE                     0x8
+#define APB_DMA_ERR_INT_STS                    0x10
+#define APB_DMA_ERR_INT_EN                     0x20
+
+/*
+ * unset to select APB source
+ * set to select AHB source
+ */
+#define APB_DMA_SOURCE_SELECT                  0x40
+
+/*
+ * unset to select APB destination
+ * set to select AHB destination
+ */
+#define APB_DMA_DEST_SELECT                    0x80
+
+#define APB_DMA_SOURCE                         0x100
+#define APB_DMA_SOURCE_MASK                    0x700
+/*
+ * 000: no increment
+ * 001: +1 (busrt=0), +4  (burst=1)
+ * 010: +2 (burst=0), +8  (burst=1)
+ * 011: +4 (burst=0), +16 (burst=1)
+ * 101: -1 (burst=0), -4  (burst=1)
+ * 110: -2 (burst=0), -8  (burst=1)
+ * 111: -4 (burst=0), -16 (burst=1)
+ */
+#define APB_DMA_SOURCE_INC_0                   0
+#define APB_DMA_SOURCE_INC_1_4                 0x100
+#define APB_DMA_SOURCE_INC_2_8                 0x200
+#define APB_DMA_SOURCE_INC_4_16                        0x300
+#define APB_DMA_SOURCE_DEC_1_4                 0x500
+#define APB_DMA_SOURCE_DEC_2_8                 0x600
+#define APB_DMA_SOURCE_DEC_4_16                        0x700
+
+#define APB_DMA_DEST                           0x1000
+#define APB_DMA_DEST_MASK                      0x7000
+/*
+ * 000: no increment
+ * 001: +1 (busrt=0), +4  (burst=1)
+ * 010: +2 (burst=0), +8  (burst=1)
+ * 011: +4 (burst=0), +16 (burst=1)
+ * 101: -1 (burst=0), -4  (burst=1)
+ * 110: -2 (burst=0), -8  (burst=1)
+ * 111: -4 (burst=0), -16 (burst=1)
+*/
+#define APB_DMA_DEST_INC_0                     0
+#define APB_DMA_DEST_INC_1_4                   0x1000
+#define APB_DMA_DEST_INC_2_8                   0x2000
+#define APB_DMA_DEST_INC_4_16                  0x3000
+#define APB_DMA_DEST_DEC_1_4                   0x5000
+#define APB_DMA_DEST_DEC_2_8                   0x6000
+#define APB_DMA_DEST_DEC_4_16                  0x7000
+
+/*
+ * request signal select of destination
+ * address for DMA hardware handshake
+ *
+ * the request line number is a property of
+ * the DMA controller itself, e.g. MMC must
+ * always request channels where
+ * dma_slave_config->slave_id == 5
+ *
+ * 0:    no request / grant signal
+ * 1-15: request / grant signal
+ */
+#define APB_DMA_DEST_REQ_NO                    0x10000
+#define APB_DMA_DEST_REQ_NO_MASK               0xf0000
+
+#define APB_DMA_DATA_WIDTH                     0x100000
+#define APB_DMA_DATA_WIDTH_MASK                        0x300000
+/*
+ * data width of transfer
+ * 00: word
+ * 01: half
+ * 10: byte
+ */
+#define APB_DMA_DATA_WIDTH_4                   0
+#define APB_DMA_DATA_WIDTH_2                   0x100000
+#define APB_DMA_DATA_WIDTH_1                   0x200000
+
+/*
+ * request signal select of source
+ * address for DMA hardware handshake
+ *
+ * the request line number is a property of
+ * the DMA controller itself, e.g. MMC must
+ * always request channels where
+ * dma_slave_config->slave_id == 5
+ *
+ * 0:    no request / grant signal
+ * 1-15: request / grant signal
+ */
+#define APB_DMA_SOURCE_REQ_NO                  0x1000000
+#define APB_DMA_SOURCE_REQ_NO_MASK             0xf000000
+
+struct moxart_dma_reg {
+       u32 source_addr;
+       u32 dest_addr;
+#define APB_DMA_CYCLES_MASK                    0x00ffffff
+       u32 cycles;     /* depend on burst mode */
+       u32 ctrl;
+};
+
+struct moxart_dma_chan {
+       struct dma_chan                 chan;
+       int                             ch_num;
+       bool                            allocated;
+       int                             error_flag;
+       struct moxart_dma_reg           *reg;
+       void                            (*callback)(void *param);
+       void                            *callback_param;
+       struct completion               dma_complete;
+       struct dma_slave_config         cfg;
+       struct dma_async_tx_descriptor  tx_desc;
+       unsigned int                    line;
+};
+
+struct moxart_dma_container {
+       int                     ctlr;
+       struct dma_device       dma_slave;
+       struct moxart_dma_chan  slave_chans[APB_DMA_MAX_CHANNEL];
+       spinlock_t dma_lock;
+};
+
+static struct device *chan2dev(struct dma_chan *chan)
+{
+       return &chan->dev->device;
+}
+
+static inline struct moxart_dma_container
+*to_dma_container(struct dma_device *d)
+{
+       return container_of(d, struct moxart_dma_container, dma_slave);
+}
+
+static inline struct moxart_dma_chan *to_moxart_dma_chan(struct dma_chan *c)
+{
+       return container_of(c, struct moxart_dma_chan, chan);
+}
+
+static int moxart_terminate_all(struct dma_chan *chan)
+{
+       struct moxart_dma_chan *ch = to_moxart_dma_chan(chan);
+       struct moxart_dma_container *c = to_dma_container(ch->chan.device);
+       u32 ctrl;
+       unsigned long flags;
+
+       dev_dbg(chan2dev(chan), "%s: ch=%p\n", __func__, ch);
+
+       spin_lock_irqsave(&c->dma_lock, flags);
+
+       ctrl = readl(&ch->reg->ctrl);
+       ctrl &= ~(APB_DMA_ENABLE | APB_DMA_FIN_INT_EN | APB_DMA_ERR_INT_EN);
+       writel(ctrl, &ch->reg->ctrl);
+
+       spin_unlock_irqrestore(&c->dma_lock, flags);
+
+       return 0;
+}
+
+static int moxart_slave_config(struct dma_chan *chan,
+                              struct dma_slave_config *cfg)
+{
+       struct moxart_dma_chan *mchan = to_moxart_dma_chan(chan);
+       struct moxart_dma_container *mc = to_dma_container(mchan->chan.device);
+       u32 ctrl;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mc->dma_lock, flags);
+
+       memcpy(&mchan->cfg, cfg, sizeof(mchan->cfg));
+
+       ctrl = readl(&mchan->reg->ctrl);
+       ctrl |= APB_DMA_BURST_MODE;
+       ctrl &= ~(APB_DMA_DEST_MASK | APB_DMA_SOURCE_MASK);
+       ctrl &= ~(APB_DMA_DEST_REQ_NO_MASK | APB_DMA_SOURCE_REQ_NO_MASK);
+
+       switch (mchan->cfg.src_addr_width) {
+       case DMA_SLAVE_BUSWIDTH_1_BYTE:
+               ctrl |= APB_DMA_DATA_WIDTH_1;
+               if (mchan->cfg.direction != DMA_MEM_TO_DEV)
+                       ctrl |= APB_DMA_DEST_INC_1_4;
+               else
+                       ctrl |= APB_DMA_SOURCE_INC_1_4;
+               break;
+       case DMA_SLAVE_BUSWIDTH_2_BYTES:
+               ctrl |= APB_DMA_DATA_WIDTH_2;
+               if (mchan->cfg.direction != DMA_MEM_TO_DEV)
+                       ctrl |= APB_DMA_DEST_INC_2_8;
+               else
+                       ctrl |= APB_DMA_SOURCE_INC_2_8;
+               break;
+       default:
+               ctrl &= ~APB_DMA_DATA_WIDTH;
+               if (mchan->cfg.direction != DMA_MEM_TO_DEV)
+                       ctrl |= APB_DMA_DEST_INC_4_16;
+               else
+                       ctrl |= APB_DMA_SOURCE_INC_4_16;
+               break;
+       }
+
+       if (mchan->cfg.direction == DMA_MEM_TO_DEV) {
+               ctrl &= ~APB_DMA_DEST_SELECT;
+               ctrl |= APB_DMA_SOURCE_SELECT;
+               ctrl |= (mchan->line << 16 &
+                        APB_DMA_DEST_REQ_NO_MASK);
+       } else {
+               ctrl |= APB_DMA_DEST_SELECT;
+               ctrl &= ~APB_DMA_SOURCE_SELECT;
+               ctrl |= (mchan->line << 24 &
+                        APB_DMA_SOURCE_REQ_NO_MASK);
+       }
+
+       writel(ctrl, &mchan->reg->ctrl);
+
+       spin_unlock_irqrestore(&mc->dma_lock, flags);
+
+       return 0;
+}
+
+static int moxart_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+                         unsigned long arg)
+{
+       int ret = 0;
+       struct dma_slave_config *config;
+
+       switch (cmd) {
+       case DMA_TERMINATE_ALL:
+               moxart_terminate_all(chan);
+               break;
+       case DMA_SLAVE_CONFIG:
+               config = (struct dma_slave_config *)arg;
+               ret = moxart_slave_config(chan, config);
+               break;
+       default:
+               ret = -ENOSYS;
+       }
+
+       return ret;
+}
+
+static dma_cookie_t moxart_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+       struct moxart_dma_chan *mchan = to_moxart_dma_chan(tx->chan);
+       struct moxart_dma_container *mc = to_dma_container(mchan->chan.device);
+       dma_cookie_t cookie;
+       u32 ctrl;
+       unsigned long flags;
+
+       mchan->callback = tx->callback;
+       mchan->callback_param = tx->callback_param;
+       mchan->error_flag = 0;
+
+       dev_dbg(chan2dev(tx->chan), "%s: mchan=%p mchan->ch_num=%d 
mchan->reg=%p\n",
+               __func__, mchan, mchan->ch_num, mchan->reg);
+
+       spin_lock_irqsave(&mc->dma_lock, flags);
+
+       cookie = dma_cookie_assign(tx);
+
+       ctrl = readl(&mchan->reg->ctrl);
+       ctrl |= (APB_DMA_FIN_INT_EN | APB_DMA_ERR_INT_EN);
+       writel(ctrl, &mchan->reg->ctrl);
+
+       spin_unlock_irqrestore(&mc->dma_lock, flags);
+
+       return cookie;
+}
+
+static struct dma_async_tx_descriptor
+*moxart_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+                     unsigned int sg_len,
+                     enum dma_transfer_direction direction,
+                     unsigned long tx_flags, void *context)
+{
+       struct moxart_dma_chan *mchan = to_moxart_dma_chan(chan);
+       struct moxart_dma_container *mc = to_dma_container(mchan->chan.device);
+       unsigned long flags;
+       unsigned int size, adr_width;
+
+       spin_lock_irqsave(&mc->dma_lock, flags);
+
+       if (direction == DMA_MEM_TO_DEV) {
+               writel(virt_to_phys((void *)sg_dma_address(&sgl[0])),
+                      &mchan->reg->source_addr);
+               writel(mchan->cfg.dst_addr, &mchan->reg->dest_addr);
+               adr_width = mchan->cfg.src_addr_width;
+       } else {
+               writel(mchan->cfg.src_addr, &mchan->reg->source_addr);
+               writel(virt_to_phys((void *)sg_dma_address(&sgl[0])),
+                      &mchan->reg->dest_addr);
+               adr_width = mchan->cfg.dst_addr_width;
+       }
+
+       size = sgl->length >> adr_width;
+
+       /*
+        * size is 4 on 64 bytes copied, i.e. once cycle copies 16 bytes
+        * ( when data_width == APB_DMAB_DATA_WIDTH_4 )
+        */
+       writel(size, &mchan->reg->cycles);
+
+       dev_dbg(chan2dev(chan), "%s: set %d DMA cycles (sgl->length=%d 
adr_width=%d)\n",
+               __func__, size, sgl->length, adr_width);
+
+       dma_async_tx_descriptor_init(&mchan->tx_desc, chan);
+       mchan->tx_desc.tx_submit = moxart_tx_submit;
+
+       spin_unlock_irqrestore(&mc->dma_lock, flags);
+
+       return &mchan->tx_desc;
+}
+
+bool moxart_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+       struct moxart_dma_chan *mchan = to_moxart_dma_chan(chan);
+       struct moxart_dma_container *mc = to_dma_container(mchan->chan.device);
+
+       if (chan->device->dev == mc->dma_slave.dev) {
+               struct moxart_dma_chan *mchan = to_moxart_dma_chan(chan);
+               unsigned int ch_req = *(unsigned int *)param;
+               dev_dbg(chan2dev(chan), "%s: mchan=%p ch_req=%d 
mchan->ch_num=%d\n",
+                       __func__, mchan, ch_req, mchan->ch_num);
+               return ch_req == mchan->ch_num;
+       } else {
+               dev_dbg(chan2dev(chan), "%s: device not registered to this DMA 
engine\n",
+                       __func__);
+               return false;
+       }
+}
+
+static struct of_dma_filter_info moxart_dma_info = {
+       .filter_fn = moxart_dma_filter_fn,
+};
+
+static struct dma_chan *moxart_of_xlate(struct of_phandle_args *dma_spec,
+                                       struct of_dma *ofdma)
+{
+       struct dma_chan *chan;
+       struct of_dma_filter_info *info = ofdma->of_dma_data;
+
+       if (!info || !info->filter_fn)
+               return NULL;
+
+       if (dma_spec->args_count != 2)
+               return NULL;
+
+       chan = dma_request_channel(info->dma_cap, info->filter_fn,
+                                  &dma_spec->args[0]);
+       if (chan)
+               to_moxart_dma_chan(chan)->line = dma_spec->args[1];
+
+       return chan;
+}
+
+static int moxart_alloc_chan_resources(struct dma_chan *chan)
+{
+       struct moxart_dma_chan *mchan = to_moxart_dma_chan(chan);
+       int i;
+       bool found = false;
+
+       for (i = 0; i < APB_DMA_MAX_CHANNEL; i++) {
+               if (i == mchan->ch_num
+                       && !mchan->allocated) {
+                       dev_dbg(chan2dev(chan), "%s: allocating channel #%d\n",
+                               __func__, mchan->ch_num);
+                       mchan->allocated = true;
+                       found = true;
+                       break;
+               }
+       }
+
+       if (!found)
+               return -ENODEV;
+
+       return 0;
+}
+
+static void moxart_free_chan_resources(struct dma_chan *chan)
+{
+       struct moxart_dma_chan *mchan = to_moxart_dma_chan(chan);
+
+       mchan->allocated = false;
+
+       dev_dbg(chan2dev(chan), "%s: freeing channel #%u\n",
+               __func__, mchan->ch_num);
+}
+
+static void moxart_issue_pending(struct dma_chan *chan)
+{
+       struct moxart_dma_chan *mchan = to_moxart_dma_chan(chan);
+       struct moxart_dma_container *mc = to_dma_container(mchan->chan.device);
+       u32 ctrl;
+       unsigned long flags;
+
+       dev_dbg(chan2dev(chan), "%s: mchan=%p\n", __func__, mchan);
+
+       spin_lock_irqsave(&mc->dma_lock, flags);
+
+       ctrl = readl(&mchan->reg->ctrl);
+       ctrl |= APB_DMA_ENABLE;
+       writel(ctrl, &mchan->reg->ctrl);
+
+       spin_unlock_irqrestore(&mc->dma_lock, flags);
+}
+
+static enum dma_status moxart_tx_status(struct dma_chan *chan,
+                                       dma_cookie_t cookie,
+                                       struct dma_tx_state *txstate)
+{
+       enum dma_status ret;
+
+       ret = dma_cookie_status(chan, cookie, txstate);
+       if (ret == DMA_SUCCESS || !txstate)
+               return ret;
+
+       return ret;
+}
+
+static void moxart_dma_init(struct dma_device *dma, struct device *dev)
+{
+       dma->device_prep_slave_sg               = moxart_prep_slave_sg;
+       dma->device_alloc_chan_resources        = moxart_alloc_chan_resources;
+       dma->device_free_chan_resources         = moxart_free_chan_resources;
+       dma->device_issue_pending               = moxart_issue_pending;
+       dma->device_tx_status                   = moxart_tx_status;
+       dma->device_control                     = moxart_control;
+       dma->dev                                = dev;
+
+       INIT_LIST_HEAD(&dma->channels);
+}
+
+static irqreturn_t moxart_dma_interrupt(int irq, void *devid)
+{
+       struct moxart_dma_container *mc = to_dma_container(devid);
+       struct moxart_dma_chan *mchan = &mc->slave_chans[0];
+       unsigned int i;
+       u32 ctrl;
+
+       pr_debug("%s\n", __func__);
+
+       for (i = 0; i < APB_DMA_MAX_CHANNEL; i++, mchan++) {
+               if (mchan->allocated) {
+                       ctrl = readl(&mchan->reg->ctrl);
+                       if (ctrl & APB_DMA_FIN_INT_STS) {
+                               ctrl &= ~APB_DMA_FIN_INT_STS;
+                               dma_cookie_complete(&mchan->tx_desc);
+                       }
+                       if (ctrl & APB_DMA_ERR_INT_STS) {
+                               ctrl &= ~APB_DMA_ERR_INT_STS;
+                               mchan->error_flag = 1;
+                       }
+                       if (mchan->callback) {
+                               pr_debug("%s: call callback for mchan=%p\n",
+                                        __func__, mchan);
+                               mchan->callback(mchan->callback_param);
+                       }
+                       mchan->error_flag = 0;
+                       writel(ctrl, &mchan->reg->ctrl);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+static struct irqaction moxart_dma_irq = {
+       .name       = "moxart-dma-engine",
+       .flags      = IRQF_DISABLED,
+       .handler    = moxart_dma_interrupt,
+};
+
+static int moxart_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct resource *res;
+       static void __iomem *dma_base_addr;
+       int ret, i;
+       unsigned int irq;
+       struct moxart_dma_chan *mchan;
+       struct moxart_dma_container *mdc;
+
+       mdc = devm_kzalloc(dev, sizeof(*mdc), GFP_KERNEL);
+       if (!mdc) {
+               dev_err(dev, "can't allocate DMA container\n");
+               return -ENOMEM;
+       }
+
+       irq = irq_of_parse_and_map(node, 0);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       dma_base_addr = devm_ioremap_resource(dev, res);
+       if (IS_ERR(dma_base_addr)) {
+               dev_err(dev, "devm_ioremap_resource failed\n");
+               return PTR_ERR(dma_base_addr);
+       }
+
+       mdc->ctlr = pdev->id;
+       spin_lock_init(&mdc->dma_lock);
+
+       dma_cap_zero(mdc->dma_slave.cap_mask);
+       dma_cap_set(DMA_SLAVE, mdc->dma_slave.cap_mask);
+
+       moxart_dma_init(&mdc->dma_slave, dev);
+
+       mchan = &mdc->slave_chans[0];
+       for (i = 0; i < APB_DMA_MAX_CHANNEL; i++, mchan++) {
+               mchan->ch_num = i;
+               mchan->reg = (struct moxart_dma_reg *)(dma_base_addr + 0x80
+                            + i * sizeof(struct moxart_dma_reg));
+               mchan->callback = NULL;
+               mchan->allocated = 0;
+               mchan->callback_param = NULL;
+
+               dma_cookie_init(&mchan->chan);
+               mchan->chan.device = &mdc->dma_slave;
+               list_add_tail(&mchan->chan.device_node,
+                             &mdc->dma_slave.channels);
+
+               dev_dbg(dev, "%s: mchans[%d]: mchan->ch_num=%d mchan->reg=%p\n",
+                       __func__, i, mchan->ch_num, mchan->reg);
+       }
+
+       ret = dma_async_device_register(&mdc->dma_slave);
+       platform_set_drvdata(pdev, mdc);
+
+       of_dma_controller_register(node, moxart_of_xlate, &moxart_dma_info);
+
+       moxart_dma_irq.dev_id = &mdc->dma_slave;
+       setup_irq(irq, &moxart_dma_irq);
+
+       dev_dbg(dev, "%s: IRQ=%d\n", __func__, irq);
+
+       return ret;
+}
+
+static int moxart_remove(struct platform_device *pdev)
+{
+       struct moxart_dma_container *m = dev_get_drvdata(&pdev->dev);
+       dma_async_device_unregister(&m->dma_slave);
+       return 0;
+}
+
+static const struct of_device_id moxart_dma_match[] = {
+       { .compatible = "moxa,moxart-dma" },
+       { }
+};
+
+static struct platform_driver moxart_driver = {
+       .probe  = moxart_probe,
+       .remove = moxart_remove,
+       .driver = {
+               .name           = "moxart-dma-engine",
+               .owner          = THIS_MODULE,
+               .of_match_table = moxart_dma_match,
+       },
+};
+
+static int moxart_init(void)
+{
+       return platform_driver_register(&moxart_driver);
+}
+subsys_initcall(moxart_init);
+
+static void __exit moxart_exit(void)
+{
+       platform_driver_unregister(&moxart_driver);
+}
+module_exit(moxart_exit);
+
+MODULE_AUTHOR("Jonas Jensen <jonas.jen...@gmail.com>");
+MODULE_DESCRIPTION("MOXART DMA engine driver");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to