summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/develop/driver-model/livetree.rst17
-rw-r--r--drivers/core/of_access.c14
-rw-r--r--drivers/core/ofnode.c11
-rw-r--r--include/dm/of_access.h10
-rw-r--r--include/dm/ofnode.h28
-rw-r--r--include/dm/ofnode_decl.h13
-rw-r--r--include/of_live.h16
-rw-r--r--lib/of_live.c14
-rw-r--r--test/dm/ofnode.c67
9 files changed, 171 insertions, 19 deletions
diff --git a/doc/develop/driver-model/livetree.rst b/doc/develop/driver-model/livetree.rst
index fb2969259d..c29f29b205 100644
--- a/doc/develop/driver-model/livetree.rst
+++ b/doc/develop/driver-model/livetree.rst
@@ -225,6 +225,23 @@ freed. Then the tree can be scanned for these 'separately allocated' nodes and
properties before freeing the memory block.
+Multiple livetrees
+------------------
+
+The livetree implementation was originally designed for use with the control
+FDT. This means that the FDT fix-ups (ft_board_setup() and the like, must use
+a flat tree.
+
+It would be helpful to use livetree for fixups, since adding a lot of nodes and
+properties would involve less memory copying and be more efficient. As a step
+towards this, an `oftree` type has been introduced. It is normally set to
+oftree_default() but can be set to other values. Eventually this should allow
+the use of FDT fixups using the ofnode interface, instead of the low-level
+libfdt one.
+
+See dm_test_ofnode_root() for some examples.
+
+
Internal implementation
-----------------------
diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c
index c20b19cb50..0e5915a43e 100644
--- a/drivers/core/of_access.c
+++ b/drivers/core/of_access.c
@@ -343,24 +343,30 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent,
#define for_each_property_of_node(dn, pp) \
for (pp = dn->properties; pp != NULL; pp = pp->next)
-struct device_node *of_find_node_opts_by_path(const char *path,
+struct device_node *of_find_node_opts_by_path(struct device_node *root,
+ const char *path,
const char **opts)
{
struct device_node *np = NULL;
struct property *pp;
const char *separator = strchr(path, ':');
+ if (!root)
+ root = gd->of_root;
if (opts)
*opts = separator ? separator + 1 : NULL;
if (strcmp(path, "/") == 0)
- return of_node_get(gd->of_root);
+ return of_node_get(root);
/* The path could begin with an alias */
if (*path != '/') {
int len;
const char *p = separator;
+ /* Only allow alias processing on the control FDT */
+ if (root != gd->of_root)
+ return NULL;
if (!p)
p = strchrnul(path, '/');
len = p - path;
@@ -383,7 +389,7 @@ struct device_node *of_find_node_opts_by_path(const char *path,
/* Step down the tree matching path components */
if (!np)
- np = of_node_get(gd->of_root);
+ np = of_node_get(root);
while (np && *path == '/') {
struct device_node *tmp = np;
@@ -791,7 +797,7 @@ int of_alias_scan(void)
name = of_get_property(of_chosen, "stdout-path", NULL);
if (name)
- of_stdout = of_find_node_opts_by_path(name,
+ of_stdout = of_find_node_opts_by_path(NULL, name,
&of_stdout_options);
}
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index a59832ebbf..bd41ef503c 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -552,6 +552,17 @@ ofnode ofnode_path(const char *path)
return offset_to_ofnode(fdt_path_offset(gd->fdt_blob, path));
}
+ofnode ofnode_path_root(oftree tree, const char *path)
+{
+ if (of_live_active())
+ return np_to_ofnode(of_find_node_opts_by_path(tree.np, path,
+ NULL));
+ else if (*path != '/' && tree.fdt != gd->fdt_blob)
+ return ofnode_null(); /* Aliases only on control FDT */
+ else
+ return offset_to_ofnode(fdt_path_offset(tree.fdt, path));
+}
+
const void *ofnode_read_chosen_prop(const char *propname, int *sizep)
{
ofnode chosen_node;
diff --git a/include/dm/of_access.h b/include/dm/of_access.h
index ec6e6e2c7c..078f2ea06c 100644
--- a/include/dm/of_access.h
+++ b/include/dm/of_access.h
@@ -197,6 +197,11 @@ struct device_node *of_get_parent(const struct device_node *np);
/**
* of_find_node_opts_by_path() - Find a node matching a full OF path
*
+ * Note that alias processing is only available on the control FDT (gd->of_root).
+ * For other trees it is skipped, so any attempt to obtain an alias will result
+ * in returning NULL.
+ *
+ * @root: Root node of the tree to use. If this is NULL, then gd->of_root is used
* @path: Either the full path to match, or if the path does not start with
* '/', the name of a property of the /aliases node (an alias). In the
* case of an alias, the node matching the alias' value will be returned.
@@ -210,12 +215,13 @@ struct device_node *of_get_parent(const struct device_node *np);
*
* Return: a node pointer or NULL if not found
*/
-struct device_node *of_find_node_opts_by_path(const char *path,
+struct device_node *of_find_node_opts_by_path(struct device_node *root,
+ const char *path,
const char **opts);
static inline struct device_node *of_find_node_by_path(const char *path)
{
- return of_find_node_opts_by_path(path, NULL);
+ return of_find_node_opts_by_path(NULL, path, NULL);
}
/**
diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h
index 5a5309d79a..d7ad5dccc1 100644
--- a/include/dm/ofnode.h
+++ b/include/dm/ofnode.h
@@ -177,6 +177,23 @@ static inline ofnode ofnode_root(void)
}
/**
+ * oftree_default() - Returns the default device tree (U-Boot's control FDT)
+ *
+ * Returns: reference to the control FDT
+ */
+static inline oftree oftree_default(void)
+{
+ oftree tree;
+
+ if (of_live_active())
+ tree.np = gd_of_root();
+ else
+ tree.fdt = (void *)gd->fdt_blob;
+
+ return tree;
+}
+
+/**
* ofnode_name_eq() - Check if the node name is equivalent to a given name
* ignoring the unit address
*
@@ -640,12 +657,23 @@ int ofnode_count_phandle_with_args(ofnode node, const char *list_name,
/**
* ofnode_path() - find a node by full path
*
+ * This uses the control FDT.
+ *
* @path: Full path to node, e.g. "/bus/spi@1"
* Return: reference to the node found. Use ofnode_valid() to check if it exists
*/
ofnode ofnode_path(const char *path);
/**
+ * ofnode_path_root() - find a node by full path from a root node
+ *
+ * @tree: Device tree to use
+ * @path: Full path to node, e.g. "/bus/spi@1"
+ * Return: reference to the node found. Use ofnode_valid() to check if it exists
+ */
+ofnode ofnode_path_root(oftree tree, const char *path);
+
+/**
* ofnode_read_chosen_prop() - get the value of a chosen property
*
* This looks for a property within the /chosen node and returns its value
diff --git a/include/dm/ofnode_decl.h b/include/dm/ofnode_decl.h
index 7c9e43e4ad..266253d5e3 100644
--- a/include/dm/ofnode_decl.h
+++ b/include/dm/ofnode_decl.h
@@ -68,5 +68,18 @@ struct ofprop {
};
};
+/**
+ * union oftree_union - reference to a tree of device tree nodes
+ *
+ * One or other of the members is used, depending on of_live_active()
+ *
+ * @np: Pointer to roott device node, used for live tree
+ * @fdt: Pointer to the flat device tree, used for flat tree
+ */
+typedef union oftree_union {
+ struct device_node *np;
+ void *fdt;
+} oftree;
+
#endif
diff --git a/include/of_live.h b/include/of_live.h
index b2b9679ae8..f59d6af335 100644
--- a/include/of_live.h
+++ b/include/of_live.h
@@ -20,4 +20,20 @@ struct device_node;
*/
int of_live_build(const void *fdt_blob, struct device_node **rootp);
+/**
+ * unflatten_device_tree() - create tree of device_nodes from flat blob
+ *
+ * Note that this allocates a single block of memory, pointed to by *mynodes.
+ * To free the tree, use free(*mynodes)
+ *
+ * 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
+ */
+int unflatten_device_tree(const void *blob, struct device_node **mynodes);
+
#endif
diff --git a/lib/of_live.c b/lib/of_live.c
index 2cb0dd9c07..30cae9ab88 100644
--- a/lib/of_live.c
+++ b/lib/of_live.c
@@ -248,19 +248,7 @@ static void *unflatten_dt_node(const void *blob, void *mem, int *poffset,
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)
+int unflatten_device_tree(const void *blob, struct device_node **mynodes)
{
unsigned long size;
int start;
diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c
index 61ae1db62d..6a252f3f50 100644
--- a/test/dm/ofnode.c
+++ b/test/dm/ofnode.c
@@ -3,6 +3,7 @@
#include <common.h>
#include <dm.h>
#include <log.h>
+#include <of_live.h>
#include <dm/of_extra.h>
#include <dm/test.h>
#include <test/test.h>
@@ -469,3 +470,69 @@ static int dm_test_ofnode_get_phy(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_ofnode_get_phy, 0);
+
+/**
+ * make_ofnode_fdt() - Create an FDT for testing with ofnode
+ *
+ * The size is set to the minimum needed
+ *
+ * @uts: Test state
+ * @fdt: Place to write FDT
+ * @size: Maximum size of space for fdt
+ */
+static int make_ofnode_fdt(struct unit_test_state *uts, void *fdt, int size)
+{
+ ut_assertok(fdt_create(fdt, size));
+ ut_assertok(fdt_finish_reservemap(fdt));
+ ut_assert(fdt_begin_node(fdt, "") >= 0);
+
+ ut_assert(fdt_begin_node(fdt, "aliases") >= 0);
+ ut_assertok(fdt_property_string(fdt, "mmc0", "/new-mmc"));
+ ut_assertok(fdt_end_node(fdt));
+
+ ut_assert(fdt_begin_node(fdt, "new-mmc") >= 0);
+ ut_assertok(fdt_end_node(fdt));
+
+ ut_assertok(fdt_end_node(fdt));
+ ut_assertok(fdt_finish(fdt));
+
+ return 0;
+}
+
+static int dm_test_ofnode_root(struct unit_test_state *uts)
+{
+ struct device_node *root = NULL;
+ char fdt[256];
+ oftree tree;
+ ofnode node;
+
+ /* Check that aliases work on the control FDT */
+ node = ofnode_get_aliases_node("ethernet3");
+ ut_assert(ofnode_valid(node));
+ ut_asserteq_str("sbe5", ofnode_get_name(node));
+
+ ut_assertok(make_ofnode_fdt(uts, fdt, sizeof(fdt)));
+ if (of_live_active()) {
+ ut_assertok(unflatten_device_tree(fdt, &root));
+ tree.np = root;
+ } else {
+ tree.fdt = fdt;
+ }
+
+ /* Make sure they don't work on this new tree */
+ node = ofnode_path_root(tree, "mmc0");
+ ut_assert(!ofnode_valid(node));
+
+ /* It should appear in the new tree */
+ node = ofnode_path_root(tree, "/new-mmc");
+ ut_assert(ofnode_valid(node));
+
+ /* ...and not in the control FDT */
+ node = ofnode_path_root(oftree_default(), "/new-mmc");
+ ut_assert(!ofnode_valid(node));
+
+ free(root);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_root, UT_TESTF_SCAN_FDT);