1 /* 2 * Device tree based initialization code for reserved memory. 3 * 4 * Copyright (c) 2013, The Linux Foundation. All Rights Reserved. 5 * Copyright (c) 2013,2014 Samsung Electronics Co., Ltd. 6 * http://www.samsung.com 7 * Author: Marek Szyprowski <m.szyprowski@samsung.com> 8 * Author: Josh Cartwright <joshc@codeaurora.org> 9 * 10 * This program is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU General Public License as 12 * published by the Free Software Foundation; either version 2 of the 13 * License or (at your optional) any later version of the license. 14 */ 15 16 #include <linux/err.h> 17 #include <linux/of.h> 18 #include <linux/of_fdt.h> 19 #include <linux/of_platform.h> 20 #include <linux/mm.h> 21 #include <linux/sizes.h> 22 #include <linux/of_reserved_mem.h> 23 24 #define MAX_RESERVED_REGIONS 16 25 static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; 26 static int reserved_mem_count; 27 28 #if defined(CONFIG_HAVE_MEMBLOCK) 29 #include <linux/memblock.h> 30 int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, 31 phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, 32 phys_addr_t *res_base) 33 { 34 /* 35 * We use __memblock_alloc_base() because memblock_alloc_base() 36 * panic()s on allocation failure. 37 */ 38 phys_addr_t base = __memblock_alloc_base(size, align, end); 39 if (!base) 40 return -ENOMEM; 41 42 /* 43 * Check if the allocated region fits in to start..end window 44 */ 45 if (base < start) { 46 memblock_free(base, size); 47 return -ENOMEM; 48 } 49 50 *res_base = base; 51 if (nomap) 52 return memblock_remove(base, size); 53 return 0; 54 } 55 #else 56 int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, 57 phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, 58 phys_addr_t *res_base) 59 { 60 pr_err("Reserved memory not supported, ignoring region 0x%llx%s\n", 61 size, nomap ? " (nomap)" : ""); 62 return -ENOSYS; 63 } 64 #endif 65 66 /** 67 * res_mem_save_node() - save fdt node for second pass initialization 68 */ 69 void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname, 70 phys_addr_t base, phys_addr_t size) 71 { 72 struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; 73 74 if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) { 75 pr_err("Reserved memory: not enough space all defined regions.\n"); 76 return; 77 } 78 79 rmem->fdt_node = node; 80 rmem->name = uname; 81 rmem->base = base; 82 rmem->size = size; 83 84 reserved_mem_count++; 85 return; 86 } 87 88 /** 89 * res_mem_alloc_size() - allocate reserved memory described by 'size', 'align' 90 * and 'alloc-ranges' properties 91 */ 92 static int __init __reserved_mem_alloc_size(unsigned long node, 93 const char *uname, phys_addr_t *res_base, phys_addr_t *res_size) 94 { 95 int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); 96 phys_addr_t start = 0, end = 0; 97 phys_addr_t base = 0, align = 0, size; 98 unsigned long len; 99 __be32 *prop; 100 int nomap; 101 int ret; 102 103 prop = of_get_flat_dt_prop(node, "size", &len); 104 if (!prop) 105 return -EINVAL; 106 107 if (len != dt_root_size_cells * sizeof(__be32)) { 108 pr_err("Reserved memory: invalid size property in '%s' node.\n", 109 uname); 110 return -EINVAL; 111 } 112 size = dt_mem_next_cell(dt_root_size_cells, &prop); 113 114 nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; 115 116 prop = of_get_flat_dt_prop(node, "alignment", &len); 117 if (prop) { 118 if (len != dt_root_addr_cells * sizeof(__be32)) { 119 pr_err("Reserved memory: invalid alignment property in '%s' node.\n", 120 uname); 121 return -EINVAL; 122 } 123 align = dt_mem_next_cell(dt_root_addr_cells, &prop); 124 } 125 126 prop = of_get_flat_dt_prop(node, "alloc-ranges", &len); 127 if (prop) { 128 129 if (len % t_len != 0) { 130 pr_err("Reserved memory: invalid alloc-ranges property in '%s', skipping node.\n", 131 uname); 132 return -EINVAL; 133 } 134 135 base = 0; 136 137 while (len > 0) { 138 start = dt_mem_next_cell(dt_root_addr_cells, &prop); 139 end = start + dt_mem_next_cell(dt_root_size_cells, 140 &prop); 141 142 ret = early_init_dt_alloc_reserved_memory_arch(size, 143 align, start, end, nomap, &base); 144 if (ret == 0) { 145 pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", 146 uname, &base, 147 (unsigned long)size / SZ_1M); 148 break; 149 } 150 len -= t_len; 151 } 152 153 } else { 154 ret = early_init_dt_alloc_reserved_memory_arch(size, align, 155 0, 0, nomap, &base); 156 if (ret == 0) 157 pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", 158 uname, &base, (unsigned long)size / SZ_1M); 159 } 160 161 if (base == 0) { 162 pr_info("Reserved memory: failed to allocate memory for node '%s'\n", 163 uname); 164 return -ENOMEM; 165 } 166 167 *res_base = base; 168 *res_size = size; 169 170 return 0; 171 } 172 173 static const struct of_device_id __rmem_of_table_sentinel 174 __used __section(__reservedmem_of_table_end); 175 176 /** 177 * res_mem_init_node() - call region specific reserved memory init code 178 */ 179 static int __init __reserved_mem_init_node(struct reserved_mem *rmem) 180 { 181 extern const struct of_device_id __reservedmem_of_table[]; 182 const struct of_device_id *i; 183 184 for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) { 185 reservedmem_of_init_fn initfn = i->data; 186 const char *compat = i->compatible; 187 188 if (!of_flat_dt_is_compatible(rmem->fdt_node, compat)) 189 continue; 190 191 if (initfn(rmem, rmem->fdt_node, rmem->name) == 0) { 192 pr_info("Reserved memory: initialized node %s, compatible id %s\n", 193 rmem->name, compat); 194 return 0; 195 } 196 } 197 return -ENOENT; 198 } 199 200 /** 201 * fdt_init_reserved_mem - allocate and init all saved reserved memory regions 202 */ 203 void __init fdt_init_reserved_mem(void) 204 { 205 int i; 206 for (i = 0; i < reserved_mem_count; i++) { 207 struct reserved_mem *rmem = &reserved_mem[i]; 208 unsigned long node = rmem->fdt_node; 209 int err = 0; 210 211 if (rmem->size == 0) 212 err = __reserved_mem_alloc_size(node, rmem->name, 213 &rmem->base, &rmem->size); 214 if (err == 0) 215 __reserved_mem_init_node(rmem); 216 } 217 } 218