diff options
Diffstat (limited to 'drivers/of')
| -rw-r--r-- | drivers/of/address.c | 4 | ||||
| -rw-r--r-- | drivers/of/base.c | 47 | ||||
| -rw-r--r-- | drivers/of/fdt.c | 101 | ||||
| -rw-r--r-- | drivers/of/irq.c | 24 | ||||
| -rw-r--r-- | drivers/of/of_kunit_helpers.c | 5 | ||||
| -rw-r--r-- | drivers/of/of_reserved_mem.c | 69 | ||||
| -rw-r--r-- | drivers/of/overlay.c | 3 |
7 files changed, 164 insertions, 89 deletions
diff --git a/drivers/of/address.c b/drivers/of/address.c index f0f8f0dd191c..4034d798c55a 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -328,10 +328,6 @@ static int of_bus_default_flags_match(struct device_node *np) static int of_bus_default_match(struct device_node *np) { - /* - * Check for presence first since of_bus_n_addr_cells() will warn when - * walking parent nodes. - */ return of_property_present(np, "#address-cells"); } diff --git a/drivers/of/base.c b/drivers/of/base.c index 7043acd971a0..0b65039ece53 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -434,6 +434,53 @@ 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 + * @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. + */ +bool of_machine_device_match(const struct of_device_id *matches) +{ + struct device_node *root; + const struct of_device_id *match = NULL; + + root = of_find_node_by_path("/"); + if (root) { + match = of_match_node(matches, root); + of_node_put(root); + } + + return match != NULL; +} +EXPORT_SYMBOL(of_machine_device_match); + +/** + * of_machine_get_match_data - Tell if root of device tree has a matching of_match structure + * @matches: NULL terminated array of of_device_id match structures to search in + * + * Returns data associated with matched entry or NULL + */ +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); + + if (!match) + return NULL; + + return match->data; +} +EXPORT_SYMBOL(of_machine_get_match_data); + static bool __of_device_is_status(const struct device_node *device, const char * const*strings) { diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 0edd639898a6..d378d4b4109f 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -625,6 +625,47 @@ const void *__init of_get_flat_dt_prop(unsigned long node, const char *name, return fdt_getprop(initial_boot_params, node, name, size); } +const __be32 *__init of_flat_dt_get_addr_size_prop(unsigned long node, + const char *name, + int *entries) +{ + const __be32 *prop; + int len, elen = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); + + prop = of_get_flat_dt_prop(node, name, &len); + if (!prop || len % elen) { + *entries = 0; + return NULL; + } + + *entries = len / elen; + return prop; +} + +bool __init of_flat_dt_get_addr_size(unsigned long node, const char *name, + u64 *addr, u64 *size) +{ + const __be32 *prop; + int entries; + + prop = of_flat_dt_get_addr_size_prop(node, name, &entries); + if (!prop || entries != 1) + return false; + + of_flat_dt_read_addr_size(prop, 0, addr, size); + return true; +} + +void __init of_flat_dt_read_addr_size(const __be32 *prop, int entry_index, + u64 *addr, u64 *size) +{ + int entry_cells = dt_root_addr_cells + dt_root_size_cells; + prop += entry_cells * entry_index; + + *addr = dt_mem_next_cell(dt_root_addr_cells, &prop); + *size = dt_mem_next_cell(dt_root_size_cells, &prop); +} + /** * of_fdt_is_compatible - Return true if given node from the given blob has * compat in its compatible list @@ -812,21 +853,15 @@ static void __init early_init_dt_check_for_initrd(unsigned long node) */ static void __init early_init_dt_check_for_elfcorehdr(unsigned long node) { - const __be32 *prop; - int len; - if (!IS_ENABLED(CONFIG_CRASH_DUMP)) return; pr_debug("Looking for elfcorehdr property... "); - prop = of_get_flat_dt_prop(node, "linux,elfcorehdr", &len); - if (!prop || (len < (dt_root_addr_cells + dt_root_size_cells))) + if (!of_flat_dt_get_addr_size(node, "linux,elfcorehdr", + &elfcorehdr_addr, &elfcorehdr_size)) return; - elfcorehdr_addr = dt_mem_next_cell(dt_root_addr_cells, &prop); - elfcorehdr_size = dt_mem_next_cell(dt_root_size_cells, &prop); - pr_debug("elfcorehdr_start=0x%llx elfcorehdr_size=0x%llx\n", elfcorehdr_addr, elfcorehdr_size); } @@ -849,8 +884,9 @@ static unsigned long chosen_node_offset = -FDT_ERR_NOTFOUND; void __init early_init_dt_check_for_usable_mem_range(void) { struct memblock_region rgn[MAX_USABLE_RANGES] = {0}; - const __be32 *prop, *endp; + const __be32 *prop; int len, i; + u64 base, size; unsigned long node = chosen_node_offset; if ((long)node < 0) @@ -858,14 +894,17 @@ void __init early_init_dt_check_for_usable_mem_range(void) pr_debug("Looking for usable-memory-range property... "); - prop = of_get_flat_dt_prop(node, "linux,usable-memory-range", &len); - if (!prop || (len % (dt_root_addr_cells + dt_root_size_cells))) + prop = of_flat_dt_get_addr_size_prop(node, "linux,usable-memory-range", + &len); + if (!prop) return; - endp = prop + (len / sizeof(__be32)); - for (i = 0; i < MAX_USABLE_RANGES && prop < endp; i++) { - rgn[i].base = dt_mem_next_cell(dt_root_addr_cells, &prop); - rgn[i].size = dt_mem_next_cell(dt_root_size_cells, &prop); + len = min(len, MAX_USABLE_RANGES); + + for (i = 0; i < len; i++) { + of_flat_dt_read_addr_size(prop, i, &base, &size); + rgn[i].base = base; + rgn[i].size = size; pr_debug("cap_mem_regions[%d]: base=%pa, size=%pa\n", i, &rgn[i].base, &rgn[i].size); @@ -883,26 +922,18 @@ static void __init early_init_dt_check_kho(void) { unsigned long node = chosen_node_offset; u64 fdt_start, fdt_size, scratch_start, scratch_size; - const __be32 *p; - int l; if (!IS_ENABLED(CONFIG_KEXEC_HANDOVER) || (long)node < 0) return; - p = of_get_flat_dt_prop(node, "linux,kho-fdt", &l); - if (l != (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32)) + if (!of_flat_dt_get_addr_size(node, "linux,kho-fdt", + &fdt_start, &fdt_size)) return; - fdt_start = dt_mem_next_cell(dt_root_addr_cells, &p); - fdt_size = dt_mem_next_cell(dt_root_addr_cells, &p); - - p = of_get_flat_dt_prop(node, "linux,kho-scratch", &l); - if (l != (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32)) + if (!of_flat_dt_get_addr_size(node, "linux,kho-scratch", + &scratch_start, &scratch_size)) return; - scratch_start = dt_mem_next_cell(dt_root_addr_cells, &p); - scratch_size = dt_mem_next_cell(dt_root_addr_cells, &p); - kho_populate(fdt_start, fdt_size, scratch_start, scratch_size); } @@ -1002,8 +1033,8 @@ int __init early_init_dt_scan_memory(void) fdt_for_each_subnode(node, fdt, 0) { const char *type = of_get_flat_dt_prop(node, "device_type", NULL); - const __be32 *reg, *endp; - int l; + const __be32 *reg; + int i, l; bool hotpluggable; /* We are scanning "memory" nodes only */ @@ -1013,23 +1044,21 @@ int __init early_init_dt_scan_memory(void) if (!of_fdt_device_is_available(fdt, node)) continue; - reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l); + reg = of_flat_dt_get_addr_size_prop(node, "linux,usable-memory", &l); if (reg == NULL) - reg = of_get_flat_dt_prop(node, "reg", &l); + reg = of_flat_dt_get_addr_size_prop(node, "reg", &l); if (reg == NULL) continue; - endp = reg + (l / sizeof(__be32)); hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL); - pr_debug("memory scan node %s, reg size %d,\n", + pr_debug("memory scan node %s, reg {addr,size} entries %d,\n", fdt_get_name(fdt, node, NULL), l); - while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { + for (i = 0; i < l; i++) { u64 base, size; - base = dt_mem_next_cell(dt_root_addr_cells, ®); - size = dt_mem_next_cell(dt_root_size_cells, ®); + of_flat_dt_read_addr_size(reg, i, &base, &size); if (size == 0) continue; diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 1cd93549d093..e3816819dbfe 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -479,6 +479,26 @@ out: } EXPORT_SYMBOL_GPL(of_irq_get); +const struct cpumask *of_irq_get_affinity(struct device_node *dev, int index) +{ + struct of_phandle_args oirq; + struct irq_fwspec_info info; + struct irq_fwspec fwspec; + int rc; + + rc = of_irq_parse_one(dev, index, &oirq); + if (rc) + return NULL; + + of_phandle_args_to_fwspec(oirq.np, oirq.args, oirq.args_count, + &fwspec); + + if (irq_populate_fwspec_info(&fwspec, &info)) + return NULL; + + return info.affinity; +} + /** * of_irq_get_byname - Decode a node's IRQ and return it as a Linux IRQ number * @dev: pointer to device tree node @@ -593,8 +613,10 @@ void __init of_irq_init(const struct of_device_id *matches) * are the same distance away from the root irq controller. */ desc->interrupt_parent = of_parse_phandle(np, "interrupts-extended", 0); - if (!desc->interrupt_parent) + if (!desc->interrupt_parent && of_property_present(np, "interrupts")) desc->interrupt_parent = of_irq_find_parent(np); + else if (!desc->interrupt_parent) + desc->interrupt_parent = of_parse_phandle(np, "interrupt-parent", 0); if (desc->interrupt_parent == np) { of_node_put(desc->interrupt_parent); desc->interrupt_parent = NULL; diff --git a/drivers/of/of_kunit_helpers.c b/drivers/of/of_kunit_helpers.c index 7b3ed5a382aa..f6ed1af8b62a 100644 --- a/drivers/of/of_kunit_helpers.c +++ b/drivers/of/of_kunit_helpers.c @@ -18,8 +18,9 @@ */ void of_root_kunit_skip(struct kunit *test) { - if (IS_ENABLED(CONFIG_ARM64) && IS_ENABLED(CONFIG_ACPI) && !of_root) - kunit_skip(test, "arm64+acpi doesn't populate a root node"); + if ((IS_ENABLED(CONFIG_ARM64) || IS_ENABLED(CONFIG_RISCV)) && + IS_ENABLED(CONFIG_ACPI) && !of_root) + kunit_skip(test, "arm64/riscv+acpi doesn't populate a root node"); } EXPORT_SYMBOL_GPL(of_root_kunit_skip); diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 2e9ea751ed2d..5619ec917858 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -154,27 +154,24 @@ static int __init early_init_dt_reserve_memory(phys_addr_t base, static int __init __reserved_mem_reserve_reg(unsigned long node, const char *uname) { - int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); phys_addr_t base, size; - int len; + int i, len; const __be32 *prop; bool nomap; - prop = of_get_flat_dt_prop(node, "reg", &len); + prop = of_flat_dt_get_addr_size_prop(node, "reg", &len); if (!prop) return -ENOENT; - if (len && len % t_len != 0) { - pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n", - uname); - return -EINVAL; - } - nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; - while (len >= t_len) { - base = dt_mem_next_cell(dt_root_addr_cells, &prop); - size = dt_mem_next_cell(dt_root_size_cells, &prop); + 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. */ @@ -187,8 +184,6 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, pr_err("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n", uname, &base, (unsigned long)(size / SZ_1M)); } - - len -= t_len; } return 0; } @@ -230,12 +225,9 @@ static void __init __rmem_check_for_overlap(void); */ void __init fdt_scan_reserved_mem_reg_nodes(void) { - int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); const void *fdt = initial_boot_params; phys_addr_t base, size; - const __be32 *prop; int node, child; - int len; if (!fdt) return; @@ -256,29 +248,21 @@ void __init fdt_scan_reserved_mem_reg_nodes(void) fdt_for_each_subnode(child, fdt, node) { const char *uname; + u64 b, s; - prop = of_get_flat_dt_prop(child, "reg", &len); - if (!prop) - continue; if (!of_fdt_device_is_available(fdt, child)) continue; - uname = fdt_get_name(fdt, child, NULL); - if (len && len % t_len != 0) { - pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n", - uname); + if (!of_flat_dt_get_addr_size(child, "reg", &b, &s)) continue; - } - if (len > t_len) - pr_warn("%s() ignores %d regions in node '%s'\n", - __func__, len / t_len - 1, uname); + base = b; + size = s; - base = dt_mem_next_cell(dt_root_addr_cells, &prop); - size = dt_mem_next_cell(dt_root_size_cells, &prop); - - if (size) + if (size) { + uname = fdt_get_name(fdt, child, NULL); fdt_reserved_mem_save_node(child, uname, base, size); + } } /* check for overlapping reserved regions */ @@ -401,10 +385,9 @@ static int __init __reserved_mem_alloc_in_range(phys_addr_t size, */ 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; bool nomap; int ret; @@ -438,19 +421,15 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam && !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; - } + 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; base = 0; ret = __reserved_mem_alloc_in_range(size, align, @@ -461,9 +440,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam (unsigned long)(size / SZ_1M)); break; } - len -= t_len; } - } else { ret = early_init_dt_alloc_reserved_memory_arch(size, align, 0, 0, nomap, &base); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 255e8362f600..5b4f42230e6c 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -1190,6 +1190,9 @@ int of_overlay_remove(int *ovcs_id) struct overlay_changeset *ovcs; int ret, ret_apply, ret_tmp; + if (*ovcs_id == 0) + return 0; + if (devicetree_corrupt()) { pr_err("suspect devicetree state, refuse to remove overlay\n"); ret = -EBUSY; |
