ARM: OMAP4: Fix errata i688 with MPU interconnect barriers.
authorSantosh Shilimkar <santosh.shilimkar@ti.com>
Sun, 26 Jun 2011 01:04:31 +0000 (18:04 -0700)
committerKevin Hilman <khilman@ti.com>
Thu, 8 Dec 2011 19:29:01 +0000 (11:29 -0800)
On OMAP4 SOC, intecronnects has many write buffers in the async bridges
and they need to be drained before CPU enters into standby state.

Patch 'OMAP4: PM: Add CPUX OFF mode support' added CPU PM support
but OMAP errata i688 (Async Bridge Corruption) needs to be taken
care to avoid issues like system freeze, CPU deadlocks, random
crashes with register accesses, synchronisation loss on initiators
operating on both interconnect port simultaneously.

As per the errata, if a data is stalled inside asynchronous bridge
because of back pressure, it may be accepted multiple times, creating
pointer misalignment that will corrupt next transfers on that data
path until next reset of the system (No recovery procedure once
the issue is hit, the path remains consistently broken).
Async bridge can be found on path between MPU to EMIF and
MPU to L3 interconnect. This situation can happen only when the
idle is initiated by a Master Request Disconnection (which is
trigged by software when executing WFI on CPU).

The work-around for this errata needs all the initiators
connected through async bridge must ensure that data path
is properly drained before issuing WFI. This condition will be
met if one Strongly ordered access is performed to the
target right before executing the WFI. In MPU case, L3 T2ASYNC
FIFO and DDR T2ASYNC FIFO needs to be drained. IO barrier ensure
that there is no synchronisation loss on initiators operating
on both interconnect port simultaneously.

Thanks to Russell for a tip to conver assembly function to
C fuction there by reducing 40 odd lines of code from the patch.

Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Signed-off-by: Richard Woodruff <r-woodruff2@ti.com>
Acked-by: Jean Pihet <j-pihet@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
Tested-by: Vishwanath BS <vishwanath.bs@ti.com>
Signed-off-by: Kevin Hilman <khilman@ti.com>
arch/arm/mach-omap2/Kconfig
arch/arm/mach-omap2/include/mach/barriers.h [new file with mode: 0644]
arch/arm/mach-omap2/io.c
arch/arm/mach-omap2/omap4-common.c
arch/arm/mach-omap2/sleep44xx.S
arch/arm/plat-omap/include/plat/sram.h
arch/arm/plat-omap/sram.c

index b6625130831d3f5378a4a0cf3d422dcc3e9c0870..50f43942c1aa8f5091b16c8a11669505f0aed1a8 100644 (file)
@@ -353,6 +353,27 @@ config OMAP3_SDRC_AC_TIMING
          wish to say no.  Selecting yes without understanding what is
          going on could result in system crashes;
 
+config OMAP4_ERRATA_I688
+       bool "OMAP4 errata: Async Bridge Corruption"
+       depends on ARCH_OMAP4
+       select ARCH_HAS_BARRIERS
+       help
+         If a data is stalled inside asynchronous bridge because of back
+         pressure, it may be accepted multiple times, creating pointer
+         misalignment that will corrupt next transfers on that data path
+         until next reset of the system (No recovery procedure once the
+         issue is hit, the path remains consistently broken). Async bridge
+         can be found on path between MPU to EMIF and MPU to L3 interconnect.
+         This situation can happen only when the idle is initiated by a
+         Master Request Disconnection (which is trigged by software when
+         executing WFI on CPU).
+         The work-around for this errata needs all the initiators connected
+         through async bridge must ensure that data path is properly drained
+         before issuing WFI. This condition will be met if one Strongly ordered
+         access is performed to the target right before executing the WFI.
+         In MPU case, L3 T2ASYNC FIFO and DDR T2ASYNC FIFO needs to be drained.
+         IO barrier ensure that there is no synchronisation loss on initiators
+         operating on both interconnect port simultaneously.
 endmenu
 
 endif
diff --git a/arch/arm/mach-omap2/include/mach/barriers.h b/arch/arm/mach-omap2/include/mach/barriers.h
new file mode 100644 (file)
index 0000000..4fa72c7
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * OMAP memory barrier header.
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *  Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *  Richard Woodruff <r-woodruff2@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MACH_BARRIERS_H
+#define __MACH_BARRIERS_H
+
+extern void omap_bus_sync(void);
+
+#define rmb()          dsb()
+#define wmb()          do { dsb(); outer_sync(); omap_bus_sync(); } while (0)
+#define mb()           wmb()
+
+#endif /* __MACH_BARRIERS_H */
index 3f565dd2ea8dd52dd0c889dcb963d6948a9b139b..65843390e7f00f3c3f751a64c5e5e0d5621b650f 100644 (file)
@@ -237,6 +237,15 @@ static struct map_desc omap44xx_io_desc[] __initdata = {
                .length         = L4_EMU_44XX_SIZE,
                .type           = MT_DEVICE,
        },
+#ifdef CONFIG_OMAP4_ERRATA_I688
+       {
+               .virtual        = OMAP4_SRAM_VA,
+               .pfn            = __phys_to_pfn(OMAP4_SRAM_PA),
+               .length         = PAGE_SIZE,
+               .type           = MT_MEMORY_SO,
+       },
+#endif
+
 };
 #endif
 
index 1b93d31fe8e9092045dc1fe5aef01f87ed5df755..bc16c818c6b72e77ae276976c887b7a85647a9df 100644 (file)
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
+#include <linux/memblock.h>
 
 #include <asm/hardware/gic.h>
 #include <asm/hardware/cache-l2x0.h>
+#include <asm/mach/map.h>
 
 #include <plat/irqs.h>
+#include <plat/sram.h>
 
 #include <mach/hardware.h>
 #include <mach/omap-wakeupgen.h>
@@ -33,6 +36,54 @@ static void __iomem *l2cache_base;
 
 static void __iomem *sar_ram_base;
 
+#ifdef CONFIG_OMAP4_ERRATA_I688
+/* Used to implement memory barrier on DRAM path */
+#define OMAP4_DRAM_BARRIER_VA                  0xfe600000
+
+void __iomem *dram_sync, *sram_sync;
+
+void omap_bus_sync(void)
+{
+       if (dram_sync && sram_sync) {
+               writel_relaxed(readl_relaxed(dram_sync), dram_sync);
+               writel_relaxed(readl_relaxed(sram_sync), sram_sync);
+               isb();
+       }
+}
+
+static int __init omap_barriers_init(void)
+{
+       struct map_desc dram_io_desc[1];
+       phys_addr_t paddr;
+       u32 size;
+
+       if (!cpu_is_omap44xx())
+               return -ENODEV;
+
+       size = ALIGN(PAGE_SIZE, SZ_1M);
+       paddr = memblock_alloc(size, SZ_1M);
+       if (!paddr) {
+               pr_err("%s: failed to reserve 4 Kbytes\n", __func__);
+               return -ENOMEM;
+       }
+       memblock_free(paddr, size);
+       memblock_remove(paddr, size);
+       dram_io_desc[0].virtual = OMAP4_DRAM_BARRIER_VA;
+       dram_io_desc[0].pfn = __phys_to_pfn(paddr);
+       dram_io_desc[0].length = size;
+       dram_io_desc[0].type = MT_MEMORY_SO;
+       iotable_init(dram_io_desc, ARRAY_SIZE(dram_io_desc));
+       dram_sync = (void __iomem *) dram_io_desc[0].virtual;
+       sram_sync = (void __iomem *) OMAP4_SRAM_VA;
+
+       pr_info("OMAP4: Map 0x%08llx to 0x%08lx for dram barrier\n",
+               (long long) paddr, dram_io_desc[0].virtual);
+
+       return 0;
+}
+core_initcall(omap_barriers_init);
+#endif
+
 void __init gic_init_irq(void)
 {
        void __iomem *omap_irq_base;
index 3154b63def351602e410684dd3fa3b4f970f0208..abd283400490396ea0c3e6f1da70dc1dda7425a3 100644 (file)
@@ -325,8 +325,16 @@ skip_l2en:
 ENDPROC(omap4_cpu_resume)
 #endif
 
+#ifndef CONFIG_OMAP4_ERRATA_I688
+ENTRY(omap_bus_sync)
+       mov     pc, lr
+ENDPROC(omap_bus_sync)
+#endif
+
 ENTRY(omap_do_wfi)
        stmfd   sp!, {lr}
+       /* Drain interconnect write buffers. */
+       bl omap_bus_sync
 
        /*
         * Execute an ISB instruction to ensure that all of the
index f500fc34d06595254fb8688cb7c633a7807c113d..75aa1b2bef519eaa8958c37a7050df66c022848a 100644 (file)
@@ -95,6 +95,10 @@ static inline void omap_push_sram_idle(void) {}
  */
 #define OMAP2_SRAM_PA          0x40200000
 #define OMAP3_SRAM_PA           0x40200000
+#ifdef CONFIG_OMAP4_ERRATA_I688
+#define OMAP4_SRAM_PA          0x40304000
+#define OMAP4_SRAM_VA          0xfe404000
+#else
 #define OMAP4_SRAM_PA          0x40300000
-
+#endif
 #endif
index 8b28664d1c62634b0d8ec68e62f1a23a5d06a27a..ad6a71a00cefaa8f54dd5c228cdba22f85333b01 100644 (file)
 #define OMAP1_SRAM_PA          0x20000000
 #define OMAP2_SRAM_PUB_PA      (OMAP2_SRAM_PA + 0xf800)
 #define OMAP3_SRAM_PUB_PA       (OMAP3_SRAM_PA + 0x8000)
+#ifdef CONFIG_OMAP4_ERRATA_I688
+#define OMAP4_SRAM_PUB_PA      OMAP4_SRAM_PA
+#else
 #define OMAP4_SRAM_PUB_PA      (OMAP4_SRAM_PA + 0x4000)
+#endif
 
 #if defined(CONFIG_ARCH_OMAP2PLUS)
 #define SRAM_BOOTLOADER_SZ     0x00
@@ -163,6 +167,10 @@ static void __init omap_map_sram(void)
        if (omap_sram_size == 0)
                return;
 
+#ifdef CONFIG_OMAP4_ERRATA_I688
+               omap_sram_start += PAGE_SIZE;
+               omap_sram_size -= SZ_16K;
+#endif
        if (cpu_is_omap34xx()) {
                /*
                 * SRAM must be marked as non-cached on OMAP3 since the