From 92291652b5741647919770c29cae8a2aac56b2bf Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 6 Sep 2022 20:27:26 -0600 Subject: [PATCH] dm: core: Add the ofnode multi-tree implementation 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 --- doc/develop/driver-model/livetree.rst | 13 +- drivers/core/ofnode.c | 170 +++++++++++++++++++++++++- include/dm/ofnode.h | 111 ++++++++++------- 3 files changed, 238 insertions(+), 56 deletions(-) diff --git a/doc/develop/driver-model/livetree.rst b/doc/develop/driver-model/livetree.rst index 4ef8c51732..76be89b963 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 7f6c47f0c0..53db7b5ff0 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -4,6 +4,8 @@ * Written by Simon Glass */ +#define LOG_CATEGORY LOGC_DT + #include #include #include @@ -18,22 +20,182 @@ #include #include +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; } diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index ad179a65e9..3a514f71f6 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,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 * @@ -88,30 +140,11 @@ static inline struct device_node *ofnode_to_np(ofnode node) return node.np; } -/** - * 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) { @@ -316,22 +349,6 @@ static inline oftree oftree_from_np(struct device_node *root) return tree; } -/** - * 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 -- 2.39.5