Skip to content

Commit 15922b5

Browse files
committed
scripts: dts: edtlib: Handle *-map property
This change introduces generating definitions corresponding to `*-map` property, which was currently discarded. For `*-map` properties are made able to be treated as a variation of phandle-array, assign sequential cell names for each group of specifiers (child_specifier_0, child_specifier_1, ..., parent_specifier_0, ...). The `*-map` data is like a two-dimensional array, so it is difficult to handle with the existing APIs, so we will also provide new APIs. Signed-off-by: TOKITA Hiroshi <[email protected]>
1 parent 04031f3 commit 15922b5

File tree

4 files changed

+437
-0
lines changed

4 files changed

+437
-0
lines changed

include/zephyr/devicetree.h

+1
Original file line numberDiff line numberDiff line change
@@ -5343,5 +5343,6 @@
53435343
#include <zephyr/devicetree/reset.h>
53445344
#include <zephyr/devicetree/mbox.h>
53455345
#include <zephyr/devicetree/port-endpoint.h>
5346+
#include <zephyr/devicetree/map.h>
53465347

53475348
#endif /* ZEPHYR_INCLUDE_DEVICETREE_H_ */

include/zephyr/devicetree/map.h

+273
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
/*
2+
* Copyright (c) 2025 TOKITA Hiroshi
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_INCLUDE_DEVICETREE_MAP_H_
8+
#define ZEPHYR_INCLUDE_DEVICETREE_MAP_H_
9+
10+
#ifdef __cplusplus
11+
extern "C" {
12+
#endif
13+
14+
/**
15+
* @defgroup devicetree-map Devicetree Map API
16+
*
17+
* @brief Helper macros for handling map properties.
18+
*
19+
* This module provides helper macros that facilitate interrupt mapping and
20+
* specifier mapping based on DeviceTree specifications. It enables the extraction
21+
* and interpretation of mapping data represented as phandle-arrays.
22+
*
23+
* In a typical DeviceTree fragment, properties ending with "-map" specify:
24+
* - The child specifier to be mapped.
25+
* - The parent node (phandle) to which the mapping applies.
26+
* - The parent specifier associated with the mapping.
27+
*
28+
* For example, when the following DeviceTree snippet are defined:
29+
*
30+
* @code{.dts}
31+
* n: node {
32+
* gpio-map = <0 1 &gpio0 2 3>, <4 5 &gpio0 6 7>;
33+
* };
34+
* @endcode
35+
*
36+
* In the first mapping entry:
37+
* - `0 1` are the child specifiers.
38+
* - &gpio0 is the parent node.
39+
* - `2 3` are the parent specifiers.
40+
*
41+
* Since map properties are implemented as phandle-arrays, macros such as
42+
* DT_PHANDLE_BY_IDX() and DT_PHA_BY_IDX() can be used to access individual elements.
43+
*
44+
* Both child and parent specifiers are treated as cells in a phandle-array.
45+
* By default, each group of specifiers is given a sequential cell name
46+
* (child_specifier_0, child_specifier_1, ..., parent_specifier_0, ...).
47+
*
48+
* If cell names are specified in dt-bindings, they will be used for the child specifier cell names.
49+
* Parent specifiers always use the default naming convention.
50+
*
51+
* Example usage:
52+
*
53+
* A mapping entry is a phandle-array whose elements can be referenced as follows:
54+
* - Child specifiers can be accessed via names such as `child_specifier_0`,
55+
* `child_specifier_1`, ...
56+
* - The parent node is accessed via DT_PHANDLE_BY_IDX().
57+
* - Parent specifiers are accessed via names such as `parent_specifier_0`,
58+
* `parent_specifier_1`, ...
59+
*
60+
* @code{.c}
61+
* int cspec_0 = DT_PHA_BY_IDX(DT_NODELABEL(n), gpio_map, 0, child_specifier_0); // 0
62+
* int cspec_1 = DT_PHA_BY_IDX(DT_NODELABEL(n), gpio_map, 0, child_specifier_1); // 1
63+
* const struct device *parent =
64+
* device_get_binding(DT_PHANDLE_BY_IDX(DT_NODELABEL(n), gpio_map, 0)); // &gpio0
65+
* int pspec_0 = DT_PHA_BY_IDX(DT_NODELABEL(n), gpio_map, 0, parent_specifier_0); // 2
66+
* int pspec_1 = DT_PHA_BY_IDX(DT_NODELABEL(n), gpio_map, 0, parent_specifier_1); // 3
67+
* @endcode
68+
*
69+
* The map helper API also provides the following macros for convenient access to
70+
* specific parts of a mapping entry:
71+
* - DT_MAP_CHILD_SPECIFIER_ARGS()
72+
* - DT_MAP_PARENT_SPECIFIER_ARGS()
73+
* - DT_MAP_PARENT_ARG()
74+
*
75+
* These macros extract, respectively, the child specifier arguments, the parent specifier
76+
* arguments, and the parent node argument from a mapping element identified by its node ID,
77+
* property name, and index.
78+
*
79+
* For instance:
80+
*
81+
* @code{.c}
82+
* #define SRC_AND_DST(node_id, prop, idx) \
83+
* { GET_ARG_N(1, DT_MAP_CHILD_SPECIFIER_ARGS(node_id, prop, idx)), \
84+
* GET_ARG_N(1, DT_MAP_PARENT_SPECIFIER_ARGS(node_id, prop, idx)) }
85+
*
86+
* int src_and_dst[2][] = {
87+
* DT_FOREACH_PROP_ELEM_SEP(DT_NODELABEL(n), gpio_map, SRC_AND_DST, (,))
88+
* };
89+
* @endcode
90+
*
91+
* The above expansion yields:
92+
*
93+
* @code{.c}
94+
* int src_and_dst[2][] = {{0, 2}, {4, 6}};
95+
* @endcode
96+
*
97+
* @ingroup devicetree
98+
* @{
99+
*/
100+
101+
/**
102+
* @brief Extracts a specified range of arguments.
103+
*
104+
* This helper macro first skips a given number of arguments and then selects
105+
* the first @p len arguments from the remaining list.
106+
*
107+
* @param start The number of arguments to skip.
108+
* @param len The number of arguments to extract after skipping.
109+
* @param ... The list of input arguments.
110+
*/
111+
#define DT_MAP_HELPER_DO_ARGS_RANGE(start, len, ...) \
112+
GET_ARGS_FIRST_N(len, GET_ARGS_LESS_N(start, __VA_ARGS__))
113+
114+
/**
115+
* @brief Extracts a range of mapping arguments for a specific field.
116+
*
117+
* This macro concatenates the field name with the appropriate suffixes to determine
118+
* the starting index and length of the arguments for a map entry, and then extracts
119+
* those arguments.
120+
*
121+
* @param name The mapping field name (e.g., CHILD_SPECIFIER, PARENT).
122+
* @param node_id The node identifier.
123+
* @param prop The property name in lowercase and underscores.
124+
* @param idx The index of the mapping entry.
125+
* @param ... Additional arguments corresponding to the mapping entry.
126+
*/
127+
#define DT_MAP_HELPER_ARGS_RANGE(name, node_id, prop, idx, ...) \
128+
DT_MAP_HELPER_DO_ARGS_RANGE(DT_CAT3(DT_MAP_, name, _IDX)(node_id, prop, idx), \
129+
DT_CAT3(DT_MAP_, name, _LEN)(node_id, prop, idx), __VA_ARGS__)
130+
131+
/**
132+
* @brief Extracts arguments by skipping a specified number of elements.
133+
*
134+
* This helper macro applies GET_ARGS_LESS_N to the provided argument list.
135+
*
136+
* @param N The number of arguments to skip.
137+
* @param ... The list of input arguments.
138+
*/
139+
#define DT_MAP_HELPER_ARGS_LESS_N(N, ...) GET_ARGS_LESS_N(N, __VA_ARGS__)
140+
141+
/**
142+
* @brief Retrieves the mapping entry at the specified index.
143+
*
144+
* @param node_id The node identifier.
145+
* @param prop The property name in lowercase with underscores.
146+
* @param idx The mapping entry index.
147+
* @return The mapping entry as a list of comma-separated values.
148+
*/
149+
#define DT_MAP_IDX(node_id, prop, idx) DT_CAT5(node_id, _P_, prop, _MAP_IDX_, idx)
150+
151+
/**
152+
* @brief Returns the number of mapping entries for the given property.
153+
*
154+
* @param node_id The node identifier.
155+
* @param prop The property name in lowercase with underscores.
156+
* @return The total count of mapping entries.
157+
*/
158+
#define DT_MAP_LEN(node_id, prop) DT_CAT4(node_id, _P_, prop, _MAP_LEN)
159+
160+
/**
161+
* @brief Retrieves the starting index of the child specifier cell within a mapping entry.
162+
*
163+
* @param node_id The node identifier.
164+
* @param prop The property name.
165+
* @param idx The mapping entry index.
166+
* @return The starting index of the child specifier cell.
167+
*/
168+
#define DT_MAP_CHILD_SPECIFIER_IDX(node_id, prop, idx) \
169+
DT_CAT7(node_id, _P_, prop, _MAP_IDX_, idx, _, CHILD_SPECIFIER_IDX)
170+
171+
/**
172+
* @brief Returns the length (number of cells) of the child specifier within a mapping entry.
173+
*
174+
* @param node_id The node identifier.
175+
* @param prop The property name.
176+
* @param idx The mapping entry index.
177+
* @return The length (in cells) of the child specifier.
178+
*/
179+
#define DT_MAP_CHILD_SPECIFIER_LEN(node_id, prop, idx) \
180+
DT_CAT7(node_id, _P_, prop, _MAP_IDX_, idx, _, CHILD_SPECIFIER_LEN)
181+
182+
/**
183+
* @brief Retrieves the starting index of the parent cell in a mapping entry.
184+
*
185+
* @param node_id The node identifier.
186+
* @param prop The property name.
187+
* @param idx The mapping entry index.
188+
* @return The starting index of the parent cell.
189+
*/
190+
#define DT_MAP_PARENT_IDX(node_id, prop, idx) \
191+
DT_CAT7(node_id, _P_, prop, _MAP_IDX_, idx, _, PARENT_IDX)
192+
193+
/**
194+
* @brief Returns the length (number of cells) of the parent cell in a mapping entry.
195+
*
196+
* @param node_id The node identifier.
197+
* @param prop The property name.
198+
* @param idx The mapping entry index.
199+
* @return The length (in cells) of the parent cell.
200+
*/
201+
#define DT_MAP_PARENT_LEN(node_id, prop, idx) \
202+
DT_CAT7(node_id, _P_, prop, _MAP_IDX_, idx, _, PARENT_LEN)
203+
204+
/**
205+
* @brief Retrieves the starting index of the parent specifier cell within a mapping entry.
206+
*
207+
* @param node_id The node identifier.
208+
* @param prop The property name.
209+
* @param idx The mapping entry index.
210+
* @return The starting index of the parent specifier cell.
211+
*/
212+
#define DT_MAP_PARENT_SPECIFIER_IDX(node_id, prop, idx) \
213+
DT_CAT7(node_id, _P_, prop, _MAP_IDX_, idx, _, PARENT_SPECIFIER_IDX)
214+
215+
/**
216+
* @brief Returns the length (number of cells) of the parent specifier in a mapping entry.
217+
*
218+
* @param node_id The node identifier.
219+
* @param prop The property name.
220+
* @param idx The mapping entry index.
221+
* @return The length (in cells) of the parent specifier.
222+
*/
223+
#define DT_MAP_PARENT_SPECIFIER_LEN(node_id, prop, idx) \
224+
DT_CAT7(node_id, _P_, prop, _MAP_IDX_, idx, _, PARENT_SPECIFIER_LEN)
225+
226+
/**
227+
* @brief Extracts the child specifier arguments from a mapping entry.
228+
*
229+
* This macro returns the comma-separated list of arguments for the child specifier.
230+
*
231+
* @param node_id The node identifier.
232+
* @param prop The property name in lowercase with underscores.
233+
* @param idx The mapping entry index.
234+
* @return The child specifier arguments.
235+
*/
236+
#define DT_MAP_CHILD_SPECIFIER_ARGS(node_id, prop, idx) \
237+
DT_MAP_HELPER_ARGS_RANGE(CHILD_SPECIFIER, node_id, prop, idx, \
238+
DT_MAP_IDX(node_id, prop, idx))
239+
240+
/**
241+
* @brief Extracts the parent node argument from a mapping entry.
242+
*
243+
* @param node_id The node identifier.
244+
* @param prop The property name in lowercase with underscores.
245+
* @param idx The mapping entry index.
246+
* @return The parent node argument.
247+
*/
248+
#define DT_MAP_PARENT_ARG(node_id, prop, idx) \
249+
DT_MAP_HELPER_ARGS_RANGE(PARENT, node_id, prop, idx, DT_MAP_IDX(node_id, prop, idx))
250+
251+
/**
252+
* @brief Extracts the parent specifier arguments from a mapping entry.
253+
*
254+
* This macro returns the comma-separated list of arguments for the parent specifier.
255+
*
256+
* @param node_id The node identifier.
257+
* @param prop The property name in lowercase with underscores.
258+
* @param idx The mapping entry index.
259+
* @return The parent specifier arguments.
260+
*/
261+
#define DT_MAP_PARENT_SPECIFIER_ARGS(node_id, prop, idx) \
262+
DT_MAP_HELPER_ARGS_RANGE(PARENT_SPECIFIER, node_id, prop, idx, \
263+
DT_MAP_IDX(node_id, prop, idx))
264+
265+
/**
266+
* @}
267+
*/
268+
269+
#ifdef __cplusplus
270+
}
271+
#endif
272+
273+
#endif /* ZEPHYR_INCLUDE_DEVICETREE_MAP_H_ */

scripts/dts/gen_defines.py

+67
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ def write_special_props(node: edtlib.Node) -> None:
288288
write_pinctrls(node)
289289
write_fixed_partitions(node)
290290
write_gpio_hogs(node)
291+
write_maps(node)
291292

292293

293294
def write_ranges(node: edtlib.Node) -> None:
@@ -579,6 +580,72 @@ def write_gpio_hogs(node: edtlib.Node) -> None:
579580
out_dt_define(macro, val)
580581

581582

583+
def write_maps(node: edtlib.Node) -> None:
584+
if len(node.maps) == 0:
585+
return
586+
587+
out_comment("Map properties:")
588+
589+
basename = str2ident(node.maps[0].basename)
590+
macro = f"{node.z_path_id}_P_{basename}_map"
591+
macro2val = {}
592+
593+
for i, cd in enumerate(node.maps):
594+
if basename != str2ident(cd.basename):
595+
err(f"Map basename mismatch: {basename} != {str2ident(cd.basename)}")
596+
597+
macro2val.update(controller_and_data_macros(cd, i, macro))
598+
599+
prop_id = f"{basename}_map"
600+
plen = len(node.maps)
601+
# DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM
602+
macro2val[f"{macro}_FOREACH_PROP_ELEM(fn)"] = ' \\\n\t'.join(
603+
f'fn(DT_{node.z_path_id}, {prop_id}, {i})' for i in range(plen)
604+
)
605+
606+
# DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_SEP
607+
macro2val[f"{macro}_FOREACH_PROP_ELEM_SEP(fn, sep)"] = ' DT_DEBRACKET_INTERNAL sep \\\n\t'.join(
608+
f'fn(DT_{node.z_path_id}, {prop_id}, {i})' for i in range(plen)
609+
)
610+
611+
# DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_VARGS
612+
macro2val[f"{macro}_FOREACH_PROP_ELEM_VARGS(fn, ...)"] = ' \\\n\t'.join(
613+
f'fn(DT_{node.z_path_id}, {prop_id}, {i}, __VA_ARGS__)' for i in range(plen)
614+
)
615+
616+
# DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_SEP_VARGS
617+
macro2val[f"{macro}_FOREACH_PROP_ELEM_SEP_VARGS(fn, sep, ...)"] = (
618+
' DT_DEBRACKET_INTERNAL sep \\\n\t'.join(
619+
f'fn(DT_{node.z_path_id}, {prop_id}, {i}, __VA_ARGS__)' for i in range(plen)
620+
)
621+
)
622+
623+
macro2val[f"{macro}_LEN"] = plen
624+
macro2val[f"{macro}_EXISTS"] = 1
625+
626+
for i, cd in enumerate(node.maps):
627+
parent_specifier_len = len([k for k in cd.data if k.startswith('parent_specifier_')])
628+
child_specifiers = list(cd.data.values())[:-parent_specifier_len]
629+
parent_specifiers = list(cd.data.values())[-parent_specifier_len:]
630+
child_specifier_len = len(child_specifiers)
631+
632+
args = []
633+
args.extend([str(v) for v in child_specifiers])
634+
args.extend(["DT_" + node_z_path_id(cd.controller)])
635+
args.extend([str(v) for v in parent_specifiers])
636+
637+
macro2val[f"{macro}_MAP_IDX_{i}"] = ", ".join(args)
638+
macro2val[f"{macro}_MAP_IDX_{i}_CHILD_SPECIFIER_IDX"] = 0
639+
macro2val[f"{macro}_MAP_IDX_{i}_CHILD_SPECIFIER_LEN"] = child_specifier_len
640+
macro2val[f"{macro}_MAP_IDX_{i}_PARENT_IDX"] = child_specifier_len
641+
macro2val[f"{macro}_MAP_IDX_{i}_PARENT_LEN"] = 1
642+
macro2val[f"{macro}_MAP_IDX_{i}_PARENT_SPECIFIER_IDX"] = child_specifier_len + 1
643+
macro2val[f"{macro}_MAP_IDX_{i}_PARENT_SPECIFIER_LEN"] = parent_specifier_len
644+
645+
for mc, val in macro2val.items():
646+
out_dt_define(mc, val)
647+
648+
582649
def write_vanilla_props(node: edtlib.Node) -> None:
583650
# Writes macros for any and all properties defined in the
584651
# "properties" section of the binding for the node.

0 commit comments

Comments
 (0)