forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
drivers: of: add initialization code for dynamic reserved memory
This patch adds support for dynamically allocated reserved memory regions declared in device tree. Such regions are defined by 'size', 'alignment' and 'alloc-ranges' properties. Based on previous code provided by Josh Cartwright <[email protected]> Signed-off-by: Marek Szyprowski <[email protected]> Signed-off-by: Grant Likely <[email protected]>
- Loading branch information
Showing
5 changed files
with
227 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
/* | ||
* Device tree based initialization code for reserved memory. | ||
* | ||
* Copyright (c) 2013, The Linux Foundation. All Rights Reserved. | ||
* Copyright (c) 2013,2014 Samsung Electronics Co., Ltd. | ||
* http://www.samsung.com | ||
* Author: Marek Szyprowski <[email protected]> | ||
* Author: Josh Cartwright <[email protected]> | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU General Public License as | ||
* published by the Free Software Foundation; either version 2 of the | ||
* License or (at your optional) any later version of the license. | ||
*/ | ||
|
||
#include <linux/err.h> | ||
#include <linux/of.h> | ||
#include <linux/of_fdt.h> | ||
#include <linux/of_platform.h> | ||
#include <linux/mm.h> | ||
#include <linux/sizes.h> | ||
#include <linux/of_reserved_mem.h> | ||
|
||
#define MAX_RESERVED_REGIONS 16 | ||
static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; | ||
static int reserved_mem_count; | ||
|
||
#if defined(CONFIG_HAVE_MEMBLOCK) | ||
#include <linux/memblock.h> | ||
int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, | ||
phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, | ||
phys_addr_t *res_base) | ||
{ | ||
/* | ||
* We use __memblock_alloc_base() because memblock_alloc_base() | ||
* panic()s on allocation failure. | ||
*/ | ||
phys_addr_t base = __memblock_alloc_base(size, align, end); | ||
if (!base) | ||
return -ENOMEM; | ||
|
||
/* | ||
* Check if the allocated region fits in to start..end window | ||
*/ | ||
if (base < start) { | ||
memblock_free(base, size); | ||
return -ENOMEM; | ||
} | ||
|
||
*res_base = base; | ||
if (nomap) | ||
return memblock_remove(base, size); | ||
return 0; | ||
} | ||
#else | ||
int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, | ||
phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, | ||
phys_addr_t *res_base) | ||
{ | ||
pr_err("Reserved memory not supported, ignoring region 0x%llx%s\n", | ||
size, nomap ? " (nomap)" : ""); | ||
return -ENOSYS; | ||
} | ||
#endif | ||
|
||
/** | ||
* res_mem_save_node() - save fdt node for second pass initialization | ||
*/ | ||
void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname, | ||
phys_addr_t base, phys_addr_t size) | ||
{ | ||
struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; | ||
|
||
if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) { | ||
pr_err("Reserved memory: not enough space all defined regions.\n"); | ||
return; | ||
} | ||
|
||
rmem->fdt_node = node; | ||
rmem->name = uname; | ||
rmem->base = base; | ||
rmem->size = size; | ||
|
||
reserved_mem_count++; | ||
return; | ||
} | ||
|
||
/** | ||
* res_mem_alloc_size() - allocate reserved memory described by 'size', 'align' | ||
* and 'alloc-ranges' properties | ||
*/ | ||
static int __init __reserved_mem_alloc_size(unsigned long node, | ||
const char *uname, phys_addr_t *res_base, phys_addr_t *res_size) | ||
{ | ||
int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); | ||
phys_addr_t start = 0, end = 0; | ||
phys_addr_t base = 0, align = 0, size; | ||
unsigned long len; | ||
__be32 *prop; | ||
int nomap; | ||
int ret; | ||
|
||
prop = of_get_flat_dt_prop(node, "size", &len); | ||
if (!prop) | ||
return -EINVAL; | ||
|
||
if (len != dt_root_size_cells * sizeof(__be32)) { | ||
pr_err("Reserved memory: invalid size property in '%s' node.\n", | ||
uname); | ||
return -EINVAL; | ||
} | ||
size = dt_mem_next_cell(dt_root_size_cells, &prop); | ||
|
||
nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; | ||
|
||
prop = of_get_flat_dt_prop(node, "alignment", &len); | ||
if (prop) { | ||
if (len != dt_root_addr_cells * sizeof(__be32)) { | ||
pr_err("Reserved memory: invalid alignment property in '%s' node.\n", | ||
uname); | ||
return -EINVAL; | ||
} | ||
align = dt_mem_next_cell(dt_root_addr_cells, &prop); | ||
} | ||
|
||
prop = of_get_flat_dt_prop(node, "alloc-ranges", &len); | ||
if (prop) { | ||
|
||
if (len % t_len != 0) { | ||
pr_err("Reserved memory: invalid alloc-ranges property in '%s', skipping node.\n", | ||
uname); | ||
return -EINVAL; | ||
} | ||
|
||
base = 0; | ||
|
||
while (len > 0) { | ||
start = dt_mem_next_cell(dt_root_addr_cells, &prop); | ||
end = start + dt_mem_next_cell(dt_root_size_cells, | ||
&prop); | ||
|
||
ret = early_init_dt_alloc_reserved_memory_arch(size, | ||
align, start, end, nomap, &base); | ||
if (ret == 0) { | ||
pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", | ||
uname, &base, | ||
(unsigned long)size / SZ_1M); | ||
break; | ||
} | ||
len -= t_len; | ||
} | ||
|
||
} else { | ||
ret = early_init_dt_alloc_reserved_memory_arch(size, align, | ||
0, 0, nomap, &base); | ||
if (ret == 0) | ||
pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", | ||
uname, &base, (unsigned long)size / SZ_1M); | ||
} | ||
|
||
if (base == 0) { | ||
pr_info("Reserved memory: failed to allocate memory for node '%s'\n", | ||
uname); | ||
return -ENOMEM; | ||
} | ||
|
||
*res_base = base; | ||
*res_size = size; | ||
|
||
return 0; | ||
} | ||
|
||
/** | ||
* fdt_init_reserved_mem - allocate and init all saved reserved memory regions | ||
*/ | ||
void __init fdt_init_reserved_mem(void) | ||
{ | ||
int i; | ||
for (i = 0; i < reserved_mem_count; i++) { | ||
struct reserved_mem *rmem = &reserved_mem[i]; | ||
unsigned long node = rmem->fdt_node; | ||
int err = 0; | ||
|
||
if (rmem->size == 0) | ||
err = __reserved_mem_alloc_size(node, rmem->name, | ||
&rmem->base, &rmem->size); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#ifndef __OF_RESERVED_MEM_H | ||
#define __OF_RESERVED_MEM_H | ||
|
||
struct reserved_mem { | ||
const char *name; | ||
unsigned long fdt_node; | ||
phys_addr_t base; | ||
phys_addr_t size; | ||
}; | ||
|
||
#ifdef CONFIG_OF_RESERVED_MEM | ||
void fdt_init_reserved_mem(void); | ||
void fdt_reserved_mem_save_node(unsigned long node, const char *uname, | ||
phys_addr_t base, phys_addr_t size); | ||
#else | ||
static inline void fdt_init_reserved_mem(void) { } | ||
static inline void fdt_reserved_mem_save_node(unsigned long node, | ||
const char *uname, phys_addr_t base, phys_addr_t size) { } | ||
#endif | ||
|
||
#endif /* __OF_RESERVED_MEM_H */ |