3c1f6e358d
Patches are generated using the "format-patch" command from the following location: *https://www.codeaurora.org/cgit/quic/kernel/galak-msm/log/?h=apq_ipq_base *rev=0771849495b4128cac2faf7d49c85c729fc48b20 Patches numbered 76/77/102/103 have already been integrated in 3.14.12, so they're not in this list. All these patches are either integrated are pending integration into kernel.org, therefore these patches should go away once the kernel gets upgraded to 3.16. Support is currently limited to AP148 board but can be extended to other platforms in the future. These changes do not cover ethernet connectivity. Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> SVN-Revision: 42334
332 lines
9.5 KiB
Diff
332 lines
9.5 KiB
Diff
From 968b497e1027ec78e986370976c76e1652aa0459 Mon Sep 17 00:00:00 2001
|
|
From: Marek Szyprowski <m.szyprowski@samsung.com>
|
|
Date: Fri, 28 Feb 2014 14:42:48 +0100
|
|
Subject: [PATCH 050/182] 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 <joshc@codeaurora.org>
|
|
|
|
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
|
|
Signed-off-by: Grant Likely <grant.likely@linaro.org>
|
|
---
|
|
drivers/of/Kconfig | 6 ++
|
|
drivers/of/Makefile | 1 +
|
|
drivers/of/fdt.c | 13 ++-
|
|
drivers/of/of_reserved_mem.c | 188 +++++++++++++++++++++++++++++++++++++++
|
|
include/linux/of_reserved_mem.h | 21 +++++
|
|
5 files changed, 227 insertions(+), 2 deletions(-)
|
|
create mode 100644 drivers/of/of_reserved_mem.c
|
|
create mode 100644 include/linux/of_reserved_mem.h
|
|
|
|
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
|
|
index c6973f1..30a7d87 100644
|
|
--- a/drivers/of/Kconfig
|
|
+++ b/drivers/of/Kconfig
|
|
@@ -75,4 +75,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
|
|
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
|
|
index efd0510..ed9660a 100644
|
|
--- a/drivers/of/Makefile
|
|
+++ b/drivers/of/Makefile
|
|
@@ -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
|
|
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
|
|
index 819e112..510c0d8 100644
|
|
--- a/drivers/of/fdt.c
|
|
+++ b/drivers/of/fdt.c
|
|
@@ -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>
|
|
@@ -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)
|
|
@@ -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;
|
|
}
|
|
@@ -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) {
|
|
@@ -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;
|
|
@@ -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();
|
|
}
|
|
|
|
/**
|
|
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
|
|
new file mode 100644
|
|
index 0000000..69b8117
|
|
--- /dev/null
|
|
+++ b/drivers/of/of_reserved_mem.c
|
|
@@ -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);
|
|
+ }
|
|
+}
|
|
diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
|
|
new file mode 100644
|
|
index 0000000..89226ed
|
|
--- /dev/null
|
|
+++ b/include/linux/of_reserved_mem.h
|
|
@@ -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 */
|
|
--
|
|
1.7.10.4
|
|
|