Skip to content

Merge binding properties from all listed compat string with certain constraints. #91188

Open
@swift-tk

Description

@swift-tk

Problem Description

Zephyr only process the first compat string to get the binding properties for a given node.
Given a DTS like below where two compat string is involved but their properties does not entirely overlap.
The common mspi-aps-z8 would have the device variant/part number property that would never make into ambiq,mspi-device that is platform specific. And ambiq,mspi-device would have ambiq,timing-config and ambiq,timing-config-mask properties that would never make into the common mspi-aps-z8; while both three properties are needed in the device tree.

	aps51216ba: aps_z8@0 {
		compatible = "ambiq,mspi-device", "mspi-aps-z8";
		size = <DT_SIZE_M(512)>;
		reg = <0>;
		status = "disabled";
		variant = "APS51216BA"
		mspi-max-frequency = <125000000>;
		mspi-io-mode = "MSPI_IO_MODE_HEX_8_8_16";
		mspi-data-rate = "MSPI_DATA_RATE_S_D_D";
		mspi-hardware-ce-num = <0>;
		mspi-dqs-enable;
		read-command = <0x20>;
		write-command = <0xA0>;
		command-length = "INSTR_1_BYTE";
		address-length = "ADDR_4_BYTE";
		rx-dummy = <7>;
		tx-dummy = <8>;
		xip-config = <1 0 DT_SIZE_M(64) 0>;
		ce-break-config = <1024 4>;
		ambiq,timing-config-mask = <0x62>;
		ambiq,timing-config = <8 7 1 0 0 3 10>;
		zephyr,pm-device-runtime-auto;
	};

Another example is when the common device(flash) binding have gpio reset related properties that are also inappropriate to appear in a platform specific binding.

Proposed Change (Summary)

Update edtlib.py to support pulling bind properties from all listed compat string given that they are on the same bus.
Or, introduce a base keyword, and merge bindings that have the same base.

Proposed Change (Detailed)

This seems pretty easy, I was able to modify the edtlib.py _init_binding to merge bindings across listed compats. Note that this would merge without constraints.

    def _init_binding(self) -> None:
        # Initializes Node._binding. It holds data from the node's binding file,
        # in the format returned by PyYAML (plain Python lists, dicts, etc.), or
        # None if the node has no binding.

        # This relies on the parent of the node having already been
        # initialized, which is guaranteed by going through the nodes in
        # node_iter() order.

        if self.path in self.edt._infer_binding_for_paths:
            self._binding_from_properties()
            return

        if self.compats:
            on_buses = self.on_buses
            merged_raw = {}
            found_binding = False

            for compat in self.compats:
                # When matching, respect the order of the 'compatible' entries,
                # and for each one first try to match against an explicitly
                # specified bus (if any) and then against any bus. This is so
                # that matching against bindings which do not specify a bus
                # works the same way in Zephyr as it does elsewhere.
                binding = None

                for bus in on_buses:
                    if (compat, bus) in self.edt._compat2binding:
                        binding = self.edt._compat2binding[compat, bus]
                        break

                if not binding and (compat, None) in self.edt._compat2binding:
                    binding = self.edt._compat2binding[compat, None]

                if binding:
                    found_binding = True
                    _merge_props(merged_raw, binding.raw, None, binding.path, False)

            if found_binding:
                self._binding = Binding(None, self.edt._binding_fname2path, raw=merged_raw)
                return
        else:
            # No 'compatible' property. See if the parent binding has
            # a compatible. This can come from one or more levels of
            # nesting with 'child-binding:'.

            binding_from_parent = self._binding_from_parent()
            if binding_from_parent:
                self._binding = binding_from_parent
                return

        # No binding found
        self._binding = None

The constraints may need further discussion. Some I come up:

  1. Merge only compat on the same bus
  2. When conflict, use binding in the most specific compat.
  3. ...

Dependencies

No response

Concerns and Unresolved Questions

No response

Alternatives Considered

alternative is to have DT support addtionalProperties feature seen in Linux/dt-schema.

Metadata

Metadata

Assignees

Labels

Architecture ReviewDiscussion in the Architecture WG requiredRFCRequest For Comments: want input from the communityarea: Devicetree

Type

Projects

Status

No status

Status

Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions