diff options
Diffstat (limited to 'drivers/of')
| -rw-r--r-- | drivers/of/base.c | 50 | ||||
| -rw-r--r-- | drivers/of/dynamic.c | 2 | ||||
| -rw-r--r-- | drivers/of/fdt.c | 65 | ||||
| -rw-r--r-- | drivers/of/kexec.c | 21 | ||||
| -rw-r--r-- | drivers/of/of_private.h | 2 | ||||
| -rw-r--r-- | drivers/of/of_reserved_mem.c | 320 | ||||
| -rw-r--r-- | drivers/of/platform.c | 14 | ||||
| -rw-r--r-- | drivers/of/property.c | 28 | ||||
| -rw-r--r-- | drivers/of/unittest.c | 4 |
9 files changed, 319 insertions, 187 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c index 57420806c1a2..a650c91897cc 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -435,13 +435,40 @@ bool of_machine_compatible_match(const char *const *compats) EXPORT_SYMBOL(of_machine_compatible_match); /** - * of_machine_device_match - Test root of device tree against a of_device_id array + * of_machine_read_compatible - Get the compatible string of this machine + * @compatible: address at which the address of the compatible string will be + * stored + * @index: index of the compatible entry in the list + * + * Returns: + * 0 on success, negative error number on failure. + */ +int of_machine_read_compatible(const char **compatible, unsigned int index) +{ + return of_property_read_string_index(of_root, "compatible", index, compatible); +} +EXPORT_SYMBOL_GPL(of_machine_read_compatible); + +/** + * of_machine_read_model - Get the model string of this machine + * @model: address at which the address of the model string will be stored + * + * Returns: + * 0 on success, negative error number on failure. + */ +int of_machine_read_model(const char **model) +{ + return of_property_read_string(of_root, "model", model); +} +EXPORT_SYMBOL_GPL(of_machine_read_model); + +/** + * of_machine_get_match - Test root of device tree against an of_device_id array * @matches: NULL terminated array of of_device_id match structures to search in * - * Returns true if the root node has any of the given compatible values in its - * compatible property. + * Returns matched entry or NULL */ -bool of_machine_device_match(const struct of_device_id *matches) +const struct of_device_id *of_machine_get_match(const struct of_device_id *matches) { struct device_node *root; const struct of_device_id *match = NULL; @@ -452,9 +479,9 @@ bool of_machine_device_match(const struct of_device_id *matches) of_node_put(root); } - return match != NULL; + return match; } -EXPORT_SYMBOL(of_machine_device_match); +EXPORT_SYMBOL(of_machine_get_match); /** * of_machine_get_match_data - Tell if root of device tree has a matching of_match structure @@ -465,15 +492,8 @@ EXPORT_SYMBOL(of_machine_device_match); const void *of_machine_get_match_data(const struct of_device_id *matches) { const struct of_device_id *match; - struct device_node *root; - - root = of_find_node_by_path("/"); - if (!root) - return NULL; - - match = of_match_node(matches, root); - of_node_put(root); + match = of_machine_get_match(matches); if (!match) return NULL; @@ -1915,7 +1935,7 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) if (name) of_stdout = of_find_node_opts_by_path(name, &of_stdout_options); if (of_stdout) - of_stdout->fwnode.flags |= FWNODE_FLAG_BEST_EFFORT; + fwnode_set_flag(&of_stdout->fwnode, FWNODE_FLAG_BEST_EFFORT); } if (!of_aliases) diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 1a06175def37..ade288372101 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -225,7 +225,7 @@ static void __of_attach_node(struct device_node *np) np->sibling = np->parent->child; np->parent->child = np; of_node_clear_flag(np, OF_DETACHED); - np->fwnode.flags |= FWNODE_FLAG_NOT_DEVICE; + fwnode_set_flag(&np->fwnode, FWNODE_FLAG_NOT_DEVICE); raw_spin_unlock_irqrestore(&devtree_lock, flags); diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 331646d667b9..82f7327c59ea 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -68,7 +68,7 @@ void __init of_fdt_limit_memory(int limit) bool of_fdt_device_is_available(const void *blob, unsigned long node) { - const char *status = fdt_getprop(blob, node, "status", NULL); + const char *status = fdt_stringlist_get(blob, node, "status", 0, NULL); if (!status) return true; @@ -677,22 +677,15 @@ void __init of_flat_dt_read_addr_size(const __be32 *prop, int entry_index, * specific compatible values. */ static int of_fdt_is_compatible(const void *blob, - unsigned long node, const char *compat) + unsigned long node, const char *compat) { const char *cp; - int cplen; - unsigned long l, score = 0; + int idx = 0, score = 0; - cp = fdt_getprop(blob, node, "compatible", &cplen); - if (cp == NULL) - return 0; - while (cplen > 0) { + while ((cp = fdt_stringlist_get(blob, node, "compatible", idx++, NULL))) { score++; if (of_compat_cmp(cp, compat, strlen(compat)) == 0) return score; - l = strlen(cp) + 1; - cp += l; - cplen -= l; } return 0; @@ -741,9 +734,10 @@ const char * __init of_flat_dt_get_machine_name(void) const char *name; unsigned long dt_root = of_get_flat_dt_root(); - name = of_get_flat_dt_prop(dt_root, "model", NULL); + name = fdt_stringlist_get(initial_boot_params, dt_root, "model", 0, NULL); if (!name) - name = of_get_flat_dt_prop(dt_root, "compatible", NULL); + name = fdt_stringlist_get(initial_boot_params, dt_root, + "compatible", 0, NULL); return name; } @@ -775,19 +769,14 @@ const void * __init of_flat_dt_match_machine(const void *default_match, } if (!best_data) { const char *prop; - int size; + int idx = 0, size; pr_err("\n unrecognized device tree list:\n[ "); - prop = of_get_flat_dt_prop(dt_root, "compatible", &size); - if (prop) { - while (size > 0) { - printk("'%s' ", prop); - size -= strlen(prop) + 1; - prop += strlen(prop) + 1; - } - } - printk("]\n\n"); + while ((prop = fdt_stringlist_get(initial_boot_params, dt_root, + "compatible", idx++, &size))) + pr_err("'%s' ", prop); + pr_err("]\n\n"); return NULL; } @@ -866,6 +855,26 @@ static void __init early_init_dt_check_for_elfcorehdr(unsigned long node) elfcorehdr_addr, elfcorehdr_size); } +static void __init early_init_dt_check_for_dmcryptkeys(unsigned long node) +{ + const char *prop_name = "linux,dmcryptkeys"; + const __be32 *prop; + + if (!IS_ENABLED(CONFIG_CRASH_DM_CRYPT)) + return; + + pr_debug("Looking for dmcryptkeys property... "); + + prop = of_get_flat_dt_prop(node, prop_name, NULL); + if (!prop) + return; + + dm_crypt_keys_addr = dt_mem_next_cell(dt_root_addr_cells, &prop); + + /* Property only accessible to crash dump kernel */ + fdt_delprop(initial_boot_params, node, prop_name); +} + static unsigned long chosen_node_offset = -FDT_ERR_NOTFOUND; /* @@ -954,9 +963,9 @@ int __init early_init_dt_scan_chosen_stdout(void) if (offset < 0) return -ENOENT; - p = fdt_getprop(fdt, offset, "stdout-path", &l); + p = fdt_stringlist_get(fdt, offset, "stdout-path", 0, &l); if (!p) - p = fdt_getprop(fdt, offset, "linux,stdout-path", &l); + p = fdt_stringlist_get(fdt, offset, "linux,stdout-path", 0, &l); if (!p || !l) return -ENOENT; @@ -1032,7 +1041,8 @@ int __init early_init_dt_scan_memory(void) const void *fdt = initial_boot_params; fdt_for_each_subnode(node, fdt, 0) { - const char *type = of_get_flat_dt_prop(node, "device_type", NULL); + const char *type = fdt_stringlist_get(fdt, node, + "device_type", 0, NULL); const __be32 *reg; int i, l; bool hotpluggable; @@ -1097,6 +1107,7 @@ int __init early_init_dt_scan_chosen(char *cmdline) early_init_dt_check_for_initrd(node); early_init_dt_check_for_elfcorehdr(node); + early_init_dt_check_for_dmcryptkeys(node); rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l); if (rng_seed && l > 0) { @@ -1274,7 +1285,7 @@ void __init unflatten_device_tree(void) void *fdt = initial_boot_params; /* Save the statically-placed regions in the reserved_mem array */ - fdt_scan_reserved_mem_reg_nodes(); + fdt_scan_reserved_mem_late(); /* Populate an empty root node when bootloader doesn't provide one */ if (!fdt) { diff --git a/drivers/of/kexec.c b/drivers/of/kexec.c index c4cf3552c018..b6837e299e7f 100644 --- a/drivers/of/kexec.c +++ b/drivers/of/kexec.c @@ -175,7 +175,7 @@ int __init ima_free_kexec_buffer(void) if (ret) return ret; - memblock_free_late(addr, size); + memblock_phys_free(addr, size); return 0; } #endif @@ -423,6 +423,25 @@ void *of_kexec_alloc_and_setup_fdt(const struct kimage *image, if (ret) goto out; + if (image->dm_crypt_keys_addr != 0) { + ret = fdt_appendprop_addrrange(fdt, 0, chosen_node, + "linux,dmcryptkeys", + image->dm_crypt_keys_addr, + image->dm_crypt_keys_sz); + + if (ret) + goto out; + + /* + * Avoid dmcryptkeys from being stomped on in kdump kernel by + * setting up memory reserve map. + */ + ret = fdt_add_mem_rsv(fdt, image->dm_crypt_keys_addr, + image->dm_crypt_keys_sz); + if (ret) + goto out; + } + #ifdef CONFIG_CRASH_DUMP /* add linux,usable-memory-range */ ret = fdt_appendprop_addrrange(fdt, 0, chosen_node, diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index df0bb00349e0..0ae16da066e2 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -186,7 +186,7 @@ static inline struct device_node *__of_get_dma_parent(const struct device_node * #endif int fdt_scan_reserved_mem(void); -void __init fdt_scan_reserved_mem_reg_nodes(void); +void __init fdt_scan_reserved_mem_late(void); bool of_fdt_device_is_available(const void *blob, unsigned long node); diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 1fd28f805610..8d5777cb5d1b 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -24,8 +24,6 @@ #include <linux/slab.h> #include <linux/memblock.h> #include <linux/kmemleak.h> -#include <linux/cma.h> -#include <linux/dma-map-ops.h> #include "of_private.h" @@ -104,30 +102,12 @@ static void __init alloc_reserved_mem_array(void) 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 - */ -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 == total_reserved_mem_cnt) { - pr_err("not enough space for all defined regions.\n"); - return; - } - - rmem->fdt_node = node; - rmem->name = uname; - rmem->base = base; - rmem->size = size; - - /* Call the region specific initialization function */ - fdt_init_reserved_mem_node(rmem); - - reserved_mem_count++; -} +static void fdt_init_reserved_mem_node(unsigned long node, const char *uname, + phys_addr_t base, phys_addr_t size); +static int fdt_validate_reserved_mem_node(unsigned long node, + phys_addr_t *align); +static int fdt_fixup_reserved_mem_node(unsigned long node, + phys_addr_t base, phys_addr_t size); static int __init early_init_dt_reserve_memory(phys_addr_t base, phys_addr_t size, bool nomap) @@ -154,21 +134,19 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, const char *uname) { phys_addr_t base, size; - int i, len; + int i, len, err; const __be32 *prop; - bool nomap, default_cma; + 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; - default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL); - if (default_cma && cma_skip_dt_default_reserved_mem()) { - pr_err("Skipping dt linux,cma-default for \"cma=\" kernel param.\n"); - return -EINVAL; - } + err = fdt_validate_reserved_mem_node(node, NULL); + if (err && err != -ENODEV) + return err; for (i = 0; i < len; i++) { u64 b, s; @@ -179,10 +157,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, 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); + fdt_fixup_reserved_mem_node(node, 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 { @@ -216,19 +191,66 @@ static int __init __reserved_mem_check_root(unsigned long node) return 0; } -static void __init __rmem_check_for_overlap(void); +static int __init __rmem_cmp(const void *a, const void *b) +{ + const struct reserved_mem *ra = a, *rb = b; + + if (ra->base < rb->base) + return -1; + + 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; + + return 0; +} + +static void __init __rmem_check_for_overlap(void) +{ + int i; + + if (reserved_mem_count < 2) + return; + + sort(reserved_mem, reserved_mem_count, sizeof(reserved_mem[0]), + __rmem_cmp, NULL); + for (i = 0; i < reserved_mem_count - 1; i++) { + struct reserved_mem *this, *next; + + this = &reserved_mem[i]; + next = &reserved_mem[i + 1]; + + if (this->base + this->size > next->base) { + phys_addr_t this_end, next_end; + + this_end = this->base + this->size; + next_end = next->base + next->size; + pr_err("OVERLAP DETECTED!\n%s (%pa--%pa) overlaps with %s (%pa--%pa)\n", + this->name, &this->base, &this_end, + next->name, &next->base, &next_end); + } + } +} /** - * fdt_scan_reserved_mem_reg_nodes() - Store info for the "reg" defined - * reserved memory regions. + * fdt_scan_reserved_mem_late() - Scan FDT and initialize remaining 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. + * This function is used to scan again through the DT and initialize the + * "static" reserved memory regions, that are defined using the "reg" + * property. Each such region is then initialized with its specific init + * function and stored in the global reserved_mem array. */ -void __init fdt_scan_reserved_mem_reg_nodes(void) +void __init fdt_scan_reserved_mem_late(void) { const void *fdt = initial_boot_params; phys_addr_t base, size; @@ -253,23 +275,25 @@ void __init fdt_scan_reserved_mem_reg_nodes(void) fdt_for_each_subnode(child, fdt, node) { const char *uname; - bool default_cma = of_get_flat_dt_prop(child, "linux,cma-default", NULL); u64 b, s; + int ret; if (!of_fdt_device_is_available(fdt, child)) continue; - if (default_cma && cma_skip_dt_default_reserved_mem()) - continue; if (!of_flat_dt_get_addr_size(child, "reg", &b, &s)) continue; + ret = fdt_validate_reserved_mem_node(child, NULL); + if (ret && ret != -ENODEV) + continue; + base = b; size = s; if (size) { uname = fdt_get_name(fdt, child, NULL); - fdt_reserved_mem_save_node(child, uname, base, size); + fdt_init_reserved_mem_node(child, uname, base, size); } } @@ -280,7 +304,14 @@ void __init fdt_scan_reserved_mem_reg_nodes(void) 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 + * fdt_scan_reserved_mem() - reserve and allocate memory occupied by + * reserved memory regions. + * + * This function is used to scan through the FDT and mark memory occupied + * by all static (defined by the "reg" property) reserved memory regions. + * Then memory for all dynamic regions (defined by size & alignment) is + * allocated, a region specific init function is called and region information + * is stored in the reserved_mem array. */ int __init fdt_scan_reserved_mem(void) { @@ -397,7 +428,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam phys_addr_t base = 0, align = 0, size; int i, len; const __be32 *prop; - bool nomap, default_cma; + bool nomap; int ret; prop = of_get_flat_dt_prop(node, "size", &len); @@ -421,19 +452,10 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam } nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; - default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL); - if (default_cma && cma_skip_dt_default_reserved_mem()) { - pr_err("Skipping dt linux,cma-default for \"cma=\" kernel param.\n"); - return -EINVAL; - } - - /* 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) - && !nomap) - align = max_t(phys_addr_t, align, CMA_MIN_ALIGNMENT_BYTES); + ret = fdt_validate_reserved_mem_node(node, &align); + if (ret && ret != -ENODEV) + return ret; prop = of_flat_dt_get_addr_size_prop(node, "alloc-ranges", &len); if (prop) { @@ -468,121 +490,151 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam uname, (unsigned long)(size / SZ_1M)); return -ENOMEM; } - /* 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); + + fdt_fixup_reserved_mem_node(node, base, size); + fdt_init_reserved_mem_node(node, uname, base, size); + return 0; } +extern const struct of_device_id __reservedmem_of_table[]; static const struct of_device_id __rmem_of_table_sentinel __used __section("__reservedmem_of_table_end"); -/* - * __reserved_mem_init_node() - call region specific reserved memory init code +/** + * fdt_fixup_reserved_mem_node() - call fixup function for a reserved memory node + * @node: FDT node to fixup + * @base: base address of the reserved memory region + * @size: size of the reserved memory region + * + * This function iterates through the reserved memory drivers and calls + * the node_fixup callback for the compatible entry matching the node. + * + * Return: 0 on success, -ENODEV if no compatible match found */ -static int __init __reserved_mem_init_node(struct reserved_mem *rmem) +static int __init fdt_fixup_reserved_mem_node(unsigned long node, + phys_addr_t base, phys_addr_t size) { - extern const struct of_device_id __reservedmem_of_table[]; const struct of_device_id *i; - int ret = -ENOENT; + int ret = -ENODEV; - for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) { - reservedmem_of_init_fn initfn = i->data; - const char *compat = i->compatible; + for (i = __reservedmem_of_table; ret == -ENODEV && + i < &__rmem_of_table_sentinel; i++) { + const struct reserved_mem_ops *ops = i->data; - if (!of_flat_dt_is_compatible(rmem->fdt_node, compat)) + if (!of_flat_dt_is_compatible(node, i->compatible)) continue; - ret = initfn(rmem); - if (ret == 0) { - pr_info("initialized node %s, compatible id %s\n", - rmem->name, compat); - break; - } + if (ops->node_fixup) + ret = ops->node_fixup(node, base, size); } return ret; } -static int __init __rmem_cmp(const void *a, const void *b) +/** + * fdt_validate_reserved_mem_node() - validate a reserved memory node + * @node: FDT node to validate + * @align: pointer to store the validated alignment (may be modified by callback) + * + * This function iterates through the reserved memory drivers and calls + * the node_validate callback for the compatible entry matching the node. + * + * Return: 0 on success, -ENODEV if no compatible match found + */ +static int __init fdt_validate_reserved_mem_node(unsigned long node, phys_addr_t *align) { - const struct reserved_mem *ra = a, *rb = b; - - if (ra->base < rb->base) - return -1; - - if (ra->base > rb->base) - return 1; + const struct of_device_id *i; + int ret = -ENODEV; - /* - * 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; + for (i = __reservedmem_of_table; ret == -ENODEV && + i < &__rmem_of_table_sentinel; i++) { + const struct reserved_mem_ops *ops = i->data; - if (ra->fdt_node < rb->fdt_node) - return -1; - if (ra->fdt_node > rb->fdt_node) - return 1; + if (!of_flat_dt_is_compatible(node, i->compatible)) + continue; - return 0; + if (ops->node_validate) + ret = ops->node_validate(node, align); + } + return ret; } -static void __init __rmem_check_for_overlap(void) +/** + * __reserved_mem_init_node() - initialize a reserved memory region + * @rmem: reserved_mem structure to initialize + * @node: FDT node describing the reserved memory region + * + * This function iterates through the reserved memory drivers and calls the + * node_init callback for the compatible entry matching the node. On success, + * the operations pointer is stored in the reserved_mem structure. + * + * Return: 0 on success, -ENODEV if no compatible match found + */ +static int __init __reserved_mem_init_node(struct reserved_mem *rmem, + unsigned long node) { - int i; - - if (reserved_mem_count < 2) - return; - - sort(reserved_mem, reserved_mem_count, sizeof(reserved_mem[0]), - __rmem_cmp, NULL); - for (i = 0; i < reserved_mem_count - 1; i++) { - struct reserved_mem *this, *next; + const struct of_device_id *i; + int ret = -ENODEV; - this = &reserved_mem[i]; - next = &reserved_mem[i + 1]; + for (i = __reservedmem_of_table; ret == -ENODEV && + i < &__rmem_of_table_sentinel; i++) { + const struct reserved_mem_ops *ops = i->data; + const char *compat = i->compatible; - if (this->base + this->size > next->base) { - phys_addr_t this_end, next_end; + if (!of_flat_dt_is_compatible(node, compat)) + continue; - this_end = this->base + this->size; - next_end = next->base + next->size; - pr_err("OVERLAP DETECTED!\n%s (%pa--%pa) overlaps with %s (%pa--%pa)\n", - this->name, &this->base, &this_end, - next->name, &next->base, &next_end); + ret = ops->node_init(node, rmem); + if (ret == 0) { + rmem->ops = ops; + pr_info("initialized node %s, compatible id %s\n", + rmem->name, compat); + return ret; } } + return ret; } /** * fdt_init_reserved_mem_node() - Initialize a reserved memory region - * @rmem: reserved_mem struct of the memory region to be initialized. + * @node: fdt node of the initialized region + * @uname: name of the reserved memory node + * @base: base address of the reserved memory region + * @size: size of the reserved memory region * - * This function is used to call the region specific initialization - * function for a reserved memory region. + * This function calls the region-specific initialization function for a + * reserved memory region and saves all region-specific data to the + * reserved_mem array to allow of_reserved_mem_lookup() to find it. */ -static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem) +static void __init fdt_init_reserved_mem_node(unsigned long node, const char *uname, + phys_addr_t base, phys_addr_t size) { - unsigned long node = rmem->fdt_node; int err = 0; bool nomap; + struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; + + if (reserved_mem_count == total_reserved_mem_cnt) { + pr_err("not enough space for all defined regions.\n"); + return; + } + + rmem->name = uname; + rmem->base = base; + rmem->size = size; + nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; - err = __reserved_mem_init_node(rmem); - if (err != 0 && err != -ENOENT) { + err = __reserved_mem_init_node(rmem, node); + if (err != 0 && err != -ENODEV) { pr_info("node %s compatible matching fail\n", rmem->name); + rmem->name = NULL; + if (nomap) memblock_clear_nomap(rmem->base, rmem->size); else memblock_phys_free(rmem->base, rmem->size); + return; } else { phys_addr_t end = rmem->base + rmem->size - 1; bool reusable = @@ -594,6 +646,8 @@ static void __init fdt_init_reserved_mem_node(struct reserved_mem *rmem) reusable ? "reusable" : "non-reusable", rmem->name ? rmem->name : "unknown"); } + + reserved_mem_count++; } struct rmem_assigned_device { diff --git a/drivers/of/platform.c b/drivers/of/platform.c index ba591fbceb56..a42224f9d1a8 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -500,7 +500,7 @@ static const struct of_device_id reserved_mem_matches[] = { static int __init of_platform_default_populate_init(void) { - struct device_node *node; + struct device_node *node, *reserved; device_links_supplier_sync_state_pause(); @@ -563,8 +563,14 @@ static int __init of_platform_default_populate_init(void) * platform_devices for every node in /reserved-memory with a * "compatible", */ - for_each_matching_node(node, reserved_mem_matches) - of_platform_device_create(node, NULL, NULL); + reserved = of_find_node_by_path("/reserved-memory"); + if (reserved) { + for_each_child_of_node(reserved, node) { + if (of_match_node(reserved_mem_matches, node)) + of_platform_device_create(node, NULL, NULL); + } + of_node_put(reserved); + } node = of_find_node_by_path("/firmware"); if (node) { @@ -742,7 +748,7 @@ static int of_platform_notify(struct notifier_block *nb, * Clear the flag before adding the device so that fw_devlink * doesn't skip adding consumers to this device. */ - rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_DEVICE; + fwnode_clear_flag(&rd->dn->fwnode, FWNODE_FLAG_NOT_DEVICE); /* pdev_parent may be NULL when no bus platform device */ pdev_parent = of_find_device_by_node(parent); pdev = of_platform_device_create(rd->dn, NULL, diff --git a/drivers/of/property.c b/drivers/of/property.c index 50d95d512bf5..136946f8b746 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -88,7 +88,7 @@ EXPORT_SYMBOL(of_graph_is_present); * Search for a property in a device node and count the number of elements of * size elem_size in it. * - * Return: The number of elements on sucess, -EINVAL if the property does not + * Return: The number of elements on success, -EINVAL if the property does not * exist or its length does not match a multiple of elem_size and -ENODATA if * the property does not have a value. */ @@ -1620,12 +1620,36 @@ static int of_fwnode_irq_get(const struct fwnode_handle *fwnode, return of_irq_get(to_of_node(fwnode), index); } +static int match_property_by_path(const char *node_path, const char *prop_name, + const char *value) +{ + struct device_node *np __free(device_node) = of_find_node_by_path(node_path); + + return of_property_match_string(np, prop_name, value); +} + +static bool of_is_fwnode_add_links_supported(void) +{ + static int is_supported = -1; + + if (!IS_ENABLED(CONFIG_X86)) + return true; + + if (is_supported != -1) + return !!is_supported; + + is_supported = !((match_property_by_path("/soc", "compatible", "intel,ce4100-cp") >= 0) || + (match_property_by_path("/", "architecture", "OLPC") >= 0)); + + return !!is_supported; +} + static int of_fwnode_add_links(struct fwnode_handle *fwnode) { const struct property *p; struct device_node *con_np = to_of_node(fwnode); - if (IS_ENABLED(CONFIG_X86)) + if (!of_is_fwnode_add_links_supported()) return 0; if (!con_np) diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 2940295843e6..4078569a0f96 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -896,8 +896,6 @@ static void __init of_unittest_changeset(void) unittest(!of_changeset_apply(&chgset), "apply failed\n"); - of_node_put(nchangeset); - /* Make sure node names are constructed correctly */ unittest((np = of_find_node_by_path("/testcase-data/changeset/n2/n21")), "'%pOF' not added\n", n21); @@ -919,6 +917,7 @@ static void __init of_unittest_changeset(void) if (!ret) unittest(strcmp(propstr, "hello") == 0, "original value not in updated property after revert"); + of_node_put(nchangeset); of_changeset_destroy(&chgset); of_node_put(n1); @@ -4318,7 +4317,6 @@ static int testdrv_probe(struct pci_dev *pdev, const struct pci_device_id *id) size = info->dtbo_end - info->dtbo_begin; ret = of_overlay_fdt_apply(info->dtbo_begin, size, &ovcs_id, dn); - of_node_put(dn); if (ret) return ret; |
