Commit 5e55eda3 authored by Ulf Magnusson's avatar Ulf Magnusson Committed by Carles Cufi
Browse files

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: default avatarUlf Magnusson <Ulf.Magnusson@nordicsemi.no>
parent 527343df
Loading
Loading
Loading
Loading
+58 −36
Original line number Diff line number Diff line
@@ -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 "bus" in binding:
            return binding["bus"]

        # Legacy key
        if "child-bus" in binding:
            return binding["child-bus"]
        if self.parent.bus:
            # The parent node is a bus node
            return self.parent

        # 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

+9 −9
Original line number Diff line number Diff line
@@ -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]

+5 −2
Original line number Diff line number Diff line
@@ -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 {
+19 −1
Original line number Diff line number Diff line
@@ -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:'
    #