summaryrefslogtreecommitdiffstats
path: root/drivers/of/base.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of/base.c')
-rw-r--r--drivers/of/base.c958
1 files changed, 797 insertions, 161 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 9ede052274..b22959dabe 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1,23 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* base.c - basic devicetree functions
*
* Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*
* based on Linux devicetree support
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <common.h>
+#include <deep-probe.h>
#include <of.h>
#include <of_address.h>
#include <errno.h>
@@ -26,12 +16,44 @@
#include <memory.h>
#include <linux/sizes.h>
#include <of_graph.h>
+#include <string.h>
+#include <libfile.h>
+#include <linux/clk.h>
#include <linux/ctype.h>
-#include <linux/amba/bus.h>
#include <linux/err.h>
static struct device_node *root_node;
+/**
+ * of_node_has_prefix - Test if a node name has a given prefix
+ * @np: The node name to test
+ * @prefix: The prefix to see if @np starts with
+ *
+ * Returns:
+ * * strlen(@prefix) if @np starts with @prefix
+ * * 0 if @np does not start with @prefix
+ */
+size_t of_node_has_prefix(const struct device_node *np, const char *prefix)
+{
+ return np ? str_has_prefix(kbasename(np->full_name), prefix) : 0;
+}
+EXPORT_SYMBOL(of_node_has_prefix);
+
+bool of_node_name_eq(const struct device_node *np, const char *name)
+{
+ const char *node_name;
+ size_t len;
+
+ if (!np)
+ return false;
+
+ node_name = kbasename(np->full_name);
+ len = strchrnul(node_name, '@') - node_name;
+
+ return (strlen(name) == len) && (strncmp(node_name, name, len) == 0);
+}
+EXPORT_SYMBOL(of_node_name_eq);
+
/*
* Iterate over all nodes of a tree. As a devicetree does not
* have a dedicated list head, the start node (usually the root
@@ -147,16 +169,40 @@ static void of_alias_add(struct alias_prop *ap, struct device_node *np,
strncpy(ap->stem, stem, stem_len);
ap->stem[stem_len] = 0;
list_add_tail(&ap->link, &aliases_lookup);
- pr_debug("adding DT alias:%s: stem=%s id=%i node=%s\n",
- ap->alias, ap->stem, ap->id, np->full_name);
+ pr_debug("adding DT alias:%s: stem=%s id=%i node=%pOF\n",
+ ap->alias, ap->stem, ap->id, np);
+}
+
+static struct device_node *of_alias_resolve(struct device_node *root, struct property *pp)
+{
+ /* Skip those we do not want to proceed */
+ if (!of_prop_cmp(pp->name, "name") ||
+ !of_prop_cmp(pp->name, "phandle") ||
+ !of_prop_cmp(pp->name, "linux,phandle"))
+ return NULL;
+
+ return of_find_node_by_path_from(root, of_property_get_value(pp));
+}
+
+static int of_alias_id_parse(const char *start, int *len)
+{
+ const char *end = start + strlen(start);
+
+ /* walk the alias backwards to extract the id and work out
+ * the 'stem' string */
+ while (isdigit(*(end-1)) && end > start)
+ end--;
+
+ *len = end - start;
+
+ return simple_strtol(end, NULL, 10);
}
/**
* of_alias_scan - Scan all properties of 'aliases' node
*
* The function scans all the properties of 'aliases' node and populates
- * the global lookup table with the properties. It returns the
- * number of alias_prop found, or error code in error case.
+ * the global lookup table with the properties.
*/
void of_alias_scan(void)
{
@@ -177,28 +223,15 @@ void of_alias_scan(void)
list_for_each_entry(pp, &of_aliases->properties, list) {
const char *start = pp->name;
- const char *end = start + strlen(start);
struct device_node *np;
struct alias_prop *ap;
int id, len;
- /* Skip those we do not want to proceed */
- if (!of_prop_cmp(pp->name, "name") ||
- !of_prop_cmp(pp->name, "phandle") ||
- !of_prop_cmp(pp->name, "linux,phandle"))
- continue;
-
- np = of_find_node_by_path(of_property_get_value(pp));
+ np = of_alias_resolve(root_node, pp);
if (!np)
continue;
- /* walk the alias backwards to extract the id and work out
- * the 'stem' string */
- while (isdigit(*(end-1)) && end > start)
- end--;
- len = end - start;
-
- id = simple_strtol(end, NULL, 10);
+ id = of_alias_id_parse(start, &len);
if (id < 0)
continue;
@@ -237,6 +270,41 @@ int of_alias_get_id(struct device_node *np, const char *stem)
}
EXPORT_SYMBOL_GPL(of_alias_get_id);
+int of_alias_get_id_from(struct device_node *root, struct device_node *np,
+ const char *stem)
+{
+ struct device_node *aliasnp, *entrynp;
+ struct property *pp;
+
+ if (!root)
+ return of_alias_get_id(np, stem);
+
+ aliasnp = of_find_node_by_path_from(root, "/aliases");
+ if (!aliasnp)
+ return -ENODEV;
+
+ for_each_property_of_node(aliasnp, pp) {
+ const char *start = pp->name;
+ int id, len;
+
+ entrynp = of_alias_resolve(root_node, pp);
+ if (entrynp != np)
+ continue;
+
+ id = of_alias_id_parse(start, &len);
+ if (id < 0)
+ continue;
+
+ if (strncasecmp(start, stem, len))
+ continue;
+
+ return id;
+ }
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(of_alias_get_id_from);
+
const char *of_alias_get(struct device_node *np)
{
struct alias_prop *app;
@@ -478,7 +546,9 @@ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
EXPORT_SYMBOL(of_get_cpu_node);
/** Checks if the given "compat" string matches one of the strings in
- * the device's "compatible" property
+ * the device's "compatible" property. Returns 0 on mismatch and a
+ * positive score on match with the maximum being OF_DEVICE_COMPATIBLE_MAX_SCORE,
+ * which is only returned if the first compatible matched.
*/
int of_device_is_compatible(const struct device_node *device,
const char *compat)
@@ -491,7 +561,7 @@ int of_device_is_compatible(const struct device_node *device,
for (cp = of_prop_next_string(prop, NULL); cp;
cp = of_prop_next_string(prop, cp), index++) {
if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
- score = INT_MAX/2 - (index << 2);
+ score = OF_DEVICE_COMPATIBLE_MAX_SCORE - (index << 2);
break;
}
}
@@ -501,6 +571,29 @@ int of_device_is_compatible(const struct device_node *device,
EXPORT_SYMBOL(of_device_is_compatible);
/**
+ * of_find_node_by_name_address - Find a node by its full name
+ * @from: The node to start searching from or NULL, the node
+ * you pass will not be searched, only the next one
+ * will; typically, you pass what the previous call
+ * returned.
+ * @name: The name string to match against
+ *
+ * Returns a pointer to the node found or NULL.
+ */
+struct device_node *of_find_node_by_name_address(struct device_node *from,
+ const char *name)
+{
+ struct device_node *np;
+
+ of_tree_for_each_node_from(np, from)
+ if (np->name && !of_node_cmp(np->name, name))
+ return np;
+
+ return NULL;
+}
+EXPORT_SYMBOL(of_find_node_by_name_address);
+
+/**
* of_find_node_by_name - Find a node by its "name" property
* @from: The node to start searching from or NULL, the node
* you pass will not be searched, only the next one
@@ -516,7 +609,7 @@ struct device_node *of_find_node_by_name(struct device_node *from,
struct device_node *np;
of_tree_for_each_node_from(np, from)
- if (np->name && !of_node_cmp(np->name, name))
+ if (np->name && of_node_name_eq(np, name))
return np;
return NULL;
@@ -624,6 +717,9 @@ const struct of_device_id *of_match_node(const struct of_device_id *matches,
if (score > best_score) {
best_match = matches;
best_score = score;
+
+ if (score == OF_DEVICE_COMPATIBLE_MAX_SCORE)
+ break;
}
}
@@ -664,11 +760,11 @@ struct device_node *of_find_matching_node_and_match(struct device_node *from,
}
EXPORT_SYMBOL(of_find_matching_node_and_match);
-int of_match(struct device_d *dev, struct driver_d *drv)
+int of_match(struct device *dev, struct driver *drv)
{
const struct of_device_id *id;
- id = of_match_node(drv->of_compatible, dev->device_node);
+ id = of_match_node(drv->of_compatible, dev->of_node);
if (!id)
return 1;
@@ -739,6 +835,38 @@ int of_property_read_u32_index(const struct device_node *np,
EXPORT_SYMBOL_GPL(of_property_read_u32_index);
/**
+ * of_property_count_elems_of_size - Count the number of elements in a property
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @elem_size: size of the individual element
+ *
+ * Search for a property in a device node and count the number of elements of
+ * size elem_size in it. Returns number of elements on sucess, -EINVAL if the
+ * property does not exist or its length does not match a multiple of elem_size
+ * and -ENODATA if the property does not have a value.
+ */
+int of_property_count_elems_of_size(const struct device_node *np,
+ const char *propname, int elem_size)
+{
+ struct property *prop = of_find_property(np, propname, NULL);
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+
+ if (prop->length % elem_size != 0) {
+ pr_err("size of %s in node %pOF is not a multiple of %d\n",
+ propname, np, elem_size);
+ return -EINVAL;
+ }
+
+ return prop->length / elem_size;
+}
+EXPORT_SYMBOL_GPL(of_property_count_elems_of_size);
+
+/**
* of_property_read_u8_array - Find and read an array of u8 from a property.
*
* @np: device node from which the property value is to be read.
@@ -864,6 +992,43 @@ int of_property_read_u64(const struct device_node *np, const char *propname,
EXPORT_SYMBOL_GPL(of_property_read_u64);
/**
+ * of_property_read_u64_array - Find and read an array of 64 bit integers
+ * from a property.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_value: pointer to return value, modified only if return value is 0.
+ * @sz: number of array elements to read
+ *
+ * Search for a property in a device node and read 64-bit value(s) from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_value is modified only if a valid u64 value can be decoded.
+ */
+int of_property_read_variable_u64_array(const struct device_node *np,
+ const char *propname, u64 *out_values,
+ size_t sz)
+{
+ size_t count;
+ const __be32 *val = of_find_property_value_of_size(np, propname,
+ (sz * sizeof(*out_values)));
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ count = sz;
+ while (count--) {
+ *out_values++ = of_read_number(val, 2);
+ val += 2;
+ }
+
+ return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u64_array);
+
+/**
* of_property_read_string - Find and read a string from a property
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
@@ -905,7 +1070,7 @@ EXPORT_SYMBOL_GPL(of_property_read_string);
* This function searches a string list property and returns the index
* of a specific string value.
*/
-int of_property_match_string(struct device_node *np, const char *propname,
+int of_property_match_string(const struct device_node *np, const char *propname,
const char *string)
{
struct property *prop = of_find_property(np, propname, NULL);
@@ -934,7 +1099,7 @@ int of_property_match_string(struct device_node *np, const char *propname,
}
EXPORT_SYMBOL_GPL(of_property_match_string);
-const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur,
+const __be32 *of_prop_next_u32(const struct property *prop, const __be32 *cur,
u32 *pu)
{
const void *curv = cur;
@@ -960,7 +1125,7 @@ out_val:
}
EXPORT_SYMBOL_GPL(of_prop_next_u32);
-const char *of_prop_next_string(struct property *prop, const char *cur)
+const char *of_prop_next_string(const struct property *prop, const char *cur)
{
const void *curv = cur;
const void *value;
@@ -986,6 +1151,7 @@ EXPORT_SYMBOL_GPL(of_prop_next_string);
*
* @np: device node from which the property is to be set.
* @propname: name of the property to be set.
+ * @value true to set, false to delete
*
* Search for a property in a device node and create or delete the property.
* If the property already exists and write value is false, the property is
@@ -1154,6 +1320,53 @@ int of_property_write_u64_array(struct device_node *np,
}
/**
+ * of_property_write_strings - Write strings to a property. If
+ * the property does not exist, it will be created and appended to the given
+ * device node.
+ *
+ * @np: device node to which the property value is to be written.
+ * @propname: name of the property to be written.
+ * @...: pointers to strings to write
+ *
+ * Search for a property in a device node and write a string to
+ * it. If the property does not exist, it will be created and appended to
+ * the device node. Returns 0 on success, -ENOMEM if the property or array
+ * of elements cannot be created, -EINVAL if no strings specified.
+ */
+int of_property_write_strings(struct device_node *np,
+ const char *propname, ...)
+{
+ const char *val;
+ char *buf = NULL, *next;
+ size_t len = 0;
+ va_list ap;
+ int ret = 0;
+
+ va_start(ap, propname);
+ for (val = va_arg(ap, char *); val; val = va_arg(ap, char *))
+ len += strlen(val) + 1;
+ va_end(ap);
+
+ if (!len)
+ return -EINVAL;
+
+ buf = malloc(len);
+ if (!buf)
+ return -ENOMEM;
+
+ next = buf;
+
+ va_start(ap, propname);
+ for (val = va_arg(ap, char *); val; val = va_arg(ap, char *))
+ next = stpcpy(next, val) + 1;
+ va_end(ap);
+
+ ret = of_set_property(np, propname, buf, len, 1);
+ free(buf);
+ return ret;
+}
+
+/**
* of_property_write_string - Write a string to a property. If
* the property does not exist, it will be created and appended to the given
* device node.
@@ -1281,15 +1494,13 @@ static int __of_parse_phandle_with_args(const struct device_node *np,
*/
node = of_find_node_by_phandle(phandle);
if (!node) {
- pr_err("%s: could not find phandle\n",
- np->full_name);
+ pr_err("%pOF: could not find phandle\n", np);
goto err;
}
if (cells_name &&
of_property_read_u32(node, cells_name, &count)) {
- pr_err("%s: could not get %s for %s\n",
- np->full_name, cells_name,
- node->full_name);
+ pr_err("%pOF: could not get %s for %pOF\n",
+ np, cells_name, node);
goto err;
}
@@ -1298,8 +1509,7 @@ static int __of_parse_phandle_with_args(const struct device_node *np,
* remaining property data length
*/
if (list + count > list_end) {
- pr_err("%s: arguments longer than property\n",
- np->full_name);
+ pr_err("%pOF: arguments longer than property\n", np);
goto err;
}
}
@@ -1521,6 +1731,9 @@ int of_modalias_node(struct device_node *node, char *modalias, int len)
}
EXPORT_SYMBOL_GPL(of_modalias_node);
+static struct device_node *of_chosen;
+static const char *of_model;
+
struct device_node *of_get_root_node(void)
{
return root_node;
@@ -1533,11 +1746,59 @@ int of_set_root_node(struct device_node *node)
root_node = node;
+ of_chosen = of_find_node_by_path("/chosen");
+ of_property_read_string(root_node, "model", &of_model);
+
+ if (of_model)
+ barebox_set_model(of_model);
+
of_alias_scan();
return 0;
}
+static int barebox_of_populate(void)
+{
+ if (IS_ENABLED(CONFIG_OFDEVICE) && deep_probe_is_supported())
+ return of_probe();
+
+ return 0;
+}
+of_populate_initcall(barebox_of_populate);
+
+int barebox_register_of(struct device_node *root)
+{
+ if (root_node)
+ return -EBUSY;
+
+ of_set_root_node(root);
+ of_fix_tree(root);
+
+ if (IS_ENABLED(CONFIG_OFDEVICE)) {
+ of_clk_init();
+ if (!deep_probe_is_supported())
+ return of_probe();
+ }
+
+ return 0;
+}
+
+int barebox_register_fdt(const void *dtb)
+{
+ struct device_node *root;
+
+ if (root_node)
+ return -EBUSY;
+
+ root = of_unflatten_dtb(dtb, INT_MAX);
+ if (IS_ERR(root)) {
+ pr_err("Cannot unflatten dtb: %pe\n", root);
+ return PTR_ERR(root);
+ }
+
+ return barebox_register_of(root);
+}
+
/**
* of_device_is_available - check if a device is available for use
*
@@ -1681,6 +1942,31 @@ int of_get_available_child_count(const struct device_node *parent)
EXPORT_SYMBOL(of_get_available_child_count);
/**
+ * of_get_compatible_child - Find compatible child node
+ * @parent: parent node
+ * @compatible: compatible string
+ *
+ * Lookup child node whose compatible property contains the given compatible
+ * string.
+ *
+ * Returns a node pointer with refcount incremented, use of_node_put() on it
+ * when done; or NULL if not found.
+ */
+struct device_node *of_get_compatible_child(const struct device_node *parent,
+ const char *compatible)
+{
+ struct device_node *child;
+
+ for_each_child_of_node(parent, child) {
+ if (of_device_is_compatible(child, compatible))
+ return child;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(of_get_compatible_child);
+
+/**
* of_get_child_by_name - Find the child node by name for a given parent
* @node: parent node
* @name: child name to look for.
@@ -1739,68 +2025,83 @@ int of_property_read_string_helper(const struct device_node *np,
return i <= 0 ? -ENODATA : i;
}
-static void __of_print_nodes(struct device_node *node, int indent, const char *prefix)
+static void __of_print_property_prefixed(const struct property *p, int indent,
+ unsigned maxpropsize, const char *prefix)
+{
+ unsigned length;
+
+ printf("%s%*s%s", prefix, indent * 8, "", p->name);
+
+ length = min_t(unsigned, p->length, maxpropsize);
+ if (length) {
+ printf(" = ");
+ of_print_property(of_property_get_value(p), length);
+ }
+ if (length != p->length)
+ printf(" /* %u more bytes omitted */", p->length - length);
+
+ printf(";\n");
+}
+
+static int __of_print_nodes(struct device_node *node, int indent,
+ unsigned maxpropsize, const char *prefix)
{
struct device_node *n;
struct property *p;
+ int ret;
if (!node)
- return;
+ return 0;
if (!prefix)
prefix = "";
printf("%s%*s%s%s\n", prefix, indent * 8, "", node->name, node->name ? " {" : "{");
- list_for_each_entry(p, &node->properties, list) {
- printf("%s%*s%s", prefix, (indent + 1) * 8, "", p->name);
- if (p->length) {
- printf(" = ");
- of_print_property(of_property_get_value(p), p->length);
- }
- printf(";\n");
- }
+ list_for_each_entry(p, &node->properties, list)
+ __of_print_property_prefixed(p, indent + 1, maxpropsize, prefix);
+
+ if (ctrlc())
+ return -EINTR;
list_for_each_entry(n, &node->children, parent_list) {
- __of_print_nodes(n, indent + 1, prefix);
+ ret = __of_print_nodes(n, indent + 1, maxpropsize, prefix);
+ if (ret)
+ return ret;
}
printf("%s%*s};\n", prefix, indent * 8, "");
+ return 0;
}
-void of_print_nodes(struct device_node *node, int indent)
+void of_print_nodes(struct device_node *node, int indent, unsigned maxpropsize)
{
- __of_print_nodes(node, indent, NULL);
+ __of_print_nodes(node, indent, maxpropsize, NULL);
}
-static void __of_print_property(struct property *p, int indent)
+static void __of_print_property(struct property *p, int indent, unsigned maxpropsize)
{
- int i;
+ __of_print_property_prefixed(p, indent, maxpropsize, "");
+}
- for (i = 0; i < indent; i++)
- printf("\t");
+void of_print_properties(struct device_node *node, unsigned maxpropsize)
+{
+ struct property *prop;
- printf("%s", p->name);
- if (p->length) {
- printf(" = ");
- of_print_property(of_property_get_value(p), p->length);
- }
- printf(";\n");
+ list_for_each_entry(prop, &node->properties, list)
+ __of_print_property(prop, 0, maxpropsize);
}
static int __of_print_parents(struct device_node *node)
{
- int indent, i;
+ int indent;
if (!node->parent)
return 0;
indent = __of_print_parents(node->parent);
- for (i = 0; i < indent; i++)
- printf("\t");
-
- printf("%s {\n", node->name);
+ printf("%*s%s {\n", indent * 8, "", node->name);
return indent + 1;
}
@@ -1841,57 +2142,76 @@ static void of_print_close(struct device_node *node, int *printed)
* This function compares two device trees against each other and prints
* a diff-like result.
*/
-void of_diff(struct device_node *a, struct device_node *b, int indent)
+int of_diff(struct device_node *a, struct device_node *b, int indent)
{
struct property *ap, *bp;
struct device_node *ca, *cb;
- int printed = 0;
+ int printed = 0, diff = 0;
+ bool silent = indent < 0;
list_for_each_entry(ap, &a->properties, list) {
bp = of_find_property(b, ap->name, NULL);
if (!bp) {
+ diff++;
+ if (silent)
+ continue;
of_print_parents(a, &printed);
printf("- ");
- __of_print_property(ap, indent);
+ __of_print_property(ap, indent, ~0);
continue;
}
if (ap->length != bp->length || memcmp(of_property_get_value(ap), of_property_get_value(bp), bp->length)) {
+ diff++;
+ if (silent)
+ continue;
of_print_parents(a, &printed);
printf("- ");
- __of_print_property(ap, indent);
+ __of_print_property(ap, indent, ~0);
printf("+ ");
- __of_print_property(bp, indent);
+ __of_print_property(bp, indent, ~0);
}
}
list_for_each_entry(bp, &b->properties, list) {
ap = of_find_property(a, bp->name, NULL);
if (!ap) {
+ diff++;
+ if (silent)
+ continue;
of_print_parents(a, &printed);
printf("+ ");
- __of_print_property(bp, indent);
+ __of_print_property(bp, indent, ~0);
}
}
for_each_child_of_node(a, ca) {
cb = of_get_child_by_name(b, ca->name);
if (cb) {
- of_diff(ca, cb, indent + 1);
+ diff += of_diff(ca, cb, silent ? indent : indent + 1);
} else {
+ diff++;
+ if (silent)
+ continue;
of_print_parents(a, &printed);
- __of_print_nodes(ca, indent, "-");
+ __of_print_nodes(ca, indent, ~0, "- ");
}
}
for_each_child_of_node(b, cb) {
if (!of_get_child_by_name(a, cb->name)) {
+ diff++;
+ if (silent)
+ continue;
of_print_parents(a, &printed);
- __of_print_nodes(cb, indent, "+");
+ __of_print_nodes(cb, indent, ~0, "+ ");
}
}
- of_print_close(a, &printed);
+ if (!silent)
+ of_print_close(a, &printed);
+
+ return diff;
}
struct device_node *of_new_node(struct device_node *parent, const char *name)
@@ -1908,8 +2228,8 @@ struct device_node *of_new_node(struct device_node *parent, const char *name)
if (parent) {
node->name = xstrdup(name);
- node->full_name = basprintf("%s/%s",
- node->parent->full_name, name);
+ node->full_name = basprintf("%pOF/%s",
+ node->parent, name);
list_add(&node->list, &parent->list);
} else {
node->name = xstrdup("");
@@ -1920,6 +2240,21 @@ struct device_node *of_new_node(struct device_node *parent, const char *name)
return node;
}
+struct property *__of_new_property(struct device_node *node, const char *name,
+ void *data, int len)
+{
+ struct property *prop;
+
+ prop = xzalloc(sizeof(*prop));
+ prop->name = xstrdup(name);
+ prop->length = len;
+ prop->value = data;
+
+ list_add_tail(&prop->list, &node->properties);
+
+ return prop;
+}
+
/**
* of_new_property - Add a new property to a node
* @node: device node to which the property is added
@@ -1935,19 +2270,13 @@ struct device_node *of_new_node(struct device_node *parent, const char *name)
struct property *of_new_property(struct device_node *node, const char *name,
const void *data, int len)
{
- struct property *prop;
-
- prop = xzalloc(sizeof(*prop));
- prop->name = xstrdup(name);
- prop->length = len;
- prop->value = xzalloc(len);
+ char *buf;
+ buf = xzalloc(len);
if (data)
- memcpy(prop->value, data, len);
+ memcpy(buf, data, len);
- list_add_tail(&prop->list, &node->properties);
-
- return prop;
+ return __of_new_property(node, name, buf, len);
}
/**
@@ -1991,6 +2320,38 @@ void of_delete_property(struct property *pp)
free(pp);
}
+struct property *of_rename_property(struct device_node *np,
+ const char *old_name, const char *new_name)
+{
+ struct property *pp;
+
+ pp = of_find_property(np, old_name, NULL);
+ if (!pp)
+ return NULL;
+
+ of_property_write_bool(np, new_name, false);
+
+ free(pp->name);
+ pp->name = xstrdup(new_name);
+ return pp;
+}
+
+struct property *of_copy_property(const struct device_node *src,
+ const char *propname,
+ struct device_node *dst)
+{
+ struct property *prop;
+
+ prop = of_find_property(src, propname, NULL);
+ if (!prop)
+ return NULL;
+
+ return of_new_property(dst, propname,
+ of_property_get_value(prop), prop->length);
+}
+EXPORT_SYMBOL_GPL(of_copy_property);
+
+
/**
* of_set_property - create a property for a given node
* @node - the node
@@ -2019,6 +2380,101 @@ int of_set_property(struct device_node *np, const char *name, const void *val, i
return 0;
}
+int of_append_property(struct device_node *np, const char *name, const void *val, int len)
+{
+ struct property *pp;
+ int orig_len;
+ void *buf;
+
+ if (!np)
+ return -ENOENT;
+
+ pp = of_find_property(np, name, NULL);
+ if (!pp) {
+ of_new_property(np, name, val, len);
+ return 0;
+ }
+
+ orig_len = pp->length;
+ buf = realloc(pp->value, orig_len + len);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy(buf + orig_len, val, len);
+
+ pp->value = buf;
+ pp->length += len;
+
+ if (pp->value_const) {
+ memcpy(buf, pp->value_const, orig_len);
+ pp->value_const = NULL;
+ }
+
+ return 0;
+}
+
+int of_prepend_property(struct device_node *np, const char *name, const void *val, int len)
+{
+ struct property *pp;
+ const void *oldval;
+ void *buf;
+ int oldlen;
+
+ pp = of_find_property(np, name, &oldlen);
+ if (!pp) {
+ of_new_property(np, name, val, len);
+ return 0;
+ }
+
+ oldval = of_property_get_value(pp);
+
+ buf = malloc(len + oldlen);
+ if (!buf)
+ return -ENOMEM;
+
+ memcpy(buf, val, len);
+ memcpy(buf + len, oldval, oldlen);
+
+ free(pp->value);
+ pp->value = buf;
+ pp->length = len + oldlen;
+ pp->value_const = NULL;
+
+ return 0;
+}
+
+int of_property_sprintf(struct device_node *np,
+ const char *propname, const char *fmt, ...)
+{
+ struct property *pp;
+ struct va_format vaf;
+ char *buf = NULL;
+ va_list args;
+ int len;
+
+ if (!np)
+ return -ENOENT;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ len = asprintf(&buf, "%pV", &vaf);
+ va_end(args);
+
+ if (len < 0)
+ return -ENOMEM;
+
+ len++; /* trailing NUL */
+
+ pp = of_find_property(np, propname, NULL);
+ of_delete_property(pp);
+
+ __of_new_property(np, propname, buf, len);
+ return len;
+}
+
+static int mem_bank_num;
+
int of_add_memory(struct device_node *node, bool dump)
{
const char *device_type;
@@ -2030,22 +2486,25 @@ int of_add_memory(struct device_node *node, bool dump)
return -ENXIO;
while (!of_address_to_resource(node, n, &res)) {
- if (!resource_size(&res)) {
- n++;
+ int err;
+ n++;
+ if (!resource_size(&res))
continue;
- }
- of_add_memory_bank(node, dump, n,
+ if (!of_device_is_available(node))
+ continue;
+
+ err = of_add_memory_bank(node, dump, mem_bank_num,
res.start, resource_size(&res));
- n++;
+ if (err)
+ ret = err;
+
+ mem_bank_num++;
}
- return 0;
+ return ret;
}
-static struct device_node *of_chosen;
-static const char *of_model;
-
const char *of_get_model(void)
{
return of_model;
@@ -2055,36 +2514,96 @@ const struct of_device_id of_default_bus_match_table[] = {
{
.compatible = "simple-bus",
}, {
+ .compatible = "simple-pm-bus",
+ }, {
.compatible = "simple-mfd",
}, {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, of_default_bus_match_table);
+
+static int of_probe_memory(void)
+{
+ struct device_node *memory = root_node;
+ int ret = 0;
+ if (!IS_ENABLED(CONFIG_OFDEVICE))
+ return 0;
+
+ /* Parse all available node with "memory" device_type */
+ while (1) {
+ int err;
+
+ memory = of_find_node_by_type(memory, "memory");
+ if (!memory)
+ break;
+
+ err = of_add_memory(memory, false);
+ if (err)
+ ret = err;
+ }
+
+ return ret;
+}
+mem_initcall(of_probe_memory);
+
+static void of_platform_device_create_root(struct device_node *np)
+{
+ static struct device *dev;
+ int ret;
+
+ if (dev)
+ return;
+
+ dev = xzalloc(sizeof(*dev));
+ dev->id = DEVICE_ID_SINGLE;
+ dev->of_node = np;
+ dev_set_name(dev, "machine");
+
+ ret = platform_device_register(dev);
+ if (ret)
+ free_device(dev);
+}
+
+static const struct of_device_id reserved_mem_matches[] = {
+ { .compatible = "nvmem-rmem" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, reserved_mem_matches);
+
+/**
+ * of_probe - Probe unflattened device tree starting at of_get_root_node
+ *
+ * The function walks the device tree and creates devices as needed.
+ * With care, it can be called more than once, but if you really need that,
+ * consider first if deep probe would help instead.
+ */
int of_probe(void)
{
- struct device_node *memory, *firmware;
+ struct device_node *node;
if(!root_node)
return -ENODEV;
- of_chosen = of_find_node_by_path("/chosen");
- of_property_read_string(root_node, "model", &of_model);
-
- if (of_model)
- barebox_set_model(of_model);
+ /*
+ * We do this first thing, so board drivers can patch the device
+ * tree prior to device creation if needed.
+ */
+ of_platform_device_create_root(root_node);
- memory = of_find_node_by_path("/memory");
- if (!memory)
- memory = of_find_node_by_type(root_node, "memory");
- if (memory)
- of_add_memory(memory, false);
+ /*
+ * Handle certain compatibles explicitly, since we don't want to create
+ * platform_devices for every node in /reserved-memory with a
+ * "compatible",
+ */
+ for_each_matching_node(node, reserved_mem_matches)
+ of_platform_device_create(node, NULL);
- firmware = of_find_node_by_path("/firmware");
- if (firmware)
- of_platform_populate(firmware, NULL, NULL);
+ node = of_find_node_by_path("/firmware");
+ if (node)
+ of_platform_populate(node, NULL, NULL);
- of_clk_init(root_node, NULL);
of_platform_populate(root_node, of_default_bus_match_table, NULL);
return 0;
@@ -2134,31 +2653,51 @@ out:
return dn;
}
-struct device_node *of_copy_node(struct device_node *parent, const struct device_node *other)
+void of_merge_nodes(struct device_node *np, const struct device_node *other)
{
- struct device_node *np, *child;
+ struct device_node *child;
struct property *pp;
- np = of_new_node(parent, other->name);
-
list_for_each_entry(pp, &other->properties, list)
of_new_property(np, pp->name, pp->value, pp->length);
for_each_child_of_node(other, child)
of_copy_node(np, child);
+}
+
+struct device_node *of_copy_node(struct device_node *parent, const struct device_node *other)
+{
+ struct device_node *np;
+
+ np = of_new_node(parent, other->name);
+ np->phandle = other->phandle;
+
+ of_merge_nodes(np, other);
return np;
}
+struct device_node *of_dup(const struct device_node *root)
+{
+ if (IS_ERR_OR_NULL(root))
+ return ERR_CAST(root);
+
+ return of_copy_node(NULL, root);
+}
+
void of_delete_node(struct device_node *node)
{
struct device_node *n, *nt;
struct property *p, *pt;
- struct device_d *dev;
if (!node)
return;
+ if (node == root_node) {
+ pr_err("Won't delete root device node\n");
+ return;
+ }
+
list_for_each_entry_safe(p, pt, &node->properties, list)
of_delete_property(p);
@@ -2170,47 +2709,72 @@ void of_delete_node(struct device_node *node)
list_del(&node->list);
}
- dev = of_find_device_by_node(node);
- if (dev)
- dev->device_node = NULL;
-
free(node->name);
free(node->full_name);
free(node);
-
- if (node == root_node)
- of_set_root_node(NULL);
}
-int of_device_is_stdout_path(struct device_d *dev)
-{
+/*
+ * of_find_node_by_chosen - Find a node given a chosen property pointing at it
+ * @propname: the name of the property containing a path or alias
+ * The function will lookup the first string in the property
+ * value up to the first : character or till \0.
+ * @options The Remainder (without : or \0 at the end) will be written
+ * to *options if not NULL.
+ */
+struct device_node *of_find_node_by_chosen(const char *propname,
+ const char **options)
+{
+ const char *value, *p;
+ char *buf;
struct device_node *dn;
- const char *name;
- const char *p;
- char *q;
- if (!dev->device_node)
- return 0;
+ value = of_get_property(of_chosen, propname, NULL);
+ if (!value)
+ return NULL;
- name = of_get_property(of_chosen, "stdout-path", NULL);
- if (!name)
- name = of_get_property(of_chosen, "linux,stdout-path", NULL);
+ p = strchrnul(value, ':');
+ buf = xstrndup(value, p - value);
- if (!name)
- return 0;
+ dn = of_find_node_by_path_or_alias(NULL, buf);
- /* This could make use of strchrnul if it were available */
- p = strchr(name, ':');
- if (!p)
- p = name + strlen(name);
+ free(buf);
+
+ if (options && *p)
+ *options = p + 1;
+
+ return dn;
+}
+
+struct device_node *of_get_stdoutpath(unsigned int *baudrate)
+{
+ const char *opts = NULL;
+ struct device_node *dn;
+
+ dn = of_find_node_by_chosen("stdout-path", &opts);
+ if (!dn)
+ dn = of_find_node_by_chosen("linux,stdout-path", &opts);
+ if (!dn)
+ return NULL;
+
+ if (baudrate && opts) {
+ unsigned rate = simple_strtoul(opts, NULL, 10);
+ if (rate)
+ *baudrate = rate;
+ }
- q = xstrndup(name, p - name);
+ return dn;
+}
- dn = of_find_node_by_path_or_alias(NULL, q);
+int of_device_is_stdout_path(struct device *dev, unsigned int *baudrate)
+{
+ unsigned int tmp = *baudrate;
- free(q);
+ if (!dev || !dev->of_node || dev->of_node != of_get_stdoutpath(&tmp))
+ return false;
- return dn == dev->device_node;
+ *baudrate = tmp;
+ return true;
}
/**
@@ -2294,6 +2858,21 @@ int of_device_enable_path(const char *path)
}
/**
+ * of_device_enable_by_alias - enable a device node by alias
+ * @alias - the alias of the device tree node to enable
+ */
+int of_device_enable_by_alias(const char *alias)
+{
+ struct device_node *node;
+
+ node = of_find_node_by_alias(NULL, alias);
+ if (!node)
+ return -ENODEV;
+
+ return of_device_enable(node);
+}
+
+/**
* of_device_disable - disable a devicenode device
* @node - the node to disable
*
@@ -2322,6 +2901,52 @@ int of_device_disable_path(const char *path)
}
/**
+ * of_device_disable_by_alias - disable a devicenode by alias
+ * @alias - the alias of the device tree node to disable
+ */
+int of_device_disable_by_alias(const char *alias)
+{
+ struct device_node *node;
+
+ node = of_find_node_by_alias(NULL, alias);
+ if (!node)
+ return -ENODEV;
+
+ return of_device_disable(node);
+}
+
+/**
+ * of_read_file - unflatten oftree file
+ * @filename - path to file to unflatten its contents
+ *
+ * Returns the root node of the tree or an error pointer on error.
+ */
+struct device_node *of_read_file(const char *filename)
+{
+ void *fdt;
+ size_t size;
+ struct device_node *root;
+
+ fdt = read_file(filename, &size);
+ if (!fdt) {
+ pr_err("unable to read %s: %m\n", filename);
+ return ERR_PTR(-errno);
+ }
+
+ if (IS_ENABLED(CONFIG_FILETYPE) && file_detect_type(fdt, size) != filetype_oftree) {
+ pr_err("%s is not a flat device tree file.\n", filename);
+ root = ERR_PTR(-EINVAL);
+ goto out;
+ }
+
+ root = of_unflatten_dtb(fdt, size);
+out:
+ free(fdt);
+
+ return root;
+}
+
+/**
* of_get_reproducible_name() - get a reproducible name of a node
* @node: The node to get a name from
*
@@ -2404,12 +3029,23 @@ struct device_node *of_find_node_by_reproducible_name(struct device_node *from,
return NULL;
}
+struct device_node *of_get_node_by_reproducible_name(struct device_node *dstroot,
+ struct device_node *srcnp)
+{
+ struct device_node *dstnp;
+ char *name;
+
+ name = of_get_reproducible_name(srcnp);
+ dstnp = of_find_node_by_reproducible_name(dstroot, name);
+ free(name);
+
+ return dstnp;
+}
+
/**
* of_graph_parse_endpoint() - parse common endpoint node properties
* @node: pointer to endpoint device_node
* @endpoint: pointer to the OF endpoint data structure
- *
- * The caller should hold a reference to @node.
*/
int of_graph_parse_endpoint(const struct device_node *node,
struct of_endpoint *endpoint)
@@ -2417,8 +3053,8 @@ int of_graph_parse_endpoint(const struct device_node *node,
struct device_node *port_node = of_get_parent(node);
if (!port_node)
- pr_warn("%s(): endpoint %s has no parent node\n",
- __func__, node->full_name);
+ pr_warn("%s(): endpoint %pOF has no parent node\n",
+ __func__, node);
memset(endpoint, 0, sizeof(*endpoint));
@@ -2490,15 +3126,15 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
port = of_get_child_by_name(parent, "port");
if (!port) {
- pr_err("%s(): no port node found in %s\n",
- __func__, parent->full_name);
+ pr_err("%s(): no port node found in %pOF\n",
+ __func__, parent);
return NULL;
}
} else {
port = of_get_parent(prev);
if (!port) {
- pr_warn("%s(): endpoint %s has no parent node\n",
- __func__, prev->full_name);
+ pr_warn("%s(): endpoint %pOF has no parent node\n",
+ __func__, prev);
return NULL;
}
}