msm: 8974: Add function to reserve memory from device tree

Memory reservations for memory pools or using memblock remove
must happen early at bootup. Add a function at early boot to
walk the flattened device tree and extract memory reservation
information from appropriate bindings in device tree. To ensure
that the memory is only reserved when a driver is enabled,
drivers must put EXPORT_COMPAT(<compat string>) in the driver
as well as adding the binding to the device tree. More
documentation is available in
Documentation/devicetree/bindings/arm/msm/memory-reserve.txt

Change-Id: I28fa71d7a30cea9af5447acb5d2dde562fa0f6de
Signed-off-by: Laura Abbott <lauraa@codeaurora.org>
This commit is contained in:
Laura Abbott 2012-07-10 10:27:06 -07:00 committed by Stephen Boyd
parent 7fdf9a4be3
commit 56dec94b0a
6 changed files with 143 additions and 0 deletions

View File

@ -185,6 +185,7 @@ SECTIONS
INIT_SETUP(16)
INIT_CALLS
CON_INITCALL
COMPAT_EXPORTS
SECURITY_INITCALL
INIT_RAM_FS
}

View File

@ -383,6 +383,7 @@ static struct reserve_info msm_8974_reserve_info __initdata = {
static void __init msm_8974_early_memory(void)
{
reserve_info = &msm_8974_reserve_info;
of_scan_flat_dt(dt_scan_for_memory_reserve, msm_8974_reserve_table);
}
void __init msm_8974_reserve(void)

View File

@ -118,6 +118,23 @@ void find_membank0_hole(void);
(virt) - MEMBANK0_PAGE_OFFSET + MEMBANK0_PHYS_OFFSET)
#endif
/*
* Need a temporary unique variable that no one will ever see to
* hold the compat string. Line number gives this easily.
* Need another layer of indirection to get __LINE__ to expand
* properly as opposed to appending and ending up with
* __compat___LINE__
*/
#define __CONCAT(a, b) ___CONCAT(a, b)
#define ___CONCAT(a, b) a ## b
#define EXPORT_COMPAT(com) \
static char *__CONCAT(__compat_, __LINE__) __used \
__attribute((__section__(".exportcompat.init"))) = com
extern char *__compat_exports_start[];
extern char *__compat_exports_end[];
#endif
#if defined CONFIG_ARCH_MSM_SCORPION || defined CONFIG_ARCH_MSM_KRAIT
@ -135,4 +152,5 @@ void find_membank0_hole(void);
#ifndef CONFIG_ARCH_MSM7X27
#define CONSISTENT_DMA_SIZE (SZ_1M * 14)
#endif

View File

@ -66,5 +66,8 @@ struct reserve_info {
extern struct reserve_info *reserve_info;
int __init dt_scan_for_memory_reserve(unsigned long node, const char *uname,
int depth, void *data);
unsigned long __init reserve_memory_for_fmem(unsigned long, unsigned long);
#endif

View File

@ -37,6 +37,7 @@
#include <mach/msm_iomap.h>
#include <mach/socinfo.h>
#include <linux/sched.h>
#include <linux/of_fdt.h>
/* fixme */
#include <asm/tlbflush.h>
@ -381,3 +382,117 @@ int release_fmem_c_region(void *unused)
{
return fmem_set_state(FMEM_T_STATE);
}
static char * const memtype_names[] = {
[MEMTYPE_SMI_KERNEL] = "SMI_KERNEL",
[MEMTYPE_SMI] = "SMI",
[MEMTYPE_EBI0] = "EBI0",
[MEMTYPE_EBI1] = "EBI1",
};
static int reserve_memory_type(char *mem_name,
struct memtype_reserve *reserve_table,
int size)
{
int i;
for (i = 0; i < ARRAY_SIZE(memtype_names); i++) {
if (memtype_names[i] && strcmp(mem_name,
memtype_names[i]) == 0) {
reserve_table[i].size += size;
return 0;
}
}
pr_err("Could not find memory type %s\n", mem_name);
return -EINVAL;
}
static int check_for_compat(unsigned long node)
{
char **start = __compat_exports_start;
for ( ; start < __compat_exports_end; start++)
if (of_flat_dt_is_compatible(node, *start))
return 1;
return 0;
}
int __init dt_scan_for_memory_reserve(unsigned long node, const char *uname,
int depth, void *data)
{
char *memory_name_prop;
unsigned int *memory_remove_prop;
unsigned long memory_name_prop_length;
unsigned long memory_remove_prop_length;
unsigned long memory_size_prop_length;
unsigned int *memory_size_prop;
unsigned int memory_size;
unsigned int memory_start;
int ret;
memory_name_prop = of_get_flat_dt_prop(node,
"qcom,memory-reservation-type",
&memory_name_prop_length);
memory_remove_prop = of_get_flat_dt_prop(node,
"qcom,memblock-remove",
&memory_remove_prop_length);
if (memory_name_prop || memory_remove_prop) {
if (!check_for_compat(node))
goto out;
} else {
goto out;
}
if (memory_name_prop) {
if (strnlen(memory_name_prop, memory_name_prop_length) == 0) {
WARN(1, "Memory name was malformed\n");
goto mem_remove;
}
memory_size_prop = of_get_flat_dt_prop(node,
"qcom,memory-reservation-size",
&memory_size_prop_length);
if (memory_size_prop &&
(memory_size_prop_length == sizeof(unsigned int))) {
memory_size = be32_to_cpu(*memory_size_prop);
if (reserve_memory_type(memory_name_prop,
data, memory_size) == 0)
pr_info("%s reserved %s size %x\n",
uname, memory_name_prop, memory_size);
else
WARN(1, "Node %s reserve failed\n",
uname);
} else {
WARN(1, "Node %s specified bad/nonexistent size\n",
uname);
}
}
mem_remove:
if (memory_remove_prop) {
if (memory_remove_prop_length != (2*sizeof(unsigned int))) {
WARN(1, "Memory remove malformed\n");
goto out;
}
memory_start = be32_to_cpu(memory_remove_prop[0]);
memory_size = be32_to_cpu(memory_remove_prop[1]);
ret = memblock_remove(memory_start, memory_size);
if (ret)
WARN(1, "Failed to remove memory %x-%x\n",
memory_start, memory_start+memory_size);
else
pr_info("Node %s removed memory %x-%x\n", uname,
memory_start, memory_start+memory_size);
}
out:
return 0;
}

View File

@ -645,6 +645,11 @@
*(.security_initcall.init) \
VMLINUX_SYMBOL(__security_initcall_end) = .;
#define COMPAT_EXPORTS \
VMLINUX_SYMBOL(__compat_exports_start) = .; \
*(.exportcompat.init) \
VMLINUX_SYMBOL(__compat_exports_end) = .;
#ifdef CONFIG_BLK_DEV_INITRD
#define INIT_RAM_FS \
. = ALIGN(4); \