blob: eedde854b360f33fed3ef89fe11bad70ae505c73 [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 Magnussone3113802019-10-14 17:45:45 +0200278 bus = _binding_bus(binding)
279
280 # Do not allow two different bindings to have the same
Ulf Magnusson379145f2019-11-26 22:19:55 +0100281 # 'compatible:'/'on-bus:' combo
Ulf Magnussone3113802019-10-14 17:45:45 +0200282 old_binding = self._compat2binding.get((binding_compat, bus))
283 if old_binding:
284 msg = "both {} and {} have 'compatible: {}'".format(
285 old_binding[1], binding_path, binding_compat)
286 if bus is not None:
Ulf Magnusson379145f2019-11-26 22:19:55 +0100287 msg += " and 'on-bus: {}'".format(bus)
Ulf Magnussone3113802019-10-14 17:45:45 +0200288 _err(msg)
289
290 self._compat2binding[binding_compat, 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
443 node._init_binding()
444 node._init_regs()
445 node._set_instance_no()
Ulf Magnusson06b746c2019-08-09 20:38:17 +0200446
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200447 self.nodes.append(node)
448 self._node2enode[dt_node] = node
Ulf Magnusson62d57412018-12-17 20:09:47 +0100449
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200450 for node in self.nodes:
Ulf Magnussoneef8d192019-10-30 16:05:30 +0100451 # These depend on all Node objects having been created, because
452 # they (either always or sometimes) reference other nodes, so we
453 # run them separately
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200454 node._init_props()
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200455 node._init_interrupts()
Ulf Magnussoneef8d192019-10-30 16:05:30 +0100456 node._init_pinctrls()
Ulf Magnusson62d57412018-12-17 20:09:47 +0100457
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200458 def _check_binding(self, binding, binding_path):
459 # Does sanity checking on 'binding'. Only takes 'self' for the sake of
460 # self._warn().
461
Ulf Magnusson36b7ca42019-11-19 10:38:25 +0100462 if "title" in binding:
463 # This message is the message that people copy-pasting the old
464 # format will see in practice
465 self._warn("'title:' in {} is deprecated and will be removed (and "
466 "was never used). Just put a 'description:' that "
467 "describes the device instead. Use other bindings as "
468 "a reference, and note that all bindings were updated "
469 "recently. Think about what information would be "
470 "useful to other people (e.g. explanations of "
471 "acronyms, or datasheet links), and put that in as "
472 "well. The description text shows up as a comment "
473 "in the generated header. See yaml-multiline.info for "
474 "how to deal with multiple lines. You probably want "
475 "'description: |'.".format(binding_path))
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200476
Ulf Magnusson36b7ca42019-11-19 10:38:25 +0100477 if "description" not in binding:
478 _err("missing 'description' property in " + binding_path)
479
480 for prop in "title", "description":
481 if prop in binding and (not isinstance(binding[prop], str) or
482 not binding[prop]):
483 _err("malformed or empty '{}' in {}"
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200484 .format(prop, binding_path))
485
486 ok_top = {"title", "description", "compatible", "properties", "#cells",
Ulf Magnusson379145f2019-11-26 22:19:55 +0100487 "bus", "on-bus", "parent-bus", "child-bus", "parent", "child",
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200488 "child-binding", "sub-node"}
489
490 for prop in binding:
491 if prop not in ok_top and not prop.endswith("-cells"):
492 _err("unknown key '{}' in {}, expected one of {}, or *-cells"
493 .format(prop, binding_path, ", ".join(ok_top)))
494
Ulf Magnusson379145f2019-11-26 22:19:55 +0100495 for bus_key in "bus", "on-bus":
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200496 if bus_key in binding and \
497 not isinstance(binding[bus_key], str):
Ulf Magnusson36b7ca42019-11-19 10:38:25 +0100498 _err("malformed '{}:' value in {}, expected string"
499 .format(bus_key, binding_path))
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200500
Ulf Magnusson379145f2019-11-26 22:19:55 +0100501 # There are two legacy syntaxes for 'bus:' and 'on-bus:':
502 #
503 # child/parent-bus: foo
504 # child/parent: bus: foo
505 #
506 # We support both, with deprecation warnings.
507 for pc in "parent", "child":
508 # Legacy 'parent/child-bus:' keys
509 bus_key = pc + "-bus"
510 if bus_key in binding:
511 self._warn("'{}:' in {} is deprecated and will be removed - "
512 "please use a top-level '{}:' key instead (see "
513 "binding-template.yaml)"
514 .format(bus_key, binding_path,
515 "bus" if bus_key == "child-bus" else "on-bus"))
516
517 if not isinstance(binding[bus_key], str):
518 _err("malformed '{}:' value in {}, expected string"
519 .format(bus_key, binding_path))
520
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200521 # Legacy 'child/parent: bus: ...' keys
522 if pc in binding:
Ulf Magnusson379145f2019-11-26 22:19:55 +0100523 self._warn("'{}: bus: ...' in {} is deprecated and will be "
524 "removed - please use a top-level '{}' key instead "
525 "(see binding-template.yaml)"
526 .format(pc, binding_path,
527 "bus" if pc == "child" else "on-bus:"))
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200528
529 # Just 'bus:' is expected
530 if binding[pc].keys() != {"bus"}:
531 _err("expected (just) 'bus:' in '{}:' in {}"
532 .format(pc, binding_path))
533
534 if not isinstance(binding[pc]["bus"], str):
535 _err("malformed '{}: bus:' value in {}, expected string"
536 .format(pc, binding_path))
537
538 self._check_binding_properties(binding, binding_path)
539
540 if "child-binding" in binding:
541 if not isinstance(binding["child-binding"], dict):
542 _err("malformed 'child-binding:' in {}, expected a binding "
543 "(dictionary with keys/values)".format(binding_path))
544
545 self._check_binding(binding["child-binding"], binding_path)
546
547 if "sub-node" in binding:
548 self._warn("'sub-node: properties: ...' in {} is deprecated and "
549 "will be removed - please give a full binding for the "
550 "child node in 'child-binding:' instead (see "
551 "binding-template.yaml)".format(binding_path))
552
553 if binding["sub-node"].keys() != {"properties"}:
554 _err("expected (just) 'properties:' in 'sub-node:' in {}"
555 .format(binding_path))
556
557 self._check_binding_properties(binding["sub-node"], binding_path)
558
559 if "#cells" in binding:
560 self._warn('"#cells:" in {} is deprecated and will be removed - '
561 "please put 'interrupt-cells:', 'pwm-cells:', "
562 "'gpio-cells:', etc., instead. The name should match "
563 "the name of the corresponding phandle-array property "
564 "(see binding-template.yaml)".format(binding_path))
565
566 def ok_cells_val(val):
567 # Returns True if 'val' is an okay value for '*-cells:' (or the
568 # legacy '#cells:')
569
570 return isinstance(val, list) and \
571 all(isinstance(elm, str) for elm in val)
572
573 for key, val in binding.items():
574 if key.endswith("-cells") or key == "#cells":
575 if not ok_cells_val(val):
576 _err("malformed '{}:' in {}, expected a list of strings"
577 .format(key, binding_path))
578
579 def _check_binding_properties(self, binding, binding_path):
580 # _check_binding() helper for checking the contents of 'properties:'.
581 # Only takes 'self' for the sake of self._warn().
582
583 if "properties" not in binding:
584 return
585
586 ok_prop_keys = {"description", "type", "required", "category",
587 "constraint", "enum", "const", "default"}
588
589 for prop_name, options in binding["properties"].items():
590 for key in options:
591 if key == "category":
592 self._warn(
593 "please put 'required: {}' instead of 'category: {}' "
594 "in properties: {}: ...' in {} - 'category' will be "
595 "removed".format(
596 "true" if options["category"] == "required"
597 else "false",
598 options["category"], prop_name, binding_path))
599
600 if key not in ok_prop_keys:
601 _err("unknown setting '{}' in 'properties: {}: ...' in {}, "
602 "expected one of {}".format(
603 key, prop_name, binding_path,
604 ", ".join(ok_prop_keys)))
605
606 _check_prop_type_and_default(
607 prop_name, options.get("type"),
608 options.get("required") or options.get("category") == "required",
609 options.get("default"), binding_path)
610
611 if "required" in options and not isinstance(options["required"], bool):
612 _err("malformed 'required:' setting '{}' for '{}' in 'properties' "
613 "in {}, expected true/false"
614 .format(options["required"], prop_name, binding_path))
615
616 if "description" in options and \
617 not isinstance(options["description"], str):
618 _err("missing, malformed, or empty 'description' for '{}' in "
619 "'properties' in {}".format(prop_name, binding_path))
620
621 if "enum" in options and not isinstance(options["enum"], list):
622 _err("enum in {} for property '{}' is not a list"
623 .format(binding_path, prop_name))
624
625 if "const" in options and not isinstance(options["const"], (int, str)):
626 _err("const in {} for property '{}' is not a scalar"
627 .format(binding_path, prop_name))
628
629 def _warn(self, msg):
630 print("warning: " + msg, file=self._warn_file)
Ulf Magnusson62d57412018-12-17 20:09:47 +0100631
632
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200633class Node:
Ulf Magnusson62d57412018-12-17 20:09:47 +0100634 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200635 Represents a devicetree node, augmented with information from bindings, and
636 with some interpretation of devicetree properties. There's a one-to-one
637 correspondence between devicetree nodes and Nodes.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100638
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200639 These attributes are available on Node objects:
Ulf Magnusson62d57412018-12-17 20:09:47 +0100640
641 edt:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200642 The EDT instance this node is from
Ulf Magnusson62d57412018-12-17 20:09:47 +0100643
644 name:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200645 The name of the node
Ulf Magnusson62d57412018-12-17 20:09:47 +0100646
647 unit_addr:
648 An integer with the ...@<unit-address> portion of the node name,
649 translated through any 'ranges' properties on parent nodes, or None if
650 the node name has no unit-address portion
651
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200652 description:
653 The description string from the binding for the node, or None if the node
Ulf Magnussonf3f88a82019-10-28 13:02:21 +0100654 has no binding. Leading and trailing whitespace (including newlines) is
655 removed.
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200656
Ulf Magnusson62d57412018-12-17 20:09:47 +0100657 path:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200658 The devicetree path of the node
Ulf Magnusson62d57412018-12-17 20:09:47 +0100659
660 label:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200661 The text from the 'label' property on the node, or None if the node has
662 no 'label'
Ulf Magnusson62d57412018-12-17 20:09:47 +0100663
664 parent:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200665 The Node instance for the devicetree parent of the Node, or None if the
666 node is the root node
Ulf Magnusson110526e2019-09-21 04:14:33 +0200667
668 children:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200669 A dictionary with the Node instances for the devicetree children of the
670 node, indexed by name
Ulf Magnusson62d57412018-12-17 20:09:47 +0100671
Peter A. Bigotea956f42019-09-27 11:31:36 -0500672 dep_ordinal:
673 A non-negative integer value such that the value for a Node is
674 less than the value for all Nodes that depend on it.
675
676 The ordinal is defined for all Nodes including those that are not
677 'enabled', and is unique among nodes in its EDT 'nodes' list.
678
Ulf Magnusson2e1d2882019-11-06 22:36:50 +0100679 required_by:
680 A list with the nodes that directly depend on the node
681
682 depends_on:
683 A list with the nodes that the node directly depends on
684
Ulf Magnusson62d57412018-12-17 20:09:47 +0100685 enabled:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200686 True unless the node has 'status = "disabled"'
Ulf Magnusson62d57412018-12-17 20:09:47 +0100687
688 read_only:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200689 True if the node has a 'read-only' property, and False otherwise
Ulf Magnusson62d57412018-12-17 20:09:47 +0100690
691 instance_no:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200692 Dictionary that maps each 'compatible' string for the node to a unique
693 index among all nodes that have that 'compatible' string.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100694
695 As an example, 'instance_no["foo,led"] == 3' can be read as "this is the
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200696 fourth foo,led node".
Ulf Magnusson62d57412018-12-17 20:09:47 +0100697
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200698 Only enabled nodes (status != "disabled") are counted. 'instance_no' is
699 meaningless for disabled nodes.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100700
701 matching_compat:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200702 The 'compatible' string for the binding that matched the node, or None if
703 the node has no binding
Ulf Magnusson62d57412018-12-17 20:09:47 +0100704
Ulf Magnusson62d57412018-12-17 20:09:47 +0100705 binding_path:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200706 The path to the binding file for the node, or None if the node has no
Ulf Magnusson62d57412018-12-17 20:09:47 +0100707 binding
708
709 compats:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200710 A list of 'compatible' strings for the node, in the same order that
Ulf Magnusson62d57412018-12-17 20:09:47 +0100711 they're listed in the .dts file
712
713 regs:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200714 A list of Register objects for the node's registers
Ulf Magnusson62d57412018-12-17 20:09:47 +0100715
716 props:
Ulf Magnusson72158852019-11-12 18:33:00 +0100717 A collections.OrderedDict that maps property names to Property objects.
718 Property objects are created for all devicetree properties on the node
719 that are mentioned in 'properties:' in the binding.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100720
721 aliases:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200722 A list of aliases for the node. This is fetched from the /aliases node.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100723
724 interrupts:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +0200725 A list of ControllerAndData objects for the interrupts generated by the
Ulf Magnussoneef8d192019-10-30 16:05:30 +0100726 node. The list is empty if the node does not generate interrupts.
727
728 pinctrls:
729 A list of PinCtrl objects for the pinctrl-<index> properties on the
730 node, sorted by index. The list is empty if the node does not have any
731 pinctrl-<index> properties.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100732
733 bus:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200734 The bus for the node as specified in its binding, e.g. "i2c" or "spi".
Ulf Magnusson62d57412018-12-17 20:09:47 +0100735 None if the binding doesn't specify a bus.
736
737 flash_controller:
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200738 The flash controller for the node. Only meaningful for nodes representing
739 flash partitions.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100740 """
741 @property
742 def name(self):
743 "See the class docstring"
744 return self._node.name
745
746 @property
747 def unit_addr(self):
748 "See the class docstring"
749
750 # TODO: Return a plain string here later, like dtlib.Node.unit_addr?
751
752 if "@" not in self.name:
753 return None
754
755 try:
756 addr = int(self.name.split("@", 1)[1], 16)
757 except ValueError:
758 _err("{!r} has non-hex unit address".format(self))
759
760 addr = _translate(addr, self._node)
761
762 if self.regs and self.regs[0].addr != addr:
Ulf Magnusson7c26c172019-09-28 15:03:45 +0200763 self.edt._warn("unit-address and first reg (0x{:x}) don't match "
764 "for {}".format(self.regs[0].addr, self.name))
Ulf Magnusson62d57412018-12-17 20:09:47 +0100765
766 return addr
767
768 @property
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200769 def description(self):
770 "See the class docstring."
771 if self._binding and "description" in self._binding:
Ulf Magnussonf3f88a82019-10-28 13:02:21 +0100772 return self._binding["description"].strip()
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200773 return None
774
775 @property
Ulf Magnusson62d57412018-12-17 20:09:47 +0100776 def path(self):
777 "See the class docstring"
778 return self._node.path
779
780 @property
781 def label(self):
782 "See the class docstring"
783 if "label" in self._node.props:
784 return self._node.props["label"].to_string()
785 return None
786
787 @property
788 def parent(self):
789 "See the class docstring"
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200790 return self.edt._node2enode.get(self._node.parent)
Ulf Magnusson62d57412018-12-17 20:09:47 +0100791
792 @property
Ulf Magnusson110526e2019-09-21 04:14:33 +0200793 def children(self):
794 "See the class docstring"
795 # Could be initialized statically too to preserve identity, but not
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200796 # sure if needed. Parent nodes being initialized before their children
797 # would need to be kept in mind.
Ulf Magnusson72158852019-11-12 18:33:00 +0100798 return OrderedDict((name, self.edt._node2enode[node])
799 for name, node in self._node.nodes.items())
Ulf Magnusson110526e2019-09-21 04:14:33 +0200800
801 @property
Ulf Magnusson2e1d2882019-11-06 22:36:50 +0100802 def required_by(self):
803 "See the class docstring"
804 return self.edt._graph.required_by(self)
805
806 @property
807 def depends_on(self):
808 "See the class docstring"
809 return self.edt._graph.depends_on(self)
810
811 @property
Ulf Magnusson62d57412018-12-17 20:09:47 +0100812 def enabled(self):
813 "See the class docstring"
814 return "status" not in self._node.props or \
815 self._node.props["status"].to_string() != "disabled"
816
817 @property
818 def read_only(self):
819 "See the class docstring"
820 return "read-only" in self._node.props
821
822 @property
823 def aliases(self):
824 "See the class docstring"
825 return [alias for alias, node in self._node.dt.alias2node.items()
826 if node is self._node]
827
828 @property
829 def bus(self):
830 "See the class docstring"
831 return _binding_bus(self._binding)
832
833 @property
834 def flash_controller(self):
835 "See the class docstring"
836
837 # The node path might be something like
838 # /flash-controller@4001E000/flash@0/partitions/partition@fc000. We go
839 # up two levels to get the flash and check its compat. The flash
840 # controller might be the flash itself (for cases like NOR flashes).
841 # For the case of 'soc-nv-flash', we assume the controller is the
842 # parent of the flash node.
843
844 if not self.parent or not self.parent.parent:
845 _err("flash partition {!r} lacks parent or grandparent node"
846 .format(self))
847
848 controller = self.parent.parent
849 if controller.matching_compat == "soc-nv-flash":
850 return controller.parent
851 return controller
852
853 def __repr__(self):
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200854 return "<Node {} in '{}', {}>".format(
Ulf Magnusson62d57412018-12-17 20:09:47 +0100855 self.path, self.edt.dts_path,
856 "binding " + self.binding_path if self.binding_path
857 else "no binding")
858
Ulf Magnusson62d57412018-12-17 20:09:47 +0100859 def _init_binding(self):
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200860 # Initializes Node.matching_compat, Node._binding, and
861 # Node.binding_path.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100862 #
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200863 # Node._binding holds the data from the node's binding file, in the
Ulf Magnusson62d57412018-12-17 20:09:47 +0100864 # format returned by PyYAML (plain Python lists, dicts, etc.), or None
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200865 # if the node has no binding.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100866
Ulf Magnusson73ac1462019-09-23 05:14:18 +0200867 # This relies on the parent of the node having already been
Ulf Magnusson62d57412018-12-17 20:09:47 +0100868 # initialized, which is guaranteed by going through the nodes in
869 # node_iter() order.
870
871 if "compatible" in self._node.props:
872 self.compats = self._node.props["compatible"].to_strings()
873 bus = self._bus_from_parent_binding()
874
875 for compat in self.compats:
876 if (compat, bus) in self.edt._compat2binding:
877 # Binding found
878 self.matching_compat = compat
879 self._binding, self.binding_path = \
880 self.edt._compat2binding[compat, bus]
881
Ulf Magnusson62d57412018-12-17 20:09:47 +0100882 return
883 else:
Ulf Magnusson0b1ab4a2019-09-17 18:24:30 +0200884 # No 'compatible' property. See if the parent binding has a
885 # 'child-binding:' key that gives the binding (or a legacy
886 # 'sub-node:' key).
Ulf Magnusson62d57412018-12-17 20:09:47 +0100887
888 self.compats = []
889
Ulf Magnusson0b1ab4a2019-09-17 18:24:30 +0200890 binding_from_parent = self._binding_from_parent()
891 if binding_from_parent:
892 self._binding = binding_from_parent
Ulf Magnusson62d57412018-12-17 20:09:47 +0100893 self.binding_path = self.parent.binding_path
Ulf Magnusson62d57412018-12-17 20:09:47 +0100894 self.matching_compat = self.parent.matching_compat
Ulf Magnusson0b1ab4a2019-09-17 18:24:30 +0200895
Ulf Magnusson62d57412018-12-17 20:09:47 +0100896 return
897
898 # No binding found
Ulf Magnusson72bfa832019-10-14 17:30:22 +0200899 self._binding = self.binding_path = self.matching_compat = None
Ulf Magnusson62d57412018-12-17 20:09:47 +0100900
Ulf Magnusson0b1ab4a2019-09-17 18:24:30 +0200901 def _binding_from_parent(self):
902 # Returns the binding from 'child-binding:' in the parent node's
903 # binding (or from the legacy 'sub-node:' key), or None if missing
904
905 if not self.parent:
906 return None
907
908 pbinding = self.parent._binding
909 if not pbinding:
910 return None
911
912 if "child-binding" in pbinding:
913 return pbinding["child-binding"]
914
915 # Backwards compatibility
916 if "sub-node" in pbinding:
917 return {"title": pbinding["title"],
918 "description": pbinding["description"],
919 "properties": pbinding["sub-node"]["properties"]}
920
921 return None
922
Ulf Magnusson62d57412018-12-17 20:09:47 +0100923 def _bus_from_parent_binding(self):
Ulf Magnusson379145f2019-11-26 22:19:55 +0100924 # _init_binding() helper. Returns the bus specified by 'bus:' in the
925 # parent binding (or the legacy 'child-bus:'/'child: bus:'), or None if
Ulf Magnusson1ebe9452019-09-16 16:42:18 +0200926 # missing.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100927
928 if not self.parent:
929 return None
930
931 binding = self.parent._binding
Ulf Magnusson1ebe9452019-09-16 16:42:18 +0200932 if not binding:
933 return None
934
Ulf Magnusson379145f2019-11-26 22:19:55 +0100935 if "bus" in binding:
936 return binding["bus"]
937
938 # Legacy key
Ulf Magnusson1ebe9452019-09-16 16:42:18 +0200939 if "child-bus" in binding:
940 return binding["child-bus"]
941
942 # Legacy key
943 if "child" in binding:
944 # _check_binding() has checked that the "bus" key exists
945 return binding["child"]["bus"]
946
Ulf Magnusson62d57412018-12-17 20:09:47 +0100947 return None
948
949 def _init_props(self):
Ulf Magnussonb918e252019-08-30 03:49:43 +0200950 # Creates self.props. See the class docstring. Also checks that all
951 # properties on the node are declared in its binding.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100952
Ulf Magnusson72158852019-11-12 18:33:00 +0100953 self.props = OrderedDict()
Ulf Magnusson62d57412018-12-17 20:09:47 +0100954
Ulf Magnussonb918e252019-08-30 03:49:43 +0200955 if not self._binding:
Ulf Magnusson62d57412018-12-17 20:09:47 +0100956 return
957
Ulf Magnussonb918e252019-08-30 03:49:43 +0200958 # Initialize self.props
959 if "properties" in self._binding:
960 for name, options in self._binding["properties"].items():
961 self._init_prop(name, options)
962
963 self._check_undeclared_props()
Ulf Magnusson62d57412018-12-17 20:09:47 +0100964
965 def _init_prop(self, name, options):
966 # _init_props() helper for initializing a single property
967
Ulf Magnusson62d57412018-12-17 20:09:47 +0100968 prop_type = options.get("type")
969 if not prop_type:
970 _err("'{}' in {} lacks 'type'".format(name, self.binding_path))
971
Ulf Magnusson8d317bc2019-09-06 12:31:55 +0200972 val = self._prop_val(
973 name, prop_type,
Ulf Magnussonff1f7522019-08-29 22:21:33 +0200974 options.get("required") or options.get("category") == "required",
975 options.get("default"))
Ulf Magnusson8d317bc2019-09-06 12:31:55 +0200976
Ulf Magnusson62d57412018-12-17 20:09:47 +0100977 if val is None:
Ulf Magnussonfcd665a2019-08-28 00:22:01 +0200978 # 'required: false' property that wasn't there, or a property type
979 # for which we store no data.
Ulf Magnusson62d57412018-12-17 20:09:47 +0100980 return
981
Kumar Gala62bf2672019-08-09 14:51:58 -0500982 enum = options.get("enum")
983 if enum and val not in enum:
984 _err("value of property '{}' on {} in {} ({!r}) is not in 'enum' "
985 "list in {} ({!r})"
986 .format(name, self.path, self.edt.dts_path, val,
987 self.binding_path, enum))
988
Kumar Gala3d143742019-08-09 16:03:46 -0500989 const = options.get("const")
990 if const is not None and val != const:
991 _err("value of property '{}' on {} in {} ({!r}) is different from "
992 "the 'const' value specified in {} ({!r})"
993 .format(name, self.path, self.edt.dts_path, val,
994 self.binding_path, const))
995
Kumar Gala5dd715b2019-08-09 09:49:22 -0500996 # Skip properties that start with '#', like '#size-cells', and mapping
997 # properties like 'gpio-map'/'interrupt-map'
998 if name[0] == "#" or name.endswith("-map"):
999 return
1000
Ulf Magnusson62d57412018-12-17 20:09:47 +01001001 prop = Property()
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001002 prop.node = self
Ulf Magnusson62d57412018-12-17 20:09:47 +01001003 prop.name = name
1004 prop.description = options.get("description")
1005 if prop.description:
Ulf Magnussonf3f88a82019-10-28 13:02:21 +01001006 prop.description = prop.description.strip()
Ulf Magnusson62d57412018-12-17 20:09:47 +01001007 prop.val = val
Ulf Magnusson165dde02019-08-14 11:13:14 +02001008 prop.type = prop_type
Kumar Gala62bf2672019-08-09 14:51:58 -05001009 prop.enum_index = None if enum is None else enum.index(val)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001010
1011 self.props[name] = prop
1012
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001013 def _prop_val(self, name, prop_type, required, default):
Ulf Magnusson62d57412018-12-17 20:09:47 +01001014 # _init_prop() helper for getting the property's value
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001015 #
1016 # name:
1017 # Property name from binding
1018 #
1019 # prop_type:
1020 # Property type from binding (a string like "int")
1021 #
1022 # optional:
1023 # True if the property isn't required to exist
1024 #
1025 # default:
1026 # Default value to use when the property doesn't exist, or None if
1027 # the binding doesn't give a default value
Ulf Magnusson62d57412018-12-17 20:09:47 +01001028
1029 node = self._node
Ulf Magnusson06b746c2019-08-09 20:38:17 +02001030 prop = node.props.get(name)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001031
Ulf Magnusson62d57412018-12-17 20:09:47 +01001032 if not prop:
Ulf Magnussonfcd665a2019-08-28 00:22:01 +02001033 if required and self.enabled:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001034 _err("'{}' is marked as required in 'properties:' in {}, but "
1035 "does not appear in {!r}".format(
1036 name, self.binding_path, node))
1037
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001038 if default is not None:
1039 # YAML doesn't have a native format for byte arrays. We need to
1040 # convert those from an array like [0x12, 0x34, ...]. The
1041 # format has already been checked in
1042 # _check_prop_type_and_default().
1043 if prop_type == "uint8-array":
1044 return bytes(default)
1045 return default
1046
Ulf Magnusson0c4f4a92019-09-30 16:42:07 +02001047 return False if prop_type == "boolean" else None
1048
1049 if prop_type == "boolean":
1050 if prop.type is not TYPE_EMPTY:
1051 _err("'{0}' in {1!r} is defined with 'type: boolean' in {2}, "
1052 "but is assigned a value ('{3}') instead of being empty "
1053 "('{0};')".format(name, node, self.binding_path, prop))
1054 return True
Ulf Magnusson62d57412018-12-17 20:09:47 +01001055
1056 if prop_type == "int":
1057 return prop.to_num()
1058
1059 if prop_type == "array":
1060 return prop.to_nums()
1061
1062 if prop_type == "uint8-array":
Ulf Magnusson06b746c2019-08-09 20:38:17 +02001063 return prop.to_bytes()
Ulf Magnusson62d57412018-12-17 20:09:47 +01001064
1065 if prop_type == "string":
1066 return prop.to_string()
1067
1068 if prop_type == "string-array":
1069 return prop.to_strings()
1070
Ulf Magnusson06b746c2019-08-09 20:38:17 +02001071 if prop_type == "phandle":
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001072 return self.edt._node2enode[prop.to_node()]
Ulf Magnusson06b746c2019-08-09 20:38:17 +02001073
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001074 if prop_type == "phandles":
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001075 return [self.edt._node2enode[node] for node in prop.to_nodes()]
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001076
1077 if prop_type == "phandle-array":
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001078 # This type is a bit high-level for dtlib as it involves
1079 # information from bindings and *-names properties, so there's no
1080 # to_phandle_array() in dtlib. Do the type check ourselves.
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001081 if prop.type not in (TYPE_PHANDLE, TYPE_PHANDLES_AND_NUMS):
1082 _err("expected property '{0}' in {1} in {2} to be assigned "
1083 "with '{0} = < &foo 1 2 ... &bar 3 4 ... >' (a mix of "
1084 "phandles and numbers), not '{3}'"
1085 .format(name, node.path, node.dt.filename, prop))
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001086
1087 return self._standard_phandle_val_list(prop)
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001088
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001089 # prop_type == "compound". We have already checked that the 'type:'
1090 # value is valid, in _check_binding().
1091 #
1092 # 'compound' is a dummy type for properties that don't fit any of the
1093 # patterns above, so that we can require all entries in 'properties:'
1094 # to have a 'type: ...'. No Property object is created for it.
1095 return None
Ulf Magnusson62d57412018-12-17 20:09:47 +01001096
Ulf Magnussonb918e252019-08-30 03:49:43 +02001097 def _check_undeclared_props(self):
1098 # Checks that all properties are declared in the binding
1099
1100 if "properties" in self._binding:
1101 declared_props = self._binding["properties"].keys()
1102 else:
1103 declared_props = set()
1104
1105 for prop_name in self._node.props:
1106 # Allow a few special properties to not be declared in the binding
1107 if prop_name.endswith("-controller") or \
1108 prop_name.startswith("#") or \
1109 prop_name.startswith("pinctrl-") or \
1110 prop_name in {
1111 "compatible", "status", "ranges", "phandle",
1112 "interrupt-parent", "interrupts-extended", "device_type"}:
1113 continue
1114
1115 if prop_name not in declared_props:
1116 _err("'{}' appears in {} in {}, but is not declared in "
1117 "'properties:' in {}"
1118 .format(prop_name, self._node.path, self.edt.dts_path,
1119 self.binding_path))
1120
Ulf Magnusson62d57412018-12-17 20:09:47 +01001121 def _init_regs(self):
Ulf Magnusson95deec12019-08-06 21:51:06 +02001122 # Initializes self.regs
Ulf Magnusson62d57412018-12-17 20:09:47 +01001123
1124 node = self._node
1125
1126 self.regs = []
1127
1128 if "reg" not in node.props:
1129 return
1130
1131 address_cells = _address_cells(node)
1132 size_cells = _size_cells(node)
1133
1134 for raw_reg in _slice(node, "reg", 4*(address_cells + size_cells)):
1135 reg = Register()
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001136 reg.node = self
Ulf Magnusson62d57412018-12-17 20:09:47 +01001137 reg.addr = _translate(to_num(raw_reg[:4*address_cells]), node)
1138 reg.size = to_num(raw_reg[4*address_cells:])
1139 if size_cells != 0 and reg.size == 0:
1140 _err("zero-sized 'reg' in {!r} seems meaningless (maybe you "
1141 "want a size of one or #size-cells = 0 instead)"
1142 .format(self._node))
1143
1144 self.regs.append(reg)
1145
Ulf Magnusson95deec12019-08-06 21:51:06 +02001146 _add_names(node, "reg", self.regs)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001147
Ulf Magnussoneef8d192019-10-30 16:05:30 +01001148 def _init_pinctrls(self):
1149 # Initializes self.pinctrls from any pinctrl-<index> properties
1150
1151 node = self._node
1152
1153 # pinctrl-<index> properties
1154 pinctrl_props = [prop for name, prop in node.props.items()
1155 if re.match("pinctrl-[0-9]+", name)]
1156 # Sort by index
1157 pinctrl_props.sort(key=lambda prop: prop.name)
1158
1159 # Check indices
1160 for i, prop in enumerate(pinctrl_props):
1161 if prop.name != "pinctrl-" + str(i):
1162 _err("missing 'pinctrl-{}' property on {!r} - indices should "
1163 "be contiguous and start from zero".format(i, node))
1164
1165 self.pinctrls = []
1166 for prop in pinctrl_props:
1167 pinctrl = PinCtrl()
1168 pinctrl.node = self
1169 pinctrl.conf_nodes = [
1170 self.edt._node2enode[node] for node in prop.to_nodes()
1171 ]
1172 self.pinctrls.append(pinctrl)
1173
1174 _add_names(node, "pinctrl", self.pinctrls)
1175
Ulf Magnusson62d57412018-12-17 20:09:47 +01001176 def _init_interrupts(self):
Ulf Magnusson95deec12019-08-06 21:51:06 +02001177 # Initializes self.interrupts
Ulf Magnusson62d57412018-12-17 20:09:47 +01001178
1179 node = self._node
1180
1181 self.interrupts = []
1182
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001183 for controller_node, data in _interrupts(node):
1184 interrupt = ControllerAndData()
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001185 interrupt.node = self
1186 interrupt.controller = self.edt._node2enode[controller_node]
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001187 interrupt.data = self._named_cells(interrupt.controller, data,
Ulf Magnusson6b875042019-09-23 07:40:16 +02001188 "interrupt")
Ulf Magnusson62d57412018-12-17 20:09:47 +01001189
1190 self.interrupts.append(interrupt)
1191
Ulf Magnusson95deec12019-08-06 21:51:06 +02001192 _add_names(node, "interrupt", self.interrupts)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001193
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001194 def _standard_phandle_val_list(self, prop):
1195 # Parses a property like
Ulf Magnusson95deec12019-08-06 21:51:06 +02001196 #
1197 # <name>s = <phandle value phandle value ...>
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001198 # (e.g., pwms = <&foo 1 2 &bar 3 4>)
Ulf Magnusson95deec12019-08-06 21:51:06 +02001199 #
1200 # , where each phandle points to a node that has a
1201 #
1202 # #<name>-cells = <size>
1203 #
1204 # property that gives the number of cells in the value after the
Ulf Magnusson567c3482019-09-26 20:34:13 +02001205 # phandle. These values are given names in *-cells in the binding for
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001206 # the controller.
1207 #
1208 # Also parses any
Ulf Magnusson95deec12019-08-06 21:51:06 +02001209 #
1210 # <name>-names = "...", "...", ...
1211 #
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001212 # Returns a list of ControllerAndData instances.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001213
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001214 if prop.name.endswith("gpios"):
1215 # There's some slight special-casing for *-gpios properties in that
1216 # e.g. foo-gpios still maps to #gpio-cells rather than
1217 # #foo-gpio-cells
1218 basename = "gpio"
1219 else:
1220 # Strip -s. We've already checked that the property names end in -s
1221 # in _check_binding().
1222 basename = prop.name[:-1]
Ulf Magnusson62d57412018-12-17 20:09:47 +01001223
Ulf Magnusson95deec12019-08-06 21:51:06 +02001224 res = []
Ulf Magnusson62d57412018-12-17 20:09:47 +01001225
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001226 for controller_node, data in _phandle_val_list(prop, basename):
1227 mapped_controller, mapped_data = \
1228 _map_phandle_array_entry(prop.node, controller_node, data,
1229 basename)
Ulf Magnusson95deec12019-08-06 21:51:06 +02001230
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001231 entry = ControllerAndData()
1232 entry.node = self
1233 entry.controller = self.edt._node2enode[mapped_controller]
1234 entry.data = self._named_cells(entry.controller, mapped_data,
1235 basename)
Ulf Magnusson95deec12019-08-06 21:51:06 +02001236
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001237 res.append(entry)
1238
1239 _add_names(self._node, basename, res)
Ulf Magnusson95deec12019-08-06 21:51:06 +02001240
1241 return res
Ulf Magnusson62d57412018-12-17 20:09:47 +01001242
Ulf Magnusson567c3482019-09-26 20:34:13 +02001243 def _named_cells(self, controller, data, basename):
1244 # Returns a dictionary that maps <basename>-cells names given in the
1245 # binding for 'controller' to cell values. 'data' is the raw data, as a
1246 # byte array.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001247
1248 if not controller._binding:
1249 _err("{} controller {!r} for {!r} lacks binding"
Ulf Magnusson567c3482019-09-26 20:34:13 +02001250 .format(basename, controller._node, self._node))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001251
Ulf Magnusson567c3482019-09-26 20:34:13 +02001252 if basename + "-cells" in controller._binding:
1253 cell_names = controller._binding[basename + "-cells"]
1254 elif "#cells" in controller._binding:
1255 # Backwards compatibility
Ulf Magnusson62d57412018-12-17 20:09:47 +01001256 cell_names = controller._binding["#cells"]
Ulf Magnusson62d57412018-12-17 20:09:47 +01001257 else:
Ulf Magnusson567c3482019-09-26 20:34:13 +02001258 # Treat no *-cells in the binding the same as an empty *-cells, so
1259 # that bindings don't have to have e.g. an empty 'clock-cells:' for
Ulf Magnusson62d57412018-12-17 20:09:47 +01001260 # '#clock-cells = <0>'.
1261 cell_names = []
1262
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001263 data_list = to_nums(data)
1264 if len(data_list) != len(cell_names):
Ulf Magnusson567c3482019-09-26 20:34:13 +02001265 _err("unexpected '{}-cells:' length in binding for {!r} - {} "
1266 "instead of {}"
1267 .format(basename, controller._node, len(cell_names),
1268 len(data_list)))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001269
Ulf Magnusson72158852019-11-12 18:33:00 +01001270 return OrderedDict(zip(cell_names, data_list))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001271
1272 def _set_instance_no(self):
1273 # Initializes self.instance_no
1274
1275 self.instance_no = {}
1276
1277 for compat in self.compats:
1278 self.instance_no[compat] = 0
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001279 for other_node in self.edt.nodes:
1280 if compat in other_node.compats and other_node.enabled:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001281 self.instance_no[compat] += 1
1282
1283
1284class Register:
1285 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001286 Represents a register on a node.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001287
1288 These attributes are available on Register objects:
1289
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001290 node:
1291 The Node instance this register is from
Ulf Magnusson62d57412018-12-17 20:09:47 +01001292
1293 name:
1294 The name of the register as given in the 'reg-names' property, or None if
1295 there is no 'reg-names' property
1296
1297 addr:
1298 The starting address of the register, in the parent address space. Any
1299 'ranges' properties are taken into account.
1300
1301 size:
1302 The length of the register in bytes
1303 """
1304 def __repr__(self):
1305 fields = []
1306
1307 if self.name is not None:
1308 fields.append("name: " + self.name)
1309 fields.append("addr: " + hex(self.addr))
1310 fields.append("size: " + hex(self.size))
1311
1312 return "<Register, {}>".format(", ".join(fields))
1313
1314
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001315class ControllerAndData:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001316 """
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001317 Represents an entry in an 'interrupts' or 'type: phandle-array' property
1318 value, e.g. <&ctrl-1 4 0> in
Ulf Magnusson62d57412018-12-17 20:09:47 +01001319
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001320 cs-gpios = <&ctrl-1 4 0 &ctrl-2 3 4>;
1321
1322 These attributes are available on ControllerAndData objects:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001323
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001324 node:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001325 The Node instance the property appears on
Ulf Magnusson62d57412018-12-17 20:09:47 +01001326
1327 controller:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001328 The Node instance for the controller (e.g. the controller the interrupt
1329 gets sent to for interrupts)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001330
Ulf Magnusson6b875042019-09-23 07:40:16 +02001331 data:
Ulf Magnusson567c3482019-09-26 20:34:13 +02001332 A dictionary that maps names from the *-cells key in the binding for the
1333 controller to data values, e.g. {"pin": 4, "flags": 0} for the example
1334 above.
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001335
1336 'interrupts = <1 2>' might give {"irq": 1, "level": 2}.
1337
1338 name:
1339 The name of the entry as given in
1340 'interrupt-names'/'gpio-names'/'pwm-names'/etc., or None if there is no
1341 *-names property
Ulf Magnusson62d57412018-12-17 20:09:47 +01001342 """
1343 def __repr__(self):
1344 fields = []
1345
1346 if self.name is not None:
1347 fields.append("name: " + self.name)
1348
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001349 fields.append("controller: {}".format(self.controller))
Ulf Magnusson6b875042019-09-23 07:40:16 +02001350 fields.append("data: {}".format(self.data))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001351
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001352 return "<ControllerAndData, {}>".format(", ".join(fields))
Jim Paris67f53ba2019-08-07 15:05:23 -04001353
1354
Ulf Magnussoneef8d192019-10-30 16:05:30 +01001355class PinCtrl:
1356 """
1357 Represents a pin control configuration for a set of pins on a device,
1358 e.g. pinctrl-0 or pinctrl-1.
1359
1360 These attributes are available on PinCtrl objects:
1361
1362 node:
1363 The Node instance the pinctrl-* property is on
1364
1365 name:
1366 The name of the configuration, as given in pinctrl-names, or None if
1367 there is no pinctrl-names property
1368
1369 conf_nodes:
1370 A list of Node instances for the pin configuration nodes, e.g.
1371 the nodes pointed at by &state_1 and &state_2 in
1372
1373 pinctrl-0 = <&state_1 &state_2>;
1374 """
1375 def __repr__(self):
1376 fields = []
1377
1378 if self.name is not None:
1379 fields.append("name: " + self.name)
1380
1381 fields.append("configuration nodes: " + str(self.conf_nodes))
1382
1383 return "<PinCtrl, {}>".format(", ".join(fields))
1384
1385
Ulf Magnusson62d57412018-12-17 20:09:47 +01001386class Property:
1387 """
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001388 Represents a property on a Node, as set in its DT node and with
Ulf Magnusson62d57412018-12-17 20:09:47 +01001389 additional info from the 'properties:' section of the binding.
1390
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001391 Only properties mentioned in 'properties:' get created. Properties of type
1392 'compound' currently do not get Property instances, as I'm not sure what
1393 information to store for them.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001394
1395 These attributes are available on Property objects:
1396
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001397 node:
1398 The Node instance the property is on
Ulf Magnusson62d57412018-12-17 20:09:47 +01001399
1400 name:
1401 The name of the property
1402
1403 description:
1404 The description string from the property as given in the binding, or None
Ulf Magnussonf3f88a82019-10-28 13:02:21 +01001405 if missing. Leading and trailing whitespace (including newlines) is
1406 removed.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001407
Ulf Magnusson165dde02019-08-14 11:13:14 +02001408 type:
1409 A string with the type of the property, as given in the binding.
1410
Ulf Magnusson62d57412018-12-17 20:09:47 +01001411 val:
Ulf Magnusson06b746c2019-08-09 20:38:17 +02001412 The value of the property, with the format determined by the 'type:' key
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001413 from the binding.
1414
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001415 - For 'type: int/array/string/string-array', 'val' is what you'd expect
1416 (a Python integer or string, or a list of them)
Ulf Magnussonc42873f2019-08-13 11:52:10 +02001417
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001418 - For 'type: phandle', 'val' is the pointed-to Node instance
1419
1420 - For 'type: phandles', 'val' is a list of the pointed-to Node
1421 instances
1422
1423 - For 'type: phandle-array', 'val' is a list of ControllerAndData
1424 instances. See the documentation for that class.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001425
1426 enum_index:
1427 The index of the property's value in the 'enum:' list in the binding, or
1428 None if the binding has no 'enum:'
1429 """
1430 def __repr__(self):
1431 fields = ["name: " + self.name,
1432 # repr() to deal with lists
Ulf Magnusson165dde02019-08-14 11:13:14 +02001433 "type: " + self.type,
Ulf Magnusson62d57412018-12-17 20:09:47 +01001434 "value: " + repr(self.val)]
1435
1436 if self.enum_index is not None:
1437 fields.append("enum index: {}".format(self.enum_index))
1438
1439 return "<Property, {}>".format(", ".join(fields))
1440
1441
1442class EDTError(Exception):
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001443 "Exception raised for devicetree- and binding-related errors"
Ulf Magnusson62d57412018-12-17 20:09:47 +01001444
1445
1446#
1447# Public global functions
1448#
1449
1450
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001451def spi_dev_cs_gpio(node):
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001452 # Returns an SPI device's GPIO chip select if it exists, as a
1453 # ControllerAndData instance, and None otherwise. See
Ulf Magnusson62d57412018-12-17 20:09:47 +01001454 # Documentation/devicetree/bindings/spi/spi-bus.txt in the Linux kernel.
1455
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001456 if not (node.bus == "spi" and node.parent and
1457 "cs-gpios" in node.parent.props):
1458 return None
Ulf Magnusson62d57412018-12-17 20:09:47 +01001459
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001460 if not node.regs:
1461 _err("{!r} needs a 'reg' property, to look up the chip select index "
1462 "for SPI".format(node))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001463
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001464 parent_cs_lst = node.parent.props["cs-gpios"].val
1465
1466 # cs-gpios is indexed by the unit address
1467 cs_index = node.regs[0].addr
1468 if cs_index >= len(parent_cs_lst):
1469 _err("index from 'regs' in {!r} ({}) is >= number of cs-gpios "
1470 "in {!r} ({})".format(
1471 node, cs_index, node.parent, len(parent_cs_lst)))
1472
1473 return parent_cs_lst[cs_index]
Ulf Magnusson62d57412018-12-17 20:09:47 +01001474
1475
1476#
1477# Private global functions
1478#
1479
1480
1481def _dt_compats(dt):
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001482 # Returns a set() with all 'compatible' strings in the devicetree
Ulf Magnusson62d57412018-12-17 20:09:47 +01001483 # represented by dt (a dtlib.DT instance)
1484
1485 return {compat
1486 for node in dt.node_iter()
1487 if "compatible" in node.props
1488 for compat in node.props["compatible"].to_strings()}
1489
1490
Michael Scottb8909432019-08-02 11:42:06 -07001491def _binding_paths(bindings_dirs):
Ulf Magnusson62d57412018-12-17 20:09:47 +01001492 # Returns a list with the paths to all bindings (.yaml files) in
Michael Scottb8909432019-08-02 11:42:06 -07001493 # 'bindings_dirs'
Ulf Magnusson62d57412018-12-17 20:09:47 +01001494
Michael Scottb8909432019-08-02 11:42:06 -07001495 binding_paths = []
1496
1497 for bindings_dir in bindings_dirs:
1498 for root, _, filenames in os.walk(bindings_dir):
1499 for filename in filenames:
1500 if filename.endswith(".yaml"):
1501 binding_paths.append(os.path.join(root, filename))
1502
1503 return binding_paths
Ulf Magnusson62d57412018-12-17 20:09:47 +01001504
1505
Ulf Magnusson62d57412018-12-17 20:09:47 +01001506def _binding_bus(binding):
Ulf Magnusson379145f2019-11-26 22:19:55 +01001507 # Returns the bus specified by 'on-bus:' in the binding (or the
1508 # legacy 'parent-bus:' and 'parent: bus:'), or None if missing
Ulf Magnusson62d57412018-12-17 20:09:47 +01001509
Ulf Magnusson1ebe9452019-09-16 16:42:18 +02001510 if not binding:
1511 return None
1512
Ulf Magnusson379145f2019-11-26 22:19:55 +01001513 if "on-bus" in binding:
1514 return binding["on-bus"]
1515
1516 # Legacy key
Ulf Magnusson1ebe9452019-09-16 16:42:18 +02001517 if "parent-bus" in binding:
1518 return binding["parent-bus"]
1519
1520 # Legacy key
1521 if "parent" in binding:
1522 # _check_binding() has checked that the "bus" key exists
1523 return binding["parent"]["bus"]
1524
Ulf Magnusson62d57412018-12-17 20:09:47 +01001525 return None
1526
1527
1528def _binding_inc_error(msg):
1529 # Helper for reporting errors in the !include implementation
1530
1531 raise yaml.constructor.ConstructorError(None, None, "error: " + msg)
1532
1533
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001534def _merge_props(to_dict, from_dict, parent, binding_path, check_required):
Ulf Magnussond834b692019-08-21 16:41:03 +02001535 # Recursively merges 'from_dict' into 'to_dict', to implement 'include:'.
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001536 #
1537 # If 'from_dict' and 'to_dict' contain a 'required:' key for the same
1538 # property, then the values are ORed together.
1539 #
1540 # If 'check_required' is True, then an error is raised if 'from_dict' has
1541 # 'required: true' while 'to_dict' has 'required: false'. This prevents
1542 # bindings from "downgrading" requirements from bindings they include,
1543 # which might help keep bindings well-organized.
1544 #
1545 # It's an error for most other keys to appear in both 'from_dict' and
1546 # 'to_dict'. When it's not an error, the value in 'to_dict' takes
1547 # precedence.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001548 #
1549 # 'parent' is the name of the parent key containing 'to_dict' and
1550 # 'from_dict', and 'binding_path' is the path to the top-level binding.
1551 # These are used to generate errors for sketchy property overwrites.
1552
1553 for prop in from_dict:
1554 if isinstance(to_dict.get(prop), dict) and \
1555 isinstance(from_dict[prop], dict):
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001556 _merge_props(to_dict[prop], from_dict[prop], prop, binding_path,
1557 check_required)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001558 elif prop not in to_dict:
1559 to_dict[prop] = from_dict[prop]
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001560 elif _bad_overwrite(to_dict, from_dict, prop, check_required):
Ulf Magnussond834b692019-08-21 16:41:03 +02001561 _err("{} (in '{}'): '{}' from included file overwritten "
Ulf Magnusson62d57412018-12-17 20:09:47 +01001562 "('{}' replaced with '{}')".format(
1563 binding_path, parent, prop, from_dict[prop],
1564 to_dict[prop]))
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001565 elif prop == "required":
1566 # Need a separate check here, because this code runs before
1567 # _check_binding()
1568 if not (isinstance(from_dict["required"], bool) and
1569 isinstance(to_dict["required"], bool)):
1570 _err("malformed 'required:' setting for '{}' in 'properties' "
1571 "in {}, expected true/false".format(parent, binding_path))
1572
1573 # 'required: true' takes precedence
1574 to_dict["required"] = to_dict["required"] or from_dict["required"]
1575 elif prop == "category":
1576 # Legacy property key. 'category: required' takes precedence.
1577 if "required" in (to_dict["category"], from_dict["category"]):
1578 to_dict["category"] = "required"
Ulf Magnusson62d57412018-12-17 20:09:47 +01001579
1580
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001581def _bad_overwrite(to_dict, from_dict, prop, check_required):
Ulf Magnusson62d57412018-12-17 20:09:47 +01001582 # _merge_props() helper. Returns True in cases where it's bad that
1583 # to_dict[prop] takes precedence over from_dict[prop].
1584
Ulf Magnusson62d57412018-12-17 20:09:47 +01001585 if to_dict[prop] == from_dict[prop]:
1586 return False
1587
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001588 # These are overridden deliberately
1589 if prop in {"title", "description", "compatible"}:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001590 return False
1591
Ulf Magnusson2845d8f2019-09-10 19:17:29 +02001592 if prop == "required":
1593 if not check_required:
1594 return False
1595 return from_dict[prop] and not to_dict[prop]
1596
1597 # Legacy property key
1598 if prop == "category":
1599 if not check_required:
1600 return False
1601 return from_dict[prop] == "required" and to_dict[prop] == "optional"
1602
Ulf Magnusson62d57412018-12-17 20:09:47 +01001603 return True
1604
1605
Ulf Magnusson8f225292019-09-05 22:31:09 +02001606def _binding_include(loader, node):
1607 # Implements !include, for backwards compatibility. '!include [foo, bar]'
1608 # just becomes [foo, bar].
1609
1610 if isinstance(node, yaml.ScalarNode):
1611 # !include foo.yaml
1612 return [loader.construct_scalar(node)]
1613
1614 if isinstance(node, yaml.SequenceNode):
1615 # !include [foo.yaml, bar.yaml]
1616 return loader.construct_sequence(node)
1617
1618 _binding_inc_error("unrecognised node type in !include statement")
1619
1620
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001621def _check_prop_type_and_default(prop_name, prop_type, required, default,
1622 binding_path):
1623 # _check_binding() helper. Checks 'type:' and 'default:' for the property
1624 # named 'prop_name'
1625
1626 if prop_type is None:
1627 _err("missing 'type:' for '{}' in 'properties' in {}"
1628 .format(prop_name, binding_path))
1629
1630 ok_types = {"boolean", "int", "array", "uint8-array", "string",
1631 "string-array", "phandle", "phandles", "phandle-array",
1632 "compound"}
1633
1634 if prop_type not in ok_types:
1635 _err("'{}' in 'properties:' in {} has unknown type '{}', expected one "
1636 "of {}".format(prop_name, binding_path, prop_type,
1637 ", ".join(ok_types)))
1638
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001639 if prop_type == "phandle-array" and not prop_name.endswith("s"):
1640 _err("'{}' in 'properties:' in {} is 'type: phandle-array', but its "
1641 "name does not end in -s. This is required since property names "
1642 "like '#pwm-cells' and 'pwm-names' get derived from 'pwms', for "
1643 "example.".format(prop_name, binding_path))
1644
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001645 # Check default
1646
1647 if default is None:
1648 return
1649
Ulf Magnussonff1f7522019-08-29 22:21:33 +02001650 if prop_type in {"boolean", "compound", "phandle", "phandles",
1651 "phandle-array"}:
1652 _err("'default:' can't be combined with 'type: {}' for '{}' in "
1653 "'properties:' in {}".format(prop_type, prop_name, binding_path))
1654
1655 def ok_default():
1656 # Returns True if 'default' is an okay default for the property's type
1657
1658 if prop_type == "int" and isinstance(default, int) or \
1659 prop_type == "string" and isinstance(default, str):
1660 return True
1661
1662 # array, uint8-array, or string-array
1663
1664 if not isinstance(default, list):
1665 return False
1666
1667 if prop_type == "array" and \
1668 all(isinstance(val, int) for val in default):
1669 return True
1670
1671 if prop_type == "uint8-array" and \
1672 all(isinstance(val, int) and 0 <= val <= 255 for val in default):
1673 return True
1674
1675 # string-array
1676 return all(isinstance(val, str) for val in default)
1677
1678 if not ok_default():
1679 _err("'default: {}' is invalid for '{}' in 'properties:' in {}, which "
1680 "has type {}".format(default, prop_name, binding_path, prop_type))
1681
1682
Ulf Magnusson62d57412018-12-17 20:09:47 +01001683def _translate(addr, node):
1684 # Recursively translates 'addr' on 'node' to the address space(s) of its
1685 # parent(s), by looking at 'ranges' properties. Returns the translated
1686 # address.
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001687 #
1688 # node:
1689 # dtlib.Node instance
Ulf Magnusson62d57412018-12-17 20:09:47 +01001690
1691 if not node.parent or "ranges" not in node.parent.props:
1692 # No translation
1693 return addr
1694
1695 if not node.parent.props["ranges"].value:
1696 # DT spec.: "If the property is defined with an <empty> value, it
1697 # specifies that the parent and child address space is identical, and
1698 # no address translation is required."
1699 #
1700 # Treat this the same as a 'range' that explicitly does a one-to-one
1701 # mapping, as opposed to there not being any translation.
1702 return _translate(addr, node.parent)
1703
1704 # Gives the size of each component in a translation 3-tuple in 'ranges'
1705 child_address_cells = _address_cells(node)
1706 parent_address_cells = _address_cells(node.parent)
1707 child_size_cells = _size_cells(node)
1708
1709 # Number of cells for one translation 3-tuple in 'ranges'
1710 entry_cells = child_address_cells + parent_address_cells + child_size_cells
1711
1712 for raw_range in _slice(node.parent, "ranges", 4*entry_cells):
1713 child_addr = to_num(raw_range[:4*child_address_cells])
1714 raw_range = raw_range[4*child_address_cells:]
1715
1716 parent_addr = to_num(raw_range[:4*parent_address_cells])
1717 raw_range = raw_range[4*parent_address_cells:]
1718
1719 child_len = to_num(raw_range)
1720
1721 if child_addr <= addr < child_addr + child_len:
1722 # 'addr' is within range of a translation in 'ranges'. Recursively
1723 # translate it and return the result.
1724 return _translate(parent_addr + addr - child_addr, node.parent)
1725
1726 # 'addr' is not within range of any translation in 'ranges'
1727 return addr
1728
1729
1730def _add_names(node, names_ident, objs):
1731 # Helper for registering names from <foo>-names properties.
1732 #
1733 # node:
Ulf Magnusson73ac1462019-09-23 05:14:18 +02001734 # edtlib.Node instance
Ulf Magnusson62d57412018-12-17 20:09:47 +01001735 #
1736 # names-ident:
Ulf Magnusson95deec12019-08-06 21:51:06 +02001737 # The <foo> part of <foo>-names, e.g. "reg" for "reg-names"
Ulf Magnusson62d57412018-12-17 20:09:47 +01001738 #
1739 # objs:
1740 # list of objects whose .name field should be set
1741
Ulf Magnusson95deec12019-08-06 21:51:06 +02001742 full_names_ident = names_ident + "-names"
1743
1744 if full_names_ident in node.props:
1745 names = node.props[full_names_ident].to_strings()
Ulf Magnusson62d57412018-12-17 20:09:47 +01001746 if len(names) != len(objs):
Ulf Magnussoneef8d192019-10-30 16:05:30 +01001747 _err("{} property in {} in {} has {} strings, expected {} strings"
1748 .format(full_names_ident, node.path, node.dt.filename,
1749 len(names), len(objs)))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001750
1751 for obj, name in zip(objs, names):
1752 obj.name = name
1753 else:
1754 for obj in objs:
1755 obj.name = None
1756
1757
1758def _interrupt_parent(node):
1759 # Returns the node pointed at by the closest 'interrupt-parent', searching
1760 # the parents of 'node'. As of writing, this behavior isn't specified in
1761 # the DT spec., but seems to match what some .dts files except.
1762
1763 while node:
1764 if "interrupt-parent" in node.props:
1765 return node.props["interrupt-parent"].to_node()
1766 node = node.parent
1767
1768 _err("{!r} has an 'interrupts' property, but neither the node nor any "
1769 "of its parents has an 'interrupt-parent' property".format(node))
1770
1771
1772def _interrupts(node):
Ulf Magnusson6b875042019-09-23 07:40:16 +02001773 # Returns a list of (<controller>, <data>) tuples, with one tuple per
Ulf Magnusson62d57412018-12-17 20:09:47 +01001774 # interrupt generated by 'node'. <controller> is the destination of the
Ulf Magnusson6b875042019-09-23 07:40:16 +02001775 # interrupt (possibly after mapping through an 'interrupt-map'), and <data>
1776 # the data associated with the interrupt (as a 'bytes' object).
Ulf Magnusson62d57412018-12-17 20:09:47 +01001777
1778 # Takes precedence over 'interrupts' if both are present
1779 if "interrupts-extended" in node.props:
1780 prop = node.props["interrupts-extended"]
1781 return [_map_interrupt(node, iparent, spec)
Ulf Magnusson95deec12019-08-06 21:51:06 +02001782 for iparent, spec in _phandle_val_list(prop, "interrupt")]
Ulf Magnusson62d57412018-12-17 20:09:47 +01001783
1784 if "interrupts" in node.props:
1785 # Treat 'interrupts' as a special case of 'interrupts-extended', with
1786 # the same interrupt parent for all interrupts
1787
1788 iparent = _interrupt_parent(node)
1789 interrupt_cells = _interrupt_cells(iparent)
1790
1791 return [_map_interrupt(node, iparent, raw)
1792 for raw in _slice(node, "interrupts", 4*interrupt_cells)]
1793
1794 return []
1795
1796
1797def _map_interrupt(child, parent, child_spec):
Ulf Magnusson6b875042019-09-23 07:40:16 +02001798 # Translates an interrupt headed from 'child' to 'parent' with data
Ulf Magnusson62d57412018-12-17 20:09:47 +01001799 # 'child_spec' through any 'interrupt-map' properties. Returns a
Ulf Magnusson6b875042019-09-23 07:40:16 +02001800 # (<controller>, <data>) tuple with the final destination after mapping.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001801
1802 if "interrupt-controller" in parent.props:
1803 return (parent, child_spec)
1804
1805 def own_address_cells(node):
1806 # Used for parents pointed at by 'interrupt-map'. We can't use
1807 # _address_cells(), because it's the #address-cells property on 'node'
1808 # itself that matters.
1809
1810 address_cells = node.props.get("#address-cells")
1811 if not address_cells:
1812 _err("missing #address-cells on {!r} (while handling interrupt-map)"
1813 .format(node))
1814 return address_cells.to_num()
1815
1816 def spec_len_fn(node):
1817 # Can't use _address_cells() here, because it's the #address-cells
1818 # property on 'node' itself that matters
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001819 return own_address_cells(node) + _interrupt_cells(node)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001820
1821 parent, raw_spec = _map(
1822 "interrupt", child, parent, _raw_unit_addr(child) + child_spec,
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001823 spec_len_fn, require_controller=True)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001824
1825 # Strip the parent unit address part, if any
1826 return (parent, raw_spec[4*own_address_cells(parent):])
1827
1828
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001829def _map_phandle_array_entry(child, parent, child_spec, basename):
Ulf Magnusson6b875042019-09-23 07:40:16 +02001830 # Returns a (<controller>, <data>) tuple with the final destination after
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001831 # mapping through any '<basename>-map' (e.g. gpio-map) properties. See
1832 # _map_interrupt().
Ulf Magnusson62d57412018-12-17 20:09:47 +01001833
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001834 def spec_len_fn(node):
1835 prop_name = "#{}-cells".format(basename)
1836 if prop_name not in node.props:
1837 _err("expected '{}' property on {!r} (referenced by {!r})"
1838 .format(prop_name, node, child))
1839 return node.props[prop_name].to_num()
Ulf Magnusson62d57412018-12-17 20:09:47 +01001840
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001841 # Do not require <prefix>-controller for anything but interrupts for now
1842 return _map(basename, child, parent, child_spec, spec_len_fn,
1843 require_controller=False)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001844
1845
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001846def _map(prefix, child, parent, child_spec, spec_len_fn, require_controller):
Ulf Magnusson62d57412018-12-17 20:09:47 +01001847 # Common code for mapping through <prefix>-map properties, e.g.
1848 # interrupt-map and gpio-map.
1849 #
1850 # prefix:
1851 # The prefix, e.g. "interrupt" or "gpio"
1852 #
1853 # child:
1854 # The "sender", e.g. the node with 'interrupts = <...>'
1855 #
1856 # parent:
1857 # The "receiver", e.g. a node with 'interrupt-map = <...>' or
1858 # 'interrupt-controller' (no mapping)
1859 #
1860 # child_spec:
1861 # The data associated with the interrupt/GPIO/etc., as a 'bytes' object,
1862 # e.g. <1 2> for 'foo-gpios = <&gpio1 1 2>'.
1863 #
1864 # spec_len_fn:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001865 # Function called on a parent specified in a *-map property to get the
1866 # length of the parent specifier (data after phandle in *-map), in cells
1867 #
1868 # require_controller:
1869 # If True, the final controller node after mapping is required to have
1870 # to have a <prefix>-controller property.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001871
1872 map_prop = parent.props.get(prefix + "-map")
1873 if not map_prop:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001874 if require_controller and prefix + "-controller" not in parent.props:
Ulf Magnusson62d57412018-12-17 20:09:47 +01001875 _err("expected '{}-controller' property on {!r} "
1876 "(referenced by {!r})".format(prefix, parent, child))
1877
1878 # No mapping
1879 return (parent, child_spec)
1880
1881 masked_child_spec = _mask(prefix, child, parent, child_spec)
1882
1883 raw = map_prop.value
1884 while raw:
1885 if len(raw) < len(child_spec):
Ulf Magnusson6b875042019-09-23 07:40:16 +02001886 _err("bad value for {!r}, missing/truncated child data"
Ulf Magnusson62d57412018-12-17 20:09:47 +01001887 .format(map_prop))
1888 child_spec_entry = raw[:len(child_spec)]
1889 raw = raw[len(child_spec):]
1890
1891 if len(raw) < 4:
1892 _err("bad value for {!r}, missing/truncated phandle"
1893 .format(map_prop))
1894 phandle = to_num(raw[:4])
1895 raw = raw[4:]
1896
1897 # Parent specified in *-map
1898 map_parent = parent.dt.phandle2node.get(phandle)
1899 if not map_parent:
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001900 _err("bad phandle ({}) in {!r}".format(phandle, map_prop))
Ulf Magnusson62d57412018-12-17 20:09:47 +01001901
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001902 map_parent_spec_len = 4*spec_len_fn(map_parent)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001903 if len(raw) < map_parent_spec_len:
Ulf Magnusson6b875042019-09-23 07:40:16 +02001904 _err("bad value for {!r}, missing/truncated parent data"
Ulf Magnusson62d57412018-12-17 20:09:47 +01001905 .format(map_prop))
1906 parent_spec = raw[:map_parent_spec_len]
1907 raw = raw[map_parent_spec_len:]
1908
Ulf Magnusson6b875042019-09-23 07:40:16 +02001909 # Got one *-map row. Check if it matches the child data.
Ulf Magnusson62d57412018-12-17 20:09:47 +01001910 if child_spec_entry == masked_child_spec:
1911 # Handle *-map-pass-thru
1912 parent_spec = _pass_thru(
1913 prefix, child, parent, child_spec, parent_spec)
1914
1915 # Found match. Recursively map and return it.
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001916 return _map(prefix, parent, map_parent, parent_spec, spec_len_fn,
1917 require_controller)
Ulf Magnusson62d57412018-12-17 20:09:47 +01001918
Ulf Magnussonb97ed9e2019-09-23 09:10:22 +02001919 _err("child specifier for {!r} ({}) does not appear in {!r}"
Ulf Magnusson62d57412018-12-17 20:09:47 +01001920 .format(child, child_spec, map_prop))
1921
1922
1923def _mask(prefix, child, parent, child_spec):
1924 # Common code for handling <prefix>-mask properties, e.g. interrupt-mask.
1925 # See _map() for the parameters.
1926
1927 mask_prop = parent.props.get(prefix + "-map-mask")
1928 if not mask_prop:
1929 # No mask
1930 return child_spec
1931
1932 mask = mask_prop.value
1933 if len(mask) != len(child_spec):
1934 _err("{!r}: expected '{}-mask' in {!r} to be {} bytes, is {} bytes"
1935 .format(child, prefix, parent, len(child_spec), len(mask)))
1936
1937 return _and(child_spec, mask)
1938
1939
1940def _pass_thru(prefix, child, parent, child_spec, parent_spec):
1941 # Common code for handling <prefix>-map-thru properties, e.g.
1942 # interrupt-pass-thru.
1943 #
1944 # parent_spec:
Ulf Magnusson6b875042019-09-23 07:40:16 +02001945 # The parent data from the matched entry in the <prefix>-map property
Ulf Magnusson62d57412018-12-17 20:09:47 +01001946 #
1947 # See _map() for the other parameters.
1948
1949 pass_thru_prop = parent.props.get(prefix + "-map-pass-thru")
1950 if not pass_thru_prop:
1951 # No pass-thru
1952 return parent_spec
1953
1954 pass_thru = pass_thru_prop.value
1955 if len(pass_thru) != len(child_spec):
1956 _err("{!r}: expected '{}-map-pass-thru' in {!r} to be {} bytes, is {} bytes"
1957 .format(child, prefix, parent, len(child_spec), len(pass_thru)))
1958
1959 res = _or(_and(child_spec, pass_thru),
1960 _and(parent_spec, _not(pass_thru)))
1961
1962 # Truncate to length of parent spec.
1963 return res[-len(parent_spec):]
1964
1965
1966def _raw_unit_addr(node):
1967 # _map_interrupt() helper. Returns the unit address (derived from 'reg' and
1968 # #address-cells) as a raw 'bytes'
1969
1970 if 'reg' not in node.props:
1971 _err("{!r} lacks 'reg' property (needed for 'interrupt-map' unit "
1972 "address lookup)".format(node))
1973
1974 addr_len = 4*_address_cells(node)
1975
1976 if len(node.props['reg'].value) < addr_len:
1977 _err("{!r} has too short 'reg' property (while doing 'interrupt-map' "
1978 "unit address lookup)".format(node))
1979
1980 return node.props['reg'].value[:addr_len]
1981
1982
1983def _and(b1, b2):
1984 # Returns the bitwise AND of the two 'bytes' objects b1 and b2. Pads
1985 # with ones on the left if the lengths are not equal.
1986
1987 # Pad on the left, to equal length
1988 maxlen = max(len(b1), len(b2))
1989 return bytes(x & y for x, y in zip(b1.rjust(maxlen, b'\xff'),
1990 b2.rjust(maxlen, b'\xff')))
1991
1992
1993def _or(b1, b2):
1994 # Returns the bitwise OR of the two 'bytes' objects b1 and b2. Pads with
1995 # zeros on the left if the lengths are not equal.
1996
1997 # Pad on the left, to equal length
1998 maxlen = max(len(b1), len(b2))
1999 return bytes(x | y for x, y in zip(b1.rjust(maxlen, b'\x00'),
2000 b2.rjust(maxlen, b'\x00')))
2001
2002
2003def _not(b):
2004 # Returns the bitwise not of the 'bytes' object 'b'
2005
2006 # ANDing with 0xFF avoids negative numbers
2007 return bytes(~x & 0xFF for x in b)
2008
2009
Ulf Magnusson95deec12019-08-06 21:51:06 +02002010def _phandle_val_list(prop, n_cells_name):
2011 # Parses a '<phandle> <value> <phandle> <value> ...' value. The number of
2012 # cells that make up each <value> is derived from the node pointed at by
2013 # the preceding <phandle>.
Ulf Magnusson62d57412018-12-17 20:09:47 +01002014 #
2015 # prop:
2016 # dtlib.Property with value to parse
2017 #
Ulf Magnusson95deec12019-08-06 21:51:06 +02002018 # n_cells_name:
2019 # The <name> part of the #<name>-cells property to look for on the nodes
2020 # the phandles point to, e.g. "gpio" for #gpio-cells.
Ulf Magnusson62d57412018-12-17 20:09:47 +01002021 #
2022 # Returns a list of (<node>, <value>) tuples, where <node> is the node
Ulf Magnusson95deec12019-08-06 21:51:06 +02002023 # pointed at by <phandle>.
2024
2025 full_n_cells_name = "#{}-cells".format(n_cells_name)
Ulf Magnusson62d57412018-12-17 20:09:47 +01002026
2027 res = []
2028
2029 raw = prop.value
2030 while raw:
2031 if len(raw) < 4:
2032 # Not enough room for phandle
2033 _err("bad value for " + repr(prop))
2034 phandle = to_num(raw[:4])
2035 raw = raw[4:]
2036
2037 node = prop.node.dt.phandle2node.get(phandle)
2038 if not node:
2039 _err("bad phandle in " + repr(prop))
2040
Ulf Magnusson95deec12019-08-06 21:51:06 +02002041 if full_n_cells_name not in node.props:
2042 _err("{!r} lacks {}".format(node, full_n_cells_name))
2043
2044 n_cells = node.props[full_n_cells_name].to_num()
Ulf Magnusson62d57412018-12-17 20:09:47 +01002045 if len(raw) < 4*n_cells:
2046 _err("missing data after phandle in " + repr(prop))
2047
2048 res.append((node, raw[:4*n_cells]))
2049 raw = raw[4*n_cells:]
2050
2051 return res
2052
2053
2054def _address_cells(node):
2055 # Returns the #address-cells setting for 'node', giving the number of <u32>
2056 # cells used to encode the address in the 'reg' property
2057
2058 if "#address-cells" in node.parent.props:
2059 return node.parent.props["#address-cells"].to_num()
2060 return 2 # Default value per DT spec.
2061
2062
2063def _size_cells(node):
2064 # Returns the #size-cells setting for 'node', giving the number of <u32>
2065 # cells used to encode the size in the 'reg' property
2066
2067 if "#size-cells" in node.parent.props:
2068 return node.parent.props["#size-cells"].to_num()
2069 return 1 # Default value per DT spec.
2070
2071
2072def _interrupt_cells(node):
2073 # Returns the #interrupt-cells property value on 'node', erroring out if
2074 # 'node' has no #interrupt-cells property
2075
2076 if "#interrupt-cells" not in node.props:
2077 _err("{!r} lacks #interrupt-cells".format(node))
2078 return node.props["#interrupt-cells"].to_num()
2079
2080
Ulf Magnusson62d57412018-12-17 20:09:47 +01002081def _slice(node, prop_name, size):
2082 # Splits node.props[prop_name].value into 'size'-sized chunks, returning a
2083 # list of chunks. Raises EDTError if the length of the property is not
2084 # evenly divisible by 'size'.
2085
2086 raw = node.props[prop_name].value
2087 if len(raw) % size:
2088 _err("'{}' property in {!r} has length {}, which is not evenly "
2089 "divisible by {}".format(prop_name, node, len(raw), size))
2090
2091 return [raw[i:i + size] for i in range(0, len(raw), size)]
2092
2093
Ulf Magnussonacf276f2019-08-07 19:33:45 +02002094def _check_dt(dt):
2095 # Does devicetree sanity checks. dtlib is meant to be general and
2096 # anything-goes except for very special properties like phandle, but in
2097 # edtlib we can be pickier.
2098
2099 # Check that 'status' has one of the values given in the devicetree spec.
2100
Ulf Magnussonfc5cd772019-09-26 13:47:58 +02002101 # Accept "ok" for backwards compatibility
2102 ok_status = {"ok", "okay", "disabled", "reserved", "fail", "fail-sss"}
Ulf Magnussonacf276f2019-08-07 19:33:45 +02002103
2104 for node in dt.node_iter():
2105 if "status" in node.props:
2106 try:
2107 status_val = node.props["status"].to_string()
2108 except DTError as e:
2109 # The error message gives the path
2110 _err(str(e))
2111
2112 if status_val not in ok_status:
2113 _err("unknown 'status' value \"{}\" in {} in {}, expected one "
2114 "of {} (see the devicetree specification)"
2115 .format(status_val, node.path, node.dt.filename,
2116 ", ".join(ok_status)))
2117
Ulf Magnusson378254a2019-08-14 18:44:35 +02002118 ranges_prop = node.props.get("ranges")
2119 if ranges_prop:
2120 if ranges_prop.type not in (TYPE_EMPTY, TYPE_NUMS):
2121 _err("expected 'ranges = < ... >;' in {} in {}, not '{}' "
2122 "(see the devicetree specification)"
2123 .format(node.path, node.dt.filename, ranges_prop))
2124
Ulf Magnussonacf276f2019-08-07 19:33:45 +02002125
Ulf Magnusson62d57412018-12-17 20:09:47 +01002126def _err(msg):
2127 raise EDTError(msg)
Ulf Magnusson0e239942019-11-12 18:21:02 +01002128
2129
2130# Custom PyYAML binding loader class to avoid modifying yaml.Loader directly,
2131# which could interfere with YAML loading in clients
2132class _BindingLoader(Loader):
2133 pass
2134
2135
2136# Add legacy '!include foo.yaml' handling
2137_BindingLoader.add_constructor("!include", _binding_include)
Ulf Magnusson72158852019-11-12 18:33:00 +01002138
2139# Use OrderedDict instead of plain dict for YAML mappings, to preserve
2140# insertion order on Python 3.5 and earlier (plain dicts only preserve
2141# insertion order on Python 3.6+). This makes testing easier and avoids
2142# surprises.
2143#
2144# Adapted from
2145# https://stackoverflow.com/questions/5121931/in-python-how-can-you-load-yaml-mappings-as-ordereddicts.
2146# Hopefully this API stays stable.
2147_BindingLoader.add_constructor(
2148 yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
2149 lambda loader, node: OrderedDict(loader.construct_pairs(node)))