From b0a03ae7afe6c33be2fb6fdd8a8faf52d080c509 Mon Sep 17 00:00:00 2001 From: Peter Bigot Date: Tue, 1 Sep 2020 10:22:31 -0500 Subject: [PATCH 1/3] edtlib: support inferring binding from node content Clean up of devicetree tooling removed generation of information present in devicetree in nodes that have no compatible, or for extra properties not defined by a binding. Discussion proposed that these properties should be allowed, but only in a defined node /zephyr,user. For that node infer bindings based on the presence of properties. Signed-off-by: Peter Bigot --- scripts/dts/edtlib.py | 59 +++++++++++++++++++++++++++++++++++---- scripts/dts/test.dts | 16 +++++++++++ scripts/dts/testedtlib.py | 12 ++++++++ 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/scripts/dts/edtlib.py b/scripts/dts/edtlib.py index fc6c4d51c3a24..26c7a123e413a 100644 --- a/scripts/dts/edtlib.py +++ b/scripts/dts/edtlib.py @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/scripts/dts/test.dts b/scripts/dts/test.dts index 695f925cfe2a7..64330d13dd468 100644 --- a/scripts/dts/test.dts +++ b/scripts/dts/test.dts @@ -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>; + }; + // // For testing that neither 'include: [foo.yaml, bar.yaml]' nor // 'include: [bar.yaml, foo.yaml]' causes errors when one of the files diff --git a/scripts/dts/testedtlib.py b/scripts/dts/testedtlib.py index 1ef332e401cd1..88236ae73ab79 100755 --- a/scripts/dts/testedtlib.py +++ b/scripts/dts/testedtlib.py @@ -215,6 +215,18 @@ def run(): verify_streq(edt.get_node("/defaults").props, r"OrderedDict([('int', ), ('array', ), ('uint8-array', ), ('string', ), ('string-array', ), ('default-not-used', )])") + # + # 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', ), ('bytes', ), ('number', ), ('numbers', ), ('string', ), ('strings', ), ('handle', >), ('phandles', , ]>), ('phandle-array-foos', , data: OrderedDict([('one', 1), ('two', 2)])>]>)])") + # # Test having multiple directories with bindings, with a different .dts file # From 6b020910e2cf24114d91870a52946536a5c9e5dd Mon Sep 17 00:00:00 2001 From: Peter Bigot Date: Wed, 2 Sep 2020 05:05:19 -0500 Subject: [PATCH 2/3] gen_defines: infer bindings for /zephyr,user Tell the EDT instance that properties of the /zephyr,user node should be generated based on the binding types inferred from the property content. Signed-off-by: Peter Bigot --- scripts/dts/gen_defines.py | 11 +++++++++-- scripts/dts/gen_legacy_defines.py | 8 +++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py index be725e4e958d2..3bf91f39dc581 100755 --- a/scripts/dts/gen_defines.py +++ b/scripts/dts/gen_defines.py @@ -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}") @@ -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" diff --git a/scripts/dts/gen_legacy_defines.py b/scripts/dts/gen_legacy_defines.py index a358334e1f01d..e4b45696eda30 100755 --- a/scripts/dts/gen_legacy_defines.py +++ b/scripts/dts/gen_legacy_defines.py @@ -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" From d0fa72b939bbc1a29c6349a522438085bc533815 Mon Sep 17 00:00:00 2001 From: Peter Bigot Date: Wed, 2 Sep 2020 05:10:21 -0500 Subject: [PATCH 3/3] doc: guides: dts: document inferred bindings for /zephyr,user Identify the special case of /zephyr,user as a node for which property values are generated without having to define a binding. Signed-off-by: Peter Bigot --- doc/guides/dts/bindings.rst | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/doc/guides/dts/bindings.rst b/doc/guides/dts/bindings.rst index 5dcb6df5775cc..94e06c6079ca7 100644 --- a/doc/guides/dts/bindings.rst +++ b/doc/guides/dts/bindings.rst @@ -11,8 +11,9 @@ particular devicetree are useful to :ref:`device drivers ` 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: @@ -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>; + }; + };