ARM: mvebu: reserve the first 10 KB of each memory bank for suspend/resume
authorThomas Petazzoni <thomas.petazzoni@free-electrons.com>
Fri, 21 Nov 2014 16:00:07 +0000 (17:00 +0100)
committerJason Cooper <jason@lakedaemon.net>
Sun, 30 Nov 2014 16:40:13 +0000 (16:40 +0000)
When going out of suspend to RAM, the Marvell EBU platforms go through
the bootloader, which re-configures the DRAM controller. To achieve
this, the bootloader executes a piece of code called the "DDR3
training code". It does some reads/writes to the memory to find out
the optimal timings for the memory chip being used.

This has the nasty side effect that the first 10 KB of each DRAM
chip-select are overwritten by the bootloader when exiting the suspend
to RAM state.

Therefore, this commit implements the ->reserve() hook for the 'struct
machine_desc' used on Armada XP, to reserve the 10 KB of each DRAM
chip-select using the memblock API.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Acked-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Link: https://lkml.kernel.org/r/1416585613-2113-11-git-send-email-thomas.petazzoni@free-electrons.com
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
arch/arm/mach-mvebu/board-v7.c

index 6478626..bc52231 100644 (file)
 #include <linux/init.h>
 #include <linux/clk-provider.h>
 #include <linux/of_address.h>
+#include <linux/of_fdt.h>
 #include <linux/of_platform.h>
 #include <linux/io.h>
 #include <linux/clocksource.h>
 #include <linux/dma-mapping.h>
+#include <linux/memblock.h>
 #include <linux/mbus.h>
 #include <linux/signal.h>
 #include <linux/slab.h>
@@ -56,6 +58,54 @@ void __iomem *mvebu_get_scu_base(void)
        return scu_base;
 }
 
+/*
+ * When returning from suspend, the platform goes through the
+ * bootloader, which executes its DDR3 training code. This code has
+ * the unfortunate idea of using the first 10 KB of each DRAM bank to
+ * exercise the RAM and calculate the optimal timings. Therefore, this
+ * area of RAM is overwritten, and shouldn't be used by the kernel if
+ * suspend/resume is supported.
+ */
+
+#ifdef CONFIG_SUSPEND
+#define MVEBU_DDR_TRAINING_AREA_SZ (10 * SZ_1K)
+static int __init mvebu_scan_mem(unsigned long node, const char *uname,
+                                int depth, void *data)
+{
+       const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+       const __be32 *reg, *endp;
+       int l;
+
+       if (type == NULL || strcmp(type, "memory"))
+               return 0;
+
+       reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
+       if (reg == NULL)
+               reg = of_get_flat_dt_prop(node, "reg", &l);
+       if (reg == NULL)
+               return 0;
+
+       endp = reg + (l / sizeof(__be32));
+       while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
+               u64 base, size;
+
+               base = dt_mem_next_cell(dt_root_addr_cells, &reg);
+               size = dt_mem_next_cell(dt_root_size_cells, &reg);
+
+               memblock_reserve(base, MVEBU_DDR_TRAINING_AREA_SZ);
+       }
+
+       return 0;
+}
+
+static void __init mvebu_memblock_reserve(void)
+{
+       of_scan_flat_dt(mvebu_scan_mem, NULL);
+}
+#else
+static void __init mvebu_memblock_reserve(void) {}
+#endif
+
 /*
  * Early versions of Armada 375 SoC have a bug where the BootROM
  * leaves an external data abort pending. The kernel is hit by this
@@ -210,6 +260,7 @@ DT_MACHINE_START(ARMADA_370_XP_DT, "Marvell Armada 370/XP (Device Tree)")
        .init_machine   = mvebu_dt_init,
        .init_irq       = mvebu_init_irq,
        .restart        = mvebu_restart,
+       .reserve        = mvebu_memblock_reserve,
        .dt_compat      = armada_370_xp_dt_compat,
 MACHINE_END