blob: 99aac7d720cea96f5965e2d114374486d866f5cb [file] [log] [blame]
Martí Bolívardc85edd2020-02-28 15:26:52 -08001#!/usr/bin/env python3
2
3# Copyright (c) 2019 - 2020 Nordic Semiconductor ASA
4# Copyright (c) 2019 Linaro Limited
Benedikt Schmidtfe3287a2024-09-09 11:18:56 +02005# Copyright (c) 2024 SILA Embedded Solutions GmbH
Martí Bolívardc85edd2020-02-28 15:26:52 -08006# SPDX-License-Identifier: BSD-3-Clause
7
Benedikt Schmidtfe3287a2024-09-09 11:18:56 +02008# This script uses edtlib to generate a header file from a pickled
9# edt file.
Martí Bolívardc85edd2020-02-28 15:26:52 -080010#
11# Note: Do not access private (_-prefixed) identifiers from edtlib here (and
12# also note that edtlib is not meant to expose the dtlib API directly).
13# Instead, think of what API you need, and add it as a public documented API in
14# edtlib. This will keep this script simple.
15
16import argparse
Martí Bolívara3fae2f2020-03-25 14:18:27 -070017from collections import defaultdict
Martí Bolívardc85edd2020-02-28 15:26:52 -080018import os
19import pathlib
Martí Bolívar533f4512020-07-01 10:43:43 -070020import pickle
Martí Bolívardc85edd2020-02-28 15:26:52 -080021import re
22import sys
Florian Grandelde846c72024-09-06 10:15:55 +020023from typing import Iterable, NoReturn, Optional
Martí Bolívardc85edd2020-02-28 15:26:52 -080024
Jordan Yates8e4107f2022-04-30 21:13:52 +100025sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'python-devicetree',
26 'src'))
Martí Bolívar53328472021-03-26 16:18:58 -070027
Benedikt Schmidtfe3287a2024-09-09 11:18:56 +020028import edtlib_logger
Martí Bolívar53328472021-03-26 16:18:58 -070029from devicetree import edtlib
Martí Bolívardc85edd2020-02-28 15:26:52 -080030
Martí Bolívar09858492020-12-08 09:41:49 -080031
Martí Bolívardc85edd2020-02-28 15:26:52 -080032def main():
33 global header_file
Kumar Galabd973782020-05-06 20:54:29 -050034 global flash_area_num
Martí Bolívardc85edd2020-02-28 15:26:52 -080035
36 args = parse_args()
37
Benedikt Schmidtfe3287a2024-09-09 11:18:56 +020038 edtlib_logger.setup_edtlib_logging()
Martí Bolívar09858492020-12-08 09:41:49 -080039
Benedikt Schmidtfe3287a2024-09-09 11:18:56 +020040 with open(args.edt_pickle, 'rb') as f:
41 edt = pickle.load(f)
Martí Bolívardc85edd2020-02-28 15:26:52 -080042
Kumar Galabd973782020-05-06 20:54:29 -050043 flash_area_num = 0
44
Martí Bolívar533f4512020-07-01 10:43:43 -070045 # Create the generated header.
Martí Bolívardc85edd2020-02-28 15:26:52 -080046 with open(args.header_out, "w", encoding="utf-8") as header_file:
47 write_top_comment(edt)
48
Gerard Marull-Paretasd77f4e62022-07-05 16:50:36 +020049 write_utils()
50
Florian Grandel945925b2024-09-06 10:17:26 +020051 sorted_nodes = sorted(edt.nodes, key=lambda node: node.dep_ordinal)
52
Dominik Ermelba8b74d2020-04-17 06:32:28 +000053 # populate all z_path_id first so any children references will
54 # work correctly.
Florian Grandel945925b2024-09-06 10:17:26 +020055 for node in sorted_nodes:
Martí Bolívar186bace2020-04-08 15:02:18 -070056 node.z_path_id = node_z_path_id(node)
Dominik Ermelba8b74d2020-04-17 06:32:28 +000057
Jordan Yates9c98d4f2021-07-28 20:01:16 +100058 # Check to see if we have duplicate "zephyr,memory-region" property values.
59 regions = dict()
Florian Grandel945925b2024-09-06 10:17:26 +020060 for node in sorted_nodes:
Jordan Yates9c98d4f2021-07-28 20:01:16 +100061 if 'zephyr,memory-region' in node.props:
62 region = node.props['zephyr,memory-region'].val
63 if region in regions:
64 sys.exit(f"ERROR: Duplicate 'zephyr,memory-region' ({region}) properties "
65 f"between {regions[region].path} and {node.path}")
66 regions[region] = node
67
Florian Grandel945925b2024-09-06 10:17:26 +020068 for node in sorted_nodes:
Martí Bolívardc85edd2020-02-28 15:26:52 -080069 write_node_comment(node)
70
Kumar Gala270a05f2021-02-24 11:28:21 -060071 out_comment("Node's full path:")
Martí Bolívar00ffc7e2020-12-13 12:27:04 -080072 out_dt_define(f"{node.z_path_id}_PATH", f'"{escape(node.path)}"')
73
Kumar Gala4aac9082021-02-24 10:44:07 -060074 out_comment("Node's name with unit-address:")
75 out_dt_define(f"{node.z_path_id}_FULL_NAME",
76 f'"{escape(node.name)}"')
TOKITA Hiroshi767d1ce2024-09-12 22:58:53 +090077 out_dt_define(f"{node.z_path_id}_FULL_NAME_UNQUOTED",
78 f'{escape(node.name)}')
79 out_dt_define(f"{node.z_path_id}_FULL_NAME_TOKEN",
80 f'{edtlib.str_as_token(escape(node.name))}')
81 out_dt_define(f"{node.z_path_id}_FULL_NAME_UPPER_TOKEN",
82 f'{edtlib.str_as_token(escape(node.name)).upper()}')
Kumar Gala4aac9082021-02-24 10:44:07 -060083
Martí Bolívar6e273432020-04-08 15:04:15 -070084 if node.parent is not None:
85 out_comment(f"Node parent ({node.parent.path}) identifier:")
86 out_dt_define(f"{node.z_path_id}_PARENT",
87 f"DT_{node.parent.z_path_id}")
88
Martí Bolívar50f9b3c2022-03-23 13:41:09 -070089 out_comment(f"Node's index in its parent's list of children:")
90 out_dt_define(f"{node.z_path_id}_CHILD_IDX",
91 node.parent.child_index(node))
92
Martí Bolívar74abb2b2024-04-24 19:22:42 -060093 out_comment("Helpers for dealing with node labels:")
94 out_dt_define(f"{node.z_path_id}_NODELABEL_NUM", len(node.labels))
95 out_dt_define(f"{node.z_path_id}_FOREACH_NODELABEL(fn)",
96 " ".join(f"fn({nodelabel})" for nodelabel in node.labels))
97 out_dt_define(f"{node.z_path_id}_FOREACH_NODELABEL_VARGS(fn, ...)",
98 " ".join(f"fn({nodelabel}, __VA_ARGS__)" for nodelabel in node.labels))
99
James Roy4553a212024-11-14 21:10:00 +0800100 write_parent(node)
Martí Bolívar355cc012022-03-23 13:26:24 -0700101 write_children(node)
Martí Bolívar305379e2020-06-08 14:59:19 -0700102 write_dep_info(node)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800103 write_idents_and_existence(node)
104 write_bus(node)
105 write_special_props(node)
106 write_vanilla_props(node)
107
108 write_chosen(edt)
Martí Bolívar190197e2022-07-20 13:10:33 -0700109 write_global_macros(edt)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800110
Florian Grandelde846c72024-09-06 10:15:55 +0200111
112def node_z_path_id(node: edtlib.Node) -> str:
Martí Bolívar186bace2020-04-08 15:02:18 -0700113 # Return the node specific bit of the node's path identifier:
114 #
115 # - the root node's path "/" has path identifier "N"
116 # - "/foo" has "N_S_foo"
117 # - "/foo/bar" has "N_S_foo_S_bar"
118 # - "/foo/bar@123" has "N_S_foo_S_bar_123"
119 #
120 # This is used throughout this file to generate macros related to
121 # the node.
122
123 components = ["N"]
124 if node.parent is not None:
125 components.extend(f"S_{str2ident(component)}" for component in
126 node.path.split("/")[1:])
127
128 return "_".join(components)
129
Florian Grandelde846c72024-09-06 10:15:55 +0200130
131def parse_args() -> argparse.Namespace:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800132 # Returns parsed command-line arguments
133
Jamie McCraeec704442023-01-04 16:08:36 +0000134 parser = argparse.ArgumentParser(allow_abbrev=False)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800135 parser.add_argument("--header-out", required=True,
136 help="path to write header to")
Benedikt Schmidtfe3287a2024-09-09 11:18:56 +0200137 parser.add_argument("--edt-pickle",
138 help="path to read pickled edtlib.EDT object from")
Martí Bolívardc85edd2020-02-28 15:26:52 -0800139
140 return parser.parse_args()
141
142
Florian Grandelde846c72024-09-06 10:15:55 +0200143def write_top_comment(edt: edtlib.EDT) -> None:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800144 # Writes an overview comment with misc. info at the top of the header and
145 # configuration file
146
147 s = f"""\
148Generated by gen_defines.py
149
150DTS input file:
151 {edt.dts_path}
152
153Directories with bindings:
154 {", ".join(map(relativize, edt.bindings_dirs))}
155
Martí Bolívar305379e2020-06-08 14:59:19 -0700156Node dependency ordering (ordinal and path):
Martí Bolívardc85edd2020-02-28 15:26:52 -0800157"""
158
Martí Bolívarb6db2012020-08-24 13:33:53 -0700159 for scc in edt.scc_order:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800160 if len(scc) > 1:
161 err("cycle in devicetree involving "
162 + ", ".join(node.path for node in scc))
163 s += f" {scc[0].dep_ordinal:<3} {scc[0].path}\n"
164
165 s += """
166Definitions derived from these nodes in dependency order are next,
167followed by /chosen nodes.
168"""
169
170 out_comment(s, blank_before=False)
171
172
Florian Grandelde846c72024-09-06 10:15:55 +0200173def write_utils() -> None:
Gerard Marull-Paretasd77f4e62022-07-05 16:50:36 +0200174 # Writes utility macros
175
176 out_comment("Used to remove brackets from around a single argument")
177 out_define("DT_DEBRACKET_INTERNAL(...)", "__VA_ARGS__")
178
179
Florian Grandelde846c72024-09-06 10:15:55 +0200180def write_node_comment(node: edtlib.Node) -> None:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800181 # Writes a comment describing 'node' to the header and configuration file
182
183 s = f"""\
Martí Bolívarb6e6ba02020-04-08 15:09:46 -0700184Devicetree node: {node.path}
185
Martí Bolívar305379e2020-06-08 14:59:19 -0700186Node identifier: DT_{node.z_path_id}
Martí Bolívardc85edd2020-02-28 15:26:52 -0800187"""
188
189 if node.matching_compat:
Peter Bigot932532e2020-09-02 05:05:19 -0500190 if node.binding_path:
191 s += f"""
Martí Bolívardc85edd2020-02-28 15:26:52 -0800192Binding (compatible = {node.matching_compat}):
193 {relativize(node.binding_path)}
194"""
Peter Bigot932532e2020-09-02 05:05:19 -0500195 else:
196 s += f"""
197Binding (compatible = {node.matching_compat}):
198 No yaml (bindings inferred from properties)
199"""
Martí Bolívardc85edd2020-02-28 15:26:52 -0800200
Martí Bolívardc85edd2020-02-28 15:26:52 -0800201 if node.description:
Martí Bolívarf7d33f22020-10-30 17:45:27 -0700202 # We used to put descriptions in the generated file, but
203 # devicetree bindings now have pages in the HTML
204 # documentation. Let users who are accustomed to digging
205 # around in the generated file where to find the descriptions
206 # now.
207 #
208 # Keeping them here would mean that the descriptions
209 # themselves couldn't contain C multi-line comments, which is
210 # inconvenient when we want to do things like quote snippets
211 # of .dtsi files within the descriptions, or otherwise
212 # include the string "*/".
213 s += ("\n(Descriptions have moved to the Devicetree Bindings Index\n"
214 "in the documentation.)\n")
Martí Bolívardc85edd2020-02-28 15:26:52 -0800215
216 out_comment(s)
217
218
Florian Grandelde846c72024-09-06 10:15:55 +0200219def relativize(path) -> Optional[str]:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800220 # If 'path' is within $ZEPHYR_BASE, returns it relative to $ZEPHYR_BASE,
221 # with a "$ZEPHYR_BASE/..." hint at the start of the string. Otherwise,
222 # returns 'path' unchanged.
223
224 zbase = os.getenv("ZEPHYR_BASE")
225 if zbase is None:
226 return path
227
228 try:
229 return str("$ZEPHYR_BASE" / pathlib.Path(path).relative_to(zbase))
230 except ValueError:
231 # Not within ZEPHYR_BASE
232 return path
233
234
Florian Grandelde846c72024-09-06 10:15:55 +0200235def write_idents_and_existence(node: edtlib.Node) -> None:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800236 # Writes macros related to the node's aliases, labels, etc.,
237 # as well as existence flags.
238
239 # Aliases
240 idents = [f"N_ALIAS_{str2ident(alias)}" for alias in node.aliases]
241 # Instances
242 for compat in node.compats:
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700243 instance_no = node.edt.compat2nodes[compat].index(node)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800244 idents.append(f"N_INST_{instance_no}_{str2ident(compat)}")
245 # Node labels
246 idents.extend(f"N_NODELABEL_{str2ident(label)}" for label in node.labels)
247
248 out_comment("Existence and alternate IDs:")
James Royab959d52024-12-05 21:10:53 +0800249 out_dt_define(f"{node.z_path_id}_EXISTS", 1)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800250
251 # Only determine maxlen if we have any idents
252 if idents:
James Royab959d52024-12-05 21:10:53 +0800253 maxlen = max(len(f"DT_{ident}") for ident in idents)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800254 for ident in idents:
James Royab959d52024-12-05 21:10:53 +0800255 out_dt_define(ident, f"DT_{node.z_path_id}", width=maxlen)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800256
257
Florian Grandelde846c72024-09-06 10:15:55 +0200258def write_bus(node: edtlib.Node) -> None:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800259 # Macros about the node's bus controller, if there is one
260
261 bus = node.bus_node
262 if not bus:
263 return
264
Daniel Leung418c9152022-08-26 10:52:32 -0700265 out_comment(f"Bus info (controller: '{bus.path}', type: '{node.on_buses}')")
266
267 for one_bus in node.on_buses:
268 out_dt_define(f"{node.z_path_id}_BUS_{str2ident(one_bus)}", 1)
269
Martí Bolívardc85edd2020-02-28 15:26:52 -0800270 out_dt_define(f"{node.z_path_id}_BUS", f"DT_{bus.z_path_id}")
271
272
Florian Grandelde846c72024-09-06 10:15:55 +0200273def write_special_props(node: edtlib.Node) -> None:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800274 # Writes required macros for special case properties, when the
275 # data cannot otherwise be obtained from write_vanilla_props()
276 # results
277
Martí Bolívardc85edd2020-02-28 15:26:52 -0800278 # Macros that are special to the devicetree specification
Martí Bolívar7f69a032021-08-11 15:14:51 -0700279 out_comment("Macros for properties that are special in the specification:")
Martí Bolívardc85edd2020-02-28 15:26:52 -0800280 write_regs(node)
Neil Armstrong1e8f0f32021-06-24 10:14:05 +0200281 write_ranges(node)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800282 write_interrupts(node)
283 write_compatibles(node)
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700284 write_status(node)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800285
Martí Bolívar7f69a032021-08-11 15:14:51 -0700286 # Macros that are special to bindings inherited from Linux, which
287 # we can't capture with the current bindings language.
Martí Bolívar9df04932021-08-11 15:43:24 -0700288 write_pinctrls(node)
Martí Bolívar7f69a032021-08-11 15:14:51 -0700289 write_fixed_partitions(node)
Henrik Brix Andersen28819152023-01-13 11:32:27 +0100290 write_gpio_hogs(node)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800291
Florian Grandelde846c72024-09-06 10:15:55 +0200292
293def write_ranges(node: edtlib.Node) -> None:
Neil Armstrong1e8f0f32021-06-24 10:14:05 +0200294 # ranges property: edtlib knows the right #address-cells and
295 # #size-cells of parent and child, and can therefore pack the
296 # child & parent addresses and sizes correctly
297
298 idx_vals = []
299 path_id = node.z_path_id
300
301 if node.ranges is not None:
302 idx_vals.append((f"{path_id}_RANGES_NUM", len(node.ranges)))
303
304 for i,range in enumerate(node.ranges):
305 idx_vals.append((f"{path_id}_RANGES_IDX_{i}_EXISTS", 1))
306
Daniel Leung418c9152022-08-26 10:52:32 -0700307 if "pcie" in node.buses:
Neil Armstrong1e8f0f32021-06-24 10:14:05 +0200308 idx_vals.append((f"{path_id}_RANGES_IDX_{i}_VAL_CHILD_BUS_FLAGS_EXISTS", 1))
309 idx_macro = f"{path_id}_RANGES_IDX_{i}_VAL_CHILD_BUS_FLAGS"
310 idx_value = range.child_bus_addr >> ((range.child_bus_cells - 1) * 32)
311 idx_vals.append((idx_macro,
312 f"{idx_value} /* {hex(idx_value)} */"))
313 if range.child_bus_addr is not None:
314 idx_macro = f"{path_id}_RANGES_IDX_{i}_VAL_CHILD_BUS_ADDRESS"
Daniel Leung418c9152022-08-26 10:52:32 -0700315 if "pcie" in node.buses:
Neil Armstrong1e8f0f32021-06-24 10:14:05 +0200316 idx_value = range.child_bus_addr & ((1 << (range.child_bus_cells - 1) * 32) - 1)
317 else:
318 idx_value = range.child_bus_addr
319 idx_vals.append((idx_macro,
320 f"{idx_value} /* {hex(idx_value)} */"))
321 if range.parent_bus_addr is not None:
322 idx_macro = f"{path_id}_RANGES_IDX_{i}_VAL_PARENT_BUS_ADDRESS"
323 idx_vals.append((idx_macro,
324 f"{range.parent_bus_addr} /* {hex(range.parent_bus_addr)} */"))
325 if range.length is not None:
326 idx_macro = f"{path_id}_RANGES_IDX_{i}_VAL_LENGTH"
327 idx_vals.append((idx_macro,
328 f"{range.length} /* {hex(range.length)} */"))
329
330 for macro, val in idx_vals:
331 out_dt_define(macro, val)
332
333 out_dt_define(f"{path_id}_FOREACH_RANGE(fn)",
334 " ".join(f"fn(DT_{path_id}, {i})" for i,range in enumerate(node.ranges)))
335
Florian Grandelde846c72024-09-06 10:15:55 +0200336
337def write_regs(node: edtlib.Node) -> None:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800338 # reg property: edtlib knows the right #address-cells and
339 # #size-cells, and can therefore pack the register base addresses
340 # and sizes correctly
341
342 idx_vals = []
343 name_vals = []
344 path_id = node.z_path_id
345
346 if node.regs is not None:
347 idx_vals.append((f"{path_id}_REG_NUM", len(node.regs)))
348
349 for i, reg in enumerate(node.regs):
Kumar Gala4e2ad002020-04-14 14:27:20 -0500350 idx_vals.append((f"{path_id}_REG_IDX_{i}_EXISTS", 1))
Martí Bolívardc85edd2020-02-28 15:26:52 -0800351 if reg.addr is not None:
352 idx_macro = f"{path_id}_REG_IDX_{i}_VAL_ADDRESS"
353 idx_vals.append((idx_macro,
354 f"{reg.addr} /* {hex(reg.addr)} */"))
355 if reg.name:
Fin Maaßfb8b30d2024-05-28 11:56:42 +0200356 name_vals.append((f"{path_id}_REG_NAME_{reg.name}_EXISTS", 1))
Martí Bolívardc85edd2020-02-28 15:26:52 -0800357 name_macro = f"{path_id}_REG_NAME_{reg.name}_VAL_ADDRESS"
358 name_vals.append((name_macro, f"DT_{idx_macro}"))
359
360 if reg.size is not None:
361 idx_macro = f"{path_id}_REG_IDX_{i}_VAL_SIZE"
362 idx_vals.append((idx_macro,
363 f"{reg.size} /* {hex(reg.size)} */"))
364 if reg.name:
365 name_macro = f"{path_id}_REG_NAME_{reg.name}_VAL_SIZE"
366 name_vals.append((name_macro, f"DT_{idx_macro}"))
367
368 for macro, val in idx_vals:
369 out_dt_define(macro, val)
370 for macro, val in name_vals:
371 out_dt_define(macro, val)
372
Florian Grandelde846c72024-09-06 10:15:55 +0200373
374def write_interrupts(node: edtlib.Node) -> None:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800375 # interrupts property: we have some hard-coded logic for interrupt
376 # mapping here.
377 #
Yong Cong Sindf2c0682023-10-02 12:42:24 +0800378 # TODO: can we push map_arm_gic_irq_type() out of Python and into C with
Martí Bolívardc85edd2020-02-28 15:26:52 -0800379 # macro magic in devicetree.h?
380
381 def map_arm_gic_irq_type(irq, irq_num):
382 # Maps ARM GIC IRQ (type)+(index) combo to linear IRQ number
383 if "type" not in irq.data:
384 err(f"Expected binding for {irq.controller!r} to have 'type' in "
385 "interrupt-cells")
386 irq_type = irq.data["type"]
387
388 if irq_type == 0: # GIC_SPI
389 return irq_num + 32
390 if irq_type == 1: # GIC_PPI
391 return irq_num + 16
392 err(f"Invalid interrupt type specified for {irq!r}")
393
Martí Bolívardc85edd2020-02-28 15:26:52 -0800394 idx_vals = []
395 name_vals = []
396 path_id = node.z_path_id
397
398 if node.interrupts is not None:
399 idx_vals.append((f"{path_id}_IRQ_NUM", len(node.interrupts)))
400
401 for i, irq in enumerate(node.interrupts):
402 for cell_name, cell_value in irq.data.items():
403 name = str2ident(cell_name)
404
405 if cell_name == "irq":
406 if "arm,gic" in irq.controller.compats:
407 cell_value = map_arm_gic_irq_type(irq, cell_value)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800408
Kumar Gala4e2ad002020-04-14 14:27:20 -0500409 idx_vals.append((f"{path_id}_IRQ_IDX_{i}_EXISTS", 1))
Martí Bolívardc85edd2020-02-28 15:26:52 -0800410 idx_macro = f"{path_id}_IRQ_IDX_{i}_VAL_{name}"
411 idx_vals.append((idx_macro, cell_value))
412 idx_vals.append((idx_macro + "_EXISTS", 1))
413 if irq.name:
James Roy802eac72024-12-28 21:23:31 +0800414 name_macro = (
415 f"{path_id}_IRQ_NAME_{str2ident(irq.name)}_VAL_{name}")
Martí Bolívardc85edd2020-02-28 15:26:52 -0800416 name_vals.append((name_macro, f"DT_{idx_macro}"))
417 name_vals.append((name_macro + "_EXISTS", 1))
418
Bjarki Arge Andreasen08d6ff02023-11-26 11:34:06 +0100419 idx_controller_macro = f"{path_id}_IRQ_IDX_{i}_CONTROLLER"
420 idx_controller_path = f"DT_{irq.controller.z_path_id}"
421 idx_vals.append((idx_controller_macro, idx_controller_path))
422 if irq.name:
423 name_controller_macro = f"{path_id}_IRQ_NAME_{str2ident(irq.name)}_CONTROLLER"
424 name_vals.append((name_controller_macro, f"DT_{idx_controller_macro}"))
425
Yong Cong Sin450a66f2023-12-22 16:20:10 +0800426 # Interrupt controller info
427 irqs = []
428 while node.interrupts is not None and len(node.interrupts) > 0:
429 irq = node.interrupts[0]
430 irqs.append(irq)
431 if node == irq.controller:
432 break
433 node = irq.controller
434 idx_vals.append((f"{path_id}_IRQ_LEVEL", len(irqs)))
435
Martí Bolívardc85edd2020-02-28 15:26:52 -0800436 for macro, val in idx_vals:
437 out_dt_define(macro, val)
438 for macro, val in name_vals:
439 out_dt_define(macro, val)
440
441
Florian Grandelde846c72024-09-06 10:15:55 +0200442def write_compatibles(node: edtlib.Node) -> None:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800443 # Writes a macro for each of the node's compatibles. We don't care
444 # about whether edtlib / Zephyr's binding language recognizes
445 # them. The compatibles the node provides are what is important.
446
Maureen Helm5b5aa6e2022-08-24 13:44:51 -0500447 for i, compat in enumerate(node.compats):
Martí Bolívardc85edd2020-02-28 15:26:52 -0800448 out_dt_define(
449 f"{node.z_path_id}_COMPAT_MATCHES_{str2ident(compat)}", 1)
450
Maureen Helm5b5aa6e2022-08-24 13:44:51 -0500451 if node.edt.compat2vendor[compat]:
452 out_dt_define(f"{node.z_path_id}_COMPAT_VENDOR_IDX_{i}_EXISTS", 1)
453 out_dt_define(f"{node.z_path_id}_COMPAT_VENDOR_IDX_{i}",
454 quote_str(node.edt.compat2vendor[compat]))
Martí Bolívardc85edd2020-02-28 15:26:52 -0800455
Maureen Helme73c3632022-09-07 17:17:18 -0500456 if node.edt.compat2model[compat]:
457 out_dt_define(f"{node.z_path_id}_COMPAT_MODEL_IDX_{i}_EXISTS", 1)
458 out_dt_define(f"{node.z_path_id}_COMPAT_MODEL_IDX_{i}",
459 quote_str(node.edt.compat2model[compat]))
460
James Roy4553a212024-11-14 21:10:00 +0800461def write_parent(node: edtlib.Node) -> None:
462 # Visit all parent nodes.
463 def _visit_parent_node(node: edtlib.Node):
464 while node is not None:
465 yield node.parent
466 node = node.parent
467
468 # Writes helper macros for dealing with node's parent.
469 out_dt_define(f"{node.z_path_id}_FOREACH_ANCESTOR(fn)",
470 " ".join(f"fn(DT_{parent.z_path_id})" for parent in
471 _visit_parent_node(node) if parent is not None))
Florian Grandelde846c72024-09-06 10:15:55 +0200472
473def write_children(node: edtlib.Node) -> None:
Martí Bolívar355cc012022-03-23 13:26:24 -0700474 # Writes helper macros for dealing with node's children.
Dominik Ermelba8b74d2020-04-17 06:32:28 +0000475
Martí Bolívar7b2a7282022-07-08 11:04:46 -0700476 out_comment("Helper macros for child nodes of this node.")
477
Swift Tian5871ff02024-04-25 18:52:22 +0800478 out_dt_define(f"{node.z_path_id}_CHILD_NUM", len(node.children))
479
480 ok_nodes_num = 0
481 for child in node.children.values():
482 if child.status == "okay":
483 ok_nodes_num = ok_nodes_num + 1
484
485 out_dt_define(f"{node.z_path_id}_CHILD_NUM_STATUS_OKAY", ok_nodes_num)
486
Kumar Gala4a5a90a2020-05-08 12:25:25 -0500487 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD(fn)",
488 " ".join(f"fn(DT_{child.z_path_id})" for child in
489 node.children.values()))
Dominik Ermelba8b74d2020-04-17 06:32:28 +0000490
Gerard Marull-Paretasfff9ecb2022-07-05 16:52:36 +0200491 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_SEP(fn, sep)",
492 " DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id})"
493 for child in node.children.values()))
494
Arvin Farahmandd0b9c032021-05-06 11:19:29 -0400495 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_VARGS(fn, ...)",
Gerard Marull-Paretasfff9ecb2022-07-05 16:52:36 +0200496 " ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)"
497 for child in node.children.values()))
Dominik Ermelba8b74d2020-04-17 06:32:28 +0000498
Gerard Marull-Paretasfff9ecb2022-07-05 16:52:36 +0200499 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_SEP_VARGS(fn, sep, ...)",
500 " DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)"
501 for child in node.children.values()))
Hou Zhiqiang0700a242021-04-26 16:22:38 +0800502
Gerard Marull-Paretasfff9ecb2022-07-05 16:52:36 +0200503 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY(fn)",
504 " ".join(f"fn(DT_{child.z_path_id})"
505 for child in node.children.values() if child.status == "okay"))
506
507 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_SEP(fn, sep)",
508 " DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id})"
509 for child in node.children.values() if child.status == "okay"))
510
Arvin Farahmandd0b9c032021-05-06 11:19:29 -0400511 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_VARGS(fn, ...)",
Gerard Marull-Paretasfff9ecb2022-07-05 16:52:36 +0200512 " ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)"
513 for child in node.children.values() if child.status == "okay"))
514
515 out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_SEP_VARGS(fn, sep, ...)",
516 " DT_DEBRACKET_INTERNAL sep ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)"
517 for child in node.children.values() if child.status == "okay"))
Hou Zhiqiang0700a242021-04-26 16:22:38 +0800518
519
Florian Grandelde846c72024-09-06 10:15:55 +0200520def write_status(node: edtlib.Node) -> None:
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700521 out_dt_define(f"{node.z_path_id}_STATUS_{str2ident(node.status)}", 1)
522
523
Florian Grandelde846c72024-09-06 10:15:55 +0200524def write_pinctrls(node: edtlib.Node) -> None:
Martí Bolívar9df04932021-08-11 15:43:24 -0700525 # Write special macros for pinctrl-<index> and pinctrl-names properties.
526
527 out_comment("Pin control (pinctrl-<i>, pinctrl-names) properties:")
528
529 out_dt_define(f"{node.z_path_id}_PINCTRL_NUM", len(node.pinctrls))
530
531 if not node.pinctrls:
532 return
533
534 for pc_idx, pinctrl in enumerate(node.pinctrls):
535 out_dt_define(f"{node.z_path_id}_PINCTRL_IDX_{pc_idx}_EXISTS", 1)
536
537 if not pinctrl.name:
538 continue
539
540 name = pinctrl.name_as_token
541
542 # Below we rely on the fact that edtlib ensures the
543 # pinctrl-<pc_idx> properties are contiguous, start from 0,
544 # and contain only phandles.
545 out_dt_define(f"{node.z_path_id}_PINCTRL_IDX_{pc_idx}_TOKEN", name)
546 out_dt_define(f"{node.z_path_id}_PINCTRL_IDX_{pc_idx}_UPPER_TOKEN", name.upper())
547 out_dt_define(f"{node.z_path_id}_PINCTRL_NAME_{name}_EXISTS", 1)
548 out_dt_define(f"{node.z_path_id}_PINCTRL_NAME_{name}_IDX", pc_idx)
549 for idx, ph in enumerate(pinctrl.conf_nodes):
550 out_dt_define(f"{node.z_path_id}_PINCTRL_NAME_{name}_IDX_{idx}_PH",
551 f"DT_{ph.z_path_id}")
552
553
Florian Grandelde846c72024-09-06 10:15:55 +0200554def write_fixed_partitions(node: edtlib.Node) -> None:
Martí Bolívar7f69a032021-08-11 15:14:51 -0700555 # Macros for child nodes of each fixed-partitions node.
556
557 if not (node.parent and "fixed-partitions" in node.parent.compats):
558 return
559
560 global flash_area_num
561 out_comment("fixed-partitions identifier:")
562 out_dt_define(f"{node.z_path_id}_PARTITION_ID", flash_area_num)
563 flash_area_num += 1
564
565
Florian Grandelde846c72024-09-06 10:15:55 +0200566def write_gpio_hogs(node: edtlib.Node) -> None:
Henrik Brix Andersen28819152023-01-13 11:32:27 +0100567 # Write special macros for gpio-hog node properties.
568
569 macro = f"{node.z_path_id}_GPIO_HOGS"
570 macro2val = {}
571 for i, entry in enumerate(node.gpio_hogs):
572 macro2val.update(controller_and_data_macros(entry, i, macro))
573
574 if macro2val:
575 out_comment("GPIO hog properties:")
576 out_dt_define(f"{macro}_EXISTS", 1)
577 out_dt_define(f"{macro}_NUM", len(node.gpio_hogs))
578 for macro, val in macro2val.items():
579 out_dt_define(macro, val)
580
Florian Grandelde846c72024-09-06 10:15:55 +0200581
582def write_vanilla_props(node: edtlib.Node) -> None:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800583 # Writes macros for any and all properties defined in the
584 # "properties" section of the binding for the node.
585 #
586 # This does generate macros for special properties as well, like
587 # regs, etc. Just let that be rather than bothering to add
588 # never-ending amounts of special case code here to skip special
589 # properties. This function's macros can't conflict with
590 # write_special_props() macros, because they're in different
591 # namespaces. Special cases aren't special enough to break the rules.
592
593 macro2val = {}
594 for prop_name, prop in node.props.items():
Martí Bolívar9c229a42021-04-14 15:26:42 -0700595 prop_id = str2ident(prop_name)
596 macro = f"{node.z_path_id}_P_{prop_id}"
Martí Bolívardc85edd2020-02-28 15:26:52 -0800597 val = prop2value(prop)
598 if val is not None:
599 # DT_N_<node-id>_P_<prop-id>
600 macro2val[macro] = val
601
Carlo Caionef4db14f2021-05-17 17:24:27 +0200602 if prop.spec.type == 'string':
Joel Hirsbrunner8b02bc92024-10-04 21:29:37 +0200603 macro2val.update(string_macros(macro, prop.val))
Martí Bolívar0c29e072023-05-06 14:42:15 -0700604 # DT_N_<node-id>_P_<prop-id>_IDX_0:
605 # DT_N_<node-id>_P_<prop-id>_IDX_0_EXISTS:
606 # Allows treating the string like a degenerate case of a
607 # string-array of length 1.
James Royab959d52024-12-05 21:10:53 +0800608 macro2val[f"{macro}_IDX_0"] = quote_str(prop.val)
609 macro2val[f"{macro}_IDX_0_EXISTS"] = 1
Carlo Caionef4db14f2021-05-17 17:24:27 +0200610
Joel Hirsbrunner8b02bc92024-10-04 21:29:37 +0200611 if prop.enum_indices is not None:
612 macro2val.update(enum_macros(prop, macro))
Martí Bolívardc85edd2020-02-28 15:26:52 -0800613
614 if "phandle" in prop.type:
615 macro2val.update(phandle_macros(prop, macro))
616 elif "array" in prop.type:
Joel Hirsbrunner8b02bc92024-10-04 21:29:37 +0200617 macro2val.update(array_macros(prop, macro))
Martí Bolívardc85edd2020-02-28 15:26:52 -0800618
Martí Bolívar0c29e072023-05-06 14:42:15 -0700619 plen = prop_len(prop)
620 if plen is not None:
Martí Bolívar9c229a42021-04-14 15:26:42 -0700621 # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM
James Roy802eac72024-12-28 21:23:31 +0800622 macro2val[f"{macro}_FOREACH_PROP_ELEM(fn)"] = (
623 ' \\\n\t'.join(f'fn(DT_{node.z_path_id}, {prop_id}, {i})'
624 for i in range(plen)))
Gerard Marull-Paretasfdea3c92022-09-06 15:31:15 +0200625
Martí Bolívar52043692023-05-05 15:30:38 -0700626 # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_SEP
James Roy802eac72024-12-28 21:23:31 +0800627 macro2val[f"{macro}_FOREACH_PROP_ELEM_SEP(fn, sep)"] = (
Gerard Marull-Paretasfdea3c92022-09-06 15:31:15 +0200628 ' DT_DEBRACKET_INTERNAL sep \\\n\t'.join(
629 f'fn(DT_{node.z_path_id}, {prop_id}, {i})'
James Roy802eac72024-12-28 21:23:31 +0800630 for i in range(plen)))
Martí Bolívar9c229a42021-04-14 15:26:42 -0700631
Martí Bolívar52043692023-05-05 15:30:38 -0700632 # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_VARGS
James Roy802eac72024-12-28 21:23:31 +0800633 macro2val[f"{macro}_FOREACH_PROP_ELEM_VARGS(fn, ...)"] = (
Gerard Marull-Paretasfdea3c92022-09-06 15:31:15 +0200634 ' \\\n\t'.join(
635 f'fn(DT_{node.z_path_id}, {prop_id}, {i}, __VA_ARGS__)'
James Roy802eac72024-12-28 21:23:31 +0800636 for i in range(plen)))
Gerard Marull-Paretasfdea3c92022-09-06 15:31:15 +0200637
Martí Bolívar52043692023-05-05 15:30:38 -0700638 # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM_SEP_VARGS
James Roy802eac72024-12-28 21:23:31 +0800639 macro2val[f"{macro}_FOREACH_PROP_ELEM_SEP_VARGS(fn, sep, ...)"] = (
Gerard Marull-Paretasfdea3c92022-09-06 15:31:15 +0200640 ' DT_DEBRACKET_INTERNAL sep \\\n\t'.join(
641 f'fn(DT_{node.z_path_id}, {prop_id}, {i}, __VA_ARGS__)'
James Roy802eac72024-12-28 21:23:31 +0800642 for i in range(plen)))
Arvin Farahmandd0b9c032021-05-06 11:19:29 -0400643
Martí Bolívardc85edd2020-02-28 15:26:52 -0800644 # DT_N_<node-id>_P_<prop-id>_LEN
James Royab959d52024-12-05 21:10:53 +0800645 macro2val[f"{macro}_LEN"] = plen
Martí Bolívardc85edd2020-02-28 15:26:52 -0800646
Martí Bolívar52043692023-05-05 15:30:38 -0700647 # DT_N_<node-id>_P_<prop-id>_EXISTS
Martí Bolívardc85edd2020-02-28 15:26:52 -0800648 macro2val[f"{macro}_EXISTS"] = 1
649
650 if macro2val:
651 out_comment("Generic property macros:")
652 for macro, val in macro2val.items():
653 out_dt_define(macro, val)
654 else:
655 out_comment("(No generic property macros)")
656
657
Joel Hirsbrunner8b02bc92024-10-04 21:29:37 +0200658def string_macros(macro: str, val: str):
659 # Returns a dict of macros for a string 'val'.
660 # The 'macro' argument is the N_<node-id>_P_<prop-id>... part.
661
662 as_token = edtlib.str_as_token(val)
663 return {
664 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_STRING_UNQUOTED
665 f"{macro}_STRING_UNQUOTED": escape_unquoted(val),
666 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_STRING_TOKEN
667 f"{macro}_STRING_TOKEN": as_token,
668 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_STRING_UPPER_TOKEN
669 f"{macro}_STRING_UPPER_TOKEN": as_token.upper()}
670
671
672def enum_macros(prop: edtlib.Property, macro: str):
673 # Returns a dict of macros for property 'prop' with a defined enum in their dt-binding.
674 # The 'macro' argument is the N_<node-id>_P_<prop-id> part.
675
676 spec = prop.spec
677 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_ENUM_IDX
678 ret = {f"{macro}_IDX_{i}_ENUM_IDX": index for i, index in enumerate(prop.enum_indices)}
679 val = prop.val_as_tokens if spec.enum_tokenizable else (prop.val if isinstance(prop.val, list) else [prop.val])
680
681 for i, subval in enumerate(val):
682 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS
James Royab959d52024-12-05 21:10:53 +0800683 ret[f"{macro}_IDX_{i}_EXISTS"] = 1
Joel Hirsbrunner8b02bc92024-10-04 21:29:37 +0200684 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_ENUM_VAL_<val>_EXISTS 1
James Royab959d52024-12-05 21:10:53 +0800685 ret[f"{macro}_IDX_{i}_ENUM_VAL_{subval}_EXISTS"] = 1
Joel Hirsbrunner8b02bc92024-10-04 21:29:37 +0200686
687 return ret
688
689
690def array_macros(prop: edtlib.Property, macro: str):
691 # Returns a dict of macros for array property 'prop'.
692 # The 'macro' argument is the N_<node-id>_P_<prop-id> part.
693
694 ret = {}
695 for i, subval in enumerate(prop.val):
696 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS
James Royab959d52024-12-05 21:10:53 +0800697 ret[f"{macro}_IDX_{i}_EXISTS"] = 1
Joel Hirsbrunner8b02bc92024-10-04 21:29:37 +0200698
699 # DT_N_<node-id>_P_<prop-id>_IDX_<i>
700 if isinstance(subval, str):
James Royab959d52024-12-05 21:10:53 +0800701 ret[f"{macro}_IDX_{i}"] = quote_str(subval)
Joel Hirsbrunner8b02bc92024-10-04 21:29:37 +0200702 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_STRING_...
James Royab959d52024-12-05 21:10:53 +0800703 ret.update(string_macros(f"{macro}_IDX_{i}", subval))
Joel Hirsbrunner8b02bc92024-10-04 21:29:37 +0200704 else:
James Royab959d52024-12-05 21:10:53 +0800705 ret[f"{macro}_IDX_{i}"] = subval
Joel Hirsbrunner8b02bc92024-10-04 21:29:37 +0200706
707 return ret
708
709
Florian Grandelde846c72024-09-06 10:15:55 +0200710def write_dep_info(node: edtlib.Node) -> None:
Martí Bolívar305379e2020-06-08 14:59:19 -0700711 # Write dependency-related information about the node.
712
713 def fmt_dep_list(dep_list):
714 if dep_list:
715 # Sort the list by dependency ordinal for predictability.
716 sorted_list = sorted(dep_list, key=lambda node: node.dep_ordinal)
James Roy802eac72024-12-28 21:23:31 +0800717 return ("\\\n\t" + " \\\n\t"
718 .join(f"{n.dep_ordinal}, /* {n.path} */"
719 for n in sorted_list))
Martí Bolívar305379e2020-06-08 14:59:19 -0700720 else:
721 return "/* nothing */"
722
Luca Burelli16d71d02025-01-09 11:45:23 +0100723 out_comment("Node's hash:")
724 out_dt_define(f"{node.z_path_id}_HASH", node.hash)
725
Martí Bolívar305379e2020-06-08 14:59:19 -0700726 out_comment("Node's dependency ordinal:")
727 out_dt_define(f"{node.z_path_id}_ORD", node.dep_ordinal)
Jordan Yatesb6e03412023-07-15 21:47:02 +1000728 out_dt_define(f"{node.z_path_id}_ORD_STR_SORTABLE", f"{node.dep_ordinal:0>5}")
Martí Bolívar305379e2020-06-08 14:59:19 -0700729
730 out_comment("Ordinals for what this node depends on directly:")
731 out_dt_define(f"{node.z_path_id}_REQUIRES_ORDS",
732 fmt_dep_list(node.depends_on))
733
734 out_comment("Ordinals for what depends directly on this node:")
735 out_dt_define(f"{node.z_path_id}_SUPPORTS_ORDS",
736 fmt_dep_list(node.required_by))
737
738
Florian Grandelde846c72024-09-06 10:15:55 +0200739def prop2value(prop: edtlib.Property) -> edtlib.PropertyValType:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800740 # Gets the macro value for property 'prop', if there is
741 # a single well-defined C rvalue that it can be represented as.
742 # Returns None if there isn't one.
743
744 if prop.type == "string":
745 return quote_str(prop.val)
746
747 if prop.type == "int":
748 return prop.val
749
750 if prop.type == "boolean":
751 return 1 if prop.val else 0
752
753 if prop.type in ["array", "uint8-array"]:
754 return list2init(f"{val} /* {hex(val)} */" for val in prop.val)
755
756 if prop.type == "string-array":
757 return list2init(quote_str(val) for val in prop.val)
758
759 # phandle, phandles, phandle-array, path, compound: nothing
760 return None
761
762
Florian Grandelde846c72024-09-06 10:15:55 +0200763def prop_len(prop: edtlib.Property) -> Optional[int]:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800764 # Returns the property's length if and only if we should generate
765 # a _LEN macro for the property. Otherwise, returns None.
766 #
Martí Bolívar0c29e072023-05-06 14:42:15 -0700767 # The set of types handled here coincides with the allowable types
768 # that can be used with DT_PROP_LEN(). If you change this set,
769 # make sure to update the doxygen string for that macro, and make
770 # sure that DT_FOREACH_PROP_ELEM() works for the new types too.
771 #
Neil Armstrong1e8f0f32021-06-24 10:14:05 +0200772 # This deliberately excludes ranges, dma-ranges, reg and interrupts.
Martí Bolívardc85edd2020-02-28 15:26:52 -0800773 # While they have array type, their lengths as arrays are
774 # basically nonsense semantically due to #address-cells and
Neil Armstrong1e8f0f32021-06-24 10:14:05 +0200775 # #size-cells for "reg", #interrupt-cells for "interrupts"
776 # and #address-cells, #size-cells and the #address-cells from the
777 # parent node for "ranges" and "dma-ranges".
Martí Bolívardc85edd2020-02-28 15:26:52 -0800778 #
779 # We have special purpose macros for the number of register blocks
780 # / interrupt specifiers. Excluding them from this list means
781 # DT_PROP_LEN(node_id, ...) fails fast at the devicetree.h layer
782 # with a build error. This forces users to switch to the right
783 # macros.
784
Martí Bolívar8aa83f62023-05-05 15:48:53 -0700785 if prop.type in ["phandle", "string"]:
786 # phandle is treated as a phandles of length 1.
787 # string is treated as a string-array of length 1.
Martí Bolívardc85edd2020-02-28 15:26:52 -0800788 return 1
789
790 if (prop.type in ["array", "uint8-array", "string-array",
791 "phandles", "phandle-array"] and
Neil Armstrong1e8f0f32021-06-24 10:14:05 +0200792 prop.name not in ["ranges", "dma-ranges", "reg", "interrupts"]):
Martí Bolívardc85edd2020-02-28 15:26:52 -0800793 return len(prop.val)
794
795 return None
796
797
Florian Grandelde846c72024-09-06 10:15:55 +0200798def phandle_macros(prop: edtlib.Property, macro: str) -> dict:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800799 # Returns a dict of macros for phandle or phandles property 'prop'.
800 #
801 # The 'macro' argument is the N_<node-id>_P_<prop-id> bit.
802 #
803 # These are currently special because we can't serialize their
804 # values without using label properties, which we're trying to get
805 # away from needing in Zephyr. (Label properties are great for
806 # humans, but have drawbacks for code size and boot time.)
807 #
808 # The names look a bit weird to make it easier for devicetree.h
809 # to use the same macros for phandle, phandles, and phandle-array.
810
811 ret = {}
812
813 if prop.type == "phandle":
814 # A phandle is treated as a phandles with fixed length 1.
Kumar Gala7b9fbcd2021-08-12 14:57:49 -0500815 ret[f"{macro}"] = f"DT_{prop.val.z_path_id}"
816 ret[f"{macro}_IDX_0"] = f"DT_{prop.val.z_path_id}"
Martí Bolívardc85edd2020-02-28 15:26:52 -0800817 ret[f"{macro}_IDX_0_PH"] = f"DT_{prop.val.z_path_id}"
Martí Bolívarffc03122020-11-12 20:27:20 -0800818 ret[f"{macro}_IDX_0_EXISTS"] = 1
Martí Bolívardc85edd2020-02-28 15:26:52 -0800819 elif prop.type == "phandles":
820 for i, node in enumerate(prop.val):
Kumar Gala7b9fbcd2021-08-12 14:57:49 -0500821 ret[f"{macro}_IDX_{i}"] = f"DT_{node.z_path_id}"
Martí Bolívardc85edd2020-02-28 15:26:52 -0800822 ret[f"{macro}_IDX_{i}_PH"] = f"DT_{node.z_path_id}"
Martí Bolívarffc03122020-11-12 20:27:20 -0800823 ret[f"{macro}_IDX_{i}_EXISTS"] = 1
Martí Bolívardc85edd2020-02-28 15:26:52 -0800824 elif prop.type == "phandle-array":
825 for i, entry in enumerate(prop.val):
Martí Bolívar38ede5a2020-12-17 14:12:01 -0800826 if entry is None:
827 # Unspecified element. The phandle-array at this index
828 # does not point at a ControllerAndData value, but
829 # subsequent indices in the array may.
830 ret[f"{macro}_IDX_{i}_EXISTS"] = 0
831 continue
832
Martí Bolívardc85edd2020-02-28 15:26:52 -0800833 ret.update(controller_and_data_macros(entry, i, macro))
834
835 return ret
836
837
Florian Grandelde846c72024-09-06 10:15:55 +0200838def controller_and_data_macros(entry: edtlib.ControllerAndData, i: int, macro: str):
Martí Bolívardc85edd2020-02-28 15:26:52 -0800839 # Helper procedure used by phandle_macros().
840 #
841 # Its purpose is to write the "controller" (i.e. label property of
842 # the phandle's node) and associated data macros for a
843 # ControllerAndData.
844
845 ret = {}
846 data = entry.data
847
Martí Bolívarffc03122020-11-12 20:27:20 -0800848 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS
849 ret[f"{macro}_IDX_{i}_EXISTS"] = 1
Martí Bolívardc85edd2020-02-28 15:26:52 -0800850 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_PH
851 ret[f"{macro}_IDX_{i}_PH"] = f"DT_{entry.controller.z_path_id}"
852 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_VAL_<VAL>
853 for cell, val in data.items():
854 ret[f"{macro}_IDX_{i}_VAL_{str2ident(cell)}"] = val
855 ret[f"{macro}_IDX_{i}_VAL_{str2ident(cell)}_EXISTS"] = 1
856
857 if not entry.name:
858 return ret
859
860 name = str2ident(entry.name)
Erwan Gouriou6c8617a2020-04-06 14:56:11 +0200861 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_EXISTS
862 ret[f"{macro}_IDX_{i}_EXISTS"] = 1
Martí Bolívardc85edd2020-02-28 15:26:52 -0800863 # DT_N_<node-id>_P_<prop-id>_IDX_<i>_NAME
864 ret[f"{macro}_IDX_{i}_NAME"] = quote_str(entry.name)
865 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_PH
866 ret[f"{macro}_NAME_{name}_PH"] = f"DT_{entry.controller.z_path_id}"
Erwan Gouriou6c8617a2020-04-06 14:56:11 +0200867 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_EXISTS
868 ret[f"{macro}_NAME_{name}_EXISTS"] = 1
Martí Bolívardc85edd2020-02-28 15:26:52 -0800869 # DT_N_<node-id>_P_<prop-id>_NAME_<NAME>_VAL_<VAL>
870 for cell, val in data.items():
871 cell_ident = str2ident(cell)
James Roy802eac72024-12-28 21:23:31 +0800872 ret[f"{macro}_NAME_{name}_VAL_{cell_ident}"] = (
873 f"DT_{macro}_IDX_{i}_VAL_{cell_ident}")
Martí Bolívardc85edd2020-02-28 15:26:52 -0800874 ret[f"{macro}_NAME_{name}_VAL_{cell_ident}_EXISTS"] = 1
875
876 return ret
877
878
Florian Grandelde846c72024-09-06 10:15:55 +0200879def write_chosen(edt: edtlib.EDT):
Martí Bolívardc85edd2020-02-28 15:26:52 -0800880 # Tree-wide information such as chosen nodes is printed here.
881
882 out_comment("Chosen nodes\n")
883 chosen = {}
884 for name, node in edt.chosen_nodes.items():
885 chosen[f"DT_CHOSEN_{str2ident(name)}"] = f"DT_{node.z_path_id}"
886 chosen[f"DT_CHOSEN_{str2ident(name)}_EXISTS"] = 1
Kumar Gala299bfd02020-03-25 15:32:58 -0500887 max_len = max(map(len, chosen), default=0)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800888 for macro, value in chosen.items():
889 out_define(macro, value, width=max_len)
890
891
Florian Grandelde846c72024-09-06 10:15:55 +0200892def write_global_macros(edt: edtlib.EDT):
Martí Bolívar190197e2022-07-20 13:10:33 -0700893 # Global or tree-wide information, such as number of instances
894 # with status "okay" for each compatible, is printed here.
Martí Bolívardc85edd2020-02-28 15:26:52 -0800895
Martí Bolívarf0d11f72022-07-20 13:15:56 -0700896
897 out_comment("Macros for iterating over all nodes and enabled nodes")
898 out_dt_define("FOREACH_HELPER(fn)",
899 " ".join(f"fn(DT_{node.z_path_id})" for node in edt.nodes))
900 out_dt_define("FOREACH_OKAY_HELPER(fn)",
901 " ".join(f"fn(DT_{node.z_path_id})" for node in edt.nodes
902 if node.status == "okay"))
Carlo Caione935268e2023-07-04 17:50:12 +0200903 out_dt_define("FOREACH_VARGS_HELPER(fn, ...)",
904 " ".join(f"fn(DT_{node.z_path_id}, __VA_ARGS__)" for node in edt.nodes))
905 out_dt_define("FOREACH_OKAY_VARGS_HELPER(fn, ...)",
906 " ".join(f"fn(DT_{node.z_path_id}, __VA_ARGS__)" for node in edt.nodes
907 if node.status == "okay"))
Martí Bolívarf0d11f72022-07-20 13:15:56 -0700908
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700909 n_okay_macros = {}
910 for_each_macros = {}
911 compat2buses = defaultdict(list) # just for "okay" nodes
912 for compat, okay_nodes in edt.compat2okay.items():
913 for node in okay_nodes:
Daniel Leung418c9152022-08-26 10:52:32 -0700914 buses = node.on_buses
915 for bus in buses:
916 if bus is not None and bus not in compat2buses[compat]:
917 compat2buses[compat].append(bus)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800918
Martí Bolívar63d55292020-04-06 15:13:53 -0700919 ident = str2ident(compat)
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700920 n_okay_macros[f"DT_N_INST_{ident}_NUM_OKAY"] = len(okay_nodes)
Martí Bolívare7d42ff2021-08-05 15:24:50 -0700921
922 # Helpers for non-INST for-each macros that take node
923 # identifiers as arguments.
James Roy802eac72024-12-28 21:23:31 +0800924 for_each_macros[f"DT_FOREACH_OKAY_{ident}(fn)"] = (
Martí Bolívare7d42ff2021-08-05 15:24:50 -0700925 " ".join(f"fn(DT_{node.z_path_id})"
James Roy802eac72024-12-28 21:23:31 +0800926 for node in okay_nodes))
927 for_each_macros[f"DT_FOREACH_OKAY_VARGS_{ident}(fn, ...)"] = (
Martí Bolívare7d42ff2021-08-05 15:24:50 -0700928 " ".join(f"fn(DT_{node.z_path_id}, __VA_ARGS__)"
James Roy802eac72024-12-28 21:23:31 +0800929 for node in okay_nodes))
Martí Bolívare7d42ff2021-08-05 15:24:50 -0700930
931 # Helpers for INST versions of for-each macros, which take
932 # instance numbers. We emit separate helpers for these because
933 # avoiding an intermediate node_id --> instance number
934 # conversion in the preprocessor helps to keep the macro
935 # expansions simpler. That hopefully eases debugging.
James Roy802eac72024-12-28 21:23:31 +0800936 for_each_macros[f"DT_FOREACH_OKAY_INST_{ident}(fn)"] = (
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700937 " ".join(f"fn({edt.compat2nodes[compat].index(node)})"
James Roy802eac72024-12-28 21:23:31 +0800938 for node in okay_nodes))
939 for_each_macros[f"DT_FOREACH_OKAY_INST_VARGS_{ident}(fn, ...)"] = (
Arvin Farahmandd0b9c032021-05-06 11:19:29 -0400940 " ".join(f"fn({edt.compat2nodes[compat].index(node)}, __VA_ARGS__)"
James Roy802eac72024-12-28 21:23:31 +0800941 for node in okay_nodes))
Arvin Farahmandd0b9c032021-05-06 11:19:29 -0400942
Kumar Galabd973782020-05-06 20:54:29 -0500943 for compat, nodes in edt.compat2nodes.items():
944 for node in nodes:
945 if compat == "fixed-partitions":
946 for child in node.children.values():
947 if "label" in child.props:
948 label = child.props["label"].val
949 macro = f"COMPAT_{str2ident(compat)}_LABEL_{str2ident(label)}"
950 val = f"DT_{child.z_path_id}"
951
952 out_dt_define(macro, val)
953 out_dt_define(macro + "_EXISTS", 1)
954
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700955 out_comment('Macros for compatibles with status "okay" nodes\n')
956 for compat, okay_nodes in edt.compat2okay.items():
957 if okay_nodes:
958 out_define(f"DT_COMPAT_HAS_OKAY_{str2ident(compat)}", 1)
959
960 out_comment('Macros for status "okay" instances of each compatible\n')
961 for macro, value in n_okay_macros.items():
Martí Bolívar63d55292020-04-06 15:13:53 -0700962 out_define(macro, value)
963 for macro, value in for_each_macros.items():
964 out_define(macro, value)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800965
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700966 out_comment('Bus information for status "okay" nodes of each compatible\n')
Martí Bolívara3fae2f2020-03-25 14:18:27 -0700967 for compat, buses in compat2buses.items():
968 for bus in buses:
969 out_define(
970 f"DT_COMPAT_{str2ident(compat)}_BUS_{str2ident(bus)}", 1)
Martí Bolívardc85edd2020-02-28 15:26:52 -0800971
Florian Grandelde846c72024-09-06 10:15:55 +0200972
973def str2ident(s: str) -> str:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800974 # Converts 's' to a form suitable for (part of) an identifier
975
976 return re.sub('[-,.@/+]', '_', s.lower())
977
978
Florian Grandelde846c72024-09-06 10:15:55 +0200979def list2init(l: Iterable[str]) -> str:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800980 # Converts 'l', a Python list (or iterable), to a C array initializer
981
982 return "{" + ", ".join(l) + "}"
983
984
Florian Grandelde846c72024-09-06 10:15:55 +0200985def out_dt_define(
986 macro: str,
987 val: str,
988 width: Optional[int] = None,
989 deprecation_msg: Optional[str] = None,
990) -> str:
Martí Bolívardc85edd2020-02-28 15:26:52 -0800991 # Writes "#define DT_<macro> <val>" to the header file
992 #
993 # The macro will be left-justified to 'width' characters if that
994 # is specified, and the value will follow immediately after in
995 # that case. Otherwise, this function decides how to add
996 # whitespace between 'macro' and 'val'.
997 #
998 # If a 'deprecation_msg' string is passed, the generated identifiers will
999 # generate a warning if used, via __WARN(<deprecation_msg>)).
1000 #
1001 # Returns the full generated macro for 'macro', with leading "DT_".
James Royab959d52024-12-05 21:10:53 +08001002 ret = f"DT_{macro}"
Martí Bolívardc85edd2020-02-28 15:26:52 -08001003 out_define(ret, val, width=width, deprecation_msg=deprecation_msg)
1004 return ret
1005
1006
Florian Grandelde846c72024-09-06 10:15:55 +02001007def out_define(
1008 macro: str,
1009 val: str,
1010 width: Optional[int] = None,
1011 deprecation_msg: Optional[str] = None,
1012) -> None:
Martí Bolívardc85edd2020-02-28 15:26:52 -08001013 # Helper for out_dt_define(). Outputs "#define <macro> <val>",
1014 # adds a deprecation message if given, and allocates whitespace
1015 # unless told not to.
1016
1017 warn = fr' __WARN("{deprecation_msg}")' if deprecation_msg else ""
1018
1019 if width:
1020 s = f"#define {macro.ljust(width)}{warn} {val}"
1021 else:
1022 s = f"#define {macro}{warn} {val}"
1023
1024 print(s, file=header_file)
1025
1026
Florian Grandelde846c72024-09-06 10:15:55 +02001027def out_comment(s: str, blank_before=True) -> None:
Martí Bolívardc85edd2020-02-28 15:26:52 -08001028 # Writes 's' as a comment to the header and configuration file. 's' is
1029 # allowed to have multiple lines. blank_before=True adds a blank line
1030 # before the comment.
1031
1032 if blank_before:
1033 print(file=header_file)
1034
1035 if "\n" in s:
1036 # Format multi-line comments like
1037 #
1038 # /*
1039 # * first line
1040 # * second line
1041 # *
1042 # * empty line before this line
1043 # */
1044 res = ["/*"]
1045 for line in s.splitlines():
1046 # Avoid an extra space after '*' for empty lines. They turn red in
1047 # Vim if space error checking is on, which is annoying.
James Royab959d52024-12-05 21:10:53 +08001048 res.append(f" * {line}".rstrip())
Martí Bolívardc85edd2020-02-28 15:26:52 -08001049 res.append(" */")
1050 print("\n".join(res), file=header_file)
1051 else:
1052 # Format single-line comments like
1053 #
1054 # /* foo bar */
James Royab959d52024-12-05 21:10:53 +08001055 print(f"/* {s} */", file=header_file)
Martí Bolívardc85edd2020-02-28 15:26:52 -08001056
Joel Spadin6edefd82024-09-14 20:17:32 -05001057ESCAPE_TABLE = str.maketrans(
1058 {
1059 "\n": "\\n",
1060 "\r": "\\r",
1061 "\\": "\\\\",
1062 '"': '\\"',
1063 }
1064)
Martí Bolívardc85edd2020-02-28 15:26:52 -08001065
Joel Spadin6edefd82024-09-14 20:17:32 -05001066
1067def escape(s: str) -> str:
1068 # Backslash-escapes any double quotes, backslashes, and new lines in 's'
1069
1070 return s.translate(ESCAPE_TABLE)
Martí Bolívardc85edd2020-02-28 15:26:52 -08001071
1072
Florian Grandelde846c72024-09-06 10:15:55 +02001073def quote_str(s: str) -> str:
Martí Bolívardc85edd2020-02-28 15:26:52 -08001074 # Puts quotes around 's' and escapes any double quotes and
1075 # backslashes within it
1076
1077 return f'"{escape(s)}"'
1078
1079
Joel Spadin6edefd82024-09-14 20:17:32 -05001080def escape_unquoted(s: str) -> str:
1081 # C macros cannot contain line breaks, so replace them with spaces.
1082 # Whitespace is used to separate preprocessor tokens, but it does not matter
1083 # which whitespace characters are used, so a line break and a space are
1084 # equivalent with regards to unquoted strings being used as C code.
1085
1086 return s.replace("\r", " ").replace("\n", " ")
1087
1088
Florian Grandelde846c72024-09-06 10:15:55 +02001089def err(s: str) -> NoReturn:
Martí Bolívardc85edd2020-02-28 15:26:52 -08001090 raise Exception(s)
1091
1092
1093if __name__ == "__main__":
1094 main()