162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Device tree based initialization code for reserved memory. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2013, 2015 The Linux Foundation. All Rights Reserved. 662306a36Sopenharmony_ci * Copyright (c) 2013,2014 Samsung Electronics Co., Ltd. 762306a36Sopenharmony_ci * http://www.samsung.com 862306a36Sopenharmony_ci * Author: Marek Szyprowski <m.szyprowski@samsung.com> 962306a36Sopenharmony_ci * Author: Josh Cartwright <joshc@codeaurora.org> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define pr_fmt(fmt) "OF: reserved mem: " fmt 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_fdt.h> 1762306a36Sopenharmony_ci#include <linux/of_platform.h> 1862306a36Sopenharmony_ci#include <linux/mm.h> 1962306a36Sopenharmony_ci#include <linux/sizes.h> 2062306a36Sopenharmony_ci#include <linux/of_reserved_mem.h> 2162306a36Sopenharmony_ci#include <linux/sort.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/memblock.h> 2462306a36Sopenharmony_ci#include <linux/kmemleak.h> 2562306a36Sopenharmony_ci#include <linux/cma.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "of_private.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define MAX_RESERVED_REGIONS 64 3062306a36Sopenharmony_cistatic struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; 3162306a36Sopenharmony_cistatic int reserved_mem_count; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, 3462306a36Sopenharmony_ci phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, 3562306a36Sopenharmony_ci phys_addr_t *res_base) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci phys_addr_t base; 3862306a36Sopenharmony_ci int err = 0; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci end = !end ? MEMBLOCK_ALLOC_ANYWHERE : end; 4162306a36Sopenharmony_ci align = !align ? SMP_CACHE_BYTES : align; 4262306a36Sopenharmony_ci base = memblock_phys_alloc_range(size, align, start, end); 4362306a36Sopenharmony_ci if (!base) 4462306a36Sopenharmony_ci return -ENOMEM; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci *res_base = base; 4762306a36Sopenharmony_ci if (nomap) { 4862306a36Sopenharmony_ci err = memblock_mark_nomap(base, size); 4962306a36Sopenharmony_ci if (err) 5062306a36Sopenharmony_ci memblock_phys_free(base, size); 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci kmemleak_ignore_phys(base); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return err; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * fdt_reserved_mem_save_node() - save fdt node for second pass initialization 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_civoid __init fdt_reserved_mem_save_node(unsigned long node, const char *uname, 6262306a36Sopenharmony_ci phys_addr_t base, phys_addr_t size) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) { 6762306a36Sopenharmony_ci pr_err("not enough space for all defined regions.\n"); 6862306a36Sopenharmony_ci return; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci rmem->fdt_node = node; 7262306a36Sopenharmony_ci rmem->name = uname; 7362306a36Sopenharmony_ci rmem->base = base; 7462306a36Sopenharmony_ci rmem->size = size; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci reserved_mem_count++; 7762306a36Sopenharmony_ci return; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* 8162306a36Sopenharmony_ci * __reserved_mem_alloc_in_range() - allocate reserved memory described with 8262306a36Sopenharmony_ci * 'alloc-ranges'. Choose bottom-up/top-down depending on nearby existing 8362306a36Sopenharmony_ci * reserved regions to keep the reserved memory contiguous if possible. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cistatic int __init __reserved_mem_alloc_in_range(phys_addr_t size, 8662306a36Sopenharmony_ci phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, 8762306a36Sopenharmony_ci phys_addr_t *res_base) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci bool prev_bottom_up = memblock_bottom_up(); 9062306a36Sopenharmony_ci bool bottom_up = false, top_down = false; 9162306a36Sopenharmony_ci int ret, i; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci for (i = 0; i < reserved_mem_count; i++) { 9462306a36Sopenharmony_ci struct reserved_mem *rmem = &reserved_mem[i]; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* Skip regions that were not reserved yet */ 9762306a36Sopenharmony_ci if (rmem->size == 0) 9862306a36Sopenharmony_ci continue; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* 10162306a36Sopenharmony_ci * If range starts next to an existing reservation, use bottom-up: 10262306a36Sopenharmony_ci * |....RRRR................RRRRRRRR..............| 10362306a36Sopenharmony_ci * --RRRR------ 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci if (start >= rmem->base && start <= (rmem->base + rmem->size)) 10662306a36Sopenharmony_ci bottom_up = true; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* 10962306a36Sopenharmony_ci * If range ends next to an existing reservation, use top-down: 11062306a36Sopenharmony_ci * |....RRRR................RRRRRRRR..............| 11162306a36Sopenharmony_ci * -------RRRR----- 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci if (end >= rmem->base && end <= (rmem->base + rmem->size)) 11462306a36Sopenharmony_ci top_down = true; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Change setting only if either bottom-up or top-down was selected */ 11862306a36Sopenharmony_ci if (bottom_up != top_down) 11962306a36Sopenharmony_ci memblock_set_bottom_up(bottom_up); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci ret = early_init_dt_alloc_reserved_memory_arch(size, align, 12262306a36Sopenharmony_ci start, end, nomap, res_base); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Restore old setting if needed */ 12562306a36Sopenharmony_ci if (bottom_up != top_down) 12662306a36Sopenharmony_ci memblock_set_bottom_up(prev_bottom_up); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return ret; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* 13262306a36Sopenharmony_ci * __reserved_mem_alloc_size() - allocate reserved memory described by 13362306a36Sopenharmony_ci * 'size', 'alignment' and 'alloc-ranges' properties. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_cistatic int __init __reserved_mem_alloc_size(unsigned long node, 13662306a36Sopenharmony_ci const char *uname, phys_addr_t *res_base, phys_addr_t *res_size) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); 13962306a36Sopenharmony_ci phys_addr_t start = 0, end = 0; 14062306a36Sopenharmony_ci phys_addr_t base = 0, align = 0, size; 14162306a36Sopenharmony_ci int len; 14262306a36Sopenharmony_ci const __be32 *prop; 14362306a36Sopenharmony_ci bool nomap; 14462306a36Sopenharmony_ci int ret; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "size", &len); 14762306a36Sopenharmony_ci if (!prop) 14862306a36Sopenharmony_ci return -EINVAL; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (len != dt_root_size_cells * sizeof(__be32)) { 15162306a36Sopenharmony_ci pr_err("invalid size property in '%s' node.\n", uname); 15262306a36Sopenharmony_ci return -EINVAL; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci size = dt_mem_next_cell(dt_root_size_cells, &prop); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "alignment", &len); 15762306a36Sopenharmony_ci if (prop) { 15862306a36Sopenharmony_ci if (len != dt_root_addr_cells * sizeof(__be32)) { 15962306a36Sopenharmony_ci pr_err("invalid alignment property in '%s' node.\n", 16062306a36Sopenharmony_ci uname); 16162306a36Sopenharmony_ci return -EINVAL; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci align = dt_mem_next_cell(dt_root_addr_cells, &prop); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Need adjust the alignment to satisfy the CMA requirement */ 16962306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_CMA) 17062306a36Sopenharmony_ci && of_flat_dt_is_compatible(node, "shared-dma-pool") 17162306a36Sopenharmony_ci && of_get_flat_dt_prop(node, "reusable", NULL) 17262306a36Sopenharmony_ci && !nomap) 17362306a36Sopenharmony_ci align = max_t(phys_addr_t, align, CMA_MIN_ALIGNMENT_BYTES); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "alloc-ranges", &len); 17662306a36Sopenharmony_ci if (prop) { 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (len % t_len != 0) { 17962306a36Sopenharmony_ci pr_err("invalid alloc-ranges property in '%s', skipping node.\n", 18062306a36Sopenharmony_ci uname); 18162306a36Sopenharmony_ci return -EINVAL; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci base = 0; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci while (len > 0) { 18762306a36Sopenharmony_ci start = dt_mem_next_cell(dt_root_addr_cells, &prop); 18862306a36Sopenharmony_ci end = start + dt_mem_next_cell(dt_root_size_cells, 18962306a36Sopenharmony_ci &prop); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci ret = __reserved_mem_alloc_in_range(size, align, 19262306a36Sopenharmony_ci start, end, nomap, &base); 19362306a36Sopenharmony_ci if (ret == 0) { 19462306a36Sopenharmony_ci pr_debug("allocated memory for '%s' node: base %pa, size %lu MiB\n", 19562306a36Sopenharmony_ci uname, &base, 19662306a36Sopenharmony_ci (unsigned long)(size / SZ_1M)); 19762306a36Sopenharmony_ci break; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci len -= t_len; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci } else { 20362306a36Sopenharmony_ci ret = early_init_dt_alloc_reserved_memory_arch(size, align, 20462306a36Sopenharmony_ci 0, 0, nomap, &base); 20562306a36Sopenharmony_ci if (ret == 0) 20662306a36Sopenharmony_ci pr_debug("allocated memory for '%s' node: base %pa, size %lu MiB\n", 20762306a36Sopenharmony_ci uname, &base, (unsigned long)(size / SZ_1M)); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (base == 0) { 21162306a36Sopenharmony_ci pr_err("failed to allocate memory for node '%s': size %lu MiB\n", 21262306a36Sopenharmony_ci uname, (unsigned long)(size / SZ_1M)); 21362306a36Sopenharmony_ci return -ENOMEM; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci *res_base = base; 21762306a36Sopenharmony_ci *res_size = size; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic const struct of_device_id __rmem_of_table_sentinel 22362306a36Sopenharmony_ci __used __section("__reservedmem_of_table_end"); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* 22662306a36Sopenharmony_ci * __reserved_mem_init_node() - call region specific reserved memory init code 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_cistatic int __init __reserved_mem_init_node(struct reserved_mem *rmem) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci extern const struct of_device_id __reservedmem_of_table[]; 23162306a36Sopenharmony_ci const struct of_device_id *i; 23262306a36Sopenharmony_ci int ret = -ENOENT; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) { 23562306a36Sopenharmony_ci reservedmem_of_init_fn initfn = i->data; 23662306a36Sopenharmony_ci const char *compat = i->compatible; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (!of_flat_dt_is_compatible(rmem->fdt_node, compat)) 23962306a36Sopenharmony_ci continue; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ret = initfn(rmem); 24262306a36Sopenharmony_ci if (ret == 0) { 24362306a36Sopenharmony_ci pr_info("initialized node %s, compatible id %s\n", 24462306a36Sopenharmony_ci rmem->name, compat); 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci return ret; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int __init __rmem_cmp(const void *a, const void *b) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci const struct reserved_mem *ra = a, *rb = b; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (ra->base < rb->base) 25662306a36Sopenharmony_ci return -1; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (ra->base > rb->base) 25962306a36Sopenharmony_ci return 1; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* 26262306a36Sopenharmony_ci * Put the dynamic allocations (address == 0, size == 0) before static 26362306a36Sopenharmony_ci * allocations at address 0x0 so that overlap detection works 26462306a36Sopenharmony_ci * correctly. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_ci if (ra->size < rb->size) 26762306a36Sopenharmony_ci return -1; 26862306a36Sopenharmony_ci if (ra->size > rb->size) 26962306a36Sopenharmony_ci return 1; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (ra->fdt_node < rb->fdt_node) 27262306a36Sopenharmony_ci return -1; 27362306a36Sopenharmony_ci if (ra->fdt_node > rb->fdt_node) 27462306a36Sopenharmony_ci return 1; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic void __init __rmem_check_for_overlap(void) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci int i; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (reserved_mem_count < 2) 28462306a36Sopenharmony_ci return; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci sort(reserved_mem, reserved_mem_count, sizeof(reserved_mem[0]), 28762306a36Sopenharmony_ci __rmem_cmp, NULL); 28862306a36Sopenharmony_ci for (i = 0; i < reserved_mem_count - 1; i++) { 28962306a36Sopenharmony_ci struct reserved_mem *this, *next; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci this = &reserved_mem[i]; 29262306a36Sopenharmony_ci next = &reserved_mem[i + 1]; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (this->base + this->size > next->base) { 29562306a36Sopenharmony_ci phys_addr_t this_end, next_end; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci this_end = this->base + this->size; 29862306a36Sopenharmony_ci next_end = next->base + next->size; 29962306a36Sopenharmony_ci pr_err("OVERLAP DETECTED!\n%s (%pa--%pa) overlaps with %s (%pa--%pa)\n", 30062306a36Sopenharmony_ci this->name, &this->base, &this_end, 30162306a36Sopenharmony_ci next->name, &next->base, &next_end); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/** 30762306a36Sopenharmony_ci * fdt_init_reserved_mem() - allocate and init all saved reserved memory regions 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_civoid __init fdt_init_reserved_mem(void) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci int i; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* check for overlapping reserved regions */ 31462306a36Sopenharmony_ci __rmem_check_for_overlap(); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci for (i = 0; i < reserved_mem_count; i++) { 31762306a36Sopenharmony_ci struct reserved_mem *rmem = &reserved_mem[i]; 31862306a36Sopenharmony_ci unsigned long node = rmem->fdt_node; 31962306a36Sopenharmony_ci int len; 32062306a36Sopenharmony_ci const __be32 *prop; 32162306a36Sopenharmony_ci int err = 0; 32262306a36Sopenharmony_ci bool nomap; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; 32562306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "phandle", &len); 32662306a36Sopenharmony_ci if (!prop) 32762306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "linux,phandle", &len); 32862306a36Sopenharmony_ci if (prop) 32962306a36Sopenharmony_ci rmem->phandle = of_read_number(prop, len/4); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (rmem->size == 0) 33262306a36Sopenharmony_ci err = __reserved_mem_alloc_size(node, rmem->name, 33362306a36Sopenharmony_ci &rmem->base, &rmem->size); 33462306a36Sopenharmony_ci if (err == 0) { 33562306a36Sopenharmony_ci err = __reserved_mem_init_node(rmem); 33662306a36Sopenharmony_ci if (err != 0 && err != -ENOENT) { 33762306a36Sopenharmony_ci pr_info("node %s compatible matching fail\n", 33862306a36Sopenharmony_ci rmem->name); 33962306a36Sopenharmony_ci if (nomap) 34062306a36Sopenharmony_ci memblock_clear_nomap(rmem->base, rmem->size); 34162306a36Sopenharmony_ci else 34262306a36Sopenharmony_ci memblock_phys_free(rmem->base, 34362306a36Sopenharmony_ci rmem->size); 34462306a36Sopenharmony_ci } else { 34562306a36Sopenharmony_ci phys_addr_t end = rmem->base + rmem->size - 1; 34662306a36Sopenharmony_ci bool reusable = 34762306a36Sopenharmony_ci (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci pr_info("%pa..%pa (%lu KiB) %s %s %s\n", 35062306a36Sopenharmony_ci &rmem->base, &end, (unsigned long)(rmem->size / SZ_1K), 35162306a36Sopenharmony_ci nomap ? "nomap" : "map", 35262306a36Sopenharmony_ci reusable ? "reusable" : "non-reusable", 35362306a36Sopenharmony_ci rmem->name ? rmem->name : "unknown"); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic inline struct reserved_mem *__find_rmem(struct device_node *node) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci unsigned int i; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (!node->phandle) 36462306a36Sopenharmony_ci return NULL; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci for (i = 0; i < reserved_mem_count; i++) 36762306a36Sopenharmony_ci if (reserved_mem[i].phandle == node->phandle) 36862306a36Sopenharmony_ci return &reserved_mem[i]; 36962306a36Sopenharmony_ci return NULL; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistruct rmem_assigned_device { 37362306a36Sopenharmony_ci struct device *dev; 37462306a36Sopenharmony_ci struct reserved_mem *rmem; 37562306a36Sopenharmony_ci struct list_head list; 37662306a36Sopenharmony_ci}; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic LIST_HEAD(of_rmem_assigned_device_list); 37962306a36Sopenharmony_cistatic DEFINE_MUTEX(of_rmem_assigned_device_mutex); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci/** 38262306a36Sopenharmony_ci * of_reserved_mem_device_init_by_idx() - assign reserved memory region to 38362306a36Sopenharmony_ci * given device 38462306a36Sopenharmony_ci * @dev: Pointer to the device to configure 38562306a36Sopenharmony_ci * @np: Pointer to the device_node with 'reserved-memory' property 38662306a36Sopenharmony_ci * @idx: Index of selected region 38762306a36Sopenharmony_ci * 38862306a36Sopenharmony_ci * This function assigns respective DMA-mapping operations based on reserved 38962306a36Sopenharmony_ci * memory region specified by 'memory-region' property in @np node to the @dev 39062306a36Sopenharmony_ci * device. When driver needs to use more than one reserved memory region, it 39162306a36Sopenharmony_ci * should allocate child devices and initialize regions by name for each of 39262306a36Sopenharmony_ci * child device. 39362306a36Sopenharmony_ci * 39462306a36Sopenharmony_ci * Returns error code or zero on success. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ciint of_reserved_mem_device_init_by_idx(struct device *dev, 39762306a36Sopenharmony_ci struct device_node *np, int idx) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct rmem_assigned_device *rd; 40062306a36Sopenharmony_ci struct device_node *target; 40162306a36Sopenharmony_ci struct reserved_mem *rmem; 40262306a36Sopenharmony_ci int ret; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (!np || !dev) 40562306a36Sopenharmony_ci return -EINVAL; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci target = of_parse_phandle(np, "memory-region", idx); 40862306a36Sopenharmony_ci if (!target) 40962306a36Sopenharmony_ci return -ENODEV; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (!of_device_is_available(target)) { 41262306a36Sopenharmony_ci of_node_put(target); 41362306a36Sopenharmony_ci return 0; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci rmem = __find_rmem(target); 41762306a36Sopenharmony_ci of_node_put(target); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (!rmem || !rmem->ops || !rmem->ops->device_init) 42062306a36Sopenharmony_ci return -EINVAL; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci rd = kmalloc(sizeof(struct rmem_assigned_device), GFP_KERNEL); 42362306a36Sopenharmony_ci if (!rd) 42462306a36Sopenharmony_ci return -ENOMEM; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci ret = rmem->ops->device_init(rmem, dev); 42762306a36Sopenharmony_ci if (ret == 0) { 42862306a36Sopenharmony_ci rd->dev = dev; 42962306a36Sopenharmony_ci rd->rmem = rmem; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci mutex_lock(&of_rmem_assigned_device_mutex); 43262306a36Sopenharmony_ci list_add(&rd->list, &of_rmem_assigned_device_list); 43362306a36Sopenharmony_ci mutex_unlock(&of_rmem_assigned_device_mutex); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci dev_info(dev, "assigned reserved memory node %s\n", rmem->name); 43662306a36Sopenharmony_ci } else { 43762306a36Sopenharmony_ci kfree(rd); 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return ret; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_reserved_mem_device_init_by_idx); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/** 44562306a36Sopenharmony_ci * of_reserved_mem_device_init_by_name() - assign named reserved memory region 44662306a36Sopenharmony_ci * to given device 44762306a36Sopenharmony_ci * @dev: pointer to the device to configure 44862306a36Sopenharmony_ci * @np: pointer to the device node with 'memory-region' property 44962306a36Sopenharmony_ci * @name: name of the selected memory region 45062306a36Sopenharmony_ci * 45162306a36Sopenharmony_ci * Returns: 0 on success or a negative error-code on failure. 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ciint of_reserved_mem_device_init_by_name(struct device *dev, 45462306a36Sopenharmony_ci struct device_node *np, 45562306a36Sopenharmony_ci const char *name) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci int idx = of_property_match_string(np, "memory-region-names", name); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci return of_reserved_mem_device_init_by_idx(dev, np, idx); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_reserved_mem_device_init_by_name); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci/** 46462306a36Sopenharmony_ci * of_reserved_mem_device_release() - release reserved memory device structures 46562306a36Sopenharmony_ci * @dev: Pointer to the device to deconfigure 46662306a36Sopenharmony_ci * 46762306a36Sopenharmony_ci * This function releases structures allocated for memory region handling for 46862306a36Sopenharmony_ci * the given device. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_civoid of_reserved_mem_device_release(struct device *dev) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct rmem_assigned_device *rd, *tmp; 47362306a36Sopenharmony_ci LIST_HEAD(release_list); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci mutex_lock(&of_rmem_assigned_device_mutex); 47662306a36Sopenharmony_ci list_for_each_entry_safe(rd, tmp, &of_rmem_assigned_device_list, list) { 47762306a36Sopenharmony_ci if (rd->dev == dev) 47862306a36Sopenharmony_ci list_move_tail(&rd->list, &release_list); 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci mutex_unlock(&of_rmem_assigned_device_mutex); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci list_for_each_entry_safe(rd, tmp, &release_list, list) { 48362306a36Sopenharmony_ci if (rd->rmem && rd->rmem->ops && rd->rmem->ops->device_release) 48462306a36Sopenharmony_ci rd->rmem->ops->device_release(rd->rmem, dev); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci kfree(rd); 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_reserved_mem_device_release); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci/** 49262306a36Sopenharmony_ci * of_reserved_mem_lookup() - acquire reserved_mem from a device node 49362306a36Sopenharmony_ci * @np: node pointer of the desired reserved-memory region 49462306a36Sopenharmony_ci * 49562306a36Sopenharmony_ci * This function allows drivers to acquire a reference to the reserved_mem 49662306a36Sopenharmony_ci * struct based on a device node handle. 49762306a36Sopenharmony_ci * 49862306a36Sopenharmony_ci * Returns a reserved_mem reference, or NULL on error. 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_cistruct reserved_mem *of_reserved_mem_lookup(struct device_node *np) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci const char *name; 50362306a36Sopenharmony_ci int i; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (!np->full_name) 50662306a36Sopenharmony_ci return NULL; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci name = kbasename(np->full_name); 50962306a36Sopenharmony_ci for (i = 0; i < reserved_mem_count; i++) 51062306a36Sopenharmony_ci if (!strcmp(reserved_mem[i].name, name)) 51162306a36Sopenharmony_ci return &reserved_mem[i]; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return NULL; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_reserved_mem_lookup); 516