diff options
author | Simon Glass <sjg@chromium.org> | 2022-09-06 20:27:26 -0600 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2022-09-29 16:11:32 -0400 |
commit | 826afb94a2af31f7336030e1ffb301fa53251252 (patch) | |
tree | 2e3e39958f32fc797921c96d5816c49bdb6c6cda | |
parent | f2119ed3b1dbfc0cecb32ccf5c562b48bde60eb3 (diff) | |
download | u-boot-826afb94a2af31f7336030e1ffb301fa53251252.zip u-boot-826afb94a2af31f7336030e1ffb301fa53251252.tar.gz u-boot-826afb94a2af31f7336030e1ffb301fa53251252.tar.bz2 |
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 <sjg@chromium.org>
-rw-r--r-- | doc/develop/driver-model/livetree.rst | 13 | ||||
-rw-r--r-- | drivers/core/ofnode.c | 170 | ||||
-rw-r--r-- | 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 4ef8c51..76be89b 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 784ed37..08bc103 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,22 +20,182 @@ #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; } diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index ad179a6..3a514f7 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 * @@ -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 * |