of: Move all FDT reserved-memory handling into of_reserved_mem.c
[linux-2.6-microblaze.git] / drivers / of / of_reserved_mem.c
index 7ec94cf..8236eca 100644 (file)
@@ -12,6 +12,7 @@
 #define pr_fmt(fmt)    "OF: reserved mem: " fmt
 
 #include <linux/err.h>
+#include <linux/libfdt.h>
 #include <linux/of.h>
 #include <linux/of_fdt.h>
 #include <linux/of_platform.h>
@@ -58,8 +59,8 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
 /*
  * fdt_reserved_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)
+static 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];
 
@@ -77,6 +78,126 @@ void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
        return;
 }
 
+static int __init early_init_dt_reserve_memory(phys_addr_t base,
+                                              phys_addr_t size, bool nomap)
+{
+       if (nomap) {
+               /*
+                * If the memory is already reserved (by another region), we
+                * should not allow it to be marked nomap, but don't worry
+                * if the region isn't memory as it won't be mapped.
+                */
+               if (memblock_overlaps_region(&memblock.memory, base, size) &&
+                   memblock_is_region_reserved(base, size))
+                       return -EBUSY;
+
+               return memblock_mark_nomap(base, size);
+       }
+       return memblock_reserve(base, size);
+}
+
+/*
+ * __reserved_mem_reserve_reg() - reserve all memory described in 'reg' property
+ */
+static int __init __reserved_mem_reserve_reg(unsigned long node,
+                                            const char *uname)
+{
+       int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
+       phys_addr_t base, size;
+       int len;
+       const __be32 *prop;
+       int first = 1;
+       bool nomap;
+
+       prop = of_get_flat_dt_prop(node, "reg", &len);
+       if (!prop)
+               return -ENOENT;
+
+       if (len && len % t_len != 0) {
+               pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
+                      uname);
+               return -EINVAL;
+       }
+
+       nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
+
+       while (len >= t_len) {
+               base = dt_mem_next_cell(dt_root_addr_cells, &prop);
+               size = dt_mem_next_cell(dt_root_size_cells, &prop);
+
+               if (size &&
+                   early_init_dt_reserve_memory(base, size, nomap) == 0)
+                       pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n",
+                               uname, &base, (unsigned long)(size / SZ_1M));
+               else
+                       pr_err("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n",
+                              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;
+}
+
+/*
+ * __reserved_mem_check_root() - check if #size-cells, #address-cells provided
+ * in /reserved-memory matches the values supported by the current implementation,
+ * also check if ranges property has been provided
+ */
+static int __init __reserved_mem_check_root(unsigned long node)
+{
+       const __be32 *prop;
+
+       prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
+       if (!prop || be32_to_cpup(prop) != dt_root_size_cells)
+               return -EINVAL;
+
+       prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
+       if (!prop || be32_to_cpup(prop) != dt_root_addr_cells)
+               return -EINVAL;
+
+       prop = of_get_flat_dt_prop(node, "ranges", NULL);
+       if (!prop)
+               return -EINVAL;
+       return 0;
+}
+
+/*
+ * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
+ */
+int __init fdt_scan_reserved_mem(void)
+{
+       int node, child;
+       const void *fdt = initial_boot_params;
+
+       node = fdt_path_offset(fdt, "/reserved-memory");
+       if (node < 0)
+               return -ENODEV;
+
+       if (__reserved_mem_check_root(node) != 0) {
+               pr_err("Reserved memory: unsupported node format, ignoring\n");
+               return -EINVAL;
+       }
+
+       fdt_for_each_subnode(child, fdt, node) {
+               const char *uname;
+               int err;
+
+               if (!of_fdt_device_is_available(fdt, child))
+                       continue;
+
+               uname = fdt_get_name(fdt, child, NULL);
+
+               err = __reserved_mem_reserve_reg(child, uname);
+               if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL))
+                       fdt_reserved_mem_save_node(child, uname, 0, 0);
+       }
+       return 0;
+}
+
 /*
  * __reserved_mem_alloc_in_range() - allocate reserved memory described with
  *     'alloc-ranges'. Choose bottom-up/top-down depending on nearby existing