diff mbox

[1/2] OMAP: DMA: prevent races while setting M idle mode to nostandby

Message ID 20101130132347.13286.40388.sendpatchset@ahunter-work.research.nokia.com (mailing list archive)
State Superseded, archived
Delegated to: Tony Lindgren
Headers show

Commit Message

Adrian Hunter Nov. 30, 2010, 1:23 p.m. UTC
None
diff mbox

Patch

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;
 }