diff options
Diffstat (limited to 'drivers/of')
-rw-r--r-- | drivers/of/address.c | 113 | ||||
-rw-r--r-- | drivers/of/base.c | 34 | ||||
-rw-r--r-- | drivers/of/device.c | 42 | ||||
-rw-r--r-- | drivers/of/dynamic.c | 37 | ||||
-rw-r--r-- | drivers/of/irq.c | 143 | ||||
-rw-r--r-- | drivers/of/of_private.h | 4 | ||||
-rw-r--r-- | drivers/of/of_reserved_mem.c | 22 | ||||
-rw-r--r-- | drivers/of/of_test.c | 1 | ||||
-rw-r--r-- | drivers/of/overlay.c | 11 | ||||
-rw-r--r-- | drivers/of/property.c | 81 | ||||
-rw-r--r-- | drivers/of/resolver.c | 35 | ||||
-rw-r--r-- | drivers/of/unittest.c | 14 |
12 files changed, 233 insertions, 304 deletions
diff --git a/drivers/of/address.c b/drivers/of/address.c index ae46a36059..d669ce25b5 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -486,34 +486,30 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, * device that had registered logical PIO mapping, and the return code is * relative to that node. */ -static u64 __of_translate_address(struct device_node *dev, +static u64 __of_translate_address(struct device_node *node, struct device_node *(*get_parent)(const struct device_node *), const __be32 *in_addr, const char *rprop, struct device_node **host) { - struct device_node *parent = NULL; + struct device_node *dev __free(device_node) = of_node_get(node); + struct device_node *parent __free(device_node) = get_parent(dev); struct of_bus *bus, *pbus; __be32 addr[OF_MAX_ADDR_CELLS]; int na, ns, pna, pns; - u64 result = OF_BAD_ADDR; pr_debug("** translation for device %pOF **\n", dev); - /* Increase refcount at current level */ - of_node_get(dev); - *host = NULL; - /* Get parent & match bus type */ - parent = get_parent(dev); + if (parent == NULL) - goto bail; + return OF_BAD_ADDR; bus = of_match_bus(parent); /* Count address cells & copy address locally */ bus->count_cells(dev, &na, &ns); if (!OF_CHECK_COUNTS(na, ns)) { pr_debug("Bad cell count for %pOF\n", dev); - goto bail; + return OF_BAD_ADDR; } memcpy(addr, in_addr, na * 4); @@ -533,8 +529,7 @@ static u64 __of_translate_address(struct device_node *dev, /* If root, we have finished */ if (parent == NULL) { pr_debug("reached root node\n"); - result = of_read_number(addr, na); - break; + return of_read_number(addr, na); } /* @@ -543,11 +538,11 @@ static u64 __of_translate_address(struct device_node *dev, */ iorange = find_io_range_by_fwnode(&dev->fwnode); if (iorange && (iorange->flags != LOGIC_PIO_CPU_MMIO)) { - result = of_read_number(addr + 1, na - 1); + u64 result = of_read_number(addr + 1, na - 1); pr_debug("indirectIO matched(%pOF) 0x%llx\n", dev, result); - *host = of_node_get(dev); - break; + *host = no_free_ptr(dev); + return result; } /* Get new parent bus and counts */ @@ -555,7 +550,7 @@ static u64 __of_translate_address(struct device_node *dev, pbus->count_cells(dev, &pna, &pns); if (!OF_CHECK_COUNTS(pna, pns)) { pr_err("Bad cell count for %pOF\n", dev); - break; + return OF_BAD_ADDR; } pr_debug("parent bus is %s (na=%d, ns=%d) on %pOF\n", @@ -563,7 +558,7 @@ static u64 __of_translate_address(struct device_node *dev, /* Apply bus translation */ if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop)) - break; + return OF_BAD_ADDR; /* Complete the move up one level */ na = pna; @@ -572,11 +567,8 @@ static u64 __of_translate_address(struct device_node *dev, of_dump_addr("one level translation:", addr, na); } - bail: - of_node_put(parent); - of_node_put(dev); - return result; + unreachable(); } u64 of_translate_address(struct device_node *dev, const __be32 *in_addr) @@ -654,19 +646,16 @@ EXPORT_SYMBOL(of_translate_dma_address); const __be32 *of_translate_dma_region(struct device_node *dev, const __be32 *prop, phys_addr_t *start, size_t *length) { - struct device_node *parent; + struct device_node *parent __free(device_node) = __of_get_dma_parent(dev); u64 address, size; int na, ns; - parent = __of_get_dma_parent(dev); if (!parent) return NULL; na = of_bus_n_addr_cells(parent); ns = of_bus_n_size_cells(parent); - of_node_put(parent); - address = of_translate_dma_address(dev, prop); if (address == OF_BAD_ADDR) return NULL; @@ -688,21 +677,19 @@ const __be32 *__of_get_address(struct device_node *dev, int index, int bar_no, { const __be32 *prop; unsigned int psize; - struct device_node *parent; + struct device_node *parent __free(device_node) = of_get_parent(dev); struct of_bus *bus; int onesize, i, na, ns; - /* Get parent & match bus type */ - parent = of_get_parent(dev); if (parent == NULL) return NULL; + + /* match the parent's bus type */ bus = of_match_bus(parent); - if (strcmp(bus->name, "pci") && (bar_no >= 0)) { - of_node_put(parent); + if (strcmp(bus->name, "pci") && (bar_no >= 0)) return NULL; - } + bus->count_cells(dev, &na, &ns); - of_node_put(parent); if (!OF_CHECK_ADDR_COUNT(na)) return NULL; @@ -888,14 +875,13 @@ static u64 of_translate_ioport(struct device_node *dev, const __be32 *in_addr, */ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) { - struct device_node *node = of_node_get(np); + struct device_node *node __free(device_node) = of_node_get(np); const __be32 *ranges = NULL; bool found_dma_ranges = false; struct of_range_parser parser; struct of_range range; struct bus_dma_region *r; int len, num_ranges = 0; - int ret = 0; while (node) { ranges = of_get_property(node, "dma-ranges", &len); @@ -905,10 +891,9 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) break; /* Once we find 'dma-ranges', then a missing one is an error */ - if (found_dma_ranges && !ranges) { - ret = -ENODEV; - goto out; - } + if (found_dma_ranges && !ranges) + return -ENODEV; + found_dma_ranges = true; node = of_get_next_dma_parent(node); @@ -916,10 +901,8 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) if (!node || !ranges) { pr_debug("no dma-ranges found for node(%pOF)\n", np); - ret = -ENODEV; - goto out; + return -ENODEV; } - of_dma_range_parser_init(&parser, node); for_each_of_range(&parser, &range) { if (range.cpu_addr == OF_BAD_ADDR) { @@ -930,16 +913,12 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) num_ranges++; } - if (!num_ranges) { - ret = -EINVAL; - goto out; - } + if (!num_ranges) + return -EINVAL; r = kcalloc(num_ranges + 1, sizeof(*r), GFP_KERNEL); - if (!r) { - ret = -ENOMEM; - goto out; - } + if (!r) + return -ENOMEM; /* * Record all info in the generic DMA ranges array for struct device, @@ -957,9 +936,7 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map) r->size = range.size; r++; } -out: - of_node_put(node); - return ret; + return 0; } #endif /* CONFIG_HAS_DMA */ @@ -1016,24 +993,18 @@ phys_addr_t __init of_dma_get_max_cpu_address(struct device_node *np) */ bool of_dma_is_coherent(struct device_node *np) { - struct device_node *node; - bool is_coherent = dma_default_coherent; - - node = of_node_get(np); + struct device_node *node __free(device_node) = of_node_get(np); while (node) { - if (of_property_read_bool(node, "dma-coherent")) { - is_coherent = true; - break; - } - if (of_property_read_bool(node, "dma-noncoherent")) { - is_coherent = false; - break; - } + if (of_property_read_bool(node, "dma-coherent")) + return true; + + if (of_property_read_bool(node, "dma-noncoherent")) + return false; + node = of_get_next_dma_parent(node); } - of_node_put(node); - return is_coherent; + return dma_default_coherent; } EXPORT_SYMBOL_GPL(of_dma_is_coherent); @@ -1049,20 +1020,14 @@ EXPORT_SYMBOL_GPL(of_dma_is_coherent); */ static bool of_mmio_is_nonposted(struct device_node *np) { - struct device_node *parent; - bool nonposted; - if (!IS_ENABLED(CONFIG_ARCH_APPLE)) return false; - parent = of_get_parent(np); + struct device_node *parent __free(device_node) = of_get_parent(np); if (!parent) return false; - nonposted = of_property_read_bool(parent, "nonposted-mmio"); - - of_node_put(parent); - return nonposted; + return 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 8856c67c46..20603d3c99 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -16,6 +16,7 @@ #define pr_fmt(fmt) "OF: " fmt +#include <linux/cleanup.h> #include <linux/console.h> #include <linux/ctype.h> #include <linux/cpu.h> @@ -1393,8 +1394,10 @@ int of_parse_phandle_with_args_map(const struct device_node *np, const char *stem_name, int index, struct of_phandle_args *out_args) { - char *cells_name, *map_name = NULL, *mask_name = NULL; - char *pass_name = NULL; + char *cells_name __free(kfree) = kasprintf(GFP_KERNEL, "#%s-cells", stem_name); + char *map_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map", stem_name); + char *mask_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name); + char *pass_name __free(kfree) = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name); struct device_node *cur, *new = NULL; const __be32 *map, *mask, *pass; static const __be32 dummy_mask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) }; @@ -1407,27 +1410,13 @@ int of_parse_phandle_with_args_map(const struct device_node *np, if (index < 0) return -EINVAL; - cells_name = kasprintf(GFP_KERNEL, "#%s-cells", stem_name); - if (!cells_name) + if (!cells_name || !map_name || !mask_name || !pass_name) return -ENOMEM; - ret = -ENOMEM; - map_name = kasprintf(GFP_KERNEL, "%s-map", stem_name); - if (!map_name) - goto free; - - mask_name = kasprintf(GFP_KERNEL, "%s-map-mask", stem_name); - if (!mask_name) - goto free; - - pass_name = kasprintf(GFP_KERNEL, "%s-map-pass-thru", stem_name); - if (!pass_name) - goto free; - ret = __of_parse_phandle_with_args(np, list_name, cells_name, -1, index, out_args); if (ret) - goto free; + return ret; /* Get the #<list>-cells property */ cur = out_args->np; @@ -1444,8 +1433,7 @@ int of_parse_phandle_with_args_map(const struct device_node *np, /* Get the <list>-map property */ map = of_get_property(cur, map_name, &map_len); if (!map) { - ret = 0; - goto free; + return 0; } map_len /= sizeof(u32); @@ -1521,12 +1509,6 @@ int of_parse_phandle_with_args_map(const struct device_node *np, put: of_node_put(cur); of_node_put(new); -free: - kfree(mask_name); - kfree(map_name); - kfree(cells_name); - kfree(pass_name); - return ret; } EXPORT_SYMBOL(of_parse_phandle_with_args_map); diff --git a/drivers/of/device.c b/drivers/of/device.c index de89f99063..312c633612 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -95,8 +95,7 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, { const struct bus_dma_region *map = NULL; struct device_node *bus_np; - u64 dma_start = 0; - u64 mask, end, size = 0; + u64 mask, end = 0; bool coherent; int iommu_ret; int ret; @@ -117,34 +116,8 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, if (!force_dma) return ret == -ENODEV ? 0 : ret; } else { - const struct bus_dma_region *r = map; - u64 dma_end = 0; - /* Determine the overall bounds of all DMA regions */ - for (dma_start = ~0; r->size; r++) { - /* Take lower and upper limits */ - if (r->dma_start < dma_start) - dma_start = r->dma_start; - if (r->dma_start + r->size > dma_end) - dma_end = r->dma_start + r->size; - } - size = dma_end - dma_start; - - /* - * Add a work around to treat the size as mask + 1 in case - * it is defined in DT as a mask. - */ - if (size & 1) { - dev_warn(dev, "Invalid size 0x%llx for dma-range(s)\n", - size); - size = size + 1; - } - - if (!size) { - dev_err(dev, "Adjusted size 0x%llx invalid\n", size); - kfree(map); - return -EINVAL; - } + end = dma_range_map_max(map); } /* @@ -158,16 +131,15 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, dev->dma_mask = &dev->coherent_dma_mask; } - if (!size && dev->coherent_dma_mask) - size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1); - else if (!size) - size = 1ULL << 32; + if (!end && dev->coherent_dma_mask) + end = dev->coherent_dma_mask; + else if (!end) + end = (1ULL << 32) - 1; /* * Limit coherent and dma mask based on size and default mask * set by the driver. */ - end = dma_start + size - 1; mask = DMA_BIT_MASK(ilog2(end) + 1); dev->coherent_dma_mask &= mask; *dev->dma_mask &= mask; @@ -201,7 +173,7 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, } else dev_dbg(dev, "device is behind an iommu\n"); - arch_setup_dma_ops(dev, dma_start, size, coherent); + arch_setup_dma_ops(dev, coherent); if (iommu_ret) of_dma_set_restricted_buffer(dev, np); diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 4d57a4e341..dda6092e6d 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -9,6 +9,7 @@ #define pr_fmt(fmt) "OF: " fmt +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/of.h> #include <linux/spinlock.h> @@ -306,15 +307,20 @@ int of_detach_node(struct device_node *np) } EXPORT_SYMBOL_GPL(of_detach_node); +void __of_prop_free(struct property *prop) +{ + kfree(prop->name); + kfree(prop->value); + kfree(prop); +} + static void property_list_free(struct property *prop_list) { struct property *prop, *next; for (prop = prop_list; prop != NULL; prop = next) { next = prop->next; - kfree(prop->name); - kfree(prop->value); - kfree(prop); + __of_prop_free(prop); } } @@ -427,9 +433,7 @@ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags) return new; err_free: - kfree(new->name); - kfree(new->value); - kfree(new); + __of_prop_free(new); return NULL; } @@ -471,9 +475,7 @@ struct device_node *__of_node_dup(const struct device_node *np, if (!new_pp) goto err_prop; if (__of_add_property(node, new_pp)) { - kfree(new_pp->name); - kfree(new_pp->value); - kfree(new_pp); + __of_prop_free(new_pp); goto err_prop; } } @@ -933,11 +935,8 @@ static int of_changeset_add_prop_helper(struct of_changeset *ocs, return -ENOMEM; ret = of_changeset_add_property(ocs, np, new_pp); - if (ret) { - kfree(new_pp->name); - kfree(new_pp->value); - kfree(new_pp); - } + if (ret) + __of_prop_free(new_pp); return ret; } @@ -1033,10 +1032,9 @@ int of_changeset_add_prop_u32_array(struct of_changeset *ocs, const u32 *array, size_t sz) { struct property prop; - __be32 *val; - int i, ret; + __be32 *val __free(kfree) = kcalloc(sz, sizeof(__be32), GFP_KERNEL); + int i; - val = kcalloc(sz, sizeof(__be32), GFP_KERNEL); if (!val) return -ENOMEM; @@ -1046,9 +1044,6 @@ int of_changeset_add_prop_u32_array(struct of_changeset *ocs, prop.length = sizeof(u32) * sz; prop.value = (void *)val; - ret = of_changeset_add_prop_helper(ocs, np, &prop); - kfree(val); - - return ret; + return of_changeset_add_prop_helper(ocs, np, &prop); } EXPORT_SYMBOL_GPL(of_changeset_add_prop_u32_array); diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 174900072c..c94203ce65 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -25,6 +25,8 @@ #include <linux/string.h> #include <linux/slab.h> +#include "of_private.h" + /** * irq_of_parse_and_map - Parse and map an interrupt into linux virq space * @dev: Device node of the device whose interrupt is to be mapped @@ -79,7 +81,8 @@ EXPORT_SYMBOL_GPL(of_irq_find_parent); /* * These interrupt controllers abuse interrupt-map for unspeakable * reasons and rely on the core code to *ignore* it (the drivers do - * their own parsing of the property). + * their own parsing of the property). The PAsemi entry covers a + * non-sensical interrupt-map that is better left ignored. * * If you think of adding to the list for something *new*, think * again. There is a high chance that you will be sent back to the @@ -93,9 +96,61 @@ static const char * const of_irq_imap_abusers[] = { "fsl,ls1043a-extirq", "fsl,ls1088a-extirq", "renesas,rza1-irqc", + "pasemi,rootbus", NULL, }; +const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len, struct of_phandle_args *out_irq) +{ + u32 intsize, addrsize; + struct device_node *np; + + /* Get the interrupt parent */ + if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) + np = of_node_get(of_irq_dflt_pic); + else + np = of_find_node_by_phandle(be32_to_cpup(imap)); + imap++; + + /* Check if not found */ + if (!np) { + pr_debug(" -> imap parent not found !\n"); + return NULL; + } + + /* Get #interrupt-cells and #address-cells of new parent */ + if (of_property_read_u32(np, "#interrupt-cells", + &intsize)) { + pr_debug(" -> parent lacks #interrupt-cells!\n"); + of_node_put(np); + return NULL; + } + if (of_property_read_u32(np, "#address-cells", + &addrsize)) + addrsize = 0; + + pr_debug(" -> intsize=%d, addrsize=%d\n", + intsize, addrsize); + + /* Check for malformed properties */ + if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS) + || (len < (addrsize + intsize))) { + of_node_put(np); + return NULL; + } + + pr_debug(" -> imaplen=%d\n", len); + + imap += addrsize + intsize; + + out_irq->np = np; + for (int i = 0; i < intsize; i++) + out_irq->args[i] = be32_to_cpup(imap - intsize + i); + out_irq->args_count = intsize; + + return imap; +} + /** * of_irq_parse_raw - Low level interrupt tree parsing * @addr: address specifier (start of "reg" property of the device) in be32 format @@ -112,12 +167,12 @@ static const char * const of_irq_imap_abusers[] = { */ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) { - struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; + struct device_node *ipar, *tnode, *old = NULL; __be32 initial_match_array[MAX_PHANDLE_ARGS]; const __be32 *match_array = initial_match_array; - const __be32 *tmp, *imap, *imask, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) }; - u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; - int imaplen, match, i, rc = -EINVAL; + const __be32 *tmp, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) }; + u32 intsize = 1, addrsize; + int i, rc = -EINVAL; #ifdef DEBUG of_print_phandle_args("of_irq_parse_raw: ", out_irq); @@ -176,6 +231,9 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) /* Now start the actual "proper" walk of the interrupt tree */ while (ipar != NULL) { + int imaplen, match; + const __be32 *imap, *oldimap, *imask; + struct device_node *newpar; /* * Now check if cursor is an interrupt-controller and * if it is then we are done, unless there is an @@ -216,7 +274,7 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) /* Parse interrupt-map */ match = 0; - while (imaplen > (addrsize + intsize + 1) && !match) { + while (imaplen > (addrsize + intsize + 1)) { /* Compare specifiers */ match = 1; for (i = 0; i < (addrsize + intsize); i++, imaplen--) @@ -224,74 +282,31 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); - /* Get the interrupt parent */ - if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) - newpar = of_node_get(of_irq_dflt_pic); - else - newpar = of_find_node_by_phandle(be32_to_cpup(imap)); - imap++; - --imaplen; - - /* Check if not found */ - if (newpar == NULL) { - pr_debug(" -> imap parent not found !\n"); + oldimap = imap; + imap = of_irq_parse_imap_parent(oldimap, imaplen, out_irq); + if (!imap) goto fail; - } - - if (!of_device_is_available(newpar)) - match = 0; - - /* Get #interrupt-cells and #address-cells of new - * parent - */ - if (of_property_read_u32(newpar, "#interrupt-cells", - &newintsize)) { - pr_debug(" -> parent lacks #interrupt-cells!\n"); - goto fail; - } - if (of_property_read_u32(newpar, "#address-cells", - &newaddrsize)) - newaddrsize = 0; - pr_debug(" -> newintsize=%d, newaddrsize=%d\n", - newintsize, newaddrsize); - - /* Check for malformed properties */ - if (WARN_ON(newaddrsize + newintsize > MAX_PHANDLE_ARGS) - || (imaplen < (newaddrsize + newintsize))) { - rc = -EFAULT; - goto fail; - } - - imap += newaddrsize + newintsize; - imaplen -= newaddrsize + newintsize; + match &= of_device_is_available(out_irq->np); + if (match) + break; + of_node_put(out_irq->np); + imaplen -= imap - oldimap; pr_debug(" -> imaplen=%d\n", imaplen); } - if (!match) { - if (intc) { - /* - * The PASEMI Nemo is a known offender, so - * let's only warn for anyone else. - */ - WARN(!IS_ENABLED(CONFIG_PPC_PASEMI), - "%pOF interrupt-map failed, using interrupt-controller\n", - ipar); - return 0; - } - + if (!match) goto fail; - } /* * Successfully parsed an interrupt-map translation; copy new * interrupt specifier into the out_irq structure */ - match_array = imap - newaddrsize - newintsize; - for (i = 0; i < newintsize; i++) - out_irq->args[i] = be32_to_cpup(imap - newintsize + i); - out_irq->args_count = intsize = newintsize; - addrsize = newaddrsize; + match_array = oldimap + 1; + + newpar = out_irq->np; + intsize = out_irq->args_count; + addrsize = (imap - match_array) - intsize; if (ipar == newpar) { pr_debug("%pOF interrupt-map entry to self\n", ipar); @@ -300,7 +315,6 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) skiplevel: /* Iterate again with new parent */ - out_irq->np = newpar; pr_debug(" -> new parent: %pOF\n", newpar); of_node_put(ipar); ipar = newpar; @@ -310,7 +324,6 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq) fail: of_node_put(ipar); - of_node_put(newpar); return rc; } diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 485483524b..04aa2a91f8 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -123,6 +123,7 @@ extern void *__unflatten_device_tree(const void *blob, * own the devtree lock or work on detached trees only. */ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags); +void __of_prop_free(struct property *prop); struct device_node *__of_node_dup(const struct device_node *np, const char *full_name); @@ -158,6 +159,9 @@ extern void __of_sysfs_remove_bin_file(struct device_node *np, extern int of_bus_n_addr_cells(struct device_node *np); extern int of_bus_n_size_cells(struct device_node *np); +const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len, + struct of_phandle_args *out_irq); + struct bus_dma_region; #if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_HAS_DMA) int of_dma_get_range(struct device_node *np, diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 8236ecae29..46e1c3fbc7 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -437,17 +437,10 @@ void __init fdt_init_reserved_mem(void) for (i = 0; i < reserved_mem_count; i++) { struct reserved_mem *rmem = &reserved_mem[i]; unsigned long node = rmem->fdt_node; - int len; - const __be32 *prop; int err = 0; bool nomap; nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; - prop = of_get_flat_dt_prop(node, "phandle", &len); - if (!prop) - prop = of_get_flat_dt_prop(node, "linux,phandle", &len); - if (prop) - rmem->phandle = of_read_number(prop, len/4); if (rmem->size == 0) err = __reserved_mem_alloc_size(node, rmem->name, @@ -477,19 +470,6 @@ void __init fdt_init_reserved_mem(void) } } -static inline struct reserved_mem *__find_rmem(struct device_node *node) -{ - unsigned int i; - - if (!node->phandle) - return NULL; - - for (i = 0; i < reserved_mem_count; i++) - if (reserved_mem[i].phandle == node->phandle) - return &reserved_mem[i]; - return NULL; -} - struct rmem_assigned_device { struct device *dev; struct reserved_mem *rmem; @@ -534,7 +514,7 @@ int of_reserved_mem_device_init_by_idx(struct device *dev, return 0; } - rmem = __find_rmem(target); + rmem = of_reserved_mem_lookup(target); of_node_put(target); if (!rmem || !rmem->ops || !rmem->ops->device_init) diff --git a/drivers/of/of_test.c b/drivers/of/of_test.c index a9301d293f..c85a258bc6 100644 --- a/drivers/of/of_test.c +++ b/drivers/of/of_test.c @@ -54,4 +54,5 @@ static struct kunit_suite of_dtb_suite = { kunit_test_suites( &of_dtb_suite, ); +MODULE_DESCRIPTION("KUnit tests for OF APIs"); MODULE_LICENSE("GPL"); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 2ae7e9d24a..4d861a75d6 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -262,9 +262,7 @@ static struct property *dup_and_fixup_symbol_prop( return new_prop; err_free_new_prop: - kfree(new_prop->name); - kfree(new_prop->value); - kfree(new_prop); + __of_prop_free(new_prop); err_free_target_path: kfree(target_path); @@ -361,11 +359,8 @@ static int add_changeset_property(struct overlay_changeset *ovcs, pr_err("WARNING: memory leak will occur if overlay removed, property: %pOF/%s\n", target->np, new_prop->name); - if (ret) { - kfree(new_prop->name); - kfree(new_prop->value); - kfree(new_prop); - } + if (ret) + __of_prop_free(new_prop); return ret; } diff --git a/drivers/of/property.c b/drivers/of/property.c index a6358ee99b..164d77cb94 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -40,15 +40,12 @@ */ bool of_graph_is_present(const struct device_node *node) { - struct device_node *ports, *port; + struct device_node *ports __free(device_node) = of_get_child_by_name(node, "ports"); - ports = of_get_child_by_name(node, "ports"); if (ports) node = ports; - port = of_get_child_by_name(node, "port"); - of_node_put(ports); - of_node_put(port); + struct device_node *port __free(device_node) = of_get_child_by_name(node, "port"); return !!port; } @@ -579,7 +576,8 @@ EXPORT_SYMBOL_GPL(of_prop_next_string); int of_graph_parse_endpoint(const struct device_node *node, struct of_endpoint *endpoint) { - struct device_node *port_node = of_get_parent(node); + struct device_node *port_node __free(device_node) = + of_get_parent(node); WARN_ONCE(!port_node, "%s(): endpoint %pOF has no parent node\n", __func__, node); @@ -594,8 +592,6 @@ int of_graph_parse_endpoint(const struct device_node *node, of_property_read_u32(port_node, "reg", &endpoint->port); of_property_read_u32(node, "reg", &endpoint->id); - of_node_put(port_node); - return 0; } EXPORT_SYMBOL(of_graph_parse_endpoint); @@ -610,25 +606,22 @@ EXPORT_SYMBOL(of_graph_parse_endpoint); */ struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id) { - struct device_node *node, *port; + struct device_node *node __free(device_node) = of_get_child_by_name(parent, "ports"); - node = of_get_child_by_name(parent, "ports"); if (node) parent = node; - for_each_child_of_node(parent, port) { + for_each_child_of_node_scoped(parent, port) { u32 port_id = 0; if (!of_node_name_eq(port, "port")) continue; of_property_read_u32(port, "reg", &port_id); if (id == port_id) - break; + return_ptr(port); } - of_node_put(node); - - return port; + return NULL; } EXPORT_SYMBOL(of_graph_get_port_by_id); @@ -655,15 +648,13 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, * parent port node. */ if (!prev) { - struct device_node *node; + struct device_node *node __free(device_node) = + of_get_child_by_name(parent, "ports"); - node = of_get_child_by_name(parent, "ports"); if (node) parent = node; port = of_get_child_by_name(parent, "port"); - of_node_put(node); - if (!port) { pr_debug("graph: no port node found in %pOF\n", parent); return NULL; @@ -1052,15 +1043,13 @@ static int of_fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode, struct fwnode_endpoint *endpoint) { const struct device_node *node = to_of_node(fwnode); - struct device_node *port_node = of_get_parent(node); + struct device_node *port_node __free(device_node) = of_get_parent(node); endpoint->local_fwnode = fwnode; of_property_read_u32(port_node, "reg", &endpoint->port); of_property_read_u32(node, "reg", &endpoint->id); - of_node_put(port_node); - return 0; } @@ -1252,6 +1241,9 @@ DEFINE_SIMPLE_PROP(backlight, "backlight", NULL) DEFINE_SIMPLE_PROP(panel, "panel", NULL) DEFINE_SIMPLE_PROP(msi_parent, "msi-parent", "#msi-cells") DEFINE_SIMPLE_PROP(post_init_providers, "post-init-providers", NULL) +DEFINE_SIMPLE_PROP(access_controllers, "access-controllers", "#access-controller-cells") +DEFINE_SIMPLE_PROP(pses, "pses", "#pse-cells") +DEFINE_SIMPLE_PROP(power_supplies, "power-supplies", NULL) DEFINE_SUFFIX_PROP(regulators, "-supply", NULL) DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells") @@ -1311,6 +1303,47 @@ static struct device_node *parse_interrupts(struct device_node *np, return of_irq_parse_one(np, index, &sup_args) ? NULL : sup_args.np; } +static struct device_node *parse_interrupt_map(struct device_node *np, + const char *prop_name, int index) +{ + const __be32 *imap, *imap_end; + struct of_phandle_args sup_args; + u32 addrcells, intcells; + int imaplen; + + if (!IS_ENABLED(CONFIG_OF_IRQ)) + return NULL; + + if (strcmp(prop_name, "interrupt-map")) + return NULL; + + if (of_property_read_u32(np, "#interrupt-cells", &intcells)) + return NULL; + addrcells = of_bus_n_addr_cells(np); + + imap = of_get_property(np, "interrupt-map", &imaplen); + imaplen /= sizeof(*imap); + if (!imap) + return NULL; + + imap_end = imap + imaplen; + + for (int i = 0; imap + addrcells + intcells + 1 < imap_end; i++) { + imap += addrcells + intcells; + + imap = of_irq_parse_imap_parent(imap, imap_end - imap, &sup_args); + if (!imap) + return NULL; + + if (i == index) + return sup_args.np; + + of_node_put(sup_args.np); + } + + return NULL; +} + static struct device_node *parse_remote_endpoint(struct device_node *np, const char *prop_name, int index) @@ -1357,8 +1390,12 @@ static const struct supplier_bindings of_supplier_bindings[] = { { .parse_prop = parse_backlight, }, { .parse_prop = parse_panel, }, { .parse_prop = parse_msi_parent, }, + { .parse_prop = parse_pses, }, + { .parse_prop = parse_power_supplies, }, { .parse_prop = parse_gpio_compat, }, { .parse_prop = parse_interrupts, }, + { .parse_prop = parse_interrupt_map, }, + { .parse_prop = parse_access_controllers, }, { .parse_prop = parse_regulators, }, { .parse_prop = parse_gpio, }, { .parse_prop = parse_gpios, }, diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c index b278ab4338..2780928764 100644 --- a/drivers/of/resolver.c +++ b/drivers/of/resolver.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) "OF: resolver: " fmt +#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> @@ -74,11 +75,11 @@ static int update_usages_of_a_phandle_reference(struct device_node *overlay, { struct device_node *refnode; struct property *prop; - char *value, *cur, *end, *node_path, *prop_name, *s; + char *value __free(kfree) = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL); + char *cur, *end, *node_path, *prop_name, *s; int offset, len; int err = 0; - value = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL); if (!value) return -ENOMEM; @@ -89,23 +90,19 @@ static int update_usages_of_a_phandle_reference(struct device_node *overlay, node_path = cur; s = strchr(cur, ':'); - if (!s) { - err = -EINVAL; - goto err_fail; - } + if (!s) + return -EINVAL; *s++ = '\0'; prop_name = s; s = strchr(s, ':'); - if (!s) { - err = -EINVAL; - goto err_fail; - } + if (!s) + return -EINVAL; *s++ = '\0'; err = kstrtoint(s, 10, &offset); if (err) - goto err_fail; + return err; refnode = __of_find_node_by_full_path(of_node_get(overlay), node_path); if (!refnode) @@ -117,22 +114,16 @@ static int update_usages_of_a_phandle_reference(struct device_node *overlay, } of_node_put(refnode); - if (!prop) { - err = -ENOENT; - goto err_fail; - } + if (!prop) + return -ENOENT; - if (offset < 0 || offset + sizeof(__be32) > prop->length) { - err = -EINVAL; - goto err_fail; - } + if (offset < 0 || offset + sizeof(__be32) > prop->length) + return -EINVAL; *(__be32 *)(prop->value + offset) = cpu_to_be32(phandle); } -err_fail: - kfree(value); - return err; + return 0; } /* compare nodes taking into account that 'name' strips out the @ part */ diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 6b5c36b6a7..445ad13dab 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -795,15 +795,11 @@ static void __init of_unittest_property_copy(void) new = __of_prop_dup(&p1, GFP_KERNEL); unittest(new && propcmp(&p1, new), "empty property didn't copy correctly\n"); - kfree(new->value); - kfree(new->name); - kfree(new); + __of_prop_free(new); new = __of_prop_dup(&p2, GFP_KERNEL); unittest(new && propcmp(&p2, new), "non-empty property didn't copy correctly\n"); - kfree(new->value); - kfree(new->name); - kfree(new); + __of_prop_free(new); #endif } @@ -2815,7 +2811,7 @@ static int unittest_i2c_mux_probe(struct i2c_client *client) if (!muxc) return -ENOMEM; for (i = 0; i < nchans; i++) { - if (i2c_mux_add_adapter(muxc, 0, i, 0)) { + if (i2c_mux_add_adapter(muxc, 0, i)) { dev_err(dev, "Failed to register mux #%d\n", i); i2c_mux_del_adapters(muxc); return -ENODEV; @@ -3718,9 +3714,7 @@ static __init void of_unittest_overlay_high_level(void) goto err_unlock; } if (__of_add_property(of_symbols, new_prop)) { - kfree(new_prop->name); - kfree(new_prop->value); - kfree(new_prop); + __of_prop_free(new_prop); /* "name" auto-generated by unflatten */ if (!strcmp(prop->name, "name")) continue; |