diff options
Diffstat (limited to 'drivers/of/of_reserved_mem.c')
| -rw-r--r-- | drivers/of/of_reserved_mem.c | 647 |
1 files changed, 522 insertions, 125 deletions
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 1977ee0adcb1..5619ec917858 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -12,6 +12,8 @@ #define pr_fmt(fmt) "OF: reserved mem: " fmt #include <linux/err.h> +#include <linux/ioport.h> +#include <linux/libfdt.h> #include <linux/of.h> #include <linux/of_fdt.h> #include <linux/of_platform.h> @@ -21,50 +23,98 @@ #include <linux/sort.h> #include <linux/slab.h> #include <linux/memblock.h> +#include <linux/kmemleak.h> +#include <linux/cma.h> +#include <linux/dma-map-ops.h> -#define MAX_RESERVED_REGIONS 32 -static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; +#include "of_private.h" + +static struct reserved_mem reserved_mem_array[MAX_RESERVED_REGIONS] __initdata; +static struct reserved_mem *reserved_mem __refdata = reserved_mem_array; +static int total_reserved_mem_cnt = MAX_RESERVED_REGIONS; static int reserved_mem_count; -int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, +static int __init 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) { phys_addr_t base; - /* - * We use __memblock_alloc_base() because memblock_alloc_base() - * panic()s on allocation failure. - */ + int err = 0; + end = !end ? MEMBLOCK_ALLOC_ANYWHERE : end; align = !align ? SMP_CACHE_BYTES : align; - base = __memblock_alloc_base(size, align, end); + base = memblock_phys_alloc_range(size, align, start, 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) { + err = memblock_mark_nomap(base, size); + if (err) + memblock_phys_free(base, size); } - *res_base = base; - if (nomap) - return memblock_remove(base, size); - return 0; + if (!err) + kmemleak_ignore_phys(base); + + return err; } -/** - * res_mem_save_node() - save fdt node for second pass initialization +/* + * alloc_reserved_mem_array() - allocate memory for the reserved_mem + * array using memblock + * + * This function is used to allocate memory for the reserved_mem + * array according to the total number of reserved memory regions + * defined in the DT. + * After the new array is allocated, the information stored in + * the initial static array is copied over to this new array and + * the new array is used from this point on. + */ +static void __init alloc_reserved_mem_array(void) +{ + struct reserved_mem *new_array; + size_t alloc_size, copy_size, memset_size; + + alloc_size = array_size(total_reserved_mem_cnt, sizeof(*new_array)); + if (alloc_size == SIZE_MAX) { + pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW); + return; + } + + new_array = memblock_alloc(alloc_size, SMP_CACHE_BYTES); + if (!new_array) { + pr_err("Failed to allocate memory for reserved_mem array with err: %d", -ENOMEM); + return; + } + + copy_size = array_size(reserved_mem_count, sizeof(*new_array)); + if (copy_size == SIZE_MAX) { + memblock_free(new_array, alloc_size); + total_reserved_mem_cnt = MAX_RESERVED_REGIONS; + pr_err("Failed to allocate memory for reserved_mem array with err: %d", -EOVERFLOW); + return; + } + + memset_size = alloc_size - copy_size; + + memcpy(new_array, reserved_mem, copy_size); + memset(new_array + reserved_mem_count, 0, memset_size); + + reserved_mem = new_array; +} + +static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem); +/* + * 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]; - if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) { - pr_err("not enough space all defined regions.\n"); + if (reserved_mem_count == total_reserved_mem_cnt) { + pr_err("not enough space for all defined regions.\n"); return; } @@ -73,23 +123,273 @@ void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname, rmem->base = base; rmem->size = size; + /* Call the region specific initialization function */ + fdt_init_reserved_mem_node(rmem); + reserved_mem_count++; 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) +{ + phys_addr_t base, size; + int i, len; + const __be32 *prop; + bool nomap; + + prop = of_flat_dt_get_addr_size_prop(node, "reg", &len); + if (!prop) + return -ENOENT; + + nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; + + for (i = 0; i < len; i++) { + u64 b, s; + + of_flat_dt_read_addr_size(prop, i, &b, &s); + + base = b; + size = s; + + if (size && early_init_dt_reserve_memory(base, size, nomap) == 0) { + /* Architecture specific contiguous memory fixup. */ + if (of_flat_dt_is_compatible(node, "shared-dma-pool") && + of_get_flat_dt_prop(node, "reusable", NULL)) + dma_contiguous_early_fixup(base, size); + 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)); + } + } + 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; +} + +static void __init __rmem_check_for_overlap(void); + /** - * res_mem_alloc_size() - allocate reserved memory described by 'size', 'align' - * and 'alloc-ranges' properties + * fdt_scan_reserved_mem_reg_nodes() - Store info for the "reg" defined + * reserved memory regions. + * + * This function is used to scan through the DT and store the + * information for the reserved memory regions that are defined using + * the "reg" property. The region node number, name, base address, and + * size are all stored in the reserved_mem array by calling the + * fdt_reserved_mem_save_node() function. */ -static int __init __reserved_mem_alloc_size(unsigned long node, - const char *uname, phys_addr_t *res_base, phys_addr_t *res_size) +void __init fdt_scan_reserved_mem_reg_nodes(void) +{ + const void *fdt = initial_boot_params; + phys_addr_t base, size; + int node, child; + + if (!fdt) + return; + + node = fdt_path_offset(fdt, "/reserved-memory"); + if (node < 0) { + pr_info("Reserved memory: No reserved-memory node in the DT\n"); + return; + } + + /* Attempt dynamic allocation of a new reserved_mem array */ + alloc_reserved_mem_array(); + + if (__reserved_mem_check_root(node)) { + pr_err("Reserved memory: unsupported node format, ignoring\n"); + return; + } + + fdt_for_each_subnode(child, fdt, node) { + const char *uname; + u64 b, s; + + if (!of_fdt_device_is_available(fdt, child)) + continue; + + if (!of_flat_dt_get_addr_size(child, "reg", &b, &s)) + continue; + + base = b; + size = s; + + if (size) { + uname = fdt_get_name(fdt, child, NULL); + fdt_reserved_mem_save_node(child, uname, base, size); + } + } + + /* check for overlapping reserved regions */ + __rmem_check_for_overlap(); +} + +static int __init __reserved_mem_alloc_size(unsigned long node, const char *uname); + +/* + * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory + */ +int __init fdt_scan_reserved_mem(void) +{ + int node, child; + int dynamic_nodes_cnt = 0, count = 0; + int dynamic_nodes[MAX_RESERVED_REGIONS]; + 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) + count++; + /* + * Save the nodes for the dynamically-placed regions + * into an array which will be used for allocation right + * after all the statically-placed regions are reserved + * or marked as no-map. This is done to avoid dynamically + * allocating from one of the statically-placed regions. + */ + if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) { + dynamic_nodes[dynamic_nodes_cnt] = child; + dynamic_nodes_cnt++; + } + } + for (int i = 0; i < dynamic_nodes_cnt; i++) { + const char *uname; + int err; + + child = dynamic_nodes[i]; + uname = fdt_get_name(fdt, child, NULL); + err = __reserved_mem_alloc_size(child, uname); + if (!err) + count++; + } + total_reserved_mem_cnt = count; + return 0; +} + +/* + * __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. + */ +static int __init __reserved_mem_alloc_size(unsigned long node, const char *uname) { - 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; - int len; + int i, len; const __be32 *prop; - int nomap; + bool nomap; int ret; prop = of_get_flat_dt_prop(node, "size", &len); @@ -102,8 +402,6 @@ static int __init __reserved_mem_alloc_size(unsigned long node, } 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)) { @@ -114,73 +412,68 @@ static int __init __reserved_mem_alloc_size(unsigned long node, align = dt_mem_next_cell(dt_root_addr_cells, &prop); } + nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; + /* Need adjust the alignment to satisfy the CMA requirement */ if (IS_ENABLED(CONFIG_CMA) && of_flat_dt_is_compatible(node, "shared-dma-pool") && of_get_flat_dt_prop(node, "reusable", NULL) - && !of_get_flat_dt_prop(node, "no-map", NULL)) { - unsigned long order = - max_t(unsigned long, MAX_ORDER - 1, pageblock_order); - - align = max(align, (phys_addr_t)PAGE_SIZE << order); - } + && !nomap) + align = max_t(phys_addr_t, align, CMA_MIN_ALIGNMENT_BYTES); - prop = of_get_flat_dt_prop(node, "alloc-ranges", &len); + prop = of_flat_dt_get_addr_size_prop(node, "alloc-ranges", &len); if (prop) { + for (i = 0; i < len; i++) { + u64 b, s; - if (len % t_len != 0) { - pr_err("invalid alloc-ranges property in '%s', skipping node.\n", - uname); - return -EINVAL; - } - - base = 0; + of_flat_dt_read_addr_size(prop, i, &b, &s); - while (len > 0) { - start = dt_mem_next_cell(dt_root_addr_cells, &prop); - end = start + dt_mem_next_cell(dt_root_size_cells, - &prop); + start = b; + end = b + s; - ret = early_init_dt_alloc_reserved_memory_arch(size, - align, start, end, nomap, &base); + base = 0; + 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 %ld MiB\n", + pr_debug("allocated memory for '%s' node: base %pa, size %lu MiB\n", uname, &base, - (unsigned long)size / SZ_1M); + (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("allocated memory for '%s' node: base %pa, size %ld MiB\n", - uname, &base, (unsigned long)size / SZ_1M); + pr_debug("allocated memory for '%s' node: base %pa, size %lu MiB\n", + uname, &base, (unsigned long)(size / SZ_1M)); } if (base == 0) { - pr_info("failed to allocate memory for node '%s'\n", uname); + pr_err("failed to allocate memory for node '%s': size %lu MiB\n", + uname, (unsigned long)(size / SZ_1M)); return -ENOMEM; } - - *res_base = base; - *res_size = size; - + /* Architecture specific contiguous memory fixup. */ + if (of_flat_dt_is_compatible(node, "shared-dma-pool") && + of_get_flat_dt_prop(node, "reusable", NULL)) + dma_contiguous_early_fixup(base, size); + /* Save region in the reserved_mem array */ + fdt_reserved_mem_save_node(node, uname, base, size); return 0; } static const struct of_device_id __rmem_of_table_sentinel - __used __section(__reservedmem_of_table_end); + __used __section("__reservedmem_of_table_end"); -/** - * res_mem_init_node() - call region specific reserved memory init code +/* + * __reserved_mem_init_node() - call region specific reserved memory init code */ static int __init __reserved_mem_init_node(struct reserved_mem *rmem) { extern const struct of_device_id __reservedmem_of_table[]; const struct of_device_id *i; + int ret = -ENOENT; for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) { reservedmem_of_init_fn initfn = i->data; @@ -189,13 +482,14 @@ static int __init __reserved_mem_init_node(struct reserved_mem *rmem) if (!of_flat_dt_is_compatible(rmem->fdt_node, compat)) continue; - if (initfn(rmem) == 0) { + ret = initfn(rmem); + if (ret == 0) { pr_info("initialized node %s, compatible id %s\n", rmem->name, compat); - return 0; + break; } } - return -ENOENT; + return ret; } static int __init __rmem_cmp(const void *a, const void *b) @@ -208,6 +502,21 @@ static int __init __rmem_cmp(const void *a, const void *b) if (ra->base > rb->base) return 1; + /* + * Put the dynamic allocations (address == 0, size == 0) before static + * allocations at address 0x0 so that overlap detection works + * correctly. + */ + if (ra->size < rb->size) + return -1; + 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; } @@ -225,8 +534,7 @@ static void __init __rmem_check_for_overlap(void) this = &reserved_mem[i]; next = &reserved_mem[i + 1]; - if (!(this->base && next->base)) - continue; + if (this->base + this->size > next->base) { phys_addr_t this_end, next_end; @@ -240,49 +548,40 @@ static void __init __rmem_check_for_overlap(void) } /** - * fdt_init_reserved_mem - allocate and init all saved reserved memory regions + * fdt_init_reserved_mem_node() - Initialize a reserved memory region + * @rmem: reserved_mem struct of the memory region to be initialized. + * + * This function is used to call the region specific initialization + * function for a reserved memory region. */ -void __init fdt_init_reserved_mem(void) +static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem) { - int i; - - /* check for overlapping reserved regions */ - __rmem_check_for_overlap(); - - for (i = 0; i < reserved_mem_count; i++) { - struct reserved_mem *rmem = &reserved_mem[i]; - unsigned long node = rmem->fdt_node; - int len; - const __be32 *prop; - int err = 0; + unsigned long node = rmem->fdt_node; + int err = 0; + bool nomap; - prop = of_get_flat_dt_prop(node, "phandle", &len); - if (!prop) - prop = of_get_flat_dt_prop(node, "linux,phandle", &len); - if (prop) - rmem->phandle = of_read_number(prop, len/4); + nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; - if (rmem->size == 0) - err = __reserved_mem_alloc_size(node, rmem->name, - &rmem->base, &rmem->size); - if (err == 0) - __reserved_mem_init_node(rmem); + err = __reserved_mem_init_node(rmem); + if (err != 0 && err != -ENOENT) { + pr_info("node %s compatible matching fail\n", rmem->name); + if (nomap) + memblock_clear_nomap(rmem->base, rmem->size); + else + memblock_phys_free(rmem->base, rmem->size); + } else { + phys_addr_t end = rmem->base + rmem->size - 1; + bool reusable = + (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL; + + pr_info("%pa..%pa (%lu KiB) %s %s %s\n", + &rmem->base, &end, (unsigned long)(rmem->size / SZ_1K), + nomap ? "nomap" : "map", + reusable ? "reusable" : "non-reusable", + rmem->name ? rmem->name : "unknown"); } } -static inline struct reserved_mem *__find_rmem(struct device_node *node) -{ - unsigned int i; - - if (!node->phandle) - return NULL; - - for (i = 0; i < reserved_mem_count; i++) - if (reserved_mem[i].phandle == node->phandle) - return &reserved_mem[i]; - return NULL; -} - struct rmem_assigned_device { struct device *dev; struct reserved_mem *rmem; @@ -322,7 +621,12 @@ int of_reserved_mem_device_init_by_idx(struct device *dev, if (!target) return -ENODEV; - rmem = __find_rmem(target); + if (!of_device_is_available(target)) { + of_node_put(target); + return 0; + } + + rmem = of_reserved_mem_lookup(target); of_node_put(target); if (!rmem || !rmem->ops || !rmem->ops->device_init) @@ -340,10 +644,6 @@ int of_reserved_mem_device_init_by_idx(struct device *dev, mutex_lock(&of_rmem_assigned_device_mutex); list_add(&rd->list, &of_rmem_assigned_device_list); mutex_unlock(&of_rmem_assigned_device_mutex); - /* ensure that dma_ops is set for virtual devices - * using reserved memory - */ - of_dma_configure(dev, np, true); dev_info(dev, "assigned reserved memory node %s\n", rmem->name); } else { @@ -355,6 +655,25 @@ int of_reserved_mem_device_init_by_idx(struct device *dev, EXPORT_SYMBOL_GPL(of_reserved_mem_device_init_by_idx); /** + * of_reserved_mem_device_init_by_name() - assign named reserved memory region + * to given device + * @dev: pointer to the device to configure + * @np: pointer to the device node with 'memory-region' property + * @name: name of the selected memory region + * + * Returns: 0 on success or a negative error-code on failure. + */ +int of_reserved_mem_device_init_by_name(struct device *dev, + struct device_node *np, + const char *name) +{ + int idx = of_property_match_string(np, "memory-region-names", name); + + return of_reserved_mem_device_init_by_idx(dev, np, idx); +} +EXPORT_SYMBOL_GPL(of_reserved_mem_device_init_by_name); + +/** * of_reserved_mem_device_release() - release reserved memory device structures * @dev: Pointer to the device to deconfigure * @@ -363,24 +682,22 @@ EXPORT_SYMBOL_GPL(of_reserved_mem_device_init_by_idx); */ void of_reserved_mem_device_release(struct device *dev) { - struct rmem_assigned_device *rd; - struct reserved_mem *rmem = NULL; + struct rmem_assigned_device *rd, *tmp; + LIST_HEAD(release_list); mutex_lock(&of_rmem_assigned_device_mutex); - list_for_each_entry(rd, &of_rmem_assigned_device_list, list) { - if (rd->dev == dev) { - rmem = rd->rmem; - list_del(&rd->list); - kfree(rd); - break; - } + list_for_each_entry_safe(rd, tmp, &of_rmem_assigned_device_list, list) { + if (rd->dev == dev) + list_move_tail(&rd->list, &release_list); } mutex_unlock(&of_rmem_assigned_device_mutex); - if (!rmem || !rmem->ops || !rmem->ops->device_release) - return; + list_for_each_entry_safe(rd, tmp, &release_list, list) { + if (rd->rmem && rd->rmem->ops && rd->rmem->ops->device_release) + rd->rmem->ops->device_release(rd->rmem, dev); - rmem->ops->device_release(rmem, dev); + kfree(rd); + } } EXPORT_SYMBOL_GPL(of_reserved_mem_device_release); @@ -409,3 +726,83 @@ struct reserved_mem *of_reserved_mem_lookup(struct device_node *np) return NULL; } EXPORT_SYMBOL_GPL(of_reserved_mem_lookup); + +/** + * of_reserved_mem_region_to_resource() - Get a reserved memory region as a resource + * @np: node containing 'memory-region' property + * @idx: index of 'memory-region' property to lookup + * @res: Pointer to a struct resource to fill in with reserved region + * + * This function allows drivers to lookup a node's 'memory-region' property + * entries by index and return a struct resource for the entry. + * + * Returns 0 on success with @res filled in. Returns -ENODEV if 'memory-region' + * is missing or unavailable, -EINVAL for any other error. + */ +int of_reserved_mem_region_to_resource(const struct device_node *np, + unsigned int idx, struct resource *res) +{ + struct reserved_mem *rmem; + + if (!np) + return -EINVAL; + + struct device_node __free(device_node) *target = of_parse_phandle(np, "memory-region", idx); + if (!target || !of_device_is_available(target)) + return -ENODEV; + + rmem = of_reserved_mem_lookup(target); + if (!rmem) + return -EINVAL; + + resource_set_range(res, rmem->base, rmem->size); + res->flags = IORESOURCE_MEM; + res->name = rmem->name; + return 0; +} +EXPORT_SYMBOL_GPL(of_reserved_mem_region_to_resource); + +/** + * of_reserved_mem_region_to_resource_byname() - Get a reserved memory region as a resource + * @np: node containing 'memory-region' property + * @name: name of 'memory-region' property entry to lookup + * @res: Pointer to a struct resource to fill in with reserved region + * + * This function allows drivers to lookup a node's 'memory-region' property + * entries by name and return a struct resource for the entry. + * + * Returns 0 on success with @res filled in, or a negative error-code on + * failure. + */ +int of_reserved_mem_region_to_resource_byname(const struct device_node *np, + const char *name, + struct resource *res) +{ + int idx; + + if (!name) + return -EINVAL; + + idx = of_property_match_string(np, "memory-region-names", name); + if (idx < 0) + return idx; + + return of_reserved_mem_region_to_resource(np, idx, res); +} +EXPORT_SYMBOL_GPL(of_reserved_mem_region_to_resource_byname); + +/** + * of_reserved_mem_region_count() - Return the number of 'memory-region' entries + * @np: node containing 'memory-region' property + * + * This function allows drivers to retrieve the number of entries for a node's + * 'memory-region' property. + * + * Returns the number of entries on success, or negative error code on a + * malformed property. + */ +int of_reserved_mem_region_count(const struct device_node *np) +{ + return of_count_phandle_with_args(np, "memory-region", NULL); +} +EXPORT_SYMBOL_GPL(of_reserved_mem_region_count); |
