dm: core: Add the ofnode multi-tree implementation
authorSimon Glass <sjg@chromium.org>
Wed, 7 Sep 2022 02:27:26 +0000 (20:27 -0600)
committerTom Rini <trini@konsulko.com>
Fri, 30 Sep 2022 02:43:43 +0000 (22:43 -0400)
Add the logic to redirect requests for the device tree through a function
which can look up the tree ID. This works by using the top bits of
ofnode.of_offset to encode a tree.

It is assumed that there will only be a few device trees used at runtime,
typically the control FDT (always tree ID 0) and possibly a separate FDT
to be passed the OS.

The maximum number of device trees supported at runtime is 8, with this
implementation. That would use bits 30:28 of the node-offset value,
meaning that the positive offset range is limited to bits 27:0, versus
30:1 with this feature disabled. That still allows a device tree of up
to 256MB, which should be enough for most FITs. Larger ones can be
supported by using external data with the FIT, or by enabling OF_LIVE.

Update the documentation a little and fix up the comment for
ofnode_valid().

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

index 4ef8c51..76be89b 100644 (file)
@@ -250,11 +250,14 @@ 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.
+oftree_default() but can be set to other values using oftree_from_fdt().
+So long as OF_LIVE is disabled, it is possible to do fixups using the ofnode
+interface. The OF_LIVE support required addition of the flattening step at the
+end.
+
+See dm_test_ofnode_root() for some examples. The ofnode_path_root() function
+causes a flat device tree to be 'registered' such that it can be used by the
+ofnode interface.
 
 
 Internal implementation
index 7f6c47f..53db7b5 100644 (file)
@@ -4,6 +4,8 @@
  * Written by Simon Glass <sjg@chromium.org>
  */
 
+#define LOG_CATEGORY   LOGC_DT
+
 #include <common.h>
 #include <dm.h>
 #include <fdtdec.h>
 #include <linux/ioport.h>
 #include <asm/global_data.h>
 
+DECLARE_GLOBAL_DATA_PTR;
+
+#if CONFIG_IS_ENABLED(OFNODE_MULTI_TREE)
+static void *oftree_list[CONFIG_OFNODE_MULTI_TREE_MAX];
+static int oftree_count;
+
+void oftree_reset(void)
+{
+       if (gd->flags & GD_FLG_RELOC) {
+               oftree_count = 0;
+               oftree_list[oftree_count++] = (void *)gd->fdt_blob;
+       }
+}
+
+static int oftree_find(const void *fdt)
+{
+       int i;
+
+       for (i = 0; i < oftree_count; i++) {
+               if (fdt == oftree_list[i])
+                       return i;
+       }
+
+       return -1;
+}
+
+static oftree oftree_ensure(void *fdt)
+{
+       oftree tree;
+       int i;
+
+       if (gd->flags & GD_FLG_RELOC) {
+               i = oftree_find(fdt);
+               if (i == -1) {
+                       if (oftree_count == CONFIG_OFNODE_MULTI_TREE_MAX) {
+                               log_warning("Too many registered device trees (max %d)\n",
+                                           CONFIG_OFNODE_MULTI_TREE_MAX);
+                               return oftree_null();
+                       }
+
+                       /* register the new tree */
+                       i = oftree_count++;
+                       oftree_list[i] = fdt;
+                       log_debug("oftree: registered tree %d: %p\n", i, fdt);
+               }
+       } else {
+               if (fdt != gd->fdt_blob) {
+                       log_debug("Cannot only access control FDT before relocation\n");
+                       return oftree_null();
+               }
+       }
+
+       tree.fdt = fdt;
+
+       return tree;
+}
+
+void *ofnode_lookup_fdt(ofnode node)
+{
+       if (gd->flags & GD_FLG_RELOC) {
+               uint i = OFTREE_TREE_ID(node.of_offset);
+
+               if (i > oftree_count) {
+                       log_debug("Invalid tree ID %x\n", i);
+                       return NULL;
+               }
+
+               return oftree_list[i];
+       } else {
+               return (void *)gd->fdt_blob;
+       }
+}
+
+void *ofnode_to_fdt(ofnode node)
+{
+#ifdef OF_CHECKS
+       if (of_live_active())
+               return NULL;
+#endif
+       if (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) && ofnode_valid(node))
+               return ofnode_lookup_fdt(node);
+
+       /* Use the control FDT by default */
+       return (void *)gd->fdt_blob;
+}
+
+/**
+ * ofnode_to_offset() - convert an ofnode to a flat DT offset
+ *
+ * This cannot be called if the reference contains a node pointer.
+ *
+ * @node: Reference containing offset (possibly invalid)
+ * Return: DT offset (can be -1)
+ */
+int ofnode_to_offset(ofnode node)
+{
+#ifdef OF_CHECKS
+       if (of_live_active())
+               return -1;
+#endif
+       if (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) && node.of_offset >= 0)
+               return OFTREE_OFFSET(node.of_offset);
+
+       return node.of_offset;
+}
+
+oftree oftree_from_fdt(void *fdt)
+{
+       oftree tree;
+
+       if (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE))
+               return oftree_ensure(fdt);
+
+       tree.fdt = fdt;
+
+       return tree;
+}
+
+/**
+ * noffset_to_ofnode() - convert a DT offset to an ofnode
+ *
+ * @other_node: Node in the same tree to use as a reference
+ * @of_offset: DT offset (either valid, or -1)
+ * Return: reference to the associated DT offset
+ */
+ofnode noffset_to_ofnode(ofnode other_node, int of_offset)
+{
+       ofnode node;
+
+       if (of_live_active())
+               node.np = NULL;
+       else if (!CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) || of_offset < 0 ||
+                !ofnode_valid(other_node))
+               node.of_offset = of_offset;
+       else
+               node.of_offset = OFTREE_MAKE_NODE(other_node.of_offset,
+                                                 of_offset);
+
+       return node;
+}
+
+#else /* !OFNODE_MULTI_TREE */
+
+static inline int oftree_find(const void *fdt)
+{
+       return 0;
+}
+
+#endif /* OFNODE_MULTI_TREE */
+
 /**
  * ofnode_from_tree_offset() - get an ofnode from a tree offset (flat tree)
  *
- * Looks up the tree and returns an ofnode with the correct of_offset
+ * Looks up the tree and returns an ofnode with the correct of_offset (i.e.
+ * containing the tree ID).
  *
- * If @offset is < 0 then this returns an ofnode with that offset
+ * If @offset is < 0 then this returns an ofnode with that offset and no tree
+ * ID.
  *
  * @tree: tree to check
  * @offset: offset within that tree (can be < 0)
- * @return node for that offset
+ * @return node for that offset, with the correct ID
  */
 static ofnode ofnode_from_tree_offset(oftree tree, int offset)
 {
        ofnode node;
 
-       node.of_offset = offset;
+       if (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) && offset >= 0) {
+               int tree_id = oftree_find(tree.fdt);
+
+               if (tree_id == -1)
+                       return ofnode_null();
+               node.of_offset = OFTREE_NODE(tree_id, offset);
+       } else {
+               node.of_offset = offset;
+       }
 
        return node;
 }
index ad179a6..3a514f7 100644 (file)
@@ -27,13 +27,14 @@ struct ofnode_phandle_args {
        uint32_t args[OF_MAX_PHANDLE_ARGS];
 };
 
+#if CONFIG_IS_ENABLED(OFNODE_MULTI_TREE)
 /**
  * oftree_reset() - reset the state of the oftree list
  *
  * Reset the oftree list so it can be started again. This should be called
  * once the control FDT is in place, but before the ofnode interface is used.
  */
-static inline void oftree_reset(void) {}
+void oftree_reset(void);
 
 /**
  * ofnode_to_fdt() - convert an ofnode to a flat DT pointer
@@ -43,26 +44,50 @@ static inline void oftree_reset(void) {}
  * @node: Reference containing offset (possibly invalid)
  * Return: DT offset (can be NULL)
  */
+__attribute_const__ void *ofnode_to_fdt(ofnode node);
+
+/**
+ * ofnode_to_offset() - convert an ofnode to a flat DT offset
+ *
+ * This cannot be called if the reference contains a node pointer.
+ *
+ * @node: Reference containing offset (possibly invalid)
+ * Return: DT offset (can be -1)
+ */
+__attribute_const__ int ofnode_to_offset(ofnode node);
+
+/**
+ * oftree_from_fdt() - Returns an oftree from a flat device tree pointer
+ *
+ * @fdt: Device tree to use
+ *
+ * Returns: reference to the given node
+ */
+oftree oftree_from_fdt(void *fdt);
+
+/**
+ * noffset_to_ofnode() - convert a DT offset to an ofnode
+ *
+ * @other_node: Node in the same tree to use as a reference
+ * @of_offset: DT offset (either valid, or -1)
+ * Return: reference to the associated DT offset
+ */
+ofnode noffset_to_ofnode(ofnode other_node, int of_offset);
+
+#else /* !OFNODE_MULTI_TREE */
+static inline void oftree_reset(void) {}
+
 static inline void *ofnode_to_fdt(ofnode node)
 {
 #ifdef OF_CHECKS
        if (of_live_active())
                return NULL;
 #endif
-
        /* Use the control FDT by default */
        return (void *)gd->fdt_blob;
 }
 
-/**
- * ofnode_to_offset() - convert an ofnode to a flat DT offset
- *
- * This cannot be called if the reference contains a node pointer.
- *
- * @node: Reference containing offset (possibly invalid)
- * Return: DT offset (can be -1)
- */
-static inline int ofnode_to_offset(ofnode node)
+static inline __attribute_const__ int ofnode_to_offset(ofnode node)
 {
 #ifdef OF_CHECKS
        if (of_live_active())
@@ -71,6 +96,33 @@ static inline int ofnode_to_offset(ofnode node)
        return node.of_offset;
 }
 
+static inline oftree oftree_from_fdt(void *fdt)
+{
+       oftree tree;
+
+       /* we cannot access other trees without OFNODE_MULTI_TREE */
+       if (fdt == gd->fdt_blob)
+               tree.fdt = fdt;
+       else
+               tree.fdt = NULL;
+
+       return tree;
+}
+
+static inline ofnode noffset_to_ofnode(ofnode other_node, int of_offset)
+{
+       ofnode node;
+
+       if (of_live_active())
+               node.np = NULL;
+       else
+               node.of_offset = of_offset;
+
+       return node;
+}
+
+#endif /* OFNODE_MULTI_TREE */
+
 /**
  * ofnode_to_np() - convert an ofnode to a live DT node pointer
  *
@@ -89,29 +141,10 @@ static inline struct device_node *ofnode_to_np(ofnode node)
 }
 
 /**
- * noffset_to_ofnode() - convert a DT offset to an ofnode
- *
- * @other_node: Node in the same tree to use as a reference
- * @of_offset: DT offset (either valid, or -1)
- * Return: reference to the associated DT offset
- */
-static inline ofnode noffset_to_ofnode(ofnode other_node, int of_offset)
-{
-       ofnode node;
-
-       if (of_live_active())
-               node.np = NULL;
-       else
-               node.of_offset = of_offset;
-
-       return node;
-}
-
-/**
  * ofnode_valid() - check if an ofnode is valid
  *
  * @node: Reference containing offset (possibly invalid)
- * Return: true if the reference contains a valid ofnode, false if it is NULL
+ * Return: true if the reference contains a valid ofnode, false if not
  */
 static inline bool ofnode_valid(ofnode node)
 {
@@ -317,22 +350,6 @@ static inline oftree oftree_from_np(struct device_node *root)
 }
 
 /**
- * oftree_from_fdt() - Returns an oftree from a flat device tree pointer
- *
- * @fdt: Device tree to use
- *
- * Returns: reference to the given node
- */
-static inline oftree oftree_from_fdt(void *fdt)
-{
-       oftree tree;
-
-       tree.fdt = fdt;
-
-       return tree;
-}
-
-/**
  * ofnode_name_eq() - Check if the node name is equivalent to a given name
  *                    ignoring the unit address
  *