[PATCH 2/3] OMAP2+: DMA: prevent races while setting M idle mode to nostandby
G, Manjunath Kondaiah
manjugk at ti.com
Thu Feb 24 05:14:34 EST 2011
If two DMA users tries to set no mstandby mode 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.
Initial patch is created by Adrian Hunter <adrian.hunter at nokia.com>
https://patchwork.kernel.org/patch/366831/
Patch reworked to use API implemented at hwmod layer.
Signed-off-by: G, Manjunath Kondaiah <manjugk at ti.com>
Signed-off-by: Adrian Hunter <adrian.hunter at nokia.com>
---
arch/arm/mach-omap1/dma.c | 1 +
arch/arm/mach-omap2/dma.c | 16 +++++++++++++
arch/arm/plat-omap/dma.c | 40 +++++++++++++++++++--------------
arch/arm/plat-omap/include/plat/dma.h | 1 +
4 files changed, 41 insertions(+), 17 deletions(-)
diff --git a/arch/arm/mach-omap1/dma.c b/arch/arm/mach-omap1/dma.c
index d855934..fa2d1b0 100644
--- a/arch/arm/mach-omap1/dma.c
+++ b/arch/arm/mach-omap1/dma.c
@@ -351,6 +351,7 @@ static int __init omap1_system_dma_init(void)
p->dma_write = dma_write;
p->dma_read = dma_read;
p->disable_irq_lch = NULL;
+ p->midlemode = NULL;
p->errata = configure_dma_errata();
diff --git a/arch/arm/mach-omap2/dma.c b/arch/arm/mach-omap2/dma.c
index 34922b2..6e12e71 100644
--- a/arch/arm/mach-omap2/dma.c
+++ b/arch/arm/mach-omap2/dma.c
@@ -36,7 +36,9 @@
static u32 errata;
static u8 dma_stride;
+static u32 midlemode_save_cnt;
+static struct platform_device *pdev;
static struct omap_dma_dev_attr *d;
static enum omap_reg_offsets dma_common_ch_start, dma_common_ch_end;
@@ -117,6 +119,18 @@ static inline u32 dma_read(int reg, int lch)
return val;
}
+static void midlemode_nostandby(bool nostandby)
+{
+ /* TODO: midlemode_save_cnt can be moved to hwmod layer? */
+ if (nostandby) {
+ omap_device_require_no_mstandby(pdev);
+ midlemode_save_cnt += 1;
+ } else {
+ omap_device_release_no_mstandby(pdev);
+ midlemode_save_cnt -= 1;
+ }
+}
+
static inline void omap2_disable_irq_lch(int lch)
{
u32 val;
@@ -253,6 +267,7 @@ static int __init omap2_system_dma_init_dev(struct omap_hwmod *oh, void *unused)
p->clear_dma = omap2_clear_dma;
p->dma_write = dma_write;
p->dma_read = dma_read;
+ p->midlemode = midlemode_nostandby;
p->clear_lch_regs = NULL;
@@ -286,6 +301,7 @@ static int __init omap2_system_dma_init_dev(struct omap_hwmod *oh, void *unused)
dev_err(&od->pdev.dev, "%s: kzalloc fail\n", __func__);
return -ENOMEM;
}
+ pdev = &od->pdev;
return 0;
}
diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c
index 8536308..84879eb 100644
--- a/arch/arm/plat-omap/dma.c
+++ b/arch/arm/plat-omap/dma.c
@@ -38,8 +38,9 @@
#include <asm/system.h>
#include <mach/hardware.h>
-#include <plat/dma.h>
+#include <plat/dma.h>
+#include <plat/omap_device.h>
#include <plat/tc.h>
#undef DEBUG
@@ -924,6 +925,7 @@ EXPORT_SYMBOL(omap_start_dma);
void omap_stop_dma(int lch)
{
u32 l;
+ unsigned long flags;
/* Disable all interrupts on the channel */
if (cpu_class_is_omap1())
@@ -933,14 +935,13 @@ void omap_stop_dma(int lch)
if (IS_DMA_ERRATA(DMA_ERRATA_i541) &&
(l & OMAP_DMA_CCR_SEL_SRC_DST_SYNC)) {
int i = 0;
- u32 sys_cf;
/* Configure No-Standby */
- l = p->dma_read(OCP_SYSCONFIG, lch);
- sys_cf = l;
- l &= ~DMA_SYSCONFIG_MIDLEMODE_MASK;
- l |= DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_NO_IDLE);
- p->dma_write(l , OCP_SYSCONFIG, 0);
+ if (p->midlemode) {
+ spin_lock_irqsave(&dma_chan_lock, flags);
+ p->midlemode(true);
+ spin_unlock_irqrestore(&dma_chan_lock, flags);
+ }
l = p->dma_read(CCR, lch);
l &= ~OMAP_DMA_CCR_EN;
@@ -958,7 +959,11 @@ void omap_stop_dma(int lch)
printk(KERN_ERR "DMA drain did not complete on "
"lch %d\n", lch);
/* Restore OCP_SYSCONFIG */
- p->dma_write(sys_cf, OCP_SYSCONFIG, lch);
+ if (p->midlemode) {
+ spin_lock_irqsave(&dma_chan_lock, flags);
+ p->midlemode(false);
+ spin_unlock_irqrestore(&dma_chan_lock, flags);
+ }
} else {
l &= ~OMAP_DMA_CCR_EN;
p->dma_write(l, CCR, lch);
@@ -1610,7 +1615,7 @@ int omap_stop_dma_chain_transfers(int chain_id)
{
int *channels;
u32 l, i;
- u32 sys_cf = 0;
+ unsigned long flags;
/* Check for input params */
if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
@@ -1625,12 +1630,10 @@ int omap_stop_dma_chain_transfers(int chain_id)
}
channels = dma_linked_lch[chain_id].linked_dmach_q;
- if (IS_DMA_ERRATA(DMA_ERRATA_i88)) {
- sys_cf = p->dma_read(OCP_SYSCONFIG, 0);
- l = sys_cf;
- /* Middle mode reg set no Standby */
- l &= ~((1 << 12)|(1 << 13));
- p->dma_write(l, OCP_SYSCONFIG, 0);
+ if (IS_DMA_ERRATA(DMA_ERRATA_i88) && p->midlemode) {
+ spin_lock_irqsave(&dma_chan_lock, flags);
+ p->midlemode(true);
+ spin_unlock_irqrestore(&dma_chan_lock, flags);
}
for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) {
@@ -1650,8 +1653,11 @@ int omap_stop_dma_chain_transfers(int chain_id)
/* Reset the Queue pointers */
OMAP_DMA_CHAIN_QINIT(chain_id);
- if (IS_DMA_ERRATA(DMA_ERRATA_i88))
- p->dma_write(sys_cf, OCP_SYSCONFIG, 0);
+ if (IS_DMA_ERRATA(DMA_ERRATA_i88 && p->midlemode)) {
+ spin_lock_irqsave(&dma_chan_lock, flags);
+ p->midlemode(false);
+ spin_unlock_irqrestore(&dma_chan_lock, flags);
+ }
return 0;
}
diff --git a/arch/arm/plat-omap/include/plat/dma.h b/arch/arm/plat-omap/include/plat/dma.h
index d1c916f..b20dc5e 100644
--- a/arch/arm/plat-omap/include/plat/dma.h
+++ b/arch/arm/plat-omap/include/plat/dma.h
@@ -435,6 +435,7 @@ struct omap_system_dma_plat_info {
void (*clear_dma)(int lch);
void (*dma_write)(u32 val, int reg, int lch);
u32 (*dma_read)(int reg, int lch);
+ void (*midlemode)(bool nostandby);
};
extern void omap_set_dma_priority(int lch, int dst_port, int priority);
--
1.7.1
More information about the linux-arm-kernel
mailing list