Merge branch 'msm-mmc_sdcc' of git://codeaurora.org/quic/kernel/dwalker/linux-msm
[sfrench/cifs-2.6.git] / arch / arm / mach-msm / dma.c
index f5420f9585c59ec504dd85325c55e42141e50f96..d029d1f5f9e2c16343d9650afe4190531d1b635d 100644 (file)
@@ -13,6 +13,8 @@
  *
  */
 
+#include <linux/clk.h>
+#include <linux/err.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
 #include <mach/dma.h>
@@ -26,6 +28,7 @@ enum {
 };
 
 static DEFINE_SPINLOCK(msm_dmov_lock);
+static struct clk *msm_dmov_clk;
 static unsigned int channel_active;
 static struct list_head ready_commands[MSM_DMOV_CHANNEL_COUNT];
 static struct list_head active_commands[MSM_DMOV_CHANNEL_COUNT];
@@ -54,6 +57,9 @@ void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd)
        unsigned int status;
 
        spin_lock_irqsave(&msm_dmov_lock, irq_flags);
+       if (!channel_active)
+               clk_enable(msm_dmov_clk);
+       dsb();
        status = readl(DMOV_STATUS(id));
        if (list_empty(&ready_commands[id]) &&
                (status & DMOV_STATUS_CMD_PTR_RDY)) {
@@ -63,6 +69,8 @@ void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd)
                        writel(DMOV_CONFIG_IRQ_EN, DMOV_CONFIG(id));
                }
 #endif
+               if (cmd->execute_func)
+                       cmd->execute_func(cmd);
                PRINT_IO("msm_dmov_enqueue_cmd(%d), start command, status %x\n", id, status);
                list_add_tail(&cmd->list, &active_commands[id]);
                if (!channel_active)
@@ -70,6 +78,8 @@ void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd)
                channel_active |= 1U << id;
                writel(cmd->cmdptr, DMOV_CMD_PTR(id));
        } else {
+               if (!channel_active)
+                       clk_disable(msm_dmov_clk);
                if (list_empty(&active_commands[id]))
                        PRINT_ERROR("msm_dmov_enqueue_cmd(%d), error datamover stalled, status %x\n", id, status);
 
@@ -108,6 +118,7 @@ int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr)
 
        cmd.dmov_cmd.cmdptr = cmdptr;
        cmd.dmov_cmd.complete_func = dmov_exec_cmdptr_complete_func;
+       cmd.dmov_cmd.execute_func = NULL;
        cmd.id = id;
        init_completion(&cmd.complete);
 
@@ -165,6 +176,7 @@ static irqreturn_t msm_datamover_irq_handler(int irq, void *dev_id)
                                        "for %p, result %x\n", id, cmd, ch_result);
                                if (cmd) {
                                        list_del(&cmd->list);
+                                       dsb();
                                        cmd->complete_func(cmd, ch_result, NULL);
                                }
                        }
@@ -181,6 +193,7 @@ static irqreturn_t msm_datamover_irq_handler(int irq, void *dev_id)
                                PRINT_FLOW("msm_datamover_irq_handler id %d, flush, result %x, flush0 %x\n", id, ch_result, errdata.flush[0]);
                                if (cmd) {
                                        list_del(&cmd->list);
+                                       dsb();
                                        cmd->complete_func(cmd, ch_result, &errdata);
                                }
                        }
@@ -198,6 +211,7 @@ static irqreturn_t msm_datamover_irq_handler(int irq, void *dev_id)
                                PRINT_ERROR("msm_datamover_irq_handler id %d, error, result %x, flush0 %x\n", id, ch_result, errdata.flush[0]);
                                if (cmd) {
                                        list_del(&cmd->list);
+                                       dsb();
                                        cmd->complete_func(cmd, ch_result, &errdata);
                                }
                                /* this does not seem to work, once we get an error */
@@ -210,6 +224,8 @@ static irqreturn_t msm_datamover_irq_handler(int irq, void *dev_id)
                                cmd = list_entry(ready_commands[id].next, typeof(*cmd), list);
                                list_del(&cmd->list);
                                list_add_tail(&cmd->list, &active_commands[id]);
+                               if (cmd->execute_func)
+                                       cmd->execute_func(cmd);
                                PRINT_FLOW("msm_datamover_irq_handler id %d, start command\n", id);
                                writel(cmd->cmdptr, DMOV_CMD_PTR(id));
                        }
@@ -219,8 +235,10 @@ static irqreturn_t msm_datamover_irq_handler(int irq, void *dev_id)
                PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status);
        }
 
-       if (!channel_active)
-               disable_irq(INT_ADM_AARM);
+       if (!channel_active) {
+               disable_irq_nosync(INT_ADM_AARM);
+               clk_disable(msm_dmov_clk);
+       }
 
        spin_unlock_irqrestore(&msm_dmov_lock, irq_flags);
        return IRQ_HANDLED;
@@ -230,11 +248,17 @@ static int __init msm_init_datamover(void)
 {
        int i;
        int ret;
+       struct clk *clk;
+
        for (i = 0; i < MSM_DMOV_CHANNEL_COUNT; i++) {
                INIT_LIST_HEAD(&ready_commands[i]);
                INIT_LIST_HEAD(&active_commands[i]);
                writel(DMOV_CONFIG_IRQ_EN | DMOV_CONFIG_FORCE_TOP_PTR_RSLT | DMOV_CONFIG_FORCE_FLUSH_RSLT, DMOV_CONFIG(i));
        }
+       clk = clk_get(NULL, "adm_clk");
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+       msm_dmov_clk = clk;
        ret = request_irq(INT_ADM_AARM, msm_datamover_irq_handler, 0, "msmdatamover", NULL);
        if (ret)
                return ret;