
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.
Signed-off-by: Simon Glass sjg@chromium.org ---
doc/develop/driver-model/livetree.rst | 13 +-- drivers/core/ofnode.c | 124 +++++++++++++++++++++++++- include/dm/ofnode.h | 41 ++++++--- 3 files changed, 161 insertions(+), 17 deletions(-)
diff --git a/doc/develop/driver-model/livetree.rst b/doc/develop/driver-model/livetree.rst index 4ef8c517325..76be89b9633 100644 --- a/doc/develop/driver-model/livetree.rst +++ b/doc/develop/driver-model/livetree.rst @@ -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 diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index a5c6e309615..b2b4b8d54cc 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -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> @@ -18,6 +20,126 @@ #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 ofnode oftree_ensure(void *fdt, const char *path) +{ + int offset, i; + ofnode node; + + 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 ofnode_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 ofnode_null(); + } + } + + offset = fdt_path_offset(fdt, path); + if (offset < 0) { + log_debug("Unable to find path '%s' in tree %p\n", path, fdt); + return ofnode_null(); + } + + node.of_offset = OFTREE_NODE(i, offset); + + return node; +} + +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; +} + +#else /* !OFNODE_MULTI_TREE */ + +static ofnode oftree_ensure(void *fdt, const char *path) +{ + return offset_to_ofnode(fdt_path_offset(fdt, path)); +} +#endif /* OFNODE_MULTI_TREE */ + bool ofnode_name_eq(ofnode node, const char *name) { const char *node_name; @@ -575,7 +697,7 @@ ofnode ofnode_path_root(oftree tree, const char *path) 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)); + return oftree_ensure(tree.fdt, path); }
const void *ofnode_read_chosen_prop(const char *propname, int *sizep) diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index a79909c12e1..4d614b0a6e3 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -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,32 @@ 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); + +#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()) @@ -70,6 +77,7 @@ static inline int ofnode_to_offset(ofnode node) #endif return node.of_offset; } +#endif /* OFNODE_MULTI_TREE */
/** * ofnode_to_np() - convert an ofnode to a live DT node pointer @@ -139,6 +147,17 @@ static inline bool ofnode_valid(ofnode node) return node.of_offset >= 0; }
+/** + * ofnode_lookup_fdt() - look up the FDT for a node + * + * Given a node this returns a pointer to the device tree containing that node. + * This can only be called when the flat tree is in use + * + * @node: Node to look up, may be ofnode_null() + * @return associated device tree, or gd->fdt_blob if @node is ofnode_null() + */ +void *ofnode_lookup_fdt(ofnode node); + /** * oftree_lookup_fdt() - object the FDT pointer from an oftree *