summaryrefslogtreecommitdiff
path: root/drivers/of
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-01-30 10:57:22 +0300
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-01-30 10:57:22 +0300
commit1b62d134d3c5f9e67de096af7ea3e9fe48966f17 (patch)
treebe30467e997cc8ba0d350309dd498f00cb69969b /drivers/of
parent7a37052adb5e843bcfff6c98aee9b60bb087b910 (diff)
parente9ca038a94f5a41c0689c5f441fd9c5a567e6f39 (diff)
downloadlinux-1b62d134d3c5f9e67de096af7ea3e9fe48966f17.tar.xz
Merge back earlier ACPICA changes for v4.11.
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/base.c9
-rw-r--r--drivers/of/fdt.c19
-rw-r--r--drivers/of/irq.c1
-rw-r--r--drivers/of/of_mdio.c21
-rw-r--r--drivers/of/of_numa.c7
-rw-r--r--drivers/of/of_pci.c21
-rw-r--r--drivers/of/overlay.c47
-rw-r--r--drivers/of/platform.c6
-rw-r--r--drivers/of/resolver.c358
9 files changed, 275 insertions, 214 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c
index a0bccb54a9bd..d4bea3c797d6 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1534,9 +1534,12 @@ void of_print_phandle_args(const char *msg, const struct of_phandle_args *args)
{
int i;
printk("%s %s", msg, of_node_full_name(args->np));
- for (i = 0; i < args->args_count; i++)
- printk(i ? ",%08x" : ":%08x", args->args[i]);
- printk("\n");
+ for (i = 0; i < args->args_count; i++) {
+ const char delim = i ? ',' : ':';
+
+ pr_cont("%c%08x", delim, args->args[i]);
+ }
+ pr_cont("\n");
}
int of_phandle_iterator_init(struct of_phandle_iterator *it,
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index c89d5d231a0e..c9b5cac03b36 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -1015,6 +1015,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
const __be32 *reg, *endp;
int l;
+ bool hotpluggable;
/* We are scanning "memory" nodes only */
if (type == NULL) {
@@ -1034,6 +1035,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
return 0;
endp = reg + (l / sizeof(__be32));
+ hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL);
pr_debug("memory scan node %s, reg size %d,\n", uname, l);
@@ -1049,6 +1051,13 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
(unsigned long long)size);
early_init_dt_add_memory_arch(base, size);
+
+ if (!hotpluggable)
+ continue;
+
+ if (early_init_dt_mark_hotplug_memory_arch(base, size))
+ pr_warn("failed to mark hotplug range 0x%llx - 0x%llx\n",
+ base, base + size);
}
return 0;
@@ -1146,6 +1155,11 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
memblock_add(base, size);
}
+int __init __weak early_init_dt_mark_hotplug_memory_arch(u64 base, u64 size)
+{
+ return memblock_mark_hotplug(base, size);
+}
+
int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
phys_addr_t size, bool nomap)
{
@@ -1168,6 +1182,11 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
WARN_ON(1);
}
+int __init __weak early_init_dt_mark_hotplug_memory_arch(u64 base, u64 size)
+{
+ return -ENOSYS;
+}
+
int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
phys_addr_t size, bool nomap)
{
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 393fea85eb4e..3fda9a32defb 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -697,3 +697,4 @@ void of_msi_configure(struct device *dev, struct device_node *np)
dev_set_msi_domain(dev,
of_msi_get_domain(dev, np, DOMAIN_BUS_PLATFORM_MSI));
}
+EXPORT_SYMBOL_GPL(of_msi_configure);
diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
index b470f7e3521d..262281bd68fa 100644
--- a/drivers/of/of_mdio.c
+++ b/drivers/of/of_mdio.c
@@ -292,6 +292,7 @@ struct phy_device *of_phy_find_device(struct device_node *phy_np)
mdiodev = to_mdio_device(d);
if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY)
return to_phy_device(d);
+ put_device(d);
}
return NULL;
@@ -456,8 +457,11 @@ int of_phy_register_fixed_link(struct device_node *np)
status.link = 1;
status.duplex = of_property_read_bool(fixed_link_node,
"full-duplex");
- if (of_property_read_u32(fixed_link_node, "speed", &status.speed))
+ if (of_property_read_u32(fixed_link_node, "speed",
+ &status.speed)) {
+ of_node_put(fixed_link_node);
return -EINVAL;
+ }
status.pause = of_property_read_bool(fixed_link_node, "pause");
status.asym_pause = of_property_read_bool(fixed_link_node,
"asym-pause");
@@ -486,3 +490,18 @@ int of_phy_register_fixed_link(struct device_node *np)
return -ENODEV;
}
EXPORT_SYMBOL(of_phy_register_fixed_link);
+
+void of_phy_deregister_fixed_link(struct device_node *np)
+{
+ struct phy_device *phydev;
+
+ phydev = of_phy_find_device(np);
+ if (!phydev)
+ return;
+
+ fixed_phy_unregister(phydev);
+
+ put_device(&phydev->mdio.dev); /* of_phy_find_device() */
+ phy_device_free(phydev); /* fixed_phy_register() */
+}
+EXPORT_SYMBOL(of_phy_deregister_fixed_link);
diff --git a/drivers/of/of_numa.c b/drivers/of/of_numa.c
index f63d4b0deff0..a53982a330ea 100644
--- a/drivers/of/of_numa.c
+++ b/drivers/of/of_numa.c
@@ -176,7 +176,12 @@ int of_node_to_nid(struct device_node *device)
np->name);
of_node_put(np);
- if (!r)
+ /*
+ * If numa=off passed on command line, or with a defective
+ * device tree, the nid may not be in the set of possible
+ * nodes. Check for this case and return NUMA_NO_NODE.
+ */
+ if (!r && nid < MAX_NUMNODES && node_possible(nid))
return nid;
return NUMA_NO_NODE;
diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
index b58be12ab277..0ee42c3e66a1 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -120,6 +120,27 @@ int of_get_pci_domain_nr(struct device_node *node)
EXPORT_SYMBOL_GPL(of_get_pci_domain_nr);
/**
+ * This function will try to find the limitation of link speed by finding
+ * a property called "max-link-speed" of the given device node.
+ *
+ * @node: device tree node with the max link speed information
+ *
+ * Returns the associated max link speed from DT, or a negative value if the
+ * required property is not found or is invalid.
+ */
+int of_pci_get_max_link_speed(struct device_node *node)
+{
+ u32 max_link_speed;
+
+ if (of_property_read_u32(node, "max-link-speed", &max_link_speed) ||
+ max_link_speed > 4)
+ return -EINVAL;
+
+ return max_link_speed;
+}
+EXPORT_SYMBOL_GPL(of_pci_get_max_link_speed);
+
+/**
* of_pci_check_probe_only - Setup probe only mode if linux,pci-probe-only
* is present and valid
*/
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 318dbb51e7a2..0d4cda7050e0 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -58,6 +58,41 @@ struct of_overlay {
static int of_overlay_apply_one(struct of_overlay *ov,
struct device_node *target, const struct device_node *overlay);
+static BLOCKING_NOTIFIER_HEAD(of_overlay_chain);
+
+int of_overlay_notifier_register(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&of_overlay_chain, nb);
+}
+EXPORT_SYMBOL_GPL(of_overlay_notifier_register);
+
+int of_overlay_notifier_unregister(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&of_overlay_chain, nb);
+}
+EXPORT_SYMBOL_GPL(of_overlay_notifier_unregister);
+
+static int of_overlay_notify(struct of_overlay *ov,
+ enum of_overlay_notify_action action)
+{
+ struct of_overlay_notify_data nd;
+ int i, ret;
+
+ for (i = 0; i < ov->count; i++) {
+ struct of_overlay_info *ovinfo = &ov->ovinfo_tab[i];
+
+ nd.target = ovinfo->target;
+ nd.overlay = ovinfo->overlay;
+
+ ret = blocking_notifier_call_chain(&of_overlay_chain,
+ action, &nd);
+ if (ret)
+ return notifier_to_errno(ret);
+ }
+
+ return 0;
+}
+
static int of_overlay_apply_single_property(struct of_overlay *ov,
struct device_node *target, struct property *prop)
{
@@ -368,6 +403,13 @@ int of_overlay_create(struct device_node *tree)
goto err_free_idr;
}
+ err = of_overlay_notify(ov, OF_OVERLAY_PRE_APPLY);
+ if (err < 0) {
+ pr_err("%s: Pre-apply notifier failed (err=%d)\n",
+ __func__, err);
+ goto err_free_idr;
+ }
+
/* apply the overlay */
err = of_overlay_apply(ov);
if (err)
@@ -382,6 +424,8 @@ int of_overlay_create(struct device_node *tree)
/* add to the tail of the overlay list */
list_add_tail(&ov->node, &ov_list);
+ of_overlay_notify(ov, OF_OVERLAY_POST_APPLY);
+
mutex_unlock(&of_mutex);
return id;
@@ -498,9 +542,10 @@ int of_overlay_destroy(int id)
goto out;
}
-
+ of_overlay_notify(ov, OF_OVERLAY_PRE_REMOVE);
list_del(&ov->node);
__of_changeset_revert(&ov->cset);
+ of_overlay_notify(ov, OF_OVERLAY_POST_REMOVE);
of_free_overlay_info(ov);
idr_remove(&ov_idr, id);
of_changeset_destroy(&ov->cset);
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index e4bf07d20f9b..b8064bc2b6eb 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -45,6 +45,9 @@ static int of_dev_node_match(struct device *dev, void *data)
* of_find_device_by_node - Find the platform_device associated with a node
* @np: Pointer to device tree node
*
+ * Takes a reference to the embedded struct device which needs to be dropped
+ * after use.
+ *
* Returns platform_device pointer, or NULL if not found
*/
struct platform_device *of_find_device_by_node(struct device_node *np)
@@ -558,9 +561,6 @@ static int of_platform_device_destroy(struct device *dev, void *data)
* of the given device (and, recurrently, their children) that have been
* created from their respective device tree nodes (and only those,
* leaving others - eg. manually created - unharmed).
- *
- * Returns 0 when all children devices have been removed or
- * -EBUSY when some children remained.
*/
void of_platform_depopulate(struct device *parent)
{
diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
index 46325d6394cf..8bf12e904fd2 100644
--- a/drivers/of/resolver.c
+++ b/drivers/of/resolver.c
@@ -28,20 +28,19 @@
* Find a node with the give full name by recursively following any of
* the child node links.
*/
-static struct device_node *__of_find_node_by_full_name(struct device_node *node,
+static struct device_node *find_node_by_full_name(struct device_node *node,
const char *full_name)
{
struct device_node *child, *found;
- if (node == NULL)
+ if (!node)
return NULL;
- /* check */
- if (of_node_cmp(node->full_name, full_name) == 0)
+ if (!of_node_cmp(node->full_name, full_name))
return of_node_get(node);
for_each_child_of_node(node, child) {
- found = __of_find_node_by_full_name(child, full_name);
+ found = find_node_by_full_name(child, full_name);
if (found != NULL) {
of_node_put(child);
return found;
@@ -51,16 +50,12 @@ static struct device_node *__of_find_node_by_full_name(struct device_node *node,
return NULL;
}
-/*
- * Find live tree's maximum phandle value.
- */
-static phandle of_get_tree_max_phandle(void)
+static phandle live_tree_max_phandle(void)
{
struct device_node *node;
phandle phandle;
unsigned long flags;
- /* now search recursively */
raw_spin_lock_irqsave(&devtree_lock, flags);
phandle = 0;
for_each_of_allnodes(node) {
@@ -73,131 +68,102 @@ static phandle of_get_tree_max_phandle(void)
return phandle;
}
-/*
- * Adjust a subtree's phandle values by a given delta.
- * Makes sure not to just adjust the device node's phandle value,
- * but modify the phandle properties values as well.
- */
-static void __of_adjust_tree_phandles(struct device_node *node,
+static void adjust_overlay_phandles(struct device_node *overlay,
int phandle_delta)
{
struct device_node *child;
struct property *prop;
phandle phandle;
- /* first adjust the node's phandle direct value */
- if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
- node->phandle += phandle_delta;
+ /* adjust node's phandle in node */
+ if (overlay->phandle != 0 && overlay->phandle != OF_PHANDLE_ILLEGAL)
+ overlay->phandle += phandle_delta;
- /* now adjust phandle & linux,phandle values */
- for_each_property_of_node(node, prop) {
+ /* copy adjusted phandle into *phandle properties */
+ for_each_property_of_node(overlay, prop) {
- /* only look for these two */
- if (of_prop_cmp(prop->name, "phandle") != 0 &&
- of_prop_cmp(prop->name, "linux,phandle") != 0)
+ if (of_prop_cmp(prop->name, "phandle") &&
+ of_prop_cmp(prop->name, "linux,phandle"))
continue;
- /* must be big enough */
if (prop->length < 4)
continue;
- /* read phandle value */
phandle = be32_to_cpup(prop->value);
- if (phandle == OF_PHANDLE_ILLEGAL) /* unresolved */
+ if (phandle == OF_PHANDLE_ILLEGAL)
continue;
- /* adjust */
- *(uint32_t *)prop->value = cpu_to_be32(node->phandle);
+ *(uint32_t *)prop->value = cpu_to_be32(overlay->phandle);
}
- /* now do the children recursively */
- for_each_child_of_node(node, child)
- __of_adjust_tree_phandles(child, phandle_delta);
+ for_each_child_of_node(overlay, child)
+ adjust_overlay_phandles(child, phandle_delta);
}
-static int __of_adjust_phandle_ref(struct device_node *node,
- struct property *rprop, int value)
+static int update_usages_of_a_phandle_reference(struct device_node *overlay,
+ struct property *prop_fixup, phandle phandle)
{
- phandle phandle;
struct device_node *refnode;
- struct property *sprop;
- char *propval, *propcur, *propend, *nodestr, *propstr, *s;
- int offset, propcurlen;
+ struct property *prop;
+ char *value, *cur, *end, *node_path, *prop_name, *s;
+ int offset, len;
int err = 0;
- /* make a copy */
- propval = kmalloc(rprop->length, GFP_KERNEL);
- if (!propval) {
- pr_err("%s: Could not copy value of '%s'\n",
- __func__, rprop->name);
+ value = kmalloc(prop_fixup->length, GFP_KERNEL);
+ if (!value)
return -ENOMEM;
- }
- memcpy(propval, rprop->value, rprop->length);
+ memcpy(value, prop_fixup->value, prop_fixup->length);
- propend = propval + rprop->length;
- for (propcur = propval; propcur < propend; propcur += propcurlen + 1) {
- propcurlen = strlen(propcur);
+ /* prop_fixup contains a list of tuples of path:property_name:offset */
+ end = value + prop_fixup->length;
+ for (cur = value; cur < end; cur += len + 1) {
+ len = strlen(cur);
- nodestr = propcur;
- s = strchr(propcur, ':');
+ node_path = cur;
+ s = strchr(cur, ':');
if (!s) {
- pr_err("%s: Illegal symbol entry '%s' (1)\n",
- __func__, propcur);
err = -EINVAL;
goto err_fail;
}
*s++ = '\0';
- propstr = s;
+ prop_name = s;
s = strchr(s, ':');
if (!s) {
- pr_err("%s: Illegal symbol entry '%s' (2)\n",
- __func__, (char *)rprop->value);
err = -EINVAL;
goto err_fail;
}
-
*s++ = '\0';
+
err = kstrtoint(s, 10, &offset);
- if (err != 0) {
- pr_err("%s: Could get offset '%s'\n",
- __func__, (char *)rprop->value);
+ if (err)
goto err_fail;
- }
- /* look into the resolve node for the full path */
- refnode = __of_find_node_by_full_name(node, nodestr);
- if (!refnode) {
- pr_warn("%s: Could not find refnode '%s'\n",
- __func__, (char *)rprop->value);
+ refnode = find_node_by_full_name(overlay, node_path);
+ if (!refnode)
continue;
- }
- /* now find the property */
- for_each_property_of_node(refnode, sprop) {
- if (of_prop_cmp(sprop->name, propstr) == 0)
+ for_each_property_of_node(refnode, prop) {
+ if (!of_prop_cmp(prop->name, prop_name))
break;
}
of_node_put(refnode);
- if (!sprop) {
- pr_err("%s: Could not find property '%s'\n",
- __func__, (char *)rprop->value);
+ if (!prop) {
err = -ENOENT;
goto err_fail;
}
- phandle = value;
- *(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle);
+ *(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
}
err_fail:
- kfree(propval);
+ kfree(value);
return err;
}
/* compare nodes taking into account that 'name' strips out the @ part */
-static int __of_node_name_cmp(const struct device_node *dn1,
+static int node_name_cmp(const struct device_node *dn1,
const struct device_node *dn2)
{
const char *n1 = strrchr(dn1->full_name, '/') ? : "/";
@@ -208,85 +174,77 @@ static int __of_node_name_cmp(const struct device_node *dn1,
/*
* Adjust the local phandle references by the given phandle delta.
- * Assumes the existances of a __local_fixups__ node at the root.
- * Assumes that __of_verify_tree_phandle_references has been called.
- * Does not take any devtree locks so make sure you call this on a tree
- * which is at the detached state.
+ *
+ * Subtree @local_fixups, which is overlay node __local_fixups__,
+ * mirrors the fragment node structure at the root of the overlay.
+ *
+ * For each property in the fragments that contains a phandle reference,
+ * @local_fixups has a property of the same name that contains a list
+ * of offsets of the phandle reference(s) within the respective property
+ * value(s). The values at these offsets will be fixed up.
*/
-static int __of_adjust_tree_phandle_references(struct device_node *node,
- struct device_node *target, int phandle_delta)
+static int adjust_local_phandle_references(struct device_node *local_fixups,
+ struct device_node *overlay, int phandle_delta)
{
- struct device_node *child, *childtarget;
- struct property *rprop, *sprop;
+ struct device_node *child, *overlay_child;
+ struct property *prop_fix, *prop;
int err, i, count;
unsigned int off;
phandle phandle;
- if (node == NULL)
+ if (!local_fixups)
return 0;
- for_each_property_of_node(node, rprop) {
+ for_each_property_of_node(local_fixups, prop_fix) {
/* skip properties added automatically */
- if (of_prop_cmp(rprop->name, "name") == 0 ||
- of_prop_cmp(rprop->name, "phandle") == 0 ||
- of_prop_cmp(rprop->name, "linux,phandle") == 0)
+ if (!of_prop_cmp(prop_fix->name, "name") ||
+ !of_prop_cmp(prop_fix->name, "phandle") ||
+ !of_prop_cmp(prop_fix->name, "linux,phandle"))
continue;
- if ((rprop->length % 4) != 0 || rprop->length == 0) {
- pr_err("%s: Illegal property (size) '%s' @%s\n",
- __func__, rprop->name, node->full_name);
+ if ((prop_fix->length % 4) != 0 || prop_fix->length == 0)
return -EINVAL;
- }
- count = rprop->length / sizeof(__be32);
+ count = prop_fix->length / sizeof(__be32);
- /* now find the target property */
- for_each_property_of_node(target, sprop) {
- if (of_prop_cmp(sprop->name, rprop->name) == 0)
+ for_each_property_of_node(overlay, prop) {
+ if (!of_prop_cmp(prop->name, prop_fix->name))
break;
}
- if (sprop == NULL) {
- pr_err("%s: Could not find target property '%s' @%s\n",
- __func__, rprop->name, node->full_name);
+ if (!prop)
return -EINVAL;
- }
for (i = 0; i < count; i++) {
- off = be32_to_cpu(((__be32 *)rprop->value)[i]);
- /* make sure the offset doesn't overstep (even wrap) */
- if (off >= sprop->length ||
- (off + 4) > sprop->length) {
- pr_err("%s: Illegal property '%s' @%s\n",
- __func__, rprop->name,
- node->full_name);
+ off = be32_to_cpu(((__be32 *)prop_fix->value)[i]);
+ if ((off + 4) > prop->length)
return -EINVAL;
- }
-
- if (phandle_delta) {
- /* adjust */
- phandle = be32_to_cpu(*(__be32 *)(sprop->value + off));
- phandle += phandle_delta;
- *(__be32 *)(sprop->value + off) = cpu_to_be32(phandle);
- }
+
+ phandle = be32_to_cpu(*(__be32 *)(prop->value + off));
+ phandle += phandle_delta;
+ *(__be32 *)(prop->value + off) = cpu_to_be32(phandle);
}
}
- for_each_child_of_node(node, child) {
-
- for_each_child_of_node(target, childtarget)
- if (__of_node_name_cmp(child, childtarget) == 0)
+ /*
+ * These nested loops recurse down two subtrees in parallel, where the
+ * node names in the two subtrees match.
+ *
+ * The roots of the subtrees are the overlay's __local_fixups__ node
+ * and the overlay's root node.
+ */
+ for_each_child_of_node(local_fixups, child) {
+
+ for_each_child_of_node(overlay, overlay_child)
+ if (!node_name_cmp(child, overlay_child))
break;
- if (!childtarget) {
- pr_err("%s: Could not find target child '%s' @%s\n",
- __func__, child->name, node->full_name);
+ if (!overlay_child)
return -EINVAL;
- }
- err = __of_adjust_tree_phandle_references(child, childtarget,
+ err = adjust_local_phandle_references(child, overlay_child,
phandle_delta);
- if (err != 0)
+ if (err)
return err;
}
@@ -294,111 +252,103 @@ static int __of_adjust_tree_phandle_references(struct device_node *node,
}
/**
- * of_resolve - Resolve the given node against the live tree.
+ * of_resolve_phandles - Relocate and resolve overlay against live tree
*
- * @resolve: Node to resolve
+ * @overlay: Pointer to devicetree overlay to relocate and resolve
*
- * Perform dynamic Device Tree resolution against the live tree
- * to the given node to resolve. This depends on the live tree
- * having a __symbols__ node, and the resolve node the __fixups__ &
- * __local_fixups__ nodes (if needed).
- * The result of the operation is a resolve node that it's contents
- * are fit to be inserted or operate upon the live tree.
- * Returns 0 on success or a negative error value on error.
+ * Modify (relocate) values of local phandles in @overlay to a range that
+ * does not conflict with the live expanded devicetree. Update references
+ * to the local phandles in @overlay. Update (resolve) phandle references
+ * in @overlay that refer to the live expanded devicetree.
+ *
+ * Phandle values in the live tree are in the range of
+ * 1 .. live_tree_max_phandle(). The range of phandle values in the overlay
+ * also begin with at 1. Adjust the phandle values in the overlay to begin
+ * at live_tree_max_phandle() + 1. Update references to the phandles to
+ * the adjusted phandle values.
+ *
+ * The name of each property in the "__fixups__" node in the overlay matches
+ * the name of a symbol (a label) in the live tree. The values of each
+ * property in the "__fixups__" node is a list of the property values in the
+ * overlay that need to be updated to contain the phandle reference
+ * corresponding to that symbol in the live tree. Update the references in
+ * the overlay with the phandle values in the live tree.
+ *
+ * @overlay must be detached.
+ *
+ * Resolving and applying @overlay to the live expanded devicetree must be
+ * protected by a mechanism to ensure that multiple overlays are processed
+ * in a single threaded manner so that multiple overlays will not relocate
+ * phandles to overlapping ranges. The mechanism to enforce this is not
+ * yet implemented.
+ *
+ * Return: %0 on success or a negative error value on error.
*/
-int of_resolve_phandles(struct device_node *resolve)
+int of_resolve_phandles(struct device_node *overlay)
{
- struct device_node *child, *childroot, *refnode;
- struct device_node *root_sym, *resolve_sym, *resolve_fix;
- struct property *rprop;
+ struct device_node *child, *local_fixups, *refnode;
+ struct device_node *tree_symbols, *overlay_fixups;
+ struct property *prop;
const char *refpath;
phandle phandle, phandle_delta;
int err;
- if (!resolve)
- pr_err("%s: null node\n", __func__);
- if (resolve && !of_node_check_flag(resolve, OF_DETACHED))
- pr_err("%s: node %s not detached\n", __func__,
- resolve->full_name);
- /* the resolve node must exist, and be detached */
- if (!resolve || !of_node_check_flag(resolve, OF_DETACHED))
- return -EINVAL;
-
- /* first we need to adjust the phandles */
- phandle_delta = of_get_tree_max_phandle() + 1;
- __of_adjust_tree_phandles(resolve, phandle_delta);
-
- /* locate the local fixups */
- childroot = NULL;
- for_each_child_of_node(resolve, childroot)
- if (of_node_cmp(childroot->name, "__local_fixups__") == 0)
- break;
-
- if (childroot != NULL) {
- /* resolve root is guaranteed to be the '/' */
- err = __of_adjust_tree_phandle_references(childroot,
- resolve, 0);
- if (err != 0)
- return err;
+ tree_symbols = NULL;
- BUG_ON(__of_adjust_tree_phandle_references(childroot,
- resolve, phandle_delta));
+ if (!overlay) {
+ pr_err("null overlay\n");
+ err = -EINVAL;
+ goto out;
+ }
+ if (!of_node_check_flag(overlay, OF_DETACHED)) {
+ pr_err("overlay not detached\n");
+ err = -EINVAL;
+ goto out;
}
- root_sym = NULL;
- resolve_sym = NULL;
- resolve_fix = NULL;
-
- /* this may fail (if no fixups are required) */
- root_sym = of_find_node_by_path("/__symbols__");
+ phandle_delta = live_tree_max_phandle() + 1;
+ adjust_overlay_phandles(overlay, phandle_delta);
- /* locate the symbols & fixups nodes on resolve */
- for_each_child_of_node(resolve, child) {
+ for_each_child_of_node(overlay, local_fixups)
+ if (!of_node_cmp(local_fixups->name, "__local_fixups__"))
+ break;
- if (!resolve_sym &&
- of_node_cmp(child->name, "__symbols__") == 0)
- resolve_sym = child;
+ err = adjust_local_phandle_references(local_fixups, overlay, phandle_delta);
+ if (err)
+ goto out;
- if (!resolve_fix &&
- of_node_cmp(child->name, "__fixups__") == 0)
- resolve_fix = child;
+ overlay_fixups = NULL;
- /* both found, don't bother anymore */
- if (resolve_sym && resolve_fix)
- break;
+ for_each_child_of_node(overlay, child) {
+ if (!of_node_cmp(child->name, "__fixups__"))
+ overlay_fixups = child;
}
- /* we do allow for the case where no fixups are needed */
- if (!resolve_fix) {
- err = 0; /* no error */
+ if (!overlay_fixups) {
+ err = 0;
goto out;
}
- /* we need to fixup, but no root symbols... */
- if (!root_sym) {
- pr_err("%s: no symbols in root of device tree.\n", __func__);
+ 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;
}
- for_each_property_of_node(resolve_fix, rprop) {
+ for_each_property_of_node(overlay_fixups, prop) {
/* skip properties added automatically */
- if (of_prop_cmp(rprop->name, "name") == 0)
+ if (!of_prop_cmp(prop->name, "name"))
continue;
- err = of_property_read_string(root_sym,
- rprop->name, &refpath);
- if (err != 0) {
- pr_err("%s: Could not find symbol '%s'\n",
- __func__, rprop->name);
+ err = of_property_read_string(tree_symbols,
+ prop->name, &refpath);
+ if (err)
goto out;
- }
refnode = of_find_node_by_path(refpath);
if (!refnode) {
- pr_err("%s: Could not find node by path '%s'\n",
- __func__, refpath);
err = -ENOENT;
goto out;
}
@@ -406,17 +356,15 @@ int of_resolve_phandles(struct device_node *resolve)
phandle = refnode->phandle;
of_node_put(refnode);
- pr_debug("%s: %s phandle is 0x%08x\n",
- __func__, rprop->name, phandle);
-
- err = __of_adjust_phandle_ref(resolve, rprop, phandle);
+ err = update_usages_of_a_phandle_reference(overlay, prop, phandle);
if (err)
break;
}
out:
- /* NULL is handled by of_node_put as NOP */
- of_node_put(root_sym);
+ if (err)
+ pr_err("overlay phandle fixup failed: %d\n", err);
+ of_node_put(tree_symbols);
return err;
}