scripts: dts: Generalize handling of phandle-array types

Generating generic information for 'type: phandle-array' properties in
edtlib was difficult due to defining phandle-array as just a list of
phandles and numbers. To make sense of a phandle-array property like
'pwms', you have to know that #pwm-cells is expected to appear on
each referenced controller, and that the binding for the controller has
a #cells.

Because of this, handling of various 'type: phandle-array' properties
was previously hardcoded in edtlib and exposed through properties like
Node.pwms, instead of through the generic Node.props (though with a lot
of shared code).

In practice, it turns out that all 'type: phandle-array' properties in
Zephyr work exactly the same way: They all have names that end in -s,
the 's' is removed to derive the name of related properties, and they
all look up #cells in the binding for the controller, which gives names
to the data values.

Strengthen the definition of 'type: phandle-array' to mean a property
that works exactly like the existing phandle-array properties (which
also means requiring that the name ends in -s). This removes a ton of
hardcoding from edtlib and allows new 'type: phandle-array' properties
to be added without making any code changes.

If we ever need a property type that's a list of phandles and numbers
but that doesn't follow this scheme, then we could add a separate type
for it. We should check if the standard scheme is fine first though.

The only property type for which no information is generated is now
'compound'.

There's some inconsistency in how we generate identifiers for clocks
compared to other 'type: phandle-array' properties, so keep
special-casing them for now in gen_defines.py (see the comment in
write_clocks()).

This change also enabled a bunch of other simplifications, like reusing
the ControllerAndData class for interrupts.

Piggyback generalization of *-map properties so that they work for any
phandle-array properties. It's now possible to have things like
'io-channel-map', if you need to.

Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
This commit is contained in:
Ulf Magnusson 2019-09-23 09:10:22 +02:00 committed by Kumar Gala
commit b97ed9e4b4
20 changed files with 390 additions and 618 deletions

View file

@ -86,37 +86,66 @@ parent-bus: <string describing bus type, e.g. "i2c">
# const: <string | int>
# default: <default>
#
# 'type: boolean' is for properties used as flags that don't take a value, e.g.
# 'hw-flow-control;'. The macro generated for the property gets set to 1 if the
# property exists on the node, and to 0 otherwise. When combined with
# 'required: true', this type just forces the flag to appear on the node,
# though the output will always be the same in that case (1).
# These types are available:
#
# 'type: uint8-array' is for what the device tree specification calls
# 'bytestring'. Properties of type 'uint8-array' should be set like this:
# - 'type: boolean' is for properties used as flags that don't take a value,
# e.g. 'hw-flow-control;'. The macro generated for the property gets set to
# 1 if the property exists on the node, and to 0 otherwise. When combined
# with 'required: true', this type just forces the flag to appear on the
# node, though the output will always be the same in that case (1).
#
# foo = [89 AB CD];
# Warning: Since a macro is always generated, don't use #ifdef in tests.
# Do this instead:
#
# Each value is a byte in hex.
# #if DT_SOME_BOOLEAN_PROP == 1
#
# 'type: phandle' is for properties that are assigned a single phandle, like
# this:
# - 'type: uint8-array' is for what the device tree specification calls
# 'bytestring'. Properties of type 'uint8-array' should be set like this:
#
# foo = <&label>;
# foo = [89 AB CD];
#
# 'type: phandles' is for properties that are assigned a list of (just)
# phandles, like this:
# Each value is a byte in hex.
#
# foo = <&label1 &label2 ...>;
# - 'type: phandle' is for properties that are assigned a single phandle,
# like this:
#
# 'type: phandle-array' is for properties that are assigned a list of phandles
# and (possibly) 32-bit numbers, like this:
# foo = <&label>;
#
# foo = <&label1 1 2 &label2 3 4 ...>;
# - 'type: phandles' is for properties that are assigned a list of (just)
# phandles, like this:
#
# 'type: compound' is a catch-all for more complex types, e.g.
# foo = <&label1 &label2 ...>;
#
# foo = <&label1>, [01 02];
# - 'type: phandle-array' is for properties that take a list of phandles and
# (possibly) 32-bit numbers, like this:
#
# pwms = <&ctrl-1 1 2 &ctrl-2 3 4>;
#
# This type requires that the property works in the standard way that
# devicetree properties like pwms, clocks, *-gpios, and io-channels work.
# Taking 'pwms' as an example, the final -s is stripped from the property
# name, and #pwm-cells is looked up in the node for the controller
# (&ctrl-1/&ctrl-2) to determine the number of data values after the
# phandle. The binding for each controller must also have a #cells key,
# giving names to data values. See below for an explanation of #cells.
#
# A *-names (e.g. pwm-names) property can appear on the node as well,
# giving a name to each entry (the 'pwms' example above has two entries,
# <&ctrl-1 1 2> and <&ctrl-2 3 4>).
#
# Because other property names are derived from the name of the property by
# removing the final -s, the property name must end in -s. An error is
# raised if it doesn't.
#
# *-gpios properties are special-cased so that e.g. foo-gpios resolves to
# #gpio-cells rather than #foo-gpio-cells.
#
# All phandle-array properties support mapping through *-map properties,
# e.g. gpio-map. See the devicetree spec.
#
# - 'type: compound' is a catch-all for more complex types, e.g.
#
# foo = <&label1>, [01 02];
#
# 'type: array' and the other array types also allow splitting the value into
# several <> blocks, e.g. like this:
@ -256,8 +285,8 @@ child-binding:
required: true
# If the binding describes an interrupt controller, GPIO controller, pinmux
# device, or any other node referenced by other nodes, then #cells should be
# given.
# device, or any other node referenced by other nodes via 'phandle-array'
# properties, then #cells should be given.
#
# To understand the purpose of #cells, assume that some node has
#

View file

@ -32,8 +32,11 @@ properties:
required: false
description: interrupts for device
# Does not follow the 'type: phandle-array' scheme, but gets type-checked
# by the code. Declare it here just so that other bindings can make it
# 'required: true' easily if they want to.
interrupts-extended:
type: phandle-array
type: compound
required: false
description: extended interrupt specifier for device

View file

@ -320,20 +320,11 @@ class EDT:
for node in self.nodes:
# Node._init_props() depends on all Node objects having been
# created, due to 'type: phandle', so we run it separately.
# Property.val is set to the pointed-to Node instance for phandles,
# which must exist.
# created, due to 'type: phandle/phandles/phandle-array', so we run
# it separately. Property.val includes the pointed-to Node
# instance(s) for phandles, so the Node objects must exist.
node._init_props()
for node in self.nodes:
# These also depend on all Node objects having been created, and
# might also depend on all Node.props having been initialized
# (_init_clocks() does as of writing).
node._init_interrupts()
node._init_gpios()
node._init_pwms()
node._init_iochannels()
node._init_clocks()
def __repr__(self):
return "<EDT for '{}', binding directories '{}'>".format(
@ -418,26 +409,8 @@ class Node:
A list of aliases for the node. This is fetched from the /aliases node.
interrupts:
A list of Interrupt objects for the interrupts generated by the node
gpios:
A dictionary that maps the <prefix> part in '<prefix>-gpios' properties
to a list of GPIO objects (see the GPIO class).
For example, 'foo-gpios = <&gpio1 1 2 &gpio2 3 4>' makes gpios["foo"] a
list of two GPIO objects.
pwms:
A list of PWM objects, derived from the 'pwms' property. The list is
empty if the node has no 'pwms' property.
iochannels:
A list of IOChannel objects, derived from the 'io-channels' property. The
list is empty if the node has no 'io-channels' property.
clocks:
A list of Clock objects, derived from the 'clocks' property. The list is
empty if the node has no 'clocks' property.
A list of ControllerAndData objects for the interrupts generated by the
node
bus:
The bus for the node as specified in its binding, e.g. "i2c" or "spi".
@ -771,14 +744,16 @@ class Node:
return [self.edt._node2enode[node] for node in prop.to_nodes()]
if prop_type == "phandle-array":
# This property type only does a type check. No Property object is
# created for it.
# This type is a bit high-level for dtlib as it involves
# information from bindings and *-names properties, so there's no
# to_phandle_array() in dtlib. Do the type check ourselves.
if prop.type not in (TYPE_PHANDLE, TYPE_PHANDLES_AND_NUMS):
_err("expected property '{0}' in {1} in {2} to be assigned "
"with '{0} = < &foo 1 2 ... &bar 3 4 ... >' (a mix of "
"phandles and numbers), not '{3}'"
.format(name, node.path, node.dt.filename, prop))
return None
return self._standard_phandle_val_list(prop)
# prop_type == "compound". We have already checked that the 'type:'
# value is valid, in _check_binding().
@ -847,111 +822,71 @@ class Node:
self.interrupts = []
for controller_node, spec in _interrupts(node):
interrupt = Interrupt()
for controller_node, data in _interrupts(node):
interrupt = ControllerAndData()
interrupt.node = self
interrupt.controller = self.edt._node2enode[controller_node]
interrupt.data = self._named_cells(interrupt.controller, spec,
interrupt.data = self._named_cells(interrupt.controller, data,
"interrupt")
self.interrupts.append(interrupt)
_add_names(node, "interrupt", self.interrupts)
def _init_gpios(self):
# Initializes self.gpios
self.gpios = {}
for prefix, gpios in _gpios(self._node).items():
self.gpios[prefix] = []
for controller_node, spec in gpios:
gpio = GPIO()
gpio.node = self
gpio.controller = self.edt._node2enode[controller_node]
gpio.data = self._named_cells(gpio.controller, spec, "GPIO")
gpio.name = prefix
self.gpios[prefix].append(gpio)
def _init_clocks(self):
# Initializes self.clocks
self.clocks = self._simple_phandle_val_list("clock", Clock)
# Initialize Clock.frequency
for clock in self.clocks:
controller = clock.controller
if "fixed-clock" in controller.compats:
if "clock-frequency" not in controller.props:
_err("{!r} is a 'fixed-clock', but either lacks a "
"'clock-frequency' property or does not have "
"it specified in its binding".format(controller))
clock.frequency = controller.props["clock-frequency"].val
else:
clock.frequency = None
def _init_pwms(self):
# Initializes self.pwms
self.pwms = self._simple_phandle_val_list("pwm", PWM)
def _init_iochannels(self):
# Initializes self.iochannels
self.iochannels = self._simple_phandle_val_list("io-channel", IOChannel)
def _simple_phandle_val_list(self, name, cls):
# Helper for parsing properties like
def _standard_phandle_val_list(self, prop):
# Parses a property like
#
# <name>s = <phandle value phandle value ...>
# (e.g., gpios = <&foo 1 2 &bar 3 4>)
# (e.g., pwms = <&foo 1 2 &bar 3 4>)
#
# , where each phandle points to a node that has a
#
# #<name>-cells = <size>
#
# property that gives the number of cells in the value after the
# phandle. Also parses any
# phandle. These values are given names in #cells in the binding for
# the controller.
#
# Also parses any
#
# <name>-names = "...", "...", ...
#
# Arguments:
#
# name:
# The <name>, e.g. "clock" or "pwm". Note: no -s in the argument.
#
# cls:
# A class object. Instances of this class will be created and the
# 'node', 'controller', 'data', and 'name' fields initialized. See
# the documentation for e.g. the PWM class.
#
# Returns a list of 'cls' instances.
# Returns a list of ControllerAndData instances.
prop = self._node.props.get(name + "s")
if not prop:
return []
if prop.name.endswith("gpios"):
# There's some slight special-casing for *-gpios properties in that
# e.g. foo-gpios still maps to #gpio-cells rather than
# #foo-gpio-cells
basename = "gpio"
else:
# Strip -s. We've already checked that the property names end in -s
# in _check_binding().
basename = prop.name[:-1]
res = []
for controller_node, spec in _phandle_val_list(prop, name):
obj = cls()
obj.node = self
obj.controller = self.edt._node2enode[controller_node]
obj.data = self._named_cells(obj.controller, spec, name)
for controller_node, data in _phandle_val_list(prop, basename):
mapped_controller, mapped_data = \
_map_phandle_array_entry(prop.node, controller_node, data,
basename)
res.append(obj)
entry = ControllerAndData()
entry.node = self
entry.controller = self.edt._node2enode[mapped_controller]
entry.data = self._named_cells(entry.controller, mapped_data,
basename)
_add_names(self._node, name, res)
res.append(entry)
_add_names(self._node, basename, res)
return res
def _named_cells(self, controller, spec, controller_s):
# _init_{interrupts,gpios}() helper. Returns a dictionary that maps
# #cell names given in the binding for 'controller' to cell values.
# 'spec' is the raw interrupt/GPIO data, and 'controller_s' a string
# that gives the context (for error messages).
def _named_cells(self, controller, data, controller_s):
# Returns a dictionary that maps #cells names given in the binding for
# 'controller' to cell values. 'data' is the raw data, as a byte array,
# and 'controller_s' a string that gives the context (for error
# messages).
if not controller._binding:
_err("{} controller {!r} for {!r} lacks binding"
@ -968,13 +903,13 @@ class Node:
# '#clock-cells = <0>'.
cell_names = []
spec_list = to_nums(spec)
if len(spec_list) != len(cell_names):
data_list = to_nums(data)
if len(data_list) != len(cell_names):
_err("unexpected #cells length in binding for {!r} - {} instead "
"of {}".format(controller._node, len(cell_names),
len(spec_list)))
len(data_list)))
return dict(zip(cell_names, spec_list))
return dict(zip(cell_names, data_list))
def _set_instance_no(self):
# Initializes self.instance_no
@ -1019,28 +954,33 @@ class Register:
return "<Register, {}>".format(", ".join(fields))
class Interrupt:
class ControllerAndData:
"""
Represents an interrupt generated by a node.
Represents an entry in an 'interrupts' or 'type: phandle-array' property
value, e.g. <&ctrl-1 4 0> in
These attributes are available on Interrupt objects:
cs-gpios = <&ctrl-1 4 0 &ctrl-2 3 4>;
These attributes are available on ControllerAndData objects:
node:
The Node instance that generated the interrupt
name:
The name of the interrupt as given in the 'interrupt-names' property, or
None if there is no 'interrupt-names' property
The Node instance the property appears on
controller:
The Node instance for the controller the interrupt gets sent to. Any
'interrupt-map' is taken into account, so that this is the final
controller node.
The Node instance for the controller (e.g. the controller the interrupt
gets sent to for interrupts)
data:
A dictionary that maps names from the #cells portion of the binding to
cell values in the interrupt data. 'interrupts = <1 2>' might give
{"irq": 1, "level": 2}, for example.
A dictionary that maps names from the #cells key in the binding for
the controller to data values, e.g. {"pin": 4, "flags": 0} for the
example above.
'interrupts = <1 2>' might give {"irq": 1, "level": 2}.
name:
The name of the entry as given in
'interrupt-names'/'gpio-names'/'pwm-names'/etc., or None if there is no
*-names property
"""
def __repr__(self):
fields = []
@ -1048,151 +988,10 @@ class Interrupt:
if self.name is not None:
fields.append("name: " + self.name)
fields.append("target: {}".format(self.controller))
fields.append("controller: {}".format(self.controller))
fields.append("data: {}".format(self.data))
return "<Interrupt, {}>".format(", ".join(fields))
class GPIO:
"""
Represents a GPIO used by a node.
These attributes are available on GPIO objects:
node:
The Node instance that uses the GPIO
name:
The name of the gpio as extracted out of the "<NAME>-gpios" property. If
the property is just "gpios" than there is no name.
controller:
The Node instance for the controller of the GPIO
data:
A dictionary that maps names from the #cells portion of the binding to
cell values in the GPIO data. 'foo-gpios = <&gpioc 5 0>' might give
{"pin": 5, "flags": 0}, for example.
"""
def __repr__(self):
fields = []
if self.name is not None:
fields.append("name: " + self.name)
fields.append("target: {}".format(self.controller))
fields.append("data: {}".format(self.data))
return "<GPIO, {}>".format(", ".join(fields))
class Clock:
"""
Represents a clock used by a node.
These attributes are available on Clock objects:
node:
The Node instance that uses the clock
name:
The name of the clock as given in the 'clock-names' property, or
None if there is no 'clock-names' property
controller:
The Node instance for the controller of the clock.
frequency:
The frequency of the clock for fixed clocks ('fixed-clock' in
'compatible'), as an integer. Derived from the 'clock-frequency'
property. None if the clock is not a fixed clock.
data:
A dictionary that maps names from the #cells portion of the binding to
cell values in the clock data
"""
def __repr__(self):
fields = []
if self.name is not None:
fields.append("name: " + self.name)
if self.frequency is not None:
fields.append("frequency: {}".format(self.frequency))
fields.append("target: {}".format(self.controller))
fields.append("data: {}".format(self.data))
return "<Clock, {}>".format(", ".join(fields))
class PWM:
"""
Represents a PWM used by a node.
These attributes are available on PWM objects:
node:
The Node instance that uses the PWM
name:
The name of the pwm as given in the 'pwm-names' property, or the
node name if the 'pwm-names' property doesn't exist.
controller:
The Node instance for the controller of the PWM
data:
A dictionary that maps names from the #cells portion of the binding to
cell values in the PWM data. 'pwms = <&pwm 0 5000000>' might give
{"channel": 0, "period": 5000000}, for example.
"""
def __repr__(self):
fields = []
if self.name is not None:
fields.append("name: " + self.name)
fields.append("target: {}".format(self.controller))
fields.append("data: {}".format(self.data))
return "<PWM, {}>".format(", ".join(fields))
class IOChannel:
"""
Represents an IO channel used by a node, similar to the property used
by the Linux IIO bindings and described at:
https://www.kernel.org/doc/Documentation/devicetree/bindings/iio/iio-bindings.txt
These attributes are available on IO channel objects:
node:
The Node instance that uses the IO channel
name:
The name of the IO channel as given in the 'io-channel-names' property,
or the node name if the 'io-channel-names' property doesn't exist.
controller:
The Node instance for the controller of the IO channel
data:
A dictionary that maps names from the #cells portion of the binding to
cell values in the io-channel data. 'io-channels = <&adc 3>' might give
{"input": 3}, for example.
"""
def __repr__(self):
fields = []
if self.name is not None:
fields.append("name: " + self.name)
fields.append("target: {}".format(self.controller))
fields.append("data: {}".format(self.data))
return "<IOChannel, {}>".format(", ".join(fields))
return "<ControllerAndData, {}>".format(", ".join(fields))
class Property:
@ -1200,9 +999,9 @@ class Property:
Represents a property on a Node, as set in its DT node and with
additional info from the 'properties:' section of the binding.
Only properties mentioned in 'properties:' get created. Properties with
type 'phandle-array' or type 'compound' do not get Property instances.
These types only exist for type checking.
Only properties mentioned in 'properties:' get created. Properties of type
'compound' currently do not get Property instances, as I'm not sure what
information to store for them.
These attributes are available on Property objects:
@ -1223,10 +1022,16 @@ class Property:
The value of the property, with the format determined by the 'type:' key
from the binding.
For 'type: phandle' properties, this is the pointed-to Node instance.
- For 'type: int/array/string/string-array', 'val' is what you'd expect
(a Python integer or string, or a list of them)
For 'type: phandles' properties, this is a list of the pointed-to Node
instances.
- For 'type: phandle', 'val' is the pointed-to Node instance
- For 'type: phandles', 'val' is a list of the pointed-to Node
instances
- For 'type: phandle-array', 'val' is a list of ControllerAndData
instances. See the documentation for that class.
enum_index:
The index of the property's value in the 'enum:' list in the binding, or
@ -1254,23 +1059,28 @@ class EDTError(Exception):
def spi_dev_cs_gpio(node):
# Returns an SPI device's GPIO chip select if it exists, as a GPIO
# instance, and None otherwise. See
# Returns an SPI device's GPIO chip select if it exists, as a
# ControllerAndData instance, and None otherwise. See
# Documentation/devicetree/bindings/spi/spi-bus.txt in the Linux kernel.
if node.bus == "spi" and node.parent:
parent_cs = node.parent.gpios.get("cs")
if parent_cs:
# cs-gpios is indexed by the unit address
cs_index = node.regs[0].addr
if cs_index >= len(parent_cs):
_err("index from 'regs' in {!r} ({}) is >= number of cs-gpios "
"in {!r} ({})".format(
node, cs_index, node.parent, len(parent_cs)))
if not (node.bus == "spi" and node.parent and
"cs-gpios" in node.parent.props):
return None
return parent_cs[cs_index]
if not node.regs:
_err("{!r} needs a 'reg' property, to look up the chip select index "
"for SPI".format(node))
return None
parent_cs_lst = node.parent.props["cs-gpios"].val
# cs-gpios is indexed by the unit address
cs_index = node.regs[0].addr
if cs_index >= len(parent_cs_lst):
_err("index from 'regs' in {!r} ({}) is >= number of cs-gpios "
"in {!r} ({})".format(
node, cs_index, node.parent, len(parent_cs_lst)))
return parent_cs_lst[cs_index]
#
@ -1596,6 +1406,12 @@ def _check_prop_type_and_default(prop_name, prop_type, required, default,
"of {}".format(prop_name, binding_path, prop_type,
", ".join(ok_types)))
if prop_type == "phandle-array" and not prop_name.endswith("s"):
_err("'{}' in 'properties:' in {} is 'type: phandle-array', but its "
"name does not end in -s. This is required since property names "
"like '#pwm-cells' and 'pwm-names' get derived from 'pwms', for "
"example.".format(prop_name, binding_path))
# Check default
if default is None:
@ -1774,53 +1590,34 @@ def _map_interrupt(child, parent, child_spec):
def spec_len_fn(node):
# Can't use _address_cells() here, because it's the #address-cells
# property on 'node' itself that matters
return 4*(own_address_cells(node) + _interrupt_cells(node))
return own_address_cells(node) + _interrupt_cells(node)
parent, raw_spec = _map(
"interrupt", child, parent, _raw_unit_addr(child) + child_spec,
spec_len_fn)
spec_len_fn, require_controller=True)
# Strip the parent unit address part, if any
return (parent, raw_spec[4*own_address_cells(parent):])
def _gpios(node):
# Returns a dictionary that maps '<prefix>-gpios' prefixes to lists of
# (<controller>, <data>) tuples (possibly after mapping through an
# gpio-map). <controller> is a dtlib.Node.
res = {}
for name, prop in node.props.items():
if name.endswith("-gpios") or name == "gpios":
# Get the prefix from the property name:
# - gpios -> "" (deprecated, should have a prefix)
# - foo-gpios -> "foo"
# - etc.
prefix = name[:-5]
if prefix.endswith("-"):
prefix = prefix[:-1]
res[prefix] = [
_map_gpio(prop.node, controller, spec)
for controller, spec in _phandle_val_list(prop, "gpio")
]
return res
def _map_gpio(child, parent, child_spec):
def _map_phandle_array_entry(child, parent, child_spec, basename):
# Returns a (<controller>, <data>) tuple with the final destination after
# mapping through any 'gpio-map' properties. See _map_interrupt().
# mapping through any '<basename>-map' (e.g. gpio-map) properties. See
# _map_interrupt().
if "gpio-map" not in parent.props:
return (parent, child_spec)
def spec_len_fn(node):
prop_name = "#{}-cells".format(basename)
if prop_name not in node.props:
_err("expected '{}' property on {!r} (referenced by {!r})"
.format(prop_name, node, child))
return node.props[prop_name].to_num()
return _map("gpio", child, parent, child_spec,
lambda node: 4*_gpio_cells(node))
# Do not require <prefix>-controller for anything but interrupts for now
return _map(basename, child, parent, child_spec, spec_len_fn,
require_controller=False)
def _map(prefix, child, parent, child_spec, spec_len_fn):
def _map(prefix, child, parent, child_spec, spec_len_fn, require_controller):
# Common code for mapping through <prefix>-map properties, e.g.
# interrupt-map and gpio-map.
#
@ -1839,12 +1636,16 @@ def _map(prefix, child, parent, child_spec, spec_len_fn):
# e.g. <1 2> for 'foo-gpios = <&gpio1 1 2>'.
#
# spec_len_fn:
# Function called on a parent specified in a *-map property to get its
# *-cells value, e.g. #interrupt-cells
# Function called on a parent specified in a *-map property to get the
# length of the parent specifier (data after phandle in *-map), in cells
#
# require_controller:
# If True, the final controller node after mapping is required to have
# to have a <prefix>-controller property.
map_prop = parent.props.get(prefix + "-map")
if not map_prop:
if prefix + "-controller" not in parent.props:
if require_controller and prefix + "-controller" not in parent.props:
_err("expected '{}-controller' property on {!r} "
"(referenced by {!r})".format(prefix, parent, child))
@ -1870,9 +1671,9 @@ def _map(prefix, child, parent, child_spec, spec_len_fn):
# Parent specified in *-map
map_parent = parent.dt.phandle2node.get(phandle)
if not map_parent:
_err("bad phandle in " + repr(map_prop))
_err("bad phandle ({}) in {!r}".format(phandle, map_prop))
map_parent_spec_len = spec_len_fn(map_parent)
map_parent_spec_len = 4*spec_len_fn(map_parent)
if len(raw) < map_parent_spec_len:
_err("bad value for {!r}, missing/truncated parent data"
.format(map_prop))
@ -1886,9 +1687,10 @@ def _map(prefix, child, parent, child_spec, spec_len_fn):
prefix, child, parent, child_spec, parent_spec)
# Found match. Recursively map and return it.
return _map(prefix, parent, map_parent, parent_spec, spec_len_fn)
return _map(prefix, parent, map_parent, parent_spec, spec_len_fn,
require_controller)
_err("child data for {!r} ({}) does not appear in {!r}"
_err("child specifier for {!r} ({}) does not appear in {!r}"
.format(child, child_spec, map_prop))
@ -2050,12 +1852,6 @@ def _interrupt_cells(node):
return node.props["#interrupt-cells"].to_num()
def _gpio_cells(node):
if "#gpio-cells" not in node.props:
_err("{!r} lacks #gpio-cells".format(node))
return node.props["#gpio-cells"].to_num()
def _slice(node, prop_name, size):
# Splits node.props[prop_name].value into 'size'-sized chunks, returning a
# list of chunks. Raises EDTError if the length of the property is not

View file

@ -61,13 +61,9 @@ def main():
write_regs(node)
write_irqs(node)
for gpios in node.gpios.values():
write_phandle_val_list(node, gpios, "GPIO")
write_phandle_val_list(node, node.pwms, "PWM")
write_phandle_val_list(node, node.iochannels, "IO_CHANNEL")
write_props(node)
write_clocks(node)
write_spi_dev(node)
write_props(node)
write_bus(node)
write_existence_flags(node)
@ -143,7 +139,13 @@ def write_props(node):
if prop.name[0] == "#" or prop.name.endswith("-map"):
continue
# Skip phandles
# See write_clocks()
if prop.name == "clocks":
continue
# edtlib provides these as well (Property.val becomes an edtlib.Node
# and a list of edtlib.Nodes, respectively). Nothing is generated for
# them currently though.
if prop.type in {"phandle", "phandles"}:
continue
@ -174,6 +176,8 @@ def write_props(node):
elif prop.type == "uint8-array":
out_dev(node, ident,
"{ " + ", ".join("0x{:02x}".format(b) for b in prop.val) + " }")
elif prop.type == "phandle-array":
write_phandle_val_list(prop)
# Generate DT_..._ENUM if there's an 'enum:' key in the binding
if prop.enum_index is not None:
@ -452,59 +456,61 @@ def write_spi_dev(node):
cs_gpio = edtlib.spi_dev_cs_gpio(node)
if cs_gpio is not None:
write_phandle_val_list_entry(node, cs_gpio, None, "GPIO")
write_phandle_val_list_entry(node, cs_gpio, None, "CS_GPIOS")
def write_phandle_val_list(node, entries, ident):
def write_phandle_val_list(prop):
# Writes output for a phandle/value list, e.g.
#
# pwms = <&pwm-ctrl-1 10 20
# &pwm-ctrl-2 30 40>;
#
# node:
# Device used to generate device prefixes (see 'ident' below)
# prop:
# phandle/value Property instance.
#
# entries:
# List of entries (two for 'pwms' above). This might be a list of
# edtlib.PWM instances, for example. If only one entry is given it
# does not have a suffix '_0', and the '_COUNT' and group initializer
# are not emitted.
# If only one entry appears in 'prop' (the example above has two), the
# generated identifier won't get a '_0' suffix, and the '_COUNT' and
# group initializer are skipped too.
#
# ident:
# Base identifier. For example, "PWM" generates output like this:
# The base identifier is derived from the property name. For example, 'pwms = ...'
# generates output like this:
#
# #define <device prefix>_PWMS_CONTROLLER_0 "PWM_0" (name taken from 'label = ...')
# #define <device prefix>_PWMS_CHANNEL_0 123 (name taken from #cells in binding)
# #define <device prefix>_PWMS_0 {"PWM_0", 123}
# #define <device prefix>_PWMS_CONTROLLER_1 "PWM_1"
# #define <device prefix>_PWMS_CHANNEL_1 456
# #define <device prefix>_PWMS_1 {"PWM_1", 456}
# #define <device prefix>_PWMS_COUNT 2
# #define <device prefix>_PWMS {<device prefix>_PWMS_0, <device prefix>_PWMS_1}
# ...
#
# Note: Do not add an "S" to 'ident'. It's added automatically, which
# forces consistency.
# #define <device prefix>_PWMS_CONTROLLER_0 "PWM_0" (name taken from 'label = ...')
# #define <device prefix>_PWMS_CHANNEL_0 123 (name taken from #cells in binding)
# #define <device prefix>_PWMS_0 {"PWM_0", 123}
# #define <device prefix>_PWMS_CONTROLLER_1 "PWM_1"
# #define <device prefix>_PWMS_CHANNEL_1 456
# #define <device prefix>_PWMS_1 {"PWM_1", 456}
# #define <device prefix>_PWMS_COUNT 2
# #define <device prefix>_PWMS {<device prefix>_PWMS_0, <device prefix>_PWMS_1}
# ...
# pwms -> PWMS
# foo-gpios -> FOO_GPIOS
ident = str2ident(prop.name)
initializer_vals = []
for i, entry in enumerate(entries):
for i, entry in enumerate(prop.val):
initializer_vals.append(write_phandle_val_list_entry(
node, entry, i if len(entries) > 1 else None, ident))
if len(entries) > 1:
out_dev(node, ident + "S_COUNT", len(initializer_vals))
out_dev(node, ident + "S", "{" + ", ".join(initializer_vals) + "}")
prop.node, entry, i if len(prop.val) > 1 else None, ident))
if len(prop.val) > 1:
out_dev(prop.node, ident + "_COUNT", len(initializer_vals))
out_dev(prop.node, ident, "{" + ", ".join(initializer_vals) + "}")
def write_phandle_val_list_entry(node, entry, i, ident):
# write_phandle_val_list() helper. We could get rid of it if it wasn't for
# write_spi_dev(). Adds 'i' as an index to identifiers unless it's None.
#
# 'entry' is an edtlib.ControllerAndData instance.
#
# Returns the identifier for the macro that provides the
# initializer for the entire entry.
initializer_vals = []
if entry.controller.label is not None:
ctrl_ident = ident + "S_CONTROLLER" # e.g. PWMS_CONTROLLER
ctrl_ident = ident + "_CONTROLLER" # e.g. PWMS_CONTROLLER
if entry.name:
ctrl_ident = str2ident(entry.name) + "_" + ctrl_ident
# Ugly backwards compatibility hack. Only add the index if there's
@ -515,7 +521,7 @@ def write_phandle_val_list_entry(node, entry, i, ident):
out_dev_s(node, ctrl_ident, entry.controller.label)
for cell, val in entry.data.items():
cell_ident = ident + "S_" + str2ident(cell) # e.g. PWMS_CHANNEL
cell_ident = ident + "_" + str2ident(cell) # e.g. PWMS_CHANNEL
if entry.name:
# From e.g. 'pwm-names = ...'
cell_ident = str2ident(entry.name) + "_" + cell_ident
@ -526,7 +532,7 @@ def write_phandle_val_list_entry(node, entry, i, ident):
initializer_vals += entry.data.values()
initializer_ident = ident + "S"
initializer_ident = ident
if entry.name:
initializer_ident += "_" + str2ident(entry.name)
if i is not None:
@ -536,25 +542,42 @@ def write_phandle_val_list_entry(node, entry, i, ident):
def write_clocks(node):
# Writes clock controller and clock data for the clock in the node's
# 'clock' property
# Writes clock information.
#
# Most of this ought to be handled in write_props(), but the identifiers
# that get generated for 'clocks' are inconsistent with the with other
# 'phandle-array' properties.
#
# See https://github.com/zephyrproject-rtos/zephyr/pull/19327#issuecomment-534081845.
for clock_i, clock in enumerate(node.clocks):
if clock.controller.label is not None:
out_dev_s(node, "CLOCK_CONTROLLER", clock.controller.label)
if "clocks" not in node.props:
return
if clock.frequency is not None:
out_dev(node, "CLOCKS_CLOCK_FREQUENCY", clock.frequency)
for clock_i, clock in enumerate(node.props["clocks"].val):
controller = clock.controller
for spec, val in clock.data.items():
if controller.label is not None:
out_dev_s(node, "CLOCK_CONTROLLER", controller.label)
for name, val in clock.data.items():
if clock_i == 0:
clk_name_alias = "CLOCK_" + str2ident(spec)
clk_name_alias = "CLOCK_" + str2ident(name)
else:
clk_name_alias = None
out_dev(node, "CLOCK_{}_{}".format(str2ident(spec), clock_i), val,
out_dev(node, "CLOCK_{}_{}".format(str2ident(name), clock_i), val,
name_alias=clk_name_alias)
if "fixed-clock" not in controller.compats:
continue
if "clock-frequency" not in controller.props:
err("{!r} is a 'fixed-clock' but lacks a 'clock-frequency' "
"property".format(controller))
out_dev(node, "CLOCKS_CLOCK_FREQUENCY",
controller.props["clock-frequency"].val)
def str2ident(s):
# Converts 's' to a form suitable for (part of) an identifier

View file

@ -1,9 +0,0 @@
# SPDX-License-Identifier: BSD-3-Clause
title: Clock source with one cell
description: Clock source with one cell
compatible: "clock-one-cell"
"#cells":
- one

View file

@ -1,10 +0,0 @@
# SPDX-License-Identifier: BSD-3-Clause
title: Clock source with two cells
description: Clock source with two cells
compatible: "clock-two-cell"
"#cells":
- one
- two

View file

@ -1,4 +1,4 @@
# A file that mentions a 'compatible' string without actually implementing it.
# Used to check for issues with how we optimize binding loading.
# clock-one-cell
# props

View file

@ -1,10 +0,0 @@
# SPDX-License-Identifier: BSD-3-Clause
title: Fixed clock
description: Fixed clock
compatible: "fixed-clock"
properties:
clock-frequency:
type: int

View file

@ -1,9 +0,0 @@
# SPDX-License-Identifier: BSD-3-Clause
title: GPIO controller with one cell
description: GPIO controller with one cell
compatible: "gpio-one-cell"
"#cells":
- one

View file

@ -1,10 +0,0 @@
# SPDX-License-Identifier: BSD-3-Clause
title: GPIO controller with two cells
description: GPIO controller with two cells
compatible: "gpio-two-cell"
"#cells":
- one
- two

View file

@ -0,0 +1,9 @@
# SPDX-License-Identifier: BSD-3-Clause
title: GPIO destination for mapping test
description: GPIO destination for mapping test
compatible: "gpio-dst"
"#cells":
- val

View file

@ -0,0 +1,10 @@
# SPDX-License-Identifier: BSD-3-Clause
title: GPIO source for mapping test
description: GPIO source for mapping test
compatible: "gpio-src"
properties:
foo-gpios:
type: phandle-array

View file

@ -1,11 +0,0 @@
#
# SPDX-License-Identifier: BSD-3-Clause
#
title: IO channel with one cell
description: IO channel with one cell
compatible: "io-channel"
"#cells":
- one

View file

@ -0,0 +1,9 @@
# SPDX-License-Identifier: BSD-3-Clause
title: Controller with one data value
description: Controller with one data value
compatible: "phandle-array-controller-1"
"#cells":
- one

View file

@ -0,0 +1,10 @@
# SPDX-License-Identifier: BSD-3-Clause
title: Controller with two data values
description: Controller with two data values
compatible: "phandle-array-controller-2"
"#cells":
- one
- two

View file

@ -35,5 +35,11 @@ properties:
phandle-refs:
type: phandles
phandle-refs-and-vals:
phandle-array-foos:
type: phandle-array
# There's some slight special-casing for GPIOs in that 'foo-gpios = ...'
# gets resolved to #gpio-cells rather than #foo-gpio-cells, so test that
# too
foo-gpios:
type: phandle-array

View file

@ -1,6 +0,0 @@
# SPDX-License-Identifier: BSD-3-Clause
title: PWM source with zero cells
description: PWM source with zero cells
compatible: "pwm-zero-cell"

View file

@ -1,9 +0,0 @@
# SPDX-License-Identifier: BSD-3-Clause
title: PWM source with one cell
description: PWM source with one cell
compatible: "pwm-one-cell"
"#cells":
- one

View file

@ -123,94 +123,6 @@
};
};
//
// GPIOS
//
gpio-test {
controller-1 {
compatible = "gpio-two-cell";
#gpio-cells = <2>;
gpio-controller;
};
node {
gpios = <&{/gpio-test/controller-0} 1
&{/gpio-test/controller-1} 2 3>;
foo-gpios = <&{/gpio-test/controller-1} 4 5>;
bar-gpios = <&{/gpio-test/controller-1} 6 7>;
};
// Putting this controller last gives us some coverage for ordering
// issues during initialization
controller-0 {
compatible = "gpio-one-cell";
#gpio-cells = <1>;
gpio-controller;
};
};
//
// Clocks
//
clock-test {
fixed-clock {
// 'fixed-clock' is currently special-cased in the code
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <123>;
};
clock-1 {
compatible = "clock-one-cell";
#clock-cells = <1>;
};
clock-2 {
compatible = "clock-two-cell";
#clock-cells = <2>;
};
node {
clocks = <&{/clock-test/fixed-clock}
&{/clock-test/clock-1} 1
&{/clock-test/clock-2} 1 2>;
clock-names = "fixed", "one-cell", "two-cell";
};
};
//
// PWMs
//
pwm-test {
pwm-0 {
compatible = "pwm-zero-cell";
#pwm-cells = <0>;
};
pwm-1 {
compatible = "pwm-one-cell";
#pwm-cells = <1>;
};
node {
pwms = <&{/pwm-test/pwm-0}
&{/pwm-test/pwm-1} 1>;
pwm-names = "zero-cell", "one-cell";
};
};
//
// IO channels
//
// Lots of common code with PWMs and clocks, so just test the basics
io-channel-test {
io-channel {
compatible = "io-channel";
#io-channel-cells = <1>;
};
node {
io-channels = <&{/io-channel-test/io-channel} 1>;
io-channel-names = "io-channel";
};
};
//
// 'reg'
//
@ -274,7 +186,7 @@
};
//
// For testing Device.parent and Device.children
// For testing Node.parent and Node.children
//
parent {
@ -299,7 +211,7 @@
};
//
// For testing Device.props (derived from 'properties:' in the binding)
// For testing Node.props (derived from 'properties:' in the binding)
//
props {
@ -310,19 +222,49 @@
uint8-array = [ 12 34 ];
string = "foo";
string-array = "foo", "bar", "baz";
phandle-ref = < &{/props/node} >;
phandle-refs = < &{/props/node} &{/props/node2} >;
phandle-refs-and-vals = < &{/props/node} 1 &{/props/node2} 2 >;
phandle-ref = < &{/props/ctrl-1} >;
phandle-refs = < &{/props/ctrl-1} &{/props/ctrl-2} >;
phandle-array-foos = < &{/props/ctrl-1} 1 &{/props/ctrl-2} 2 3 >;
foo-gpios = < &{/props/ctrl-1} 1 >;
node {
ctrl-1 {
compatible = "phandle-array-controller-1";
#phandle-array-foo-cells = <1>;
#gpio-cells = <1>;
};
node2 {
ctrl-2 {
compatible = "phandle-array-controller-2";
#phandle-array-foo-cells = <2>;
};
};
//
// For testing Device.props with 'default:' values in binding
// Test <prefix>-map, via gpio-map
//
gpio-map {
source {
compatible = "gpio-src";
foo-gpios = <&{/gpio-map/connector} 3 4
&{/gpio-map/connector} 1 2>;
};
connector {
#gpio-cells = <2>;
// Use different data lengths for source and
// destination to make it a bit trickier
gpio-map = <1 2 &{/gpio-map/destination} 5
3 4 &{/gpio-map/destination} 6>;
};
destination {
compatible = "gpio-dst";
gpio-controller;
#gpio-cells = <1>;
};
};
//
// For testing Node.props with 'default:' values in binding
//
defaults {

View file

@ -41,47 +41,19 @@ def run():
#
verify_streq(edt.get_node("/interrupt-parent-test/node").interrupts,
"[<Interrupt, name: foo, target: <Node /interrupt-parent-test/controller in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 1, 'two': 2, 'three': 3}>, <Interrupt, name: bar, target: <Node /interrupt-parent-test/controller in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 4, 'two': 5, 'three': 6}>]")
"[<ControllerAndData, name: foo, controller: <Node /interrupt-parent-test/controller in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 1, 'two': 2, 'three': 3}>, <ControllerAndData, name: bar, controller: <Node /interrupt-parent-test/controller in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 4, 'two': 5, 'three': 6}>]")
verify_streq(edt.get_node("/interrupts-extended-test/node").interrupts,
"[<Interrupt, target: <Node /interrupts-extended-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: {'one': 1}>, <Interrupt, target: <Node /interrupts-extended-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: {'one': 2, 'two': 3}>, <Interrupt, target: <Node /interrupts-extended-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 4, 'two': 5, 'three': 6}>]")
"[<ControllerAndData, controller: <Node /interrupts-extended-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: {'one': 1}>, <ControllerAndData, controller: <Node /interrupts-extended-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: {'one': 2, 'two': 3}>, <ControllerAndData, controller: <Node /interrupts-extended-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 4, 'two': 5, 'three': 6}>]")
verify_streq(edt.get_node("/interrupt-map-test/node@0").interrupts,
"[<Interrupt, target: <Node /interrupt-map-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: {'one': 0}>, <Interrupt, target: <Node /interrupt-map-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: {'one': 0, 'two': 1}>, <Interrupt, target: <Node /interrupt-map-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 0, 'two': 0, 'three': 2}>]")
"[<ControllerAndData, controller: <Node /interrupt-map-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: {'one': 0}>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: {'one': 0, 'two': 1}>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 0, 'two': 0, 'three': 2}>]")
verify_streq(edt.get_node("/interrupt-map-test/node@1").interrupts,
"[<Interrupt, target: <Node /interrupt-map-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: {'one': 3}>, <Interrupt, target: <Node /interrupt-map-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: {'one': 0, 'two': 4}>, <Interrupt, target: <Node /interrupt-map-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 0, 'two': 0, 'three': 5}>]")
"[<ControllerAndData, controller: <Node /interrupt-map-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: {'one': 3}>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: {'one': 0, 'two': 4}>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: {'one': 0, 'two': 0, 'three': 5}>]")
verify_streq(edt.get_node("/interrupt-map-bitops-test/node@70000000E").interrupts,
"[<Interrupt, target: <Node /interrupt-map-bitops-test/controller in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: {'one': 3, 'two': 2}>]")
#
# Test GPIOs
#
verify_streq(edt.get_node("/gpio-test/node").gpios,
"{'': [<GPIO, name: , target: <Node /gpio-test/controller-0 in 'test.dts', binding test-bindings/gpio-1-cell.yaml>, data: {'one': 1}>, <GPIO, name: , target: <Node /gpio-test/controller-1 in 'test.dts', binding test-bindings/gpio-2-cell.yaml>, data: {'one': 2, 'two': 3}>], 'foo': [<GPIO, name: foo, target: <Node /gpio-test/controller-1 in 'test.dts', binding test-bindings/gpio-2-cell.yaml>, data: {'one': 4, 'two': 5}>], 'bar': [<GPIO, name: bar, target: <Node /gpio-test/controller-1 in 'test.dts', binding test-bindings/gpio-2-cell.yaml>, data: {'one': 6, 'two': 7}>]}")
#
# Test clocks
#
verify_streq(edt.get_node("/clock-test/node").clocks,
"[<Clock, name: fixed, frequency: 123, target: <Node /clock-test/fixed-clock in 'test.dts', binding test-bindings/fixed-clock.yaml>, data: {}>, <Clock, name: one-cell, target: <Node /clock-test/clock-1 in 'test.dts', binding test-bindings/clock-1-cell.yaml>, data: {'one': 1}>, <Clock, name: two-cell, target: <Node /clock-test/clock-2 in 'test.dts', binding test-bindings/clock-2-cell.yaml>, data: {'one': 1, 'two': 2}>]")
#
# Test PWMs
#
verify_streq(edt.get_node("/pwm-test/node").pwms,
"[<PWM, name: zero-cell, target: <Node /pwm-test/pwm-0 in 'test.dts', binding test-bindings/pwm-0-cell.yaml>, data: {}>, <PWM, name: one-cell, target: <Node /pwm-test/pwm-1 in 'test.dts', binding test-bindings/pwm-1-cell.yaml>, data: {'one': 1}>]")
#
# Test IO channels
#
verify_streq(edt.get_node("/io-channel-test/node").iochannels,
"[<IOChannel, name: io-channel, target: <Node /io-channel-test/io-channel in 'test.dts', binding test-bindings/io-channel.yaml>, data: {'one': 1}>]")
"[<ControllerAndData, controller: <Node /interrupt-map-bitops-test/controller in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: {'one': 3, 'two': 2}>]")
#
# Test 'reg'
@ -100,7 +72,7 @@ def run():
"[<Register, addr: 0x30000000200000001, size: 0x1>]")
#
# Test Device.parent and Device.children
# Test Node.parent and Node.children
#
verify_eq(edt.get_node("/").parent, None)
@ -164,11 +136,48 @@ def run():
"{'child-prop': <Property, name: child-prop, type: int, value: 3>}")
#
# Test Device.property (derived from DT and 'properties:' in the binding)
# Test Node.props (derived from DT and 'properties:' in the binding)
#
verify_streq(edt.get_node("/props").props,
r"{'nonexistent-boolean': <Property, name: nonexistent-boolean, type: boolean, value: False>, 'existent-boolean': <Property, name: existent-boolean, type: boolean, value: True>, 'int': <Property, name: int, type: int, value: 1>, 'array': <Property, name: array, type: array, value: [1, 2, 3]>, 'uint8-array': <Property, name: uint8-array, type: uint8-array, value: b'\x124'>, 'string': <Property, name: string, type: string, value: 'foo'>, 'string-array': <Property, name: string-array, type: string-array, value: ['foo', 'bar', 'baz']>, 'phandle-ref': <Property, name: phandle-ref, type: phandle, value: <Node /props/node in 'test.dts', no binding>>, 'phandle-refs': <Property, name: phandle-refs, type: phandles, value: [<Node /props/node in 'test.dts', no binding>, <Node /props/node2 in 'test.dts', no binding>]>}")
verify_streq(edt.get_node("/props").props["int"],
"<Property, name: int, type: int, value: 1>")
verify_streq(edt.get_node("/props").props["existent-boolean"],
"<Property, name: existent-boolean, type: boolean, value: True>")
verify_streq(edt.get_node("/props").props["nonexistent-boolean"],
"<Property, name: nonexistent-boolean, type: boolean, value: False>")
verify_streq(edt.get_node("/props").props["array"],
"<Property, name: array, type: array, value: [1, 2, 3]>")
verify_streq(edt.get_node("/props").props["uint8-array"],
r"<Property, name: uint8-array, type: uint8-array, value: b'\x124'>")
verify_streq(edt.get_node("/props").props["string"],
"<Property, name: string, type: string, value: 'foo'>")
verify_streq(edt.get_node("/props").props["string-array"],
"<Property, name: string-array, type: string-array, value: ['foo', 'bar', 'baz']>")
verify_streq(edt.get_node("/props").props["phandle-ref"],
"<Property, name: phandle-ref, type: phandle, value: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>>")
verify_streq(edt.get_node("/props").props["phandle-refs"],
"<Property, name: phandle-refs, type: phandles, value: [<Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, <Node /props/ctrl-2 in 'test.dts', binding test-bindings/phandle-array-controller-2.yaml>]>")
verify_streq(edt.get_node("/props").props["phandle-array-foos"],
"<Property, name: phandle-array-foos, type: phandle-array, value: [<ControllerAndData, controller: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, data: {'one': 1}>, <ControllerAndData, controller: <Node /props/ctrl-2 in 'test.dts', binding test-bindings/phandle-array-controller-2.yaml>, data: {'one': 2, 'two': 3}>]>")
verify_streq(edt.get_node("/props").props["foo-gpios"],
"<Property, name: foo-gpios, type: phandle-array, value: [<ControllerAndData, controller: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, data: {'one': 1}>]>")
#
# Test <prefix>-map, via gpio-map (the most common case)
#
verify_streq(edt.get_node("/gpio-map/source").props["foo-gpios"],
"<Property, name: foo-gpios, type: phandle-array, value: [<ControllerAndData, controller: <Node /gpio-map/destination in 'test.dts', binding test-bindings/gpio-dst.yaml>, data: {'val': 6}>, <ControllerAndData, controller: <Node /gpio-map/destination in 'test.dts', binding test-bindings/gpio-dst.yaml>, data: {'val': 5}>]>")
#
# Test property default values given in bindings