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)
|
binding = self._merge_included_bindings(binding, binding_path)
|
||||||
self._check_binding(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
|
# Do not allow two different bindings to have the same
|
||||||
# 'compatible:'/'on-bus:' combo
|
# 'compatible:'/'on-bus:' combo
|
||||||
old_binding = self._compat2binding.get((binding_compat, bus))
|
old_binding = self._compat2binding.get((binding_compat, on_bus))
|
||||||
if old_binding:
|
if old_binding:
|
||||||
msg = "both {} and {} have 'compatible: {}'".format(
|
msg = "both {} and {} have 'compatible: {}'".format(
|
||||||
old_binding[1], binding_path, binding_compat)
|
old_binding[1], binding_path, binding_compat)
|
||||||
if bus is not None:
|
if on_bus is not None:
|
||||||
msg += " and 'on-bus: {}'".format(bus)
|
msg += " and 'on-bus: {}'".format(on_bus)
|
||||||
_err(msg)
|
_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):
|
def _binding_compat(self, binding, binding_path):
|
||||||
# Returns the string listed in 'compatible:' in 'binding', or None if
|
# Returns the string listed in 'compatible:' in 'binding', or None if
|
||||||
|
@ -440,6 +440,7 @@ class EDT:
|
||||||
node = Node()
|
node = Node()
|
||||||
node.edt = self
|
node.edt = self
|
||||||
node._node = dt_node
|
node._node = dt_node
|
||||||
|
node.bus_node = node._bus_node()
|
||||||
node._init_binding()
|
node._init_binding()
|
||||||
node._init_regs()
|
node._init_regs()
|
||||||
node._set_instance_no()
|
node._set_instance_no()
|
||||||
|
@ -731,8 +732,19 @@ class Node:
|
||||||
pinctrl-<index> properties.
|
pinctrl-<index> properties.
|
||||||
|
|
||||||
bus:
|
bus:
|
||||||
The bus for the node as specified in its binding, e.g. "i2c" or "spi".
|
If the node is a bus node (has a 'bus:' key in its binding), then this
|
||||||
None if the binding doesn't specify a bus.
|
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:
|
flash_controller:
|
||||||
The flash controller for the node. Only meaningful for nodes representing
|
The flash controller for the node. Only meaningful for nodes representing
|
||||||
|
@ -828,7 +840,29 @@ class Node:
|
||||||
@property
|
@property
|
||||||
def bus(self):
|
def bus(self):
|
||||||
"See the class docstring"
|
"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
|
@property
|
||||||
def flash_controller(self):
|
def flash_controller(self):
|
||||||
|
@ -870,14 +904,14 @@ class Node:
|
||||||
|
|
||||||
if "compatible" in self._node.props:
|
if "compatible" in self._node.props:
|
||||||
self.compats = self._node.props["compatible"].to_strings()
|
self.compats = self._node.props["compatible"].to_strings()
|
||||||
bus = self._bus_from_parent_binding()
|
on_bus = self.on_bus
|
||||||
|
|
||||||
for compat in self.compats:
|
for compat in self.compats:
|
||||||
if (compat, bus) in self.edt._compat2binding:
|
if (compat, on_bus) in self.edt._compat2binding:
|
||||||
# Binding found
|
# Binding found
|
||||||
self.matching_compat = compat
|
self.matching_compat = compat
|
||||||
self._binding, self.binding_path = \
|
self._binding, self.binding_path = \
|
||||||
self.edt._compat2binding[compat, bus]
|
self.edt._compat2binding[compat, on_bus]
|
||||||
|
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
@ -920,31 +954,20 @@ class Node:
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _bus_from_parent_binding(self):
|
def _bus_node(self):
|
||||||
# _init_binding() helper. Returns the bus specified by 'bus:' in the
|
# Returns the value for self.bus_node. Relies on parent nodes being
|
||||||
# parent binding (or the legacy 'child-bus:'/'child: bus:'), or None if
|
# initialized before their children.
|
||||||
# missing.
|
|
||||||
|
|
||||||
if not self.parent:
|
if not self.parent:
|
||||||
|
# This is the root node
|
||||||
return None
|
return None
|
||||||
|
|
||||||
binding = self.parent._binding
|
if self.parent.bus:
|
||||||
if not binding:
|
# The parent node is a bus node
|
||||||
return None
|
return self.parent
|
||||||
|
|
||||||
if "bus" in binding:
|
# Same bus node as parent (possibly None)
|
||||||
return binding["bus"]
|
return self.parent.bus_node
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
def _init_props(self):
|
def _init_props(self):
|
||||||
# Creates self.props. See the class docstring. Also checks that all
|
# 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
|
# ControllerAndData instance, and None otherwise. See
|
||||||
# Documentation/devicetree/bindings/spi/spi-bus.txt in the Linux kernel.
|
# Documentation/devicetree/bindings/spi/spi-bus.txt in the Linux kernel.
|
||||||
|
|
||||||
if not (node.bus == "spi" and node.parent and
|
if not (node.on_bus == "spi" and "cs-gpios" in node.bus_node.props):
|
||||||
"cs-gpios" in node.parent.props):
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not node.regs:
|
if not node.regs:
|
||||||
_err("{!r} needs a 'reg' property, to look up the chip select index "
|
_err("{!r} needs a 'reg' property, to look up the chip select index "
|
||||||
"for SPI".format(node))
|
"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-gpios is indexed by the unit address
|
||||||
cs_index = node.regs[0].addr
|
cs_index = node.regs[0].addr
|
||||||
if cs_index >= len(parent_cs_lst):
|
if cs_index >= len(parent_cs_lst):
|
||||||
_err("index from 'regs' in {!r} ({}) is >= number of cs-gpios "
|
_err("index from 'regs' in {!r} ({}) is >= number of cs-gpios "
|
||||||
"in {!r} ({})".format(
|
"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]
|
return parent_cs_lst[cs_index]
|
||||||
|
|
||||||
|
@ -1503,7 +1525,7 @@ def _binding_paths(bindings_dirs):
|
||||||
return binding_paths
|
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
|
# Returns the bus specified by 'on-bus:' in the binding (or the
|
||||||
# legacy 'parent-bus:' and 'parent: bus:'), or None if missing
|
# legacy 'parent-bus:' and 'parent: bus:'), or None if missing
|
||||||
|
|
||||||
|
|
|
@ -244,18 +244,18 @@ def write_props(node):
|
||||||
def write_bus(node):
|
def write_bus(node):
|
||||||
# Generate bus-related #defines
|
# Generate bus-related #defines
|
||||||
|
|
||||||
if not node.bus:
|
if not node.bus_node:
|
||||||
return
|
return
|
||||||
|
|
||||||
if node.parent.label is None:
|
if node.bus_node.label is None:
|
||||||
err("missing 'label' property on {!r}".format(node.parent))
|
err("missing 'label' property on bus node {!r}".format(node.bus_node))
|
||||||
|
|
||||||
# #define DT_<DEV-IDENT>_BUS_NAME <BUS-LABEL>
|
# #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:
|
for compat in node.compats:
|
||||||
# #define DT_<COMPAT>_BUS_<BUS-TYPE> 1
|
# #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):
|
def write_existence_flags(node):
|
||||||
|
@ -307,9 +307,9 @@ def dev_ident(node):
|
||||||
|
|
||||||
ident = ""
|
ident = ""
|
||||||
|
|
||||||
if node.bus:
|
if node.bus_node:
|
||||||
ident += "{}_{:X}_".format(
|
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))
|
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 {}"
|
err("expected zephyr,flash to have a single register, has {}"
|
||||||
.format(len(node.regs)))
|
.format(len(node.regs)))
|
||||||
|
|
||||||
if node.bus == "spi" and len(node.parent.regs) == 2:
|
if node.on_bus == "spi" and len(node.bus_node.regs) == 2:
|
||||||
reg = node.parent.regs[1] # QSPI flash
|
reg = node.bus_node.regs[1] # QSPI flash
|
||||||
else:
|
else:
|
||||||
reg = node.regs[0]
|
reg = node.regs[0]
|
||||||
|
|
||||||
|
|
|
@ -298,12 +298,15 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
buses {
|
buses {
|
||||||
// The nodes below will map to different bindings since they
|
// The 'node' nodes below will map to different bindings since
|
||||||
// appear on different buses
|
// they appear on different buses
|
||||||
foo-bus {
|
foo-bus {
|
||||||
compatible = "foo-bus";
|
compatible = "foo-bus";
|
||||||
node {
|
node {
|
||||||
compatible = "on-bus";
|
compatible = "on-bus";
|
||||||
|
nested {
|
||||||
|
compatible = "on-bus";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
bar-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:'
|
# 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,
|
verify_streq(edt.get_node("/buses/foo-bus/node").binding_path,
|
||||||
"test-bindings/device-on-foo-bus.yaml")
|
"test-bindings/device-on-foo-bus.yaml")
|
||||||
|
|
||||||
verify_streq(edt.get_node("/buses/bar-bus/node").binding_path,
|
verify_streq(edt.get_node("/buses/bar-bus/node").binding_path,
|
||||||
"test-bindings/device-on-bar-bus.yaml")
|
"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:'
|
# Test 'child-binding:'
|
||||||
#
|
#
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue