>From 8c0f4490d93b67326ff24f6ce1c7e925b08d96b3 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <adrian.hun...@nokia.com>
Date: Mon, 22 Nov 2010 11:32:48 +0200
Subject: [PATCH 1/2] OMAP: DMA: prevent races while setting M idle mode to 
nostandby

In a couple of OMAP errata cases, sDMA M idle mode must be
set temporarily to nostandby.  If two DMA users were to do
that at the same time, a race condition would arise.
Prevent that by using a spin lock and counting up/down the
number of times nostandby is set/reset.

Signed-off-by: Adrian Hunter <adrian.hun...@nokia.com>
---
 arch/arm/plat-omap/dma.c |   59 ++++++++++++++++++++++++++++++++++-----------
 1 files changed, 44 insertions(+), 15 deletions(-)

diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c
index a863f55..6158c99 100644
--- a/arch/arm/plat-omap/dma.c
+++ b/arch/arm/plat-omap/dma.c
@@ -139,6 +139,9 @@ static spinlock_t dma_chan_lock;
 static struct omap_dma_lch *dma_chan;
 static void __iomem *omap_dma_base;
 
+static u32 midlemode_saved;
+static int midlemode_save_cnt;
+
 static const u8 omap1_dma_irq[OMAP1_LOGICAL_DMA_CH_COUNT] = {
        INT_DMA_CH0_6, INT_DMA_CH1_7, INT_DMA_CH2_8, INT_DMA_CH3,
        INT_DMA_CH4, INT_DMA_CH5, INT_1610_DMA_CH6, INT_1610_DMA_CH7,
@@ -1016,6 +1019,41 @@ void omap_start_dma(int lch)
 }
 EXPORT_SYMBOL(omap_start_dma);
 
+static void midlemode_nostandby(void)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dma_chan_lock, flags);
+       if (!midlemode_save_cnt) {
+               u32 l;
+
+               midlemode_saved = dma_read(OCP_SYSCONFIG);
+               l = midlemode_saved;
+               l &= ~DMA_SYSCONFIG_MIDLEMODE_MASK;
+               l |= DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_NO_IDLE);
+               dma_write(l, OCP_SYSCONFIG);
+       }
+       midlemode_save_cnt += 1;
+       spin_unlock_irqrestore(&dma_chan_lock, flags);
+}
+
+static void midlemode_restore(void)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dma_chan_lock, flags);
+       midlemode_save_cnt -= 1;
+       if (!midlemode_save_cnt) {
+               u32 l;
+
+               l = dma_read(OCP_SYSCONFIG);
+               l &= ~DMA_SYSCONFIG_MIDLEMODE_MASK;
+               l |= midlemode_saved & DMA_SYSCONFIG_MIDLEMODE_MASK;
+               dma_write(l, OCP_SYSCONFIG);
+       }
+       spin_unlock_irqrestore(&dma_chan_lock, flags);
+}
+
 void omap_stop_dma(int lch)
 {
        u32 l;
@@ -1028,16 +1066,10 @@ void omap_stop_dma(int lch)
        /* OMAP3 Errata i541: sDMA FIFO draining does not finish */
        if (cpu_is_omap34xx() && (l & OMAP_DMA_CCR_SEL_SRC_DST_SYNC)) {
                int i = 0;
-               u32 sys_cf;
 
                /* Configure No-Standby */
-               l = dma_read(OCP_SYSCONFIG);
-               sys_cf = l;
-               l &= ~DMA_SYSCONFIG_MIDLEMODE_MASK;
-               l |= DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_NO_IDLE);
-               dma_write(l , OCP_SYSCONFIG);
+               midlemode_nostandby();
 
-               l = dma_read(CCR(lch));
                l &= ~OMAP_DMA_CCR_EN;
                dma_write(l, CCR(lch));
 
@@ -1053,7 +1085,7 @@ void omap_stop_dma(int lch)
                        printk(KERN_ERR "DMA drain did not complete on "
                                        "lch %d\n", lch);
                /* Restore OCP_SYSCONFIG */
-               dma_write(sys_cf, OCP_SYSCONFIG);
+               midlemode_restore();
        } else {
                l &= ~OMAP_DMA_CCR_EN;
                dma_write(l, CCR(lch));
@@ -1711,7 +1743,6 @@ int omap_stop_dma_chain_transfers(int chain_id)
 {
        int *channels;
        u32 l, i;
-       u32 sys_cf;
 
        /* Check for input params */
        if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
@@ -1730,11 +1761,9 @@ int omap_stop_dma_chain_transfers(int chain_id)
         * DMA Errata:
         * Special programming model needed to disable DMA before end of block
         */
-       sys_cf = dma_read(OCP_SYSCONFIG);
-       l = sys_cf;
-       /* Middle mode reg set no Standby */
-       l &= ~((1 << 12)|(1 << 13));
-       dma_write(l, OCP_SYSCONFIG);
+
+       /* M idle mode reg set no Standby */
+       midlemode_nostandby();
 
        for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) {
 
@@ -1754,7 +1783,7 @@ int omap_stop_dma_chain_transfers(int chain_id)
        OMAP_DMA_CHAIN_QINIT(chain_id);
 
        /* Errata - put in the old value */
-       dma_write(sys_cf, OCP_SYSCONFIG);
+       midlemode_restore();
 
        return 0;
 }
-- 
1.7.0.4

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

Reply via email to