msm: 8974: Enable DONT_MAP_HOLE_AFTER_MEMBANK0

If this config option is enabled, the kernel does not map the
memory corresponding to the largest hole into the virtual
memory resulting in more lowmem. If multiple holes exist, the
largest hole in the first 256MB of the memory is not mapped.

In device tree based targets the kernel is no longer dependant
on the bootloader for the start and size of the memory holes.
Thus the meminfo is adjusted to reflect the memory hole, if it
is present, using the start and end of the memory hole, while
parsing the device tree.

For non-device tree based targets, the meminfo is used to find
the start and size of the memory hole. No adjustment is needed
to be made to the meminfo, as the hole is taken into account,
based on the memory information passed by the atags.

Change-Id: Ib2619f72ac2b4142330534c8bbe1c7d9f64ea38c
Signed-off-by: Neeti Desai <neetid@codeaurora.org>
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index a0868c7..42e52db 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -279,6 +279,7 @@
 	select MEMORY_HOLE_CARVEOUT
 	select MSM_RPM_STATS_LOG
 	select QMI_ENCDEC
+	select DONT_MAP_HOLE_AFTER_MEMBANK0
 
 config ARCH_MPQ8092
 	bool "MPQ8092"
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index b092a53..c47b688 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -71,10 +71,17 @@
 	.paddr_to_memtype = msm8974_paddr_to_memtype,
 };
 
-static void __init msm8974_early_memory(void)
+void __init msm_8974_reserve(void)
 {
 	reserve_info = &msm8974_reserve_info;
 	of_scan_flat_dt(dt_scan_for_memory_reserve, msm8974_reserve_table);
+	msm_reserve();
+}
+
+static void __init msm8974_early_memory(void)
+{
+	reserve_info = &msm8974_reserve_info;
+	of_scan_flat_dt(dt_scan_for_memory_hole, msm8974_reserve_table);
 }
 
 #define BIMC_BASE	0xfc380000
@@ -389,7 +396,7 @@
 	.handle_irq = gic_handle_irq,
 	.timer = &msm_dt_timer,
 	.dt_compat = msm8974_dt_match,
-	.reserve = msm_reserve,
+	.reserve = msm_8974_reserve,
 	.init_very_early = msm8974_init_very_early,
 	.restart = msm_restart,
 MACHINE_END
diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h
index acfbe4a..d089924 100644
--- a/arch/arm/mach-msm/include/mach/memory.h
+++ b/arch/arm/mach-msm/include/mach/memory.h
@@ -98,26 +98,25 @@
 #define finish_arch_switch(prev)	do { store_ttbr0(); } while (0)
 #endif
 
+#define MAX_HOLE_ADDRESS    (PHYS_OFFSET + 0x10000000)
+extern unsigned long memory_hole_offset;
+extern unsigned long memory_hole_start;
+extern unsigned long memory_hole_end;
 #ifdef CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0
-extern unsigned long membank0_size;
-extern unsigned long membank1_start;
-void find_membank0_hole(void);
+void find_memory_hole(void);
 
-#define MEMBANK0_PHYS_OFFSET PHYS_OFFSET
-#define MEMBANK0_PAGE_OFFSET PAGE_OFFSET
-
-#define MEMBANK1_PHYS_OFFSET (membank1_start)
-#define MEMBANK1_PAGE_OFFSET (MEMBANK0_PAGE_OFFSET + (membank0_size))
+#define MEM_HOLE_END_PHYS_OFFSET (memory_hole_end)
+#define MEM_HOLE_PAGE_OFFSET (PAGE_OFFSET + memory_hole_offset)
 
 #define __phys_to_virt(phys)				\
-	((MEMBANK1_PHYS_OFFSET && ((phys) >= MEMBANK1_PHYS_OFFSET)) ?	\
-	(phys) - MEMBANK1_PHYS_OFFSET + MEMBANK1_PAGE_OFFSET :	\
-	(phys) - MEMBANK0_PHYS_OFFSET + MEMBANK0_PAGE_OFFSET)
+	((MEM_HOLE_END_PHYS_OFFSET && ((phys) >= MEM_HOLE_END_PHYS_OFFSET)) ? \
+	(phys) - MEM_HOLE_END_PHYS_OFFSET + MEM_HOLE_PAGE_OFFSET :	\
+	(phys) - PHYS_OFFSET + PAGE_OFFSET)
 
 #define __virt_to_phys(virt)				\
-	((MEMBANK1_PHYS_OFFSET && ((virt) >= MEMBANK1_PAGE_OFFSET)) ?	\
-	(virt) - MEMBANK1_PAGE_OFFSET + MEMBANK1_PHYS_OFFSET :	\
-	(virt) - MEMBANK0_PAGE_OFFSET + MEMBANK0_PHYS_OFFSET)
+	((MEM_HOLE_END_PHYS_OFFSET && ((virt) >= MEM_HOLE_PAGE_OFFSET)) ? \
+	(virt) - MEM_HOLE_PAGE_OFFSET + MEM_HOLE_END_PHYS_OFFSET :	\
+	(virt) - PAGE_OFFSET + PHYS_OFFSET)
 #endif
 
 /*
diff --git a/arch/arm/mach-msm/include/mach/msm_memtypes.h b/arch/arm/mach-msm/include/mach/msm_memtypes.h
index 5ca5861..80e454a 100644
--- a/arch/arm/mach-msm/include/mach/msm_memtypes.h
+++ b/arch/arm/mach-msm/include/mach/msm_memtypes.h
@@ -68,6 +68,8 @@
 
 int __init dt_scan_for_memory_reserve(unsigned long node, const char *uname,
 					int depth, void *data);
-
+int __init dt_scan_for_memory_hole(unsigned long node, const char *uname,
+					int depth, void *data);
+void adjust_meminfo(unsigned long start, unsigned long size);
 unsigned long __init reserve_memory_for_fmem(unsigned long, unsigned long);
 #endif
diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c
index 3fe65b8..9cc2a9d 100644
--- a/arch/arm/mach-msm/memory.c
+++ b/arch/arm/mach-msm/memory.c
@@ -27,6 +27,7 @@
 #include <asm/setup.h>
 #include <asm/mach-types.h>
 #include <mach/msm_memtypes.h>
+#include <mach/memory.h>
 #include <linux/hardirq.h>
 #if defined(CONFIG_MSM_NPA_REMOTE)
 #include "npa_remote.h"
@@ -365,7 +366,7 @@
 	return ret;
 }
 
-static int check_for_compat(unsigned long node)
+static int __init check_for_compat(unsigned long node)
 {
 	char **start = __compat_exports_start;
 
@@ -454,6 +455,79 @@
 	return 0;
 }
 
+/* This function scans the device tree to populate the memory hole table */
+int __init dt_scan_for_memory_hole(unsigned long node, const char *uname,
+		int depth, void *data)
+{
+	unsigned int *memory_remove_prop;
+	unsigned long memory_remove_prop_length;
+	unsigned long hole_start;
+	unsigned long hole_size;
+
+	memory_remove_prop = of_get_flat_dt_prop(node,
+						"qcom,memblock-remove",
+						&memory_remove_prop_length);
+
+	if (memory_remove_prop) {
+		if (!check_for_compat(node))
+			goto out;
+	} else {
+		goto out;
+	}
+
+	if (memory_remove_prop) {
+		if (memory_remove_prop_length != (2*sizeof(unsigned int))) {
+			WARN(1, "Memory remove malformed\n");
+			goto out;
+		}
+
+		hole_start = be32_to_cpu(memory_remove_prop[0]);
+		hole_size = be32_to_cpu(memory_remove_prop[1]);
+
+		if (hole_start + hole_size <= MAX_HOLE_ADDRESS) {
+			if (memory_hole_start == 0 && memory_hole_end == 0) {
+				memory_hole_start = hole_start;
+				memory_hole_end = hole_start + hole_size;
+			} else if ((memory_hole_end - memory_hole_start)
+							<= hole_size) {
+				memory_hole_start = hole_start;
+				memory_hole_end = hole_start + hole_size;
+			}
+		}
+		adjust_meminfo(hole_start, hole_size);
+	}
+
+out:
+	return 0;
+}
+
+/*
+ * Split the memory bank to reflect the hole, if present,
+ * using the start and end of the memory hole.
+ */
+void adjust_meminfo(unsigned long start, unsigned long size)
+{
+	int i, j;
+
+	for (i = 0, j = 0; i < meminfo.nr_banks; i++) {
+		struct membank *bank = &meminfo.bank[j];
+		*bank = meminfo.bank[i];
+
+		if (((start + size) <= (bank->start + bank->size)) &&
+			(start >= bank->start)) {
+			memmove(bank + 1, bank,
+				(meminfo.nr_banks - i) * sizeof(*bank));
+			meminfo.nr_banks++;
+			i++;
+			bank[1].size -= (start + size);
+			bank[1].start = (start + size);
+			bank[1].highmem = 0;
+			j++;
+			bank->size = start - bank->start;
+		}
+		j++;
+	}
+}
 unsigned long get_ddr_size(void)
 {
 	unsigned int i;