blob: b0d180fbefe39e96d543d2cef0dcba574ca9ab17 [file] [log] [blame]
Ulf Magnusson62d57412018-12-17 20:09:47 +01001# Copyright (c) 2019 Nordic Semiconductor ASA
2# Copyright (c) 2019 Linaro Limited
3# SPDX-License-Identifier: BSD-3-Clause
4
5# Tip: You can view just the documentation with 'pydoc3 edtlib'
6
7"""
Ulf Magnusson73ac1462019-09-23 05:14:18 +02008Library for working with devicetrees at a higher level compared to dtlib. Like
9dtlib, this library presents a tree of devicetree nodes, but the nodes are
10augmented with information from bindings and include some interpretation of
Martí Bolívar8640a8d2020-10-12 11:33:01 -070011properties. Some of this interpretation is based on conventions established
12by the Linux kernel, so the Documentation/devicetree/bindings in the Linux
13source code is sometimes good reference material.
Ulf Magnusson62d57412018-12-17 20:09:47 +010014
Martí Bolívar7165b772020-10-12 12:32:25 -070015Bindings are YAML files that describe devicetree nodes. Devicetree
Martí Bolívar8640a8d2020-10-12 11:33:01 -070016nodes are usually mapped to bindings via their 'compatible = "..."' property,
17but a binding can also come from a 'child-binding:' key in the binding for the
Martí Bolívar7165b772020-10-12 12:32:25 -070018parent devicetree node.
Ulf Magnusson62d57412018-12-17 20:09:47 +010019
Ulf Magnusson73ac1462019-09-23 05:14:18 +020020Each devicetree node (dtlib.Node) gets a corresponding edtlib.Node instance,
21which has all the information related to the node.
Ulf Magnusson62d57412018-12-17 20:09:47 +010022
Martí Bolívar7165b772020-10-12 12:32:25 -070023The top-level entry points for the library are the EDT and Binding classes.
Martí Bolívar2c19ccc2020-10-19 21:42:53 -070024See their constructor docstrings for details. There is also a
25bindings_from_paths() helper function.
Ulf Magnusson62d57412018-12-17 20:09:47 +010026"""
27
Martí Bolívara8612f72020-09-29 17:04:34 -070028# NOTE: testedtlib.py is the test suite for this library.
Ulf Magnusson62d57412018-12-17 20:09:47 +010029
30# Implementation notes
31# --------------------
32#
33# A '_' prefix on an identifier in Python is a convention for marking it private.
34# Please do not access private things. Instead, think of what API you need, and
35# add it.
36#
Ulf Magnusson35166c42020-02-14 03:27:59 +010037# This module is not meant to have any global state. It should be possible to
38# create several EDT objects with independent binding paths and flags. If you
39# need to add a configuration parameter or the like, store it in the EDT
40# instance, and initialize it e.g. with a constructor argument.
41#
Ulf Magnusson62d57412018-12-17 20:09:47 +010042# This library is layered on top of dtlib, and is not meant to expose it to
43# clients. This keeps the header generation script simple.
44#
45# General biased advice:
46#
47# - Consider using @property for APIs that don't need parameters. It makes
48# functions look like attributes, which is less awkward in clients, and makes
49# it easy to switch back and forth between variables and functions.
50#
51# - Think about the data type of the thing you're exposing. Exposing something
52# as e.g. a list or a dictionary is often nicer and more flexible than adding
53# a function.
54#
55# - Avoid get_*() prefixes on functions. Name them after the thing they return
56# instead. This often makes the code read more naturally in callers.
57#
58# Also, consider using @property instead of get_*().
59#
60# - Don't expose dtlib stuff directly.
61#
62# - Add documentation for any new APIs you add.
63#
64# The convention here is that docstrings (quoted strings) are used for public
65# APIs, and "doc comments" for internal functions.
66#
67# @properties are documented in the class docstring, as if they were
68# variables. See the existing @properties for a template.
Ulf Magnusson62d57412018-12-17 20:09:47 +010069
Ulf Magnusson88db84b2020-01-28 04:46:12 +010070from collections import OrderedDict, defaultdict
Martí Bolívar09858492020-12-08 09:41:49 -080071import logging
Ulf Magnussond55ed932019-11-12 18:37:15 +010072import os
73import re
Ulf Magnussond55ed932019-11-12 18:37:15 +010074
75import yaml
76try:
77 # Use the C LibYAML parser if available, rather than the Python parser.
78 # This makes e.g. gen_defines.py more than twice as fast.
79 from yaml import CLoader as Loader
80except ImportError:
81 from yaml import Loader
82
Peter Bigot32e61592020-09-01 10:22:31 -050083from dtlib import DT, DTError, to_num, to_nums, TYPE_EMPTY, TYPE_BYTES, \
84 TYPE_NUM, TYPE_NUMS, TYPE_STRING, TYPE_STRINGS, \
85 TYPE_PHANDLE, TYPE_PHANDLES, TYPE_PHANDLES_AND_NUMS
Ulf Magnussond55ed932019-11-12 18:37:15 +010086from grutils import Graph
87
Andrei Gansarie87fec12019-11-20 16:31:55 +020088
Ulf Magnusson62d57412018-12-17 20:09:47 +010089#
90# Public classes
91#
92
93
94class EDT:
95 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +020096 Represents a devicetree augmented with information from bindings.
Ulf Magnusson62d57412018-12-17 20:09:47 +010097
98 These attributes are available on EDT objects:
99
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200100 nodes:
101 A list of Node objects for the nodes that appear in the devicetree
Ulf Magnusson62d57412018-12-17 20:09:47 +0100102
Martí Bolívareac56e42020-05-06 11:22:30 -0700103 compat2nodes:
104 A collections.defaultdict that maps each 'compatible' string that appears
105 on some Node to a list of Nodes with that compatible.
106
107 compat2okay:
108 Like compat2nodes, but just for nodes with status 'okay'.
109
Martí Bolívar2707d2a2020-04-22 18:05:41 -0700110 label2node:
111 A collections.OrderedDict that maps a node label to the node with
112 that label.
113
Peter Bigot44394e32020-08-24 13:35:25 -0700114 dep_ord2node:
115 A collections.OrderedDict that maps an ordinal to the node with
116 that dependency ordinal.
117
Martí Bolívar0de9a082020-03-11 16:10:16 -0700118 chosen_nodes:
119 A collections.OrderedDict that maps the properties defined on the
120 devicetree's /chosen node to their values. 'chosen' is indexed by
121 property name (a string), and values are converted to Node objects.
122 Note that properties of the /chosen node which can't be converted
123 to a Node are not included in the value.
Ulf Magnusson88db84b2020-01-28 04:46:12 +0100124
Ulf Magnusson62d57412018-12-17 20:09:47 +0100125 dts_path:
126 The .dts path passed to __init__()
127
Ulf Magnussona758af42020-01-29 06:25:20 +0100128 dts_source:
129 The final DTS source code of the loaded devicetree after merging nodes
130 and processing /delete-node/ and /delete-property/, as a string
131
Michael Scottb8909432019-08-02 11:42:06 -0700132 bindings_dirs:
133 The bindings directory paths passed to __init__()
Martí Bolívare76b7202020-07-01 10:37:34 -0700134
Martí Bolívarb6db2012020-08-24 13:33:53 -0700135 scc_order:
136 A list of lists of Nodes. All elements of each list
137 depend on each other, and the Nodes in any list do not depend
138 on any Node in a subsequent list. Each list defines a Strongly
139 Connected Component (SCC) of the graph.
140
141 For an acyclic graph each list will be a singleton. Cycles
142 will be represented by lists with multiple nodes. Cycles are
143 not expected to be present in devicetree graphs.
144
Martí Bolívare76b7202020-07-01 10:37:34 -0700145 The standard library's pickle module can be used to marshal and
146 unmarshal EDT objects.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100147 """
Martí Bolívar09858492020-12-08 09:41:49 -0800148 def __init__(self, dts, bindings_dirs,
Kumar Galabc48f1c2020-05-01 12:33:00 -0500149 warn_reg_unit_address_mismatch=True,
Kumar Gala3a685662020-05-08 05:40:52 -0500150 default_prop_types=True,
Peter Bigot32e61592020-09-01 10:22:31 -0500151 support_fixed_partitions_on_any_bus=True,
152 infer_binding_for_paths=None):
Martí Bolívar7165b772020-10-12 12:32:25 -0700153 """EDT constructor.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100154
155 dts:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200156 Path to devicetree .dts file
Ulf Magnusson62d57412018-12-17 20:09:47 +0100157
Michael Scottb8909432019-08-02 11:42:06 -0700158 bindings_dirs:
159 List of paths to directories containing bindings, in YAML format.
160 These directories are recursively searched for .yaml files.
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200161
Ulf Magnusson35166c42020-02-14 03:27:59 +0100162 warn_reg_unit_address_mismatch (default: True):
Martí Bolívar09858492020-12-08 09:41:49 -0800163 If True, a warning is logged if a node has a 'reg' property where
Ulf Magnusson35166c42020-02-14 03:27:59 +0100164 the address of the first entry does not match the unit address of the
165 node
Kumar Galabc48f1c2020-05-01 12:33:00 -0500166
167 default_prop_types (default: True):
168 If True, default property types will be used when a node has no
169 bindings.
Kumar Gala3a685662020-05-08 05:40:52 -0500170
171 support_fixed_partitions_on_any_bus (default True):
172 If True, set the Node.bus for 'fixed-partitions' compatible nodes
173 to None. This allows 'fixed-partitions' binding to match regardless
174 of the bus the 'fixed-partition' is under.
Peter Bigot32e61592020-09-01 10:22:31 -0500175
176 infer_binding_for_paths (default: None):
177 An iterable of devicetree paths identifying nodes for which bindings
178 should be inferred from the node content. (Child nodes are not
179 processed.) Pass none if no nodes should support inferred bindings.
180
Ulf Magnusson62d57412018-12-17 20:09:47 +0100181 """
Ulf Magnusson35166c42020-02-14 03:27:59 +0100182 self._warn_reg_unit_address_mismatch = warn_reg_unit_address_mismatch
Kumar Galabc48f1c2020-05-01 12:33:00 -0500183 self._default_prop_types = default_prop_types
Kumar Gala3a685662020-05-08 05:40:52 -0500184 self._fixed_partitions_no_bus = support_fixed_partitions_on_any_bus
Peter Bigot32e61592020-09-01 10:22:31 -0500185 self._infer_binding_for_paths = set(infer_binding_for_paths or [])
Ulf Magnusson35166c42020-02-14 03:27:59 +0100186
Ulf Magnusson62d57412018-12-17 20:09:47 +0100187 self.dts_path = dts
Michael Scottb8909432019-08-02 11:42:06 -0700188 self.bindings_dirs = bindings_dirs
Ulf Magnusson62d57412018-12-17 20:09:47 +0100189
190 self._dt = DT(dts)
Ulf Magnussonacf276f2019-08-07 19:33:45 +0200191 _check_dt(self._dt)
Ulf Magnusson62d57412018-12-17 20:09:47 +0100192
Martí Bolívar7165b772020-10-12 12:32:25 -0700193 self._init_compat2binding()
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200194 self._init_nodes()
Martí Bolívarb6db2012020-08-24 13:33:53 -0700195 self._init_graph()
Martí Bolívar2707d2a2020-04-22 18:05:41 -0700196 self._init_luts()
Ulf Magnusson62d57412018-12-17 20:09:47 +0100197
Martí Bolívarf673e202020-12-08 12:55:44 -0800198 self._check()
199
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200200 def get_node(self, path):
Ulf Magnusson62d57412018-12-17 20:09:47 +0100201 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200202 Returns the Node at the DT path or alias 'path'. Raises EDTError if the
203 path or alias doesn't exist.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100204 """
205 try:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200206 return self._node2enode[self._dt.get_node(path)]
Ulf Magnusson62d57412018-12-17 20:09:47 +0100207 except DTError as e:
208 _err(e)
209
Martí Bolívar0de9a082020-03-11 16:10:16 -0700210 @property
211 def chosen_nodes(self):
212 ret = OrderedDict()
213
214 try:
215 chosen = self._dt.get_node("/chosen")
216 except DTError:
217 return ret
218
219 for name, prop in chosen.props.items():
220 try:
221 node = prop.to_path()
222 except DTError:
223 # DTS value is not phandle or string, or path doesn't exist
224 continue
225
226 ret[name] = self._node2enode[node]
227
228 return ret
229
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200230 def chosen_node(self, name):
Ulf Magnusson62d57412018-12-17 20:09:47 +0100231 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200232 Returns the Node pointed at by the property named 'name' in /chosen, or
233 None if the property is missing
Ulf Magnusson62d57412018-12-17 20:09:47 +0100234 """
Martí Bolívar0de9a082020-03-11 16:10:16 -0700235 return self.chosen_nodes.get(name)
Ulf Magnusson62d57412018-12-17 20:09:47 +0100236
Ulf Magnussona758af42020-01-29 06:25:20 +0100237 @property
238 def dts_source(self):
239 return f"{self._dt}"
240
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200241 def __repr__(self):
242 return "<EDT for '{}', binding directories '{}'>".format(
243 self.dts_path, self.bindings_dirs)
244
Martí Bolívarb6db2012020-08-24 13:33:53 -0700245 @property
Peter A. Bigotea956f42019-09-27 11:31:36 -0500246 def scc_order(self):
Peter A. Bigotea956f42019-09-27 11:31:36 -0500247 try:
248 return self._graph.scc_order()
249 except Exception as e:
250 raise EDTError(e)
251
Martí Bolívarb6db2012020-08-24 13:33:53 -0700252 def _init_graph(self):
Peter A. Bigotea956f42019-09-27 11:31:36 -0500253 # Constructs a graph of dependencies between Node instances,
Martí Bolívarb6db2012020-08-24 13:33:53 -0700254 # which is usable for computing a partial order over the dependencies.
255 # The algorithm supports detecting dependency loops.
256 #
257 # Actually computing the SCC order is lazily deferred to the
258 # first time the scc_order property is read.
Peter A. Bigotea956f42019-09-27 11:31:36 -0500259
260 self._graph = Graph()
261
262 for node in self.nodes:
263 # A Node always depends on its parent.
264 for child in node.children.values():
265 self._graph.add_edge(child, node)
266
267 # A Node depends on any Nodes present in 'phandle',
268 # 'phandles', or 'phandle-array' property values.
269 for prop in node.props.values():
270 if prop.type == 'phandle':
271 self._graph.add_edge(node, prop.val)
272 elif prop.type == 'phandles':
273 for phandle_node in prop.val:
274 self._graph.add_edge(node, phandle_node)
275 elif prop.type == 'phandle-array':
276 for cd in prop.val:
Martí Bolívar38ede5a2020-12-17 14:12:01 -0800277 if cd is None:
278 continue
Peter A. Bigotea956f42019-09-27 11:31:36 -0500279 self._graph.add_edge(node, cd.controller)
280
281 # A Node depends on whatever supports the interrupts it
282 # generates.
283 for intr in node.interrupts:
284 self._graph.add_edge(node, intr.controller)
285
Martí Bolívar7165b772020-10-12 12:32:25 -0700286 def _init_compat2binding(self):
287 # Creates self._compat2binding, a dictionary that maps
288 # (<compatible>, <bus>) tuples (both strings) to Binding objects.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100289 #
Martí Bolívar7165b772020-10-12 12:32:25 -0700290 # The Binding objects are created from YAML files discovered
291 # in self.bindings_dirs as needed.
292 #
293 # For example, self._compat2binding["company,dev", "can"]
294 # contains the Binding for the 'company,dev' device, when it
295 # appears on the CAN bus.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100296 #
297 # For bindings that don't specify a bus, <bus> is None, so that e.g.
Martí Bolívar7165b772020-10-12 12:32:25 -0700298 # self._compat2binding["company,notonbus", None] is the Binding.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100299 #
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200300 # Only bindings for 'compatible' strings that appear in the devicetree
Ulf Magnusson62d57412018-12-17 20:09:47 +0100301 # are loaded.
302
Ulf Magnusson14138c32019-08-16 14:43:58 +0200303 dt_compats = _dt_compats(self._dt)
304 # Searches for any 'compatible' string mentioned in the devicetree
305 # files, with a regex
306 dt_compats_search = re.compile(
307 "|".join(re.escape(compat) for compat in dt_compats)
308 ).search
309
Martí Bolívar7165b772020-10-12 12:32:25 -0700310 self._binding_paths = _binding_paths(self.bindings_dirs)
311 self._binding_fname2path = {os.path.basename(path): path
312 for path in self._binding_paths}
Ulf Magnusson14138c32019-08-16 14:43:58 +0200313
Ulf Magnusson62d57412018-12-17 20:09:47 +0100314 self._compat2binding = {}
315 for binding_path in self._binding_paths:
Ulf Magnusson14138c32019-08-16 14:43:58 +0200316 with open(binding_path, encoding="utf-8") as f:
317 contents = f.read()
318
319 # As an optimization, skip parsing files that don't contain any of
Ulf Magnussonb92ceb72019-10-29 08:48:05 +0100320 # the .dts 'compatible' strings, which should be reasonably safe
Ulf Magnusson14138c32019-08-16 14:43:58 +0200321 if not dt_compats_search(contents):
322 continue
323
324 # Load the binding and check that it actually matches one of the
325 # compatibles. Might get false positives above due to comments and
326 # stuff.
327
Ulf Magnusson4c6ea2d2019-09-19 22:54:55 +0200328 try:
329 # Parsed PyYAML output (Python lists/dictionaries/strings/etc.,
330 # representing the file)
Martí Bolívar7165b772020-10-12 12:32:25 -0700331 raw = yaml.load(contents, Loader=_BindingLoader)
Ulf Magnusson4c6ea2d2019-09-19 22:54:55 +0200332 except yaml.YAMLError as e:
Martí Bolívar09858492020-12-08 09:41:49 -0800333 _LOG.warning(
334 f"'{binding_path}' appears in binding directories "
335 f"but isn't valid YAML: {e}")
Ulf Magnusson4c6ea2d2019-09-19 22:54:55 +0200336 continue
Ulf Magnusson14138c32019-08-16 14:43:58 +0200337
Martí Bolívar7165b772020-10-12 12:32:25 -0700338 # Convert the raw data to a Binding object, erroring out
339 # if necessary.
340 binding = self._binding(raw, binding_path, dt_compats)
Kumar Gala4e2988d2020-06-09 11:18:05 -0500341
Martí Bolívar7165b772020-10-12 12:32:25 -0700342 if binding is None:
343 # Either the file is not a binding or it's a binding
344 # whose compatible does not appear in the devicetree
345 # (picked up via some unrelated text in the binding
346 # file that happened to match a compatible).
Ulf Magnusson14138c32019-08-16 14:43:58 +0200347 continue
348
Ulf Magnussone3113802019-10-14 17:45:45 +0200349 # Do not allow two different bindings to have the same
Ulf Magnusson379145f2019-11-26 22:19:55 +0100350 # 'compatible:'/'on-bus:' combo
Martí Bolívar7165b772020-10-12 12:32:25 -0700351 old_binding = self._compat2binding.get((binding.compatible,
352 binding.on_bus))
Ulf Magnussone3113802019-10-14 17:45:45 +0200353 if old_binding:
Martí Bolívar7165b772020-10-12 12:32:25 -0700354 msg = (f"both {old_binding.path} and {binding_path} have "
355 f"'compatible: {binding.compatible}'")
356 if binding.on_bus is not None:
357 msg += f" and 'on-bus: {binding.on_bus}'"
Ulf Magnussone3113802019-10-14 17:45:45 +0200358 _err(msg)
359
Martí Bolívar7165b772020-10-12 12:32:25 -0700360 # Register the binding.
361 self._compat2binding[binding.compatible, binding.on_bus] = binding
Ulf Magnusson62d57412018-12-17 20:09:47 +0100362
Martí Bolívar7165b772020-10-12 12:32:25 -0700363 def _binding(self, raw, binding_path, dt_compats):
364 # Convert a 'raw' binding from YAML to a Binding object and return it.
Ulf Magnussond834b692019-08-21 16:41:03 +0200365 #
Martí Bolívar7165b772020-10-12 12:32:25 -0700366 # Error out if the raw data looks like an invalid binding.
367 #
368 # Return None if the file doesn't contain a binding or the
369 # binding's compatible isn't in dt_compats.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100370
Martí Bolívar7165b772020-10-12 12:32:25 -0700371 # Get the 'compatible:' string.
372 if raw is None or "compatible" not in raw:
373 # Empty file, binding fragment, spurious file, etc.
374 return None
Ulf Magnusson62d57412018-12-17 20:09:47 +0100375
Martí Bolívar7165b772020-10-12 12:32:25 -0700376 compatible = raw["compatible"]
Ulf Magnusson62d57412018-12-17 20:09:47 +0100377
Martí Bolívar7165b772020-10-12 12:32:25 -0700378 if compatible not in dt_compats:
379 # Not a compatible we care about.
380 return None
Kumar Galae75ac552020-04-08 15:25:56 -0500381
Martí Bolívar7165b772020-10-12 12:32:25 -0700382 # Initialize and return the Binding object.
Martí Bolívar09858492020-12-08 09:41:49 -0800383 return Binding(binding_path, self._binding_fname2path, raw=raw)
Ulf Magnusson62d57412018-12-17 20:09:47 +0100384
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200385 def _init_nodes(self):
386 # Creates a list of edtlib.Node objects from the dtlib.Node objects, in
387 # self.nodes
Ulf Magnusson62d57412018-12-17 20:09:47 +0100388
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200389 # Maps each dtlib.Node to its corresponding edtlib.Node
390 self._node2enode = {}
Ulf Magnusson62d57412018-12-17 20:09:47 +0100391
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200392 self.nodes = []
Ulf Magnusson62d57412018-12-17 20:09:47 +0100393
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200394 for dt_node in self._dt.node_iter():
395 # Warning: We depend on parent Nodes being created before their
Ulf Magnusson06b746c2019-08-09 20:38:17 +0200396 # children. This is guaranteed by node_iter().
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200397 node = Node()
398 node.edt = self
399 node._node = dt_node
Kumar Gala06edcb12020-05-07 15:39:21 -0500400 if "compatible" in node._node.props:
401 node.compats = node._node.props["compatible"].to_strings()
402 else:
403 node.compats = []
Kumar Gala3a685662020-05-08 05:40:52 -0500404 node.bus_node = node._bus_node(self._fixed_partitions_no_bus)
Martí Bolívar09858492020-12-08 09:41:49 -0800405 node._init_binding()
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200406 node._init_regs()
Ulf Magnusson06b746c2019-08-09 20:38:17 +0200407
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200408 self.nodes.append(node)
409 self._node2enode[dt_node] = node
Ulf Magnusson62d57412018-12-17 20:09:47 +0100410
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200411 for node in self.nodes:
Ulf Magnussoneef8d192019-10-30 16:05:30 +0100412 # These depend on all Node objects having been created, because
413 # they (either always or sometimes) reference other nodes, so we
414 # run them separately
Kumar Galabc48f1c2020-05-01 12:33:00 -0500415 node._init_props(default_prop_types=self._default_prop_types)
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200416 node._init_interrupts()
Ulf Magnussoneef8d192019-10-30 16:05:30 +0100417 node._init_pinctrls()
Ulf Magnusson62d57412018-12-17 20:09:47 +0100418
Martí Bolívare05c94e2020-07-01 13:25:15 -0700419 if self._warn_reg_unit_address_mismatch:
420 # This warning matches the simple_bus_reg warning in dtc
421 for node in self.nodes:
422 if node.regs and node.regs[0].addr != node.unit_addr:
Martí Bolívar09858492020-12-08 09:41:49 -0800423 _LOG.warning("unit address and first address in 'reg' "
424 f"(0x{node.regs[0].addr:x}) don't match for "
425 f"{node.path}")
Martí Bolívare05c94e2020-07-01 13:25:15 -0700426
Martí Bolívar2707d2a2020-04-22 18:05:41 -0700427 def _init_luts(self):
Martí Bolívareac56e42020-05-06 11:22:30 -0700428 # Initialize node lookup tables (LUTs).
Ulf Magnusson88db84b2020-01-28 04:46:12 +0100429
Martí Bolívar2707d2a2020-04-22 18:05:41 -0700430 self.label2node = OrderedDict()
Peter Bigot44394e32020-08-24 13:35:25 -0700431 self.dep_ord2node = OrderedDict()
Martí Bolívareac56e42020-05-06 11:22:30 -0700432 self.compat2nodes = defaultdict(list)
433 self.compat2okay = defaultdict(list)
Martí Bolívar2707d2a2020-04-22 18:05:41 -0700434
Ulf Magnusson88db84b2020-01-28 04:46:12 +0100435 for node in self.nodes:
Martí Bolívar2707d2a2020-04-22 18:05:41 -0700436 for label in node.labels:
437 self.label2node[label] = node
438
Martí Bolívareac56e42020-05-06 11:22:30 -0700439 for compat in node.compats:
440 self.compat2nodes[compat].append(node)
441
Martí Bolívareac56e42020-05-06 11:22:30 -0700442 if node.status == "okay":
443 self.compat2okay[compat].append(node)
444
Peter Bigot44394e32020-08-24 13:35:25 -0700445 for nodeset in self.scc_order:
446 node = nodeset[0]
447 self.dep_ord2node[node.dep_ordinal] = node
448
Martí Bolívarf673e202020-12-08 12:55:44 -0800449 def _check(self):
450 # Tree-wide checks and warnings.
451
452 for binding in self._compat2binding.values():
453 for spec in binding.prop2specs.values():
454 if not spec.enum or spec.type != 'string':
455 continue
456
457 if not spec.enum_tokenizable:
458 _LOG.warning(
459 f"compatible '{binding.compatible}' "
460 f"in binding '{binding.path}' has non-tokenizable enum "
461 f"for property '{spec.name}': " +
462 ', '.join(repr(x) for x in spec.enum))
463 elif not spec.enum_upper_tokenizable:
464 _LOG.warning(
465 f"compatible '{binding.compatible}' "
466 f"in binding '{binding.path}' has enum for property "
467 f"'{spec.name}' that is only tokenizable "
468 'in lowercase: ' +
469 ', '.join(repr(x) for x in spec.enum))
Ulf Magnusson62d57412018-12-17 20:09:47 +0100470
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200471class Node:
Ulf Magnusson62d57412018-12-17 20:09:47 +0100472 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200473 Represents a devicetree node, augmented with information from bindings, and
474 with some interpretation of devicetree properties. There's a one-to-one
475 correspondence between devicetree nodes and Nodes.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100476
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200477 These attributes are available on Node objects:
Ulf Magnusson62d57412018-12-17 20:09:47 +0100478
479 edt:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200480 The EDT instance this node is from
Ulf Magnusson62d57412018-12-17 20:09:47 +0100481
482 name:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200483 The name of the node
Ulf Magnusson62d57412018-12-17 20:09:47 +0100484
485 unit_addr:
486 An integer with the ...@<unit-address> portion of the node name,
487 translated through any 'ranges' properties on parent nodes, or None if
488 the node name has no unit-address portion
489
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200490 description:
491 The description string from the binding for the node, or None if the node
Ulf Magnussonf3f88a82019-10-28 13:02:21 +0100492 has no binding. Leading and trailing whitespace (including newlines) is
493 removed.
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200494
Ulf Magnusson62d57412018-12-17 20:09:47 +0100495 path:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200496 The devicetree path of the node
Ulf Magnusson62d57412018-12-17 20:09:47 +0100497
498 label:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200499 The text from the 'label' property on the node, or None if the node has
500 no 'label'
Ulf Magnusson62d57412018-12-17 20:09:47 +0100501
Martí Bolívar7846fd12020-02-12 22:18:39 -0800502 labels:
503 A list of all of the devicetree labels for the node, in the same order
504 as the labels appear, but with duplicates removed.
505
506 This corresponds to the actual devicetree source labels, unlike the
507 "label" attribute, which is the value of a devicetree property named
508 "label".
509
Ulf Magnusson62d57412018-12-17 20:09:47 +0100510 parent:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200511 The Node instance for the devicetree parent of the Node, or None if the
512 node is the root node
Ulf Magnusson110526e2019-09-21 04:14:33 +0200513
514 children:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200515 A dictionary with the Node instances for the devicetree children of the
516 node, indexed by name
Ulf Magnusson62d57412018-12-17 20:09:47 +0100517
Peter A. Bigotea956f42019-09-27 11:31:36 -0500518 dep_ordinal:
519 A non-negative integer value such that the value for a Node is
520 less than the value for all Nodes that depend on it.
521
Martí Bolívar81650082020-10-05 20:02:13 -0700522 The ordinal is defined for all Nodes, and is unique among nodes in its
523 EDT 'nodes' list.
Peter A. Bigotea956f42019-09-27 11:31:36 -0500524
Ulf Magnusson2e1d2882019-11-06 22:36:50 +0100525 required_by:
526 A list with the nodes that directly depend on the node
527
528 depends_on:
529 A list with the nodes that the node directly depends on
530
Martí Bolívarbd0ecc82020-05-05 16:37:26 -0700531 status:
532 The node's status property value, as a string, or "okay" if the node
533 has no status property set. If the node's status property is "ok",
534 it is converted to "okay" for consistency.
535
Ulf Magnusson62d57412018-12-17 20:09:47 +0100536 read_only:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200537 True if the node has a 'read-only' property, and False otherwise
Ulf Magnusson62d57412018-12-17 20:09:47 +0100538
Ulf Magnusson62d57412018-12-17 20:09:47 +0100539 matching_compat:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200540 The 'compatible' string for the binding that matched the node, or None if
541 the node has no binding
Ulf Magnusson62d57412018-12-17 20:09:47 +0100542
Ulf Magnusson62d57412018-12-17 20:09:47 +0100543 binding_path:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200544 The path to the binding file for the node, or None if the node has no
Ulf Magnusson62d57412018-12-17 20:09:47 +0100545 binding
546
547 compats:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200548 A list of 'compatible' strings for the node, in the same order that
Ulf Magnusson62d57412018-12-17 20:09:47 +0100549 they're listed in the .dts file
550
551 regs:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200552 A list of Register objects for the node's registers
Ulf Magnusson62d57412018-12-17 20:09:47 +0100553
554 props:
Ulf Magnusson72158852019-11-12 18:33:00 +0100555 A collections.OrderedDict that maps property names to Property objects.
556 Property objects are created for all devicetree properties on the node
557 that are mentioned in 'properties:' in the binding.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100558
559 aliases:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200560 A list of aliases for the node. This is fetched from the /aliases node.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100561
562 interrupts:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +0200563 A list of ControllerAndData objects for the interrupts generated by the
Ulf Magnussoneef8d192019-10-30 16:05:30 +0100564 node. The list is empty if the node does not generate interrupts.
565
566 pinctrls:
567 A list of PinCtrl objects for the pinctrl-<index> properties on the
568 node, sorted by index. The list is empty if the node does not have any
569 pinctrl-<index> properties.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100570
571 bus:
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100572 If the node is a bus node (has a 'bus:' key in its binding), then this
573 attribute holds the bus type, e.g. "i2c" or "spi". If the node is not a
574 bus node, then this attribute is None.
575
576 on_bus:
577 The bus the node appears on, e.g. "i2c" or "spi". The bus is determined
578 by searching upwards for a parent node whose binding has a 'bus:' key,
579 returning the value of the first 'bus:' key found. If none of the node's
580 parents has a 'bus:' key, this attribute is None.
581
582 bus_node:
583 Like on_bus, but contains the Node for the bus controller, or None if the
584 node is not on a bus.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100585
586 flash_controller:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200587 The flash controller for the node. Only meaningful for nodes representing
588 flash partitions.
Ulf Magnusson8cacb9f2020-02-16 08:45:37 +0100589
590 spi_cs_gpio:
591 The device's SPI GPIO chip select as a ControllerAndData instance, if it
592 exists, and None otherwise. See
593 Documentation/devicetree/bindings/spi/spi-controller.yaml in the Linux kernel.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100594 """
595 @property
596 def name(self):
597 "See the class docstring"
598 return self._node.name
599
600 @property
601 def unit_addr(self):
602 "See the class docstring"
603
604 # TODO: Return a plain string here later, like dtlib.Node.unit_addr?
605
606 if "@" not in self.name:
607 return None
608
609 try:
610 addr = int(self.name.split("@", 1)[1], 16)
611 except ValueError:
612 _err("{!r} has non-hex unit address".format(self))
613
Martí Bolívare05c94e2020-07-01 13:25:15 -0700614 return _translate(addr, self._node)
Ulf Magnusson62d57412018-12-17 20:09:47 +0100615
616 @property
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200617 def description(self):
618 "See the class docstring."
Martí Bolívar7165b772020-10-12 12:32:25 -0700619 if self._binding:
620 return self._binding.description
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200621 return None
622
623 @property
Ulf Magnusson62d57412018-12-17 20:09:47 +0100624 def path(self):
625 "See the class docstring"
626 return self._node.path
627
628 @property
629 def label(self):
630 "See the class docstring"
631 if "label" in self._node.props:
632 return self._node.props["label"].to_string()
633 return None
634
635 @property
Martí Bolívar7846fd12020-02-12 22:18:39 -0800636 def labels(self):
637 "See the class docstring"
638 return self._node.labels
639
640 @property
Ulf Magnusson62d57412018-12-17 20:09:47 +0100641 def parent(self):
642 "See the class docstring"
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200643 return self.edt._node2enode.get(self._node.parent)
Ulf Magnusson62d57412018-12-17 20:09:47 +0100644
645 @property
Ulf Magnusson110526e2019-09-21 04:14:33 +0200646 def children(self):
647 "See the class docstring"
648 # Could be initialized statically too to preserve identity, but not
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200649 # sure if needed. Parent nodes being initialized before their children
650 # would need to be kept in mind.
Ulf Magnusson72158852019-11-12 18:33:00 +0100651 return OrderedDict((name, self.edt._node2enode[node])
652 for name, node in self._node.nodes.items())
Ulf Magnusson110526e2019-09-21 04:14:33 +0200653
654 @property
Ulf Magnusson2e1d2882019-11-06 22:36:50 +0100655 def required_by(self):
656 "See the class docstring"
657 return self.edt._graph.required_by(self)
658
659 @property
660 def depends_on(self):
661 "See the class docstring"
662 return self.edt._graph.depends_on(self)
663
664 @property
Martí Bolívarbd0ecc82020-05-05 16:37:26 -0700665 def status(self):
666 "See the class docstring"
667 status = self._node.props.get("status")
668
669 if status is None:
670 as_string = "okay"
671 else:
672 as_string = status.to_string()
673
674 if as_string == "ok":
675 as_string = "okay"
676
677 return as_string
678
679 @property
Ulf Magnusson62d57412018-12-17 20:09:47 +0100680 def read_only(self):
681 "See the class docstring"
682 return "read-only" in self._node.props
683
684 @property
685 def aliases(self):
686 "See the class docstring"
687 return [alias for alias, node in self._node.dt.alias2node.items()
688 if node is self._node]
689
690 @property
691 def bus(self):
692 "See the class docstring"
Martí Bolívar7165b772020-10-12 12:32:25 -0700693 if self._binding:
694 return self._binding.bus
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100695 return None
696
697 @property
698 def on_bus(self):
699 "See the class docstring"
700 bus_node = self.bus_node
701 return bus_node.bus if bus_node else None
Ulf Magnusson62d57412018-12-17 20:09:47 +0100702
703 @property
704 def flash_controller(self):
705 "See the class docstring"
706
707 # The node path might be something like
708 # /flash-controller@4001E000/flash@0/partitions/partition@fc000. We go
709 # up two levels to get the flash and check its compat. The flash
710 # controller might be the flash itself (for cases like NOR flashes).
711 # For the case of 'soc-nv-flash', we assume the controller is the
712 # parent of the flash node.
713
714 if not self.parent or not self.parent.parent:
715 _err("flash partition {!r} lacks parent or grandparent node"
716 .format(self))
717
718 controller = self.parent.parent
719 if controller.matching_compat == "soc-nv-flash":
720 return controller.parent
721 return controller
722
Ulf Magnusson8cacb9f2020-02-16 08:45:37 +0100723 @property
724 def spi_cs_gpio(self):
725 "See the class docstring"
726
727 if not (self.on_bus == "spi" and "cs-gpios" in self.bus_node.props):
728 return None
729
730 if not self.regs:
731 _err("{!r} needs a 'reg' property, to look up the chip select index "
732 "for SPI".format(self))
733
734 parent_cs_lst = self.bus_node.props["cs-gpios"].val
735
736 # cs-gpios is indexed by the unit address
737 cs_index = self.regs[0].addr
738 if cs_index >= len(parent_cs_lst):
739 _err("index from 'regs' in {!r} ({}) is >= number of cs-gpios "
740 "in {!r} ({})".format(
741 self, cs_index, self.bus_node, len(parent_cs_lst)))
742
743 return parent_cs_lst[cs_index]
744
Ulf Magnusson62d57412018-12-17 20:09:47 +0100745 def __repr__(self):
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200746 return "<Node {} in '{}', {}>".format(
Ulf Magnusson62d57412018-12-17 20:09:47 +0100747 self.path, self.edt.dts_path,
748 "binding " + self.binding_path if self.binding_path
749 else "no binding")
750
Martí Bolívar09858492020-12-08 09:41:49 -0800751 def _init_binding(self):
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200752 # Initializes Node.matching_compat, Node._binding, and
753 # Node.binding_path.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100754 #
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200755 # Node._binding holds the data from the node's binding file, in the
Ulf Magnusson62d57412018-12-17 20:09:47 +0100756 # format returned by PyYAML (plain Python lists, dicts, etc.), or None
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200757 # if the node has no binding.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100758
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200759 # This relies on the parent of the node having already been
Ulf Magnusson62d57412018-12-17 20:09:47 +0100760 # initialized, which is guaranteed by going through the nodes in
761 # node_iter() order.
762
Peter Bigot32e61592020-09-01 10:22:31 -0500763 if self.path in self.edt._infer_binding_for_paths:
Martí Bolívar09858492020-12-08 09:41:49 -0800764 self._binding_from_properties()
Peter Bigot32e61592020-09-01 10:22:31 -0500765 return
766
Kumar Gala06edcb12020-05-07 15:39:21 -0500767 if self.compats:
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100768 on_bus = self.on_bus
Ulf Magnusson62d57412018-12-17 20:09:47 +0100769
770 for compat in self.compats:
Johan Hedberg4ba38782020-12-21 12:52:32 +0200771 # When matching, respect the order of the 'compatible' entries,
772 # and for each one first try to match against an explicitly
773 # specified bus (if any) and then against any bus. This is so
774 # that matching against bindings which do not specify a bus
775 # works the same way in Zephyr as it does elsewhere.
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100776 if (compat, on_bus) in self.edt._compat2binding:
Martí Bolívar7165b772020-10-12 12:32:25 -0700777 binding = self.edt._compat2binding[compat, on_bus]
Johan Hedberg4ba38782020-12-21 12:52:32 +0200778 elif (compat, None) in self.edt._compat2binding:
779 binding = self.edt._compat2binding[compat, None]
780 else:
781 continue
782
783 self.binding_path = binding.path
784 self.matching_compat = compat
785 self._binding = binding
786 return
Ulf Magnusson62d57412018-12-17 20:09:47 +0100787 else:
Martí Bolívar7165b772020-10-12 12:32:25 -0700788 # No 'compatible' property. See if the parent binding has
789 # a compatible. This can come from one or more levels of
790 # nesting with 'child-binding:'.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100791
Ulf Magnusson0b1ab4a2019-09-17 18:24:30 +0200792 binding_from_parent = self._binding_from_parent()
793 if binding_from_parent:
794 self._binding = binding_from_parent
Martí Bolívar7165b772020-10-12 12:32:25 -0700795 self.binding_path = self._binding.path
796 self.matching_compat = self._binding.compatible
Ulf Magnusson0b1ab4a2019-09-17 18:24:30 +0200797
Ulf Magnusson62d57412018-12-17 20:09:47 +0100798 return
799
800 # No binding found
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200801 self._binding = self.binding_path = self.matching_compat = None
Ulf Magnusson62d57412018-12-17 20:09:47 +0100802
Martí Bolívar09858492020-12-08 09:41:49 -0800803 def _binding_from_properties(self):
Martí Bolívar7165b772020-10-12 12:32:25 -0700804 # Sets up a Binding object synthesized from the properties in the node.
Peter Bigot32e61592020-09-01 10:22:31 -0500805
806 if self.compats:
807 _err(f"compatible in node with inferred binding: {self.path}")
808
Martí Bolívar7165b772020-10-12 12:32:25 -0700809 # Synthesize a 'raw' binding as if it had been parsed from YAML.
810 raw = {
811 'description': 'Inferred binding from properties, via edtlib.',
812 'properties': {},
813 }
Peter Bigot32e61592020-09-01 10:22:31 -0500814 for name, prop in self._node.props.items():
815 pp = OrderedDict()
Peter Bigot32e61592020-09-01 10:22:31 -0500816 if prop.type == TYPE_EMPTY:
817 pp["type"] = "boolean"
818 elif prop.type == TYPE_BYTES:
819 pp["type"] = "uint8-array"
820 elif prop.type == TYPE_NUM:
821 pp["type"] = "int"
822 elif prop.type == TYPE_NUMS:
823 pp["type"] = "array"
824 elif prop.type == TYPE_STRING:
825 pp["type"] = "string"
826 elif prop.type == TYPE_STRINGS:
827 pp["type"] = "string-array"
828 elif prop.type == TYPE_PHANDLE:
829 pp["type"] = "phandle"
830 elif prop.type == TYPE_PHANDLES:
831 pp["type"] = "phandles"
832 elif prop.type == TYPE_PHANDLES_AND_NUMS:
833 pp["type"] = "phandle-array"
834 else:
835 _err(f"cannot infer binding from property: {prop}")
Martí Bolívar7165b772020-10-12 12:32:25 -0700836 raw['properties'][name] = pp
837
838 # Set up Node state.
839 self.binding_path = None
840 self.matching_compat = None
841 self.compats = []
Martí Bolívar09858492020-12-08 09:41:49 -0800842 self._binding = Binding(None, {}, raw=raw, require_compatible=False)
Peter Bigot32e61592020-09-01 10:22:31 -0500843
Ulf Magnusson0b1ab4a2019-09-17 18:24:30 +0200844 def _binding_from_parent(self):
845 # Returns the binding from 'child-binding:' in the parent node's
Martí Bolívar7165b772020-10-12 12:32:25 -0700846 # binding.
Ulf Magnusson0b1ab4a2019-09-17 18:24:30 +0200847
848 if not self.parent:
849 return None
850
851 pbinding = self.parent._binding
852 if not pbinding:
853 return None
854
Martí Bolívar7165b772020-10-12 12:32:25 -0700855 if pbinding.child_binding:
856 return pbinding.child_binding
Ulf Magnusson0b1ab4a2019-09-17 18:24:30 +0200857
858 return None
859
Kumar Gala3a685662020-05-08 05:40:52 -0500860 def _bus_node(self, support_fixed_partitions_on_any_bus = True):
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100861 # Returns the value for self.bus_node. Relies on parent nodes being
862 # initialized before their children.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100863
864 if not self.parent:
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100865 # This is the root node
Ulf Magnusson62d57412018-12-17 20:09:47 +0100866 return None
867
Kumar Gala3a685662020-05-08 05:40:52 -0500868 # Treat 'fixed-partitions' as if they are not on any bus. The reason is
Kumar Gala058842b2020-05-06 22:36:50 -0500869 # that flash nodes might be on a SPI or controller or SoC bus. Having
870 # bus be None means we'll always match the binding for fixed-partitions
871 # also this means want processing the fixed-partitions node we wouldn't
872 # try to do anything bus specific with it.
Kumar Gala3a685662020-05-08 05:40:52 -0500873 if support_fixed_partitions_on_any_bus and "fixed-partitions" in self.compats:
Kumar Gala058842b2020-05-06 22:36:50 -0500874 return None
875
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100876 if self.parent.bus:
877 # The parent node is a bus node
878 return self.parent
Ulf Magnusson1ebe9452019-09-16 16:42:18 +0200879
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100880 # Same bus node as parent (possibly None)
881 return self.parent.bus_node
Ulf Magnusson62d57412018-12-17 20:09:47 +0100882
Kumar Galabc48f1c2020-05-01 12:33:00 -0500883 def _init_props(self, default_prop_types=False):
Ulf Magnussonb918e252019-08-30 03:49:43 +0200884 # Creates self.props. See the class docstring. Also checks that all
885 # properties on the node are declared in its binding.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100886
Ulf Magnusson72158852019-11-12 18:33:00 +0100887 self.props = OrderedDict()
Ulf Magnusson62d57412018-12-17 20:09:47 +0100888
Kumar Galabc48f1c2020-05-01 12:33:00 -0500889 node = self._node
890 if self._binding:
Martí Bolívar7165b772020-10-12 12:32:25 -0700891 prop2specs = self._binding.prop2specs
Kumar Galabc48f1c2020-05-01 12:33:00 -0500892 else:
Martí Bolívar7165b772020-10-12 12:32:25 -0700893 prop2specs = None
Ulf Magnusson62d57412018-12-17 20:09:47 +0100894
Ulf Magnussonb918e252019-08-30 03:49:43 +0200895 # Initialize self.props
Martí Bolívar7165b772020-10-12 12:32:25 -0700896 if prop2specs:
897 for prop_spec in prop2specs.values():
898 self._init_prop(prop_spec)
Kumar Galabc48f1c2020-05-01 12:33:00 -0500899 self._check_undeclared_props()
900 elif default_prop_types:
901 for name in node.props:
Martí Bolívar88094ec2020-12-02 13:52:43 -0800902 if name not in _DEFAULT_PROP_SPECS:
903 continue
904 prop_spec = _DEFAULT_PROP_SPECS[name]
905 val = self._prop_val(name, prop_spec.type, False, False, None)
906 self.props[name] = Property(prop_spec, val, self)
Ulf Magnusson62d57412018-12-17 20:09:47 +0100907
Martí Bolívar7165b772020-10-12 12:32:25 -0700908 def _init_prop(self, prop_spec):
909 # _init_props() helper for initializing a single property.
910 # 'prop_spec' is a PropertySpec object from the node's binding.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100911
Martí Bolívar7165b772020-10-12 12:32:25 -0700912 name = prop_spec.name
913 prop_type = prop_spec.type
Ulf Magnusson62d57412018-12-17 20:09:47 +0100914 if not prop_type:
915 _err("'{}' in {} lacks 'type'".format(name, self.binding_path))
916
Kumar Gala33db7b52020-10-17 11:46:03 -0500917 val = self._prop_val(name, prop_type, prop_spec.deprecated,
918 prop_spec.required, prop_spec.default)
Ulf Magnusson8d317bc2019-09-06 12:31:55 +0200919
Ulf Magnusson62d57412018-12-17 20:09:47 +0100920 if val is None:
Ulf Magnussonfcd665a2019-08-28 00:22:01 +0200921 # 'required: false' property that wasn't there, or a property type
922 # for which we store no data.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100923 return
924
Martí Bolívar7165b772020-10-12 12:32:25 -0700925 enum = prop_spec.enum
Kumar Gala62bf2672019-08-09 14:51:58 -0500926 if enum and val not in enum:
927 _err("value of property '{}' on {} in {} ({!r}) is not in 'enum' "
928 "list in {} ({!r})"
929 .format(name, self.path, self.edt.dts_path, val,
930 self.binding_path, enum))
931
Martí Bolívar7165b772020-10-12 12:32:25 -0700932 const = prop_spec.const
Kumar Gala3d143742019-08-09 16:03:46 -0500933 if const is not None and val != const:
934 _err("value of property '{}' on {} in {} ({!r}) is different from "
935 "the 'const' value specified in {} ({!r})"
936 .format(name, self.path, self.edt.dts_path, val,
937 self.binding_path, const))
938
Kumar Gala5dd715b2019-08-09 09:49:22 -0500939 # Skip properties that start with '#', like '#size-cells', and mapping
940 # properties like 'gpio-map'/'interrupt-map'
941 if name[0] == "#" or name.endswith("-map"):
942 return
943
Martí Bolívar88094ec2020-12-02 13:52:43 -0800944 self.props[name] = Property(prop_spec, val, self)
Ulf Magnusson62d57412018-12-17 20:09:47 +0100945
Kumar Gala33db7b52020-10-17 11:46:03 -0500946 def _prop_val(self, name, prop_type, deprecated, required, default):
Ulf Magnusson62d57412018-12-17 20:09:47 +0100947 # _init_prop() helper for getting the property's value
Ulf Magnussonff1f7522019-08-29 22:21:33 +0200948 #
949 # name:
950 # Property name from binding
951 #
952 # prop_type:
953 # Property type from binding (a string like "int")
954 #
Kumar Gala33db7b52020-10-17 11:46:03 -0500955 # deprecated:
956 # True if the property is deprecated
957 #
Kumar Galafc8c0c02020-10-17 11:34:38 -0500958 # required:
959 # True if the property is required to exist
Ulf Magnussonff1f7522019-08-29 22:21:33 +0200960 #
961 # default:
962 # Default value to use when the property doesn't exist, or None if
963 # the binding doesn't give a default value
Ulf Magnusson62d57412018-12-17 20:09:47 +0100964
965 node = self._node
Ulf Magnusson06b746c2019-08-09 20:38:17 +0200966 prop = node.props.get(name)
Ulf Magnusson62d57412018-12-17 20:09:47 +0100967
Kumar Gala33db7b52020-10-17 11:46:03 -0500968 if prop and deprecated:
Martí Bolívar09858492020-12-08 09:41:49 -0800969 _LOG.warning(f"'{name}' is marked as deprecated in 'properties:' "
970 f"in {self.binding_path} for node {node.path}.")
Kumar Gala33db7b52020-10-17 11:46:03 -0500971
Ulf Magnusson62d57412018-12-17 20:09:47 +0100972 if not prop:
Martí Bolívar81650082020-10-05 20:02:13 -0700973 if required and self.status == "okay":
Ulf Magnusson62d57412018-12-17 20:09:47 +0100974 _err("'{}' is marked as required in 'properties:' in {}, but "
975 "does not appear in {!r}".format(
976 name, self.binding_path, node))
977
Ulf Magnussonff1f7522019-08-29 22:21:33 +0200978 if default is not None:
979 # YAML doesn't have a native format for byte arrays. We need to
980 # convert those from an array like [0x12, 0x34, ...]. The
981 # format has already been checked in
982 # _check_prop_type_and_default().
983 if prop_type == "uint8-array":
984 return bytes(default)
985 return default
986
Ulf Magnusson0c4f4a92019-09-30 16:42:07 +0200987 return False if prop_type == "boolean" else None
988
989 if prop_type == "boolean":
990 if prop.type is not TYPE_EMPTY:
991 _err("'{0}' in {1!r} is defined with 'type: boolean' in {2}, "
992 "but is assigned a value ('{3}') instead of being empty "
993 "('{0};')".format(name, node, self.binding_path, prop))
994 return True
Ulf Magnusson62d57412018-12-17 20:09:47 +0100995
996 if prop_type == "int":
997 return prop.to_num()
998
999 if prop_type == "array":
1000 return prop.to_nums()
1001
1002 if prop_type == "uint8-array":
Ulf Magnusson06b746c2019-08-09 20:38:17 +02001003 return prop.to_bytes()
Ulf Magnusson62d57412018-12-17 20:09:47 +01001004
1005 if prop_type == "string":
1006 return prop.to_string()
1007
1008 if prop_type == "string-array":
1009 return prop.to_strings()
1010
Ulf Magnusson06b746c2019-08-09 20:38:17 +02001011 if prop_type == "phandle":
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001012 return self.edt._node2enode[prop.to_node()]
Ulf Magnusson06b746c2019-08-09 20:38:17 +02001013
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001014 if prop_type == "phandles":
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001015 return [self.edt._node2enode[node] for node in prop.to_nodes()]
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001016
1017 if prop_type == "phandle-array":
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001018 # This type is a bit high-level for dtlib as it involves
1019 # information from bindings and *-names properties, so there's no
1020 # to_phandle_array() in dtlib. Do the type check ourselves.
Martí Bolívar1ea7bf02020-10-05 09:34:02 -07001021 if prop.type not in (TYPE_PHANDLE, TYPE_PHANDLES, TYPE_PHANDLES_AND_NUMS):
1022 _err(f"expected property '{name}' in {node.path} in "
1023 f"{node.dt.filename} to be assigned "
1024 f"with '{name} = < &foo ... &bar 1 ... &baz 2 3 >' "
1025 f"(a mix of phandles and numbers), not '{prop}'")
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001026
1027 return self._standard_phandle_val_list(prop)
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001028
Ulf Magnusson23a5b492019-12-30 22:52:11 +01001029 if prop_type == "path":
1030 return self.edt._node2enode[prop.to_path()]
1031
Martí Bolívar7165b772020-10-12 12:32:25 -07001032 # prop_type == "compound". Checking that the 'type:'
1033 # value is valid is done in _check_prop_type_and_default().
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001034 #
1035 # 'compound' is a dummy type for properties that don't fit any of the
1036 # patterns above, so that we can require all entries in 'properties:'
1037 # to have a 'type: ...'. No Property object is created for it.
1038 return None
Ulf Magnusson62d57412018-12-17 20:09:47 +01001039
Ulf Magnussonb918e252019-08-30 03:49:43 +02001040 def _check_undeclared_props(self):
1041 # Checks that all properties are declared in the binding
1042
Ulf Magnussonb918e252019-08-30 03:49:43 +02001043 for prop_name in self._node.props:
1044 # Allow a few special properties to not be declared in the binding
1045 if prop_name.endswith("-controller") or \
1046 prop_name.startswith("#") or \
1047 prop_name.startswith("pinctrl-") or \
1048 prop_name in {
1049 "compatible", "status", "ranges", "phandle",
1050 "interrupt-parent", "interrupts-extended", "device_type"}:
1051 continue
1052
Martí Bolívar7165b772020-10-12 12:32:25 -07001053 if prop_name not in self._binding.prop2specs:
Ulf Magnussonb918e252019-08-30 03:49:43 +02001054 _err("'{}' appears in {} in {}, but is not declared in "
1055 "'properties:' in {}"
1056 .format(prop_name, self._node.path, self.edt.dts_path,
1057 self.binding_path))
1058
Ulf Magnusson62d57412018-12-17 20:09:47 +01001059 def _init_regs(self):
Ulf Magnusson95deec12019-08-06 21:51:06 +02001060 # Initializes self.regs
Ulf Magnusson62d57412018-12-17 20:09:47 +01001061
1062 node = self._node
1063
1064 self.regs = []
1065
1066 if "reg" not in node.props:
1067 return
1068
1069 address_cells = _address_cells(node)
1070 size_cells = _size_cells(node)
1071
Ulf Magnusson57b2d272019-12-28 17:11:32 +01001072 for raw_reg in _slice(node, "reg", 4*(address_cells + size_cells),
1073 "4*(<#address-cells> (= {}) + <#size-cells> (= {}))"
1074 .format(address_cells, size_cells)):
Ulf Magnusson62d57412018-12-17 20:09:47 +01001075 reg = Register()
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001076 reg.node = self
Kumar Gala8af311a2020-03-13 12:43:56 -05001077 if address_cells == 0:
1078 reg.addr = None
1079 else:
1080 reg.addr = _translate(to_num(raw_reg[:4*address_cells]), node)
1081 if size_cells == 0:
1082 reg.size = None
1083 else:
1084 reg.size = to_num(raw_reg[4*address_cells:])
Ulf Magnusson62d57412018-12-17 20:09:47 +01001085 if size_cells != 0 and reg.size == 0:
1086 _err("zero-sized 'reg' in {!r} seems meaningless (maybe you "
1087 "want a size of one or #size-cells = 0 instead)"
1088 .format(self._node))
1089
1090 self.regs.append(reg)
1091
Ulf Magnusson95deec12019-08-06 21:51:06 +02001092 _add_names(node, "reg", self.regs)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001093
Ulf Magnussoneef8d192019-10-30 16:05:30 +01001094 def _init_pinctrls(self):
1095 # Initializes self.pinctrls from any pinctrl-<index> properties
1096
1097 node = self._node
1098
1099 # pinctrl-<index> properties
1100 pinctrl_props = [prop for name, prop in node.props.items()
1101 if re.match("pinctrl-[0-9]+", name)]
1102 # Sort by index
1103 pinctrl_props.sort(key=lambda prop: prop.name)
1104
1105 # Check indices
1106 for i, prop in enumerate(pinctrl_props):
1107 if prop.name != "pinctrl-" + str(i):
1108 _err("missing 'pinctrl-{}' property on {!r} - indices should "
1109 "be contiguous and start from zero".format(i, node))
1110
1111 self.pinctrls = []
1112 for prop in pinctrl_props:
1113 pinctrl = PinCtrl()
1114 pinctrl.node = self
1115 pinctrl.conf_nodes = [
1116 self.edt._node2enode[node] for node in prop.to_nodes()
1117 ]
1118 self.pinctrls.append(pinctrl)
1119
1120 _add_names(node, "pinctrl", self.pinctrls)
1121
Ulf Magnusson62d57412018-12-17 20:09:47 +01001122 def _init_interrupts(self):
Ulf Magnusson95deec12019-08-06 21:51:06 +02001123 # Initializes self.interrupts
Ulf Magnusson62d57412018-12-17 20:09:47 +01001124
1125 node = self._node
1126
1127 self.interrupts = []
1128
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001129 for controller_node, data in _interrupts(node):
1130 interrupt = ControllerAndData()
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001131 interrupt.node = self
1132 interrupt.controller = self.edt._node2enode[controller_node]
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001133 interrupt.data = self._named_cells(interrupt.controller, data,
Ulf Magnusson6b875042019-09-23 07:40:16 +02001134 "interrupt")
Ulf Magnusson62d57412018-12-17 20:09:47 +01001135
1136 self.interrupts.append(interrupt)
1137
Ulf Magnusson95deec12019-08-06 21:51:06 +02001138 _add_names(node, "interrupt", self.interrupts)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001139
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001140 def _standard_phandle_val_list(self, prop):
1141 # Parses a property like
Ulf Magnusson95deec12019-08-06 21:51:06 +02001142 #
1143 # <name>s = <phandle value phandle value ...>
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001144 # (e.g., pwms = <&foo 1 2 &bar 3 4>)
Ulf Magnusson95deec12019-08-06 21:51:06 +02001145 #
1146 # , where each phandle points to a node that has a
1147 #
1148 # #<name>-cells = <size>
1149 #
1150 # property that gives the number of cells in the value after the
Ulf Magnusson567c3482019-09-26 20:34:13 +02001151 # phandle. These values are given names in *-cells in the binding for
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001152 # the controller.
1153 #
1154 # Also parses any
Ulf Magnusson95deec12019-08-06 21:51:06 +02001155 #
1156 # <name>-names = "...", "...", ...
1157 #
Martí Bolívar38ede5a2020-12-17 14:12:01 -08001158 # Returns a list of Optional[ControllerAndData] instances.
1159 # An index is None if the underlying phandle-array element
1160 # is unspecified.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001161
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001162 if prop.name.endswith("gpios"):
1163 # There's some slight special-casing for *-gpios properties in that
1164 # e.g. foo-gpios still maps to #gpio-cells rather than
1165 # #foo-gpio-cells
1166 basename = "gpio"
1167 else:
1168 # Strip -s. We've already checked that the property names end in -s
Martí Bolívar7165b772020-10-12 12:32:25 -07001169 # in _check_prop_type_and_default().
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001170 basename = prop.name[:-1]
Ulf Magnusson62d57412018-12-17 20:09:47 +01001171
Ulf Magnusson95deec12019-08-06 21:51:06 +02001172 res = []
Ulf Magnusson62d57412018-12-17 20:09:47 +01001173
Martí Bolívar38ede5a2020-12-17 14:12:01 -08001174 for item in _phandle_val_list(prop, basename):
1175 if item is None:
1176 res.append(None)
1177 continue
1178
1179 controller_node, data = item
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001180 mapped_controller, mapped_data = \
1181 _map_phandle_array_entry(prop.node, controller_node, data,
1182 basename)
Ulf Magnusson95deec12019-08-06 21:51:06 +02001183
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001184 entry = ControllerAndData()
1185 entry.node = self
1186 entry.controller = self.edt._node2enode[mapped_controller]
1187 entry.data = self._named_cells(entry.controller, mapped_data,
1188 basename)
Ulf Magnusson95deec12019-08-06 21:51:06 +02001189
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001190 res.append(entry)
1191
1192 _add_names(self._node, basename, res)
Ulf Magnusson95deec12019-08-06 21:51:06 +02001193
1194 return res
Ulf Magnusson62d57412018-12-17 20:09:47 +01001195
Ulf Magnusson567c3482019-09-26 20:34:13 +02001196 def _named_cells(self, controller, data, basename):
1197 # Returns a dictionary that maps <basename>-cells names given in the
1198 # binding for 'controller' to cell values. 'data' is the raw data, as a
1199 # byte array.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001200
1201 if not controller._binding:
1202 _err("{} controller {!r} for {!r} lacks binding"
Ulf Magnusson567c3482019-09-26 20:34:13 +02001203 .format(basename, controller._node, self._node))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001204
Martí Bolívar7165b772020-10-12 12:32:25 -07001205 if basename in controller._binding.specifier2cells:
1206 cell_names = controller._binding.specifier2cells[basename]
Ulf Magnusson62d57412018-12-17 20:09:47 +01001207 else:
Ulf Magnusson567c3482019-09-26 20:34:13 +02001208 # Treat no *-cells in the binding the same as an empty *-cells, so
1209 # that bindings don't have to have e.g. an empty 'clock-cells:' for
Ulf Magnusson62d57412018-12-17 20:09:47 +01001210 # '#clock-cells = <0>'.
1211 cell_names = []
1212
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001213 data_list = to_nums(data)
1214 if len(data_list) != len(cell_names):
Ulf Magnusson567c3482019-09-26 20:34:13 +02001215 _err("unexpected '{}-cells:' length in binding for {!r} - {} "
1216 "instead of {}"
1217 .format(basename, controller._node, len(cell_names),
1218 len(data_list)))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001219
Ulf Magnusson72158852019-11-12 18:33:00 +01001220 return OrderedDict(zip(cell_names, data_list))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001221
Ulf Magnusson62d57412018-12-17 20:09:47 +01001222
1223class Register:
1224 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001225 Represents a register on a node.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001226
1227 These attributes are available on Register objects:
1228
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001229 node:
1230 The Node instance this register is from
Ulf Magnusson62d57412018-12-17 20:09:47 +01001231
1232 name:
1233 The name of the register as given in the 'reg-names' property, or None if
1234 there is no 'reg-names' property
1235
1236 addr:
Kumar Gala8af311a2020-03-13 12:43:56 -05001237 The starting address of the register, in the parent address space, or None
1238 if #address-cells is zero. Any 'ranges' properties are taken into account.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001239
1240 size:
1241 The length of the register in bytes
1242 """
1243 def __repr__(self):
1244 fields = []
1245
1246 if self.name is not None:
1247 fields.append("name: " + self.name)
Kumar Gala8af311a2020-03-13 12:43:56 -05001248 if self.addr is not None:
1249 fields.append("addr: " + hex(self.addr))
1250 if self.size is not None:
1251 fields.append("size: " + hex(self.size))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001252
1253 return "<Register, {}>".format(", ".join(fields))
1254
1255
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001256class ControllerAndData:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001257 """
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001258 Represents an entry in an 'interrupts' or 'type: phandle-array' property
1259 value, e.g. <&ctrl-1 4 0> in
Ulf Magnusson62d57412018-12-17 20:09:47 +01001260
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001261 cs-gpios = <&ctrl-1 4 0 &ctrl-2 3 4>;
1262
1263 These attributes are available on ControllerAndData objects:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001264
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001265 node:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001266 The Node instance the property appears on
Ulf Magnusson62d57412018-12-17 20:09:47 +01001267
1268 controller:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001269 The Node instance for the controller (e.g. the controller the interrupt
1270 gets sent to for interrupts)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001271
Ulf Magnusson6b875042019-09-23 07:40:16 +02001272 data:
Ulf Magnusson567c3482019-09-26 20:34:13 +02001273 A dictionary that maps names from the *-cells key in the binding for the
1274 controller to data values, e.g. {"pin": 4, "flags": 0} for the example
1275 above.
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001276
1277 'interrupts = <1 2>' might give {"irq": 1, "level": 2}.
1278
1279 name:
1280 The name of the entry as given in
1281 'interrupt-names'/'gpio-names'/'pwm-names'/etc., or None if there is no
1282 *-names property
Ulf Magnusson62d57412018-12-17 20:09:47 +01001283 """
1284 def __repr__(self):
1285 fields = []
1286
1287 if self.name is not None:
1288 fields.append("name: " + self.name)
1289
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001290 fields.append("controller: {}".format(self.controller))
Ulf Magnusson6b875042019-09-23 07:40:16 +02001291 fields.append("data: {}".format(self.data))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001292
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001293 return "<ControllerAndData, {}>".format(", ".join(fields))
Jim Paris67f53ba2019-08-07 15:05:23 -04001294
1295
Ulf Magnussoneef8d192019-10-30 16:05:30 +01001296class PinCtrl:
1297 """
1298 Represents a pin control configuration for a set of pins on a device,
1299 e.g. pinctrl-0 or pinctrl-1.
1300
1301 These attributes are available on PinCtrl objects:
1302
1303 node:
1304 The Node instance the pinctrl-* property is on
1305
1306 name:
1307 The name of the configuration, as given in pinctrl-names, or None if
1308 there is no pinctrl-names property
1309
1310 conf_nodes:
1311 A list of Node instances for the pin configuration nodes, e.g.
1312 the nodes pointed at by &state_1 and &state_2 in
1313
1314 pinctrl-0 = <&state_1 &state_2>;
1315 """
1316 def __repr__(self):
1317 fields = []
1318
1319 if self.name is not None:
1320 fields.append("name: " + self.name)
1321
1322 fields.append("configuration nodes: " + str(self.conf_nodes))
1323
1324 return "<PinCtrl, {}>".format(", ".join(fields))
1325
1326
Ulf Magnusson62d57412018-12-17 20:09:47 +01001327class Property:
1328 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001329 Represents a property on a Node, as set in its DT node and with
Ulf Magnusson62d57412018-12-17 20:09:47 +01001330 additional info from the 'properties:' section of the binding.
1331
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001332 Only properties mentioned in 'properties:' get created. Properties of type
Martí Bolívar88094ec2020-12-02 13:52:43 -08001333 'compound' currently do not get Property instances, as it's not clear
1334 what to generate for them.
1335
1336 These attributes are available on Property objects. Several are
1337 just convenience accessors for attributes on the PropertySpec object
1338 accessible via the 'spec' attribute.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001339
1340 These attributes are available on Property objects:
1341
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001342 node:
1343 The Node instance the property is on
Ulf Magnusson62d57412018-12-17 20:09:47 +01001344
Martí Bolívar88094ec2020-12-02 13:52:43 -08001345 spec:
1346 The PropertySpec object which specifies this property.
1347
Ulf Magnusson62d57412018-12-17 20:09:47 +01001348 name:
Martí Bolívar88094ec2020-12-02 13:52:43 -08001349 Convenience for spec.name.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001350
1351 description:
Martí Bolívar88094ec2020-12-02 13:52:43 -08001352 Convenience for spec.name with leading and trailing whitespace
1353 (including newlines) removed.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001354
Ulf Magnusson165dde02019-08-14 11:13:14 +02001355 type:
Martí Bolívar88094ec2020-12-02 13:52:43 -08001356 Convenience for spec.type.
Ulf Magnusson165dde02019-08-14 11:13:14 +02001357
Ulf Magnusson62d57412018-12-17 20:09:47 +01001358 val:
Martí Bolívar88094ec2020-12-02 13:52:43 -08001359 The value of the property, with the format determined by spec.type,
1360 which comes from the 'type:' string in the binding.
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001361
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001362 - For 'type: int/array/string/string-array', 'val' is what you'd expect
1363 (a Python integer or string, or a list of them)
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001364
Ulf Magnusson23a5b492019-12-30 22:52:11 +01001365 - For 'type: phandle' and 'type: path', 'val' is the pointed-to Node
1366 instance
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001367
1368 - For 'type: phandles', 'val' is a list of the pointed-to Node
1369 instances
1370
1371 - For 'type: phandle-array', 'val' is a list of ControllerAndData
1372 instances. See the documentation for that class.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001373
Martí Bolívarb6dc0a22020-12-02 14:03:46 -08001374 val_as_token:
1375 The value of the property as a token, i.e. with non-alphanumeric
1376 characters replaced with underscores. This is only safe to access
1377 if self.enum_tokenizable returns True.
1378
Ulf Magnusson62d57412018-12-17 20:09:47 +01001379 enum_index:
Martí Bolívar88094ec2020-12-02 13:52:43 -08001380 The index of 'val' in 'spec.enum' (which comes from the 'enum:' list
1381 in the binding), or None if spec.enum is None.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001382 """
Martí Bolívar88094ec2020-12-02 13:52:43 -08001383
1384 def __init__(self, spec, val, node):
1385 self.val = val
1386 self.spec = spec
1387 self.node = node
1388
1389 @property
1390 def name(self):
1391 "See the class docstring"
1392 return self.spec.name
1393
1394 @property
1395 def description(self):
1396 "See the class docstring"
1397 return self.spec.description.strip()
1398
1399 @property
1400 def type(self):
1401 "See the class docstring"
1402 return self.spec.type
1403
1404 @property
Martí Bolívarb6dc0a22020-12-02 14:03:46 -08001405 def val_as_token(self):
1406 "See the class docstring"
1407 return re.sub(_NOT_ALPHANUM_OR_UNDERSCORE, '_', self.val)
1408
1409 @property
Martí Bolívar88094ec2020-12-02 13:52:43 -08001410 def enum_index(self):
1411 "See the class docstring"
1412 enum = self.spec.enum
1413 return enum.index(self.val) if enum else None
1414
Ulf Magnusson62d57412018-12-17 20:09:47 +01001415 def __repr__(self):
1416 fields = ["name: " + self.name,
1417 # repr() to deal with lists
Ulf Magnusson165dde02019-08-14 11:13:14 +02001418 "type: " + self.type,
Ulf Magnusson62d57412018-12-17 20:09:47 +01001419 "value: " + repr(self.val)]
1420
1421 if self.enum_index is not None:
1422 fields.append("enum index: {}".format(self.enum_index))
1423
1424 return "<Property, {}>".format(", ".join(fields))
1425
1426
Martí Bolívar7165b772020-10-12 12:32:25 -07001427class Binding:
1428 """
1429 Represents a parsed binding.
1430
1431 These attributes are available on Binding objects:
1432
1433 path:
1434 The absolute path to the file defining the binding.
1435
1436 description:
1437 The free-form description of the binding.
1438
1439 compatible:
Martí Bolívar0d4dca12020-11-03 08:34:04 -08001440 The compatible string the binding matches. This is None if the Binding is
1441 inferred from node properties. If the Binding is a child binding, then
1442 this will be inherited from the parent binding unless the child binding
1443 explicitly sets its own compatible.
Martí Bolívar7165b772020-10-12 12:32:25 -07001444
1445 prop2specs:
1446 A collections.OrderedDict mapping property names to PropertySpec objects
1447 describing those properties' values.
1448
1449 specifier2cells:
1450 A collections.OrderedDict that maps specifier space names (like "gpio",
1451 "clock", "pwm", etc.) to lists of cell names.
1452
1453 For example, if the binding YAML contains 'pin' and 'flags' cell names
1454 for the 'gpio' specifier space, like this:
1455
1456 gpio-cells:
1457 - pin
1458 - flags
1459
1460 Then the Binding object will have a 'specifier2cells' attribute mapping
1461 "gpio" to ["pin", "flags"]. A missing key should be interpreted as zero
1462 cells.
1463
1464 raw:
1465 The binding as an object parsed from YAML.
1466
1467 bus:
1468 If nodes with this binding's 'compatible' describe a bus, a string
1469 describing the bus type (like "i2c"). None otherwise.
1470
1471 on_bus:
1472 If nodes with this binding's 'compatible' appear on a bus, a string
1473 describing the bus type (like "i2c"). None otherwise.
1474
1475 child_binding:
1476 If this binding describes the properties of child nodes, then
1477 this is a Binding object for those children; it is None otherwise.
1478 A Binding object's 'child_binding.child_binding' is not None if there
1479 are multiple levels of 'child-binding' descriptions in the binding.
1480 """
1481
1482 def __init__(self, path, fname2path, raw=None,
Martí Bolívar09858492020-12-08 09:41:49 -08001483 require_compatible=True, require_description=True):
Martí Bolívar7165b772020-10-12 12:32:25 -07001484 """
1485 Binding constructor.
1486
1487 path:
1488 Path to binding YAML file. May be None.
1489
1490 fname2path:
1491 Map from include files to their absolute paths. Must
1492 not be None, but may be empty.
1493
1494 raw:
1495 Optional raw content in the binding.
1496 This does not have to have any "include:" lines resolved.
1497 May be left out, in which case 'path' is opened and read.
1498 This can be used to resolve child bindings, for example.
1499
1500 require_compatible:
1501 If True, it is an error if the binding does not contain a
1502 "compatible:" line. If False, a missing "compatible:" is
1503 not an error. Either way, "compatible:" must be a string
1504 if it is present in the binding.
1505
1506 require_description:
1507 If True, it is an error if the binding does not contain a
1508 "description:" line. If False, a missing "description:" is
1509 not an error. Either way, "description:" must be a string
1510 if it is present in the binding.
Martí Bolívar7165b772020-10-12 12:32:25 -07001511 """
Martí Bolívar7165b772020-10-12 12:32:25 -07001512 self.path = path
1513 self._fname2path = fname2path
1514
1515 if raw is None:
1516 with open(path, encoding="utf-8") as f:
1517 raw = yaml.load(f, Loader=_BindingLoader)
1518
1519 # Merge any included files into self.raw. This also pulls in
1520 # inherited child binding definitions, so it has to be done
1521 # before initializing those.
1522 self.raw = self._merge_includes(raw, self.path)
1523
1524 # Recursively initialize any child bindings. These don't
1525 # require a 'compatible' or 'description' to be well defined,
1526 # but they must be dicts.
1527 if "child-binding" in raw:
1528 if not isinstance(raw["child-binding"], dict):
1529 _err(f"malformed 'child-binding:' in {self.path}, "
1530 "expected a binding (dictionary with keys/values)")
1531 self.child_binding = Binding(path, fname2path,
1532 raw=raw["child-binding"],
1533 require_compatible=False,
1534 require_description=False)
1535 else:
1536 self.child_binding = None
1537
1538 # Make sure this is a well defined object.
1539 self._check(require_compatible, require_description)
1540
1541 # Initialize look up tables.
1542 self.prop2specs = OrderedDict()
1543 for prop_name in self.raw.get("properties", {}).keys():
1544 self.prop2specs[prop_name] = PropertySpec(prop_name, self)
1545 self.specifier2cells = OrderedDict()
1546 for key, val in self.raw.items():
1547 if key.endswith("-cells"):
1548 self.specifier2cells[key[:-len("-cells")]] = val
1549
Martí Bolívar0d4dca12020-11-03 08:34:04 -08001550 # Make child binding compatibles match ours if they are missing.
1551 if self.compatible is not None:
1552 child = self.child_binding
1553 while child is not None:
1554 if child.compatible is None:
1555 child.compatible = self.compatible
1556 child = child.child_binding
1557
Martí Bolívar7165b772020-10-12 12:32:25 -07001558 def __repr__(self):
1559 if self.compatible:
1560 compat = f" for compatible '{self.compatible}'"
1561 else:
1562 compat = ""
1563 return f"<Binding {os.path.basename(self.path)}" + compat + ">"
1564
1565 @property
1566 def description(self):
1567 "See the class docstring"
1568 return self.raw['description']
1569
1570 @property
1571 def compatible(self):
1572 "See the class docstring"
Martí Bolívar0d4dca12020-11-03 08:34:04 -08001573 if hasattr(self, '_compatible'):
1574 return self._compatible
Martí Bolívar7165b772020-10-12 12:32:25 -07001575 return self.raw.get('compatible')
1576
Martí Bolívar0d4dca12020-11-03 08:34:04 -08001577 @compatible.setter
1578 def compatible(self, compatible):
1579 "See the class docstring"
1580 self._compatible = compatible
1581
Martí Bolívar7165b772020-10-12 12:32:25 -07001582 @property
1583 def bus(self):
1584 "See the class docstring"
1585 return self.raw.get('bus')
1586
1587 @property
1588 def on_bus(self):
1589 "See the class docstring"
1590 return self.raw.get('on-bus')
1591
1592 def _merge_includes(self, raw, binding_path):
1593 # Constructor helper. Merges included files in
1594 # 'raw["include"]' into 'raw' using 'self._include_paths' as a
1595 # source of include files, removing the "include" key while
1596 # doing so.
1597 #
1598 # This treats 'binding_path' as the binding file being built up
1599 # and uses it for error messages.
1600
1601 if "include" not in raw:
1602 return raw
1603
1604 include = raw.pop("include")
1605 fnames = []
1606 if isinstance(include, str):
1607 fnames.append(include)
1608 elif isinstance(include, list):
Martí Bolívar876b9612020-10-16 10:19:54 -07001609 if not all(isinstance(elem, str) for elem in include):
Martí Bolívar7165b772020-10-12 12:32:25 -07001610 _err(f"all elements in 'include:' in {binding_path} "
1611 "should be strings")
1612 fnames += include
1613 else:
1614 _err(f"'include:' in {binding_path} "
1615 "should be a string or a list of strings")
1616
1617 # First, merge the included files together. If more than one included
1618 # file has a 'required:' for a particular property, OR the values
1619 # together, so that 'required: true' wins.
1620
1621 merged = {}
1622 for fname in fnames:
1623 _merge_props(merged, self._load_raw(fname), None, binding_path,
1624 check_required=False)
1625
1626 # Next, merge the merged included files into 'raw'. Error out if
1627 # 'raw' has 'required: false' while the merged included files have
1628 # 'required: true'.
1629
1630 _merge_props(raw, merged, None, binding_path, check_required=True)
1631
1632 return raw
1633
1634 def _load_raw(self, fname):
1635 # Returns the contents of the binding given by 'fname' after merging
1636 # any bindings it lists in 'include:' into it. 'fname' is just the
1637 # basename of the file, so we check that there aren't multiple
1638 # candidates.
1639
1640 path = self._fname2path.get(fname)
1641
1642 if not path:
1643 _err(f"'{fname}' not found")
1644
1645 with open(path, encoding="utf-8") as f:
1646 contents = yaml.load(f, Loader=_BindingLoader)
1647
1648 return self._merge_includes(contents, path)
1649
1650 def _check(self, require_compatible, require_description):
1651 # Does sanity checking on the binding.
1652
1653 raw = self.raw
1654
1655 if "compatible" in raw:
1656 compatible = raw["compatible"]
1657 if not isinstance(compatible, str):
1658 _err(f"malformed 'compatible: {compatible}' "
1659 f"field in {self.path} - "
1660 f"should be a string, not {type(compatible).__name__}")
1661 elif require_compatible:
1662 _err(f"missing 'compatible' property in {self.path}")
1663
1664 if "description" not in raw and require_description:
1665 _err(f"missing 'description' property in {self.path}")
1666
1667 for prop in "title", "description":
1668 if prop in raw and (not isinstance(raw[prop], str) or
1669 not raw[prop]):
1670 _err(f"malformed or empty '{prop}' in {self.path}")
1671
1672 ok_top = {"title", "description", "compatible", "properties",
1673 "bus", "on-bus", "parent-bus", "child-bus", "parent", "child",
1674 "child-binding", "sub-node"}
1675
1676 for prop in raw:
1677 if prop == "#cells": # clean error for users of legacy syntax
1678 _err(f"malformed '{prop}:' in {self.path}, "
1679 "expected *-cells syntax")
1680 if prop not in ok_top and not prop.endswith("-cells"):
1681 _err(f"unknown key '{prop}' in {self.path}, "
1682 "expected one of {', '.join(ok_top)}, or *-cells")
1683
1684 for bus_key in "bus", "on-bus":
1685 if bus_key in raw and \
1686 not isinstance(raw[bus_key], str):
1687 _err(f"malformed '{bus_key}:' value in {self.path}, "
1688 "expected string")
1689
1690 self._check_properties()
1691
1692 for key, val in raw.items():
1693 if key.endswith("-cells"):
1694 if not isinstance(val, list) or \
Martí Bolívar876b9612020-10-16 10:19:54 -07001695 not all(isinstance(elem, str) for elem in val):
Martí Bolívar7165b772020-10-12 12:32:25 -07001696 _err(f"malformed '{key}:' in {self.path}, "
1697 "expected a list of strings")
1698
1699 def _check_properties(self):
1700 # _check() helper for checking the contents of 'properties:'.
1701
1702 raw = self.raw
1703
1704 if "properties" not in raw:
1705 return
1706
Kumar Galac7baf2f2020-10-17 11:12:32 -05001707 ok_prop_keys = {"description", "type", "required",
Kumar Gala33db7b52020-10-17 11:46:03 -05001708 "enum", "const", "default", "deprecated"}
Martí Bolívar7165b772020-10-12 12:32:25 -07001709
1710 for prop_name, options in raw["properties"].items():
1711 for key in options:
Martí Bolívar7165b772020-10-12 12:32:25 -07001712 if key not in ok_prop_keys:
1713 _err(f"unknown setting '{key}' in "
1714 f"'properties: {prop_name}: ...' in {self.path}, "
1715 f"expected one of {', '.join(ok_prop_keys)}")
1716
1717 _check_prop_type_and_default(
1718 prop_name, options.get("type"),
Martí Bolívar7165b772020-10-12 12:32:25 -07001719 options.get("default"),
1720 self.path)
1721
Kumar Gala33db7b52020-10-17 11:46:03 -05001722 for true_false_opt in ["required", "deprecated"]:
1723 if true_false_opt in options:
1724 option = options[true_false_opt]
1725 if not isinstance(option, bool):
1726 _err(f"malformed '{true_false_opt}:' setting '{option}' "
1727 f"for '{prop_name}' in 'properties' in {self.path}, "
1728 "expected true/false")
1729
1730 if options.get("deprecated") and options.get("required"):
1731 _err(f"'{prop_name}' in 'properties' in {self.path} should not "
1732 "have both 'deprecated' and 'required' set")
Martí Bolívar7165b772020-10-12 12:32:25 -07001733
1734 if "description" in options and \
1735 not isinstance(options["description"], str):
1736 _err("missing, malformed, or empty 'description' for "
1737 f"'{prop_name}' in 'properties' in {self.path}")
1738
1739 if "enum" in options and not isinstance(options["enum"], list):
1740 _err(f"enum in {self.path} for property '{prop_name}' "
1741 "is not a list")
1742
1743 if "const" in options and not isinstance(options["const"],
1744 (int, str)):
1745 _err(f"const in {self.path} for property '{prop_name}' "
1746 "is not a scalar")
1747
Martí Bolívar7165b772020-10-12 12:32:25 -07001748
Martí Bolívar2c19ccc2020-10-19 21:42:53 -07001749def bindings_from_paths(yaml_paths, ignore_errors=False):
1750 """
1751 Get a list of Binding objects from the yaml files 'yaml_paths'.
1752
1753 If 'ignore_errors' is True, YAML files that cause an EDTError when
1754 loaded are ignored. (No other exception types are silenced.)
1755 """
1756
1757 ret = []
1758 fname2path = {os.path.basename(path): path for path in yaml_paths}
1759 for path in yaml_paths:
1760 try:
1761 ret.append(Binding(path, fname2path))
1762 except EDTError:
1763 if ignore_errors:
1764 continue
1765 raise
1766
1767 return ret
1768
Martí Bolívar7165b772020-10-12 12:32:25 -07001769class PropertySpec:
1770 """
1771 Represents a "property specification", i.e. the description of a
1772 property provided by a binding file, like its type and description.
1773
1774 These attributes are available on PropertySpec objects:
1775
1776 binding:
1777 The Binding object which defined this property.
1778
1779 name:
1780 The property's name.
1781
1782 path:
1783 The file where this property was defined. In case a binding includes
1784 other bindings, this is the file where the property was last modified.
1785
1786 type:
1787 The type of the property as a string, as given in the binding.
1788
1789 description:
1790 The free-form description of the property as a string, or None.
1791
1792 enum:
1793 A list of values the property may take as given in the binding, or None.
1794
Martí Bolívarb6dc0a22020-12-02 14:03:46 -08001795 enum_tokenizable:
1796 True if enum is not None and all the values in it are tokenizable;
1797 False otherwise.
1798
1799 A property must have string type and an "enum:" in its binding to be
1800 tokenizable. Additionally, the "enum:" values must be unique after
1801 converting all non-alphanumeric characters to underscores (so "foo bar"
1802 and "foo_bar" in the same "enum:" would not be tokenizable).
1803
1804 enum_upper_tokenizable:
1805 Like 'enum_tokenizable', with the additional restriction that the
1806 "enum:" values must be unique after uppercasing and converting
1807 non-alphanumeric characters to underscores.
1808
Martí Bolívar7165b772020-10-12 12:32:25 -07001809 const:
1810 The property's constant value as given in the binding, or None.
1811
1812 default:
1813 The property's default value as given in the binding, or None.
1814
Kumar Gala33db7b52020-10-17 11:46:03 -05001815 deprecated:
1816 True if the property is deprecated; False otherwise.
1817
Martí Bolívar7165b772020-10-12 12:32:25 -07001818 required:
1819 True if the property is marked required; False otherwise.
1820 """
1821
1822 def __init__(self, name, binding):
1823 self.binding = binding
1824 self.name = name
1825 self._raw = self.binding.raw["properties"][name]
1826
1827 def __repr__(self):
1828 return f"<PropertySpec {self.name} type '{self.type}'>"
1829
1830 @property
1831 def path(self):
1832 "See the class docstring"
1833 return self.binding.path
1834
1835 @property
1836 def type(self):
1837 "See the class docstring"
1838 return self._raw["type"]
1839
1840 @property
1841 def description(self):
1842 "See the class docstring"
1843 return self._raw.get("description")
1844
1845 @property
1846 def enum(self):
1847 "See the class docstring"
1848 return self._raw.get("enum")
1849
1850 @property
Martí Bolívarb6dc0a22020-12-02 14:03:46 -08001851 def enum_tokenizable(self):
1852 "See the class docstring"
1853 if not hasattr(self, '_enum_tokenizable'):
1854 if self.type != 'string' or self.enum is None:
1855 self._enum_tokenizable = False
1856 else:
1857 # Saving _as_tokens here lets us reuse it in
1858 # enum_upper_tokenizable.
1859 self._as_tokens = [re.sub(_NOT_ALPHANUM_OR_UNDERSCORE,
1860 '_', value)
1861 for value in self.enum]
1862 self._enum_tokenizable = (len(self._as_tokens) ==
1863 len(set(self._as_tokens)))
1864
1865 return self._enum_tokenizable
1866
1867 @property
1868 def enum_upper_tokenizable(self):
1869 "See the class docstring"
1870 if not hasattr(self, '_enum_upper_tokenizable'):
1871 if not self.enum_tokenizable:
1872 self._enum_upper_tokenizable = False
1873 else:
1874 self._enum_upper_tokenizable = \
1875 (len(self._as_tokens) ==
1876 len(set(x.upper() for x in self._as_tokens)))
1877 return self._enum_upper_tokenizable
1878
1879 @property
Martí Bolívar7165b772020-10-12 12:32:25 -07001880 def const(self):
1881 "See the class docstring"
1882 return self._raw.get("const")
1883
1884 @property
1885 def default(self):
1886 "See the class docstring"
1887 return self._raw.get("default")
1888
1889 @property
1890 def required(self):
1891 "See the class docstring"
1892 return self._raw.get("required", False)
1893
Kumar Gala33db7b52020-10-17 11:46:03 -05001894 @property
1895 def deprecated(self):
1896 "See the class docstring"
1897 return self._raw.get("deprecated", False)
Martí Bolívar7165b772020-10-12 12:32:25 -07001898
Ulf Magnusson62d57412018-12-17 20:09:47 +01001899class EDTError(Exception):
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001900 "Exception raised for devicetree- and binding-related errors"
Ulf Magnusson62d57412018-12-17 20:09:47 +01001901
1902
1903#
Ulf Magnusson62d57412018-12-17 20:09:47 +01001904# Private global functions
1905#
1906
1907
1908def _dt_compats(dt):
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001909 # Returns a set() with all 'compatible' strings in the devicetree
Ulf Magnusson62d57412018-12-17 20:09:47 +01001910 # represented by dt (a dtlib.DT instance)
1911
1912 return {compat
1913 for node in dt.node_iter()
1914 if "compatible" in node.props
1915 for compat in node.props["compatible"].to_strings()}
1916
1917
Michael Scottb8909432019-08-02 11:42:06 -07001918def _binding_paths(bindings_dirs):
Ulf Magnusson62d57412018-12-17 20:09:47 +01001919 # Returns a list with the paths to all bindings (.yaml files) in
Michael Scottb8909432019-08-02 11:42:06 -07001920 # 'bindings_dirs'
Ulf Magnusson62d57412018-12-17 20:09:47 +01001921
Michael Scottb8909432019-08-02 11:42:06 -07001922 binding_paths = []
1923
1924 for bindings_dir in bindings_dirs:
1925 for root, _, filenames in os.walk(bindings_dir):
1926 for filename in filenames:
1927 if filename.endswith(".yaml"):
1928 binding_paths.append(os.path.join(root, filename))
1929
1930 return binding_paths
Ulf Magnusson62d57412018-12-17 20:09:47 +01001931
1932
Ulf Magnusson62d57412018-12-17 20:09:47 +01001933def _binding_inc_error(msg):
1934 # Helper for reporting errors in the !include implementation
1935
1936 raise yaml.constructor.ConstructorError(None, None, "error: " + msg)
1937
1938
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001939def _merge_props(to_dict, from_dict, parent, binding_path, check_required):
Ulf Magnussond834b692019-08-21 16:41:03 +02001940 # Recursively merges 'from_dict' into 'to_dict', to implement 'include:'.
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001941 #
1942 # If 'from_dict' and 'to_dict' contain a 'required:' key for the same
1943 # property, then the values are ORed together.
1944 #
1945 # If 'check_required' is True, then an error is raised if 'from_dict' has
1946 # 'required: true' while 'to_dict' has 'required: false'. This prevents
1947 # bindings from "downgrading" requirements from bindings they include,
1948 # which might help keep bindings well-organized.
1949 #
1950 # It's an error for most other keys to appear in both 'from_dict' and
1951 # 'to_dict'. When it's not an error, the value in 'to_dict' takes
1952 # precedence.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001953 #
1954 # 'parent' is the name of the parent key containing 'to_dict' and
1955 # 'from_dict', and 'binding_path' is the path to the top-level binding.
1956 # These are used to generate errors for sketchy property overwrites.
1957
1958 for prop in from_dict:
1959 if isinstance(to_dict.get(prop), dict) and \
1960 isinstance(from_dict[prop], dict):
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001961 _merge_props(to_dict[prop], from_dict[prop], prop, binding_path,
1962 check_required)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001963 elif prop not in to_dict:
1964 to_dict[prop] = from_dict[prop]
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001965 elif _bad_overwrite(to_dict, from_dict, prop, check_required):
Ulf Magnussond834b692019-08-21 16:41:03 +02001966 _err("{} (in '{}'): '{}' from included file overwritten "
Ulf Magnusson62d57412018-12-17 20:09:47 +01001967 "('{}' replaced with '{}')".format(
1968 binding_path, parent, prop, from_dict[prop],
1969 to_dict[prop]))
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001970 elif prop == "required":
1971 # Need a separate check here, because this code runs before
Martí Bolívar7165b772020-10-12 12:32:25 -07001972 # Binding._check()
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001973 if not (isinstance(from_dict["required"], bool) and
1974 isinstance(to_dict["required"], bool)):
1975 _err("malformed 'required:' setting for '{}' in 'properties' "
1976 "in {}, expected true/false".format(parent, binding_path))
1977
1978 # 'required: true' takes precedence
1979 to_dict["required"] = to_dict["required"] or from_dict["required"]
Ulf Magnusson62d57412018-12-17 20:09:47 +01001980
1981
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001982def _bad_overwrite(to_dict, from_dict, prop, check_required):
Ulf Magnusson62d57412018-12-17 20:09:47 +01001983 # _merge_props() helper. Returns True in cases where it's bad that
1984 # to_dict[prop] takes precedence over from_dict[prop].
1985
Ulf Magnusson62d57412018-12-17 20:09:47 +01001986 if to_dict[prop] == from_dict[prop]:
1987 return False
1988
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001989 # These are overridden deliberately
1990 if prop in {"title", "description", "compatible"}:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001991 return False
1992
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001993 if prop == "required":
1994 if not check_required:
1995 return False
1996 return from_dict[prop] and not to_dict[prop]
1997
Ulf Magnusson62d57412018-12-17 20:09:47 +01001998 return True
1999
2000
Ulf Magnusson8f225292019-09-05 22:31:09 +02002001def _binding_include(loader, node):
2002 # Implements !include, for backwards compatibility. '!include [foo, bar]'
2003 # just becomes [foo, bar].
2004
2005 if isinstance(node, yaml.ScalarNode):
2006 # !include foo.yaml
2007 return [loader.construct_scalar(node)]
2008
2009 if isinstance(node, yaml.SequenceNode):
2010 # !include [foo.yaml, bar.yaml]
2011 return loader.construct_sequence(node)
2012
2013 _binding_inc_error("unrecognised node type in !include statement")
2014
2015
Kumar Gala83be5cb2020-10-17 11:23:22 -05002016def _check_prop_type_and_default(prop_name, prop_type, default, binding_path):
Martí Bolívar7165b772020-10-12 12:32:25 -07002017 # Binding._check_properties() helper. Checks 'type:' and 'default:' for the
2018 # property named 'prop_name'
Ulf Magnussonff1f7522019-08-29 22:21:33 +02002019
2020 if prop_type is None:
2021 _err("missing 'type:' for '{}' in 'properties' in {}"
2022 .format(prop_name, binding_path))
2023
2024 ok_types = {"boolean", "int", "array", "uint8-array", "string",
2025 "string-array", "phandle", "phandles", "phandle-array",
Ulf Magnusson23a5b492019-12-30 22:52:11 +01002026 "path", "compound"}
Ulf Magnussonff1f7522019-08-29 22:21:33 +02002027
2028 if prop_type not in ok_types:
2029 _err("'{}' in 'properties:' in {} has unknown type '{}', expected one "
2030 "of {}".format(prop_name, binding_path, prop_type,
2031 ", ".join(ok_types)))
2032
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02002033 if prop_type == "phandle-array" and not prop_name.endswith("s"):
2034 _err("'{}' in 'properties:' in {} is 'type: phandle-array', but its "
2035 "name does not end in -s. This is required since property names "
2036 "like '#pwm-cells' and 'pwm-names' get derived from 'pwms', for "
2037 "example.".format(prop_name, binding_path))
2038
Ulf Magnussonff1f7522019-08-29 22:21:33 +02002039 # Check default
2040
2041 if default is None:
2042 return
2043
Ulf Magnussonff1f7522019-08-29 22:21:33 +02002044 if prop_type in {"boolean", "compound", "phandle", "phandles",
Ulf Magnusson23a5b492019-12-30 22:52:11 +01002045 "phandle-array", "path"}:
Ulf Magnussonff1f7522019-08-29 22:21:33 +02002046 _err("'default:' can't be combined with 'type: {}' for '{}' in "
2047 "'properties:' in {}".format(prop_type, prop_name, binding_path))
2048
2049 def ok_default():
2050 # Returns True if 'default' is an okay default for the property's type
2051
2052 if prop_type == "int" and isinstance(default, int) or \
2053 prop_type == "string" and isinstance(default, str):
2054 return True
2055
2056 # array, uint8-array, or string-array
2057
2058 if not isinstance(default, list):
2059 return False
2060
2061 if prop_type == "array" and \
2062 all(isinstance(val, int) for val in default):
2063 return True
2064
2065 if prop_type == "uint8-array" and \
2066 all(isinstance(val, int) and 0 <= val <= 255 for val in default):
2067 return True
2068
2069 # string-array
2070 return all(isinstance(val, str) for val in default)
2071
2072 if not ok_default():
2073 _err("'default: {}' is invalid for '{}' in 'properties:' in {}, which "
2074 "has type {}".format(default, prop_name, binding_path, prop_type))
2075
2076
Ulf Magnusson62d57412018-12-17 20:09:47 +01002077def _translate(addr, node):
2078 # Recursively translates 'addr' on 'node' to the address space(s) of its
2079 # parent(s), by looking at 'ranges' properties. Returns the translated
2080 # address.
Ulf Magnusson73ac1462019-09-23 05:14:18 +02002081 #
2082 # node:
2083 # dtlib.Node instance
Ulf Magnusson62d57412018-12-17 20:09:47 +01002084
2085 if not node.parent or "ranges" not in node.parent.props:
2086 # No translation
2087 return addr
2088
2089 if not node.parent.props["ranges"].value:
2090 # DT spec.: "If the property is defined with an <empty> value, it
2091 # specifies that the parent and child address space is identical, and
2092 # no address translation is required."
2093 #
2094 # Treat this the same as a 'range' that explicitly does a one-to-one
2095 # mapping, as opposed to there not being any translation.
2096 return _translate(addr, node.parent)
2097
2098 # Gives the size of each component in a translation 3-tuple in 'ranges'
2099 child_address_cells = _address_cells(node)
2100 parent_address_cells = _address_cells(node.parent)
2101 child_size_cells = _size_cells(node)
2102
2103 # Number of cells for one translation 3-tuple in 'ranges'
2104 entry_cells = child_address_cells + parent_address_cells + child_size_cells
2105
Ulf Magnusson57b2d272019-12-28 17:11:32 +01002106 for raw_range in _slice(node.parent, "ranges", 4*entry_cells,
2107 "4*(<#address-cells> (= {}) + "
2108 "<#address-cells for parent> (= {}) + "
2109 "<#size-cells> (= {}))"
2110 .format(child_address_cells, parent_address_cells,
2111 child_size_cells)):
Ulf Magnusson62d57412018-12-17 20:09:47 +01002112 child_addr = to_num(raw_range[:4*child_address_cells])
2113 raw_range = raw_range[4*child_address_cells:]
2114
2115 parent_addr = to_num(raw_range[:4*parent_address_cells])
2116 raw_range = raw_range[4*parent_address_cells:]
2117
2118 child_len = to_num(raw_range)
2119
2120 if child_addr <= addr < child_addr + child_len:
2121 # 'addr' is within range of a translation in 'ranges'. Recursively
2122 # translate it and return the result.
2123 return _translate(parent_addr + addr - child_addr, node.parent)
2124
2125 # 'addr' is not within range of any translation in 'ranges'
2126 return addr
2127
2128
2129def _add_names(node, names_ident, objs):
2130 # Helper for registering names from <foo>-names properties.
2131 #
2132 # node:
Ulf Magnusson73ac1462019-09-23 05:14:18 +02002133 # edtlib.Node instance
Ulf Magnusson62d57412018-12-17 20:09:47 +01002134 #
2135 # names-ident:
Ulf Magnusson95deec12019-08-06 21:51:06 +02002136 # The <foo> part of <foo>-names, e.g. "reg" for "reg-names"
Ulf Magnusson62d57412018-12-17 20:09:47 +01002137 #
2138 # objs:
2139 # list of objects whose .name field should be set
2140
Ulf Magnusson95deec12019-08-06 21:51:06 +02002141 full_names_ident = names_ident + "-names"
2142
2143 if full_names_ident in node.props:
2144 names = node.props[full_names_ident].to_strings()
Ulf Magnusson62d57412018-12-17 20:09:47 +01002145 if len(names) != len(objs):
Ulf Magnussoneef8d192019-10-30 16:05:30 +01002146 _err("{} property in {} in {} has {} strings, expected {} strings"
2147 .format(full_names_ident, node.path, node.dt.filename,
2148 len(names), len(objs)))
Ulf Magnusson62d57412018-12-17 20:09:47 +01002149
2150 for obj, name in zip(objs, names):
Martí Bolívar38ede5a2020-12-17 14:12:01 -08002151 if obj is None:
2152 continue
Ulf Magnusson62d57412018-12-17 20:09:47 +01002153 obj.name = name
2154 else:
2155 for obj in objs:
Martí Bolívar38ede5a2020-12-17 14:12:01 -08002156 if obj is not None:
2157 obj.name = None
Ulf Magnusson62d57412018-12-17 20:09:47 +01002158
2159
2160def _interrupt_parent(node):
2161 # Returns the node pointed at by the closest 'interrupt-parent', searching
2162 # the parents of 'node'. As of writing, this behavior isn't specified in
2163 # the DT spec., but seems to match what some .dts files except.
2164
2165 while node:
2166 if "interrupt-parent" in node.props:
2167 return node.props["interrupt-parent"].to_node()
2168 node = node.parent
2169
2170 _err("{!r} has an 'interrupts' property, but neither the node nor any "
2171 "of its parents has an 'interrupt-parent' property".format(node))
2172
2173
2174def _interrupts(node):
Ulf Magnusson6b875042019-09-23 07:40:16 +02002175 # Returns a list of (<controller>, <data>) tuples, with one tuple per
Ulf Magnusson62d57412018-12-17 20:09:47 +01002176 # interrupt generated by 'node'. <controller> is the destination of the
Ulf Magnusson6b875042019-09-23 07:40:16 +02002177 # interrupt (possibly after mapping through an 'interrupt-map'), and <data>
2178 # the data associated with the interrupt (as a 'bytes' object).
Ulf Magnusson62d57412018-12-17 20:09:47 +01002179
2180 # Takes precedence over 'interrupts' if both are present
2181 if "interrupts-extended" in node.props:
2182 prop = node.props["interrupts-extended"]
2183 return [_map_interrupt(node, iparent, spec)
Ulf Magnusson95deec12019-08-06 21:51:06 +02002184 for iparent, spec in _phandle_val_list(prop, "interrupt")]
Ulf Magnusson62d57412018-12-17 20:09:47 +01002185
2186 if "interrupts" in node.props:
2187 # Treat 'interrupts' as a special case of 'interrupts-extended', with
2188 # the same interrupt parent for all interrupts
2189
2190 iparent = _interrupt_parent(node)
2191 interrupt_cells = _interrupt_cells(iparent)
2192
2193 return [_map_interrupt(node, iparent, raw)
Ulf Magnusson57b2d272019-12-28 17:11:32 +01002194 for raw in _slice(node, "interrupts", 4*interrupt_cells,
2195 "4*<#interrupt-cells>")]
Ulf Magnusson62d57412018-12-17 20:09:47 +01002196
2197 return []
2198
2199
2200def _map_interrupt(child, parent, child_spec):
Ulf Magnusson6b875042019-09-23 07:40:16 +02002201 # Translates an interrupt headed from 'child' to 'parent' with data
Ulf Magnusson62d57412018-12-17 20:09:47 +01002202 # 'child_spec' through any 'interrupt-map' properties. Returns a
Ulf Magnusson6b875042019-09-23 07:40:16 +02002203 # (<controller>, <data>) tuple with the final destination after mapping.
Ulf Magnusson62d57412018-12-17 20:09:47 +01002204
2205 if "interrupt-controller" in parent.props:
2206 return (parent, child_spec)
2207
2208 def own_address_cells(node):
2209 # Used for parents pointed at by 'interrupt-map'. We can't use
2210 # _address_cells(), because it's the #address-cells property on 'node'
2211 # itself that matters.
2212
2213 address_cells = node.props.get("#address-cells")
2214 if not address_cells:
2215 _err("missing #address-cells on {!r} (while handling interrupt-map)"
2216 .format(node))
2217 return address_cells.to_num()
2218
2219 def spec_len_fn(node):
2220 # Can't use _address_cells() here, because it's the #address-cells
2221 # property on 'node' itself that matters
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02002222 return own_address_cells(node) + _interrupt_cells(node)
Ulf Magnusson62d57412018-12-17 20:09:47 +01002223
2224 parent, raw_spec = _map(
2225 "interrupt", child, parent, _raw_unit_addr(child) + child_spec,
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02002226 spec_len_fn, require_controller=True)
Ulf Magnusson62d57412018-12-17 20:09:47 +01002227
2228 # Strip the parent unit address part, if any
2229 return (parent, raw_spec[4*own_address_cells(parent):])
2230
2231
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02002232def _map_phandle_array_entry(child, parent, child_spec, basename):
Ulf Magnusson6b875042019-09-23 07:40:16 +02002233 # Returns a (<controller>, <data>) tuple with the final destination after
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02002234 # mapping through any '<basename>-map' (e.g. gpio-map) properties. See
2235 # _map_interrupt().
Ulf Magnusson62d57412018-12-17 20:09:47 +01002236
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02002237 def spec_len_fn(node):
2238 prop_name = "#{}-cells".format(basename)
2239 if prop_name not in node.props:
2240 _err("expected '{}' property on {!r} (referenced by {!r})"
2241 .format(prop_name, node, child))
2242 return node.props[prop_name].to_num()
Ulf Magnusson62d57412018-12-17 20:09:47 +01002243
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02002244 # Do not require <prefix>-controller for anything but interrupts for now
2245 return _map(basename, child, parent, child_spec, spec_len_fn,
2246 require_controller=False)
Ulf Magnusson62d57412018-12-17 20:09:47 +01002247
2248
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02002249def _map(prefix, child, parent, child_spec, spec_len_fn, require_controller):
Ulf Magnusson62d57412018-12-17 20:09:47 +01002250 # Common code for mapping through <prefix>-map properties, e.g.
2251 # interrupt-map and gpio-map.
2252 #
2253 # prefix:
2254 # The prefix, e.g. "interrupt" or "gpio"
2255 #
2256 # child:
2257 # The "sender", e.g. the node with 'interrupts = <...>'
2258 #
2259 # parent:
2260 # The "receiver", e.g. a node with 'interrupt-map = <...>' or
2261 # 'interrupt-controller' (no mapping)
2262 #
2263 # child_spec:
2264 # The data associated with the interrupt/GPIO/etc., as a 'bytes' object,
2265 # e.g. <1 2> for 'foo-gpios = <&gpio1 1 2>'.
2266 #
2267 # spec_len_fn:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02002268 # Function called on a parent specified in a *-map property to get the
2269 # length of the parent specifier (data after phandle in *-map), in cells
2270 #
2271 # require_controller:
2272 # If True, the final controller node after mapping is required to have
2273 # to have a <prefix>-controller property.
Ulf Magnusson62d57412018-12-17 20:09:47 +01002274
2275 map_prop = parent.props.get(prefix + "-map")
2276 if not map_prop:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02002277 if require_controller and prefix + "-controller" not in parent.props:
Ulf Magnusson62d57412018-12-17 20:09:47 +01002278 _err("expected '{}-controller' property on {!r} "
2279 "(referenced by {!r})".format(prefix, parent, child))
2280
2281 # No mapping
2282 return (parent, child_spec)
2283
2284 masked_child_spec = _mask(prefix, child, parent, child_spec)
2285
2286 raw = map_prop.value
2287 while raw:
2288 if len(raw) < len(child_spec):
Ulf Magnusson6b875042019-09-23 07:40:16 +02002289 _err("bad value for {!r}, missing/truncated child data"
Ulf Magnusson62d57412018-12-17 20:09:47 +01002290 .format(map_prop))
2291 child_spec_entry = raw[:len(child_spec)]
2292 raw = raw[len(child_spec):]
2293
2294 if len(raw) < 4:
2295 _err("bad value for {!r}, missing/truncated phandle"
2296 .format(map_prop))
2297 phandle = to_num(raw[:4])
2298 raw = raw[4:]
2299
2300 # Parent specified in *-map
2301 map_parent = parent.dt.phandle2node.get(phandle)
2302 if not map_parent:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02002303 _err("bad phandle ({}) in {!r}".format(phandle, map_prop))
Ulf Magnusson62d57412018-12-17 20:09:47 +01002304
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02002305 map_parent_spec_len = 4*spec_len_fn(map_parent)
Ulf Magnusson62d57412018-12-17 20:09:47 +01002306 if len(raw) < map_parent_spec_len:
Ulf Magnusson6b875042019-09-23 07:40:16 +02002307 _err("bad value for {!r}, missing/truncated parent data"
Ulf Magnusson62d57412018-12-17 20:09:47 +01002308 .format(map_prop))
2309 parent_spec = raw[:map_parent_spec_len]
2310 raw = raw[map_parent_spec_len:]
2311
Ulf Magnusson6b875042019-09-23 07:40:16 +02002312 # Got one *-map row. Check if it matches the child data.
Ulf Magnusson62d57412018-12-17 20:09:47 +01002313 if child_spec_entry == masked_child_spec:
2314 # Handle *-map-pass-thru
2315 parent_spec = _pass_thru(
2316 prefix, child, parent, child_spec, parent_spec)
2317
2318 # Found match. Recursively map and return it.
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02002319 return _map(prefix, parent, map_parent, parent_spec, spec_len_fn,
2320 require_controller)
Ulf Magnusson62d57412018-12-17 20:09:47 +01002321
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02002322 _err("child specifier for {!r} ({}) does not appear in {!r}"
Ulf Magnusson62d57412018-12-17 20:09:47 +01002323 .format(child, child_spec, map_prop))
2324
2325
2326def _mask(prefix, child, parent, child_spec):
2327 # Common code for handling <prefix>-mask properties, e.g. interrupt-mask.
2328 # See _map() for the parameters.
2329
2330 mask_prop = parent.props.get(prefix + "-map-mask")
2331 if not mask_prop:
2332 # No mask
2333 return child_spec
2334
2335 mask = mask_prop.value
2336 if len(mask) != len(child_spec):
2337 _err("{!r}: expected '{}-mask' in {!r} to be {} bytes, is {} bytes"
2338 .format(child, prefix, parent, len(child_spec), len(mask)))
2339
2340 return _and(child_spec, mask)
2341
2342
2343def _pass_thru(prefix, child, parent, child_spec, parent_spec):
2344 # Common code for handling <prefix>-map-thru properties, e.g.
2345 # interrupt-pass-thru.
2346 #
2347 # parent_spec:
Ulf Magnusson6b875042019-09-23 07:40:16 +02002348 # The parent data from the matched entry in the <prefix>-map property
Ulf Magnusson62d57412018-12-17 20:09:47 +01002349 #
2350 # See _map() for the other parameters.
2351
2352 pass_thru_prop = parent.props.get(prefix + "-map-pass-thru")
2353 if not pass_thru_prop:
2354 # No pass-thru
2355 return parent_spec
2356
2357 pass_thru = pass_thru_prop.value
2358 if len(pass_thru) != len(child_spec):
2359 _err("{!r}: expected '{}-map-pass-thru' in {!r} to be {} bytes, is {} bytes"
2360 .format(child, prefix, parent, len(child_spec), len(pass_thru)))
2361
2362 res = _or(_and(child_spec, pass_thru),
2363 _and(parent_spec, _not(pass_thru)))
2364
2365 # Truncate to length of parent spec.
2366 return res[-len(parent_spec):]
2367
2368
2369def _raw_unit_addr(node):
2370 # _map_interrupt() helper. Returns the unit address (derived from 'reg' and
2371 # #address-cells) as a raw 'bytes'
2372
2373 if 'reg' not in node.props:
2374 _err("{!r} lacks 'reg' property (needed for 'interrupt-map' unit "
2375 "address lookup)".format(node))
2376
2377 addr_len = 4*_address_cells(node)
2378
2379 if len(node.props['reg'].value) < addr_len:
2380 _err("{!r} has too short 'reg' property (while doing 'interrupt-map' "
2381 "unit address lookup)".format(node))
2382
2383 return node.props['reg'].value[:addr_len]
2384
2385
2386def _and(b1, b2):
2387 # Returns the bitwise AND of the two 'bytes' objects b1 and b2. Pads
2388 # with ones on the left if the lengths are not equal.
2389
2390 # Pad on the left, to equal length
2391 maxlen = max(len(b1), len(b2))
2392 return bytes(x & y for x, y in zip(b1.rjust(maxlen, b'\xff'),
2393 b2.rjust(maxlen, b'\xff')))
2394
2395
2396def _or(b1, b2):
2397 # Returns the bitwise OR of the two 'bytes' objects b1 and b2. Pads with
2398 # zeros on the left if the lengths are not equal.
2399
2400 # Pad on the left, to equal length
2401 maxlen = max(len(b1), len(b2))
2402 return bytes(x | y for x, y in zip(b1.rjust(maxlen, b'\x00'),
2403 b2.rjust(maxlen, b'\x00')))
2404
2405
2406def _not(b):
2407 # Returns the bitwise not of the 'bytes' object 'b'
2408
2409 # ANDing with 0xFF avoids negative numbers
2410 return bytes(~x & 0xFF for x in b)
2411
2412
Ulf Magnusson95deec12019-08-06 21:51:06 +02002413def _phandle_val_list(prop, n_cells_name):
2414 # Parses a '<phandle> <value> <phandle> <value> ...' value. The number of
2415 # cells that make up each <value> is derived from the node pointed at by
2416 # the preceding <phandle>.
Ulf Magnusson62d57412018-12-17 20:09:47 +01002417 #
2418 # prop:
2419 # dtlib.Property with value to parse
2420 #
Ulf Magnusson95deec12019-08-06 21:51:06 +02002421 # n_cells_name:
2422 # The <name> part of the #<name>-cells property to look for on the nodes
2423 # the phandles point to, e.g. "gpio" for #gpio-cells.
Ulf Magnusson62d57412018-12-17 20:09:47 +01002424 #
Martí Bolívar38ede5a2020-12-17 14:12:01 -08002425 # Returns a list[Optional[tuple]].
2426 #
2427 # Each tuple in the list is a (<node>, <value>) pair, where <node>
2428 # is the node pointed at by <phandle>. If <phandle> does not refer
2429 # to a node, the entire list element is None.
Ulf Magnusson95deec12019-08-06 21:51:06 +02002430
2431 full_n_cells_name = "#{}-cells".format(n_cells_name)
Ulf Magnusson62d57412018-12-17 20:09:47 +01002432
2433 res = []
2434
2435 raw = prop.value
2436 while raw:
2437 if len(raw) < 4:
2438 # Not enough room for phandle
2439 _err("bad value for " + repr(prop))
2440 phandle = to_num(raw[:4])
2441 raw = raw[4:]
2442
2443 node = prop.node.dt.phandle2node.get(phandle)
2444 if not node:
Martí Bolívar38ede5a2020-12-17 14:12:01 -08002445 # Unspecified phandle-array element. This is valid; a 0
2446 # phandle value followed by no cells is an empty element.
2447 res.append(None)
2448 continue
Ulf Magnusson62d57412018-12-17 20:09:47 +01002449
Ulf Magnusson95deec12019-08-06 21:51:06 +02002450 if full_n_cells_name not in node.props:
2451 _err("{!r} lacks {}".format(node, full_n_cells_name))
2452
2453 n_cells = node.props[full_n_cells_name].to_num()
Ulf Magnusson62d57412018-12-17 20:09:47 +01002454 if len(raw) < 4*n_cells:
2455 _err("missing data after phandle in " + repr(prop))
2456
2457 res.append((node, raw[:4*n_cells]))
2458 raw = raw[4*n_cells:]
2459
2460 return res
2461
2462
2463def _address_cells(node):
2464 # Returns the #address-cells setting for 'node', giving the number of <u32>
2465 # cells used to encode the address in the 'reg' property
2466
2467 if "#address-cells" in node.parent.props:
2468 return node.parent.props["#address-cells"].to_num()
2469 return 2 # Default value per DT spec.
2470
2471
2472def _size_cells(node):
2473 # Returns the #size-cells setting for 'node', giving the number of <u32>
2474 # cells used to encode the size in the 'reg' property
2475
2476 if "#size-cells" in node.parent.props:
2477 return node.parent.props["#size-cells"].to_num()
2478 return 1 # Default value per DT spec.
2479
2480
2481def _interrupt_cells(node):
2482 # Returns the #interrupt-cells property value on 'node', erroring out if
2483 # 'node' has no #interrupt-cells property
2484
2485 if "#interrupt-cells" not in node.props:
2486 _err("{!r} lacks #interrupt-cells".format(node))
2487 return node.props["#interrupt-cells"].to_num()
2488
2489
Ulf Magnusson57b2d272019-12-28 17:11:32 +01002490def _slice(node, prop_name, size, size_hint):
Ulf Magnusson62d57412018-12-17 20:09:47 +01002491 # Splits node.props[prop_name].value into 'size'-sized chunks, returning a
2492 # list of chunks. Raises EDTError if the length of the property is not
Ulf Magnusson57b2d272019-12-28 17:11:32 +01002493 # evenly divisible by 'size'. 'size_hint' is a string shown on errors that
2494 # gives a hint on how 'size' was calculated.
Ulf Magnusson62d57412018-12-17 20:09:47 +01002495
2496 raw = node.props[prop_name].value
2497 if len(raw) % size:
2498 _err("'{}' property in {!r} has length {}, which is not evenly "
Ulf Magnusson57b2d272019-12-28 17:11:32 +01002499 "divisible by {} (= {}). Note that #*-cells "
2500 "properties come either from the parent node or from the "
2501 "controller (in the case of 'interrupts')."
2502 .format(prop_name, node, len(raw), size, size_hint))
Ulf Magnusson62d57412018-12-17 20:09:47 +01002503
2504 return [raw[i:i + size] for i in range(0, len(raw), size)]
2505
2506
Ulf Magnussonacf276f2019-08-07 19:33:45 +02002507def _check_dt(dt):
2508 # Does devicetree sanity checks. dtlib is meant to be general and
2509 # anything-goes except for very special properties like phandle, but in
2510 # edtlib we can be pickier.
2511
2512 # Check that 'status' has one of the values given in the devicetree spec.
2513
Ulf Magnussonfc5cd772019-09-26 13:47:58 +02002514 # Accept "ok" for backwards compatibility
2515 ok_status = {"ok", "okay", "disabled", "reserved", "fail", "fail-sss"}
Ulf Magnussonacf276f2019-08-07 19:33:45 +02002516
2517 for node in dt.node_iter():
2518 if "status" in node.props:
2519 try:
2520 status_val = node.props["status"].to_string()
2521 except DTError as e:
2522 # The error message gives the path
2523 _err(str(e))
2524
2525 if status_val not in ok_status:
2526 _err("unknown 'status' value \"{}\" in {} in {}, expected one "
2527 "of {} (see the devicetree specification)"
2528 .format(status_val, node.path, node.dt.filename,
2529 ", ".join(ok_status)))
2530
Ulf Magnusson378254a2019-08-14 18:44:35 +02002531 ranges_prop = node.props.get("ranges")
2532 if ranges_prop:
2533 if ranges_prop.type not in (TYPE_EMPTY, TYPE_NUMS):
2534 _err("expected 'ranges = < ... >;' in {} in {}, not '{}' "
2535 "(see the devicetree specification)"
2536 .format(node.path, node.dt.filename, ranges_prop))
2537
Ulf Magnussonacf276f2019-08-07 19:33:45 +02002538
Ulf Magnusson62d57412018-12-17 20:09:47 +01002539def _err(msg):
2540 raise EDTError(msg)
Ulf Magnusson0e239942019-11-12 18:21:02 +01002541
Martí Bolívar09858492020-12-08 09:41:49 -08002542# Logging object
2543_LOG = logging.getLogger(__name__)
2544
Martí Bolívarb6dc0a22020-12-02 14:03:46 -08002545# Regular expression for non-alphanumeric-or-underscore characters.
2546_NOT_ALPHANUM_OR_UNDERSCORE = re.compile(r'\W', re.ASCII)
Ulf Magnusson0e239942019-11-12 18:21:02 +01002547
2548# Custom PyYAML binding loader class to avoid modifying yaml.Loader directly,
2549# which could interfere with YAML loading in clients
2550class _BindingLoader(Loader):
2551 pass
2552
2553
2554# Add legacy '!include foo.yaml' handling
2555_BindingLoader.add_constructor("!include", _binding_include)
Ulf Magnusson72158852019-11-12 18:33:00 +01002556
2557# Use OrderedDict instead of plain dict for YAML mappings, to preserve
2558# insertion order on Python 3.5 and earlier (plain dicts only preserve
2559# insertion order on Python 3.6+). This makes testing easier and avoids
2560# surprises.
2561#
2562# Adapted from
2563# https://stackoverflow.com/questions/5121931/in-python-how-can-you-load-yaml-mappings-as-ordereddicts.
2564# Hopefully this API stays stable.
2565_BindingLoader.add_constructor(
2566 yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
2567 lambda loader, node: OrderedDict(loader.construct_pairs(node)))
Kumar Galabc48f1c2020-05-01 12:33:00 -05002568
Martí Bolívar88094ec2020-12-02 13:52:43 -08002569#
2570# "Default" binding for properties which are defined by the spec.
2571#
2572# Zephyr: do not change the _DEFAULT_PROP_TYPES keys without
2573# updating the documentation for the DT_PROP() macro in
2574# include/devicetree.h.
2575#
2576
Kumar Galabc48f1c2020-05-01 12:33:00 -05002577_DEFAULT_PROP_TYPES = {
2578 "compatible": "string-array",
2579 "status": "string",
2580 "reg": "array",
2581 "reg-names": "string-array",
2582 "label": "string",
Martí Bolívar414ed862020-05-19 14:13:46 -07002583 "interrupts": "array",
Kumar Galabc48f1c2020-05-01 12:33:00 -05002584 "interrupts-extended": "compound",
2585 "interrupt-names": "string-array",
2586 "interrupt-controller": "boolean",
2587}
Martí Bolívar88094ec2020-12-02 13:52:43 -08002588
2589_STATUS_ENUM = "ok okay disabled reserved fail fail-sss".split()
2590
2591def _raw_default_property_for(name):
2592 ret = {
2593 'type': _DEFAULT_PROP_TYPES[name],
2594 'required': False,
2595 }
2596 if name == 'status':
2597 ret['enum'] = _STATUS_ENUM
2598 return ret
2599
2600_DEFAULT_PROP_BINDING = Binding(
2601 None, {},
2602 raw={
2603 'properties': {
2604 name: _raw_default_property_for(name)
2605 for name in _DEFAULT_PROP_TYPES
2606 },
2607 },
Martí Bolívar09858492020-12-08 09:41:49 -08002608 require_compatible=False, require_description=False,
Martí Bolívar88094ec2020-12-02 13:52:43 -08002609)
2610
2611_DEFAULT_PROP_SPECS = {
2612 name: PropertySpec(name, _DEFAULT_PROP_BINDING)
2613 for name in _DEFAULT_PROP_TYPES
2614}