Skip to content

Commit 5b3e050

Browse files
committed
scripts: dts: Add handling for *-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 5b3e050

File tree

4 files changed

+422
-0
lines changed

4 files changed

+422
-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

+263
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
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 Retrieves the mapping entry at the specified index.
133+
*
134+
* @param node_id The node identifier.
135+
* @param prop The property name in lowercase with underscores.
136+
* @param idx The mapping entry index.
137+
* @return The mapping entry as a list of comma-separated values.
138+
*/
139+
#define DT_MAP_IDX(node_id, prop, idx) DT_CAT5(node_id, _P_, prop, _MAP_IDX_, idx)
140+
141+
/**
142+
* @brief Returns the number of mapping entries for the given property.
143+
*
144+
* @param node_id The node identifier.
145+
* @param prop The property name in lowercase with underscores.
146+
* @return The total count of mapping entries.
147+
*/
148+
#define DT_MAP_LEN(node_id, prop) DT_CAT4(node_id, _P_, prop, _MAP_LEN)
149+
150+
/**
151+
* @brief Retrieves the starting index of the child specifier cell within a mapping entry.
152+
*
153+
* @param node_id The node identifier.
154+
* @param prop The property name.
155+
* @param idx The mapping entry index.
156+
* @return The starting index of the child specifier cell.
157+
*/
158+
#define DT_MAP_CHILD_SPECIFIER_IDX(node_id, prop, idx) \
159+
DT_CAT7(node_id, _P_, prop, _MAP_IDX_, idx, _, CHILD_SPECIFIER_IDX)
160+
161+
/**
162+
* @brief Returns the length (number of cells) of the child specifier within a mapping entry.
163+
*
164+
* @param node_id The node identifier.
165+
* @param prop The property name.
166+
* @param idx The mapping entry index.
167+
* @return The length (in cells) of the child specifier.
168+
*/
169+
#define DT_MAP_CHILD_SPECIFIER_LEN(node_id, prop, idx) \
170+
DT_CAT7(node_id, _P_, prop, _MAP_IDX_, idx, _, CHILD_SPECIFIER_LEN)
171+
172+
/**
173+
* @brief Retrieves the starting index of the parent cell in a mapping entry.
174+
*
175+
* @param node_id The node identifier.
176+
* @param prop The property name.
177+
* @param idx The mapping entry index.
178+
* @return The starting index of the parent cell.
179+
*/
180+
#define DT_MAP_PARENT_IDX(node_id, prop, idx) \
181+
DT_CAT7(node_id, _P_, prop, _MAP_IDX_, idx, _, PARENT_IDX)
182+
183+
/**
184+
* @brief Returns the length (number of cells) of the parent cell in a mapping entry.
185+
*
186+
* @param node_id The node identifier.
187+
* @param prop The property name.
188+
* @param idx The mapping entry index.
189+
* @return The length (in cells) of the parent cell.
190+
*/
191+
#define DT_MAP_PARENT_LEN(node_id, prop, idx) \
192+
DT_CAT7(node_id, _P_, prop, _MAP_IDX_, idx, _, PARENT_LEN)
193+
194+
/**
195+
* @brief Retrieves the starting index of the parent specifier cell within a mapping entry.
196+
*
197+
* @param node_id The node identifier.
198+
* @param prop The property name.
199+
* @param idx The mapping entry index.
200+
* @return The starting index of the parent specifier cell.
201+
*/
202+
#define DT_MAP_PARENT_SPECIFIER_IDX(node_id, prop, idx) \
203+
DT_CAT7(node_id, _P_, prop, _MAP_IDX_, idx, _, PARENT_SPECIFIER_IDX)
204+
205+
/**
206+
* @brief Returns the length (number of cells) of the parent specifier in a mapping entry.
207+
*
208+
* @param node_id The node identifier.
209+
* @param prop The property name.
210+
* @param idx The mapping entry index.
211+
* @return The length (in cells) of the parent specifier.
212+
*/
213+
#define DT_MAP_PARENT_SPECIFIER_LEN(node_id, prop, idx) \
214+
DT_CAT7(node_id, _P_, prop, _MAP_IDX_, idx, _, PARENT_SPECIFIER_LEN)
215+
216+
/**
217+
* @brief Extracts the child specifier arguments from a mapping entry.
218+
*
219+
* This macro returns the comma-separated list of arguments for the child specifier.
220+
*
221+
* @param node_id The node identifier.
222+
* @param prop The property name in lowercase with underscores.
223+
* @param idx The mapping entry index.
224+
* @return The child specifier arguments.
225+
*/
226+
#define DT_MAP_CHILD_SPECIFIER_ARGS(node_id, prop, idx) \
227+
DT_MAP_HELPER_ARGS_RANGE(CHILD_SPECIFIER, node_id, prop, idx, \
228+
DT_MAP_IDX(node_id, prop, idx))
229+
230+
/**
231+
* @brief Extracts the parent node argument from a mapping entry.
232+
*
233+
* @param node_id The node identifier.
234+
* @param prop The property name in lowercase with underscores.
235+
* @param idx The mapping entry index.
236+
* @return The parent node argument.
237+
*/
238+
#define DT_MAP_PARENT_ARG(node_id, prop, idx) \
239+
DT_MAP_HELPER_ARGS_RANGE(PARENT, node_id, prop, idx, DT_MAP_IDX(node_id, prop, idx))
240+
241+
/**
242+
* @brief Extracts the parent specifier arguments from a mapping entry.
243+
*
244+
* This macro returns the comma-separated list of arguments for the parent specifier.
245+
*
246+
* @param node_id The node identifier.
247+
* @param prop The property name in lowercase with underscores.
248+
* @param idx The mapping entry index.
249+
* @return The parent specifier arguments.
250+
*/
251+
#define DT_MAP_PARENT_SPECIFIER_ARGS(node_id, prop, idx) \
252+
DT_MAP_HELPER_ARGS_RANGE(PARENT_SPECIFIER, node_id, prop, idx, \
253+
DT_MAP_IDX(node_id, prop, idx))
254+
255+
/**
256+
* @}
257+
*/
258+
259+
#ifdef __cplusplus
260+
}
261+
#endif
262+
263+
#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)