Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions doc/guides/dts/bindings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ particular devicetree are useful to :ref:`device drivers <device_model_api>` or

*Devicetree bindings* provide the other half of this information. Zephyr
devicetree bindings are YAML files in a custom format (Zephyr does not use the
dt-schema tools used by the Linux kernel). The build system uses bindings
when generating code for :ref:`dt-from-c`.
dt-schema tools used by the Linux kernel). With one exception in
:ref:`dt-inferred-bindings` the build system uses bindings when generating
code for :ref:`dt-from-c`.

.. _dt-binding-compat:

Expand Down Expand Up @@ -73,3 +74,32 @@ Below is a template that shows the Zephyr bindings file syntax. It is stored in

.. literalinclude:: ../../../dts/binding-template.yaml
:language: yaml

.. _dt-inferred-bindings:

Inferred bindings
*****************

For sample code and applications it can be inconvenient to define a devicetree
binding when only a few simple properties are needed, such as the identify of
a GPIO for an application task. The devicetree syntax allows inference of a
binding for properties based on the value observed. This inference is
supported only for the ``/zephyr,user`` node. The properties referenced can
be accessed through the standard devicetree macros,
e.g. ``DT_PROP(DT_PATH(zephyr_user), bytes)``.

.. code-block:: DTS

/ {
zephyr,user {
boolean;
bytes = [81 82 83];
number = <23>;
numbers = <1>, <2>, <3>;
string = "text";
strings = "a", "b", "c";
handle = <&gpio0>;
handles = <&gpio0>, <&gpio1>;
signal-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
};
};
59 changes: 54 additions & 5 deletions scripts/dts/edtlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@
except ImportError:
from yaml import Loader

from dtlib import DT, DTError, to_num, to_nums, TYPE_EMPTY, TYPE_NUMS, \
TYPE_PHANDLE, TYPE_PHANDLES_AND_NUMS
from dtlib import DT, DTError, to_num, to_nums, TYPE_EMPTY, TYPE_BYTES, \
TYPE_NUM, TYPE_NUMS, TYPE_STRING, TYPE_STRINGS, \
TYPE_PHANDLE, TYPE_PHANDLES, TYPE_PHANDLES_AND_NUMS
from grutils import Graph


Expand Down Expand Up @@ -157,9 +158,9 @@ class EDT:
def __init__(self, dts, bindings_dirs, warn_file=None,
warn_reg_unit_address_mismatch=True,
default_prop_types=True,
support_fixed_partitions_on_any_bus=True):
"""
EDT constructor. This is the top-level entry point to the library.
support_fixed_partitions_on_any_bus=True,
infer_binding_for_paths=None):
"""EDT constructor. This is the top-level entry point to the library.

dts:
Path to devicetree .dts file
Expand All @@ -184,6 +185,12 @@ def __init__(self, dts, bindings_dirs, warn_file=None,
If True, set the Node.bus for 'fixed-partitions' compatible nodes
to None. This allows 'fixed-partitions' binding to match regardless
of the bus the 'fixed-partition' is under.

infer_binding_for_paths (default: None):
An iterable of devicetree paths identifying nodes for which bindings
should be inferred from the node content. (Child nodes are not
processed.) Pass none if no nodes should support inferred bindings.

"""
# Do this indirection with None in case sys.stderr is
# deliberately overridden. We'll only hold on to this file
Expand All @@ -193,6 +200,7 @@ def __init__(self, dts, bindings_dirs, warn_file=None,
self._warn_reg_unit_address_mismatch = warn_reg_unit_address_mismatch
self._default_prop_types = default_prop_types
self._fixed_partitions_no_bus = support_fixed_partitions_on_any_bus
self._infer_binding_for_paths = set(infer_binding_for_paths or [])

self.dts_path = dts
self.bindings_dirs = bindings_dirs
Expand Down Expand Up @@ -957,6 +965,10 @@ def _init_binding(self):
# 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_bus = self.on_bus

Expand Down Expand Up @@ -984,6 +996,43 @@ def _init_binding(self):
# No binding found
self._binding = self.binding_path = self.matching_compat = None

def _binding_from_properties(self):
# Returns a binding synthesized from the properties in the node.

if self.compats:
_err(f"compatible in node with inferred binding: {self.path}")

self._binding = OrderedDict()
self.matching_compat = self.path.split('/')[-1]
self.compats = [self.matching_compat]
self.binding_path = None

properties = OrderedDict()
self._binding["properties"] = properties
for name, prop in self._node.props.items():
pp = OrderedDict()
properties[name] = pp
if prop.type == TYPE_EMPTY:
pp["type"] = "boolean"
elif prop.type == TYPE_BYTES:
pp["type"] = "uint8-array"
elif prop.type == TYPE_NUM:
pp["type"] = "int"
elif prop.type == TYPE_NUMS:
pp["type"] = "array"
elif prop.type == TYPE_STRING:
pp["type"] = "string"
elif prop.type == TYPE_STRINGS:
pp["type"] = "string-array"
elif prop.type == TYPE_PHANDLE:
pp["type"] = "phandle"
elif prop.type == TYPE_PHANDLES:
pp["type"] = "phandles"
elif prop.type == TYPE_PHANDLES_AND_NUMS:
pp["type"] = "phandle-array"
else:
_err(f"cannot infer binding from property: {prop}")

def _binding_from_parent(self):
# Returns the binding from 'child-binding:' in the parent node's
# binding (or from the legacy 'sub-node:' key), or None if missing
Expand Down
11 changes: 9 additions & 2 deletions scripts/dts/gen_defines.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ def main():
# Suppress this warning if it's suppressed in dtc
warn_reg_unit_address_mismatch=
"-Wno-simple_bus_reg" not in args.dtc_flags,
default_prop_types=True)
default_prop_types=True,
infer_binding_for_paths=["/zephyr,user"])
except edtlib.EDTError as e:
sys.exit(f"devicetree error: {e}")

Expand Down Expand Up @@ -178,10 +179,16 @@ def write_node_comment(node):
"""

if node.matching_compat:
s += f"""
if node.binding_path:
s += f"""
Binding (compatible = {node.matching_compat}):
{relativize(node.binding_path)}
"""
else:
s += f"""
Binding (compatible = {node.matching_compat}):
No yaml (bindings inferred from properties)
"""

s += f"\nDependency Ordinal: {node.dep_ordinal}\n"

Expand Down
8 changes: 7 additions & 1 deletion scripts/dts/gen_legacy_defines.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,15 @@ def write_node_comment(node):
{node.path}
"""
if node.matching_compat:
s += f"""
if node.binding_path:
s += f"""
Binding (compatible = {node.matching_compat}):
{relativize(node.binding_path)}
"""
else:
s += f"""
Binding (compatible = {node.matching_compat}):
No yaml (bindings inferred from properties)
"""
else:
s += "\nNo matching binding.\n"
Expand Down
16 changes: 16 additions & 0 deletions scripts/dts/test.dts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,22 @@
};
};

//
// zephyr,user binding inference
//

zephyr,user {
boolean;
bytes = [81 82 83];
number = <23>;
numbers = <1>, <2>, <3>;
string = "text";
strings = "a", "b", "c";
handle = <&{/props/ctrl-1}>;
phandles = <&{/props/ctrl-1}>, <&{/props/ctrl-2}>;
phandle-array-foos = <&{/props/ctrl-2} 1 2>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a subnode in here as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subnodes aren't processed.

};

//
// For testing that neither 'include: [foo.yaml, bar.yaml]' nor
// 'include: [bar.yaml, foo.yaml]' causes errors when one of the files
Expand Down
12 changes: 12 additions & 0 deletions scripts/dts/testedtlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,18 @@ def run():
verify_streq(edt.get_node("/defaults").props,
r"OrderedDict([('int', <Property, name: int, type: int, value: 123>), ('array', <Property, name: array, type: array, value: [1, 2, 3]>), ('uint8-array', <Property, name: uint8-array, type: uint8-array, value: b'\x89\xab\xcd'>), ('string', <Property, name: string, type: string, value: 'hello'>), ('string-array', <Property, name: string-array, type: string-array, value: ['hello', 'there']>), ('default-not-used', <Property, name: default-not-used, type: int, value: 234>)])")

#
# Test binding inference
#

verify_streq(edt.get_node("/zephyr,user").props, r"OrderedDict()")

edt = edtlib.EDT("test.dts", ["test-bindings"], warnings,
infer_binding_for_paths=["/zephyr,user"])

verify_streq(edt.get_node("/zephyr,user").props,
r"OrderedDict([('boolean', <Property, name: boolean, type: boolean, value: True>), ('bytes', <Property, name: bytes, type: uint8-array, value: b'\x81\x82\x83'>), ('number', <Property, name: number, type: int, value: 23>), ('numbers', <Property, name: numbers, type: array, value: [1, 2, 3]>), ('string', <Property, name: string, type: string, value: 'text'>), ('strings', <Property, name: strings, type: string-array, value: ['a', 'b', 'c']>), ('handle', <Property, name: handle, type: phandle, value: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>>), ('phandles', <Property, name: phandles, type: phandles, value: [<Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, <Node /props/ctrl-2 in 'test.dts', binding test-bindings/phandle-array-controller-2.yaml>]>), ('phandle-array-foos', <Property, name: phandle-array-foos, type: phandle-array, value: [<ControllerAndData, controller: <Node /props/ctrl-2 in 'test.dts', binding test-bindings/phandle-array-controller-2.yaml>, data: OrderedDict([('one', 1), ('two', 2)])>]>)])")

#
# Test having multiple directories with bindings, with a different .dts file
#
Expand Down