From: Frank Rowand <frank.rowand@xxxxxxxx> Remove "phandle", "linux,phandle", and "ibm,phandle" properties from the internal device tree. The phandle will still be in the struct device_node phandle field. This is to resolve the issue found by Stephen Boyd [1] when he changed the type of struct property.value from void * to const void *. As a result of the type change, the overlay code had compile errors where the resolver updates phandle values. [1] http://lkml.iu.edu/hypermail/linux/kernel/1702.1/04160.html - Add sysfs infrastructure to report np->phandle, as if it was a property. - Do not create "phandle" "ibm,phandle", and "linux,phandle" properties in the expanded device tree. - Remove phandle properties in of_attach_node(), for nodes dynamically attached to the live tree. Add the phandle sysfs entry for these nodes. - When creating an overlay changeset, duplicate the node phandle in __of_node_dup(). - Remove no longer needed checks to exclude "phandle" and "linux,phandle" properties in several locations. - A side effect of these changes is that the obsolete "linux,phandle" and "ibm,phandle" properties will no longer appear in /proc/device-tree (they will appear as "phandle"). - A side effect is that the value of property "ibm,phandle" will no longer override the value of properties "phandle" and "linux,phandle". Signed-off-by: Frank Rowand <frank.rowand@xxxxxxxx> --- drivers/of/base.c | 48 +++++++++++++++++++++++++++++++++++++++--- drivers/of/dynamic.c | 55 +++++++++++++++++++++++++++++++++++++------------ drivers/of/fdt.c | 43 +++++++++++++++++++------------------- drivers/of/of_private.h | 1 + drivers/of/overlay.c | 4 +--- drivers/of/resolver.c | 23 +-------------------- include/linux/of.h | 1 + 7 files changed, 112 insertions(+), 63 deletions(-) diff --git a/drivers/of/base.c b/drivers/of/base.c index 28d5f53bc631..941c9a03471d 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -116,6 +116,19 @@ static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj, return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length); } +static ssize_t of_node_phandle_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t offset, size_t count) +{ + phandle phandle; + struct device_node *np; + + np = container_of(bin_attr, struct device_node, attr_phandle); + phandle = cpu_to_be32(np->phandle); + return memory_read_from_buffer(buf, count, &offset, &phandle, + sizeof(phandle)); +} + /* always return newly allocated name, caller must free after use */ static const char *safe_name(struct kobject *kobj, const char *orig_name) { @@ -164,6 +177,35 @@ int __of_add_property_sysfs(struct device_node *np, struct property *pp) return rc; } +/* + * In the imported device tree (fdt), phandle is a property. In the + * internal data structure it is instead stored in the struct device_node. + * Make phandle visible in sysfs as if it was a property. + */ +int __of_add_phandle_sysfs(struct device_node *np) +{ + int rc; + + if (!IS_ENABLED(CONFIG_SYSFS)) + return 0; + + if (!of_kset || !of_node_is_attached(np)) + return 0; + + if (!np->phandle || np->phandle == 0xffffffff) + return 0; + + sysfs_bin_attr_init(&np->attr_phandle); + np->attr_phandle.attr.name = "phandle"; + np->attr_phandle.attr.mode = 0444; + np->attr_phandle.size = sizeof(np->phandle); + np->attr_phandle.read = of_node_phandle_read; + + rc = sysfs_create_bin_file(&np->kobj, &np->attr_phandle); + WARN(rc, "error adding attribute phandle to node %s\n", np->full_name); + return rc; +} + int __of_attach_node_sysfs(struct device_node *np) { const char *name; @@ -193,6 +235,8 @@ int __of_attach_node_sysfs(struct device_node *np) if (rc) return rc; + __of_add_phandle_sysfs(np); + for_each_property_of_node(np, pp) __of_add_property_sysfs(np, pp); @@ -2128,9 +2172,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 (!strcmp(pp->name, "name")) continue; np = of_find_node_by_path(pp->value); diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 888fdbc09992..be320082178f 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -218,19 +218,6 @@ int of_property_notify(int action, struct device_node *np, void __of_attach_node(struct device_node *np) { - const __be32 *phandle; - int sz; - - np->name = __of_get_property(np, "name", NULL) ? : "<NULL>"; - np->type = __of_get_property(np, "device_type", NULL) ? : "<NULL>"; - - phandle = __of_get_property(np, "phandle", &sz); - if (!phandle) - phandle = __of_get_property(np, "linux,phandle", &sz); - if (IS_ENABLED(CONFIG_PPC_PSERIES) && !phandle) - phandle = __of_get_property(np, "ibm,phandle", &sz); - np->phandle = (phandle && (sz >= 4)) ? be32_to_cpup(phandle) : 0; - np->child = NULL; np->sibling = np->parent->child; np->parent->child = np; @@ -243,11 +230,48 @@ void __of_attach_node(struct device_node *np) int of_attach_node(struct device_node *np) { struct of_reconfig_data rd; + struct property *prev; + struct property *prop; + struct property *save_next; unsigned long flags; + const __be32 *phandle; + int sz; memset(&rd, 0, sizeof(rd)); rd.dn = np; + /* use "<NULL>" to be consistent with populate_node() */ + np->name = __of_get_property(np, "name", NULL) ? : "<NULL>"; + np->type = __of_get_property(np, "device_type", NULL) ? : "<NULL>"; + + phandle = __of_get_property(np, "phandle", &sz); + if (!phandle) + phandle = __of_get_property(np, "linux,phandle", &sz); + if (IS_ENABLED(CONFIG_PPC_PSERIES) && !phandle) + phandle = __of_get_property(np, "ibm,phandle", &sz); + np->phandle = (phandle && (sz >= 4)) ? be32_to_cpup(phandle) : 0; + + /* remove phandle properties from node */ + prev = NULL; + for (prop = np->properties; prop != NULL; ) { + save_next = prop->next; + if (!strcmp(prop->name, "phandle") || + !strcmp(prop->name, "ibm,phandle") || + !strcmp(prop->name, "linux,phandle")) { + if (prev) + prev->next = prop->next; + else + np->properties = prop->next; + prop->next = np->deadprops; + np->deadprops = prop; + } else { + prev = prop; + } + prop = save_next; + } + + __of_add_phandle_sysfs(np); + mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); __of_attach_node(np); @@ -429,6 +453,7 @@ struct device_node *__of_node_dup(const struct device_node *np, const char *fmt, /* Iterate over and duplicate all properties */ if (np) { struct property *pp, *new_pp; + node->phandle = np->phandle; for_each_property_of_node(np, pp) { new_pp = __of_prop_dup(pp, GFP_KERNEL); if (!new_pp) @@ -441,6 +466,10 @@ struct device_node *__of_node_dup(const struct device_node *np, const char *fmt, } } } + + node->name = __of_get_property(node, "name", NULL) ? : "<NULL>"; + node->type = __of_get_property(node, "device_type", NULL) ? : "<NULL>"; + return node; err_prop: diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 3080d9dd031d..3e3f67475fd4 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -175,6 +175,7 @@ static void populate_properties(const void *blob, struct property *pp, **pprev = NULL; int cur; bool has_name = false; + bool prop_is_phandle = false; pprev = &np->properties; for (cur = fdt_first_property_offset(blob, offset); @@ -198,35 +199,33 @@ static void populate_properties(const void *blob, if (!strcmp(pname, "name")) has_name = true; - pp = unflatten_dt_alloc(mem, sizeof(struct property), - __alignof__(struct property)); + if (strcmp(pname, "phandle") && + strcmp(pname, "linux,phandle") && + strcmp(pname, "ibm,phandle")) + pp = unflatten_dt_alloc(mem, sizeof(struct property), + __alignof__(struct property)); + else + prop_is_phandle = true; + if (dryrun) continue; - /* We accept flattened tree phandles either in - * ePAPR-style "phandle" properties, or the - * legacy "linux,phandle" properties. If both - * appear and have different values, things - * will get weird. Don't do that. + /* We accept flattened tree phandles in ePAPR-style "phandle" + * property, the legacy "linux,phandle" property, or the + * "ibm,phandle" property used in pSeries dynamic device tree. + * If more than one of them appear and have different values, + * things will get weird. Don't do that. */ - if (!strcmp(pname, "phandle") || - !strcmp(pname, "linux,phandle")) { + if (prop_is_phandle) { if (!np->phandle) np->phandle = be32_to_cpup(val); + } else { + pp->name = (char *)pname; + pp->length = sz; + pp->value = (__be32 *)val; + *pprev = pp; + pprev = &pp->next; } - - /* And we process the "ibm,phandle" property - * used in pSeries dynamic device tree - * stuff - */ - if (!strcmp(pname, "ibm,phandle")) - np->phandle = be32_to_cpup(val); - - pp->name = (char *)pname; - pp->length = sz; - pp->value = (__be32 *)val; - *pprev = pp; - pprev = &pp->next; } /* With version 0x10 we may not have the name property, diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 4ebb0149d118..1a041411b219 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -47,6 +47,7 @@ extern int of_property_notify(int action, struct device_node *np, extern void of_node_release(struct kobject *kobj); extern int __of_changeset_apply(struct of_changeset *ocs); extern int __of_changeset_revert(struct of_changeset *ocs); +extern int __of_add_phandle_sysfs(struct device_node *np); #else /* CONFIG_OF_DYNAMIC */ static inline int of_property_notify(int action, struct device_node *np, struct property *prop, struct property *old_prop) diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 7827786718d8..ca0b85f5deb1 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -101,9 +101,7 @@ static int of_overlay_apply_single_property(struct of_overlay *ov, tprop = of_find_property(target, prop->name, NULL); /* special properties are not meant to be updated (silent NOP) */ - if (of_prop_cmp(prop->name, "name") == 0 || - of_prop_cmp(prop->name, "phandle") == 0 || - of_prop_cmp(prop->name, "linux,phandle") == 0) + if (!of_prop_cmp(prop->name, "name")) return 0; propn = __of_prop_dup(prop, GFP_KERNEL); diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c index 771f4844c781..624cbaeae0a4 100644 --- a/drivers/of/resolver.c +++ b/drivers/of/resolver.c @@ -71,30 +71,11 @@ static void adjust_overlay_phandles(struct device_node *overlay, int phandle_delta) { struct device_node *child; - struct property *prop; - phandle phandle; /* adjust node's phandle in node */ if (overlay->phandle != 0 && overlay->phandle != OF_PHANDLE_ILLEGAL) overlay->phandle += phandle_delta; - /* copy adjusted phandle into *phandle properties */ - for_each_property_of_node(overlay, prop) { - - if (of_prop_cmp(prop->name, "phandle") && - of_prop_cmp(prop->name, "linux,phandle")) - continue; - - if (prop->length < 4) - continue; - - phandle = be32_to_cpup(prop->value); - if (phandle == OF_PHANDLE_ILLEGAL) - continue; - - *(__be32 *)prop->value = cpu_to_be32(overlay->phandle); - } - for_each_child_of_node(overlay, child) adjust_overlay_phandles(child, phandle_delta); } @@ -197,9 +178,7 @@ static int adjust_local_phandle_references(struct device_node *local_fixups, 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 (!of_prop_cmp(prop_fix->name, "name")) continue; if ((prop_fix->length % 4) != 0 || prop_fix->length == 0) diff --git a/include/linux/of.h b/include/linux/of.h index 50fcdb54087f..4e483782d124 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -61,6 +61,7 @@ struct device_node { struct kobject kobj; unsigned long _flags; void *data; + struct bin_attribute attr_phandle; #if defined(CONFIG_SPARC) const char *path_component_name; unsigned int unique_id; -- Frank Rowand <frank.rowand@xxxxxxxx> -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html