diff options
Diffstat (limited to 'drivers/of')
| -rw-r--r-- | drivers/of/address.c | 13 | ||||
| -rw-r--r-- | drivers/of/base.c | 33 | ||||
| -rw-r--r-- | drivers/of/device.c | 38 | ||||
| -rw-r--r-- | drivers/of/fdt.c | 34 | ||||
| -rw-r--r-- | drivers/of/irq.c | 84 | ||||
| -rw-r--r-- | drivers/of/kexec.c | 42 | ||||
| -rw-r--r-- | drivers/of/of_private.h | 7 | ||||
| -rw-r--r-- | drivers/of/of_reserved_mem.c | 84 | ||||
| -rw-r--r-- | drivers/of/overlay.c | 10 | ||||
| -rw-r--r-- | drivers/of/platform.c | 8 | ||||
| -rw-r--r-- | drivers/of/property.c | 33 | ||||
| -rw-r--r-- | drivers/of/resolver.c | 41 | ||||
| -rw-r--r-- | drivers/of/unittest-data/tests-interrupts.dtsi | 13 | ||||
| -rw-r--r-- | drivers/of/unittest.c | 77 |
14 files changed, 407 insertions, 110 deletions
diff --git a/drivers/of/address.c b/drivers/of/address.c index d177a2b9edaf..f0f8f0dd191c 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -1031,20 +1031,15 @@ EXPORT_SYMBOL_GPL(of_dma_is_coherent); * * Returns true if the "nonposted-mmio" property was found for * the device's bus. - * - * This is currently only enabled on builds that support Apple ARM devices, as - * an optimization. */ static bool of_mmio_is_nonposted(const struct device_node *np) { - if (!IS_ENABLED(CONFIG_ARCH_APPLE)) - return false; - struct device_node *parent __free(device_node) = of_get_parent(np); - if (!parent) - return false; - return of_property_read_bool(parent, "nonposted-mmio"); + if (of_property_read_bool(np, "nonposted-mmio")) + return true; + + return parent && of_property_read_bool(parent, "nonposted-mmio"); } static int __of_address_to_resource(struct device_node *dev, int index, int bar_no, diff --git a/drivers/of/base.c b/drivers/of/base.c index af6c68bbb427..7043acd971a0 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -824,6 +824,33 @@ struct device_node *of_get_child_by_name(const struct device_node *node, } EXPORT_SYMBOL(of_get_child_by_name); +/** + * of_get_available_child_by_name - Find the available child node by name for a given parent + * @node: parent node + * @name: child name to look for. + * + * This function looks for child node for given matching name and checks the + * device's availability for use. + * + * Return: A node pointer if found, with refcount incremented, use + * of_node_put() on it when done. + * Returns NULL if node is not found. + */ +struct device_node *of_get_available_child_by_name(const struct device_node *node, + const char *name) +{ + struct device_node *child; + + child = of_get_child_by_name(node, name); + if (child && !of_device_is_available(child)) { + of_node_put(child); + return NULL; + } + + return child; +} +EXPORT_SYMBOL(of_get_available_child_by_name); + struct device_node *__of_find_node_by_path(const struct device_node *parent, const char *path) { @@ -1651,7 +1678,7 @@ int __of_add_property(struct device_node *np, struct property *prop) prop->next = NULL; next = &np->properties; while (*next) { - if (strcmp(prop->name, (*next)->name) == 0) { + if (of_prop_cmp(prop->name, (*next)->name) == 0) { /* duplicate ! don't insert it */ rc = -EEXIST; goto out_unlock; @@ -1855,9 +1882,7 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) int id, len; /* Skip those we do not want to proceed */ - if (!strcmp(pp->name, "name") || - !strcmp(pp->name, "phandle") || - !strcmp(pp->name, "linux,phandle")) + if (is_pseudo_property(pp->name)) continue; np = of_find_node_by_path(pp->value); diff --git a/drivers/of/device.c b/drivers/of/device.c index edf3be197265..c80426510ec2 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -35,44 +35,35 @@ EXPORT_SYMBOL(of_match_device); static void of_dma_set_restricted_buffer(struct device *dev, struct device_node *np) { - struct device_node *node, *of_node = dev->of_node; - int count, i; + struct device_node *of_node = dev->of_node; + struct of_phandle_iterator it; + int rc, i = 0; if (!IS_ENABLED(CONFIG_DMA_RESTRICTED_POOL)) return; - count = of_property_count_elems_of_size(of_node, "memory-region", - sizeof(u32)); /* * If dev->of_node doesn't exist or doesn't contain memory-region, try * the OF node having DMA configuration. */ - if (count <= 0) { + if (!of_property_present(of_node, "memory-region")) of_node = np; - count = of_property_count_elems_of_size( - of_node, "memory-region", sizeof(u32)); - } - for (i = 0; i < count; i++) { - node = of_parse_phandle(of_node, "memory-region", i); + of_for_each_phandle(&it, rc, of_node, "memory-region", NULL, 0) { /* * There might be multiple memory regions, but only one * restricted-dma-pool region is allowed. */ - if (of_device_is_compatible(node, "restricted-dma-pool") && - of_device_is_available(node)) { - of_node_put(node); + if (of_device_is_compatible(it.node, "restricted-dma-pool") && + of_device_is_available(it.node)) { + if (of_reserved_mem_device_init_by_idx(dev, of_node, i)) + dev_warn(dev, "failed to initialise \"restricted-dma-pool\" memory node\n"); + of_node_put(it.node); break; } - of_node_put(node); + i++; } - /* - * Attempt to initialize a restricted-dma-pool region if one was found. - * Note that count can hold a negative error code. - */ - if (i < count && of_reserved_mem_device_init_by_idx(dev, of_node, i)) - dev_warn(dev, "failed to initialise \"restricted-dma-pool\" memory node\n"); } /** @@ -99,6 +90,11 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, bool coherent, set_map = false; int ret; + if (dev->dma_range_map) { + dev_dbg(dev, "dma_range_map already set\n"); + goto skip_map; + } + if (np == dev->of_node) bus_np = __of_get_dma_parent(np); else @@ -119,7 +115,7 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, end = dma_range_map_max(map); set_map = true; } - +skip_map: /* * If @dev is expected to be DMA-capable then the bus code that created * it should have initialised its dma_mask pointer by this point. For diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index aedd0e2dcd89..0edd639898a6 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -25,6 +25,7 @@ #include <linux/serial_core.h> #include <linux/sysfs.h> #include <linux/random.h> +#include <linux/kexec_handover.h> #include <asm/setup.h> /* for COMMAND_LINE_SIZE */ #include <asm/page.h> @@ -875,6 +876,36 @@ void __init early_init_dt_check_for_usable_mem_range(void) memblock_add(rgn[i].base, rgn[i].size); } +/** + * early_init_dt_check_kho - Decode info required for kexec handover from DT + */ +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)) + 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)) + 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); +} + #ifdef CONFIG_SERIAL_EARLYCON int __init early_init_dt_scan_chosen_stdout(void) @@ -1169,6 +1200,9 @@ void __init early_init_dt_scan_nodes(void) /* Handle linux,usable-memory-range property */ early_init_dt_check_for_usable_mem_range(); + + /* Handle kexec handover */ + early_init_dt_check_kho(); } bool __init early_init_dt_scan(void *dt_virt, phys_addr_t dt_phys) diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 6c843d54ebb1..f8ad79b9b1c9 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -16,6 +16,7 @@ #define pr_fmt(fmt) "OF: " fmt +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/list.h> @@ -38,11 +39,15 @@ unsigned int irq_of_parse_and_map(struct device_node *dev, int index) { struct of_phandle_args oirq; + unsigned int ret; if (of_irq_parse_one(dev, index, &oirq)) return 0; - return irq_create_of_mapping(&oirq); + ret = irq_create_of_mapping(&oirq); + of_node_put(oirq.np); + + return ret; } EXPORT_SYMBOL_GPL(irq_of_parse_and_map); @@ -50,8 +55,8 @@ EXPORT_SYMBOL_GPL(irq_of_parse_and_map); * of_irq_find_parent - Given a device node, find its interrupt parent node * @child: pointer to device node * - * Return: A pointer to the interrupt parent node, or NULL if the interrupt - * parent could not be determined. + * Return: A pointer to the interrupt parent node with refcount increased + * or NULL if the interrupt parent could not be determined. */ struct device_node *of_irq_find_parent(struct device_node *child) { @@ -165,6 +170,8 @@ const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len, struct of_ph * the specifier for each map, and then returns the translated map. * * Return: 0 on success and a negative number on error + * + * Note: refcount of node @out_irq->np is increased by 1 on success. */ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) { @@ -310,6 +317,12 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) addrsize = (imap - match_array) - intsize; if (ipar == newpar) { + /* + * We got @ipar's refcount, but the refcount was + * gotten again by of_irq_parse_imap_parent() via its + * alias @newpar. + */ + of_node_put(ipar); pr_debug("%pOF interrupt-map entry to self\n", ipar); return 0; } @@ -339,10 +352,12 @@ EXPORT_SYMBOL_GPL(of_irq_parse_raw); * This function resolves an interrupt for a node by walking the interrupt tree, * finding which interrupt controller node it is attached to, and returning the * interrupt specifier that can be used to retrieve a Linux IRQ number. + * + * Note: refcount of node @out_irq->np is increased by 1 on success. */ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq) { - struct device_node *p; + struct device_node __free(device_node) *p = NULL; const __be32 *addr; u32 intsize; int i, res, addr_len; @@ -367,41 +382,33 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar /* Try the new-style interrupts-extended first */ res = of_parse_phandle_with_args(device, "interrupts-extended", "#interrupt-cells", index, out_irq); - if (!res) - return of_irq_parse_raw(addr_buf, out_irq); - - /* Look for the interrupt parent. */ - p = of_irq_find_parent(device); - if (p == NULL) - return -EINVAL; + if (!res) { + p = out_irq->np; + } else { + /* Look for the interrupt parent. */ + p = of_irq_find_parent(device); + /* Get size of interrupt specifier */ + if (!p || of_property_read_u32(p, "#interrupt-cells", &intsize)) + return -EINVAL; + + pr_debug(" parent=%pOF, intsize=%d\n", p, intsize); + + /* Copy intspec into irq structure */ + out_irq->np = p; + out_irq->args_count = intsize; + for (i = 0; i < intsize; i++) { + res = of_property_read_u32_index(device, "interrupts", + (index * intsize) + i, + out_irq->args + i); + if (res) + return res; + } - /* Get size of interrupt specifier */ - if (of_property_read_u32(p, "#interrupt-cells", &intsize)) { - res = -EINVAL; - goto out; + pr_debug(" intspec=%d\n", *out_irq->args); } - pr_debug(" parent=%pOF, intsize=%d\n", p, intsize); - - /* Copy intspec into irq structure */ - out_irq->np = p; - out_irq->args_count = intsize; - for (i = 0; i < intsize; i++) { - res = of_property_read_u32_index(device, "interrupts", - (index * intsize) + i, - out_irq->args + i); - if (res) - goto out; - } - - pr_debug(" intspec=%d\n", *out_irq->args); - - /* Check if there are any interrupt-map translations to process */ - res = of_irq_parse_raw(addr_buf, out_irq); - out: - of_node_put(p); - return res; + return of_irq_parse_raw(addr_buf, out_irq); } EXPORT_SYMBOL_GPL(of_irq_parse_one); @@ -505,8 +512,10 @@ int of_irq_count(struct device_node *dev) struct of_phandle_args irq; int nr = 0; - while (of_irq_parse_one(dev, nr, &irq) == 0) + while (of_irq_parse_one(dev, nr, &irq) == 0) { + of_node_put(irq.np); nr++; + } return nr; } @@ -623,6 +632,8 @@ void __init of_irq_init(const struct of_device_id *matches) __func__, desc->dev, desc->dev, desc->interrupt_parent); of_node_clear_flag(desc->dev, OF_POPULATED); + of_node_put(desc->interrupt_parent); + of_node_put(desc->dev); kfree(desc); continue; } @@ -653,6 +664,7 @@ void __init of_irq_init(const struct of_device_id *matches) err: list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) { list_del(&desc->list); + of_node_put(desc->interrupt_parent); of_node_put(desc->dev); kfree(desc); } diff --git a/drivers/of/kexec.c b/drivers/of/kexec.c index 5b924597a4de..1ee2d31816ae 100644 --- a/drivers/of/kexec.c +++ b/drivers/of/kexec.c @@ -264,6 +264,43 @@ static inline int setup_ima_buffer(const struct kimage *image, void *fdt, } #endif /* CONFIG_IMA_KEXEC */ +static int kho_add_chosen(const struct kimage *image, void *fdt, int chosen_node) +{ + int ret = 0; +#ifdef CONFIG_KEXEC_HANDOVER + phys_addr_t fdt_mem = 0; + phys_addr_t fdt_len = 0; + phys_addr_t scratch_mem = 0; + phys_addr_t scratch_len = 0; + + ret = fdt_delprop(fdt, chosen_node, "linux,kho-fdt"); + if (ret && ret != -FDT_ERR_NOTFOUND) + return ret; + ret = fdt_delprop(fdt, chosen_node, "linux,kho-scratch"); + if (ret && ret != -FDT_ERR_NOTFOUND) + return ret; + + if (!image->kho.fdt || !image->kho.scratch) + return 0; + + fdt_mem = image->kho.fdt; + fdt_len = PAGE_SIZE; + scratch_mem = image->kho.scratch->mem; + scratch_len = image->kho.scratch->bufsz; + + pr_debug("Adding kho metadata to DT"); + + ret = fdt_appendprop_addrrange(fdt, 0, chosen_node, "linux,kho-fdt", + fdt_mem, fdt_len); + if (ret) + return ret; + ret = fdt_appendprop_addrrange(fdt, 0, chosen_node, "linux,kho-scratch", + scratch_mem, scratch_len); + +#endif /* CONFIG_KEXEC_HANDOVER */ + return ret; +} + /* * of_kexec_alloc_and_setup_fdt - Alloc and setup a new Flattened Device Tree * @@ -414,6 +451,11 @@ void *of_kexec_alloc_and_setup_fdt(const struct kimage *image, #endif } + /* Add kho metadata if this is a KHO image */ + ret = kho_add_chosen(image, fdt, chosen_node); + if (ret) + goto out; + /* add bootargs */ if (cmdline) { ret = fdt_setprop_string(fdt, chosen_node, "bootargs", cmdline); diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 1bdc7ceef3c5..df0bb00349e0 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -208,6 +208,13 @@ static void __maybe_unused of_dump_addr(const char *s, const __be32 *addr, int n static void __maybe_unused of_dump_addr(const char *s, const __be32 *addr, int na) { } #endif +static inline bool is_pseudo_property(const char *prop_name) +{ + return !of_prop_cmp(prop_name, "name") || + !of_prop_cmp(prop_name, "phandle") || + !of_prop_cmp(prop_name, "linux,phandle"); +} + #if IS_ENABLED(CONFIG_KUNIT) int __of_address_resource_bounds(struct resource *r, u64 start, u64 size); #endif diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 75e819f66a56..77016c0cc296 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -12,6 +12,7 @@ #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> @@ -415,12 +416,12 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam prop = of_get_flat_dt_prop(node, "alignment", &len); if (prop) { - if (len != dt_root_size_cells * sizeof(__be32)) { + if (len != dt_root_addr_cells * sizeof(__be32)) { pr_err("invalid alignment property in '%s' node.\n", uname); return -EINVAL; } - align = dt_mem_next_cell(dt_root_size_cells, &prop); + align = dt_mem_next_cell(dt_root_addr_cells, &prop); } nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; @@ -740,3 +741,82 @@ 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->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); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 434f6dd6a86c..1af6f52d0708 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -84,6 +84,12 @@ static int devicetree_state_flags; #define DTSF_APPLY_FAIL 0x01 #define DTSF_REVERT_FAIL 0x02 +static int of_prop_val_eq(const struct property *p1, const struct property *p2) +{ + return p1->length == p2->length && + !memcmp(p1->value, p2->value, (size_t)p1->length); +} + /* * If a changeset apply or revert encounters an error, an attempt will * be made to undo partial changes, but may fail. If the undo fails @@ -304,9 +310,7 @@ static int add_changeset_property(struct overlay_changeset *ovcs, int ret = 0; if (target->in_livetree) - if (!of_prop_cmp(overlay_prop->name, "name") || - !of_prop_cmp(overlay_prop->name, "phandle") || - !of_prop_cmp(overlay_prop->name, "linux,phandle")) + if (is_pseudo_property(overlay_prop->name)) return 0; if (target->in_livetree) diff --git a/drivers/of/platform.c b/drivers/of/platform.c index c6d8afb284e8..f77cb19973a5 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -334,7 +334,7 @@ static int of_platform_bus_create(struct device_node *bus, int rc = 0; /* Make sure it has a compatible property */ - if (strict && (!of_get_property(bus, "compatible", NULL))) { + if (strict && (!of_property_present(bus, "compatible"))) { pr_debug("%s() - skipping %pOF, no compatible prop\n", __func__, bus); return 0; @@ -536,8 +536,8 @@ static int __init of_platform_default_populate_init(void) * ignore errors for the rest. */ for_each_node_by_type(node, "display") { - if (!of_get_property(node, "linux,opened", NULL) || - !of_get_property(node, "linux,boot-display", NULL)) + if (!of_property_read_bool(node, "linux,opened") || + !of_property_read_bool(node, "linux,boot-display")) continue; dev = of_platform_device_create(node, "of-display", NULL); of_node_put(node); @@ -551,7 +551,7 @@ static int __init of_platform_default_populate_init(void) char buf[14]; const char *of_display_format = "of-display.%d"; - if (!of_get_property(node, "linux,opened", NULL) || node == boot_display) + if (!of_property_read_bool(node, "linux,opened") || node == boot_display) continue; ret = snprintf(buf, sizeof(buf), of_display_format, display_number++); if (ret < sizeof(buf)) diff --git a/drivers/of/property.c b/drivers/of/property.c index 208d922cc24c..c1feb631e383 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -148,6 +148,39 @@ static void *of_find_property_value_of_size(const struct device_node *np, } /** + * of_property_read_u16_index - Find and read a u16 from a multi-value property. + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @index: index of the u16 in the list of values + * @out_value: pointer to return value, modified only if no error. + * + * Search for a property in a device node and read nth 16-bit value from + * it. + * + * Return: 0 on success, -EINVAL if the property does not exist, + * -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + * + * The out_value is modified only if a valid u16 value can be decoded. + */ +int of_property_read_u16_index(const struct device_node *np, + const char *propname, + u32 index, u16 *out_value) +{ + const u16 *val = of_find_property_value_of_size(np, propname, + ((index + 1) * sizeof(*out_value)), + 0, NULL); + + if (IS_ERR(val)) + return PTR_ERR(val); + + *out_value = be16_to_cpup(((__be16 *)val) + index); + return 0; +} +EXPORT_SYMBOL_GPL(of_property_read_u16_index); + +/** * of_property_read_u32_index - Find and read a u32 from a multi-value property. * * @np: device node from which the property value is to be read. diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c index 779db058c42f..86424e145919 100644 --- a/drivers/of/resolver.c +++ b/drivers/of/resolver.c @@ -161,9 +161,7 @@ static int adjust_local_phandle_references(const struct device_node *local_fixup for_each_property_of_node(local_fixups, prop_fix) { /* skip properties added automatically */ - if (!of_prop_cmp(prop_fix->name, "name") || - !of_prop_cmp(prop_fix->name, "phandle") || - !of_prop_cmp(prop_fix->name, "linux,phandle")) + if (is_pseudo_property(prop_fix->name)) continue; if ((prop_fix->length % 4) != 0 || prop_fix->length == 0) @@ -249,25 +247,22 @@ static int adjust_local_phandle_references(const struct device_node *local_fixup */ int of_resolve_phandles(struct device_node *overlay) { - struct device_node *child, *local_fixups, *refnode; - struct device_node *tree_symbols, *overlay_fixups; + struct device_node *child, *refnode; + struct device_node *overlay_fixups; + struct device_node __free(device_node) *local_fixups = NULL; struct property *prop; const char *refpath; phandle phandle, phandle_delta; int err; - tree_symbols = NULL; - if (!overlay) { pr_err("null overlay\n"); - err = -EINVAL; - goto out; + return -EINVAL; } if (!of_node_check_flag(overlay, OF_DETACHED)) { pr_err("overlay not detached\n"); - err = -EINVAL; - goto out; + return -EINVAL; } phandle_delta = live_tree_max_phandle() + 1; @@ -279,7 +274,7 @@ int of_resolve_phandles(struct device_node *overlay) err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta); if (err) - goto out; + return err; overlay_fixups = NULL; @@ -288,16 +283,13 @@ int of_resolve_phandles(struct device_node *overlay) overlay_fixups = child; } - if (!overlay_fixups) { - err = 0; - goto out; - } + if (!overlay_fixups) + return 0; - tree_symbols = of_find_node_by_path("/__symbols__"); + struct device_node __free(device_node) *tree_symbols = of_find_node_by_path("/__symbols__"); if (!tree_symbols) { pr_err("no symbols in root of device tree.\n"); - err = -EINVAL; - goto out; + return -EINVAL; } for_each_property_of_node(overlay_fixups, prop) { @@ -311,14 +303,12 @@ int of_resolve_phandles(struct device_node *overlay) if (err) { pr_err("node label '%s' not found in live devicetree symbols table\n", prop->name); - goto out; + return err; } refnode = of_find_node_by_path(refpath); - if (!refnode) { - err = -ENOENT; - goto out; - } + if (!refnode) + return -ENOENT; phandle = refnode->phandle; of_node_put(refnode); @@ -328,11 +318,8 @@ int of_resolve_phandles(struct device_node *overlay) break; } -out: if (err) pr_err("overlay phandle fixup failed: %d\n", err); - of_node_put(tree_symbols); - return err; } EXPORT_SYMBOL_GPL(of_resolve_phandles); diff --git a/drivers/of/unittest-data/tests-interrupts.dtsi b/drivers/of/unittest-data/tests-interrupts.dtsi index 7c9f31cc131b..4ccb54f91c30 100644 --- a/drivers/of/unittest-data/tests-interrupts.dtsi +++ b/drivers/of/unittest-data/tests-interrupts.dtsi @@ -50,6 +50,13 @@ interrupt-map = <0x5000 1 2 &test_intc0 15>; }; + test_intc_intmap0: intc-intmap0 { + #interrupt-cells = <1>; + #address-cells = <1>; + interrupt-controller; + interrupt-map = <0x6000 1 &test_intc_intmap0 0x7000 2>; + }; + interrupts0 { interrupt-parent = <&test_intc0>; interrupts = <1>, <2>, <3>, <4>; @@ -60,6 +67,12 @@ interrupts = <1>, <2>, <3>, <4>; }; + interrupts2 { + reg = <0x6000 0x100>; + interrupt-parent = <&test_intc_intmap0>; + interrupts = <1>; + }; + interrupts-extended0 { reg = <0x5000 0x100>; /* diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index f88ddb1cf5d7..eeb370e0f507 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -1654,6 +1654,72 @@ static void __init of_unittest_parse_interrupts_extended(void) of_node_put(np); } +#if IS_ENABLED(CONFIG_OF_DYNAMIC) +static void __init of_unittest_irq_refcount(void) +{ + struct of_phandle_args args; + struct device_node *intc0, *int_ext0; + struct device_node *int2, *intc_intmap0; + unsigned int ref_c0, ref_c1, ref_c2; + int rc; + bool passed; + + if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC) + return; + + intc0 = of_find_node_by_path("/testcase-data/interrupts/intc0"); + int_ext0 = of_find_node_by_path("/testcase-data/interrupts/interrupts-extended0"); + intc_intmap0 = of_find_node_by_path("/testcase-data/interrupts/intc-intmap0"); + int2 = of_find_node_by_path("/testcase-data/interrupts/interrupts2"); + if (!intc0 || !int_ext0 || !intc_intmap0 || !int2) { + pr_err("missing testcase data\n"); + goto out; + } + + /* Test refcount for API of_irq_parse_one() */ + passed = true; + ref_c0 = OF_KREF_READ(intc0); + ref_c1 = ref_c0 + 1; + memset(&args, 0, sizeof(args)); + rc = of_irq_parse_one(int_ext0, 0, &args); + ref_c2 = OF_KREF_READ(intc0); + of_node_put(args.np); + + passed &= !rc; + passed &= (args.np == intc0); + passed &= (args.args_count == 1); + passed &= (args.args[0] == 1); + passed &= (ref_c1 == ref_c2); + unittest(passed, "IRQ refcount case #1 failed, original(%u) expected(%u) got(%u)\n", + ref_c0, ref_c1, ref_c2); + + /* Test refcount for API of_irq_parse_raw() */ + passed = true; + ref_c0 = OF_KREF_READ(intc_intmap0); + ref_c1 = ref_c0 + 1; + memset(&args, 0, sizeof(args)); + rc = of_irq_parse_one(int2, 0, &args); + ref_c2 = OF_KREF_READ(intc_intmap0); + of_node_put(args.np); + + passed &= !rc; + passed &= (args.np == intc_intmap0); + passed &= (args.args_count == 1); + passed &= (args.args[0] == 2); + passed &= (ref_c1 == ref_c2); + unittest(passed, "IRQ refcount case #2 failed, original(%u) expected(%u) got(%u)\n", + ref_c0, ref_c1, ref_c2); + +out: + of_node_put(int2); + of_node_put(intc_intmap0); + of_node_put(int_ext0); + of_node_put(intc0); +} +#else +static inline void __init of_unittest_irq_refcount(void) { } +#endif + static const struct of_device_id match_node_table[] = { { .data = "A", .name = "name0", }, /* Name alone is lowest priority */ { .data = "B", .type = "type1", }, /* followed by type alone */ @@ -1963,15 +2029,16 @@ static int __init unittest_data_add(void) rc = of_resolve_phandles(unittest_data_node); if (rc) { pr_err("%s: Failed to resolve phandles (rc=%i)\n", __func__, rc); - of_overlay_mutex_unlock(); - return -EINVAL; + rc = -EINVAL; + goto unlock; } /* attach the sub-tree to live tree */ if (!of_root) { pr_warn("%s: no live tree to attach sub-tree\n", __func__); kfree(unittest_data); - return -ENODEV; + rc = -ENODEV; + goto unlock; } EXPECT_BEGIN(KERN_INFO, @@ -1990,9 +2057,10 @@ static int __init unittest_data_add(void) EXPECT_END(KERN_INFO, "Duplicate name in testcase-data, renamed to \"duplicate-name#1\""); +unlock: of_overlay_mutex_unlock(); - return 0; + return rc; } #ifdef CONFIG_OF_OVERLAY @@ -4324,6 +4392,7 @@ static int __init of_unittest(void) of_unittest_changeset_prop(); of_unittest_parse_interrupts(); of_unittest_parse_interrupts_extended(); + of_unittest_irq_refcount(); of_unittest_dma_get_max_cpu_address(); of_unittest_parse_dma_ranges(); of_unittest_pci_dma_ranges(); |
