Merge tag 'dmaengine-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul...
[linux-2.6-microblaze.git] / drivers / of / of_reserved_mem.c
index 948efa9..7ec94cf 100644 (file)
@@ -77,6 +77,57 @@ void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,
        return;
 }
 
+/*
+ * __reserved_mem_alloc_in_range() - allocate reserved memory described with
+ *     'alloc-ranges'. Choose bottom-up/top-down depending on nearby existing
+ *     reserved regions to keep the reserved memory contiguous if possible.
+ */
+static int __init __reserved_mem_alloc_in_range(phys_addr_t size,
+       phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap,
+       phys_addr_t *res_base)
+{
+       bool prev_bottom_up = memblock_bottom_up();
+       bool bottom_up = false, top_down = false;
+       int ret, i;
+
+       for (i = 0; i < reserved_mem_count; i++) {
+               struct reserved_mem *rmem = &reserved_mem[i];
+
+               /* Skip regions that were not reserved yet */
+               if (rmem->size == 0)
+                       continue;
+
+               /*
+                * If range starts next to an existing reservation, use bottom-up:
+                *      |....RRRR................RRRRRRRR..............|
+                *             --RRRR------
+                */
+               if (start >= rmem->base && start <= (rmem->base + rmem->size))
+                       bottom_up = true;
+
+               /*
+                * If range ends next to an existing reservation, use top-down:
+                *      |....RRRR................RRRRRRRR..............|
+                *                    -------RRRR-----
+                */
+               if (end >= rmem->base && end <= (rmem->base + rmem->size))
+                       top_down = true;
+       }
+
+       /* Change setting only if either bottom-up or top-down was selected */
+       if (bottom_up != top_down)
+               memblock_set_bottom_up(bottom_up);
+
+       ret = early_init_dt_alloc_reserved_memory_arch(size, align,
+                       start, end, nomap, res_base);
+
+       /* Restore old setting if needed */
+       if (bottom_up != top_down)
+               memblock_set_bottom_up(prev_bottom_up);
+
+       return ret;
+}
+
 /*
  * __reserved_mem_alloc_size() - allocate reserved memory described by
  *     'size', 'alignment'  and 'alloc-ranges' properties.
@@ -137,8 +188,8 @@ static int __init __reserved_mem_alloc_size(unsigned long node,
                        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);
+                       ret = __reserved_mem_alloc_in_range(size, align,
+                                       start, end, nomap, &base);
                        if (ret == 0) {
                                pr_debug("allocated memory for '%s' node: base %pa, size %lu MiB\n",
                                        uname, &base,
@@ -217,6 +268,11 @@ static int __init __rmem_cmp(const void *a, const void *b)
        if (ra->size > rb->size)
                return 1;
 
+       if (ra->fdt_node < rb->fdt_node)
+               return -1;
+       if (ra->fdt_node > rb->fdt_node)
+               return 1;
+
        return 0;
 }