scripts: edtlib: Support nested nodes on buses
For the following devicetree, view 'nested' as being on the bus. Previously, only 'node' was considered to be on the bus. some-bus { compatible = "foo,bus-controller"; node { nested { compatible = "foo,device-on-bus"; }; }; }; In practice, this means that a 'bus:' key in the binding for 'foo,bus-controller' will now get matched up to an 'on-bus:' key in the binding for 'foo,device-on-bus'. Change the meaning of Node.bus and add two new attributes Node.on_bus and Node.bus_node, with these meanings: Node.bus: The bus type (as a string) if the node is a bus controller, and None otherwise Node.on_bus: The bus type (as a string) if the node appears on a bus, and None otherwise. The bus type is determined from the closest parent that's a bus controller. Node.bus_node: The node for the bus controller if the node appears on a bus, and None otherwise It's a bit redundant to have both Node.bus_node and Node.on_bus, since Node.on_bus is the same as Node.bus_node.bus, but Node.on_bus is pretty handy to save some None checks. Also update gen_defines.py to use Node.on_bus and Node.bus_node instead of Node.parent wherever the code deals with buses. Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
This commit is contained in:
parent
527343dfce
commit
5e55eda30e
4 changed files with 91 additions and 48 deletions
|
@ -275,19 +275,19 @@ class EDT:
|
|||
binding = self._merge_included_bindings(binding, binding_path)
|
||||
self._check_binding(binding, binding_path)
|
||||
|
||||
bus = _binding_bus(binding)
|
||||
on_bus = _on_bus_from_binding(binding)
|
||||
|
||||
# Do not allow two different bindings to have the same
|
||||
# 'compatible:'/'on-bus:' combo
|
||||
old_binding = self._compat2binding.get((binding_compat, bus))
|
||||
old_binding = self._compat2binding.get((binding_compat, on_bus))
|
||||
if old_binding:
|
||||
msg = "both {} and {} have 'compatible: {}'".format(
|
||||
old_binding[1], binding_path, binding_compat)
|
||||
if bus is not None:
|
||||
msg += " and 'on-bus: {}'".format(bus)
|
||||
if on_bus is not None:
|
||||
msg += " and 'on-bus: {}'".format(on_bus)
|
||||
_err(msg)
|
||||
|
||||
self._compat2binding[binding_compat, bus] = (binding, binding_path)
|
||||
self._compat2binding[binding_compat, on_bus] = (binding, binding_path)
|
||||
|
||||
def _binding_compat(self, binding, binding_path):
|
||||
# Returns the string listed in 'compatible:' in 'binding', or None if
|
||||
|
@ -440,6 +440,7 @@ class EDT:
|
|||
node = Node()
|
||||
node.edt = self
|
||||
node._node = dt_node
|
||||
node.bus_node = node._bus_node()
|
||||
node._init_binding()
|
||||
node._init_regs()
|
||||
node._set_instance_no()
|
||||
|
@ -731,8 +732,19 @@ class Node:
|
|||
pinctrl-<index> properties.
|
||||
|
||||
bus:
|
||||
The bus for the node as specified in its binding, e.g. "i2c" or "spi".
|
||||
None if the binding doesn't specify a bus.
|
||||
If the node is a bus node (has a 'bus:' key in its binding), then this
|
||||
attribute holds the bus type, e.g. "i2c" or "spi". If the node is not a
|
||||
bus node, then this attribute is None.
|
||||
|
||||
on_bus:
|
||||
The bus the node appears on, e.g. "i2c" or "spi". The bus is determined
|
||||
by searching upwards for a parent node whose binding has a 'bus:' key,
|
||||
returning the value of the first 'bus:' key found. If none of the node's
|
||||
parents has a 'bus:' key, this attribute is None.
|
||||
|
||||
bus_node:
|
||||
Like on_bus, but contains the Node for the bus controller, or None if the
|
||||
node is not on a bus.
|
||||
|
||||
flash_controller:
|
||||
The flash controller for the node. Only meaningful for nodes representing
|
||||
|
@ -828,7 +840,29 @@ class Node:
|
|||
@property
|
||||
def bus(self):
|
||||
"See the class docstring"
|
||||
return _binding_bus(self._binding)
|
||||
binding = self._binding
|
||||
if not binding:
|
||||
return None
|
||||
|
||||
if "bus" in binding:
|
||||
return binding["bus"]
|
||||
|
||||
# Legacy key
|
||||
if "child-bus" in binding:
|
||||
return binding["child-bus"]
|
||||
|
||||
# Legacy key
|
||||
if "child" in binding:
|
||||
# _check_binding() has checked that the "bus" key exists
|
||||
return binding["child"]["bus"]
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def on_bus(self):
|
||||
"See the class docstring"
|
||||
bus_node = self.bus_node
|
||||
return bus_node.bus if bus_node else None
|
||||
|
||||
@property
|
||||
def flash_controller(self):
|
||||
|
@ -870,14 +904,14 @@ class Node:
|
|||
|
||||
if "compatible" in self._node.props:
|
||||
self.compats = self._node.props["compatible"].to_strings()
|
||||
bus = self._bus_from_parent_binding()
|
||||
on_bus = self.on_bus
|
||||
|
||||
for compat in self.compats:
|
||||
if (compat, bus) in self.edt._compat2binding:
|
||||
if (compat, on_bus) in self.edt._compat2binding:
|
||||
# Binding found
|
||||
self.matching_compat = compat
|
||||
self._binding, self.binding_path = \
|
||||
self.edt._compat2binding[compat, bus]
|
||||
self.edt._compat2binding[compat, on_bus]
|
||||
|
||||
return
|
||||
else:
|
||||
|
@ -920,31 +954,20 @@ class Node:
|
|||
|
||||
return None
|
||||
|
||||
def _bus_from_parent_binding(self):
|
||||
# _init_binding() helper. Returns the bus specified by 'bus:' in the
|
||||
# parent binding (or the legacy 'child-bus:'/'child: bus:'), or None if
|
||||
# missing.
|
||||
def _bus_node(self):
|
||||
# Returns the value for self.bus_node. Relies on parent nodes being
|
||||
# initialized before their children.
|
||||
|
||||
if not self.parent:
|
||||
# This is the root node
|
||||
return None
|
||||
|
||||
binding = self.parent._binding
|
||||
if not binding:
|
||||
return None
|
||||
if self.parent.bus:
|
||||
# The parent node is a bus node
|
||||
return self.parent
|
||||
|
||||
if "bus" in binding:
|
||||
return binding["bus"]
|
||||
|
||||
# Legacy key
|
||||
if "child-bus" in binding:
|
||||
return binding["child-bus"]
|
||||
|
||||
# Legacy key
|
||||
if "child" in binding:
|
||||
# _check_binding() has checked that the "bus" key exists
|
||||
return binding["child"]["bus"]
|
||||
|
||||
return None
|
||||
# Same bus node as parent (possibly None)
|
||||
return self.parent.bus_node
|
||||
|
||||
def _init_props(self):
|
||||
# Creates self.props. See the class docstring. Also checks that all
|
||||
|
@ -1453,22 +1476,21 @@ def spi_dev_cs_gpio(node):
|
|||
# ControllerAndData instance, and None otherwise. See
|
||||
# Documentation/devicetree/bindings/spi/spi-bus.txt in the Linux kernel.
|
||||
|
||||
if not (node.bus == "spi" and node.parent and
|
||||
"cs-gpios" in node.parent.props):
|
||||
if not (node.on_bus == "spi" and "cs-gpios" in node.bus_node.props):
|
||||
return None
|
||||
|
||||
if not node.regs:
|
||||
_err("{!r} needs a 'reg' property, to look up the chip select index "
|
||||
"for SPI".format(node))
|
||||
|
||||
parent_cs_lst = node.parent.props["cs-gpios"].val
|
||||
parent_cs_lst = node.bus_node.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)))
|
||||
node, cs_index, node.bus_node, len(parent_cs_lst)))
|
||||
|
||||
return parent_cs_lst[cs_index]
|
||||
|
||||
|
@ -1503,7 +1525,7 @@ def _binding_paths(bindings_dirs):
|
|||
return binding_paths
|
||||
|
||||
|
||||
def _binding_bus(binding):
|
||||
def _on_bus_from_binding(binding):
|
||||
# Returns the bus specified by 'on-bus:' in the binding (or the
|
||||
# legacy 'parent-bus:' and 'parent: bus:'), or None if missing
|
||||
|
||||
|
|
|
@ -244,18 +244,18 @@ def write_props(node):
|
|||
def write_bus(node):
|
||||
# Generate bus-related #defines
|
||||
|
||||
if not node.bus:
|
||||
if not node.bus_node:
|
||||
return
|
||||
|
||||
if node.parent.label is None:
|
||||
err("missing 'label' property on {!r}".format(node.parent))
|
||||
if node.bus_node.label is None:
|
||||
err("missing 'label' property on bus node {!r}".format(node.bus_node))
|
||||
|
||||
# #define DT_<DEV-IDENT>_BUS_NAME <BUS-LABEL>
|
||||
out_dev_s(node, "BUS_NAME", str2ident(node.parent.label))
|
||||
out_dev_s(node, "BUS_NAME", str2ident(node.bus_node.label))
|
||||
|
||||
for compat in node.compats:
|
||||
# #define DT_<COMPAT>_BUS_<BUS-TYPE> 1
|
||||
out("{}_BUS_{}".format(str2ident(compat), str2ident(node.bus)), 1)
|
||||
out("{}_BUS_{}".format(str2ident(compat), str2ident(node.on_bus)), 1)
|
||||
|
||||
|
||||
def write_existence_flags(node):
|
||||
|
@ -307,9 +307,9 @@ def dev_ident(node):
|
|||
|
||||
ident = ""
|
||||
|
||||
if node.bus:
|
||||
if node.bus_node:
|
||||
ident += "{}_{:X}_".format(
|
||||
str2ident(node.parent.matching_compat), node.parent.unit_addr)
|
||||
str2ident(node.bus_node.matching_compat), node.bus_node.unit_addr)
|
||||
|
||||
ident += "{}_".format(str2ident(node.matching_compat))
|
||||
|
||||
|
@ -415,8 +415,8 @@ def write_flash_node(edt):
|
|||
err("expected zephyr,flash to have a single register, has {}"
|
||||
.format(len(node.regs)))
|
||||
|
||||
if node.bus == "spi" and len(node.parent.regs) == 2:
|
||||
reg = node.parent.regs[1] # QSPI flash
|
||||
if node.on_bus == "spi" and len(node.bus_node.regs) == 2:
|
||||
reg = node.bus_node.regs[1] # QSPI flash
|
||||
else:
|
||||
reg = node.regs[0]
|
||||
|
||||
|
|
|
@ -298,12 +298,15 @@
|
|||
//
|
||||
|
||||
buses {
|
||||
// The nodes below will map to different bindings since they
|
||||
// appear on different buses
|
||||
// The 'node' nodes below will map to different bindings since
|
||||
// they appear on different buses
|
||||
foo-bus {
|
||||
compatible = "foo-bus";
|
||||
node {
|
||||
compatible = "on-bus";
|
||||
nested {
|
||||
compatible = "on-bus";
|
||||
};
|
||||
};
|
||||
};
|
||||
bar-bus {
|
||||
|
|
|
@ -122,12 +122,30 @@ warning: "#cells:" in test-bindings/deprecated.yaml is deprecated and will be re
|
|||
# Test 'bus:' and 'on-bus:'
|
||||
#
|
||||
|
||||
verify_eq(edt.get_node("/buses/foo-bus").bus, "foo")
|
||||
# foo-bus does not itself appear on a bus
|
||||
verify_eq(edt.get_node("/buses/foo-bus").on_bus, None)
|
||||
verify_eq(edt.get_node("/buses/foo-bus").bus_node, None)
|
||||
|
||||
# foo-bus/node is not a bus node...
|
||||
verify_eq(edt.get_node("/buses/foo-bus/node").bus, None)
|
||||
# ...but is on a bus
|
||||
verify_eq(edt.get_node("/buses/foo-bus/node").on_bus, "foo")
|
||||
verify_eq(edt.get_node("/buses/foo-bus/node").bus_node.path,
|
||||
"/buses/foo-bus")
|
||||
|
||||
# Same compatible string, but different bindings from being on different
|
||||
# buses
|
||||
verify_streq(edt.get_node("/buses/foo-bus/node").binding_path,
|
||||
"test-bindings/device-on-foo-bus.yaml")
|
||||
|
||||
verify_streq(edt.get_node("/buses/bar-bus/node").binding_path,
|
||||
"test-bindings/device-on-bar-bus.yaml")
|
||||
|
||||
# foo-bus/node/nested also appears on the foo-bus bus
|
||||
verify_eq(edt.get_node("/buses/foo-bus/node/nested").on_bus, "foo")
|
||||
verify_streq(edt.get_node("/buses/foo-bus/node/nested").binding_path,
|
||||
"test-bindings/device-on-foo-bus.yaml")
|
||||
|
||||
#
|
||||
# Test 'child-binding:'
|
||||
#
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue