]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
dm: core: Introduce support for multiple trees
authorSimon Glass <sjg@chromium.org>
Sat, 30 Jul 2022 21:52:08 +0000 (15:52 -0600)
committerTom Rini <trini@konsulko.com>
Fri, 12 Aug 2022 12:14:23 +0000 (08:14 -0400)
At present ofnode only works with a single device tree, for the most part.
This is the control FDT used by U-Boot.

When booting an OS we may obtain a different device tree and want to
modify it. Add some initial support for this into the ofnode API.

Note that we don't permit aliases in this other device tree, since the
of_access implementation maintains a list of aliases collected at
start-up. Also, we don't need aliases to do fixups in the other FDT. So
make sure that flat tree and live tree processing are consistent in this
area.

Signed-off-by: Simon Glass <sjg@chromium.org>
doc/develop/driver-model/livetree.rst
drivers/core/of_access.c
drivers/core/ofnode.c
include/dm/of_access.h
include/dm/ofnode.h
include/dm/ofnode_decl.h
include/of_live.h
lib/of_live.c
test/dm/ofnode.c

index fb2969259d0780b4f67e3208968d397a240e9bb7..c29f29b205b379f082d4728fffd0f6adc8b82490 100644 (file)
@@ -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
 -----------------------
 
index c20b19cb50fbeb5630db31c088276d376bd117ab..0e5915a43e6cf285c0ae564723fc7cb11e0e4cb9 100644 (file)
@@ -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);
        }
 
index a59832ebbfb3027fc9df6fdc0d00164b8c8d3054..bd41ef503c2067e2ad86ca08951f3b5fba6c8669 100644 (file)
@@ -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;
index ec6e6e2c7c0d7c82b550a012fd046da995c423dc..078f2ea06cdfeb2d0b3a3f2a7cc4cb085b44441b 100644 (file)
@@ -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);
 }
 
 /**
index 5a5309d79a70f87cfa9b9938895169e1c951ee56..d7ad5dccc14134d0217c1c81adb7ac27bfb8a29d 100644 (file)
@@ -176,6 +176,23 @@ static inline ofnode ofnode_root(void)
        return node;
 }
 
+/**
+ * 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,11 +657,22 @@ 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
  *
index 7c9e43e4ad8728298a05a1b94d1920b6f216e5a3..266253d5e3357921ea17eb2a7cf45d8d606b1e1f 100644 (file)
@@ -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
 
index b2b9679ae84145682350d8dc3784de8840835259..f59d6af3350dbad54552101df6cfd67adced8f56 100644 (file)
@@ -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
index 2cb0dd9c073af5c322c09e9a7cd18b639eaa9566..30cae9ab8815427d4bd4fb54b9220519a7b76a6e 100644 (file)
@@ -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;
index 61ae1db62d76528b5c49312aec72b407ce051807..6a252f3f504fcba553a43c0cd0b568ee38db6f2f 100644 (file)
@@ -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);