diff --git a/scripts/dts/edtlib.py b/scripts/dts/edtlib.py index eedde854b36..e5cbdf26e9f 100644 --- a/scripts/dts/edtlib.py +++ b/scripts/dts/edtlib.py @@ -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- 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 diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py index f6b43e957ee..06b7653fe19 100755 --- a/scripts/dts/gen_defines.py +++ b/scripts/dts/gen_defines.py @@ -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__BUS_NAME - 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__BUS_ 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] diff --git a/scripts/dts/test.dts b/scripts/dts/test.dts index 90c7594a4ed..a19d13630cb 100644 --- a/scripts/dts/test.dts +++ b/scripts/dts/test.dts @@ -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 { diff --git a/scripts/dts/testedtlib.py b/scripts/dts/testedtlib.py index 5dd16ec4901..7dd89ef6912 100755 --- a/scripts/dts/testedtlib.py +++ b/scripts/dts/testedtlib.py @@ -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:' #