
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@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@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@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