diff --git a/cmake/modules/extensions.cmake b/cmake/modules/extensions.cmake index d425253a5555..4368dc19b7c9 100644 --- a/cmake/modules/extensions.cmake +++ b/cmake/modules/extensions.cmake @@ -4362,7 +4362,7 @@ function(dt_has_chosen var) endif() endforeach() - get_target_property(exists devicetree_target "DT_CHOSEN|${DT_CHOSEN_PROPERTY}") + get_target_property(exists devicetree_target "DT_CHOSEN|${DT_CHOSEN_PROPERTY}-0") if(${exists} STREQUAL exists-NOTFOUND) set(${var} FALSE PARENT_SCOPE) @@ -4374,7 +4374,7 @@ endfunction() # Usage: # dt_chosen( PROPERTY ) # -# Get a node path for a /chosen node property. +# Get path of the 1st node in a /chosen node property. # # The node's path will be returned in the parameter. The # variable will be left undefined if the chosen node does not exist. @@ -4397,7 +4397,7 @@ function(dt_chosen var) endif() endforeach() - get_target_property(${var} devicetree_target "DT_CHOSEN|${DT_CHOSEN_PROPERTY}") + get_target_property(${var} devicetree_target "DT_CHOSEN|${DT_CHOSEN_PROPERTY}-0") if(${${var}} STREQUAL ${var}-NOTFOUND) set(${var} PARENT_SCOPE) diff --git a/include/zephyr/devicetree.h b/include/zephyr/devicetree.h index 70ee27f6b9ef..dddf13b13b16 100644 --- a/include/zephyr/devicetree.h +++ b/include/zephyr/devicetree.h @@ -2909,9 +2909,9 @@ * This is only valid to call if `DT_HAS_CHOSEN(prop)` is 1. * @param prop lowercase-and-underscores property name for * the /chosen node - * @return a node identifier for the chosen node property + * @return a node identifier for the 1st node of the chosen property */ -#define DT_CHOSEN(prop) DT_CAT(DT_CHOSEN_, prop) +#define DT_CHOSEN(prop) DT_CAT3(DT_CHOSEN_, prop, _0) /** * @brief Test if the devicetree has a `/chosen` node @@ -2919,8 +2919,63 @@ * @return 1 if the chosen property exists and refers to a node, * 0 otherwise */ -#define DT_HAS_CHOSEN(prop) IS_ENABLED(DT_CAT3(DT_CHOSEN_, prop, _EXISTS)) +#define DT_HAS_CHOSEN(prop) IS_ENABLED(DT_CAT4(DT_CHOSEN_, prop, _0, _EXISTS)) +/** + * @brief Get a node identifier for a `/chosen` node property at an index. + * + * When a `/chosen` node's value at a logical index contains a phandle, this + * macro returns a node identifier for the node with that phandle. + * + * Example devicetree fragment: + * + * @code{.dts} + * chosen { + * zephyr,foo = <&n2 &n3>; + * }; + * + * n2: node-2 { ... }; + * n3: node-3 { ... }; + * @endcode + * + * Above, `zephyr,foo` chosen property has two elements: + * + * - index 0 has phandle `&n2`, which is `node-2`'s phandle + * - index 1 has phandle `&n3`, which is `node-3`'s phandle + * + * Example usage: + * + * @code{.c} + * + * DT_CHOSEN_NODE_BY_IDX(zephyr_foo, 0) // node identifier for node-2 + * DT_CHOSEN_NODE_BY_IDX(zephyr_foo, 1) // node identifier for node-3 + * @endcode + * + * This is only valid to call if `DT_CHOSEN_HAS_NODE_IDX(prop, idx)` is 1. + * @param prop lowercase-and-underscores `/chosen` node property name + * @param idx index into @p prop + * @return node identifier for the node with the phandle at that index + */ +#define DT_CHOSEN_NODE_BY_IDX(prop, idx) DT_CAT3(DT_CHOSEN_, prop, _##idx) + +/** + * @brief Test if @p prop of devicetree `/chosen` node has phandle of index @p idx + * @param prop lowercase-and-underscores devicetree property + * @param idx index into @p prop + * @return 1 if phandle of index idx of chosen property exists and refers to a node, + * 0 otherwise + */ +#define DT_CHOSEN_HAS_NODE_IDX(prop, idx) IS_ENABLED(DT_CAT4(DT_CHOSEN_, prop, _##idx, _EXISTS)) + +/** + * @brief Get number of phandles assigned to @p prop of devicetree `/chosen` node + * @param prop lowercase-and-underscores devicetree property + * @return number of phandles of `/chosen` node property + */ +#define DT_CHOSEN_NODES_NBR(prop) \ + COND_CODE_1(DT_HAS_CHOSEN(prop), \ + (DT_CAT3(DT_CHOSEN_, prop, _NODES_NBR)), \ + (0)) /** * @} */ diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py index 99aac7d720ce..af71dd76bbe5 100755 --- a/scripts/dts/gen_defines.py +++ b/scripts/dts/gen_defines.py @@ -881,9 +881,12 @@ def write_chosen(edt: edtlib.EDT): out_comment("Chosen nodes\n") chosen = {} - for name, node in edt.chosen_nodes.items(): - chosen[f"DT_CHOSEN_{str2ident(name)}"] = f"DT_{node.z_path_id}" - chosen[f"DT_CHOSEN_{str2ident(name)}_EXISTS"] = 1 + for name, prop_nodes_list in edt.chosen_props_nodes.items(): + chosen[f"DT_CHOSEN_{str2ident(name)}_NODES_NBR"] = len(prop_nodes_list) + for node_index, node in enumerate(prop_nodes_list): + chosen[f"DT_CHOSEN_{str2ident(name)}_{node_index}"] = f"DT_{node.z_path_id}" + chosen[f"DT_CHOSEN_{str2ident(name)}_{node_index}_EXISTS"] = 1 + max_len = max(map(len, chosen), default=0) for macro, value in chosen.items(): out_define(macro, value, width=max_len) diff --git a/scripts/dts/gen_dts_cmake.py b/scripts/dts/gen_dts_cmake.py index 47fd30b67b93..b8334459da09 100755 --- a/scripts/dts/gen_dts_cmake.py +++ b/scripts/dts/gen_dts_cmake.py @@ -100,10 +100,10 @@ def main(): # macros.bnf for C macros. cmake_props = [] - chosen_nodes = edt.chosen_nodes - for node in chosen_nodes: - path = chosen_nodes[node].path - cmake_props.append(f'"DT_CHOSEN|{node}" "{path}"') + chosen_nodes = edt.chosen_props_nodes + for prop_name in chosen_nodes: + for index, node in enumerate(chosen_nodes[prop_name]): + cmake_props.append(f'"DT_CHOSEN|{prop_name}-{index}" "{node.path}"') # The separate loop over edt.nodes here is meant to keep # all of the alias-related properties in one place. diff --git a/scripts/dts/python-devicetree/src/devicetree/edtlib.py b/scripts/dts/python-devicetree/src/devicetree/edtlib.py index f036bf1f1c5f..f46b90da13b0 100644 --- a/scripts/dts/python-devicetree/src/devicetree/edtlib.py +++ b/scripts/dts/python-devicetree/src/devicetree/edtlib.py @@ -1902,11 +1902,11 @@ class EDT: dep_ord2node: A dict that maps an ordinal to the node with that dependency ordinal. - chosen_nodes: + chosen_props_nodes: A dict that maps the properties defined on the devicetree's /chosen - node to their values. 'chosen' is indexed by property name (a string), - and values are converted to Node objects. Note that properties of the - /chosen node which can't be converted to a Node are not included in + node to a list of nodes. 'chosen' is indexed by property name (a string), + and values are converted to a list of Node objects. Note that properties + of the /chosen node which can't be converted to nodes are not included in the value. dts_path: @@ -2048,31 +2048,40 @@ def get_node(self, path: str) -> Node: _err(e) @property - def chosen_nodes(self) -> dict[str, Node]: - ret: dict[str, Node] = {} + def chosen_props_nodes(self) -> dict[str, list[Node]]: + ret: dict[str, list[Node]] = {} try: chosen = self._dt.get_node("/chosen") except DTError: return ret - for name, prop in chosen.props.items(): + for prop_name, prop in chosen.props.items(): try: - node = prop.to_path() + if prop.type == Type.PHANDLES: + ret[prop_name] = [self._node2enode[node] for node in prop.to_nodes()] + else: + ret[prop_name] = [self._node2enode[prop.to_path()]] except DTError: - # DTS value is not phandle or string, or path doesn't exist + # DTS value is not phandle, phandles, or string, or path doesn't exist continue - ret[name] = self._node2enode[node] - return ret + def chosen_prop_nodes(self, name: str) -> Optional[list[Node]]: + """ + Returns a list of Nodes pointed at by the property named 'name' in /chosen, or + None if the property is missing + """ + return self.chosen_props_nodes.get(name) + def chosen_node(self, name: str) -> Optional[Node]: """ Returns the Node pointed at by the property named 'name' in /chosen, or None if the property is missing """ - return self.chosen_nodes.get(name) + nodes = self.chosen_props_nodes.get(name) + return nodes[0] if nodes else None @property def dts_source(self) -> str: