Skip to content

Commit

Permalink
drivers: of: add initialization code for dynamic reserved memory
Browse files Browse the repository at this point in the history
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 <joshc@codeaurora.org>

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Grant Likely <grant.likely@linaro.org>
  • Loading branch information
mszyprow authored and glikely committed Mar 11, 2014
1 parent e8d9d1f commit 3f0c820
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 2 deletions.
6 changes: 6 additions & 0 deletions drivers/of/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,10 @@ config OF_MTD
depends on MTD
def_bool y

config OF_RESERVED_MEM
depends on OF_EARLY_FLATTREE
bool
help
Helpers to allow for reservation of memory regions

endmenu # OF
1 change: 1 addition & 0 deletions drivers/of/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o
obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
13 changes: 11 additions & 2 deletions drivers/of/fdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_reserved_mem.h>
#include <linux/sizes.h>
#include <linux/string.h>
#include <linux/errno.h>
Expand Down Expand Up @@ -450,7 +451,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
phys_addr_t base, size;
unsigned long len;
__be32 *prop;
int nomap;
int nomap, first = 1;

prop = of_get_flat_dt_prop(node, "reg", &len);
if (!prop)
Expand All @@ -477,6 +478,10 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
uname, &base, (unsigned long)size / SZ_1M);

len -= t_len;
if (first) {
fdt_reserved_mem_save_node(node, uname, base, size);
first = 0;
}
}
return 0;
}
Expand Down Expand Up @@ -512,6 +517,7 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
{
static int found;
const char *status;
int err;

if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) {
if (__reserved_mem_check_root(node) != 0) {
Expand All @@ -534,7 +540,9 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0)
return 0;

__reserved_mem_reserve_reg(node, uname);
err = __reserved_mem_reserve_reg(node, uname);
if (err == -ENOENT && of_get_flat_dt_prop(node, "size", NULL))
fdt_reserved_mem_save_node(node, uname, 0, 0);

/* scan next node */
return 0;
Expand All @@ -550,6 +558,7 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
void __init early_init_fdt_scan_reserved_mem(void)
{
of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
fdt_init_reserved_mem();
}

/**
Expand Down
188 changes: 188 additions & 0 deletions drivers/of/of_reserved_mem.c
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 <m.szyprowski@samsung.com>
* Author: Josh Cartwright <joshc@codeaurora.org>
*
* 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);
}
}
21 changes: 21 additions & 0 deletions include/linux/of_reserved_mem.h
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 */

0 comments on commit 3f0c820

Please sign in to comment.