[PATCH v4 2/4] OMAP2+: DMA: prevent races while setting M idle mode to nostandby
From: G, Manjunath Kondaiah <hidden>
Date: 2011-03-28 14:27:00
Also in:
linux-omap
Subsystem:
arm port, omap1 support, omap2+ support, the rest · Maintainers:
Russell King, Aaro Koskinen, Janusz Krzysztofik, Andreas Kemnade, Kevin Hilman, Roger Quadros, Tony Lindgren, Linus Torvalds
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 [off-list ref] https://patchwork.kernel.org/patch/366831/ Patch reworked to use API implemented at hwmod layer. Signed-off-by: G, Manjunath Kondaiah <redacted> Signed-off-by: Adrian Hunter <redacted> --- 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 2ec3b5d..5af9bb2 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