From 474d0682fad7019ad6ff5e507aca782903036ad1 Mon Sep 17 00:00:00 2001 From: Pieter De Gendt Date: Sun, 9 Nov 2025 09:51:28 +0100 Subject: [PATCH 1/5] tests: lib: devicetree: api: Format app.overlay Fix dts formatting issues with dts-linter. Signed-off-by: Pieter De Gendt --- tests/lib/devicetree/api/app.overlay | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/lib/devicetree/api/app.overlay b/tests/lib/devicetree/api/app.overlay index 0404be3974d41..c97746001a339 100644 --- a/tests/lib/devicetree/api/app.overlay +++ b/tests/lib/devicetree/api/app.overlay @@ -38,9 +38,13 @@ test_pinctrl: pin-controller { compatible = "vnd,pinctrl"; + test_pincfg_a: pincfg-a {}; + test_pincfg_b: pincfg-b {}; + test_pincfg_c: pincfg-c {}; + test_pincfg_d: pincfg-d {}; }; @@ -252,20 +256,24 @@ test_i2c_mux: i2c-mux@12 { compatible = "vnd,i2c-mux"; reg = <0x12>; + i2c-mux-ctlr-1 { compatible = "vnd,i2c-mux-controller"; #address-cells = <1>; #size-cells = <0>; + test_muxed_i2c_dev_1: muxed-i2c-dev@10 { compatible = "vnd,i2c-device"; status = "disabled"; reg = <0x10>; }; }; + i2c-mux-ctlr-2 { compatible = "vnd,i2c-mux-controller"; #address-cells = <1>; #size-cells = <0>; + test_muxed_i2c_dev_2: muxed-i2c-dev@10 { compatible = "vnd,i2c-device"; status = "disabled"; @@ -655,10 +663,12 @@ val = <0>; status = "okay"; }; + test_child_b: child-b { val = <1>; status = "okay"; }; + test_child_c: child-c { val = <2>; status = "disabled"; @@ -852,6 +862,7 @@ flash@20000000 { compatible = "soc-nv-flash"; reg = <0x20000000 0x100>; + partitions { compatible = "fixed-partitions"; #address-cells = <1>; From 3de20955f8c07519dfa7fa0421bcb2f967f37227 Mon Sep 17 00:00:00 2001 From: Pieter De Gendt Date: Sat, 8 Nov 2025 11:44:07 +0100 Subject: [PATCH 2/5] devicetree: Add DT_CHILD_BY_UNIT_ADDR Allow fetching child node identifiers by unit address. Signed-off-by: Pieter De Gendt --- doc/build/dts/macros.bnf | 13 ++++++++++ include/zephyr/devicetree.h | 52 +++++++++++++++++++++++++++++++++++++ scripts/dts/gen_defines.py | 17 ++++++++++++ 3 files changed, 82 insertions(+) diff --git a/doc/build/dts/macros.bnf b/doc/build/dts/macros.bnf index f5e676f8f44a3..5ae2ced5e2903 100644 --- a/doc/build/dts/macros.bnf +++ b/doc/build/dts/macros.bnf @@ -77,6 +77,8 @@ node-macro =/ %s"DT_N" path-id %s"_P_" prop-id %s"_FOREACH_PROP_ELEM_SEP_VARGS" ; These are used by DT_CHILD_NUM and DT_CHILD_NUM_STATUS_OKAY macros node-macro =/ %s"DT_N" path-id %s"_CHILD_NUM" node-macro =/ %s"DT_N" path-id %s"_CHILD_NUM_STATUS_OKAY" +; This is used by DT_CHILD_BY_UNIT_ADDR +node-macro =/ %s"DT_N" path-id %s"_CHILD_UNIT_ADDR_" unit-addr ; These are used internally by DT_FOREACH_CHILD, which iterates over ; each child node. node-macro =/ %s"DT_N" path-id %s"_FOREACH_CHILD" @@ -394,6 +396,17 @@ other-macro =/ %s"DT_COMPAT_" dt-name %s"_LABEL_" dt-name alternate-id = ( %s"ALIAS" / %s"NODELABEL" ) dt-name alternate-id =/ %s"INST_" 1*DIGIT "_" dt-name +; -------------------------------------------------------------------- +; unit-addr: a unit address token for child lookup helpers +; +; Children that have a unique unit address get helper macros using either +; decimal or lowercase hexadecimal forms of that address. +unit-addr = 1*DIGIT +unit-addr =/ %s"0x" 1*hexdig-lower + +; Lowercase hexadecimal digit. +hexdig-lower = DIGIT / %s"a" / %s"b" / %s"c" / %s"d" / %s"e" / %s"f" + ; -------------------------------------------------------------------- ; miscellaneous helper definitions diff --git a/include/zephyr/devicetree.h b/include/zephyr/devicetree.h index e10cb7c8c5fec..8621cec75e0b9 100644 --- a/include/zephyr/devicetree.h +++ b/include/zephyr/devicetree.h @@ -435,6 +435,42 @@ */ #define DT_CHILD(node_id, child) UTIL_CAT(node_id, DT_S_PREFIX(child)) +/** + * @brief Get a node identifier for a child node with a matching unit address + * + * @note Only works for children with unique unit addresses. + * + * Example devicetree fragment: + * + * @code{.dts} + * / { + * soc-label: soc { + * serial1: serial@40001000 { + * status = "okay"; + * current-speed = <115200>; + * ... + * }; + * }; + * }; + * @endcode + * + * Example usage with DT_PROP() to get the status of the + * `serial@40001000` node: + * + * @code{.c} + * #define SOC_NODE DT_NODELABEL(soc_label) + * DT_PROP(DT_CHILD_BY_UNIT_ADDR(SOC_NODE, 0x40001000), status) // "okay" + * @endcode + * + * @param node_id node identifier + * @param addr Unit address for the child node. Can be decimal or + * hexadecimal (lowercase and prefixed with 0x) + * + * @return node identifier for the child node with the specified unit address + */ +#define DT_CHILD_BY_UNIT_ADDR(node_id, addr) \ + DT_CAT3(node_id, _CHILD_UNIT_ADDR_, addr) + /** * @brief Get a node identifier for a status `okay` node with a compatible * @@ -4066,6 +4102,22 @@ #define DT_INST_CHILD(inst, child) \ DT_CHILD(DT_DRV_INST(inst), child) +/** + * @brief Get a node identifier for a child node with a matching unit address of DT_DRV_INST(inst) + * + * @note Only works for children with unique unit addresses. + * + * @param inst instance number + * @param addr Unit address for the child node. Can be decimal or + * hexadecimal (lowercase and prefixed with 0x) + * + * @return node identifier for the child node with the specified unit address + * + * @see DT_CHILD_BY_UNIT_ADDR + */ +#define DT_INST_CHILD_BY_UNIT_ADDR(inst, addr) \ + DT_CHILD_BY_UNIT_ADDR(DT_DRV_INST(inst), addr) + /** * @brief Get the number of child nodes of a given node * diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py index e3913dd585b79..d31e80051b89b 100755 --- a/scripts/dts/gen_defines.py +++ b/scripts/dts/gen_defines.py @@ -484,6 +484,23 @@ def write_children(node: edtlib.Node) -> None: out_dt_define(f"{node.z_path_id}_CHILD_NUM_STATUS_OKAY", ok_nodes_num) + child_unit_addrs = {} + for child in node.children.values(): + # Provide a way to query child nodes + if (addr := child.unit_addr) is not None: + child_unit_addrs.setdefault(addr, []).append(child) + + for addr, children in child_unit_addrs.items(): + if len(children) != 1: + # Duplicate unit addresses for different children, skip + continue + + # Decimal and hexadecimal variants + out_dt_define(f"{node.z_path_id}_CHILD_UNIT_ADDR_{addr}", f"DT_{children[0].z_path_id}") + out_dt_define( + f"{node.z_path_id}_CHILD_UNIT_ADDR_{hex(addr)}", f"DT_{children[0].z_path_id}" + ) + out_dt_define(f"{node.z_path_id}_FOREACH_CHILD(fn)", " ".join(f"fn(DT_{child.z_path_id})" for child in node.children.values())) From 29d6408bd97960bc07b5b22bcde55e12b4b33657 Mon Sep 17 00:00:00 2001 From: Pieter De Gendt Date: Sat, 8 Nov 2025 11:45:55 +0100 Subject: [PATCH 3/5] tests: lib: devicetree: api: Add test cases for DT_CHILD_BY_UNIT_ADDR Introduce test cases to the devicetree API test suite for DT_CHILD_BY_UNIT_ADDR and DT_INST_CHILD_BY_UNIT_ADDR. Signed-off-by: Pieter De Gendt --- dts/bindings/test/vnd,child-bindings.yaml | 3 +++ tests/lib/devicetree/api/app.overlay | 11 ++++++++--- tests/lib/devicetree/api/src/main.c | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/dts/bindings/test/vnd,child-bindings.yaml b/dts/bindings/test/vnd,child-bindings.yaml index 3b5e725f1a5f4..da565c50ffa17 100644 --- a/dts/bindings/test/vnd,child-bindings.yaml +++ b/dts/bindings/test/vnd,child-bindings.yaml @@ -10,6 +10,9 @@ include: [base.yaml] child-binding: description: Test child binding properties: + reg: + type: array + required: true val: type: int required: true diff --git a/tests/lib/devicetree/api/app.overlay b/tests/lib/devicetree/api/app.overlay index c97746001a339..4df236d10863c 100644 --- a/tests/lib/devicetree/api/app.overlay +++ b/tests/lib/devicetree/api/app.overlay @@ -658,18 +658,23 @@ /* there should only be one of these */ test_children: test-children { compatible = "vnd,child-bindings"; + #address-cells = <1>; + #size-cells = <0>; - test_child_a: child-a { + test_child_a: child@a { + reg = <0xa>; val = <0>; status = "okay"; }; - test_child_b: child-b { + test_child_b: child@b { + reg = <0xb>; val = <1>; status = "okay"; }; - test_child_c: child-c { + test_child_c: child@c { + reg = <0xc>; val = <2>; status = "disabled"; }; diff --git a/tests/lib/devicetree/api/src/main.c b/tests/lib/devicetree/api/src/main.c index 4f22ed9439bb4..7e47020e06be6 100644 --- a/tests/lib/devicetree/api/src/main.c +++ b/tests/lib/devicetree/api/src/main.c @@ -2514,6 +2514,26 @@ ZTEST(devicetree_api, test_children) zassert_equal(DT_PROP(DT_INST_CHILD(0, child_a), val), 0, ""); zassert_equal(DT_PROP(DT_INST_CHILD(0, child_b), val), 1, ""); zassert_equal(DT_PROP(DT_INST_CHILD(0, child_c), val), 2, ""); + + zassert_equal(DT_PROP(DT_CHILD_BY_UNIT_ADDR(DT_NODELABEL(test_children), 0xa), + val), 0, ""); + zassert_equal(DT_PROP(DT_CHILD_BY_UNIT_ADDR(DT_NODELABEL(test_children), 10), + val), 0, ""); + zassert_equal(DT_PROP(DT_CHILD_BY_UNIT_ADDR(DT_NODELABEL(test_children), 0xb), + val), 1, ""); + zassert_equal(DT_PROP(DT_CHILD_BY_UNIT_ADDR(DT_NODELABEL(test_children), 11), + val), 1, ""); + zassert_equal(DT_PROP(DT_CHILD_BY_UNIT_ADDR(DT_NODELABEL(test_children), 0xc), + val), 2, ""); + zassert_equal(DT_PROP(DT_CHILD_BY_UNIT_ADDR(DT_NODELABEL(test_children), 12), + val), 2, ""); + + zassert_equal(DT_PROP(DT_INST_CHILD_BY_UNIT_ADDR(0, 0xa), val), 0, ""); + zassert_equal(DT_PROP(DT_INST_CHILD_BY_UNIT_ADDR(0, 10), val), 0, ""); + zassert_equal(DT_PROP(DT_INST_CHILD_BY_UNIT_ADDR(0, 0xb), val), 1, ""); + zassert_equal(DT_PROP(DT_INST_CHILD_BY_UNIT_ADDR(0, 11), val), 1, ""); + zassert_equal(DT_PROP(DT_INST_CHILD_BY_UNIT_ADDR(0, 0xc), val), 2, ""); + zassert_equal(DT_PROP(DT_INST_CHILD_BY_UNIT_ADDR(0, 12), val), 2, ""); } #undef DT_DRV_COMPAT From c447099da1e660abc94e03413b605559d55c59a3 Mon Sep 17 00:00:00 2001 From: Pieter De Gendt Date: Sat, 8 Nov 2025 12:09:04 +0100 Subject: [PATCH 4/5] include: zephyr: drivers: adc.h: Replace FOREACH with CHILD_BY_UNIT_ADDR The macro to initialise a struct adc_dt_spec instance uses a FOREACH macro, which can be problematic when nesting in other FOREACH macros, for example in driver implementations. Replace it with DT_CHILD_BY_UNIT_ADDR. Signed-off-by: Pieter De Gendt --- include/zephyr/drivers/adc.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/include/zephyr/drivers/adc.h b/include/zephyr/drivers/adc.h index 8334f504ba147..2fe14e0c4f825 100644 --- a/include/zephyr/drivers/adc.h +++ b/include/zephyr/drivers/adc.h @@ -340,10 +340,7 @@ struct adc_dt_spec { } #define ADC_CHANNEL_DT_NODE(ctlr, input) \ - DT_FOREACH_CHILD_VARGS(ctlr, ADC_FOREACH_INPUT, input) - -#define ADC_FOREACH_INPUT(node, input) \ - IF_ENABLED(IS_EQ(DT_REG_ADDR_RAW(node), input), (node)) + DT_CHILD_BY_UNIT_ADDR(ctlr, input) #define ADC_CHANNEL_CFG_FROM_DT_NODE(node_id) \ IF_ENABLED(DT_NODE_EXISTS(node_id), \ From dd22799eca6acf81aeccf1a77ac462ab19f1e61e Mon Sep 17 00:00:00 2001 From: Pieter De Gendt Date: Wed, 12 Nov 2025 10:08:57 +0100 Subject: [PATCH 5/5] docs: releases: 4.4: Add entry for DT_CHILD_BY_UNIT_ADDR Add a DeviceTree section and entries for the new C macros. Signed-off-by: Pieter De Gendt --- doc/releases/release-notes-4.4.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/releases/release-notes-4.4.rst b/doc/releases/release-notes-4.4.rst index 57f3147e570dd..700f21c4f20d4 100644 --- a/doc/releases/release-notes-4.4.rst +++ b/doc/releases/release-notes-4.4.rst @@ -96,6 +96,11 @@ New Samples Same as above, this will also be recomputed at the time of the release. Just link the sample, further details go in the sample documentation itself. +DeviceTree +********** + +* :c:macro:`DT_CHILD_BY_UNIT_ADDR` +* :c:macro:`DT_INST_CHILD_BY_UNIT_ADDR` Libraries / Subsystems **********************