diff --git a/doc/build/dts/api/api.rst b/doc/build/dts/api/api.rst index 7e47f2c1ed9da..336178050ba46 100644 --- a/doc/build/dts/api/api.rst +++ b/doc/build/dts/api/api.rst @@ -104,6 +104,9 @@ does not apply to macros which take cell names as arguments. For-each macros =============== +The :c:macro:`DT_FOREACH_CHILD` macro allows iterating over the ancestor node +of a devicetree node. + There is currently only one "generic" for-each macro, :c:func:`DT_FOREACH_CHILD`, which allows iterating over the children of a devicetree node. diff --git a/include/zephyr/devicetree.h b/include/zephyr/devicetree.h index aba28c0ab6325..373e47f0e5f11 100644 --- a/include/zephyr/devicetree.h +++ b/include/zephyr/devicetree.h @@ -2961,6 +2961,55 @@ */ #define DT_FOREACH_STATUS_OKAY_NODE_VARGS(fn, ...) DT_FOREACH_OKAY_VARGS_HELPER(fn, __VA_ARGS__) +/** + * @brief Invokes @p fn for each ancestor of @p node_id + * + * The macro @p fn must take one parameter, which will be the identifier + * of a child node of @p node_id to enable traversal of all ancestor nodes. + * + * The ancestor will be iterated over in the same order as they + * appear in the final devicetree. + * + * Example devicetree fragment: + * + * @code{.dts} + * n: node1 { + * foobar = "foo1"; + * + * n_2: node2 { + * foobar = "foo2"; + * + * n_3: node3 { + * foobar = "foo3"; + * }; + * }; + * }; + * @endcode + * + * Example usage: + * + * @code{.c} + * #define GET_PROP(n) DT_PROP(n, foobar), + * + * const char *ancestor_names[] = { + * DT_FOREACH_ANCESTOR(DT_NODELABEL(n_3), GET_PROP) + * }; + * @endcode + * + * This expands to: + * + * @code{.c} + * const char *ancestor_names[] = { + * "foo2", "foo1", + * }; + * @endcode + * + * @param node_id node identifier + * @param fn macro to invoke + */ +#define DT_FOREACH_ANCESTOR(node_id, fn) \ + DT_CAT(node_id, _FOREACH_ANCESTOR)(fn) + /** * @brief Invokes @p fn for each child of @p node_id * diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py index 3a8fa7aaedd55..66d0fb4dfac35 100755 --- a/scripts/dts/gen_defines.py +++ b/scripts/dts/gen_defines.py @@ -97,6 +97,7 @@ def main(): out_dt_define(f"{node.z_path_id}_FOREACH_NODELABEL_VARGS(fn, ...)", " ".join(f"fn({nodelabel}, __VA_ARGS__)" for nodelabel in node.labels)) + write_parent(node) write_children(node) write_dep_info(node) write_idents_and_existence(node) @@ -457,6 +458,17 @@ def write_compatibles(node: edtlib.Node) -> None: out_dt_define(f"{node.z_path_id}_COMPAT_MODEL_IDX_{i}", quote_str(node.edt.compat2model[compat])) +def write_parent(node: edtlib.Node) -> None: + # Visit all parent nodes. + def _visit_parent_node(node: edtlib.Node): + while node is not None: + yield node.parent + node = node.parent + + # Writes helper macros for dealing with node's parent. + out_dt_define(f"{node.z_path_id}_FOREACH_ANCESTOR(fn)", + " ".join(f"fn(DT_{parent.z_path_id})" for parent in + _visit_parent_node(node) if parent is not None)) def write_children(node: edtlib.Node) -> None: # Writes helper macros for dealing with node's children. diff --git a/tests/lib/devicetree/api/app.overlay b/tests/lib/devicetree/api/app.overlay index 359f17066a2d7..3943e3847e10c 100644 --- a/tests/lib/devicetree/api/app.overlay +++ b/tests/lib/devicetree/api/app.overlay @@ -570,6 +570,18 @@ phys = <&test_transceiver1>; }; + test_parent: test-parent { + compatible = "vnd,parent-bindings"; + + test_parent_a: parent-a { + val = <0>; + + test_parent_b: parent-b { + val = <0>; + }; + }; + }; + /* there should only be one of these */ test_children: test-children { compatible = "vnd,child-bindings"; diff --git a/tests/lib/devicetree/api/src/main.c b/tests/lib/devicetree/api/src/main.c index d4dd12cf733be..2148268b5ce44 100644 --- a/tests/lib/devicetree/api/src/main.c +++ b/tests/lib/devicetree/api/src/main.c @@ -2225,6 +2225,31 @@ ZTEST(devicetree_api, test_parent) TEST_SPI_BUS_0), ""); } +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_parent_bindings +ZTEST(devicetree_api, test_parent_nodes_list) +{ + /* When traversing upwards, there are no fixed attributes and labels */ + #define TEST_FUNC(parent) { /* No operation */ } + #define TEST_FUNC_AND_COMMA(parent) TEST_FUNC(parent), + + struct vnd_parent_binding { + int val; + }; + + struct vnd_parent_binding vals_a[] = { + DT_FOREACH_ANCESTOR(DT_NODELABEL(test_parent_a), TEST_FUNC_AND_COMMA)}; + + struct vnd_parent_binding vals_b[] = { + DT_FOREACH_ANCESTOR(DT_NODELABEL(test_parent_b), TEST_FUNC_AND_COMMA)}; + + zassert_equal(ARRAY_SIZE(vals_a), 3, ""); + zassert_equal(ARRAY_SIZE(vals_b), 4, ""); + + #undef TEST_FUNC_AND_COMMA + #undef TEST_FUNC +} + #undef DT_DRV_COMPAT #define DT_DRV_COMPAT vnd_i2c_mux_controller ZTEST(devicetree_api, test_gparent)