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:
parent
d82f76a0bb
commit
b97ed9e4b4
20 changed files with 390 additions and 618 deletions
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
title: Fixed clock
|
||||
description: Fixed clock
|
||||
|
||||
compatible: "fixed-clock"
|
||||
|
||||
properties:
|
||||
clock-frequency:
|
||||
type: int
|
|
@ -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
|
|
@ -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
|
9
scripts/dts/test-bindings/gpio-dst.yaml
Normal file
9
scripts/dts/test-bindings/gpio-dst.yaml
Normal 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
|
10
scripts/dts/test-bindings/gpio-src.yaml
Normal file
10
scripts/dts/test-bindings/gpio-src.yaml
Normal 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
|
|
@ -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
|
|
@ -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
|
10
scripts/dts/test-bindings/phandle-array-controller-2.yaml
Normal file
10
scripts/dts/test-bindings/phandle-array-controller-2.yaml
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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"
|
|
@ -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
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue