dmaengine:sirf:take clock and enable it while probing
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / dma / sirf-dma.c
index 94674a9..c439529 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/of_platform.h>
+#include <linux/clk.h>
 #include <linux/sirfsoc_dma.h>
 
 #include "dmaengine.h"
@@ -32,7 +33,9 @@
 #define SIRFSOC_DMA_CH_VALID                    0x140
 #define SIRFSOC_DMA_CH_INT                      0x144
 #define SIRFSOC_DMA_INT_EN                      0x148
+#define SIRFSOC_DMA_INT_EN_CLR                 0x14C
 #define SIRFSOC_DMA_CH_LOOP_CTRL                0x150
+#define SIRFSOC_DMA_CH_LOOP_CTRL_CLR            0x15C
 
 #define SIRFSOC_DMA_MODE_CTRL_BIT               4
 #define SIRFSOC_DMA_DIR_CTRL_BIT                5
@@ -76,6 +79,8 @@ struct sirfsoc_dma {
        struct sirfsoc_dma_chan         channels[SIRFSOC_DMA_CHANNELS];
        void __iomem                    *base;
        int                             irq;
+       struct clk                      *clk;
+       bool                            is_marco;
 };
 
 #define DRV_NAME       "sirfsoc_dma"
@@ -288,17 +293,67 @@ static int sirfsoc_dma_terminate_all(struct sirfsoc_dma_chan *schan)
        int cid = schan->chan.chan_id;
        unsigned long flags;
 
-       writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) &
-               ~(1 << cid), sdma->base + SIRFSOC_DMA_INT_EN);
-       writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID);
+       spin_lock_irqsave(&schan->lock, flags);
 
-       writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL)
-               & ~((1 << cid) | 1 << (cid + 16)),
+       if (!sdma->is_marco) {
+               writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) &
+                       ~(1 << cid), sdma->base + SIRFSOC_DMA_INT_EN);
+               writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL)
+                       & ~((1 << cid) | 1 << (cid + 16)),
                        sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL);
+       } else {
+               writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_INT_EN_CLR);
+               writel_relaxed((1 << cid) | 1 << (cid + 16),
+                       sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL_CLR);
+       }
+
+       writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID);
 
-       spin_lock_irqsave(&schan->lock, flags);
        list_splice_tail_init(&schan->active, &schan->free);
        list_splice_tail_init(&schan->queued, &schan->free);
+
+       spin_unlock_irqrestore(&schan->lock, flags);
+
+       return 0;
+}
+
+static int sirfsoc_dma_pause_chan(struct sirfsoc_dma_chan *schan)
+{
+       struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(&schan->chan);
+       int cid = schan->chan.chan_id;
+       unsigned long flags;
+
+       spin_lock_irqsave(&schan->lock, flags);
+
+       if (!sdma->is_marco)
+               writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL)
+                       & ~((1 << cid) | 1 << (cid + 16)),
+                       sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL);
+       else
+               writel_relaxed((1 << cid) | 1 << (cid + 16),
+                       sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL_CLR);
+
+       spin_unlock_irqrestore(&schan->lock, flags);
+
+       return 0;
+}
+
+static int sirfsoc_dma_resume_chan(struct sirfsoc_dma_chan *schan)
+{
+       struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(&schan->chan);
+       int cid = schan->chan.chan_id;
+       unsigned long flags;
+
+       spin_lock_irqsave(&schan->lock, flags);
+
+       if (!sdma->is_marco)
+               writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL)
+                       | ((1 << cid) | 1 << (cid + 16)),
+                       sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL);
+       else
+               writel_relaxed((1 << cid) | 1 << (cid + 16),
+                       sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL);
+
        spin_unlock_irqrestore(&schan->lock, flags);
 
        return 0;
@@ -311,6 +366,10 @@ static int sirfsoc_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
        struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan);
 
        switch (cmd) {
+       case DMA_PAUSE:
+               return sirfsoc_dma_pause_chan(schan);
+       case DMA_RESUME:
+               return sirfsoc_dma_resume_chan(schan);
        case DMA_TERMINATE_ALL:
                return sirfsoc_dma_terminate_all(schan);
        case DMA_SLAVE_CONFIG:
@@ -568,6 +627,9 @@ static int sirfsoc_dma_probe(struct platform_device *op)
                return -ENOMEM;
        }
 
+       if (of_device_is_compatible(dn, "sirf,marco-dmac"))
+               sdma->is_marco = true;
+
        if (of_property_read_u32(dn, "cell-index", &id)) {
                dev_err(dev, "Fail to get DMAC index\n");
                return -ENODEV;
@@ -579,6 +641,12 @@ static int sirfsoc_dma_probe(struct platform_device *op)
                return -EINVAL;
        }
 
+       sdma->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(sdma->clk)) {
+               dev_err(dev, "failed to get a clock.\n");
+               return PTR_ERR(sdma->clk);
+       }
+
        ret = of_address_to_resource(dn, 0, &res);
        if (ret) {
                dev_err(dev, "Error parsing memory region!\n");
@@ -638,6 +706,8 @@ static int sirfsoc_dma_probe(struct platform_device *op)
 
        tasklet_init(&sdma->tasklet, sirfsoc_dma_tasklet, (unsigned long)sdma);
 
+       clk_prepare_enable(sdma->clk);
+
        /* Register DMA engine */
        dev_set_drvdata(dev, sdma);
        ret = dma_async_device_register(dma);
@@ -660,6 +730,7 @@ static int sirfsoc_dma_remove(struct platform_device *op)
        struct device *dev = &op->dev;
        struct sirfsoc_dma *sdma = dev_get_drvdata(dev);
 
+       clk_disable_unprepare(sdma->clk);
        dma_async_device_unregister(&sdma->dma);
        free_irq(sdma->irq, sdma);
        irq_dispose_mapping(sdma->irq);
@@ -668,6 +739,7 @@ static int sirfsoc_dma_remove(struct platform_device *op)
 
 static struct of_device_id sirfsoc_dma_match[] = {
        { .compatible = "sirf,prima2-dmac", },
+       { .compatible = "sirf,marco-dmac", },
        {},
 };