blob: e5cbdf26e9ff73e29be6cb8c1f282c6d1a2e4355 [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
11properties.
Ulf Magnusson62d57412018-12-17 20:09:47 +010012
Ulf Magnusson73ac1462019-09-23 05:14:18 +020013Bindings are files that describe devicetree nodes. Devicetree nodes are usually
14mapped to bindings via their 'compatible = "..."' property, but a binding can
15also come from a 'child-binding:' key in the binding for the parent devicetree
16node.
Ulf Magnusson62d57412018-12-17 20:09:47 +010017
Ulf Magnusson73ac1462019-09-23 05:14:18 +020018Each devicetree node (dtlib.Node) gets a corresponding edtlib.Node instance,
19which has all the information related to the node.
Ulf Magnusson62d57412018-12-17 20:09:47 +010020
21The top-level entry point of the library is the EDT class. EDT.__init__() takes
Ulf Magnusson73ac1462019-09-23 05:14:18 +020022a .dts file to parse and a list of paths to directories containing bindings.
Ulf Magnusson62d57412018-12-17 20:09:47 +010023"""
24
Ulf Magnusson62d57412018-12-17 20:09:47 +010025# NOTE: testedtlib.py is the test suite for this library. It can be run
Ulf Magnusson73ac1462019-09-23 05:14:18 +020026# directly as a script:
27#
28# ./testedtlib.py
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#
37# This library is layered on top of dtlib, and is not meant to expose it to
38# clients. This keeps the header generation script simple.
39#
40# General biased advice:
41#
42# - Consider using @property for APIs that don't need parameters. It makes
43# functions look like attributes, which is less awkward in clients, and makes
44# it easy to switch back and forth between variables and functions.
45#
46# - Think about the data type of the thing you're exposing. Exposing something
47# as e.g. a list or a dictionary is often nicer and more flexible than adding
48# a function.
49#
50# - Avoid get_*() prefixes on functions. Name them after the thing they return
51# instead. This often makes the code read more naturally in callers.
52#
53# Also, consider using @property instead of get_*().
54#
55# - Don't expose dtlib stuff directly.
56#
57# - Add documentation for any new APIs you add.
58#
59# The convention here is that docstrings (quoted strings) are used for public
60# APIs, and "doc comments" for internal functions.
61#
62# @properties are documented in the class docstring, as if they were
63# variables. See the existing @properties for a template.
64#
65# - Please use ""-quoted strings instead of ''-quoted strings, just to make
66# things consistent (''-quoting is more common otherwise in Python)
67
Ulf Magnussond55ed932019-11-12 18:37:15 +010068from collections import OrderedDict
69import os
70import re
71import sys
72
73import yaml
74try:
75 # Use the C LibYAML parser if available, rather than the Python parser.
76 # This makes e.g. gen_defines.py more than twice as fast.
77 from yaml import CLoader as Loader
78except ImportError:
79 from yaml import Loader
80
81from dtlib import DT, DTError, to_num, to_nums, TYPE_EMPTY, TYPE_NUMS, \
82 TYPE_PHANDLE, TYPE_PHANDLES_AND_NUMS
83from grutils import Graph
84
Ulf Magnusson62d57412018-12-17 20:09:47 +010085#
86# Public classes
87#
88
89
90class EDT:
91 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +020092 Represents a devicetree augmented with information from bindings.
Ulf Magnusson62d57412018-12-17 20:09:47 +010093
94 These attributes are available on EDT objects:
95
Ulf Magnusson73ac1462019-09-23 05:14:18 +020096 nodes:
97 A list of Node objects for the nodes that appear in the devicetree
Ulf Magnusson62d57412018-12-17 20:09:47 +010098
99 dts_path:
100 The .dts path passed to __init__()
101
Michael Scottb8909432019-08-02 11:42:06 -0700102 bindings_dirs:
103 The bindings directory paths passed to __init__()
Ulf Magnusson62d57412018-12-17 20:09:47 +0100104 """
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200105 def __init__(self, dts, bindings_dirs, warn_file=None):
Ulf Magnusson62d57412018-12-17 20:09:47 +0100106 """
107 EDT constructor. This is the top-level entry point to the library.
108
109 dts:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200110 Path to devicetree .dts file
Ulf Magnusson62d57412018-12-17 20:09:47 +0100111
Michael Scottb8909432019-08-02 11:42:06 -0700112 bindings_dirs:
113 List of paths to directories containing bindings, in YAML format.
114 These directories are recursively searched for .yaml files.
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200115
116 warn_file:
117 'file' object to write warnings to. If None, sys.stderr is used.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100118 """
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200119 # Do this indirection with None in case sys.stderr is deliberately
120 # overridden
121 self._warn_file = sys.stderr if warn_file is None else warn_file
122
Ulf Magnusson62d57412018-12-17 20:09:47 +0100123 self.dts_path = dts
Michael Scottb8909432019-08-02 11:42:06 -0700124 self.bindings_dirs = bindings_dirs
Ulf Magnusson62d57412018-12-17 20:09:47 +0100125
126 self._dt = DT(dts)
Ulf Magnussonacf276f2019-08-07 19:33:45 +0200127 _check_dt(self._dt)
Ulf Magnusson62d57412018-12-17 20:09:47 +0100128
Michael Scottb8909432019-08-02 11:42:06 -0700129 self._init_compat2binding(bindings_dirs)
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200130 self._init_nodes()
Ulf Magnusson62d57412018-12-17 20:09:47 +0100131
Peter A. Bigotea956f42019-09-27 11:31:36 -0500132 self._define_order()
133
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200134 def get_node(self, path):
Ulf Magnusson62d57412018-12-17 20:09:47 +0100135 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200136 Returns the Node at the DT path or alias 'path'. Raises EDTError if the
137 path or alias doesn't exist.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100138 """
139 try:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200140 return self._node2enode[self._dt.get_node(path)]
Ulf Magnusson62d57412018-12-17 20:09:47 +0100141 except DTError as e:
142 _err(e)
143
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200144 def chosen_node(self, name):
Ulf Magnusson62d57412018-12-17 20:09:47 +0100145 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200146 Returns the Node pointed at by the property named 'name' in /chosen, or
147 None if the property is missing
Ulf Magnusson62d57412018-12-17 20:09:47 +0100148 """
149 try:
150 chosen = self._dt.get_node("/chosen")
151 except DTError:
152 # No /chosen node
153 return None
154
155 if name not in chosen.props:
156 return None
157
Ulf Magnusson06b746c2019-08-09 20:38:17 +0200158 # to_path() checks that the node exists
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200159 return self._node2enode[chosen.props[name].to_path()]
Ulf Magnusson62d57412018-12-17 20:09:47 +0100160
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200161 def __repr__(self):
162 return "<EDT for '{}', binding directories '{}'>".format(
163 self.dts_path, self.bindings_dirs)
164
Peter A. Bigotea956f42019-09-27 11:31:36 -0500165 def scc_order(self):
166 """
167 Returns a list of lists of Nodes where all elements of each list
168 depend on each other, and the Nodes in any list do not depend
169 on any Node in a subsequent list. Each list defines a Strongly
170 Connected Component (SCC) of the graph.
171
172 For an acyclic graph each list will be a singleton. Cycles
173 will be represented by lists with multiple nodes. Cycles are
174 not expected to be present in devicetree graphs.
175 """
176 try:
177 return self._graph.scc_order()
178 except Exception as e:
179 raise EDTError(e)
180
181 def _define_order(self):
182 # Constructs a graph of dependencies between Node instances,
183 # then calculates a partial order over the dependencies. The
184 # algorithm supports detecting dependency loops.
185
186 self._graph = Graph()
187
188 for node in self.nodes:
189 # A Node always depends on its parent.
190 for child in node.children.values():
191 self._graph.add_edge(child, node)
192
193 # A Node depends on any Nodes present in 'phandle',
194 # 'phandles', or 'phandle-array' property values.
195 for prop in node.props.values():
196 if prop.type == 'phandle':
197 self._graph.add_edge(node, prop.val)
198 elif prop.type == 'phandles':
199 for phandle_node in prop.val:
200 self._graph.add_edge(node, phandle_node)
201 elif prop.type == 'phandle-array':
202 for cd in prop.val:
203 self._graph.add_edge(node, cd.controller)
204
205 # A Node depends on whatever supports the interrupts it
206 # generates.
207 for intr in node.interrupts:
208 self._graph.add_edge(node, intr.controller)
209
210 # Calculate an order that ensures no node is before any node
211 # it depends on. This sets the dep_ordinal field in each
212 # Node.
213 self.scc_order()
214
Michael Scottb8909432019-08-02 11:42:06 -0700215 def _init_compat2binding(self, bindings_dirs):
Ulf Magnusson62d57412018-12-17 20:09:47 +0100216 # Creates self._compat2binding. This is a dictionary that maps
217 # (<compatible>, <bus>) tuples (both strings) to (<binding>, <path>)
218 # tuples. <binding> is the binding in parsed PyYAML format, and <path>
219 # the path to the binding (nice for binding-related error messages).
220 #
221 # For example, self._compat2binding["company,dev", "can"] contains the
222 # binding/path for the 'company,dev' device, when it appears on the CAN
223 # bus.
224 #
225 # For bindings that don't specify a bus, <bus> is None, so that e.g.
226 # self._compat2binding["company,notonbus", None] contains the binding.
227 #
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200228 # Only bindings for 'compatible' strings that appear in the devicetree
Ulf Magnusson62d57412018-12-17 20:09:47 +0100229 # are loaded.
230
Ulf Magnusson14138c32019-08-16 14:43:58 +0200231 dt_compats = _dt_compats(self._dt)
232 # Searches for any 'compatible' string mentioned in the devicetree
233 # files, with a regex
234 dt_compats_search = re.compile(
235 "|".join(re.escape(compat) for compat in dt_compats)
236 ).search
237
238 self._binding_paths = _binding_paths(bindings_dirs)
239
Ulf Magnusson62d57412018-12-17 20:09:47 +0100240 self._compat2binding = {}
241 for binding_path in self._binding_paths:
Ulf Magnusson14138c32019-08-16 14:43:58 +0200242 with open(binding_path, encoding="utf-8") as f:
243 contents = f.read()
244
245 # As an optimization, skip parsing files that don't contain any of
Ulf Magnussonb92ceb72019-10-29 08:48:05 +0100246 # the .dts 'compatible' strings, which should be reasonably safe
Ulf Magnusson14138c32019-08-16 14:43:58 +0200247 if not dt_compats_search(contents):
248 continue
249
250 # Load the binding and check that it actually matches one of the
251 # compatibles. Might get false positives above due to comments and
252 # stuff.
253
Ulf Magnusson4c6ea2d2019-09-19 22:54:55 +0200254 try:
255 # Parsed PyYAML output (Python lists/dictionaries/strings/etc.,
256 # representing the file)
Ulf Magnusson0e239942019-11-12 18:21:02 +0100257 binding = yaml.load(contents, Loader=_BindingLoader)
Ulf Magnusson4c6ea2d2019-09-19 22:54:55 +0200258 except yaml.YAMLError as e:
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200259 self._warn("'{}' appears in binding directories but isn't "
260 "valid YAML: {}".format(binding_path, e))
Ulf Magnusson4c6ea2d2019-09-19 22:54:55 +0200261 continue
Ulf Magnusson14138c32019-08-16 14:43:58 +0200262
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200263 binding_compat = self._binding_compat(binding, binding_path)
Ulf Magnusson4c6ea2d2019-09-19 22:54:55 +0200264 if binding_compat not in dt_compats:
265 # Either not a binding (binding_compat is None -- might be a
266 # binding fragment or a spurious file), or a binding whose
267 # compatible does not appear in the devicetree (picked up via
268 # some unrelated text in the binding file that happened to
269 # match a compatible)
Ulf Magnusson14138c32019-08-16 14:43:58 +0200270 continue
271
Ulf Magnussond834b692019-08-21 16:41:03 +0200272 # It's a match. Merge in the included bindings, do sanity checks,
Ulf Magnusson14138c32019-08-16 14:43:58 +0200273 # and register the binding.
274
Ulf Magnussond834b692019-08-21 16:41:03 +0200275 binding = self._merge_included_bindings(binding, binding_path)
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200276 self._check_binding(binding, binding_path)
Ulf Magnussona0fceff2019-08-19 20:32:25 +0200277
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100278 on_bus = _on_bus_from_binding(binding)
Ulf Magnussone3113802019-10-14 17:45:45 +0200279
280 # Do not allow two different bindings to have the same
Ulf Magnusson379145f2019-11-26 22:19:55 +0100281 # 'compatible:'/'on-bus:' combo
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100282 old_binding = self._compat2binding.get((binding_compat, on_bus))
Ulf Magnussone3113802019-10-14 17:45:45 +0200283 if old_binding:
284 msg = "both {} and {} have 'compatible: {}'".format(
285 old_binding[1], binding_path, binding_compat)
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100286 if on_bus is not None:
287 msg += " and 'on-bus: {}'".format(on_bus)
Ulf Magnussone3113802019-10-14 17:45:45 +0200288 _err(msg)
289
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100290 self._compat2binding[binding_compat, on_bus] = (binding, binding_path)
Ulf Magnusson62d57412018-12-17 20:09:47 +0100291
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200292 def _binding_compat(self, binding, binding_path):
293 # Returns the string listed in 'compatible:' in 'binding', or None if
294 # no compatible is found. Only takes 'self' for the sake of
295 # self._warn().
296 #
297 # Also searches for legacy compatibles on the form
298 #
299 # properties:
300 # compatible:
301 # constraint: <string>
302
303 def new_style_compat():
304 # New-style 'compatible: "foo"' compatible
305
306 if binding is None or "compatible" not in binding:
307 # Empty file, binding fragment, spurious file, or old-style
308 # compat
309 return None
310
311 compatible = binding["compatible"]
312 if not isinstance(compatible, str):
Martí Bolívar59e2f232019-11-13 11:05:52 -0800313 _err("malformed 'compatible: {}' field in {} - "
314 "should be a string, not {}"
315 .format(compatible, binding_path,
316 type(compatible).__name__))
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200317
318 return compatible
319
320 def old_style_compat():
321 # Old-style 'constraint: "foo"' compatible
322
323 try:
324 return binding["properties"]["compatible"]["constraint"]
325 except Exception:
326 return None
327
328 new_compat = new_style_compat()
329 old_compat = old_style_compat()
330 if old_compat:
331 self._warn("The 'properties: compatible: constraint: ...' way of "
332 "specifying the compatible in {} is deprecated. Put "
333 "'compatible: \"{}\"' at the top level of the binding "
334 "instead.".format(binding_path, old_compat))
335
336 if new_compat:
337 _err("compatibles for {} should be specified with either "
338 "'compatible:' at the top level or with the legacy "
339 "'properties: compatible: constraint: ...' field, not "
340 "both".format(binding_path))
341
342 return old_compat
343
344 return new_compat
345
Ulf Magnussond834b692019-08-21 16:41:03 +0200346 def _merge_included_bindings(self, binding, binding_path):
Ulf Magnusson2845d8f2019-09-10 19:17:29 +0200347 # Merges any bindings listed in the 'include:' section of 'binding'
Ulf Magnussond834b692019-08-21 16:41:03 +0200348 # into the top level of 'binding'. Also supports the legacy
349 # 'inherits: !include ...' syntax for including bindings.
350 #
351 # Properties in 'binding' take precedence over properties from included
352 # bindings.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100353
Ulf Magnussond834b692019-08-21 16:41:03 +0200354 fnames = []
Ulf Magnusson62d57412018-12-17 20:09:47 +0100355
Ulf Magnussond834b692019-08-21 16:41:03 +0200356 if "include" in binding:
357 include = binding.pop("include")
358 if isinstance(include, str):
359 fnames.append(include)
360 elif isinstance(include, list):
361 if not all(isinstance(elm, str) for elm in include):
362 _err("all elements in 'include:' in {} should be strings"
363 .format(binding_path))
364 fnames += include
365 else:
366 _err("'include:' in {} should be a string or a list of strings"
367 .format(binding_path))
Ulf Magnusson62d57412018-12-17 20:09:47 +0100368
Ulf Magnussond834b692019-08-21 16:41:03 +0200369 # Legacy syntax
370 if "inherits" in binding:
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200371 self._warn("the 'inherits:' syntax in {} is deprecated and will "
372 "be removed - please use 'include: foo.yaml' or "
373 "'include: [foo.yaml, bar.yaml]' instead"
374 .format(binding_path))
Ulf Magnusson62d57412018-12-17 20:09:47 +0100375
Ulf Magnussond834b692019-08-21 16:41:03 +0200376 inherits = binding.pop("inherits")
377 if not isinstance(inherits, list) or \
378 not all(isinstance(elm, str) for elm in inherits):
Ulf Magnussoncff38cf2019-09-05 21:53:20 +0200379 _err("malformed 'inherits:' in " + binding_path)
Ulf Magnussond834b692019-08-21 16:41:03 +0200380 fnames += inherits
381
Ulf Magnusson2845d8f2019-09-10 19:17:29 +0200382 if not fnames:
383 return binding
384
385 # Got a list of included files in 'fnames'. Now we need to merge them
386 # together and then merge them into 'binding'.
387
388 # First, merge the included files together. If more than one included
389 # file has a 'required:' for a particular property, OR the values
390 # together, so that 'required: true' wins.
391
392 merged_included = self._load_binding(fnames[0])
393 for fname in fnames[1:]:
394 included = self._load_binding(fname)
395 _merge_props(merged_included, included, None, binding_path,
396 check_required=False)
397
398 # Next, merge the merged included files into 'binding'. Error out if
399 # 'binding' has 'required: false' while the merged included files have
400 # 'required: true'.
401
402 _merge_props(binding, merged_included, None, binding_path,
403 check_required=True)
Ulf Magnussond834b692019-08-21 16:41:03 +0200404
405 return binding
406
Ulf Magnusson2845d8f2019-09-10 19:17:29 +0200407 def _load_binding(self, fname):
408 # Returns the contents of the binding given by 'fname' after merging
409 # any bindings it lists in 'include:' into it. 'fname' is just the
410 # basename of the file, so we check that there aren't multiple
411 # candidates.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100412
413 paths = [path for path in self._binding_paths
Ulf Magnusson2845d8f2019-09-10 19:17:29 +0200414 if os.path.basename(path) == fname]
Ulf Magnusson62d57412018-12-17 20:09:47 +0100415
416 if not paths:
Ulf Magnusson2845d8f2019-09-10 19:17:29 +0200417 _err("'{}' not found".format(fname))
Ulf Magnusson62d57412018-12-17 20:09:47 +0100418
419 if len(paths) > 1:
Ulf Magnussond834b692019-08-21 16:41:03 +0200420 _err("multiple candidates for included file '{}': {}"
Ulf Magnusson2845d8f2019-09-10 19:17:29 +0200421 .format(fname, ", ".join(paths)))
Ulf Magnusson62d57412018-12-17 20:09:47 +0100422
423 with open(paths[0], encoding="utf-8") as f:
Ulf Magnusson2845d8f2019-09-10 19:17:29 +0200424 return self._merge_included_bindings(
Ulf Magnusson0e239942019-11-12 18:21:02 +0100425 yaml.load(f, Loader=_BindingLoader),
Ulf Magnusson2845d8f2019-09-10 19:17:29 +0200426 paths[0])
Ulf Magnusson62d57412018-12-17 20:09:47 +0100427
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200428 def _init_nodes(self):
429 # Creates a list of edtlib.Node objects from the dtlib.Node objects, in
430 # self.nodes
Ulf Magnusson62d57412018-12-17 20:09:47 +0100431
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200432 # Maps each dtlib.Node to its corresponding edtlib.Node
433 self._node2enode = {}
Ulf Magnusson62d57412018-12-17 20:09:47 +0100434
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200435 self.nodes = []
Ulf Magnusson62d57412018-12-17 20:09:47 +0100436
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200437 for dt_node in self._dt.node_iter():
438 # Warning: We depend on parent Nodes being created before their
Ulf Magnusson06b746c2019-08-09 20:38:17 +0200439 # children. This is guaranteed by node_iter().
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200440 node = Node()
441 node.edt = self
442 node._node = dt_node
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100443 node.bus_node = node._bus_node()
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200444 node._init_binding()
445 node._init_regs()
446 node._set_instance_no()
Ulf Magnusson06b746c2019-08-09 20:38:17 +0200447
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200448 self.nodes.append(node)
449 self._node2enode[dt_node] = node
Ulf Magnusson62d57412018-12-17 20:09:47 +0100450
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200451 for node in self.nodes:
Ulf Magnussoneef8d192019-10-30 16:05:30 +0100452 # These depend on all Node objects having been created, because
453 # they (either always or sometimes) reference other nodes, so we
454 # run them separately
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200455 node._init_props()
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200456 node._init_interrupts()
Ulf Magnussoneef8d192019-10-30 16:05:30 +0100457 node._init_pinctrls()
Ulf Magnusson62d57412018-12-17 20:09:47 +0100458
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200459 def _check_binding(self, binding, binding_path):
460 # Does sanity checking on 'binding'. Only takes 'self' for the sake of
461 # self._warn().
462
Ulf Magnusson36b7ca42019-11-19 10:38:25 +0100463 if "title" in binding:
464 # This message is the message that people copy-pasting the old
465 # format will see in practice
466 self._warn("'title:' in {} is deprecated and will be removed (and "
467 "was never used). Just put a 'description:' that "
468 "describes the device instead. Use other bindings as "
469 "a reference, and note that all bindings were updated "
470 "recently. Think about what information would be "
471 "useful to other people (e.g. explanations of "
472 "acronyms, or datasheet links), and put that in as "
473 "well. The description text shows up as a comment "
474 "in the generated header. See yaml-multiline.info for "
475 "how to deal with multiple lines. You probably want "
476 "'description: |'.".format(binding_path))
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200477
Ulf Magnusson36b7ca42019-11-19 10:38:25 +0100478 if "description" not in binding:
479 _err("missing 'description' property in " + binding_path)
480
481 for prop in "title", "description":
482 if prop in binding and (not isinstance(binding[prop], str) or
483 not binding[prop]):
484 _err("malformed or empty '{}' in {}"
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200485 .format(prop, binding_path))
486
487 ok_top = {"title", "description", "compatible", "properties", "#cells",
Ulf Magnusson379145f2019-11-26 22:19:55 +0100488 "bus", "on-bus", "parent-bus", "child-bus", "parent", "child",
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200489 "child-binding", "sub-node"}
490
491 for prop in binding:
492 if prop not in ok_top and not prop.endswith("-cells"):
493 _err("unknown key '{}' in {}, expected one of {}, or *-cells"
494 .format(prop, binding_path, ", ".join(ok_top)))
495
Ulf Magnusson379145f2019-11-26 22:19:55 +0100496 for bus_key in "bus", "on-bus":
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200497 if bus_key in binding and \
498 not isinstance(binding[bus_key], str):
Ulf Magnusson36b7ca42019-11-19 10:38:25 +0100499 _err("malformed '{}:' value in {}, expected string"
500 .format(bus_key, binding_path))
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200501
Ulf Magnusson379145f2019-11-26 22:19:55 +0100502 # There are two legacy syntaxes for 'bus:' and 'on-bus:':
503 #
504 # child/parent-bus: foo
505 # child/parent: bus: foo
506 #
507 # We support both, with deprecation warnings.
508 for pc in "parent", "child":
509 # Legacy 'parent/child-bus:' keys
510 bus_key = pc + "-bus"
511 if bus_key in binding:
512 self._warn("'{}:' in {} is deprecated and will be removed - "
513 "please use a top-level '{}:' key instead (see "
514 "binding-template.yaml)"
515 .format(bus_key, binding_path,
516 "bus" if bus_key == "child-bus" else "on-bus"))
517
518 if not isinstance(binding[bus_key], str):
519 _err("malformed '{}:' value in {}, expected string"
520 .format(bus_key, binding_path))
521
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200522 # Legacy 'child/parent: bus: ...' keys
523 if pc in binding:
Ulf Magnusson379145f2019-11-26 22:19:55 +0100524 self._warn("'{}: bus: ...' in {} is deprecated and will be "
525 "removed - please use a top-level '{}' key instead "
526 "(see binding-template.yaml)"
527 .format(pc, binding_path,
528 "bus" if pc == "child" else "on-bus:"))
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200529
530 # Just 'bus:' is expected
531 if binding[pc].keys() != {"bus"}:
532 _err("expected (just) 'bus:' in '{}:' in {}"
533 .format(pc, binding_path))
534
535 if not isinstance(binding[pc]["bus"], str):
536 _err("malformed '{}: bus:' value in {}, expected string"
537 .format(pc, binding_path))
538
539 self._check_binding_properties(binding, binding_path)
540
541 if "child-binding" in binding:
542 if not isinstance(binding["child-binding"], dict):
543 _err("malformed 'child-binding:' in {}, expected a binding "
544 "(dictionary with keys/values)".format(binding_path))
545
546 self._check_binding(binding["child-binding"], binding_path)
547
548 if "sub-node" in binding:
549 self._warn("'sub-node: properties: ...' in {} is deprecated and "
550 "will be removed - please give a full binding for the "
551 "child node in 'child-binding:' instead (see "
552 "binding-template.yaml)".format(binding_path))
553
554 if binding["sub-node"].keys() != {"properties"}:
555 _err("expected (just) 'properties:' in 'sub-node:' in {}"
556 .format(binding_path))
557
558 self._check_binding_properties(binding["sub-node"], binding_path)
559
560 if "#cells" in binding:
561 self._warn('"#cells:" in {} is deprecated and will be removed - '
562 "please put 'interrupt-cells:', 'pwm-cells:', "
563 "'gpio-cells:', etc., instead. The name should match "
564 "the name of the corresponding phandle-array property "
565 "(see binding-template.yaml)".format(binding_path))
566
567 def ok_cells_val(val):
568 # Returns True if 'val' is an okay value for '*-cells:' (or the
569 # legacy '#cells:')
570
571 return isinstance(val, list) and \
572 all(isinstance(elm, str) for elm in val)
573
574 for key, val in binding.items():
575 if key.endswith("-cells") or key == "#cells":
576 if not ok_cells_val(val):
577 _err("malformed '{}:' in {}, expected a list of strings"
578 .format(key, binding_path))
579
580 def _check_binding_properties(self, binding, binding_path):
581 # _check_binding() helper for checking the contents of 'properties:'.
582 # Only takes 'self' for the sake of self._warn().
583
584 if "properties" not in binding:
585 return
586
587 ok_prop_keys = {"description", "type", "required", "category",
588 "constraint", "enum", "const", "default"}
589
590 for prop_name, options in binding["properties"].items():
591 for key in options:
592 if key == "category":
593 self._warn(
594 "please put 'required: {}' instead of 'category: {}' "
595 "in properties: {}: ...' in {} - 'category' will be "
596 "removed".format(
597 "true" if options["category"] == "required"
598 else "false",
599 options["category"], prop_name, binding_path))
600
601 if key not in ok_prop_keys:
602 _err("unknown setting '{}' in 'properties: {}: ...' in {}, "
603 "expected one of {}".format(
604 key, prop_name, binding_path,
605 ", ".join(ok_prop_keys)))
606
607 _check_prop_type_and_default(
608 prop_name, options.get("type"),
609 options.get("required") or options.get("category") == "required",
610 options.get("default"), binding_path)
611
612 if "required" in options and not isinstance(options["required"], bool):
613 _err("malformed 'required:' setting '{}' for '{}' in 'properties' "
614 "in {}, expected true/false"
615 .format(options["required"], prop_name, binding_path))
616
617 if "description" in options and \
618 not isinstance(options["description"], str):
619 _err("missing, malformed, or empty 'description' for '{}' in "
620 "'properties' in {}".format(prop_name, binding_path))
621
622 if "enum" in options and not isinstance(options["enum"], list):
623 _err("enum in {} for property '{}' is not a list"
624 .format(binding_path, prop_name))
625
626 if "const" in options and not isinstance(options["const"], (int, str)):
627 _err("const in {} for property '{}' is not a scalar"
628 .format(binding_path, prop_name))
629
630 def _warn(self, msg):
631 print("warning: " + msg, file=self._warn_file)
Ulf Magnusson62d57412018-12-17 20:09:47 +0100632
633
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200634class Node:
Ulf Magnusson62d57412018-12-17 20:09:47 +0100635 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200636 Represents a devicetree node, augmented with information from bindings, and
637 with some interpretation of devicetree properties. There's a one-to-one
638 correspondence between devicetree nodes and Nodes.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100639
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200640 These attributes are available on Node objects:
Ulf Magnusson62d57412018-12-17 20:09:47 +0100641
642 edt:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200643 The EDT instance this node is from
Ulf Magnusson62d57412018-12-17 20:09:47 +0100644
645 name:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200646 The name of the node
Ulf Magnusson62d57412018-12-17 20:09:47 +0100647
648 unit_addr:
649 An integer with the ...@<unit-address> portion of the node name,
650 translated through any 'ranges' properties on parent nodes, or None if
651 the node name has no unit-address portion
652
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200653 description:
654 The description string from the binding for the node, or None if the node
Ulf Magnussonf3f88a82019-10-28 13:02:21 +0100655 has no binding. Leading and trailing whitespace (including newlines) is
656 removed.
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200657
Ulf Magnusson62d57412018-12-17 20:09:47 +0100658 path:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200659 The devicetree path of the node
Ulf Magnusson62d57412018-12-17 20:09:47 +0100660
661 label:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200662 The text from the 'label' property on the node, or None if the node has
663 no 'label'
Ulf Magnusson62d57412018-12-17 20:09:47 +0100664
665 parent:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200666 The Node instance for the devicetree parent of the Node, or None if the
667 node is the root node
Ulf Magnusson110526e2019-09-21 04:14:33 +0200668
669 children:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200670 A dictionary with the Node instances for the devicetree children of the
671 node, indexed by name
Ulf Magnusson62d57412018-12-17 20:09:47 +0100672
Peter A. Bigotea956f42019-09-27 11:31:36 -0500673 dep_ordinal:
674 A non-negative integer value such that the value for a Node is
675 less than the value for all Nodes that depend on it.
676
677 The ordinal is defined for all Nodes including those that are not
678 'enabled', and is unique among nodes in its EDT 'nodes' list.
679
Ulf Magnusson2e1d2882019-11-06 22:36:50 +0100680 required_by:
681 A list with the nodes that directly depend on the node
682
683 depends_on:
684 A list with the nodes that the node directly depends on
685
Ulf Magnusson62d57412018-12-17 20:09:47 +0100686 enabled:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200687 True unless the node has 'status = "disabled"'
Ulf Magnusson62d57412018-12-17 20:09:47 +0100688
689 read_only:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200690 True if the node has a 'read-only' property, and False otherwise
Ulf Magnusson62d57412018-12-17 20:09:47 +0100691
692 instance_no:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200693 Dictionary that maps each 'compatible' string for the node to a unique
694 index among all nodes that have that 'compatible' string.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100695
696 As an example, 'instance_no["foo,led"] == 3' can be read as "this is the
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200697 fourth foo,led node".
Ulf Magnusson62d57412018-12-17 20:09:47 +0100698
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200699 Only enabled nodes (status != "disabled") are counted. 'instance_no' is
700 meaningless for disabled nodes.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100701
702 matching_compat:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200703 The 'compatible' string for the binding that matched the node, or None if
704 the node has no binding
Ulf Magnusson62d57412018-12-17 20:09:47 +0100705
Ulf Magnusson62d57412018-12-17 20:09:47 +0100706 binding_path:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200707 The path to the binding file for the node, or None if the node has no
Ulf Magnusson62d57412018-12-17 20:09:47 +0100708 binding
709
710 compats:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200711 A list of 'compatible' strings for the node, in the same order that
Ulf Magnusson62d57412018-12-17 20:09:47 +0100712 they're listed in the .dts file
713
714 regs:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200715 A list of Register objects for the node's registers
Ulf Magnusson62d57412018-12-17 20:09:47 +0100716
717 props:
Ulf Magnusson72158852019-11-12 18:33:00 +0100718 A collections.OrderedDict that maps property names to Property objects.
719 Property objects are created for all devicetree properties on the node
720 that are mentioned in 'properties:' in the binding.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100721
722 aliases:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200723 A list of aliases for the node. This is fetched from the /aliases node.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100724
725 interrupts:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +0200726 A list of ControllerAndData objects for the interrupts generated by the
Ulf Magnussoneef8d192019-10-30 16:05:30 +0100727 node. The list is empty if the node does not generate interrupts.
728
729 pinctrls:
730 A list of PinCtrl objects for the pinctrl-<index> properties on the
731 node, sorted by index. The list is empty if the node does not have any
732 pinctrl-<index> properties.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100733
734 bus:
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100735 If the node is a bus node (has a 'bus:' key in its binding), then this
736 attribute holds the bus type, e.g. "i2c" or "spi". If the node is not a
737 bus node, then this attribute is None.
738
739 on_bus:
740 The bus the node appears on, e.g. "i2c" or "spi". The bus is determined
741 by searching upwards for a parent node whose binding has a 'bus:' key,
742 returning the value of the first 'bus:' key found. If none of the node's
743 parents has a 'bus:' key, this attribute is None.
744
745 bus_node:
746 Like on_bus, but contains the Node for the bus controller, or None if the
747 node is not on a bus.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100748
749 flash_controller:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200750 The flash controller for the node. Only meaningful for nodes representing
751 flash partitions.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100752 """
753 @property
754 def name(self):
755 "See the class docstring"
756 return self._node.name
757
758 @property
759 def unit_addr(self):
760 "See the class docstring"
761
762 # TODO: Return a plain string here later, like dtlib.Node.unit_addr?
763
764 if "@" not in self.name:
765 return None
766
767 try:
768 addr = int(self.name.split("@", 1)[1], 16)
769 except ValueError:
770 _err("{!r} has non-hex unit address".format(self))
771
772 addr = _translate(addr, self._node)
773
774 if self.regs and self.regs[0].addr != addr:
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200775 self.edt._warn("unit-address and first reg (0x{:x}) don't match "
776 "for {}".format(self.regs[0].addr, self.name))
Ulf Magnusson62d57412018-12-17 20:09:47 +0100777
778 return addr
779
780 @property
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200781 def description(self):
782 "See the class docstring."
783 if self._binding and "description" in self._binding:
Ulf Magnussonf3f88a82019-10-28 13:02:21 +0100784 return self._binding["description"].strip()
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200785 return None
786
787 @property
Ulf Magnusson62d57412018-12-17 20:09:47 +0100788 def path(self):
789 "See the class docstring"
790 return self._node.path
791
792 @property
793 def label(self):
794 "See the class docstring"
795 if "label" in self._node.props:
796 return self._node.props["label"].to_string()
797 return None
798
799 @property
800 def parent(self):
801 "See the class docstring"
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200802 return self.edt._node2enode.get(self._node.parent)
Ulf Magnusson62d57412018-12-17 20:09:47 +0100803
804 @property
Ulf Magnusson110526e2019-09-21 04:14:33 +0200805 def children(self):
806 "See the class docstring"
807 # Could be initialized statically too to preserve identity, but not
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200808 # sure if needed. Parent nodes being initialized before their children
809 # would need to be kept in mind.
Ulf Magnusson72158852019-11-12 18:33:00 +0100810 return OrderedDict((name, self.edt._node2enode[node])
811 for name, node in self._node.nodes.items())
Ulf Magnusson110526e2019-09-21 04:14:33 +0200812
813 @property
Ulf Magnusson2e1d2882019-11-06 22:36:50 +0100814 def required_by(self):
815 "See the class docstring"
816 return self.edt._graph.required_by(self)
817
818 @property
819 def depends_on(self):
820 "See the class docstring"
821 return self.edt._graph.depends_on(self)
822
823 @property
Ulf Magnusson62d57412018-12-17 20:09:47 +0100824 def enabled(self):
825 "See the class docstring"
826 return "status" not in self._node.props or \
827 self._node.props["status"].to_string() != "disabled"
828
829 @property
830 def read_only(self):
831 "See the class docstring"
832 return "read-only" in self._node.props
833
834 @property
835 def aliases(self):
836 "See the class docstring"
837 return [alias for alias, node in self._node.dt.alias2node.items()
838 if node is self._node]
839
840 @property
841 def bus(self):
842 "See the class docstring"
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100843 binding = self._binding
844 if not binding:
845 return None
846
847 if "bus" in binding:
848 return binding["bus"]
849
850 # Legacy key
851 if "child-bus" in binding:
852 return binding["child-bus"]
853
854 # Legacy key
855 if "child" in binding:
856 # _check_binding() has checked that the "bus" key exists
857 return binding["child"]["bus"]
858
859 return None
860
861 @property
862 def on_bus(self):
863 "See the class docstring"
864 bus_node = self.bus_node
865 return bus_node.bus if bus_node else None
Ulf Magnusson62d57412018-12-17 20:09:47 +0100866
867 @property
868 def flash_controller(self):
869 "See the class docstring"
870
871 # The node path might be something like
872 # /flash-controller@4001E000/flash@0/partitions/partition@fc000. We go
873 # up two levels to get the flash and check its compat. The flash
874 # controller might be the flash itself (for cases like NOR flashes).
875 # For the case of 'soc-nv-flash', we assume the controller is the
876 # parent of the flash node.
877
878 if not self.parent or not self.parent.parent:
879 _err("flash partition {!r} lacks parent or grandparent node"
880 .format(self))
881
882 controller = self.parent.parent
883 if controller.matching_compat == "soc-nv-flash":
884 return controller.parent
885 return controller
886
887 def __repr__(self):
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200888 return "<Node {} in '{}', {}>".format(
Ulf Magnusson62d57412018-12-17 20:09:47 +0100889 self.path, self.edt.dts_path,
890 "binding " + self.binding_path if self.binding_path
891 else "no binding")
892
Ulf Magnusson62d57412018-12-17 20:09:47 +0100893 def _init_binding(self):
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200894 # Initializes Node.matching_compat, Node._binding, and
895 # Node.binding_path.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100896 #
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200897 # Node._binding holds the data from the node's binding file, in the
Ulf Magnusson62d57412018-12-17 20:09:47 +0100898 # format returned by PyYAML (plain Python lists, dicts, etc.), or None
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200899 # if the node has no binding.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100900
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200901 # This relies on the parent of the node having already been
Ulf Magnusson62d57412018-12-17 20:09:47 +0100902 # initialized, which is guaranteed by going through the nodes in
903 # node_iter() order.
904
905 if "compatible" in self._node.props:
906 self.compats = self._node.props["compatible"].to_strings()
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100907 on_bus = self.on_bus
Ulf Magnusson62d57412018-12-17 20:09:47 +0100908
909 for compat in self.compats:
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100910 if (compat, on_bus) in self.edt._compat2binding:
Ulf Magnusson62d57412018-12-17 20:09:47 +0100911 # Binding found
912 self.matching_compat = compat
913 self._binding, self.binding_path = \
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100914 self.edt._compat2binding[compat, on_bus]
Ulf Magnusson62d57412018-12-17 20:09:47 +0100915
Ulf Magnusson62d57412018-12-17 20:09:47 +0100916 return
917 else:
Ulf Magnusson0b1ab4a2019-09-17 18:24:30 +0200918 # No 'compatible' property. See if the parent binding has a
919 # 'child-binding:' key that gives the binding (or a legacy
920 # 'sub-node:' key).
Ulf Magnusson62d57412018-12-17 20:09:47 +0100921
922 self.compats = []
923
Ulf Magnusson0b1ab4a2019-09-17 18:24:30 +0200924 binding_from_parent = self._binding_from_parent()
925 if binding_from_parent:
926 self._binding = binding_from_parent
Ulf Magnusson62d57412018-12-17 20:09:47 +0100927 self.binding_path = self.parent.binding_path
Ulf Magnusson62d57412018-12-17 20:09:47 +0100928 self.matching_compat = self.parent.matching_compat
Ulf Magnusson0b1ab4a2019-09-17 18:24:30 +0200929
Ulf Magnusson62d57412018-12-17 20:09:47 +0100930 return
931
932 # No binding found
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200933 self._binding = self.binding_path = self.matching_compat = None
Ulf Magnusson62d57412018-12-17 20:09:47 +0100934
Ulf Magnusson0b1ab4a2019-09-17 18:24:30 +0200935 def _binding_from_parent(self):
936 # Returns the binding from 'child-binding:' in the parent node's
937 # binding (or from the legacy 'sub-node:' key), or None if missing
938
939 if not self.parent:
940 return None
941
942 pbinding = self.parent._binding
943 if not pbinding:
944 return None
945
946 if "child-binding" in pbinding:
947 return pbinding["child-binding"]
948
949 # Backwards compatibility
950 if "sub-node" in pbinding:
951 return {"title": pbinding["title"],
952 "description": pbinding["description"],
953 "properties": pbinding["sub-node"]["properties"]}
954
955 return None
956
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100957 def _bus_node(self):
958 # Returns the value for self.bus_node. Relies on parent nodes being
959 # initialized before their children.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100960
961 if not self.parent:
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100962 # This is the root node
Ulf Magnusson62d57412018-12-17 20:09:47 +0100963 return None
964
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100965 if self.parent.bus:
966 # The parent node is a bus node
967 return self.parent
Ulf Magnusson1ebe9452019-09-16 16:42:18 +0200968
Ulf Magnusson5e55eda2019-11-27 04:08:36 +0100969 # Same bus node as parent (possibly None)
970 return self.parent.bus_node
Ulf Magnusson62d57412018-12-17 20:09:47 +0100971
972 def _init_props(self):
Ulf Magnussonb918e252019-08-30 03:49:43 +0200973 # Creates self.props. See the class docstring. Also checks that all
974 # properties on the node are declared in its binding.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100975
Ulf Magnusson72158852019-11-12 18:33:00 +0100976 self.props = OrderedDict()
Ulf Magnusson62d57412018-12-17 20:09:47 +0100977
Ulf Magnussonb918e252019-08-30 03:49:43 +0200978 if not self._binding:
Ulf Magnusson62d57412018-12-17 20:09:47 +0100979 return
980
Ulf Magnussonb918e252019-08-30 03:49:43 +0200981 # Initialize self.props
982 if "properties" in self._binding:
983 for name, options in self._binding["properties"].items():
984 self._init_prop(name, options)
985
986 self._check_undeclared_props()
Ulf Magnusson62d57412018-12-17 20:09:47 +0100987
988 def _init_prop(self, name, options):
989 # _init_props() helper for initializing a single property
990
Ulf Magnusson62d57412018-12-17 20:09:47 +0100991 prop_type = options.get("type")
992 if not prop_type:
993 _err("'{}' in {} lacks 'type'".format(name, self.binding_path))
994
Ulf Magnusson8d317bc2019-09-06 12:31:55 +0200995 val = self._prop_val(
996 name, prop_type,
Ulf Magnussonff1f7522019-08-29 22:21:33 +0200997 options.get("required") or options.get("category") == "required",
998 options.get("default"))
Ulf Magnusson8d317bc2019-09-06 12:31:55 +0200999
Ulf Magnusson62d57412018-12-17 20:09:47 +01001000 if val is None:
Ulf Magnussonfcd665a2019-08-28 00:22:01 +02001001 # 'required: false' property that wasn't there, or a property type
1002 # for which we store no data.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001003 return
1004
Kumar Gala62bf2672019-08-09 14:51:58 -05001005 enum = options.get("enum")
1006 if enum and val not in enum:
1007 _err("value of property '{}' on {} in {} ({!r}) is not in 'enum' "
1008 "list in {} ({!r})"
1009 .format(name, self.path, self.edt.dts_path, val,
1010 self.binding_path, enum))
1011
Kumar Gala3d143742019-08-09 16:03:46 -05001012 const = options.get("const")
1013 if const is not None and val != const:
1014 _err("value of property '{}' on {} in {} ({!r}) is different from "
1015 "the 'const' value specified in {} ({!r})"
1016 .format(name, self.path, self.edt.dts_path, val,
1017 self.binding_path, const))
1018
Kumar Gala5dd715b2019-08-09 09:49:22 -05001019 # Skip properties that start with '#', like '#size-cells', and mapping
1020 # properties like 'gpio-map'/'interrupt-map'
1021 if name[0] == "#" or name.endswith("-map"):
1022 return
1023
Ulf Magnusson62d57412018-12-17 20:09:47 +01001024 prop = Property()
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001025 prop.node = self
Ulf Magnusson62d57412018-12-17 20:09:47 +01001026 prop.name = name
1027 prop.description = options.get("description")
1028 if prop.description:
Ulf Magnussonf3f88a82019-10-28 13:02:21 +01001029 prop.description = prop.description.strip()
Ulf Magnusson62d57412018-12-17 20:09:47 +01001030 prop.val = val
Ulf Magnusson165dde02019-08-14 11:13:14 +02001031 prop.type = prop_type
Kumar Gala62bf2672019-08-09 14:51:58 -05001032 prop.enum_index = None if enum is None else enum.index(val)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001033
1034 self.props[name] = prop
1035
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001036 def _prop_val(self, name, prop_type, required, default):
Ulf Magnusson62d57412018-12-17 20:09:47 +01001037 # _init_prop() helper for getting the property's value
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001038 #
1039 # name:
1040 # Property name from binding
1041 #
1042 # prop_type:
1043 # Property type from binding (a string like "int")
1044 #
1045 # optional:
1046 # True if the property isn't required to exist
1047 #
1048 # default:
1049 # Default value to use when the property doesn't exist, or None if
1050 # the binding doesn't give a default value
Ulf Magnusson62d57412018-12-17 20:09:47 +01001051
1052 node = self._node
Ulf Magnusson06b746c2019-08-09 20:38:17 +02001053 prop = node.props.get(name)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001054
Ulf Magnusson62d57412018-12-17 20:09:47 +01001055 if not prop:
Ulf Magnussonfcd665a2019-08-28 00:22:01 +02001056 if required and self.enabled:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001057 _err("'{}' is marked as required in 'properties:' in {}, but "
1058 "does not appear in {!r}".format(
1059 name, self.binding_path, node))
1060
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001061 if default is not None:
1062 # YAML doesn't have a native format for byte arrays. We need to
1063 # convert those from an array like [0x12, 0x34, ...]. The
1064 # format has already been checked in
1065 # _check_prop_type_and_default().
1066 if prop_type == "uint8-array":
1067 return bytes(default)
1068 return default
1069
Ulf Magnusson0c4f4a92019-09-30 16:42:07 +02001070 return False if prop_type == "boolean" else None
1071
1072 if prop_type == "boolean":
1073 if prop.type is not TYPE_EMPTY:
1074 _err("'{0}' in {1!r} is defined with 'type: boolean' in {2}, "
1075 "but is assigned a value ('{3}') instead of being empty "
1076 "('{0};')".format(name, node, self.binding_path, prop))
1077 return True
Ulf Magnusson62d57412018-12-17 20:09:47 +01001078
1079 if prop_type == "int":
1080 return prop.to_num()
1081
1082 if prop_type == "array":
1083 return prop.to_nums()
1084
1085 if prop_type == "uint8-array":
Ulf Magnusson06b746c2019-08-09 20:38:17 +02001086 return prop.to_bytes()
Ulf Magnusson62d57412018-12-17 20:09:47 +01001087
1088 if prop_type == "string":
1089 return prop.to_string()
1090
1091 if prop_type == "string-array":
1092 return prop.to_strings()
1093
Ulf Magnusson06b746c2019-08-09 20:38:17 +02001094 if prop_type == "phandle":
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001095 return self.edt._node2enode[prop.to_node()]
Ulf Magnusson06b746c2019-08-09 20:38:17 +02001096
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001097 if prop_type == "phandles":
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001098 return [self.edt._node2enode[node] for node in prop.to_nodes()]
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001099
1100 if prop_type == "phandle-array":
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001101 # This type is a bit high-level for dtlib as it involves
1102 # information from bindings and *-names properties, so there's no
1103 # to_phandle_array() in dtlib. Do the type check ourselves.
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001104 if prop.type not in (TYPE_PHANDLE, TYPE_PHANDLES_AND_NUMS):
1105 _err("expected property '{0}' in {1} in {2} to be assigned "
1106 "with '{0} = < &foo 1 2 ... &bar 3 4 ... >' (a mix of "
1107 "phandles and numbers), not '{3}'"
1108 .format(name, node.path, node.dt.filename, prop))
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001109
1110 return self._standard_phandle_val_list(prop)
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001111
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001112 # prop_type == "compound". We have already checked that the 'type:'
1113 # value is valid, in _check_binding().
1114 #
1115 # 'compound' is a dummy type for properties that don't fit any of the
1116 # patterns above, so that we can require all entries in 'properties:'
1117 # to have a 'type: ...'. No Property object is created for it.
1118 return None
Ulf Magnusson62d57412018-12-17 20:09:47 +01001119
Ulf Magnussonb918e252019-08-30 03:49:43 +02001120 def _check_undeclared_props(self):
1121 # Checks that all properties are declared in the binding
1122
1123 if "properties" in self._binding:
1124 declared_props = self._binding["properties"].keys()
1125 else:
1126 declared_props = set()
1127
1128 for prop_name in self._node.props:
1129 # Allow a few special properties to not be declared in the binding
1130 if prop_name.endswith("-controller") or \
1131 prop_name.startswith("#") or \
1132 prop_name.startswith("pinctrl-") or \
1133 prop_name in {
1134 "compatible", "status", "ranges", "phandle",
1135 "interrupt-parent", "interrupts-extended", "device_type"}:
1136 continue
1137
1138 if prop_name not in declared_props:
1139 _err("'{}' appears in {} in {}, but is not declared in "
1140 "'properties:' in {}"
1141 .format(prop_name, self._node.path, self.edt.dts_path,
1142 self.binding_path))
1143
Ulf Magnusson62d57412018-12-17 20:09:47 +01001144 def _init_regs(self):
Ulf Magnusson95deec12019-08-06 21:51:06 +02001145 # Initializes self.regs
Ulf Magnusson62d57412018-12-17 20:09:47 +01001146
1147 node = self._node
1148
1149 self.regs = []
1150
1151 if "reg" not in node.props:
1152 return
1153
1154 address_cells = _address_cells(node)
1155 size_cells = _size_cells(node)
1156
1157 for raw_reg in _slice(node, "reg", 4*(address_cells + size_cells)):
1158 reg = Register()
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001159 reg.node = self
Ulf Magnusson62d57412018-12-17 20:09:47 +01001160 reg.addr = _translate(to_num(raw_reg[:4*address_cells]), node)
1161 reg.size = to_num(raw_reg[4*address_cells:])
1162 if size_cells != 0 and reg.size == 0:
1163 _err("zero-sized 'reg' in {!r} seems meaningless (maybe you "
1164 "want a size of one or #size-cells = 0 instead)"
1165 .format(self._node))
1166
1167 self.regs.append(reg)
1168
Ulf Magnusson95deec12019-08-06 21:51:06 +02001169 _add_names(node, "reg", self.regs)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001170
Ulf Magnussoneef8d192019-10-30 16:05:30 +01001171 def _init_pinctrls(self):
1172 # Initializes self.pinctrls from any pinctrl-<index> properties
1173
1174 node = self._node
1175
1176 # pinctrl-<index> properties
1177 pinctrl_props = [prop for name, prop in node.props.items()
1178 if re.match("pinctrl-[0-9]+", name)]
1179 # Sort by index
1180 pinctrl_props.sort(key=lambda prop: prop.name)
1181
1182 # Check indices
1183 for i, prop in enumerate(pinctrl_props):
1184 if prop.name != "pinctrl-" + str(i):
1185 _err("missing 'pinctrl-{}' property on {!r} - indices should "
1186 "be contiguous and start from zero".format(i, node))
1187
1188 self.pinctrls = []
1189 for prop in pinctrl_props:
1190 pinctrl = PinCtrl()
1191 pinctrl.node = self
1192 pinctrl.conf_nodes = [
1193 self.edt._node2enode[node] for node in prop.to_nodes()
1194 ]
1195 self.pinctrls.append(pinctrl)
1196
1197 _add_names(node, "pinctrl", self.pinctrls)
1198
Ulf Magnusson62d57412018-12-17 20:09:47 +01001199 def _init_interrupts(self):
Ulf Magnusson95deec12019-08-06 21:51:06 +02001200 # Initializes self.interrupts
Ulf Magnusson62d57412018-12-17 20:09:47 +01001201
1202 node = self._node
1203
1204 self.interrupts = []
1205
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001206 for controller_node, data in _interrupts(node):
1207 interrupt = ControllerAndData()
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001208 interrupt.node = self
1209 interrupt.controller = self.edt._node2enode[controller_node]
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001210 interrupt.data = self._named_cells(interrupt.controller, data,
Ulf Magnusson6b875042019-09-23 07:40:16 +02001211 "interrupt")
Ulf Magnusson62d57412018-12-17 20:09:47 +01001212
1213 self.interrupts.append(interrupt)
1214
Ulf Magnusson95deec12019-08-06 21:51:06 +02001215 _add_names(node, "interrupt", self.interrupts)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001216
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001217 def _standard_phandle_val_list(self, prop):
1218 # Parses a property like
Ulf Magnusson95deec12019-08-06 21:51:06 +02001219 #
1220 # <name>s = <phandle value phandle value ...>
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001221 # (e.g., pwms = <&foo 1 2 &bar 3 4>)
Ulf Magnusson95deec12019-08-06 21:51:06 +02001222 #
1223 # , where each phandle points to a node that has a
1224 #
1225 # #<name>-cells = <size>
1226 #
1227 # property that gives the number of cells in the value after the
Ulf Magnusson567c3482019-09-26 20:34:13 +02001228 # phandle. These values are given names in *-cells in the binding for
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001229 # the controller.
1230 #
1231 # Also parses any
Ulf Magnusson95deec12019-08-06 21:51:06 +02001232 #
1233 # <name>-names = "...", "...", ...
1234 #
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001235 # Returns a list of ControllerAndData instances.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001236
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001237 if prop.name.endswith("gpios"):
1238 # There's some slight special-casing for *-gpios properties in that
1239 # e.g. foo-gpios still maps to #gpio-cells rather than
1240 # #foo-gpio-cells
1241 basename = "gpio"
1242 else:
1243 # Strip -s. We've already checked that the property names end in -s
1244 # in _check_binding().
1245 basename = prop.name[:-1]
Ulf Magnusson62d57412018-12-17 20:09:47 +01001246
Ulf Magnusson95deec12019-08-06 21:51:06 +02001247 res = []
Ulf Magnusson62d57412018-12-17 20:09:47 +01001248
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001249 for controller_node, data in _phandle_val_list(prop, basename):
1250 mapped_controller, mapped_data = \
1251 _map_phandle_array_entry(prop.node, controller_node, data,
1252 basename)
Ulf Magnusson95deec12019-08-06 21:51:06 +02001253
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001254 entry = ControllerAndData()
1255 entry.node = self
1256 entry.controller = self.edt._node2enode[mapped_controller]
1257 entry.data = self._named_cells(entry.controller, mapped_data,
1258 basename)
Ulf Magnusson95deec12019-08-06 21:51:06 +02001259
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001260 res.append(entry)
1261
1262 _add_names(self._node, basename, res)
Ulf Magnusson95deec12019-08-06 21:51:06 +02001263
1264 return res
Ulf Magnusson62d57412018-12-17 20:09:47 +01001265
Ulf Magnusson567c3482019-09-26 20:34:13 +02001266 def _named_cells(self, controller, data, basename):
1267 # Returns a dictionary that maps <basename>-cells names given in the
1268 # binding for 'controller' to cell values. 'data' is the raw data, as a
1269 # byte array.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001270
1271 if not controller._binding:
1272 _err("{} controller {!r} for {!r} lacks binding"
Ulf Magnusson567c3482019-09-26 20:34:13 +02001273 .format(basename, controller._node, self._node))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001274
Ulf Magnusson567c3482019-09-26 20:34:13 +02001275 if basename + "-cells" in controller._binding:
1276 cell_names = controller._binding[basename + "-cells"]
1277 elif "#cells" in controller._binding:
1278 # Backwards compatibility
Ulf Magnusson62d57412018-12-17 20:09:47 +01001279 cell_names = controller._binding["#cells"]
Ulf Magnusson62d57412018-12-17 20:09:47 +01001280 else:
Ulf Magnusson567c3482019-09-26 20:34:13 +02001281 # Treat no *-cells in the binding the same as an empty *-cells, so
1282 # that bindings don't have to have e.g. an empty 'clock-cells:' for
Ulf Magnusson62d57412018-12-17 20:09:47 +01001283 # '#clock-cells = <0>'.
1284 cell_names = []
1285
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001286 data_list = to_nums(data)
1287 if len(data_list) != len(cell_names):
Ulf Magnusson567c3482019-09-26 20:34:13 +02001288 _err("unexpected '{}-cells:' length in binding for {!r} - {} "
1289 "instead of {}"
1290 .format(basename, controller._node, len(cell_names),
1291 len(data_list)))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001292
Ulf Magnusson72158852019-11-12 18:33:00 +01001293 return OrderedDict(zip(cell_names, data_list))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001294
1295 def _set_instance_no(self):
1296 # Initializes self.instance_no
1297
1298 self.instance_no = {}
1299
1300 for compat in self.compats:
1301 self.instance_no[compat] = 0
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001302 for other_node in self.edt.nodes:
1303 if compat in other_node.compats and other_node.enabled:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001304 self.instance_no[compat] += 1
1305
1306
1307class Register:
1308 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001309 Represents a register on a node.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001310
1311 These attributes are available on Register objects:
1312
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001313 node:
1314 The Node instance this register is from
Ulf Magnusson62d57412018-12-17 20:09:47 +01001315
1316 name:
1317 The name of the register as given in the 'reg-names' property, or None if
1318 there is no 'reg-names' property
1319
1320 addr:
1321 The starting address of the register, in the parent address space. Any
1322 'ranges' properties are taken into account.
1323
1324 size:
1325 The length of the register in bytes
1326 """
1327 def __repr__(self):
1328 fields = []
1329
1330 if self.name is not None:
1331 fields.append("name: " + self.name)
1332 fields.append("addr: " + hex(self.addr))
1333 fields.append("size: " + hex(self.size))
1334
1335 return "<Register, {}>".format(", ".join(fields))
1336
1337
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001338class ControllerAndData:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001339 """
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001340 Represents an entry in an 'interrupts' or 'type: phandle-array' property
1341 value, e.g. <&ctrl-1 4 0> in
Ulf Magnusson62d57412018-12-17 20:09:47 +01001342
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001343 cs-gpios = <&ctrl-1 4 0 &ctrl-2 3 4>;
1344
1345 These attributes are available on ControllerAndData objects:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001346
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001347 node:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001348 The Node instance the property appears on
Ulf Magnusson62d57412018-12-17 20:09:47 +01001349
1350 controller:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001351 The Node instance for the controller (e.g. the controller the interrupt
1352 gets sent to for interrupts)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001353
Ulf Magnusson6b875042019-09-23 07:40:16 +02001354 data:
Ulf Magnusson567c3482019-09-26 20:34:13 +02001355 A dictionary that maps names from the *-cells key in the binding for the
1356 controller to data values, e.g. {"pin": 4, "flags": 0} for the example
1357 above.
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001358
1359 'interrupts = <1 2>' might give {"irq": 1, "level": 2}.
1360
1361 name:
1362 The name of the entry as given in
1363 'interrupt-names'/'gpio-names'/'pwm-names'/etc., or None if there is no
1364 *-names property
Ulf Magnusson62d57412018-12-17 20:09:47 +01001365 """
1366 def __repr__(self):
1367 fields = []
1368
1369 if self.name is not None:
1370 fields.append("name: " + self.name)
1371
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001372 fields.append("controller: {}".format(self.controller))
Ulf Magnusson6b875042019-09-23 07:40:16 +02001373 fields.append("data: {}".format(self.data))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001374
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001375 return "<ControllerAndData, {}>".format(", ".join(fields))
Jim Paris67f53ba2019-08-07 15:05:23 -04001376
1377
Ulf Magnussoneef8d192019-10-30 16:05:30 +01001378class PinCtrl:
1379 """
1380 Represents a pin control configuration for a set of pins on a device,
1381 e.g. pinctrl-0 or pinctrl-1.
1382
1383 These attributes are available on PinCtrl objects:
1384
1385 node:
1386 The Node instance the pinctrl-* property is on
1387
1388 name:
1389 The name of the configuration, as given in pinctrl-names, or None if
1390 there is no pinctrl-names property
1391
1392 conf_nodes:
1393 A list of Node instances for the pin configuration nodes, e.g.
1394 the nodes pointed at by &state_1 and &state_2 in
1395
1396 pinctrl-0 = <&state_1 &state_2>;
1397 """
1398 def __repr__(self):
1399 fields = []
1400
1401 if self.name is not None:
1402 fields.append("name: " + self.name)
1403
1404 fields.append("configuration nodes: " + str(self.conf_nodes))
1405
1406 return "<PinCtrl, {}>".format(", ".join(fields))
1407
1408
Ulf Magnusson62d57412018-12-17 20:09:47 +01001409class Property:
1410 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001411 Represents a property on a Node, as set in its DT node and with
Ulf Magnusson62d57412018-12-17 20:09:47 +01001412 additional info from the 'properties:' section of the binding.
1413
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001414 Only properties mentioned in 'properties:' get created. Properties of type
1415 'compound' currently do not get Property instances, as I'm not sure what
1416 information to store for them.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001417
1418 These attributes are available on Property objects:
1419
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001420 node:
1421 The Node instance the property is on
Ulf Magnusson62d57412018-12-17 20:09:47 +01001422
1423 name:
1424 The name of the property
1425
1426 description:
1427 The description string from the property as given in the binding, or None
Ulf Magnussonf3f88a82019-10-28 13:02:21 +01001428 if missing. Leading and trailing whitespace (including newlines) is
1429 removed.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001430
Ulf Magnusson165dde02019-08-14 11:13:14 +02001431 type:
1432 A string with the type of the property, as given in the binding.
1433
Ulf Magnusson62d57412018-12-17 20:09:47 +01001434 val:
Ulf Magnusson06b746c2019-08-09 20:38:17 +02001435 The value of the property, with the format determined by the 'type:' key
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001436 from the binding.
1437
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001438 - For 'type: int/array/string/string-array', 'val' is what you'd expect
1439 (a Python integer or string, or a list of them)
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001440
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001441 - For 'type: phandle', 'val' is the pointed-to Node instance
1442
1443 - For 'type: phandles', 'val' is a list of the pointed-to Node
1444 instances
1445
1446 - For 'type: phandle-array', 'val' is a list of ControllerAndData
1447 instances. See the documentation for that class.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001448
1449 enum_index:
1450 The index of the property's value in the 'enum:' list in the binding, or
1451 None if the binding has no 'enum:'
1452 """
1453 def __repr__(self):
1454 fields = ["name: " + self.name,
1455 # repr() to deal with lists
Ulf Magnusson165dde02019-08-14 11:13:14 +02001456 "type: " + self.type,
Ulf Magnusson62d57412018-12-17 20:09:47 +01001457 "value: " + repr(self.val)]
1458
1459 if self.enum_index is not None:
1460 fields.append("enum index: {}".format(self.enum_index))
1461
1462 return "<Property, {}>".format(", ".join(fields))
1463
1464
1465class EDTError(Exception):
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001466 "Exception raised for devicetree- and binding-related errors"
Ulf Magnusson62d57412018-12-17 20:09:47 +01001467
1468
1469#
1470# Public global functions
1471#
1472
1473
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001474def spi_dev_cs_gpio(node):
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001475 # Returns an SPI device's GPIO chip select if it exists, as a
1476 # ControllerAndData instance, and None otherwise. See
Ulf Magnusson62d57412018-12-17 20:09:47 +01001477 # Documentation/devicetree/bindings/spi/spi-bus.txt in the Linux kernel.
1478
Ulf Magnusson5e55eda2019-11-27 04:08:36 +01001479 if not (node.on_bus == "spi" and "cs-gpios" in node.bus_node.props):
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001480 return None
Ulf Magnusson62d57412018-12-17 20:09:47 +01001481
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001482 if not node.regs:
1483 _err("{!r} needs a 'reg' property, to look up the chip select index "
1484 "for SPI".format(node))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001485
Ulf Magnusson5e55eda2019-11-27 04:08:36 +01001486 parent_cs_lst = node.bus_node.props["cs-gpios"].val
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001487
1488 # cs-gpios is indexed by the unit address
1489 cs_index = node.regs[0].addr
1490 if cs_index >= len(parent_cs_lst):
1491 _err("index from 'regs' in {!r} ({}) is >= number of cs-gpios "
1492 "in {!r} ({})".format(
Ulf Magnusson5e55eda2019-11-27 04:08:36 +01001493 node, cs_index, node.bus_node, len(parent_cs_lst)))
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001494
1495 return parent_cs_lst[cs_index]
Ulf Magnusson62d57412018-12-17 20:09:47 +01001496
1497
1498#
1499# Private global functions
1500#
1501
1502
1503def _dt_compats(dt):
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001504 # Returns a set() with all 'compatible' strings in the devicetree
Ulf Magnusson62d57412018-12-17 20:09:47 +01001505 # represented by dt (a dtlib.DT instance)
1506
1507 return {compat
1508 for node in dt.node_iter()
1509 if "compatible" in node.props
1510 for compat in node.props["compatible"].to_strings()}
1511
1512
Michael Scottb8909432019-08-02 11:42:06 -07001513def _binding_paths(bindings_dirs):
Ulf Magnusson62d57412018-12-17 20:09:47 +01001514 # Returns a list with the paths to all bindings (.yaml files) in
Michael Scottb8909432019-08-02 11:42:06 -07001515 # 'bindings_dirs'
Ulf Magnusson62d57412018-12-17 20:09:47 +01001516
Michael Scottb8909432019-08-02 11:42:06 -07001517 binding_paths = []
1518
1519 for bindings_dir in bindings_dirs:
1520 for root, _, filenames in os.walk(bindings_dir):
1521 for filename in filenames:
1522 if filename.endswith(".yaml"):
1523 binding_paths.append(os.path.join(root, filename))
1524
1525 return binding_paths
Ulf Magnusson62d57412018-12-17 20:09:47 +01001526
1527
Ulf Magnusson5e55eda2019-11-27 04:08:36 +01001528def _on_bus_from_binding(binding):
Ulf Magnusson379145f2019-11-26 22:19:55 +01001529 # Returns the bus specified by 'on-bus:' in the binding (or the
1530 # legacy 'parent-bus:' and 'parent: bus:'), or None if missing
Ulf Magnusson62d57412018-12-17 20:09:47 +01001531
Ulf Magnusson1ebe9452019-09-16 16:42:18 +02001532 if not binding:
1533 return None
1534
Ulf Magnusson379145f2019-11-26 22:19:55 +01001535 if "on-bus" in binding:
1536 return binding["on-bus"]
1537
1538 # Legacy key
Ulf Magnusson1ebe9452019-09-16 16:42:18 +02001539 if "parent-bus" in binding:
1540 return binding["parent-bus"]
1541
1542 # Legacy key
1543 if "parent" in binding:
1544 # _check_binding() has checked that the "bus" key exists
1545 return binding["parent"]["bus"]
1546
Ulf Magnusson62d57412018-12-17 20:09:47 +01001547 return None
1548
1549
1550def _binding_inc_error(msg):
1551 # Helper for reporting errors in the !include implementation
1552
1553 raise yaml.constructor.ConstructorError(None, None, "error: " + msg)
1554
1555
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001556def _merge_props(to_dict, from_dict, parent, binding_path, check_required):
Ulf Magnussond834b692019-08-21 16:41:03 +02001557 # Recursively merges 'from_dict' into 'to_dict', to implement 'include:'.
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001558 #
1559 # If 'from_dict' and 'to_dict' contain a 'required:' key for the same
1560 # property, then the values are ORed together.
1561 #
1562 # If 'check_required' is True, then an error is raised if 'from_dict' has
1563 # 'required: true' while 'to_dict' has 'required: false'. This prevents
1564 # bindings from "downgrading" requirements from bindings they include,
1565 # which might help keep bindings well-organized.
1566 #
1567 # It's an error for most other keys to appear in both 'from_dict' and
1568 # 'to_dict'. When it's not an error, the value in 'to_dict' takes
1569 # precedence.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001570 #
1571 # 'parent' is the name of the parent key containing 'to_dict' and
1572 # 'from_dict', and 'binding_path' is the path to the top-level binding.
1573 # These are used to generate errors for sketchy property overwrites.
1574
1575 for prop in from_dict:
1576 if isinstance(to_dict.get(prop), dict) and \
1577 isinstance(from_dict[prop], dict):
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001578 _merge_props(to_dict[prop], from_dict[prop], prop, binding_path,
1579 check_required)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001580 elif prop not in to_dict:
1581 to_dict[prop] = from_dict[prop]
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001582 elif _bad_overwrite(to_dict, from_dict, prop, check_required):
Ulf Magnussond834b692019-08-21 16:41:03 +02001583 _err("{} (in '{}'): '{}' from included file overwritten "
Ulf Magnusson62d57412018-12-17 20:09:47 +01001584 "('{}' replaced with '{}')".format(
1585 binding_path, parent, prop, from_dict[prop],
1586 to_dict[prop]))
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001587 elif prop == "required":
1588 # Need a separate check here, because this code runs before
1589 # _check_binding()
1590 if not (isinstance(from_dict["required"], bool) and
1591 isinstance(to_dict["required"], bool)):
1592 _err("malformed 'required:' setting for '{}' in 'properties' "
1593 "in {}, expected true/false".format(parent, binding_path))
1594
1595 # 'required: true' takes precedence
1596 to_dict["required"] = to_dict["required"] or from_dict["required"]
1597 elif prop == "category":
1598 # Legacy property key. 'category: required' takes precedence.
1599 if "required" in (to_dict["category"], from_dict["category"]):
1600 to_dict["category"] = "required"
Ulf Magnusson62d57412018-12-17 20:09:47 +01001601
1602
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001603def _bad_overwrite(to_dict, from_dict, prop, check_required):
Ulf Magnusson62d57412018-12-17 20:09:47 +01001604 # _merge_props() helper. Returns True in cases where it's bad that
1605 # to_dict[prop] takes precedence over from_dict[prop].
1606
Ulf Magnusson62d57412018-12-17 20:09:47 +01001607 if to_dict[prop] == from_dict[prop]:
1608 return False
1609
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001610 # These are overridden deliberately
1611 if prop in {"title", "description", "compatible"}:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001612 return False
1613
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001614 if prop == "required":
1615 if not check_required:
1616 return False
1617 return from_dict[prop] and not to_dict[prop]
1618
1619 # Legacy property key
1620 if prop == "category":
1621 if not check_required:
1622 return False
1623 return from_dict[prop] == "required" and to_dict[prop] == "optional"
1624
Ulf Magnusson62d57412018-12-17 20:09:47 +01001625 return True
1626
1627
Ulf Magnusson8f225292019-09-05 22:31:09 +02001628def _binding_include(loader, node):
1629 # Implements !include, for backwards compatibility. '!include [foo, bar]'
1630 # just becomes [foo, bar].
1631
1632 if isinstance(node, yaml.ScalarNode):
1633 # !include foo.yaml
1634 return [loader.construct_scalar(node)]
1635
1636 if isinstance(node, yaml.SequenceNode):
1637 # !include [foo.yaml, bar.yaml]
1638 return loader.construct_sequence(node)
1639
1640 _binding_inc_error("unrecognised node type in !include statement")
1641
1642
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001643def _check_prop_type_and_default(prop_name, prop_type, required, default,
1644 binding_path):
1645 # _check_binding() helper. Checks 'type:' and 'default:' for the property
1646 # named 'prop_name'
1647
1648 if prop_type is None:
1649 _err("missing 'type:' for '{}' in 'properties' in {}"
1650 .format(prop_name, binding_path))
1651
1652 ok_types = {"boolean", "int", "array", "uint8-array", "string",
1653 "string-array", "phandle", "phandles", "phandle-array",
1654 "compound"}
1655
1656 if prop_type not in ok_types:
1657 _err("'{}' in 'properties:' in {} has unknown type '{}', expected one "
1658 "of {}".format(prop_name, binding_path, prop_type,
1659 ", ".join(ok_types)))
1660
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001661 if prop_type == "phandle-array" and not prop_name.endswith("s"):
1662 _err("'{}' in 'properties:' in {} is 'type: phandle-array', but its "
1663 "name does not end in -s. This is required since property names "
1664 "like '#pwm-cells' and 'pwm-names' get derived from 'pwms', for "
1665 "example.".format(prop_name, binding_path))
1666
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001667 # Check default
1668
1669 if default is None:
1670 return
1671
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001672 if prop_type in {"boolean", "compound", "phandle", "phandles",
1673 "phandle-array"}:
1674 _err("'default:' can't be combined with 'type: {}' for '{}' in "
1675 "'properties:' in {}".format(prop_type, prop_name, binding_path))
1676
1677 def ok_default():
1678 # Returns True if 'default' is an okay default for the property's type
1679
1680 if prop_type == "int" and isinstance(default, int) or \
1681 prop_type == "string" and isinstance(default, str):
1682 return True
1683
1684 # array, uint8-array, or string-array
1685
1686 if not isinstance(default, list):
1687 return False
1688
1689 if prop_type == "array" and \
1690 all(isinstance(val, int) for val in default):
1691 return True
1692
1693 if prop_type == "uint8-array" and \
1694 all(isinstance(val, int) and 0 <= val <= 255 for val in default):
1695 return True
1696
1697 # string-array
1698 return all(isinstance(val, str) for val in default)
1699
1700 if not ok_default():
1701 _err("'default: {}' is invalid for '{}' in 'properties:' in {}, which "
1702 "has type {}".format(default, prop_name, binding_path, prop_type))
1703
1704
Ulf Magnusson62d57412018-12-17 20:09:47 +01001705def _translate(addr, node):
1706 # Recursively translates 'addr' on 'node' to the address space(s) of its
1707 # parent(s), by looking at 'ranges' properties. Returns the translated
1708 # address.
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001709 #
1710 # node:
1711 # dtlib.Node instance
Ulf Magnusson62d57412018-12-17 20:09:47 +01001712
1713 if not node.parent or "ranges" not in node.parent.props:
1714 # No translation
1715 return addr
1716
1717 if not node.parent.props["ranges"].value:
1718 # DT spec.: "If the property is defined with an <empty> value, it
1719 # specifies that the parent and child address space is identical, and
1720 # no address translation is required."
1721 #
1722 # Treat this the same as a 'range' that explicitly does a one-to-one
1723 # mapping, as opposed to there not being any translation.
1724 return _translate(addr, node.parent)
1725
1726 # Gives the size of each component in a translation 3-tuple in 'ranges'
1727 child_address_cells = _address_cells(node)
1728 parent_address_cells = _address_cells(node.parent)
1729 child_size_cells = _size_cells(node)
1730
1731 # Number of cells for one translation 3-tuple in 'ranges'
1732 entry_cells = child_address_cells + parent_address_cells + child_size_cells
1733
1734 for raw_range in _slice(node.parent, "ranges", 4*entry_cells):
1735 child_addr = to_num(raw_range[:4*child_address_cells])
1736 raw_range = raw_range[4*child_address_cells:]
1737
1738 parent_addr = to_num(raw_range[:4*parent_address_cells])
1739 raw_range = raw_range[4*parent_address_cells:]
1740
1741 child_len = to_num(raw_range)
1742
1743 if child_addr <= addr < child_addr + child_len:
1744 # 'addr' is within range of a translation in 'ranges'. Recursively
1745 # translate it and return the result.
1746 return _translate(parent_addr + addr - child_addr, node.parent)
1747
1748 # 'addr' is not within range of any translation in 'ranges'
1749 return addr
1750
1751
1752def _add_names(node, names_ident, objs):
1753 # Helper for registering names from <foo>-names properties.
1754 #
1755 # node:
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001756 # edtlib.Node instance
Ulf Magnusson62d57412018-12-17 20:09:47 +01001757 #
1758 # names-ident:
Ulf Magnusson95deec12019-08-06 21:51:06 +02001759 # The <foo> part of <foo>-names, e.g. "reg" for "reg-names"
Ulf Magnusson62d57412018-12-17 20:09:47 +01001760 #
1761 # objs:
1762 # list of objects whose .name field should be set
1763
Ulf Magnusson95deec12019-08-06 21:51:06 +02001764 full_names_ident = names_ident + "-names"
1765
1766 if full_names_ident in node.props:
1767 names = node.props[full_names_ident].to_strings()
Ulf Magnusson62d57412018-12-17 20:09:47 +01001768 if len(names) != len(objs):
Ulf Magnussoneef8d192019-10-30 16:05:30 +01001769 _err("{} property in {} in {} has {} strings, expected {} strings"
1770 .format(full_names_ident, node.path, node.dt.filename,
1771 len(names), len(objs)))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001772
1773 for obj, name in zip(objs, names):
1774 obj.name = name
1775 else:
1776 for obj in objs:
1777 obj.name = None
1778
1779
1780def _interrupt_parent(node):
1781 # Returns the node pointed at by the closest 'interrupt-parent', searching
1782 # the parents of 'node'. As of writing, this behavior isn't specified in
1783 # the DT spec., but seems to match what some .dts files except.
1784
1785 while node:
1786 if "interrupt-parent" in node.props:
1787 return node.props["interrupt-parent"].to_node()
1788 node = node.parent
1789
1790 _err("{!r} has an 'interrupts' property, but neither the node nor any "
1791 "of its parents has an 'interrupt-parent' property".format(node))
1792
1793
1794def _interrupts(node):
Ulf Magnusson6b875042019-09-23 07:40:16 +02001795 # Returns a list of (<controller>, <data>) tuples, with one tuple per
Ulf Magnusson62d57412018-12-17 20:09:47 +01001796 # interrupt generated by 'node'. <controller> is the destination of the
Ulf Magnusson6b875042019-09-23 07:40:16 +02001797 # interrupt (possibly after mapping through an 'interrupt-map'), and <data>
1798 # the data associated with the interrupt (as a 'bytes' object).
Ulf Magnusson62d57412018-12-17 20:09:47 +01001799
1800 # Takes precedence over 'interrupts' if both are present
1801 if "interrupts-extended" in node.props:
1802 prop = node.props["interrupts-extended"]
1803 return [_map_interrupt(node, iparent, spec)
Ulf Magnusson95deec12019-08-06 21:51:06 +02001804 for iparent, spec in _phandle_val_list(prop, "interrupt")]
Ulf Magnusson62d57412018-12-17 20:09:47 +01001805
1806 if "interrupts" in node.props:
1807 # Treat 'interrupts' as a special case of 'interrupts-extended', with
1808 # the same interrupt parent for all interrupts
1809
1810 iparent = _interrupt_parent(node)
1811 interrupt_cells = _interrupt_cells(iparent)
1812
1813 return [_map_interrupt(node, iparent, raw)
1814 for raw in _slice(node, "interrupts", 4*interrupt_cells)]
1815
1816 return []
1817
1818
1819def _map_interrupt(child, parent, child_spec):
Ulf Magnusson6b875042019-09-23 07:40:16 +02001820 # Translates an interrupt headed from 'child' to 'parent' with data
Ulf Magnusson62d57412018-12-17 20:09:47 +01001821 # 'child_spec' through any 'interrupt-map' properties. Returns a
Ulf Magnusson6b875042019-09-23 07:40:16 +02001822 # (<controller>, <data>) tuple with the final destination after mapping.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001823
1824 if "interrupt-controller" in parent.props:
1825 return (parent, child_spec)
1826
1827 def own_address_cells(node):
1828 # Used for parents pointed at by 'interrupt-map'. We can't use
1829 # _address_cells(), because it's the #address-cells property on 'node'
1830 # itself that matters.
1831
1832 address_cells = node.props.get("#address-cells")
1833 if not address_cells:
1834 _err("missing #address-cells on {!r} (while handling interrupt-map)"
1835 .format(node))
1836 return address_cells.to_num()
1837
1838 def spec_len_fn(node):
1839 # Can't use _address_cells() here, because it's the #address-cells
1840 # property on 'node' itself that matters
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001841 return own_address_cells(node) + _interrupt_cells(node)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001842
1843 parent, raw_spec = _map(
1844 "interrupt", child, parent, _raw_unit_addr(child) + child_spec,
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001845 spec_len_fn, require_controller=True)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001846
1847 # Strip the parent unit address part, if any
1848 return (parent, raw_spec[4*own_address_cells(parent):])
1849
1850
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001851def _map_phandle_array_entry(child, parent, child_spec, basename):
Ulf Magnusson6b875042019-09-23 07:40:16 +02001852 # Returns a (<controller>, <data>) tuple with the final destination after
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001853 # mapping through any '<basename>-map' (e.g. gpio-map) properties. See
1854 # _map_interrupt().
Ulf Magnusson62d57412018-12-17 20:09:47 +01001855
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001856 def spec_len_fn(node):
1857 prop_name = "#{}-cells".format(basename)
1858 if prop_name not in node.props:
1859 _err("expected '{}' property on {!r} (referenced by {!r})"
1860 .format(prop_name, node, child))
1861 return node.props[prop_name].to_num()
Ulf Magnusson62d57412018-12-17 20:09:47 +01001862
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001863 # Do not require <prefix>-controller for anything but interrupts for now
1864 return _map(basename, child, parent, child_spec, spec_len_fn,
1865 require_controller=False)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001866
1867
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001868def _map(prefix, child, parent, child_spec, spec_len_fn, require_controller):
Ulf Magnusson62d57412018-12-17 20:09:47 +01001869 # Common code for mapping through <prefix>-map properties, e.g.
1870 # interrupt-map and gpio-map.
1871 #
1872 # prefix:
1873 # The prefix, e.g. "interrupt" or "gpio"
1874 #
1875 # child:
1876 # The "sender", e.g. the node with 'interrupts = <...>'
1877 #
1878 # parent:
1879 # The "receiver", e.g. a node with 'interrupt-map = <...>' or
1880 # 'interrupt-controller' (no mapping)
1881 #
1882 # child_spec:
1883 # The data associated with the interrupt/GPIO/etc., as a 'bytes' object,
1884 # e.g. <1 2> for 'foo-gpios = <&gpio1 1 2>'.
1885 #
1886 # spec_len_fn:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001887 # Function called on a parent specified in a *-map property to get the
1888 # length of the parent specifier (data after phandle in *-map), in cells
1889 #
1890 # require_controller:
1891 # If True, the final controller node after mapping is required to have
1892 # to have a <prefix>-controller property.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001893
1894 map_prop = parent.props.get(prefix + "-map")
1895 if not map_prop:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001896 if require_controller and prefix + "-controller" not in parent.props:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001897 _err("expected '{}-controller' property on {!r} "
1898 "(referenced by {!r})".format(prefix, parent, child))
1899
1900 # No mapping
1901 return (parent, child_spec)
1902
1903 masked_child_spec = _mask(prefix, child, parent, child_spec)
1904
1905 raw = map_prop.value
1906 while raw:
1907 if len(raw) < len(child_spec):
Ulf Magnusson6b875042019-09-23 07:40:16 +02001908 _err("bad value for {!r}, missing/truncated child data"
Ulf Magnusson62d57412018-12-17 20:09:47 +01001909 .format(map_prop))
1910 child_spec_entry = raw[:len(child_spec)]
1911 raw = raw[len(child_spec):]
1912
1913 if len(raw) < 4:
1914 _err("bad value for {!r}, missing/truncated phandle"
1915 .format(map_prop))
1916 phandle = to_num(raw[:4])
1917 raw = raw[4:]
1918
1919 # Parent specified in *-map
1920 map_parent = parent.dt.phandle2node.get(phandle)
1921 if not map_parent:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001922 _err("bad phandle ({}) in {!r}".format(phandle, map_prop))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001923
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001924 map_parent_spec_len = 4*spec_len_fn(map_parent)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001925 if len(raw) < map_parent_spec_len:
Ulf Magnusson6b875042019-09-23 07:40:16 +02001926 _err("bad value for {!r}, missing/truncated parent data"
Ulf Magnusson62d57412018-12-17 20:09:47 +01001927 .format(map_prop))
1928 parent_spec = raw[:map_parent_spec_len]
1929 raw = raw[map_parent_spec_len:]
1930
Ulf Magnusson6b875042019-09-23 07:40:16 +02001931 # Got one *-map row. Check if it matches the child data.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001932 if child_spec_entry == masked_child_spec:
1933 # Handle *-map-pass-thru
1934 parent_spec = _pass_thru(
1935 prefix, child, parent, child_spec, parent_spec)
1936
1937 # Found match. Recursively map and return it.
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001938 return _map(prefix, parent, map_parent, parent_spec, spec_len_fn,
1939 require_controller)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001940
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001941 _err("child specifier for {!r} ({}) does not appear in {!r}"
Ulf Magnusson62d57412018-12-17 20:09:47 +01001942 .format(child, child_spec, map_prop))
1943
1944
1945def _mask(prefix, child, parent, child_spec):
1946 # Common code for handling <prefix>-mask properties, e.g. interrupt-mask.
1947 # See _map() for the parameters.
1948
1949 mask_prop = parent.props.get(prefix + "-map-mask")
1950 if not mask_prop:
1951 # No mask
1952 return child_spec
1953
1954 mask = mask_prop.value
1955 if len(mask) != len(child_spec):
1956 _err("{!r}: expected '{}-mask' in {!r} to be {} bytes, is {} bytes"
1957 .format(child, prefix, parent, len(child_spec), len(mask)))
1958
1959 return _and(child_spec, mask)
1960
1961
1962def _pass_thru(prefix, child, parent, child_spec, parent_spec):
1963 # Common code for handling <prefix>-map-thru properties, e.g.
1964 # interrupt-pass-thru.
1965 #
1966 # parent_spec:
Ulf Magnusson6b875042019-09-23 07:40:16 +02001967 # The parent data from the matched entry in the <prefix>-map property
Ulf Magnusson62d57412018-12-17 20:09:47 +01001968 #
1969 # See _map() for the other parameters.
1970
1971 pass_thru_prop = parent.props.get(prefix + "-map-pass-thru")
1972 if not pass_thru_prop:
1973 # No pass-thru
1974 return parent_spec
1975
1976 pass_thru = pass_thru_prop.value
1977 if len(pass_thru) != len(child_spec):
1978 _err("{!r}: expected '{}-map-pass-thru' in {!r} to be {} bytes, is {} bytes"
1979 .format(child, prefix, parent, len(child_spec), len(pass_thru)))
1980
1981 res = _or(_and(child_spec, pass_thru),
1982 _and(parent_spec, _not(pass_thru)))
1983
1984 # Truncate to length of parent spec.
1985 return res[-len(parent_spec):]
1986
1987
1988def _raw_unit_addr(node):
1989 # _map_interrupt() helper. Returns the unit address (derived from 'reg' and
1990 # #address-cells) as a raw 'bytes'
1991
1992 if 'reg' not in node.props:
1993 _err("{!r} lacks 'reg' property (needed for 'interrupt-map' unit "
1994 "address lookup)".format(node))
1995
1996 addr_len = 4*_address_cells(node)
1997
1998 if len(node.props['reg'].value) < addr_len:
1999 _err("{!r} has too short 'reg' property (while doing 'interrupt-map' "
2000 "unit address lookup)".format(node))
2001
2002 return node.props['reg'].value[:addr_len]
2003
2004
2005def _and(b1, b2):
2006 # Returns the bitwise AND of the two 'bytes' objects b1 and b2. Pads
2007 # with ones on the left if the lengths are not equal.
2008
2009 # Pad on the left, to equal length
2010 maxlen = max(len(b1), len(b2))
2011 return bytes(x & y for x, y in zip(b1.rjust(maxlen, b'\xff'),
2012 b2.rjust(maxlen, b'\xff')))
2013
2014
2015def _or(b1, b2):
2016 # Returns the bitwise OR of the two 'bytes' objects b1 and b2. Pads with
2017 # zeros on the left if the lengths are not equal.
2018
2019 # Pad on the left, to equal length
2020 maxlen = max(len(b1), len(b2))
2021 return bytes(x | y for x, y in zip(b1.rjust(maxlen, b'\x00'),
2022 b2.rjust(maxlen, b'\x00')))
2023
2024
2025def _not(b):
2026 # Returns the bitwise not of the 'bytes' object 'b'
2027
2028 # ANDing with 0xFF avoids negative numbers
2029 return bytes(~x & 0xFF for x in b)
2030
2031
Ulf Magnusson95deec12019-08-06 21:51:06 +02002032def _phandle_val_list(prop, n_cells_name):
2033 # Parses a '<phandle> <value> <phandle> <value> ...' value. The number of
2034 # cells that make up each <value> is derived from the node pointed at by
2035 # the preceding <phandle>.
Ulf Magnusson62d57412018-12-17 20:09:47 +01002036 #
2037 # prop:
2038 # dtlib.Property with value to parse
2039 #
Ulf Magnusson95deec12019-08-06 21:51:06 +02002040 # n_cells_name:
2041 # The <name> part of the #<name>-cells property to look for on the nodes
2042 # the phandles point to, e.g. "gpio" for #gpio-cells.
Ulf Magnusson62d57412018-12-17 20:09:47 +01002043 #
2044 # Returns a list of (<node>, <value>) tuples, where <node> is the node
Ulf Magnusson95deec12019-08-06 21:51:06 +02002045 # pointed at by <phandle>.
2046
2047 full_n_cells_name = "#{}-cells".format(n_cells_name)
Ulf Magnusson62d57412018-12-17 20:09:47 +01002048
2049 res = []
2050
2051 raw = prop.value
2052 while raw:
2053 if len(raw) < 4:
2054 # Not enough room for phandle
2055 _err("bad value for " + repr(prop))
2056 phandle = to_num(raw[:4])
2057 raw = raw[4:]
2058
2059 node = prop.node.dt.phandle2node.get(phandle)
2060 if not node:
2061 _err("bad phandle in " + repr(prop))
2062
Ulf Magnusson95deec12019-08-06 21:51:06 +02002063 if full_n_cells_name not in node.props:
2064 _err("{!r} lacks {}".format(node, full_n_cells_name))
2065
2066 n_cells = node.props[full_n_cells_name].to_num()
Ulf Magnusson62d57412018-12-17 20:09:47 +01002067 if len(raw) < 4*n_cells:
2068 _err("missing data after phandle in " + repr(prop))
2069
2070 res.append((node, raw[:4*n_cells]))
2071 raw = raw[4*n_cells:]
2072
2073 return res
2074
2075
2076def _address_cells(node):
2077 # Returns the #address-cells setting for 'node', giving the number of <u32>
2078 # cells used to encode the address in the 'reg' property
2079
2080 if "#address-cells" in node.parent.props:
2081 return node.parent.props["#address-cells"].to_num()
2082 return 2 # Default value per DT spec.
2083
2084
2085def _size_cells(node):
2086 # Returns the #size-cells setting for 'node', giving the number of <u32>
2087 # cells used to encode the size in the 'reg' property
2088
2089 if "#size-cells" in node.parent.props:
2090 return node.parent.props["#size-cells"].to_num()
2091 return 1 # Default value per DT spec.
2092
2093
2094def _interrupt_cells(node):
2095 # Returns the #interrupt-cells property value on 'node', erroring out if
2096 # 'node' has no #interrupt-cells property
2097
2098 if "#interrupt-cells" not in node.props:
2099 _err("{!r} lacks #interrupt-cells".format(node))
2100 return node.props["#interrupt-cells"].to_num()
2101
2102
Ulf Magnusson62d57412018-12-17 20:09:47 +01002103def _slice(node, prop_name, size):
2104 # Splits node.props[prop_name].value into 'size'-sized chunks, returning a
2105 # list of chunks. Raises EDTError if the length of the property is not
2106 # evenly divisible by 'size'.
2107
2108 raw = node.props[prop_name].value
2109 if len(raw) % size:
2110 _err("'{}' property in {!r} has length {}, which is not evenly "
2111 "divisible by {}".format(prop_name, node, len(raw), size))
2112
2113 return [raw[i:i + size] for i in range(0, len(raw), size)]
2114
2115
Ulf Magnussonacf276f2019-08-07 19:33:45 +02002116def _check_dt(dt):
2117 # Does devicetree sanity checks. dtlib is meant to be general and
2118 # anything-goes except for very special properties like phandle, but in
2119 # edtlib we can be pickier.
2120
2121 # Check that 'status' has one of the values given in the devicetree spec.
2122
Ulf Magnussonfc5cd772019-09-26 13:47:58 +02002123 # Accept "ok" for backwards compatibility
2124 ok_status = {"ok", "okay", "disabled", "reserved", "fail", "fail-sss"}
Ulf Magnussonacf276f2019-08-07 19:33:45 +02002125
2126 for node in dt.node_iter():
2127 if "status" in node.props:
2128 try:
2129 status_val = node.props["status"].to_string()
2130 except DTError as e:
2131 # The error message gives the path
2132 _err(str(e))
2133
2134 if status_val not in ok_status:
2135 _err("unknown 'status' value \"{}\" in {} in {}, expected one "
2136 "of {} (see the devicetree specification)"
2137 .format(status_val, node.path, node.dt.filename,
2138 ", ".join(ok_status)))
2139
Ulf Magnusson378254a2019-08-14 18:44:35 +02002140 ranges_prop = node.props.get("ranges")
2141 if ranges_prop:
2142 if ranges_prop.type not in (TYPE_EMPTY, TYPE_NUMS):
2143 _err("expected 'ranges = < ... >;' in {} in {}, not '{}' "
2144 "(see the devicetree specification)"
2145 .format(node.path, node.dt.filename, ranges_prop))
2146
Ulf Magnussonacf276f2019-08-07 19:33:45 +02002147
Ulf Magnusson62d57412018-12-17 20:09:47 +01002148def _err(msg):
2149 raise EDTError(msg)
Ulf Magnusson0e239942019-11-12 18:21:02 +01002150
2151
2152# Custom PyYAML binding loader class to avoid modifying yaml.Loader directly,
2153# which could interfere with YAML loading in clients
2154class _BindingLoader(Loader):
2155 pass
2156
2157
2158# Add legacy '!include foo.yaml' handling
2159_BindingLoader.add_constructor("!include", _binding_include)
Ulf Magnusson72158852019-11-12 18:33:00 +01002160
2161# Use OrderedDict instead of plain dict for YAML mappings, to preserve
2162# insertion order on Python 3.5 and earlier (plain dicts only preserve
2163# insertion order on Python 3.6+). This makes testing easier and avoids
2164# surprises.
2165#
2166# Adapted from
2167# https://stackoverflow.com/questions/5121931/in-python-how-can-you-load-yaml-mappings-as-ordereddicts.
2168# Hopefully this API stays stable.
2169_BindingLoader.add_constructor(
2170 yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
2171 lambda loader, node: OrderedDict(loader.construct_pairs(node)))