summaryrefslogtreecommitdiffstats
path: root/drivers/of
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/address.c113
-rw-r--r--drivers/of/base.c34
-rw-r--r--drivers/of/device.c42
-rw-r--r--drivers/of/dynamic.c37
-rw-r--r--drivers/of/irq.c143
-rw-r--r--drivers/of/of_private.h4
-rw-r--r--drivers/of/of_reserved_mem.c22
-rw-r--r--drivers/of/of_test.c1
-rw-r--r--drivers/of/overlay.c11
-rw-r--r--drivers/of/property.c81
-rw-r--r--drivers/of/resolver.c35
-rw-r--r--drivers/of/unittest.c14
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;