[U-Boot] [PATCH 08/22] dm: Add support for device tree references

Simon Glass sjg at chromium.org
Wed Jan 18 06:51:44 CET 2017


Since U-Boot supports both a live tree and a flat tree, we need an easy
way to access the tree without bothering about which is currently active.
To support this, introduce the concept of an of_node_ref, which can refer
either to a live tree node or a flat tree node.

For the live tree, the reference contains a pointer to the node (struct
device_node *) or NULL if the node is invalid. For the flat tree, the
reference contains the node offset or -1 if the node is invalid.

A basic set of operations is provided which use references. These are
implemented by using either libfdt functions (in the case of a flat DT
reference) or the live-tree of_...() functions.

Note that it is not possible to have both live and flat references active
at the same time. As soon as the live tree is available, everything in
U-Boot should switch to using that. This avoids confusion and allows us to
assume that the type of a reference is simply based on whether we have a
live tree yet, or not.

Signed-off-by: Simon Glass <sjg at chromium.org>
---

 drivers/core/Makefile |   1 +
 drivers/core/of_ref.c | 218 +++++++++++++++++++++++++++++++++++
 include/dm/of_ref.h   | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 525 insertions(+)
 create mode 100644 drivers/core/of_ref.c
 create mode 100644 include/dm/of_ref.h

diff --git a/drivers/core/Makefile b/drivers/core/Makefile
index 07adb61c285..0ffc38601f4 100644
--- a/drivers/core/Makefile
+++ b/drivers/core/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_$(SPL_)SIMPLE_BUS)	+= simple-bus.o
 obj-$(CONFIG_DM)	+= dump.o
 obj-$(CONFIG_$(SPL_)REGMAP)	+= regmap.o
 obj-$(CONFIG_$(SPL_)SYSCON)	+= syscon-uclass.o
+obj-$(CONFIG_OF_CONTROL) += of_ref.o
diff --git a/drivers/core/of_ref.c b/drivers/core/of_ref.c
new file mode 100644
index 00000000000..a9206b5a565
--- /dev/null
+++ b/drivers/core/of_ref.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2017 Google, Inc
+ * Written by Simon Glass <sjg at chromium.org>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <libfdt.h>
+#include <dm/of_access.h>
+#include <dm/of_ref.h>
+#include <linux/err.h>
+
+const struct device_node *_of_ref_to_node(of_node_ref node_ref)
+{
+	const void *node = node_ref.node;
+
+	if (node < gd->fdt_blob || node >= gd->fdt_blob + gd->fdt_size)
+		return node;
+	return NULL;
+}
+
+int of_ref_read_u32(of_node_ref node_ref, const char *propname, u32 *outp)
+{
+	assert(of_ref_valid(node_ref));
+	debug("%s: %s: ", __func__, propname);
+
+	if (of_ref_is_node(node_ref)) {
+		return of_read_u32(of_ref_to_node(node_ref), propname, outp);
+	} else {
+		const int *cell;
+		int len;
+
+		cell = fdt_getprop(gd->fdt_blob, of_ref_to_offset(node_ref),
+				   propname, &len);
+		if (!cell || len < sizeof(int)) {
+			debug("(not found)\n");
+			return -EINVAL;
+		}
+		*outp = fdt32_to_cpu(cell[0]);
+	}
+	debug("%#x (%d)\n", *outp, *outp);
+
+	return 0;
+}
+
+int of_ref_read_u32_default(of_node_ref node_ref, const char *propname, u32 def)
+{
+	assert(of_ref_valid(node_ref));
+	of_ref_read_u32(node_ref, propname, &def);
+
+	return def;
+}
+
+int of_ref_read_s32_default(of_node_ref node_ref, const char *propname, s32 def)
+{
+	assert(of_ref_valid(node_ref));
+	of_ref_read_u32(node_ref, propname, (u32 *)&def);
+
+	return def;
+}
+
+bool of_ref_read_bool(of_node_ref node_ref, const char *propname)
+{
+	bool val;
+
+	assert(of_ref_valid(node_ref));
+	debug("%s: %s: ", __func__, propname);
+
+	if (of_ref_is_node(node_ref)) {
+		val = !!of_find_property(of_ref_to_node(node_ref), propname,
+					 NULL);
+	} else {
+		val = !!fdt_getprop(gd->fdt_blob, of_ref_to_offset(node_ref),
+				    propname, NULL);
+	}
+	debug("%s\n", val ? "true" : "false");
+
+	return 0;
+}
+
+/**
+ * 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.
+ * @strp:	pointer to null terminated return string, modified only if
+ *		return value is 0.
+ *
+ * Search for a property in a device tree node and retrieve a null
+ * terminated string value (pointer to data, not a copy). Returns 0 on
+ * success, -EINVAL if the property does not exist, -ENODATA if property
+ * does not have a value, and -EILSEQ if the string is not null-terminated
+ * within the length of the property data.
+ *
+ * The out_string pointer is modified only if a valid string can be decoded.
+ */
+const char *of_ref_read_string(of_node_ref node_ref, const char *propname)
+{
+	const char *str = NULL;
+	int len = -1;
+
+	assert(of_ref_valid(node_ref));
+	debug("%s: %s: ", __func__, propname);
+
+	if (of_ref_is_node(node_ref)) {
+		struct property *prop = of_find_property(
+				of_ref_to_node(node_ref), propname, NULL);
+
+		if (prop) {
+			str = prop->value;
+			len = prop->length;
+		}
+	} else {
+		str = fdt_getprop(gd->fdt_blob, of_ref_to_offset(node_ref),
+				  propname, &len);
+	}
+	if (!str) {
+		debug("<not found>\n");
+		return NULL;
+	}
+	if (strnlen(str, len) >= len) {
+		debug("<invalid>\n");
+		return NULL;
+	}
+	debug("%s\n", str);
+
+	return 0;
+}
+
+of_node_ref of_ref_find_subnode(of_node_ref node_ref, const char *subnode_name)
+{
+	of_node_ref subnode_ref;
+
+	assert(of_ref_valid(node_ref));
+	debug("%s: %s: ", __func__, subnode_name);
+
+	if (of_ref_is_node(node_ref)) {
+		const struct device_node *np = of_ref_to_node(node_ref);
+		struct device_node *subnode;
+
+		for (subnode = np->child; subnode; subnode = subnode->sibling) {
+			if (!strcmp(subnode_name, subnode->name))
+				break;
+		}
+		subnode_ref = of_node_to_ref(subnode);
+	} else {
+		int subnode = fdt_subnode_offset(gd->fdt_blob,
+				of_ref_to_offset(node_ref), subnode_name);
+		subnode_ref = of_offset_to_ref(subnode);
+	}
+	debug("%s\n", of_ref_valid(subnode_ref) ?
+	      of_ref_get_name(subnode_ref) : "<none>");
+
+	return subnode_ref;
+}
+
+int of_ref_read_u32_array(of_node_ref node_ref, const char *propname,
+			  u32 *out_values, size_t sz)
+{
+	assert(of_ref_valid(node_ref));
+	debug("%s: %s: ", __func__, propname);
+
+	if (of_ref_is_node(node_ref)) {
+		return of_read_u32_array(of_ref_to_node(node_ref), propname,
+					 out_values, sz);
+	} else {
+		debug("not implemented\n");
+		return -ENOSYS;
+	}
+}
+
+of_node_ref of_ref_first_subnode(of_node_ref node_ref)
+{
+	assert(of_ref_valid(node_ref));
+	if (of_ref_is_node(node_ref))
+		return of_node_to_ref(node_ref.node->child);
+
+	return of_offset_to_ref(
+		fdt_first_subnode(gd->fdt_blob, of_ref_to_offset(node_ref)));
+}
+
+of_node_ref of_ref_next_subnode(of_node_ref node_ref)
+{
+	assert(of_ref_valid(node_ref));
+	if (of_ref_is_node(node_ref))
+		return of_node_to_ref(node_ref.node->sibling);
+
+	return of_offset_to_ref(
+		fdt_next_subnode(gd->fdt_blob, of_ref_to_offset(node_ref)));
+}
+
+const char *of_ref_get_name(of_node_ref node_ref)
+{
+	assert(of_ref_valid(node_ref));
+	if (of_ref_is_node(node_ref))
+		return node_ref.node->name;
+
+	return fdt_get_name(gd->fdt_blob, of_ref_to_offset(node_ref), NULL);
+}
+
+void of_ref_from_fdtdec_phandle_args(struct fdtdec_phandle_args *in,
+				     struct of_ref_phandle_args *out)
+{
+	assert(OF_MAX_PHANDLE_ARGS == MAX_PHANDLE_ARGS);
+	out->node_ref = of_offset_to_ref(in->node);
+	out->args_count = in->args_count;
+	memcpy(out->args, in->args, sizeof(out->args));
+}
+
+void of_ref_from_of_phandle_args(struct of_phandle_args *in,
+				 struct of_ref_phandle_args *out)
+{
+	assert(OF_MAX_PHANDLE_ARGS == MAX_PHANDLE_ARGS);
+	out->node_ref = of_node_to_ref(in->np);
+	out->args_count = in->args_count;
+	memcpy(out->args, in->args, sizeof(out->args));
+}
diff --git a/include/dm/of_ref.h b/include/dm/of_ref.h
new file mode 100644
index 00000000000..9e50283616c
--- /dev/null
+++ b/include/dm/of_ref.h
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2017 Google, Inc
+ * Written by Simon Glass <sjg at chromium.org>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _DM_OF_REF_H
+#define _DM_OF_REF_H
+
+#include <dm/of.h>
+
+/* Enable checks to protect against invalid calls */
+#define OF_CHECKS
+
+struct fdtdec_phandle_args;
+
+/**
+ * of_node_ref - reference to a device tree node
+ *
+ * This union can hold either a straightforward pointer to a struct device_node
+ * in the live device tree, or an offset within the flat device tree. In the
+ * latter case, the pointer points into the gd->fdt_blob area, with
+ * (fake_offset - gd->fdt_blob) being the integer offset within the flat DT.
+ *
+ * Thus we can reference nodes in both the live tree (once available) and the
+ * flat tree (until then). Functions are available to translate between an
+ * of_node_ref and either an offset or a struct device_node *.
+ *
+ * The reference can also hold a null offset, in which case the pointer value
+ * here is NULL. This corresponds to a struct device_node * value of NULL, or
+ * an offset of -1.
+ *
+ * For now these points use constant types, since we don't allow writing
+ * the DT.
+ */
+typedef union of_node_ref_un {
+	const struct device_node *node;
+	const void *fake_offset;
+} of_node_ref;
+
+struct of_ref_phandle_args {
+	of_node_ref node_ref;
+	int args_count;
+	uint32_t args[OF_MAX_PHANDLE_ARGS];
+};
+
+/**
+ * _of_ref_to_node() - convert a reference to a node pointer
+ *
+ * This is an internal function . Use of_ref_to_node() instead.
+ * This cannot be called if the reference contains an offset.
+ *
+ * @node_ref: Reference containing struct device_node * (possibly invalid)
+ * @return pointer to device node (can be NULL)
+ */
+const struct device_node *_of_ref_to_node(of_node_ref node_ref);
+
+/**
+ * _of_ref_to_node() - convert a reference to a live DT node pointer
+ *
+ * This cannot be called if the reference contains an offset.
+ *
+ * @node_ref: Reference containing struct device_node * (possibly invalid)
+ * @return pointer to device node (can be NULL)
+ */
+static inline const struct device_node *of_ref_to_node(of_node_ref node_ref)
+{
+#ifdef OF_CHECKS
+	if (!of_use_livetree())
+		return NULL;
+	return _of_ref_to_node(node_ref);
+#else
+	return node_ref.node;
+#endif
+}
+
+/**
+ * of_ref_to_offset() - convert a reference to a flat DT offset
+ *
+ * This cannot be called if the reference contains a node pointer.
+ *
+ * @node_ref: Reference containing offset (possibly invalid)
+ * @return DT offset (can be -1)
+ */
+static inline int of_ref_to_offset(of_node_ref node_ref)
+{
+#ifdef OF_CHECKS
+	if (of_use_livetree())
+		return -1;
+#endif
+	return node_ref.node ? (void *)node_ref.node - gd->fdt_blob : -1;
+}
+
+/**
+ * of_ref_valie() - check if a reference is valid
+ *
+ * @return true if the reference contains a valid node reference, false if it
+ * is NULL (or an offset of -1)
+ */
+static inline bool of_ref_valid(of_node_ref node_ref)
+{
+	return node_ref.node != NULL;
+}
+
+/**
+ * of_offset_to_ref() - convert a DT offset to a reference
+ *
+ * @of_offset: DT offset (either valid, or -1)
+ * @return reference to the associated DT offset
+ */
+static inline of_node_ref of_offset_to_ref(int of_offset)
+{
+	of_node_ref node_ref;
+
+	if (of_offset != -1)
+		node_ref.node = gd->fdt_blob + of_offset;
+	else
+		node_ref.node = NULL;
+
+	return node_ref;
+}
+
+/**
+ * of_node_to_ref() - convert a node pointer to a reference
+ *
+ * @np: Live node pointer (can be NULL)
+ * @return reference to the associated node pointer
+ */
+static inline of_node_ref of_node_to_ref(const struct device_node *np)
+{
+	of_node_ref node_ref;
+
+	node_ref.node = np;
+
+	return node_ref;
+}
+
+/**
+ * of_ref_is_node() - check if a reference is a node pointer
+ *
+ * This function associated that if there is a valid live tree then all
+ * references will use it. This is because using the flat DT when the live tree
+ * is valid is not permitted.
+ *
+ * @node_ref: reference to check (possibly invalid)
+ * @return true if the reference is a live node pointer, false if it is a DT
+ * offset
+ */
+static inline bool of_ref_is_node(of_node_ref node_ref)
+{
+#ifdef OF_CHECKS
+	/*
+	 * Check our assumption that flat tree offsets are not used when a
+	 * live tree is in use.
+	 */
+	assert(!of_ref_valid(node_ref) ||
+	       (of_use_livetree() ? _of_ref_to_node(node_ref)
+				  : _of_ref_to_node(node_ref)));
+#endif
+	return of_use_livetree();
+}
+
+/**
+ * of_ref_equal() - check if two references are equal
+ *
+ * @return true if equal, else false
+ */
+static inline bool of_ref_equal(of_node_ref ref1, of_node_ref ref2)
+{
+	/* We only need to compare the contents */
+	return ref1.fake_offset == ref2.fake_offset;
+}
+
+/**
+ * of_ref_read_u32() - Read a 32-bit integer from a property
+ *
+ * @ref:	valid node reference to read property from
+ * @propname:	name of the property to read from
+ * @outp:	place to put value (if found)
+ * @return 0 if OK, -ve on error
+ */
+int of_ref_read_u32(of_node_ref node, const char *propname, u32 *outp);
+
+/**
+ * of_ref_read_s32() - Read a 32-bit integer from a property
+ *
+ * @ref:	valid node reference to read property from
+ * @propname:	name of the property to read from
+ * @outp:	place to put value (if found)
+ * @return 0 if OK, -ve on error
+ */
+static inline int of_ref_read_s32(of_node_ref node_ref, const char *propname,
+				  s32 *out_value)
+{
+	return of_ref_read_u32(node_ref, propname, (u32 *)out_value);
+}
+
+/**
+ * of_ref_read_u32_default() - Read a 32-bit integer from a property
+ *
+ * @ref:	valid node reference to read property from
+ * @propname:	name of the property to read from
+ * @def:	default value to return if the property has no value
+ * @return property value, or @def if not found
+ */
+int of_ref_read_u32_default(of_node_ref ref, const char *propname, u32 def);
+
+/**
+ * of_ref_read_s32_default() - Read a 32-bit integer from a property
+ *
+ * @ref:	valid node reference to read property from
+ * @propname:	name of the property to read from
+ * @def:	default value to return if the property has no value
+ * @return property value, or @def if not found
+ */
+int of_ref_read_s32_default(of_node_ref node, const char *propname, s32 def);
+
+/**
+ * of_ref_read_string() - Read a string from a property
+ *
+ * @ref:	valid node reference to read property from
+ * @propname:	name of the property to read
+ * @return string from property value, or NULL if there is no such property
+ */
+const char *of_ref_read_string(of_node_ref node_ref, const char *propname);
+
+/**
+ * of_ref_read_u32_array - Find and read an array of 32 bit integers
+ *
+ * @node_ref:	valid node reference to read property from
+ * @propname:	name of the property to read
+ * @out_values:	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 32-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_values is modified only if a valid u32 value can be decoded.
+ */
+int of_ref_read_u32_array(of_node_ref node_ref, const char *propname,
+			  u32 *out_values, size_t sz);
+
+/**
+ * of_ref_read_bool() - read a boolean value from a property
+ *
+ * @node_ref:	valid node reference to read property from
+ * @propname:	name of property to read
+ * @return true if property is present (meaning true), false if not present
+ */
+bool of_ref_read_bool(of_node_ref node_ref, const char *propname);
+
+/**
+ * of_ref_find_subnode() - find a named subnode of a parent node
+ *
+ * @node_ref:	valid reference to parent node
+ * @subnode_name: name of subnode to find
+ * @return reference to subnode (which can be invalid if there is no such
+ * subnode)
+ */
+of_node_ref of_ref_find_subnode(of_node_ref node_ref, const char *subnode_name);
+
+/**
+ * of_ref_first_subnode() - find the first subnode of a parent node
+ *
+ * @node_ref:	valid reference to a valid parent node
+ * @return reference to the first subnode (which can be invalid if the parent
+ * node has no subnodes)
+ */
+of_node_ref of_ref_first_subnode(of_node_ref node_ref);
+
+/**
+ * of_ref_next_subnode() - find the next sibling of a subnode
+ *
+ * @node_ref:	valid reference to previous node (subling)
+ * @return reference to the next subnode (which can be invalid if the node
+ * has no more siblings)
+ */
+of_node_ref of_ref_next_subnode(of_node_ref node_ref);
+
+/**
+ * of_ref_get_name() - get the name of a node
+ *
+ * @node_ref: valid node to look up
+ * @return name or node
+ */
+const char *of_ref_get_name(of_node_ref node_ref);
+
+/**
+ * of_ref_from_fdtdec_phandle_args() - convert phandle arguments
+ *
+ * Converts phandle arguments in the legacy fdtdec format to the of_ref
+ * format.
+ *
+ * @in: structure to convert
+ * @out: place to put converted output structure
+ */
+void of_ref_from_fdtdec_phandle_args(struct fdtdec_phandle_args *in,
+				     struct of_ref_phandle_args *out);
+
+void of_ref_from_of_phandle_args(struct of_phandle_args *in,
+				 struct of_ref_phandle_args *out);
+
+#endif
-- 
2.11.0.483.g087da7b7c-goog



More information about the U-Boot mailing list