These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / dma / sirf-dma.c
index a1afda4..22ea241 100644 (file)
 
 #include "dmaengine.h"
 
+#define SIRFSOC_DMA_VER_A7V1                    1
+#define SIRFSOC_DMA_VER_A7V2                    2
+#define SIRFSOC_DMA_VER_A6                      4
+
 #define SIRFSOC_DMA_DESCRIPTORS                 16
 #define SIRFSOC_DMA_CHANNELS                    16
+#define SIRFSOC_DMA_TABLE_NUM                   256
 
 #define SIRFSOC_DMA_CH_ADDR                     0x00
 #define SIRFSOC_DMA_CH_XLEN                     0x04
 #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_INT_EN_CLR                  0x14C
 #define SIRFSOC_DMA_CH_LOOP_CTRL                0x150
-#define SIRFSOC_DMA_CH_LOOP_CTRL_CLR            0x15C
+#define SIRFSOC_DMA_CH_LOOP_CTRL_CLR            0x154
+#define SIRFSOC_DMA_WIDTH_ATLAS7                0x10
+#define SIRFSOC_DMA_VALID_ATLAS7                0x14
+#define SIRFSOC_DMA_INT_ATLAS7                  0x18
+#define SIRFSOC_DMA_INT_EN_ATLAS7               0x1c
+#define SIRFSOC_DMA_LOOP_CTRL_ATLAS7            0x20
+#define SIRFSOC_DMA_CUR_DATA_ADDR               0x34
+#define SIRFSOC_DMA_MUL_ATLAS7                  0x38
+#define SIRFSOC_DMA_CH_LOOP_CTRL_ATLAS7         0x158
+#define SIRFSOC_DMA_CH_LOOP_CTRL_CLR_ATLAS7     0x15C
+#define SIRFSOC_DMA_IOBG_SCMD_EN               0x800
+#define SIRFSOC_DMA_EARLY_RESP_SET             0x818
+#define SIRFSOC_DMA_EARLY_RESP_CLR             0x81C
 
 #define SIRFSOC_DMA_MODE_CTRL_BIT               4
 #define SIRFSOC_DMA_DIR_CTRL_BIT                5
+#define SIRFSOC_DMA_MODE_CTRL_BIT_ATLAS7        2
+#define SIRFSOC_DMA_CHAIN_CTRL_BIT_ATLAS7       3
+#define SIRFSOC_DMA_DIR_CTRL_BIT_ATLAS7         4
+#define SIRFSOC_DMA_TAB_NUM_ATLAS7              7
+#define SIRFSOC_DMA_CHAIN_INT_BIT_ATLAS7        5
+#define SIRFSOC_DMA_CHAIN_FLAG_SHIFT_ATLAS7     25
+#define SIRFSOC_DMA_CHAIN_ADDR_SHIFT            32
+
+#define SIRFSOC_DMA_INT_FINI_INT_ATLAS7         BIT(0)
+#define SIRFSOC_DMA_INT_CNT_INT_ATLAS7          BIT(1)
+#define SIRFSOC_DMA_INT_PAU_INT_ATLAS7          BIT(2)
+#define SIRFSOC_DMA_INT_LOOP_INT_ATLAS7         BIT(3)
+#define SIRFSOC_DMA_INT_INV_INT_ATLAS7          BIT(4)
+#define SIRFSOC_DMA_INT_END_INT_ATLAS7          BIT(5)
+#define SIRFSOC_DMA_INT_ALL_ATLAS7              0x3F
 
 /* xlen and dma_width register is in 4 bytes boundary */
 #define SIRFSOC_DMA_WORD_LEN                   4
+#define SIRFSOC_DMA_XLEN_MAX_V1         0x800
+#define SIRFSOC_DMA_XLEN_MAX_V2         0x1000
 
 struct sirfsoc_dma_desc {
        struct dma_async_tx_descriptor  desc;
@@ -56,7 +90,9 @@ struct sirfsoc_dma_desc {
        int             width;          /* DMA width */
        int             dir;
        bool            cyclic;         /* is loop DMA? */
+       bool            chain;          /* is chain DMA? */
        u32             addr;           /* DMA buffer address */
+       u64 chain_table[SIRFSOC_DMA_TABLE_NUM]; /* chain tbl */
 };
 
 struct sirfsoc_dma_chan {
@@ -87,10 +123,25 @@ struct sirfsoc_dma {
        void __iomem                    *base;
        int                             irq;
        struct clk                      *clk;
-       bool                            is_marco;
+       int                             type;
+       void (*exec_desc)(struct sirfsoc_dma_desc *sdesc,
+               int cid, int burst_mode, void __iomem *base);
        struct sirfsoc_dma_regs         regs_save;
 };
 
+struct sirfsoc_dmadata {
+       void (*exec)(struct sirfsoc_dma_desc *sdesc,
+               int cid, int burst_mode, void __iomem *base);
+       int type;
+};
+
+enum sirfsoc_dma_chain_flag {
+       SIRFSOC_DMA_CHAIN_NORMAL = 0x01,
+       SIRFSOC_DMA_CHAIN_PAUSE = 0x02,
+       SIRFSOC_DMA_CHAIN_LOOP = 0x03,
+       SIRFSOC_DMA_CHAIN_END = 0x04
+};
+
 #define DRV_NAME       "sirfsoc_dma"
 
 static int sirfsoc_dma_runtime_suspend(struct device *dev);
@@ -109,48 +160,105 @@ static inline struct sirfsoc_dma *dma_chan_to_sirfsoc_dma(struct dma_chan *c)
        return container_of(schan, struct sirfsoc_dma, channels[c->chan_id]);
 }
 
+static void sirfsoc_dma_execute_hw_a7v2(struct sirfsoc_dma_desc *sdesc,
+               int cid, int burst_mode, void __iomem *base)
+{
+       if (sdesc->chain) {
+               /* DMA v2 HW chain mode */
+               writel_relaxed((sdesc->dir << SIRFSOC_DMA_DIR_CTRL_BIT_ATLAS7) |
+                              (sdesc->chain <<
+                               SIRFSOC_DMA_CHAIN_CTRL_BIT_ATLAS7) |
+                              (0x8 << SIRFSOC_DMA_TAB_NUM_ATLAS7) | 0x3,
+                              base + SIRFSOC_DMA_CH_CTRL);
+       } else {
+               /* DMA v2 legacy mode */
+               writel_relaxed(sdesc->xlen, base + SIRFSOC_DMA_CH_XLEN);
+               writel_relaxed(sdesc->ylen, base + SIRFSOC_DMA_CH_YLEN);
+               writel_relaxed(sdesc->width, base + SIRFSOC_DMA_WIDTH_ATLAS7);
+               writel_relaxed((sdesc->width*((sdesc->ylen+1)>>1)),
+                               base + SIRFSOC_DMA_MUL_ATLAS7);
+               writel_relaxed((sdesc->dir << SIRFSOC_DMA_DIR_CTRL_BIT_ATLAS7) |
+                              (sdesc->chain <<
+                               SIRFSOC_DMA_CHAIN_CTRL_BIT_ATLAS7) |
+                              0x3, base + SIRFSOC_DMA_CH_CTRL);
+       }
+       writel_relaxed(sdesc->chain ? SIRFSOC_DMA_INT_END_INT_ATLAS7 :
+                      (SIRFSOC_DMA_INT_FINI_INT_ATLAS7 |
+                       SIRFSOC_DMA_INT_LOOP_INT_ATLAS7),
+                      base + SIRFSOC_DMA_INT_EN_ATLAS7);
+       writel(sdesc->addr, base + SIRFSOC_DMA_CH_ADDR);
+       if (sdesc->cyclic)
+               writel(0x10001, base + SIRFSOC_DMA_LOOP_CTRL_ATLAS7);
+}
+
+static void sirfsoc_dma_execute_hw_a7v1(struct sirfsoc_dma_desc *sdesc,
+               int cid, int burst_mode, void __iomem *base)
+{
+       writel_relaxed(1, base + SIRFSOC_DMA_IOBG_SCMD_EN);
+       writel_relaxed((1 << cid), base + SIRFSOC_DMA_EARLY_RESP_SET);
+       writel_relaxed(sdesc->width, base + SIRFSOC_DMA_WIDTH_0 + cid * 4);
+       writel_relaxed(cid | (burst_mode << SIRFSOC_DMA_MODE_CTRL_BIT) |
+                      (sdesc->dir << SIRFSOC_DMA_DIR_CTRL_BIT),
+                      base + cid * 0x10 + SIRFSOC_DMA_CH_CTRL);
+       writel_relaxed(sdesc->xlen, base + cid * 0x10 + SIRFSOC_DMA_CH_XLEN);
+       writel_relaxed(sdesc->ylen, base + cid * 0x10 + SIRFSOC_DMA_CH_YLEN);
+       writel_relaxed(readl_relaxed(base + SIRFSOC_DMA_INT_EN) |
+                      (1 << cid), base + SIRFSOC_DMA_INT_EN);
+       writel(sdesc->addr >> 2, base + cid * 0x10 + SIRFSOC_DMA_CH_ADDR);
+       if (sdesc->cyclic) {
+               writel((1 << cid) | 1 << (cid + 16) |
+                      readl_relaxed(base + SIRFSOC_DMA_CH_LOOP_CTRL_ATLAS7),
+                      base + SIRFSOC_DMA_CH_LOOP_CTRL_ATLAS7);
+       }
+
+}
+
+static void sirfsoc_dma_execute_hw_a6(struct sirfsoc_dma_desc *sdesc,
+               int cid, int burst_mode, void __iomem *base)
+{
+       writel_relaxed(sdesc->width, base + SIRFSOC_DMA_WIDTH_0 + cid * 4);
+       writel_relaxed(cid | (burst_mode << SIRFSOC_DMA_MODE_CTRL_BIT) |
+                      (sdesc->dir << SIRFSOC_DMA_DIR_CTRL_BIT),
+                      base + cid * 0x10 + SIRFSOC_DMA_CH_CTRL);
+       writel_relaxed(sdesc->xlen, base + cid * 0x10 + SIRFSOC_DMA_CH_XLEN);
+       writel_relaxed(sdesc->ylen, base + cid * 0x10 + SIRFSOC_DMA_CH_YLEN);
+       writel_relaxed(readl_relaxed(base + SIRFSOC_DMA_INT_EN) |
+                      (1 << cid), base + SIRFSOC_DMA_INT_EN);
+       writel(sdesc->addr >> 2, base + cid * 0x10 + SIRFSOC_DMA_CH_ADDR);
+       if (sdesc->cyclic) {
+               writel((1 << cid) | 1 << (cid + 16) |
+                      readl_relaxed(base + SIRFSOC_DMA_CH_LOOP_CTRL),
+                      base + SIRFSOC_DMA_CH_LOOP_CTRL);
+       }
+
+}
+
 /* Execute all queued DMA descriptors */
 static void sirfsoc_dma_execute(struct sirfsoc_dma_chan *schan)
 {
        struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(&schan->chan);
        int cid = schan->chan.chan_id;
        struct sirfsoc_dma_desc *sdesc = NULL;
+       void __iomem *base;
 
        /*
         * lock has been held by functions calling this, so we don't hold
         * lock again
         */
-
+       base = sdma->base;
        sdesc = list_first_entry(&schan->queued, struct sirfsoc_dma_desc,
-               node);
+                                node);
        /* Move the first queued descriptor to active list */
        list_move_tail(&sdesc->node, &schan->active);
 
-       /* Start the DMA transfer */
-       writel_relaxed(sdesc->width, sdma->base + SIRFSOC_DMA_WIDTH_0 +
-               cid * 4);
-       writel_relaxed(cid | (schan->mode << SIRFSOC_DMA_MODE_CTRL_BIT) |
-               (sdesc->dir << SIRFSOC_DMA_DIR_CTRL_BIT),
-               sdma->base + cid * 0x10 + SIRFSOC_DMA_CH_CTRL);
-       writel_relaxed(sdesc->xlen, sdma->base + cid * 0x10 +
-               SIRFSOC_DMA_CH_XLEN);
-       writel_relaxed(sdesc->ylen, sdma->base + cid * 0x10 +
-               SIRFSOC_DMA_CH_YLEN);
-       writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) |
-               (1 << cid), sdma->base + SIRFSOC_DMA_INT_EN);
+       if (sdma->type == SIRFSOC_DMA_VER_A7V2)
+               cid = 0;
 
-       /*
-        * writel has an implict memory write barrier to make sure data is
-        * flushed into memory before starting DMA
-        */
-       writel(sdesc->addr >> 2, sdma->base + cid * 0x10 + SIRFSOC_DMA_CH_ADDR);
+       /* Start the DMA transfer */
+       sdma->exec_desc(sdesc, cid, schan->mode, base);
 
-       if (sdesc->cyclic) {
-               writel((1 << cid) | 1 << (cid + 16) |
-                       readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL),
-                       sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL);
+       if (sdesc->cyclic)
                schan->happened_cyclic = schan->completed_cyclic = 0;
-       }
 }
 
 /* Interrupt handler */
@@ -160,27 +268,65 @@ static irqreturn_t sirfsoc_dma_irq(int irq, void *data)
        struct sirfsoc_dma_chan *schan;
        struct sirfsoc_dma_desc *sdesc = NULL;
        u32 is;
+       bool chain;
        int ch;
+       void __iomem *reg;
+
+       switch (sdma->type) {
+       case SIRFSOC_DMA_VER_A6:
+       case SIRFSOC_DMA_VER_A7V1:
+               is = readl(sdma->base + SIRFSOC_DMA_CH_INT);
+               reg = sdma->base + SIRFSOC_DMA_CH_INT;
+               while ((ch = fls(is) - 1) >= 0) {
+                       is &= ~(1 << ch);
+                       writel_relaxed(1 << ch, reg);
+                       schan = &sdma->channels[ch];
+                       spin_lock(&schan->lock);
+                       sdesc = list_first_entry(&schan->active,
+                                                struct sirfsoc_dma_desc, node);
+                       if (!sdesc->cyclic) {
+                               /* Execute queued descriptors */
+                               list_splice_tail_init(&schan->active,
+                                                     &schan->completed);
+                               dma_cookie_complete(&sdesc->desc);
+                               if (!list_empty(&schan->queued))
+                                       sirfsoc_dma_execute(schan);
+                       } else
+                               schan->happened_cyclic++;
+                       spin_unlock(&schan->lock);
+               }
+               break;
 
-       is = readl(sdma->base + SIRFSOC_DMA_CH_INT);
-       while ((ch = fls(is) - 1) >= 0) {
-               is &= ~(1 << ch);
-               writel_relaxed(1 << ch, sdma->base + SIRFSOC_DMA_CH_INT);
-               schan = &sdma->channels[ch];
+       case SIRFSOC_DMA_VER_A7V2:
+               is = readl(sdma->base + SIRFSOC_DMA_INT_ATLAS7);
 
+               reg = sdma->base + SIRFSOC_DMA_INT_ATLAS7;
+               writel_relaxed(SIRFSOC_DMA_INT_ALL_ATLAS7, reg);
+               schan = &sdma->channels[0];
                spin_lock(&schan->lock);
-
-               sdesc = list_first_entry(&schan->active, struct sirfsoc_dma_desc,
-                       node);
+               sdesc = list_first_entry(&schan->active,
+                                        struct sirfsoc_dma_desc, node);
                if (!sdesc->cyclic) {
-                       /* Execute queued descriptors */
-                       list_splice_tail_init(&schan->active, &schan->completed);
-                       if (!list_empty(&schan->queued))
-                               sirfsoc_dma_execute(schan);
-               } else
+                       chain = sdesc->chain;
+                       if ((chain && (is & SIRFSOC_DMA_INT_END_INT_ATLAS7)) ||
+                               (!chain &&
+                               (is & SIRFSOC_DMA_INT_FINI_INT_ATLAS7))) {
+                               /* Execute queued descriptors */
+                               list_splice_tail_init(&schan->active,
+                                                     &schan->completed);
+                               dma_cookie_complete(&sdesc->desc);
+                               if (!list_empty(&schan->queued))
+                                       sirfsoc_dma_execute(schan);
+                       }
+               } else if (sdesc->cyclic && (is &
+                                       SIRFSOC_DMA_INT_LOOP_INT_ATLAS7))
                        schan->happened_cyclic++;
 
                spin_unlock(&schan->lock);
+               break;
+
+       default:
+               break;
        }
 
        /* Schedule tasklet */
@@ -227,16 +373,15 @@ static void sirfsoc_dma_process_completed(struct sirfsoc_dma *sdma)
                        schan->chan.completed_cookie = last_cookie;
                        spin_unlock_irqrestore(&schan->lock, flags);
                } else {
-                       /* for cyclic channel, desc is always in active list */
-                       sdesc = list_first_entry(&schan->active, struct sirfsoc_dma_desc,
-                               node);
-
-                       if (!sdesc || (sdesc && !sdesc->cyclic)) {
-                               /* without active cyclic DMA */
+                       if (list_empty(&schan->active)) {
                                spin_unlock_irqrestore(&schan->lock, flags);
                                continue;
                        }
 
+                       /* for cyclic channel, desc is always in active list */
+                       sdesc = list_first_entry(&schan->active,
+                               struct sirfsoc_dma_desc, node);
+
                        /* cyclic DMA */
                        happened_cyclic = schan->happened_cyclic;
                        spin_unlock_irqrestore(&schan->lock, flags);
@@ -307,20 +452,35 @@ static int sirfsoc_dma_terminate_all(struct dma_chan *chan)
 
        spin_lock_irqsave(&schan->lock, flags);
 
-       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 {
+       switch (sdma->type) {
+       case SIRFSOC_DMA_VER_A7V1:
                writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_INT_EN_CLR);
+               writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_INT);
                writel_relaxed((1 << cid) | 1 << (cid + 16),
-                       sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL_CLR);
+                              sdma->base +
+                              SIRFSOC_DMA_CH_LOOP_CTRL_CLR_ATLAS7);
+               writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID);
+               break;
+       case SIRFSOC_DMA_VER_A7V2:
+               writel_relaxed(0, sdma->base + SIRFSOC_DMA_INT_EN_ATLAS7);
+               writel_relaxed(SIRFSOC_DMA_INT_ALL_ATLAS7,
+                              sdma->base + SIRFSOC_DMA_INT_ATLAS7);
+               writel_relaxed(0, sdma->base + SIRFSOC_DMA_LOOP_CTRL_ATLAS7);
+               writel_relaxed(0, sdma->base + SIRFSOC_DMA_VALID_ATLAS7);
+               break;
+       case SIRFSOC_DMA_VER_A6:
+               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);
+               writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID);
+               break;
+       default:
+               break;
        }
 
-       writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID);
-
        list_splice_tail_init(&schan->active, &schan->free);
        list_splice_tail_init(&schan->queued, &schan->free);
 
@@ -338,13 +498,25 @@ static int sirfsoc_dma_pause_chan(struct dma_chan *chan)
 
        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
+       switch (sdma->type) {
+       case SIRFSOC_DMA_VER_A7V1:
                writel_relaxed((1 << cid) | 1 << (cid + 16),
-                       sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL_CLR);
+                              sdma->base +
+                              SIRFSOC_DMA_CH_LOOP_CTRL_CLR_ATLAS7);
+               break;
+       case SIRFSOC_DMA_VER_A7V2:
+               writel_relaxed(0, sdma->base + SIRFSOC_DMA_LOOP_CTRL_ATLAS7);
+               break;
+       case SIRFSOC_DMA_VER_A6:
+               writel_relaxed(readl_relaxed(sdma->base +
+                                            SIRFSOC_DMA_CH_LOOP_CTRL) &
+                              ~((1 << cid) | 1 << (cid + 16)),
+                              sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL);
+               break;
+
+       default:
+               break;
+       }
 
        spin_unlock_irqrestore(&schan->lock, flags);
 
@@ -359,14 +531,25 @@ static int sirfsoc_dma_resume_chan(struct dma_chan *chan)
        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
+       switch (sdma->type) {
+       case SIRFSOC_DMA_VER_A7V1:
                writel_relaxed((1 << cid) | 1 << (cid + 16),
-                       sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL);
+                              sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL_ATLAS7);
+               break;
+       case SIRFSOC_DMA_VER_A7V2:
+               writel_relaxed(0x10001,
+                              sdma->base + SIRFSOC_DMA_LOOP_CTRL_ATLAS7);
+               break;
+       case SIRFSOC_DMA_VER_A6:
+               writel_relaxed(readl_relaxed(sdma->base +
+                                            SIRFSOC_DMA_CH_LOOP_CTRL) |
+                              ((1 << cid) | 1 << (cid + 16)),
+                              sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL);
+               break;
+
+       default:
+               break;
+       }
 
        spin_unlock_irqrestore(&schan->lock, flags);
 
@@ -473,14 +656,31 @@ sirfsoc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
 
        spin_lock_irqsave(&schan->lock, flags);
 
-       sdesc = list_first_entry(&schan->active, struct sirfsoc_dma_desc,
-                       node);
-       dma_request_bytes = (sdesc->xlen + 1) * (sdesc->ylen + 1) *
-               (sdesc->width * SIRFSOC_DMA_WORD_LEN);
+       if (list_empty(&schan->active)) {
+               ret = dma_cookie_status(chan, cookie, txstate);
+               dma_set_residue(txstate, 0);
+               spin_unlock_irqrestore(&schan->lock, flags);
+               return ret;
+       }
+       sdesc = list_first_entry(&schan->active, struct sirfsoc_dma_desc, node);
+       if (sdesc->cyclic)
+               dma_request_bytes = (sdesc->xlen + 1) * (sdesc->ylen + 1) *
+                       (sdesc->width * SIRFSOC_DMA_WORD_LEN);
+       else
+               dma_request_bytes = sdesc->xlen * SIRFSOC_DMA_WORD_LEN;
 
        ret = dma_cookie_status(chan, cookie, txstate);
-       dma_pos = readl_relaxed(sdma->base + cid * 0x10 + SIRFSOC_DMA_CH_ADDR)
-               << 2;
+
+       if (sdma->type == SIRFSOC_DMA_VER_A7V2)
+               cid = 0;
+
+       if (sdma->type == SIRFSOC_DMA_VER_A7V2) {
+               dma_pos = readl_relaxed(sdma->base + SIRFSOC_DMA_CUR_DATA_ADDR);
+       } else {
+               dma_pos = readl_relaxed(
+                       sdma->base + cid * 0x10 + SIRFSOC_DMA_CH_ADDR) << 2;
+       }
+
        residue = dma_request_bytes - (dma_pos - sdesc->addr);
        dma_set_residue(txstate, residue);
 
@@ -647,6 +847,7 @@ static int sirfsoc_dma_probe(struct platform_device *op)
        struct dma_device *dma;
        struct sirfsoc_dma *sdma;
        struct sirfsoc_dma_chan *schan;
+       struct sirfsoc_dmadata *data;
        struct resource res;
        ulong regs_start, regs_size;
        u32 id;
@@ -657,9 +858,11 @@ static int sirfsoc_dma_probe(struct platform_device *op)
                dev_err(dev, "Memory exhausted!\n");
                return -ENOMEM;
        }
-
-       if (of_device_is_compatible(dn, "sirf,marco-dmac"))
-               sdma->is_marco = true;
+       data = (struct sirfsoc_dmadata *)
+               (of_match_device(op->dev.driver->of_match_table,
+                                &op->dev)->data);
+       sdma->exec_desc = data->exec;
+       sdma->type = data->type;
 
        if (of_property_read_u32(dn, "cell-index", &id)) {
                dev_err(dev, "Fail to get DMAC index\n");
@@ -816,6 +1019,8 @@ static int sirfsoc_dma_pm_suspend(struct device *dev)
        struct sirfsoc_dma_chan *schan;
        int ch;
        int ret;
+       int count;
+       u32 int_offset;
 
        /*
         * if we were runtime-suspended before, resume to enable clock
@@ -827,11 +1032,19 @@ static int sirfsoc_dma_pm_suspend(struct device *dev)
                        return ret;
        }
 
+       if (sdma->type == SIRFSOC_DMA_VER_A7V2) {
+               count = 1;
+               int_offset = SIRFSOC_DMA_INT_EN_ATLAS7;
+       } else {
+               count = SIRFSOC_DMA_CHANNELS;
+               int_offset = SIRFSOC_DMA_INT_EN;
+       }
+
        /*
         * DMA controller will lose all registers while suspending
         * so we need to save registers for active channels
         */
-       for (ch = 0; ch < SIRFSOC_DMA_CHANNELS; ch++) {
+       for (ch = 0; ch < count; ch++) {
                schan = &sdma->channels[ch];
                if (list_empty(&schan->active))
                        continue;
@@ -841,7 +1054,7 @@ static int sirfsoc_dma_pm_suspend(struct device *dev)
                save->ctrl[ch] = readl_relaxed(sdma->base +
                        ch * 0x10 + SIRFSOC_DMA_CH_CTRL);
        }
-       save->interrupt_en = readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN);
+       save->interrupt_en = readl_relaxed(sdma->base + int_offset);
 
        /* Disable clock */
        sirfsoc_dma_runtime_suspend(dev);
@@ -857,14 +1070,27 @@ static int sirfsoc_dma_pm_resume(struct device *dev)
        struct sirfsoc_dma_chan *schan;
        int ch;
        int ret;
+       int count;
+       u32 int_offset;
+       u32 width_offset;
 
        /* Enable clock before accessing register */
        ret = sirfsoc_dma_runtime_resume(dev);
        if (ret < 0)
                return ret;
 
-       writel_relaxed(save->interrupt_en, sdma->base + SIRFSOC_DMA_INT_EN);
-       for (ch = 0; ch < SIRFSOC_DMA_CHANNELS; ch++) {
+       if (sdma->type == SIRFSOC_DMA_VER_A7V2) {
+               count = 1;
+               int_offset = SIRFSOC_DMA_INT_EN_ATLAS7;
+               width_offset = SIRFSOC_DMA_WIDTH_ATLAS7;
+       } else {
+               count = SIRFSOC_DMA_CHANNELS;
+               int_offset = SIRFSOC_DMA_INT_EN;
+               width_offset = SIRFSOC_DMA_WIDTH_0;
+       }
+
+       writel_relaxed(save->interrupt_en, sdma->base + int_offset);
+       for (ch = 0; ch < count; ch++) {
                schan = &sdma->channels[ch];
                if (list_empty(&schan->active))
                        continue;
@@ -872,15 +1098,21 @@ static int sirfsoc_dma_pm_resume(struct device *dev)
                        struct sirfsoc_dma_desc,
                        node);
                writel_relaxed(sdesc->width,
-                       sdma->base + SIRFSOC_DMA_WIDTH_0 + ch * 4);
+                       sdma->base + width_offset + ch * 4);
                writel_relaxed(sdesc->xlen,
                        sdma->base + ch * 0x10 + SIRFSOC_DMA_CH_XLEN);
                writel_relaxed(sdesc->ylen,
                        sdma->base + ch * 0x10 + SIRFSOC_DMA_CH_YLEN);
                writel_relaxed(save->ctrl[ch],
                        sdma->base + ch * 0x10 + SIRFSOC_DMA_CH_CTRL);
-               writel_relaxed(sdesc->addr >> 2,
-                       sdma->base + ch * 0x10 + SIRFSOC_DMA_CH_ADDR);
+               if (sdma->type == SIRFSOC_DMA_VER_A7V2) {
+                       writel_relaxed(sdesc->addr,
+                               sdma->base + SIRFSOC_DMA_CH_ADDR);
+               } else {
+                       writel_relaxed(sdesc->addr >> 2,
+                               sdma->base + ch * 0x10 + SIRFSOC_DMA_CH_ADDR);
+
+               }
        }
 
        /* if we were runtime-suspended before, suspend again */
@@ -896,11 +1128,28 @@ static const struct dev_pm_ops sirfsoc_dma_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(sirfsoc_dma_pm_suspend, sirfsoc_dma_pm_resume)
 };
 
+struct sirfsoc_dmadata sirfsoc_dmadata_a6 = {
+       .exec = sirfsoc_dma_execute_hw_a6,
+       .type = SIRFSOC_DMA_VER_A6,
+};
+
+struct sirfsoc_dmadata sirfsoc_dmadata_a7v1 = {
+       .exec = sirfsoc_dma_execute_hw_a7v1,
+       .type = SIRFSOC_DMA_VER_A7V1,
+};
+
+struct sirfsoc_dmadata sirfsoc_dmadata_a7v2 = {
+       .exec = sirfsoc_dma_execute_hw_a7v2,
+       .type = SIRFSOC_DMA_VER_A7V2,
+};
+
 static const struct of_device_id sirfsoc_dma_match[] = {
-       { .compatible = "sirf,prima2-dmac", },
-       { .compatible = "sirf,marco-dmac", },
+       { .compatible = "sirf,prima2-dmac", .data = &sirfsoc_dmadata_a6,},
+       { .compatible = "sirf,atlas7-dmac", .data = &sirfsoc_dmadata_a7v1,},
+       { .compatible = "sirf,atlas7-dmac-v2", .data = &sirfsoc_dmadata_a7v2,},
        {},
 };
+MODULE_DEVICE_TABLE(of, sirfsoc_dma_match);
 
 static struct platform_driver sirfsoc_dma_driver = {
        .probe          = sirfsoc_dma_probe,
@@ -925,7 +1174,7 @@ static void __exit sirfsoc_dma_exit(void)
 subsys_initcall(sirfsoc_dma_init);
 module_exit(sirfsoc_dma_exit);
 
-MODULE_AUTHOR("Rongjun Ying <rongjun.ying@csr.com>, "
-       "Barry Song <baohua.song@csr.com>");
+MODULE_AUTHOR("Rongjun Ying <rongjun.ying@csr.com>");
+MODULE_AUTHOR("Barry Song <baohua.song@csr.com>");
 MODULE_DESCRIPTION("SIRFSOC DMA control driver");
 MODULE_LICENSE("GPL v2");