[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