[U-Boot] [PATCH 05/22] dm: Add livetree access functions

Add a basic assortment of functions to access the live device tree.
Signed-off-by: Simon Glass sjg@chromium.org ---
include/dm/of_access.h | 164 +++++++++++++++++++++++++ lib/Makefile | 1 + lib/of_access.c | 328 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 493 insertions(+) create mode 100644 include/dm/of_access.h create mode 100644 lib/of_access.c
diff --git a/include/dm/of_access.h b/include/dm/of_access.h new file mode 100644 index 00000000000..9a157d8a5b7 --- /dev/null +++ b/include/dm/of_access.h @@ -0,0 +1,164 @@ +/* + * Originally from Linux v4.9 + * Copyright (C) 1996-2005 Paul Mackerras. + * + * Updates for PPC64 by Peter Bergner & David Engebretsen, IBM Corp. + * Updates for SPARC64 by David S. Miller + * Derived from PowerPC and Sparc prom.h files by Stephen Rothwell, IBM Corp. + * + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * Modified for U-Boot + * Copyright (c) 2017 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DM_OF_ACCESS_H +#define _DM_OF_ACCESS_H + +#include <dm/of.h> + +/** + * of_find_all_nodes - Get next node in global list + * @prev: Previous node or NULL to start iteration + * of_node_put() will be called on it + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_find_all_nodes(struct device_node *prev); + +#define for_each_of_allnodes_from(from, dn) \ + for (dn = __of_find_all_nodes(from); dn; dn = __of_find_all_nodes(dn)) +#define for_each_of_allnodes(dn) for_each_of_allnodes_from(NULL, dn) + +/* Dummy functions to mirror Linux. These are not yet used in U-Boot */ +static inline struct device_node *of_node_get(struct device_node *node) +{ + return node; +} + +static inline void of_node_put(struct device_node *node) { } + +/** + * of_find_property() - find a property in a node + * + * @np: Pointer to device node holding property + * @name: Name of property + * @lenp: If non-NULL, returns length of property + * @return pointer to property, or NULL if not found + */ +struct property *of_find_property(const struct device_node *np, + const char *name, int *lenp); + +/* + * Find a property with a given name for a given node + * and return the value. + */ +const void *of_get_property(const struct device_node *np, const char *name, + int *lenp); + +/** + * of_device_is_available - check if a device is available for use + * + * @device: Node to check for availability, with locks already held + * + * Returns true if the status property is absent or set to "okay" or "ok", + * false otherwise + */ +bool of_device_is_available(const struct device_node *device); + +/** + * of_find_node_by_phandle - Find a node given a phandle + * @handle: phandle of the node to find + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_find_node_by_phandle(phandle handle); + +/** + * of_parse_phandle - Resolve a phandle property to a device_node pointer + * @np: Pointer to device node holding phandle property + * @phandle_name: Name of property holding a phandle value + * @index: For properties holding a table of phandles, this is the index into + * the table + * + * Returns the device_node pointer with refcount incremented. Use + * of_node_put() on it when done. + */ +struct device_node *of_parse_phandle(const struct device_node *np, + const char *phandle_name, int index); + +/** + * of_parse_phandle_with_args() - Find a node pointed by phandle in a list + * @np: pointer to a device tree node containing a list + * @list_name: property name that contains a list + * @cells_name: property name that specifies phandles' arguments count + * @index: index of a phandle to parse out + * @out_args: optional pointer to output arguments structure (will be filled) + * + * This function is useful to parse lists of phandles and their arguments. + * Returns 0 on success and fills out_args, on error returns appropriate + * errno value. + * + * Caller is responsible to call of_node_put() on the returned out_args->np + * pointer. + * + * Example: + * + * phandle1: node1 { + * #list-cells = <2>; + * } + * + * phandle2: node2 { + * #list-cells = <1>; + * } + * + * node3 { + * list = <&phandle1 1 2 &phandle2 3>; + * } + * + * To get a device_node of the `node2' node you may call this: + * of_parse_phandle_with_args(node3, "list", "#list-cells", 1, &args); + */ +int of_parse_phandle_with_args(const struct device_node *np, + const char *list_name, const char *cells_name, + int index, struct of_phandle_args *out_args); + +/** + * of_read_u32() - Find and read a 32-bit integer from a property + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @outp: pointer to return value, modified only if return value is 0. + * + * Search for a property in a device node and read a 32-bit value 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_read_u32(const struct device_node *np, const char *propname, u32 *outp); + +/** + * of_read_u32_array() - Find and read an array of 32 bit integers + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @out_values: pointer to return value, modified only if return value is 0. + * @sz: number of array elements to read + * @return 0 on success, -EINVAL if the property does not exist, -ENODATA + * if property does not have a value, and -EOVERFLOW is longer than sz. + * + * Search for a property in a device node and read 32-bit value(s) from + * it. + * + * The out_values is modified only if a valid u32 value can be decoded. + */ +int of_read_u32_array(const struct device_node *np, const char *propname, + u32 *out_values, size_t sz); + +#endif diff --git a/lib/Makefile b/lib/Makefile index 23e9f1ef11d..d07a062483a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_ZLIB) += zlib/ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_TIZEN) += tizen/ obj-$(CONFIG_FIT) += libfdt/ +obj-$(CONFIG_OF_LIVE) += of_access.o obj-$(CONFIG_CMD_DHRYSTONE) += dhry/
obj-$(CONFIG_AES) += aes.o diff --git a/lib/of_access.c b/lib/of_access.c new file mode 100644 index 00000000000..e3ffb21dc8a --- /dev/null +++ b/lib/of_access.c @@ -0,0 +1,328 @@ +/* + * Originally from Linux v4.9 + * Paul Mackerras August 1996. + * Copyright (C) 1996-2005 Paul Mackerras. + * + * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. + * {engebret|bergner}@us.ibm.com + * + * Adapted for sparc and sparc64 by David S. Miller davem@davemloft.net + * + * Reconsolidated from arch/x/kernel/prom.c by Stephen Rothwell and + * Grant Likely. + * + * Modified for U-Boot + * Copyright (c) 2017 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <libfdt.h> +#include <dm/of_access.h> +#include <linux/err.h> + +DECLARE_GLOBAL_DATA_PTR; + +static inline int np_to_offset(const struct device_node *np) +{ + return (ulong)np - (ulong)gd->fdt_blob; +} + +struct property *of_find_property(const struct device_node *np, + const char *name, int *lenp) +{ + struct property *pp; + + if (!np) + return NULL; + + for (pp = np->properties; pp; pp = pp->next) { + if (strcmp(pp->name, name) == 0) { + if (lenp) + *lenp = pp->length; + break; + } + } + + return pp; +} + +struct device_node *__of_find_all_nodes(struct device_node *prev) +{ + struct device_node *np; + if (!prev) { + np = gd->of_root; + } else if (prev->child) { + np = prev->child; + } else { + /* + * Walk back up looking for a sibling, or the end of the + * structure + */ + np = prev; + while (np->parent && !np->sibling) + np = np->parent; + np = np->sibling; /* Might be null at the end of the tree */ + } + return np; +} + +struct device_node *of_find_all_nodes(struct device_node *prev) +{ + struct device_node *np; + + np = __of_find_all_nodes(prev); + of_node_get(np); + of_node_put(prev); + return np; +} + +/** + * of_find_property_value_of_size + * + * Search for a property in a device node and valid the requested size. + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @len: requested length of property value + * + * @returns the property value 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. + */ +static void *of_find_property_value_of_size(const struct device_node *np, + const char *propname, u32 len) +{ + struct property *prop = of_find_property(np, propname, NULL); + + if (!prop) + return ERR_PTR(-EINVAL); + if (!prop->value) + return ERR_PTR(-ENODATA); + if (len > prop->length) + return ERR_PTR(-EOVERFLOW); + + return prop->value; +} + +const void *of_get_property(const struct device_node *np, const char *name, + int *lenp) +{ + struct property *pp = of_find_property(np, name, lenp); + + return pp ? pp->value : NULL; +} + +bool of_device_is_available(const struct device_node *device) +{ + const char *status; + int statlen; + + if (!device) + return false; + + status = of_get_property(device, "status", &statlen); + if (status == NULL) + return true; + + if (statlen > 0) { + if (!strcmp(status, "okay") || !strcmp(status, "ok")) + return true; + } + + return false; +} + +struct device_node *of_find_node_by_phandle(phandle handle) +{ + struct device_node *np; + + if (!handle) + return NULL; + + for_each_of_allnodes(np) + if (np->phandle == handle) + break; + of_node_get(np); + return np; +} + +int of_read_u32(const struct device_node *np, const char *propname, u32 *outp) +{ + const __be32 *val; + + debug("%s: %s: ", __func__, propname); + if (!np) + return -EINVAL; + val = of_find_property_value_of_size(np, propname, sizeof(*outp)); + if (IS_ERR(val)) { + debug("(not found)\n"); + return PTR_ERR(val); + } + + *outp = be32_to_cpup(val); + debug("%#x (%d)\n", *outp, *outp); + + return 0; +} + +static int __of_parse_phandle_with_args(const struct device_node *np, + const char *list_name, + const char *cells_name, + int cell_count, int index, + struct of_phandle_args *out_args) +{ + const __be32 *list, *list_end; + int rc = 0, size, cur_index = 0; + uint32_t count = 0; + struct device_node *node = NULL; + phandle phandle; + + /* Retrieve the phandle list property */ + list = of_get_property(np, list_name, &size); + if (!list) + return -ENOENT; + list_end = list + size / sizeof(*list); + + /* Loop over the phandles until all the requested entry is found */ + while (list < list_end) { + rc = -EINVAL; + count = 0; + + /* + * If phandle is 0, then it is an empty entry with no + * arguments. Skip forward to the next entry. + */ + phandle = be32_to_cpup(list++); + if (phandle) { + /* + * Find the provider node and parse the #*-cells + * property to determine the argument length. + * + * This is not needed if the cell count is hard-coded + * (i.e. cells_name not set, but cell_count is set), + * except when we're going to return the found node + * below. + */ + if (cells_name || cur_index == index) { + node = of_find_node_by_phandle(phandle); + if (!node) { + debug("%s: could not find phandle\n", + np->full_name); + goto err; + } + } + + if (cells_name) { + if (of_read_u32(node, cells_name, &count)) { + debug("%s: could not get %s for %s\n", + np->full_name, cells_name, + node->full_name); + goto err; + } + } else { + count = cell_count; + } + + /* + * Make sure that the arguments actually fit in the + * remaining property data length + */ + if (list + count > list_end) { + debug("%s: arguments longer than property\n", + np->full_name); + goto err; + } + } + + /* + * All of the error cases above bail out of the loop, so at + * this point, the parsing is successful. If the requested + * index matches, then fill the out_args structure and return, + * or return -ENOENT for an empty entry. + */ + rc = -ENOENT; + if (cur_index == index) { + if (!phandle) + goto err; + + if (out_args) { + int i; + if (WARN_ON(count > OF_MAX_PHANDLE_ARGS)) + count = OF_MAX_PHANDLE_ARGS; + out_args->np = node; + out_args->args_count = count; + for (i = 0; i < count; i++) + out_args->args[i] = + be32_to_cpup(list++); + } else { + of_node_put(node); + } + + /* Found it! return success */ + return 0; + } + + of_node_put(node); + node = NULL; + list += count; + cur_index++; + } + + /* + * Unlock node before returning result; will be one of: + * -ENOENT : index is for empty phandle + * -EINVAL : parsing error on data + * [1..n] : Number of phandle (count mode; when index = -1) + */ + rc = index < 0 ? cur_index : -ENOENT; + err: + if (node) + of_node_put(node); + return rc; +} + +struct device_node *of_parse_phandle(const struct device_node *np, + const char *phandle_name, int index) +{ + struct of_phandle_args args; + + if (index < 0) + return NULL; + + if (__of_parse_phandle_with_args(np, phandle_name, NULL, 0, + index, &args)) + return NULL; + + return args.np; +} + +int of_parse_phandle_with_args(const struct device_node *np, + const char *list_name, const char *cells_name, + int index, struct of_phandle_args *out_args) +{ + if (index < 0) + return -EINVAL; + return __of_parse_phandle_with_args(np, list_name, cells_name, 0, + index, out_args); +} + +int of_read_u32_array(const struct device_node *np, const char *propname, + u32 *out_values, size_t sz) +{ + const __be32 *val; + + debug("%s: %s: ", __func__, propname); + val = of_find_property_value_of_size(np, propname, + sz * sizeof(*out_values)); + + if (IS_ERR(val)) + return PTR_ERR(val); + + debug("size %zd\n", sz); + while (sz--) + *out_values++ = be32_to_cpup(val++); + + return 0; +}

This function converts the flat device tree into a hierarchical one with C structures and pointers. This is easier to access.
Signed-off-by: Simon Glass sjg@chromium.org ---
include/livetree.h | 22 ++++ lib/Makefile | 2 +- lib/livetree.c | 320 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 include/livetree.h create mode 100644 lib/livetree.c
diff --git a/include/livetree.h b/include/livetree.h new file mode 100644 index 00000000000..aa0a8687c1b --- /dev/null +++ b/include/livetree.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Support for a 'live' (as opposed to flat) device tree + */ + +#ifndef _LIVETREE_H +#define _LIVETREE_H + +/** + * livetree_build() - build a live (hierarchical) tree from a flat DT + * + * @fdt_blob: Input tree to convert + * @rootp: Returns live tree that was created + * @return 0 if OK, -ve on error + */ +int livetree_build(const void *fdt_blob, struct device_node **rootp); + +#endif diff --git a/lib/Makefile b/lib/Makefile index d07a062483a..1c76c59374d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_ZLIB) += zlib/ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_TIZEN) += tizen/ obj-$(CONFIG_FIT) += libfdt/ -obj-$(CONFIG_OF_LIVE) += of_access.o +obj-$(CONFIG_OF_LIVE) += livetree.o of_access.o obj-$(CONFIG_CMD_DHRYSTONE) += dhry/
obj-$(CONFIG_AES) += aes.o diff --git a/lib/livetree.c b/lib/livetree.c new file mode 100644 index 00000000000..a1dd3320940 --- /dev/null +++ b/lib/livetree.c @@ -0,0 +1,320 @@ +/* + * Copyright 2009 Benjamin Herrenschmidt, IBM Corp + * benh@kernel.crashing.org + * + * Based on parts of drivers/of/fdt.c from Linux v4.9 + * Modifications for U-Boot + * Copyright (c) 2017 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <livetree.h> +#include <malloc.h> +#include <dm/of_access.h> +#include <linux/err.h> + +DECLARE_GLOBAL_DATA_PTR; + +static void *unflatten_dt_alloc(void **mem, unsigned long size, + unsigned long align) +{ + void *res; + + *mem = PTR_ALIGN(*mem, align); + res = *mem; + *mem += size; + + return res; +} + +/** + * unflatten_dt_node() - Alloc and populate a device_node from the flat tree + * @blob: The parent device tree blob + * @mem: Memory chunk to use for allocating device nodes and properties + * @poffset: pointer to node in flat tree + * @dad: Parent struct device_node + * @nodepp: The device_node tree created by the call + * @fpsize: Size of the node path up at t05he current depth. + * @dryrun: If true, do not allocate device nodes but still calculate needed + * memory size + */ +static void *unflatten_dt_node(const void *blob, void *mem, int *poffset, + struct device_node *dad, + struct device_node **nodepp, + unsigned long fpsize, bool dryrun) +{ + const __be32 *p; + struct device_node *np; + struct property *pp, **prev_pp = NULL; + const char *pathp; + int l; + unsigned int allocl; + static int depth; + int old_depth; + int offset; + int has_name = 0; + int new_format = 0; + + pathp = fdt_get_name(blob, *poffset, &l); + if (!pathp) + return mem; + + allocl = ++l; + + /* + * version 0x10 has a more compact unit name here instead of the full + * path. we accumulate the full path size using "fpsize", we'll rebuild + * it later. We detect this because the first character of the name is + * not '/'. + */ + if ((*pathp) != '/') { + new_format = 1; + if (fpsize == 0) { + /* + * root node: special case. fpsize accounts for path + * plus terminating zero. root node only has '/', so + * fpsize should be 2, but we want to avoid the first + * level nodes to have two '/' so we use fpsize 1 here + */ + fpsize = 1; + allocl = 2; + l = 1; + pathp = ""; + } else { + /* + * account for '/' and path size minus terminal 0 + * already in 'l' + */ + fpsize += l; + allocl = fpsize; + } + } + + np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl, + __alignof__(struct device_node)); + if (!dryrun) { + char *fn; + + fn = (char *)np + sizeof(*np); + np->full_name = fn; + if (new_format) { + /* rebuild full path for new format */ + if (dad && dad->parent) { + strcpy(fn, dad->full_name); +#ifdef DEBUG + if ((strlen(fn) + l + 1) != allocl) { + debug("%s: p: %d, l: %d, a: %d\n", + pathp, (int)strlen(fn), l, + allocl); + } +#endif + fn += strlen(fn); + } + *(fn++) = '/'; + } + memcpy(fn, pathp, l); + + prev_pp = &np->properties; + if (dad != NULL) { + np->parent = dad; + np->sibling = dad->child; + dad->child = np; + } + } + /* process properties */ + for (offset = fdt_first_property_offset(blob, *poffset); + (offset >= 0); + (offset = fdt_next_property_offset(blob, offset))) { + const char *pname; + int sz; + + p = fdt_getprop_by_offset(blob, offset, &pname, &sz); + if (!p) { + offset = -FDT_ERR_INTERNAL; + break; + } + + if (pname == NULL) { + debug("Can't find property name in list !\n"); + break; + } + if (strcmp(pname, "name") == 0) + has_name = 1; + pp = unflatten_dt_alloc(&mem, sizeof(struct property), + __alignof__(struct property)); + if (!dryrun) { + /* + * 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. */ + if ((strcmp(pname, "phandle") == 0) || + (strcmp(pname, "linux,phandle") == 0)) { + if (np->phandle == 0) + np->phandle = be32_to_cpup(p); + } + /* + * And we process the "ibm,phandle" property + * used in pSeries dynamic device tree + * stuff */ + if (strcmp(pname, "ibm,phandle") == 0) + np->phandle = be32_to_cpup(p); + pp->name = (char *)pname; + pp->length = sz; + pp->value = (__be32 *)p; + *prev_pp = pp; + prev_pp = &pp->next; + } + } + /* + * with version 0x10 we may not have the name property, recreate + * it here from the unit name if absent + */ + if (!has_name) { + const char *p1 = pathp, *ps = pathp, *pa = NULL; + int sz; + + while (*p1) { + if ((*p1) == '@') + pa = p1; + if ((*p1) == '/') + ps = p1 + 1; + p1++; + } + if (pa < ps) + pa = p1; + sz = (pa - ps) + 1; + pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz, + __alignof__(struct property)); + if (!dryrun) { + pp->name = "name"; + pp->length = sz; + pp->value = pp + 1; + *prev_pp = pp; + prev_pp = &pp->next; + memcpy(pp->value, ps, sz - 1); + ((char *)pp->value)[sz - 1] = 0; + debug("fixed up name for %s -> %s\n", pathp, + (char *)pp->value); + } + } + if (!dryrun) { + *prev_pp = NULL; + np->name = of_get_property(np, "name", NULL); + + if (!np->name) + np->name = "<NULL>"; + } + + old_depth = depth; + *poffset = fdt_next_node(blob, *poffset, &depth); + if (depth < 0) + depth = 0; + while (*poffset > 0 && depth > old_depth) + mem = unflatten_dt_node(blob, mem, poffset, np, NULL, + fpsize, dryrun); + + if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND) + printf("unflatten: error %d processing FDT\n", *poffset); + + /* + * Reverse the child list. Some drivers assumes node order matches .dts + * node order + */ + if (!dryrun && np->child) { + struct device_node *child = np->child; + np->child = NULL; + while (child) { + struct device_node *next = child->sibling; + + child->sibling = np->child; + np->child = child; + child = next; + } + } + + if (nodepp) + *nodepp = np; + + return mem; +} + +/** + * unflatten_device_tree() - create tree of device_nodes from flat blob + * + * unflattens a device-tree, creating the + * tree of struct device_node. It also fills the "name" and "type" + * pointers of the nodes so the normal device-tree walking functions + * can be used. + * @blob: The blob to expand + * @mynodes: The device_node tree created by the call + * @return 0 if OK, -ve on error + */ +static int unflatten_device_tree(const void *blob, + struct device_node **mynodes) +{ + unsigned long size; + int start; + void *mem; + + debug(" -> unflatten_device_tree()\n"); + + if (!blob) { + debug("No device tree pointer\n"); + return -EINVAL; + } + + debug("Unflattening device tree:\n"); + debug("magic: %08x\n", fdt_magic(blob)); + debug("size: %08x\n", fdt_totalsize(blob)); + debug("version: %08x\n", fdt_version(blob)); + + if (fdt_check_header(blob)) { + printf("Invalid device tree blob header\n"); + return -EINVAL; + } + + /* First pass, scan for size */ + start = 0; + size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, + 0, true); + size = ALIGN(size, 4); + + debug(" size is %lx, allocating...\n", size); + + /* Allocate memory for the expanded device tree */ + mem = malloc(size + 4); + memset(mem, '\0', size); + + *(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef); + + debug(" unflattening %p...\n", mem); + + /* Second pass, do actual unflattening */ + start = 0; + unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false); + if (be32_to_cpup(mem + size) != 0xdeadbeef) { + printf("End of tree marker overwritten: %08x\n", + be32_to_cpup(mem + size)); + } + + debug(" <- unflatten_device_tree()\n"); + + return 0; +} + +int livetree_build(const void *fdt_blob, struct device_node **rootp) +{ + int ret; + + debug("%s: start\n", __func__); + ret = unflatten_device_tree(fdt_blob, rootp); + if (ret) + debug("Failed to create livetree: err=%d\n", ret); + debug("%s: stop\n", __func__); + + return ret; +}

If enabled, build a live device tree after relocation. This can then be used by driver model.
Signed-off-by: Simon Glass sjg@chromium.org ---
common/board_r.c | 12 ++++++++++++ include/livetree.h | 2 ++ 2 files changed, 14 insertions(+)
diff --git a/common/board_r.c b/common/board_r.c index a3733526c69..3c865720036 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -33,6 +33,7 @@ #if defined(CONFIG_CMD_KGDB) #include <kgdb.h> #endif +#include <livetree.h> #include <logbuff.h> #include <malloc.h> #include <mapmem.h> @@ -314,6 +315,14 @@ static int initr_noncached(void) } #endif
+#ifdef CONFIG_OF_LIVE +static int initr_livetree(void) +{ + return livetree_build(gd->fdt_blob, + (struct device_node **)&gd->of_root); +} +#endif + #ifdef CONFIG_DM static int initr_dm(void) { @@ -781,6 +790,9 @@ init_fnc_t init_sequence_r[] = { initr_noncached, #endif bootstage_relocate, +#ifdef CONFIG_OF_LIVE + initr_livetree, +#endif #ifdef CONFIG_DM initr_dm, #endif diff --git a/include/livetree.h b/include/livetree.h index aa0a8687c1b..8f8fee73114 100644 --- a/include/livetree.h +++ b/include/livetree.h @@ -10,6 +10,8 @@ #ifndef _LIVETREE_H #define _LIVETREE_H
+struct device_node; + /** * livetree_build() - build a live (hierarchical) tree from a flat DT *

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

Some functions deal with structured data rather than simple data types. It makes sense to have these in their own file. For now this just has a function to read a flashmap entry.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/core/Makefile | 2 +- drivers/core/of_extra.c | 37 +++++++++++++++++++++++++++++++++++++ include/dm/of_extra.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 drivers/core/of_extra.c create mode 100644 include/dm/of_extra.h
diff --git a/drivers/core/Makefile b/drivers/core/Makefile index 0ffc38601f4..6d7040c2603 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -11,4 +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 +obj-$(CONFIG_OF_CONTROL) += of_extra.o of_ref.o diff --git a/drivers/core/of_extra.c b/drivers/core/of_extra.c new file mode 100644 index 00000000000..76b2bd93b9f --- /dev/null +++ b/drivers/core/of_extra.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <libfdt.h> +#include <dm/of_access.h> +#include <dm/of_extra.h> +#include <dm/of_ref.h> + +int of_read_fmap_entry(of_node_ref node_ref, const char *name, + struct fmap_entry *entry) +{ + const char *prop; + u32 reg[2]; + + if (of_ref_read_u32_array(node_ref, "reg", reg, 2)) { + debug("Node '%s' has bad/missing 'reg' property\n", name); + return -FDT_ERR_NOTFOUND; + } + entry->offset = reg[0]; + entry->length = reg[1]; + entry->used = of_ref_read_s32_default(node_ref, "used", entry->length); + prop = of_ref_read_string(node_ref, "compress"); + entry->compress_algo = prop && !strcmp(prop, "lzo") ? + FMAP_COMPRESS_LZO : FMAP_COMPRESS_NONE; + prop = of_ref_read_string(node_ref, "hash"); + if (prop) + entry->hash_size = strlen(prop); + entry->hash_algo = prop ? FMAP_HASH_SHA256 : FMAP_HASH_NONE; + entry->hash = (uint8_t *)prop; + + return 0; +} diff --git a/include/dm/of_extra.h b/include/dm/of_extra.h new file mode 100644 index 00000000000..2e30e4dde1d --- /dev/null +++ b/include/dm/of_extra.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DM_OF_EXTRA_H +#define _DM_OF_EXTRA_H + +#include <dm/of_ref.h> + +enum fmap_compress_t { + FMAP_COMPRESS_NONE, + FMAP_COMPRESS_LZO, +}; + +enum fmap_hash_t { + FMAP_HASH_NONE, + FMAP_HASH_SHA1, + FMAP_HASH_SHA256, +}; + +/* A flash map entry, containing an offset and length */ +struct fmap_entry { + uint32_t offset; + uint32_t length; + uint32_t used; /* Number of bytes used in region */ + enum fmap_compress_t compress_algo; /* Compression type */ + enum fmap_hash_t hash_algo; /* Hash algorithm */ + const uint8_t *hash; /* Hash value */ + int hash_size; /* Hash size */ +}; + +/** + * Read a flash entry from the fdt + * + * @param node_ref Reference to node to read + * @param name Name of node being read + * @param entry Place to put offset and size of this node + * @return 0 if ok, -ve on error + */ +int of_read_fmap_entry(of_node_ref node_ref, const char *name, + struct fmap_entry *entry); + +#endif

It is common to read a device-tree property from the node associated with a device. Add convenience functions to do this so that drivers do not need to deal with the difference between live tree and flat tree access methods.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/core/Makefile | 2 +- drivers/core/of_dev.c | 55 +++++++++++++++++++++++++++++++++++++++++ include/dm/of_dev.h | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 drivers/core/of_dev.c create mode 100644 include/dm/of_dev.h
diff --git a/drivers/core/Makefile b/drivers/core/Makefile index 6d7040c2603..be1e9da55c0 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -11,4 +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_extra.o of_ref.o +obj-$(CONFIG_OF_CONTROL) += of_dev.o of_extra.o of_ref.o diff --git a/drivers/core/of_dev.c b/drivers/core/of_dev.c new file mode 100644 index 00000000000..0474ef1408b --- /dev/null +++ b/drivers/core/of_dev.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <dm/of_dev.h> +#include <dm/of_ref.h> + +/** + * dev_of_node_ref() - get the DT node reference associated with a udevice + * + * TODO(sjg@chromium.org): Once dev->of_offset is dropped, we can simplify + * this function. For now it has to look at both of_node and of_offset. + * + * @dev: device to check + * @return reference of the the device's DT node + */ +of_node_ref dev_of_node_ref(struct udevice *dev) +{ +#ifdef CONFIG_OF_LIVE + return dev->node_ref; +#else + return of_offset_to_ref(dev->of_offset); +#endif +} + +int dev_read_u32_defaut(struct udevice *dev, const char *propname, int def) +{ + u32 val; + + if (of_ref_read_u32(dev_of_node_ref(dev), propname, &val)) + return def; + + return val; +} + +const char *dev_read_string(struct udevice *dev, const char *propname) +{ + return of_ref_read_string(dev_of_node_ref(dev), propname); +} + +bool dev_read_bool(struct udevice *dev, const char *propname) +{ + return of_ref_read_bool(dev_of_node_ref(dev), propname); +} + +of_node_ref dev_find_subnode(struct udevice *dev, + const char *subnode_name) +{ + return of_ref_find_subnode(dev_of_node_ref(dev), subnode_name); +} diff --git a/include/dm/of_dev.h b/include/dm/of_dev.h new file mode 100644 index 00000000000..e7962088106 --- /dev/null +++ b/include/dm/of_dev.h @@ -0,0 +1,68 @@ +/* + * Function to read values from the device tree node attached to a udevice. + * + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DM_OF_DEV_H +#define _DM_OF_DEV_H + +#include <dm/of_ref.h> + +#ifdef CONFIG_OF_LIVE +static inline const struct device_node *dev_of_node(struct udevice *dev) +{ + return of_ref_to_node(dev->node_ref); +} +#else +static inline const struct device_node *dev_of_node(struct udevice *dev) +{ + return NULL; +} +#endif + +of_node_ref dev_of_node_ref(struct udevice *dev); + +/** + * dev_read_u32_defaut() - read a 32-bit integer from a device's DT property + * + * @dev: device to read DT 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 dev_read_u32_defaut(struct udevice *dev, const char *propname, int def); + +/** + * dev_read_string() - Read a string from a device's DT property + * + * @dev: device to read DT property from + * @propname: name of the property to read + * @return string from property value, or NULL if there is no such property + */ +const char *dev_read_string(struct udevice *dev, const char *propname); + +/** + * dev_read_bool() - read a boolean value from a device's DT property + * + * @dev: device to read DT property from + * @propname: name of property to read + * @return true if property is present (meaning true), false if not present + */ +bool dev_read_bool(struct udevice *dev, const char *propname); + +/** + * dev_find_subnode() - find a named subnode of a device + * + * @dev: device whose DT node contains the subnode + * @subnode_name: name of subnode to find + * @return reference to subnode (which can be invalid if there is no such + * subnode) + */ +of_node_ref dev_find_subnode(struct udevice *dev, + const char *subbnode_name); + +#endif

When a live tree is being used we need to record the node that was used to create the device. Add a function to support this, and a new field to the udevice structure.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/core/device.c | 27 +++++++++++++++++++++++---- include/dm/device-internal.h | 6 ++++++ include/dm/device.h | 19 ++++++++++++++++++- 3 files changed, 47 insertions(+), 5 deletions(-)
diff --git a/drivers/core/device.c b/drivers/core/device.c index 70fcfc23e0c..52b8715a4ae 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -30,6 +30,7 @@ DECLARE_GLOBAL_DATA_PTR; static int device_bind_common(struct udevice *parent, const struct driver *drv, const char *name, void *platdata, ulong driver_data, int of_offset, + struct device_node *of_node, uint of_platdata_size, struct udevice **devp) { struct udevice *dev; @@ -60,7 +61,14 @@ static int device_bind_common(struct udevice *parent, const struct driver *drv, dev->platdata = platdata; dev->driver_data = driver_data; dev->name = name; +#ifdef CONFIG_OF_LIVE + if (of_use_livetree()) + dev->node_ref = of_node_to_ref(of_node); + else + dev->node_ref = of_offset_to_ref(of_offset); +#else dev->of_offset = of_offset; +#endif dev->parent = parent; dev->driver = drv; dev->uclass = uc; @@ -219,15 +227,25 @@ int device_bind_with_driver_data(struct udevice *parent, struct udevice **devp) { return device_bind_common(parent, drv, name, NULL, driver_data, - of_offset, 0, devp); + of_offset, NULL, 0, devp); +} + +int device_bind_node_with_driver_data(struct udevice *parent, + const struct driver *drv, + const char *name, ulong driver_data, + struct device_node *np, + struct udevice **devp) +{ + return device_bind_common(parent, drv, name, NULL, driver_data, + -1, np, 0, devp); }
int device_bind(struct udevice *parent, const struct driver *drv, const char *name, void *platdata, int of_offset, struct udevice **devp) { - return device_bind_common(parent, drv, name, platdata, 0, of_offset, 0, - devp); + return device_bind_common(parent, drv, name, platdata, 0, of_offset, + NULL, 0, devp); }
int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, @@ -246,7 +264,8 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, platdata_size = info->platdata_size; #endif return device_bind_common(parent, drv, info->name, - (void *)info->platdata, 0, -1, platdata_size, devp); + (void *)info->platdata, 0, -1, NULL, + platdata_size, devp); }
static void *alloc_priv(int size, uint flags) diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h index 0bf8707493a..5bc80e64aae 100644 --- a/include/dm/device-internal.h +++ b/include/dm/device-internal.h @@ -11,6 +11,7 @@ #ifndef _DM_DEVICE_INTERNAL_H #define _DM_DEVICE_INTERNAL_H
+struct device_node; struct udevice;
/** @@ -62,6 +63,11 @@ int device_bind_with_driver_data(struct udevice *parent, ulong driver_data, int of_offset, struct udevice **devp);
+int device_bind_node_with_driver_data(struct udevice *parent, + const struct driver *drv, + const char *name, ulong driver_data, + struct device_node *np, + struct udevice **devp); /** * device_bind_by_name: Create a device and bind it to a driver * diff --git a/include/dm/device.h b/include/dm/device.h index 4e95fb7773d..94248eccee7 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -18,6 +18,10 @@ #include <linux/kernel.h> #include <linux/list.h>
+#ifdef CONFIG_OF_LIVE +#include <dm/of_ref.h> +#endif + struct driver_info;
/* Driver is active (probed). Cleared when it is removed */ @@ -68,7 +72,8 @@ struct driver_info; * @platdata: Configuration data for this device * @parent_platdata: The parent bus's configuration data for this device * @uclass_platdata: The uclass's configuration data for this device - * @of_offset: Device tree node offset for this device (- for none) + * @of_noderef: Reference to device tree node for this devide) + * @of_offset: Device tree node offset for this device (-1 for none) * @driver_data: Driver data word for the entry that matched this device with * its driver * @parent: Parent of this device, or NULL for the top level device @@ -94,7 +99,11 @@ struct udevice { void *platdata; void *parent_platdata; void *uclass_platdata; +#ifdef CONFIG_OF_LIVE + of_node_ref node_ref; +#else int of_offset; +#endif ulong driver_data; struct udevice *parent; void *priv; @@ -123,12 +132,20 @@ struct udevice {
static inline int dev_of_offset(const struct udevice *dev) { +#ifdef CONFIG_OF_LIVE + return of_ref_to_offset(dev->node_ref); +#else return dev->of_offset; +#endif }
static inline void dev_set_of_offset(struct udevice *dev, int of_offset) { +#ifdef CONFIG_OF_LIVE + dev->node_ref = of_offset_to_ref(of_offset); +#else dev->of_offset = of_offset; +#endif }
/**

We already have a means to locate drivers for flat device tree nodes. Add a separate method for the live tree.
TODO: Refactor this to avoid code duplication.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/core/lists.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/dm/lists.h | 5 ++++ 2 files changed, 81 insertions(+)
diff --git a/drivers/core/lists.c b/drivers/core/lists.c index 72c55e205f9..b9bfbf5cc86 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -12,6 +12,7 @@ #include <dm/device.h> #include <dm/device-internal.h> #include <dm/lists.h> +#include <dm/of_access.h> #include <dm/platdata.h> #include <dm/uclass.h> #include <dm/util.h> @@ -199,4 +200,79 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
return result; } + +#ifdef CONFIG_OF_LIVE +/* + * TODO(sjg@chromium.org): Perhaps we can use np_to_offset(np)() to merge this + * function with lists_bind_fdt() + */ +int lists_bind_node(struct udevice *parent, struct device_node *np, + struct udevice **devp) +{ + struct driver *driver = ll_entry_start(struct driver, driver); + const int n_ents = ll_entry_count(struct driver, driver); + const struct udevice_id *id; + struct driver *entry; + struct udevice *dev; + bool found = false; + const struct property *compat_prop; + const char *compat; + int compat_length, i; + int result = 0; + int ret = 0; + + if (devp) + *devp = NULL; + dm_dbg("bind node %s\n", np->name); + + compat_prop = of_find_property(np, "compatible", &compat_length); + if (!compat_prop) { + dm_dbg("Device '%s' has no compatible string\n", np->name); + return 0; + } + + /* + * Walk through the compatible string list, attempting to match each + * compatible string in order such that we match in order of priority + * from the first string to the last. + */ + for (i = 0; i < compat_prop->length; i += strlen(compat) + 1) { + compat = compat_prop->value + i; + dm_dbg(" - attempt to match compatible string '%s'\n", + compat); + + for (entry = driver; entry != driver + n_ents; entry++) { + ret = driver_check_compatible(entry->of_match, &id, + compat); + if (!ret) + break; + } + if (entry == driver + n_ents) + continue; + + dm_dbg(" - found match at '%s'\n", entry->name); + ret = device_bind_node_with_driver_data(parent, entry, np->name, + id->data, np, &dev); + if (ret == -ENODEV) { + dm_dbg("Driver '%s' refuses to bind\n", entry->name); + continue; + } + if (ret) { + dm_warn("Error binding driver '%s': %d\n", entry->name, + ret); + return ret; + } else { + found = true; + if (devp) + *devp = dev; + } + break; + } + + if (!found && !result && ret != -ENODEV) + dm_dbg("No match for node '%s'\n", np->name); + + return result; +} +#endif /* CONFIG_OF_LIVE */ #endif diff --git a/include/dm/lists.h b/include/dm/lists.h index 4513d6a311a..2ca7079bc86 100644 --- a/include/dm/lists.h +++ b/include/dm/lists.h @@ -12,6 +12,8 @@
#include <dm/uclass-id.h>
+struct device_node; + /** * lists_driver_lookup_name() - Return u_boot_driver corresponding to name * @@ -60,6 +62,9 @@ int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only); int lists_bind_fdt(struct udevice *parent, const void *blob, int offset, struct udevice **devp);
+int lists_bind_node(struct udevice *parent, struct device_node *np, + struct udevice **devp); + /** * device_bind_driver() - bind a device to a driver *

When starting up driver model with a live tree we need to scan the tree for devices. Add code to handle this.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/core/root.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+)
diff --git a/drivers/core/root.c b/drivers/core/root.c index cd09c55d9d2..b15376a9859 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -12,9 +12,13 @@ #include <fdtdec.h> #include <malloc.h> #include <libfdt.h> +#include <livetree.h> #include <dm/device.h> #include <dm/device-internal.h> #include <dm/lists.h> +#include <dm/of.h> +#include <dm/of_access.h> +#include <dm/of_ref.h> #include <dm/platdata.h> #include <dm/root.h> #include <dm/uclass.h> @@ -165,7 +169,11 @@ int dm_init(void) if (ret) return ret; #if CONFIG_IS_ENABLED(OF_CONTROL) +# if defined(CONFIG_OF_LIVE) + DM_ROOT_NON_CONST->node_ref = of_node_to_ref(gd->of_root); +# else DM_ROOT_NON_CONST->of_offset = 0; +# endif #endif ret = device_probe(DM_ROOT_NON_CONST); if (ret) @@ -195,6 +203,36 @@ int dm_scan_platdata(bool pre_reloc_only) return ret; }
+#ifdef CONFIG_OF_LIVE +static int dm_scan_fdt_live(struct udevice *parent, + struct device_node *node_parent, + bool pre_reloc_only) +{ + struct device_node *np; + int ret = 0, err; + + for (np = node_parent->child; np; np = np->sibling) { + if (pre_reloc_only && + !of_find_property(np, "u-boot,dm-pre-reloc", NULL)) + continue; + if (!of_device_is_available(np)) { + dm_dbg(" - ignoring disabled device\n"); + continue; + } + err = lists_bind_node(parent, np, NULL); + if (err && !ret) { + ret = err; + debug("%s: ret=%d\n", np->name, ret); + } + } + + if (ret) + dm_warn("Some drivers failed to bind\n"); + + return ret; +} +#endif /* CONFIG_OF_LIVE */ + #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) /** * dm_scan_fdt_node() - Scan the device tree and bind drivers for a node @@ -249,6 +287,12 @@ int dm_scan_fdt_dev(struct udevice *dev)
int dm_scan_fdt(const void *blob, bool pre_reloc_only) { +#ifdef CONFIG_OF_LIVE + if (of_use_livetree()) + return dm_scan_fdt_live(gd->dm_root, gd->of_root, + pre_reloc_only); + else +#endif return dm_scan_fdt_node(gd->dm_root, blob, 0, pre_reloc_only); } #endif

Add function which looks up a device by its device tree node in the live tree.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/core/uclass.c | 42 ++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-internal.h | 17 +++++++++++++++++ include/dm/uclass.h | 19 +++++++++++++++++++ 3 files changed, 78 insertions(+)
diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 7de370644d7..f7430f00789 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -13,6 +13,7 @@ #include <dm/device.h> #include <dm/device-internal.h> #include <dm/lists.h> +#include <dm/of_ref.h> #include <dm/uclass.h> #include <dm/uclass-internal.h> #include <dm/util.h> @@ -287,6 +288,33 @@ int uclass_find_device_by_of_offset(enum uclass_id id, int node, return -ENODEV; }
+#ifdef CONFIG_OF_LIVE +int uclass_find_device_by_of_node(enum uclass_id id, + const struct device_node *np, + struct udevice **devp) +{ + struct uclass *uc; + struct udevice *dev; + int ret; + + *devp = NULL; + if (!np) + return -ENODEV; + ret = uclass_get(id, &uc); + if (ret) + return ret; + + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + if (of_ref_equal(dev->node_ref, of_node_to_ref(np))) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} +#endif + #if CONFIG_IS_ENABLED(OF_CONTROL) static int uclass_find_device_by_phandle(enum uclass_id id, struct udevice *parent, @@ -407,6 +435,20 @@ int uclass_get_device_by_of_offset(enum uclass_id id, int node, return uclass_get_device_tail(dev, ret, devp); }
+#ifdef CONFIG_OF_LIVE +int uclass_get_device_by_of_node(enum uclass_id id, + const struct device_node *np, + struct udevice **devp) +{ + struct udevice *dev; + int ret; + + *devp = NULL; + ret = uclass_find_device_by_of_node(id, np, &dev); + return uclass_get_device_tail(dev, ret, devp); +} +#endif + #if CONFIG_IS_ENABLED(OF_CONTROL) int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, const char *name, struct udevice **devp) diff --git a/include/dm/uclass-internal.h b/include/dm/uclass-internal.h index ad284b8445a..103656fed0d 100644 --- a/include/dm/uclass-internal.h +++ b/include/dm/uclass-internal.h @@ -115,6 +115,23 @@ int uclass_find_device_by_of_offset(enum uclass_id id, int node, struct udevice **devp);
/** + * uclass_find_device_by_of_node() - Find a uclass device by device tree node + * + * This searches the devices in the uclass for one attached to the given + * device tree node. + * + * The device is NOT probed, it is merely returned. + * + * @id: ID to look up + * @node: Device tree offset to search for (if NULL then -ENODEV is returned) + * @devp: Returns pointer to device (there is only one for each node) + * @return 0 if OK, -ve on error + */ +int uclass_find_device_by_of_node(enum uclass_id id, + const struct device_node *np, + struct udevice **devp); + +/** * uclass_bind_device() - Associate device with a uclass * * Connect the device into uclass's list of devices. diff --git a/include/dm/uclass.h b/include/dm/uclass.h index b583aa869b3..2d4958d37b6 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -14,6 +14,8 @@ #include <linker_lists.h> #include <linux/list.h>
+struct device_node; + /** * struct uclass - a U-Boot drive class, collecting together similar drivers * @@ -186,6 +188,23 @@ int uclass_get_device_by_of_offset(enum uclass_id id, int node, struct udevice **devp);
/** + * uclass_get_device_by_of_node() - Get a uclass device by device tree node + * + * This searches the devices in the uclass for one attached to the given + * device tree node. + * + * The device is probed to activate it ready for use. + * + * @id: ID to look up + * @np: Device tree node to search for (if NULL then -ENODEV is returned) + * @devp: Returns pointer to device (there is only one for each node) + * @return 0 if OK, -ve on error + */ +int uclass_get_device_by_of_node(enum uclass_id id, + const struct device_node *np, + struct udevice **devp); + +/** * uclass_get_device_by_phandle() - Get a uclass device by phandle * * This searches the devices in the uclass for one with the given phandle.

Add a function which looks up a device by its node (either in live tree or flat tree).
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/core/uclass.c | 36 ++++++++++++++++++++++++++++++++++++ include/dm/uclass-internal.h | 5 +++++ include/dm/uclass.h | 4 ++++ 3 files changed, 45 insertions(+)
diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index f7430f00789..c327aaf594d 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -13,6 +13,7 @@ #include <dm/device.h> #include <dm/device-internal.h> #include <dm/lists.h> +#include <dm/of_dev.h> #include <dm/of_ref.h> #include <dm/uclass.h> #include <dm/uclass-internal.h> @@ -315,6 +316,30 @@ int uclass_find_device_by_of_node(enum uclass_id id, } #endif
+int uclass_find_device_by_of_ref(enum uclass_id id, of_node_ref node_ref, + struct udevice **devp) +{ + struct uclass *uc; + struct udevice *dev; + int ret; + + *devp = NULL; + if (!of_ref_valid(node_ref)) + return -ENODEV; + ret = uclass_get(id, &uc); + if (ret) + return ret; + + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + if (of_ref_equal(dev_of_node_ref(dev), node_ref)) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + #if CONFIG_IS_ENABLED(OF_CONTROL) static int uclass_find_device_by_phandle(enum uclass_id id, struct udevice *parent, @@ -449,6 +474,17 @@ int uclass_get_device_by_of_node(enum uclass_id id, } #endif
+int uclass_get_device_by_of_ref(enum uclass_id id, of_node_ref node_ref, + struct udevice **devp) +{ + struct udevice *dev; + int ret; + + *devp = NULL; + ret = uclass_find_device_by_of_ref(id, node_ref, &dev); + return uclass_get_device_tail(dev, ret, devp); +} + #if CONFIG_IS_ENABLED(OF_CONTROL) int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, const char *name, struct udevice **devp) diff --git a/include/dm/uclass-internal.h b/include/dm/uclass-internal.h index 103656fed0d..bb7819b8590 100644 --- a/include/dm/uclass-internal.h +++ b/include/dm/uclass-internal.h @@ -10,6 +10,8 @@ #ifndef _DM_UCLASS_INTERNAL_H #define _DM_UCLASS_INTERNAL_H
+#include <dm/of_ref.h> + /** * uclass_get_device_tail() - handle the end of a get_device call * @@ -131,6 +133,9 @@ int uclass_find_device_by_of_node(enum uclass_id id, const struct device_node *np, struct udevice **devp);
+int uclass_find_device_by_of_ref(enum uclass_id id, of_node_ref node_ref, + struct udevice **devp); + /** * uclass_bind_device() - Associate device with a uclass * diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 2d4958d37b6..ed438f699c2 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -10,6 +10,7 @@ #ifndef _DM_UCLASS_H #define _DM_UCLASS_H
+#include <dm/of_ref.h> #include <dm/uclass-id.h> #include <linker_lists.h> #include <linux/list.h> @@ -204,6 +205,9 @@ int uclass_get_device_by_of_node(enum uclass_id id, const struct device_node *np, struct udevice **devp);
+int uclass_get_device_by_of_ref(enum uclass_id id, of_node_ref node_ref, + struct udevice **devp); + /** * uclass_get_device_by_phandle() - Get a uclass device by phandle *

Move the main part of the GPIO request function into a separate function so that it can be used by the live tree function when added. Update the xlate method to use a node reference.
TODO(sjg@chromium.org): Update the other GPIO drivers
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/gpio/gpio-uclass.c | 56 ++++++++++++++++++++++++++++------------------ drivers/gpio/sandbox.c | 4 +++- include/asm-generic/gpio.h | 6 +++-- 3 files changed, 41 insertions(+), 25 deletions(-)
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 9ab9df4ce7a..721e95cd929 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -114,9 +114,8 @@ int gpio_lookup_name(const char *name, struct udevice **devp, return 0; }
-int gpio_xlate_offs_flags(struct udevice *dev, - struct gpio_desc *desc, - struct fdtdec_phandle_args *args) +int gpio_xlate_offs_flags(struct udevice *dev, struct gpio_desc *desc, + struct of_ref_phandle_args *args) { if (args->args_count < 1) return -EINVAL; @@ -133,7 +132,7 @@ int gpio_xlate_offs_flags(struct udevice *dev, }
static int gpio_find_and_xlate(struct gpio_desc *desc, - struct fdtdec_phandle_args *args) + struct of_ref_phandle_args *args) { struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
@@ -642,37 +641,30 @@ int dm_gpio_get_values_as_int(const struct gpio_desc *desc_list, int count) return vector; }
-static int _gpio_request_by_name_nodev(const void *blob, int node, - const char *list_name, int index, - struct gpio_desc *desc, int flags, - bool add_index) +static int gpio_request_tail(int ret, of_node_ref node_ref, + struct of_ref_phandle_args *args, + const char *list_name, int index, + struct gpio_desc *desc, int flags, bool add_index) { - struct fdtdec_phandle_args args; - int ret; - + if (ret) + goto err; desc->dev = NULL; desc->offset = 0; desc->flags = 0; - ret = fdtdec_parse_phandle_with_args(blob, node, list_name, - "#gpio-cells", 0, index, &args); - if (ret) { - debug("%s: fdtdec_parse_phandle_with_args failed\n", __func__); - goto err; - }
- ret = uclass_get_device_by_of_offset(UCLASS_GPIO, args.node, - &desc->dev); + ret = uclass_get_device_by_of_ref(UCLASS_GPIO, args->node_ref, + &desc->dev); if (ret) { debug("%s: uclass_get_device_by_of_offset failed\n", __func__); goto err; } - ret = gpio_find_and_xlate(desc, &args); + ret = gpio_find_and_xlate(desc, args); if (ret) { debug("%s: gpio_find_and_xlate failed\n", __func__); goto err; } ret = dm_gpio_requestf(desc, add_index ? "%s.%s%d" : "%s.%s", - fdt_get_name(blob, node, NULL), + of_ref_get_name(node_ref), list_name, index); if (ret) { debug("%s: dm_gpio_requestf failed\n", __func__); @@ -687,10 +679,30 @@ static int _gpio_request_by_name_nodev(const void *blob, int node, return 0; err: debug("%s: Node '%s', property '%s', failed to request GPIO index %d: %d\n", - __func__, fdt_get_name(blob, node, NULL), list_name, index, ret); + __func__, of_ref_get_name(node_ref), list_name, index, ret); return ret; }
+static int _gpio_request_by_name_nodev(const void *blob, int node, + const char *list_name, int index, + struct gpio_desc *desc, int flags, + bool add_index) +{ + struct fdtdec_phandle_args fdtdec_args; + struct of_ref_phandle_args args; + int ret; + + ret = fdtdec_parse_phandle_with_args(blob, node, list_name, + "#gpio-cells", 0, index, &fdtdec_args); + if (ret) + debug("%s: fdtdec_parse_phandle_with_args failed\n", __func__); + else + of_ref_from_fdtdec_phandle_args(&fdtdec_args, &args); + + return gpio_request_tail(ret, of_offset_to_ref(node), &args, list_name, + index, desc, flags, add_index); +} + int gpio_request_by_name_nodev(const void *blob, int node, const char *list_name, int index, struct gpio_desc *desc, int flags) diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c index ae6d93013f7..5bf6bb127d1 100644 --- a/drivers/gpio/sandbox.c +++ b/drivers/gpio/sandbox.c @@ -8,6 +8,8 @@ #include <fdtdec.h> #include <malloc.h> #include <asm/gpio.h> +#include <dm/of.h> +#include <dm/of_dev.h> #include <dt-bindings/gpio/gpio.h>
DECLARE_GLOBAL_DATA_PTR; @@ -165,7 +167,7 @@ static int sb_gpio_get_function(struct udevice *dev, unsigned offset) }
static int sb_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, - struct fdtdec_phandle_args *args) + struct of_ref_phandle_args *args) { desc->offset = args->args[0]; if (args->args_count < 2) diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 4aa0004fab4..f3341e51359 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -7,6 +7,8 @@ #ifndef _ASM_GENERIC_GPIO_H_ #define _ASM_GENERIC_GPIO_H_
+struct of_ref_phandle_args; + /* * Generic GPIO API for U-Boot * @@ -214,7 +216,7 @@ struct fdtdec_phandle_args; * */ int gpio_xlate_offs_flags(struct udevice *dev, struct gpio_desc *desc, - struct fdtdec_phandle_args *args); + struct of_ref_phandle_args *args);
/** * struct struct dm_gpio_ops - Driver model GPIO operations @@ -286,7 +288,7 @@ struct dm_gpio_ops { * @return 0 if OK, -ve on error */ int (*xlate)(struct udevice *dev, struct gpio_desc *desc, - struct fdtdec_phandle_args *args); + struct of_ref_phandle_args *args); };
/**

Add support for requesting GPIOs with a live device tree.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/gpio/gpio-uclass.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-)
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 721e95cd929..377729e3c62 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -11,6 +11,8 @@ #include <fdtdec.h> #include <malloc.h> #include <asm/gpio.h> +#include <dm/of_access.h> +#include <dm/of_dev.h> #include <linux/bug.h> #include <linux/ctype.h>
@@ -711,16 +713,42 @@ int gpio_request_by_name_nodev(const void *blob, int node, flags, index > 0); }
-int gpio_request_by_name(struct udevice *dev, const char *list_name, int index, +static int _gpio_request_by_name(const struct device_node *np, + const char *list_name, int index, + struct gpio_desc *desc, int flags, + bool add_index) +{ + struct of_phandle_args of_args; + struct of_ref_phandle_args args; + int ret; + + ret = of_parse_phandle_with_args(np, list_name, "#gpio-cells", index, + &of_args); + if (ret) + debug("%s: of_parse_phandle_with_args failed\n", __func__); + else + of_ref_from_of_phandle_args(&of_args, &args); + + return gpio_request_tail(ret, of_node_to_ref(np), &args, list_name, + index, desc, flags, add_index); +} + +int gpio_request_by_name(struct udevice *dev, const char *list_name, int index, struct gpio_desc *desc, int flags) { - /* - * This isn't ideal since we don't use dev->name in the debug() - * calls in gpio_request_by_name(), but we can do this until - * gpio_request_by_name_nodev() can be dropped. - */ - return gpio_request_by_name_nodev(gd->fdt_blob, dev_of_offset(dev), - list_name, index, desc, flags); + if (of_use_livetree()) { + return _gpio_request_by_name(dev_of_node(dev), list_name, + index, desc, flags, index > 0); + } else { + /* + * This isn't ideal since we don't use dev->name in the debug() + * calls in gpio_request_by_name(), but we can do this until + * gpio_request_by_name_nodev() can be dropped. + */ + return gpio_request_by_name_nodev(gd->fdt_blob, + dev_of_offset(dev), list_name, + index, desc, flags); + } }
int gpio_request_list_by_name_nodev(const void *blob, int node,

This is not needed. Drop it.
Signed-off-by: Simon Glass sjg@chromium.org ---
include/asm-generic/gpio.h | 1 - 1 file changed, 1 deletion(-)
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index f3341e51359..efa96a5f98d 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -213,7 +213,6 @@ struct fdtdec_phandle_args; * * This routine sets the offset field to args[0] and the flags field to * GPIOD_ACTIVE_LOW if the GPIO_ACTIVE_LOW flag is present in args[1]. - * */ int gpio_xlate_offs_flags(struct udevice *dev, struct gpio_desc *desc, struct of_ref_phandle_args *args);

Use the new dm_read...() functions to access the device tree, so that a live tree can be used.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/gpio/sandbox.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c index 5bf6bb127d1..ef9a05bf880 100644 --- a/drivers/gpio/sandbox.c +++ b/drivers/gpio/sandbox.c @@ -199,10 +199,8 @@ static int sandbox_gpio_ofdata_to_platdata(struct udevice *dev) { struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
- uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), - "num-gpios", 0); - uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), - "gpio-bank-name", NULL); + uc_priv->gpio_count = dev_read_u32_defaut(dev, "num-gpios", 0); + uc_priv->bank_name = dev_read_string(dev, "gpio-bank-name");
return 0; }

This prints out the wrong pointers. Fix it.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/misc/cros_ec.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c index 3d449b2a552..e2027ea5d20 100644 --- a/drivers/misc/cros_ec.c +++ b/drivers/misc/cros_ec.c @@ -304,8 +304,7 @@ static int ec_command_inptr(struct cros_ec_dev *dev, uint8_t cmd, NULL, 0, &din, din_len); }
- debug("%s: len=%d, dinp=%p, *dinp=%p\n", __func__, len, dinp, - dinp ? *dinp : NULL); + debug("%s: len=%d, din=%p\n", __func__, len, din); if (dinp) { /* If we have any data to return, it must be 64bit-aligned */ assert(len <= 0 || !((uintptr_t)din & 7));

Convert this driver to support the live device tree and remove the old fdtdec support.
The keyboard is not yet converted.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/misc/cros_ec.c | 33 +++++++++++++++------------------ drivers/misc/cros_ec_sandbox.c | 2 +- include/cros_ec.h | 8 +++----- include/fdtdec.h | 34 ---------------------------------- lib/fdtdec.c | 32 -------------------------------- 5 files changed, 19 insertions(+), 90 deletions(-)
diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c index e2027ea5d20..624a9992b91 100644 --- a/drivers/misc/cros_ec.c +++ b/drivers/misc/cros_ec.c @@ -26,6 +26,9 @@ #include <asm/io.h> #include <asm-generic/gpio.h> #include <dm/device-internal.h> +#include <dm/of_dev.h> +#include <dm/of_extra.h> +#include <dm/of_ref.h> #include <dm/uclass-internal.h>
#ifdef DEBUG_TRACE @@ -996,15 +999,12 @@ int cros_ec_get_ldo(struct udevice *dev, uint8_t index, uint8_t *state) int cros_ec_register(struct udevice *dev) { struct cros_ec_dev *cdev = dev_get_uclass_priv(dev); - const void *blob = gd->fdt_blob; - int node = dev_of_offset(dev); char id[MSG_BYTES];
cdev->dev = dev; gpio_request_by_name(dev, "ec-interrupt", 0, &cdev->ec_int, GPIOD_IS_IN); - cdev->optimise_flash_write = fdtdec_get_bool(blob, node, - "optimise-flash-write"); + cdev->optimise_flash_write = dev_read_bool(dev, "optimise-flash-write");
if (cros_ec_check_version(cdev)) { debug("%s: Could not detect CROS-EC version\n", __func__); @@ -1023,28 +1023,26 @@ int cros_ec_register(struct udevice *dev) return 0; }
-int cros_ec_decode_ec_flash(const void *blob, int node, - struct fdt_cros_ec *config) +int cros_ec_decode_ec_flash(struct udevice *dev, struct fdt_cros_ec *config) { - int flash_node; + of_node_ref flash_node, node;
- flash_node = fdt_subnode_offset(blob, node, "flash"); - if (flash_node < 0) { + flash_node = dev_find_subnode(dev, "flash"); + if (!of_ref_valid(flash_node)) { debug("Failed to find flash node\n"); return -1; }
- if (fdtdec_read_fmap_entry(blob, flash_node, "flash", - &config->flash)) { + if (of_read_fmap_entry(flash_node, "flash", &config->flash)) { debug("Failed to decode flash node in chrome-ec'\n"); return -1; }
- config->flash_erase_value = fdtdec_get_int(blob, flash_node, - "erase-value", -1); - for (node = fdt_first_subnode(blob, flash_node); node >= 0; - node = fdt_next_subnode(blob, node)) { - const char *name = fdt_get_name(blob, node, NULL); + config->flash_erase_value = of_ref_read_s32_default(flash_node, + "erase-value", -1); + for (node = of_ref_first_subnode(flash_node); of_ref_valid(node); + node = of_ref_next_subnode(node)) { + const char *name = of_ref_get_name(node); enum ec_flash_region region;
if (0 == strcmp(name, "ro")) { @@ -1058,8 +1056,7 @@ int cros_ec_decode_ec_flash(const void *blob, int node, return -1; }
- if (fdtdec_read_fmap_entry(blob, node, "reg", - &config->region[region])) { + if (of_read_fmap_entry(node, "reg", &config->region[region])) { debug("Failed to decode flash region in chrome-ec'\n"); return -1; } diff --git a/drivers/misc/cros_ec_sandbox.c b/drivers/misc/cros_ec_sandbox.c index 848c67bc230..367e42d4eb3 100644 --- a/drivers/misc/cros_ec_sandbox.c +++ b/drivers/misc/cros_ec_sandbox.c @@ -522,7 +522,7 @@ int cros_ec_probe(struct udevice *dev) int err;
memcpy(ec, &s_state, sizeof(*ec)); - err = cros_ec_decode_ec_flash(blob, dev_of_offset(dev), &ec->ec_config); + err = cros_ec_decode_ec_flash(dev, &ec->ec_config); if (err) return err;
diff --git a/include/cros_ec.h b/include/cros_ec.h index 0271f2b827c..771a176eeab 100644 --- a/include/cros_ec.h +++ b/include/cros_ec.h @@ -11,9 +11,9 @@
#include <linux/compiler.h> #include <ec_commands.h> -#include <fdtdec.h> #include <cros_ec_message.h> #include <asm/gpio.h> +#include <dm/of_extra.h>
/* Our configuration information */ struct cros_ec_dev { @@ -377,12 +377,10 @@ int cros_ec_get_error(void); /** * Returns information from the FDT about the Chrome EC flash * - * @param blob FDT blob to use - * @param node Node offset to read from + * @param dev Device to read from * @param config Structure to use to return information */ -int cros_ec_decode_ec_flash(const void *blob, int node, - struct fdt_cros_ec *config); +int cros_ec_decode_ec_flash(struct udevice *dev, struct fdt_cros_ec *config);
/** * Check the current keyboard state, in case recovery mode is requested. diff --git a/include/fdtdec.h b/include/fdtdec.h index d074478f141..4a0a5f37f3c 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -807,40 +807,6 @@ const u8 *fdtdec_locate_byte_array(const void *blob, int node, int fdtdec_decode_region(const void *blob, int node, const char *prop_name, fdt_addr_t *basep, fdt_size_t *sizep);
-enum fmap_compress_t { - FMAP_COMPRESS_NONE, - FMAP_COMPRESS_LZO, -}; - -enum fmap_hash_t { - FMAP_HASH_NONE, - FMAP_HASH_SHA1, - FMAP_HASH_SHA256, -}; - -/* A flash map entry, containing an offset and length */ -struct fmap_entry { - uint32_t offset; - uint32_t length; - uint32_t used; /* Number of bytes used in region */ - enum fmap_compress_t compress_algo; /* Compression type */ - enum fmap_hash_t hash_algo; /* Hash algorithm */ - const uint8_t *hash; /* Hash value */ - int hash_size; /* Hash size */ -}; - -/** - * Read a flash entry from the fdt - * - * @param blob FDT blob - * @param node Offset of node to read - * @param name Name of node being read - * @param entry Place to put offset and size of this node - * @return 0 if ok, -ve on error - */ -int fdtdec_read_fmap_entry(const void *blob, int node, const char *name, - struct fmap_entry *entry); - /** * Obtain an indexed resource from a device property. * diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 81f47ef2c7f..30ee56571ba 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -932,38 +932,6 @@ int fdtdec_decode_region(const void *blob, int node, const char *prop_name, return 0; }
-/** - * Read a flash entry from the fdt - * - * @param blob FDT blob - * @param node Offset of node to read - * @param name Name of node being read - * @param entry Place to put offset and size of this node - * @return 0 if ok, -ve on error - */ -int fdtdec_read_fmap_entry(const void *blob, int node, const char *name, - struct fmap_entry *entry) -{ - const char *prop; - u32 reg[2]; - - if (fdtdec_get_int_array(blob, node, "reg", reg, 2)) { - debug("Node '%s' has bad/missing 'reg' property\n", name); - return -FDT_ERR_NOTFOUND; - } - entry->offset = reg[0]; - entry->length = reg[1]; - entry->used = fdtdec_get_int(blob, node, "used", entry->length); - prop = fdt_getprop(blob, node, "compress", NULL); - entry->compress_algo = prop && !strcmp(prop, "lzo") ? - FMAP_COMPRESS_LZO : FMAP_COMPRESS_NONE; - prop = fdt_getprop(blob, node, "hash", &entry->hash_size); - entry->hash_algo = prop ? FMAP_HASH_SHA256 : FMAP_HASH_NONE; - entry->hash = (uint8_t *)prop; - - return 0; -} - u64 fdtdec_get_number(const fdt32_t *ptr, unsigned int cells) { u64 number = 0;

This updates sandbox to use a live device tree. This means that after relocation (from board_init_r() onwards) it no-longer uses flat device tree.
Note: There are various drivers that still need conversion so this does not currently work correctly. For example it does not pass the test suite.
Signed-off-by: Simon Glass sjg@chromium.org ---
configs/sandbox_defconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 8ac231db994..7814574eade 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -51,6 +51,7 @@ CONFIG_CMD_TPM=y CONFIG_CMD_TPM_TEST=y CONFIG_CMD_EXT4_WRITE=y CONFIG_OF_CONTROL=y +CONFIG_OF_LIVE=y CONFIG_OF_HOSTFILE=y CONFIG_NETCONSOLE=y CONFIG_REGMAP=y
participants (1)
-
Simon Glass