UPSTREAM: net: arc_emac: fix sk_buff leak
[firefly-linux-kernel-4.4.55.git] / drivers / dma / imx-dma.c
index f28583370d00f7d102b47b39241d1b5d63885a80..48d85f8b95fe1e97a014a47062e3ebcf27afd314 100644 (file)
@@ -27,6 +27,8 @@
 #include <linux/clk.h>
 #include <linux/dmaengine.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_dma.h>
 
 #include <asm/irq.h>
 #include <linux/platform_data/dma-imx.h>
@@ -186,7 +188,12 @@ struct imxdma_engine {
        enum imx_dma_type               devtype;
 };
 
-static struct platform_device_id imx_dma_devtype[] = {
+struct imxdma_filter_data {
+       struct imxdma_engine    *imxdma;
+       int                      request;
+};
+
+static const struct platform_device_id imx_dma_devtype[] = {
        {
                .name = "imx1-dma",
                .driver_data = IMX1_DMA,
@@ -202,16 +209,27 @@ static struct platform_device_id imx_dma_devtype[] = {
 };
 MODULE_DEVICE_TABLE(platform, imx_dma_devtype);
 
+static const struct of_device_id imx_dma_of_dev_id[] = {
+       {
+               .compatible = "fsl,imx1-dma",
+               .data = &imx_dma_devtype[IMX1_DMA],
+       }, {
+               .compatible = "fsl,imx21-dma",
+               .data = &imx_dma_devtype[IMX21_DMA],
+       }, {
+               .compatible = "fsl,imx27-dma",
+               .data = &imx_dma_devtype[IMX27_DMA],
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, imx_dma_of_dev_id);
+
 static inline int is_imx1_dma(struct imxdma_engine *imxdma)
 {
        return imxdma->devtype == IMX1_DMA;
 }
 
-static inline int is_imx21_dma(struct imxdma_engine *imxdma)
-{
-       return imxdma->devtype == IMX21_DMA;
-}
-
 static inline int is_imx27_dma(struct imxdma_engine *imxdma)
 {
        return imxdma->devtype == IMX27_DMA;
@@ -399,12 +417,12 @@ static irqreturn_t imxdma_err_handler(int irq, void *dev_id)
                /* Tasklet error handler */
                tasklet_schedule(&imxdma->channel[i].dma_tasklet);
 
-               printk(KERN_WARNING
-                      "DMA timeout on channel %d -%s%s%s%s\n", i,
-                      errcode & IMX_DMA_ERR_BURST ?    " burst" : "",
-                      errcode & IMX_DMA_ERR_REQUEST ?  " request" : "",
-                      errcode & IMX_DMA_ERR_TRANSFER ? " transfer" : "",
-                      errcode & IMX_DMA_ERR_BUFFER ?   " buffer" : "");
+               dev_warn(imxdma->dev,
+                        "DMA timeout on channel %d -%s%s%s%s\n", i,
+                        errcode & IMX_DMA_ERR_BURST ?    " burst" : "",
+                        errcode & IMX_DMA_ERR_REQUEST ?  " request" : "",
+                        errcode & IMX_DMA_ERR_TRANSFER ? " transfer" : "",
+                        errcode & IMX_DMA_ERR_BUFFER ?   " buffer" : "");
        }
        return IRQ_HANDLED;
 }
@@ -414,17 +432,18 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac)
        struct imxdma_engine *imxdma = imxdmac->imxdma;
        int chno = imxdmac->channel;
        struct imxdma_desc *desc;
+       unsigned long flags;
 
-       spin_lock(&imxdma->lock);
+       spin_lock_irqsave(&imxdma->lock, flags);
        if (list_empty(&imxdmac->ld_active)) {
-               spin_unlock(&imxdma->lock);
+               spin_unlock_irqrestore(&imxdma->lock, flags);
                goto out;
        }
 
        desc = list_first_entry(&imxdmac->ld_active,
                                struct imxdma_desc,
                                node);
-       spin_unlock(&imxdma->lock);
+       spin_unlock_irqrestore(&imxdma->lock, flags);
 
        if (desc->sg) {
                u32 tmp;
@@ -496,7 +515,6 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
 {
        struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan);
        struct imxdma_engine *imxdma = imxdmac->imxdma;
-       unsigned long flags;
        int slot = -1;
        int i;
 
@@ -504,7 +522,6 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
        switch (d->type) {
        case IMXDMA_DESC_INTERLEAVED:
                /* Try to get a free 2D slot */
-               spin_lock_irqsave(&imxdma->lock, flags);
                for (i = 0; i < IMX_DMA_2D_SLOTS; i++) {
                        if ((imxdma->slots_2d[i].count > 0) &&
                        ((imxdma->slots_2d[i].xsr != d->x) ||
@@ -514,10 +531,8 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
                        slot = i;
                        break;
                }
-               if (slot < 0) {
-                       spin_unlock_irqrestore(&imxdma->lock, flags);
+               if (slot < 0)
                        return -EBUSY;
-               }
 
                imxdma->slots_2d[slot].xsr = d->x;
                imxdma->slots_2d[slot].ysr = d->y;
@@ -526,7 +541,6 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
 
                imxdmac->slot_2d = slot;
                imxdmac->enabled_2d = true;
-               spin_unlock_irqrestore(&imxdma->lock, flags);
 
                if (slot == IMX_DMA_2D_SLOT_A) {
                        d->config_mem &= ~CCR_MSEL_B;
@@ -553,9 +567,11 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
 
                imx_dmav1_writel(imxdma, d->len, DMA_CNTR(imxdmac->channel));
 
-               dev_dbg(imxdma->dev, "%s channel: %d dest=0x%08x src=0x%08x "
-                       "dma_length=%d\n", __func__, imxdmac->channel,
-                       d->dest, d->src, d->len);
+               dev_dbg(imxdma->dev,
+                       "%s channel: %d dest=0x%08llx src=0x%08llx dma_length=%zu\n",
+                       __func__, imxdmac->channel,
+                       (unsigned long long)d->dest,
+                       (unsigned long long)d->src, d->len);
 
                break;
        /* Cyclic transfer is the same as slave_sg with special sg configuration. */
@@ -567,20 +583,22 @@ static int imxdma_xfer_desc(struct imxdma_desc *d)
                        imx_dmav1_writel(imxdma, imxdmac->ccr_from_device,
                                         DMA_CCR(imxdmac->channel));
 
-                       dev_dbg(imxdma->dev, "%s channel: %d sg=%p sgcount=%d "
-                               "total length=%d dev_addr=0x%08x (dev2mem)\n",
-                               __func__, imxdmac->channel, d->sg, d->sgcount,
-                               d->len, imxdmac->per_address);
+                       dev_dbg(imxdma->dev,
+                               "%s channel: %d sg=%p sgcount=%d total length=%zu dev_addr=0x%08llx (dev2mem)\n",
+                               __func__, imxdmac->channel,
+                               d->sg, d->sgcount, d->len,
+                               (unsigned long long)imxdmac->per_address);
                } else if (d->direction == DMA_MEM_TO_DEV) {
                        imx_dmav1_writel(imxdma, imxdmac->per_address,
                                         DMA_DAR(imxdmac->channel));
                        imx_dmav1_writel(imxdma, imxdmac->ccr_to_device,
                                         DMA_CCR(imxdmac->channel));
 
-                       dev_dbg(imxdma->dev, "%s channel: %d sg=%p sgcount=%d "
-                               "total length=%d dev_addr=0x%08x (mem2dev)\n",
-                               __func__, imxdmac->channel, d->sg, d->sgcount,
-                               d->len, imxdmac->per_address);
+                       dev_dbg(imxdma->dev,
+                               "%s channel: %d sg=%p sgcount=%d total length=%zu dev_addr=0x%08llx (mem2dev)\n",
+                               __func__, imxdmac->channel,
+                               d->sg, d->sgcount, d->len,
+                               (unsigned long long)imxdmac->per_address);
                } else {
                        dev_err(imxdma->dev, "%s channel: %d bad dma mode\n",
                                __func__, imxdmac->channel);
@@ -602,18 +620,17 @@ static void imxdma_tasklet(unsigned long data)
        struct imxdma_channel *imxdmac = (void *)data;
        struct imxdma_engine *imxdma = imxdmac->imxdma;
        struct imxdma_desc *desc;
+       unsigned long flags;
 
-       spin_lock(&imxdma->lock);
+       spin_lock_irqsave(&imxdma->lock, flags);
 
        if (list_empty(&imxdmac->ld_active)) {
                /* Someone might have called terminate all */
-               goto out;
+               spin_unlock_irqrestore(&imxdma->lock, flags);
+               return;
        }
        desc = list_first_entry(&imxdmac->ld_active, struct imxdma_desc, node);
 
-       if (desc->desc.callback)
-               desc->desc.callback(desc->desc.callback_param);
-
        /* If we are dealing with a cyclic descriptor, keep it on ld_active
         * and dont mark the descriptor as complete.
         * Only in non-cyclic cases it would be marked as complete
@@ -640,72 +657,74 @@ static void imxdma_tasklet(unsigned long data)
                                 __func__, imxdmac->channel);
        }
 out:
-       spin_unlock(&imxdma->lock);
+       spin_unlock_irqrestore(&imxdma->lock, flags);
+
+       if (desc->desc.callback)
+               desc->desc.callback(desc->desc.callback_param);
+
 }
 
-static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
-               unsigned long arg)
+static int imxdma_terminate_all(struct dma_chan *chan)
 {
        struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
-       struct dma_slave_config *dmaengine_cfg = (void *)arg;
        struct imxdma_engine *imxdma = imxdmac->imxdma;
        unsigned long flags;
-       unsigned int mode = 0;
 
-       switch (cmd) {
-       case DMA_TERMINATE_ALL:
-               imxdma_disable_hw(imxdmac);
-
-               spin_lock_irqsave(&imxdma->lock, flags);
-               list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free);
-               list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free);
-               spin_unlock_irqrestore(&imxdma->lock, flags);
-               return 0;
-       case DMA_SLAVE_CONFIG:
-               if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) {
-                       imxdmac->per_address = dmaengine_cfg->src_addr;
-                       imxdmac->watermark_level = dmaengine_cfg->src_maxburst;
-                       imxdmac->word_size = dmaengine_cfg->src_addr_width;
-               } else {
-                       imxdmac->per_address = dmaengine_cfg->dst_addr;
-                       imxdmac->watermark_level = dmaengine_cfg->dst_maxburst;
-                       imxdmac->word_size = dmaengine_cfg->dst_addr_width;
-               }
-
-               switch (imxdmac->word_size) {
-               case DMA_SLAVE_BUSWIDTH_1_BYTE:
-                       mode = IMX_DMA_MEMSIZE_8;
-                       break;
-               case DMA_SLAVE_BUSWIDTH_2_BYTES:
-                       mode = IMX_DMA_MEMSIZE_16;
-                       break;
-               default:
-               case DMA_SLAVE_BUSWIDTH_4_BYTES:
-                       mode = IMX_DMA_MEMSIZE_32;
-                       break;
-               }
+       imxdma_disable_hw(imxdmac);
 
-               imxdmac->hw_chaining = 0;
+       spin_lock_irqsave(&imxdma->lock, flags);
+       list_splice_tail_init(&imxdmac->ld_active, &imxdmac->ld_free);
+       list_splice_tail_init(&imxdmac->ld_queue, &imxdmac->ld_free);
+       spin_unlock_irqrestore(&imxdma->lock, flags);
+       return 0;
+}
 
-               imxdmac->ccr_from_device = (mode | IMX_DMA_TYPE_FIFO) |
-                       ((IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) << 2) |
-                       CCR_REN;
-               imxdmac->ccr_to_device =
-                       (IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) |
-                       ((mode | IMX_DMA_TYPE_FIFO) << 2) | CCR_REN;
-               imx_dmav1_writel(imxdma, imxdmac->dma_request,
-                                DMA_RSSR(imxdmac->channel));
+static int imxdma_config(struct dma_chan *chan,
+                        struct dma_slave_config *dmaengine_cfg)
+{
+       struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
+       struct imxdma_engine *imxdma = imxdmac->imxdma;
+       unsigned int mode = 0;
 
-               /* Set burst length */
-               imx_dmav1_writel(imxdma, imxdmac->watermark_level *
-                               imxdmac->word_size, DMA_BLR(imxdmac->channel));
+       if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) {
+               imxdmac->per_address = dmaengine_cfg->src_addr;
+               imxdmac->watermark_level = dmaengine_cfg->src_maxburst;
+               imxdmac->word_size = dmaengine_cfg->src_addr_width;
+       } else {
+               imxdmac->per_address = dmaengine_cfg->dst_addr;
+               imxdmac->watermark_level = dmaengine_cfg->dst_maxburst;
+               imxdmac->word_size = dmaengine_cfg->dst_addr_width;
+       }
 
-               return 0;
+       switch (imxdmac->word_size) {
+       case DMA_SLAVE_BUSWIDTH_1_BYTE:
+               mode = IMX_DMA_MEMSIZE_8;
+               break;
+       case DMA_SLAVE_BUSWIDTH_2_BYTES:
+               mode = IMX_DMA_MEMSIZE_16;
+               break;
        default:
-               return -ENOSYS;
+       case DMA_SLAVE_BUSWIDTH_4_BYTES:
+               mode = IMX_DMA_MEMSIZE_32;
+               break;
        }
 
-       return -EINVAL;
+       imxdmac->hw_chaining = 0;
+
+       imxdmac->ccr_from_device = (mode | IMX_DMA_TYPE_FIFO) |
+               ((IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) << 2) |
+               CCR_REN;
+       imxdmac->ccr_to_device =
+               (IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR) |
+               ((mode | IMX_DMA_TYPE_FIFO) << 2) | CCR_REN;
+       imx_dmav1_writel(imxdma, imxdmac->dma_request,
+                        DMA_RSSR(imxdmac->channel));
+
+       /* Set burst length */
+       imx_dmav1_writel(imxdma, imxdmac->watermark_level *
+                        imxdmac->word_size, DMA_BLR(imxdmac->channel));
+
+       return 0;
 }
 
 static enum dma_status imxdma_tx_status(struct dma_chan *chan,
@@ -749,7 +768,7 @@ static int imxdma_alloc_chan_resources(struct dma_chan *chan)
                desc->desc.tx_submit = imxdma_tx_submit;
                /* txd.flags will be overwritten in prep funcs */
                desc->desc.flags = DMA_CTRL_ACK;
-               desc->status = DMA_SUCCESS;
+               desc->status = DMA_COMPLETE;
 
                list_add_tail(&desc->node, &imxdmac->ld_free);
                imxdmac->descs_allocated++;
@@ -782,10 +801,8 @@ static void imxdma_free_chan_resources(struct dma_chan *chan)
        }
        INIT_LIST_HEAD(&imxdmac->ld_free);
 
-       if (imxdmac->sg_list) {
-               kfree(imxdmac->sg_list);
-               imxdmac->sg_list = NULL;
-       }
+       kfree(imxdmac->sg_list);
+       imxdmac->sg_list = NULL;
 }
 
 static struct dma_async_tx_descriptor *imxdma_prep_slave_sg(
@@ -842,7 +859,7 @@ static struct dma_async_tx_descriptor *imxdma_prep_slave_sg(
 static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
                struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
                size_t period_len, enum dma_transfer_direction direction,
-               unsigned long flags, void *context)
+               unsigned long flags)
 {
        struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
        struct imxdma_engine *imxdma = imxdmac->imxdma;
@@ -850,7 +867,7 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
        int i;
        unsigned int periods = buf_len / period_len;
 
-       dev_dbg(imxdma->dev, "%s channel: %d buf_len=%d period_len=%d\n",
+       dev_dbg(imxdma->dev, "%s channel: %d buf_len=%zu period_len=%zu\n",
                        __func__, imxdmac->channel, buf_len, period_len);
 
        if (list_empty(&imxdmac->ld_free) ||
@@ -862,7 +879,7 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
        kfree(imxdmac->sg_list);
 
        imxdmac->sg_list = kcalloc(periods + 1,
-                       sizeof(struct scatterlist), GFP_KERNEL);
+                       sizeof(struct scatterlist), GFP_ATOMIC);
        if (!imxdmac->sg_list)
                return NULL;
 
@@ -906,8 +923,9 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_memcpy(
        struct imxdma_engine *imxdma = imxdmac->imxdma;
        struct imxdma_desc *desc;
 
-       dev_dbg(imxdma->dev, "%s channel: %d src=0x%x dst=0x%x len=%d\n",
-                       __func__, imxdmac->channel, src, dest, len);
+       dev_dbg(imxdma->dev, "%s channel: %d src=0x%llx dst=0x%llx len=%zu\n",
+               __func__, imxdmac->channel, (unsigned long long)src,
+               (unsigned long long)dest, len);
 
        if (list_empty(&imxdmac->ld_free) ||
            imxdma_chan_is_doing_cyclic(imxdmac))
@@ -936,9 +954,10 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_interleaved(
        struct imxdma_engine *imxdma = imxdmac->imxdma;
        struct imxdma_desc *desc;
 
-       dev_dbg(imxdma->dev, "%s channel: %d src_start=0x%x dst_start=0x%x\n"
-               "   src_sgl=%s dst_sgl=%s numf=%d frame_size=%d\n", __func__,
-               imxdmac->channel, xt->src_start, xt->dst_start,
+       dev_dbg(imxdma->dev, "%s channel: %d src_start=0x%llx dst_start=0x%llx\n"
+               "   src_sgl=%s dst_sgl=%s numf=%zu frame_size=%zu\n", __func__,
+               imxdmac->channel, (unsigned long long)xt->src_start,
+               (unsigned long long) xt->dst_start,
                xt->src_sgl ? "true" : "false", xt->dst_sgl ? "true" : "false",
                xt->numf, xt->frame_size);
 
@@ -996,17 +1015,55 @@ static void imxdma_issue_pending(struct dma_chan *chan)
        spin_unlock_irqrestore(&imxdma->lock, flags);
 }
 
+static bool imxdma_filter_fn(struct dma_chan *chan, void *param)
+{
+       struct imxdma_filter_data *fdata = param;
+       struct imxdma_channel *imxdma_chan = to_imxdma_chan(chan);
+
+       if (chan->device->dev != fdata->imxdma->dev)
+               return false;
+
+       imxdma_chan->dma_request = fdata->request;
+       chan->private = NULL;
+
+       return true;
+}
+
+static struct dma_chan *imxdma_xlate(struct of_phandle_args *dma_spec,
+                                               struct of_dma *ofdma)
+{
+       int count = dma_spec->args_count;
+       struct imxdma_engine *imxdma = ofdma->of_dma_data;
+       struct imxdma_filter_data fdata = {
+               .imxdma = imxdma,
+       };
+
+       if (count != 1)
+               return NULL;
+
+       fdata.request = dma_spec->args[0];
+
+       return dma_request_channel(imxdma->dma_device.cap_mask,
+                                       imxdma_filter_fn, &fdata);
+}
+
 static int __init imxdma_probe(struct platform_device *pdev)
        {
        struct imxdma_engine *imxdma;
        struct resource *res;
+       const struct of_device_id *of_id;
        int ret, i;
        int irq, irq_err;
 
+       of_id = of_match_device(imx_dma_of_dev_id, &pdev->dev);
+       if (of_id)
+               pdev->id_entry = of_id->data;
+
        imxdma = devm_kzalloc(&pdev->dev, sizeof(*imxdma), GFP_KERNEL);
        if (!imxdma)
                return -ENOMEM;
 
+       imxdma->dev = &pdev->dev;
        imxdma->devtype = pdev->id_entry->driver_data;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1026,8 +1083,12 @@ static int __init imxdma_probe(struct platform_device *pdev)
        if (IS_ERR(imxdma->dma_ahb))
                return PTR_ERR(imxdma->dma_ahb);
 
-       clk_prepare_enable(imxdma->dma_ipg);
-       clk_prepare_enable(imxdma->dma_ahb);
+       ret = clk_prepare_enable(imxdma->dma_ipg);
+       if (ret)
+               return ret;
+       ret = clk_prepare_enable(imxdma->dma_ahb);
+       if (ret)
+               goto disable_dma_ipg_clk;
 
        /* reset DMA module */
        imx_dmav1_writel(imxdma, DCR_DRST, DMA_DCR);
@@ -1037,20 +1098,20 @@ static int __init imxdma_probe(struct platform_device *pdev)
                                       dma_irq_handler, 0, "DMA", imxdma);
                if (ret) {
                        dev_warn(imxdma->dev, "Can't register IRQ for DMA\n");
-                       goto err;
+                       goto disable_dma_ahb_clk;
                }
 
                irq_err = platform_get_irq(pdev, 1);
                if (irq_err < 0) {
                        ret = irq_err;
-                       goto err;
+                       goto disable_dma_ahb_clk;
                }
 
                ret = devm_request_irq(&pdev->dev, irq_err,
                                       imxdma_err_handler, 0, "DMA", imxdma);
                if (ret) {
                        dev_warn(imxdma->dev, "Can't register ERRIRQ for DMA\n");
-                       goto err;
+                       goto disable_dma_ahb_clk;
                }
        }
 
@@ -1087,7 +1148,7 @@ static int __init imxdma_probe(struct platform_device *pdev)
                                dev_warn(imxdma->dev, "Can't register IRQ %d "
                                         "for DMA channel %d\n",
                                         irq + i, i);
-                               goto err;
+                               goto disable_dma_ahb_clk;
                        }
                        init_timer(&imxdmac->watchdog);
                        imxdmac->watchdog.function = &imxdma_watchdog;
@@ -1111,7 +1172,6 @@ static int __init imxdma_probe(struct platform_device *pdev)
                              &imxdma->dma_device.channels);
        }
 
-       imxdma->dev = &pdev->dev;
        imxdma->dma_device.dev = &pdev->dev;
 
        imxdma->dma_device.device_alloc_chan_resources = imxdma_alloc_chan_resources;
@@ -1121,26 +1181,39 @@ static int __init imxdma_probe(struct platform_device *pdev)
        imxdma->dma_device.device_prep_dma_cyclic = imxdma_prep_dma_cyclic;
        imxdma->dma_device.device_prep_dma_memcpy = imxdma_prep_dma_memcpy;
        imxdma->dma_device.device_prep_interleaved_dma = imxdma_prep_dma_interleaved;
-       imxdma->dma_device.device_control = imxdma_control;
+       imxdma->dma_device.device_config = imxdma_config;
+       imxdma->dma_device.device_terminate_all = imxdma_terminate_all;
        imxdma->dma_device.device_issue_pending = imxdma_issue_pending;
 
        platform_set_drvdata(pdev, imxdma);
 
-       imxdma->dma_device.copy_align = 2; /* 2^2 = 4 bytes alignment */
+       imxdma->dma_device.copy_align = DMAENGINE_ALIGN_4_BYTES;
        imxdma->dma_device.dev->dma_parms = &imxdma->dma_parms;
        dma_set_max_seg_size(imxdma->dma_device.dev, 0xffffff);
 
        ret = dma_async_device_register(&imxdma->dma_device);
        if (ret) {
                dev_err(&pdev->dev, "unable to register\n");
-               goto err;
+               goto disable_dma_ahb_clk;
+       }
+
+       if (pdev->dev.of_node) {
+               ret = of_dma_controller_register(pdev->dev.of_node,
+                               imxdma_xlate, imxdma);
+               if (ret) {
+                       dev_err(&pdev->dev, "unable to register of_dma_controller\n");
+                       goto err_of_dma_controller;
+               }
        }
 
        return 0;
 
-err:
-       clk_disable_unprepare(imxdma->dma_ipg);
+err_of_dma_controller:
+       dma_async_device_unregister(&imxdma->dma_device);
+disable_dma_ahb_clk:
        clk_disable_unprepare(imxdma->dma_ahb);
+disable_dma_ipg_clk:
+       clk_disable_unprepare(imxdma->dma_ipg);
        return ret;
 }
 
@@ -1150,6 +1223,9 @@ static int imxdma_remove(struct platform_device *pdev)
 
         dma_async_device_unregister(&imxdma->dma_device);
 
+       if (pdev->dev.of_node)
+               of_dma_controller_free(pdev->dev.of_node);
+
        clk_disable_unprepare(imxdma->dma_ipg);
        clk_disable_unprepare(imxdma->dma_ahb);
 
@@ -1159,6 +1235,7 @@ static int imxdma_remove(struct platform_device *pdev)
 static struct platform_driver imxdma_driver = {
        .driver         = {
                .name   = "imx-dma",
+               .of_match_table = imx_dma_of_dev_id,
        },
        .id_table       = imx_dma_devtype,
        .remove         = imxdma_remove,