scripts: dts: Replace 'sub-node:' with more general 'child-binding:'
Deprecate 'sub-node:' and add a more general 'child-binding:' mechanism to bindings. Keep supporting 'sub-node:', but print a deprecation warning when it's used. Like 'sub-node:', 'child-binding:' gives a binding to child nodes, but the binding is required to be a complete binding, and is treated (and checked) like a normal binding. 'child-binding:' can in turn contain another 'child-binding:', up to any number of levels. This is automatic from treating it like a normal binding, and from the code initializing parent Devices before child Devices. This lets nodes give bindings to grandchildren. For example, take this devicetree fragment: parent { compatible = "foo"; child-1 { grandchild-1 { ... }; grandchild-2 { ... }; }; child-2 { grandchild-3 { ... }; }; }; The binding for 'foo' could provide bindings for grandchild-1/2/3 like this: compatible: "foo" # Binding for children child-binding: title: ... description: ... ... # Binding for grandchildren child-binding: title: ... description: ... properties: ... Due to implementation issues with the old devicetree scripts, only two levels of 'child-binding:' is supported for now. This limitation will go away in Zephyr 2.2. Piggyback shortening 'description:' and 'title:' in some bindings that provide child bindings. This makes the generated header a bit neater. Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
This commit is contained in:
parent
7912f24bf3
commit
0b1ab4ab09
12 changed files with 229 additions and 85 deletions
|
@ -60,18 +60,6 @@ child-bus: <string describing bus type, e.g. "i2c">
|
|||
# on.
|
||||
parent-bus: <string describing bus type, e.g. "i2c">
|
||||
|
||||
# 'sub-node' is used to simplify cases where a node has children that can all
|
||||
# use the same binding. The contents of 'sub-node' becomes the binding for each
|
||||
# child node.
|
||||
#
|
||||
# The example below is for a binding for pwm-leds where the child nodes are
|
||||
# required to have a 'pwms' property.
|
||||
sub-node:
|
||||
properties:
|
||||
pwms:
|
||||
type: compound
|
||||
required: true
|
||||
|
||||
# 'properties' describes properties on the node, e.g.
|
||||
#
|
||||
# reg = <1 2>;
|
||||
|
@ -207,6 +195,66 @@ properties:
|
|||
required: false
|
||||
default: [0x12, 0x34] # Same as 'uint8-array-with-default = [12 34]'
|
||||
|
||||
# 'child-binding' can be used when a node has children that all share the same
|
||||
# properties. Each child gets the contents of 'child-binding' as its binding
|
||||
# (though an explicit 'compatible = ...' on the child node takes precedence, if
|
||||
# a binding is found for it).
|
||||
#
|
||||
# The example below is for a binding for PWM LEDs, where the child nodes are
|
||||
# required to have a 'pwms' property. It corresponds to this .dts structure
|
||||
# (assuming the binding has 'compatible: "pwm-leds"'):
|
||||
#
|
||||
# pwmleds {
|
||||
# compatible = "pwm-leds";
|
||||
#
|
||||
# red_pwm_led {
|
||||
# pwms = <&pwm3 4 15625000>;
|
||||
# };
|
||||
# green_pwm_led {
|
||||
# pwms = <&pwm3 0 15625000>;
|
||||
# };
|
||||
# ...
|
||||
# };
|
||||
child-binding:
|
||||
title: PWM LED
|
||||
description: LED that uses PWM
|
||||
|
||||
properties:
|
||||
pwms:
|
||||
type: phandle-array
|
||||
required: true
|
||||
|
||||
# 'child-binding' also works recursively. For example, the binding below would
|
||||
# provide a binding for the 'grandchild' node in this .dts (assuming
|
||||
# 'compatible: "foo"'):
|
||||
#
|
||||
# parent {
|
||||
# compatible = "foo";
|
||||
# child {
|
||||
# grandchild {
|
||||
# prop = <123>;
|
||||
# };
|
||||
# };
|
||||
# }
|
||||
#
|
||||
# WARNING: Due to implementation issues with legacy code, only up to two levels
|
||||
# of 'child-binding:' nesting (like below) is supported. This restriction will
|
||||
# go away in Zephyr 2.2.
|
||||
child-binding:
|
||||
title: ...
|
||||
description: ...
|
||||
|
||||
...
|
||||
|
||||
child-binding:
|
||||
title: ...
|
||||
description: ...
|
||||
|
||||
properties:
|
||||
prop:
|
||||
type: int
|
||||
required: true
|
||||
|
||||
# If the binding describes an interrupt controller, GPIO controller, pinmux
|
||||
# device, or any other node referenced by other nodes, then #cells should be
|
||||
# given.
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
# Copyright (c) 2018, Linaro Limited
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
title: GPIO KEYS
|
||||
title: GPIO KEYS parent
|
||||
|
||||
description: >
|
||||
This is a representation of the GPIO KEYS nodes
|
||||
description: GPIO KEYS parent node
|
||||
|
||||
compatible: "gpio-keys"
|
||||
|
||||
sub-node:
|
||||
child-binding:
|
||||
title: GPIO KEYS node
|
||||
description: GPIO KEYS child node
|
||||
properties:
|
||||
gpios:
|
||||
type: phandle-array
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
# Copyright (c) 2018, Linaro Limited
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
title: GPIO LED
|
||||
title: GPIO LEDs
|
||||
|
||||
description: >
|
||||
This is a representation of the LED GPIO nodes
|
||||
description: GPIO LEDs parent node
|
||||
|
||||
compatible: "gpio-leds"
|
||||
|
||||
sub-node:
|
||||
child-binding:
|
||||
title: GPIO LED node
|
||||
description: GPIO LED child node
|
||||
properties:
|
||||
gpios:
|
||||
type: phandle-array
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
# Copyright (c) 2018, Linaro Limited
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
title: PWM LED
|
||||
title: PWM LEDs
|
||||
|
||||
description: >
|
||||
This is a representation of the PWM GPIO nodes
|
||||
description: PWM LEDs parent node
|
||||
|
||||
compatible: "pwm-leds"
|
||||
|
||||
sub-node:
|
||||
child-binding:
|
||||
title: PWM LED node
|
||||
description: PWM LED child node
|
||||
properties:
|
||||
pwms:
|
||||
type: phandle-array
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
title: Flash Partitions
|
||||
title: Flash partitions parent
|
||||
|
||||
description: >
|
||||
This binding gives a base FLASH partition description
|
||||
description: Flash partitions parent node
|
||||
|
||||
compatible: "fixed-partitions"
|
||||
|
||||
|
@ -16,7 +15,9 @@ properties:
|
|||
required: false
|
||||
description: number of size cells in reg property
|
||||
|
||||
sub-node:
|
||||
child-binding:
|
||||
title: Flash partition
|
||||
description: Flash partition child node
|
||||
properties:
|
||||
label:
|
||||
required: true
|
||||
|
|
|
@ -15,9 +15,9 @@ information related to the device, derived from both the device tree and from
|
|||
the binding for the device.
|
||||
|
||||
Bindings are files that describe device tree nodes. Device tree nodes are
|
||||
usually mapped to bindings via their 'compatible = "..."' property, but binding
|
||||
data can also come from a 'sub-node:' key in the binding for the parent device
|
||||
tree node.
|
||||
usually mapped to bindings via their 'compatible = "..."' property, but a
|
||||
binding can also come from a 'child-binding:' key in the binding for the parent
|
||||
device tree node.
|
||||
|
||||
The top-level entry point of the library is the EDT class. EDT.__init__() takes
|
||||
a .dts file to parse and the path of a directory containing bindings.
|
||||
|
@ -556,29 +556,47 @@ class Device:
|
|||
|
||||
return
|
||||
else:
|
||||
# No 'compatible' property. See if the parent has a 'sub-node:' key
|
||||
# that gives the binding.
|
||||
# No 'compatible' property. See if the parent binding has a
|
||||
# 'child-binding:' key that gives the binding (or a legacy
|
||||
# 'sub-node:' key).
|
||||
|
||||
self.compats = []
|
||||
|
||||
if self.parent and self.parent._binding and \
|
||||
"sub-node" in self.parent._binding:
|
||||
|
||||
# Binding found
|
||||
self._binding = self.parent._binding["sub-node"]
|
||||
binding_from_parent = self._binding_from_parent()
|
||||
if binding_from_parent:
|
||||
self._binding = binding_from_parent
|
||||
self.binding_path = self.parent.binding_path
|
||||
|
||||
self.description = self.parent._binding.get("description")
|
||||
if self.description:
|
||||
self.description = self.description.rstrip()
|
||||
|
||||
self.matching_compat = self.parent.matching_compat
|
||||
self.description = self._binding["description"]
|
||||
|
||||
return
|
||||
|
||||
# No binding found
|
||||
self.matching_compat = self._binding = self.binding_path = \
|
||||
self._binding = self.binding_path = self.matching_compat = \
|
||||
self.description = None
|
||||
|
||||
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
|
||||
|
||||
if not self.parent:
|
||||
return None
|
||||
|
||||
pbinding = self.parent._binding
|
||||
if not pbinding:
|
||||
return None
|
||||
|
||||
if "child-binding" in pbinding:
|
||||
return pbinding["child-binding"]
|
||||
|
||||
# Backwards compatibility
|
||||
if "sub-node" in pbinding:
|
||||
return {"title": pbinding["title"],
|
||||
"description": pbinding["description"],
|
||||
"properties": pbinding["sub-node"]["properties"]}
|
||||
|
||||
return None
|
||||
|
||||
def _bus_from_parent_binding(self):
|
||||
# _init_binding() helper. Returns the bus specified by 'child-bus: ...'
|
||||
# in the parent binding (or the legacy 'child: bus: ...'), or None if
|
||||
|
@ -1439,7 +1457,8 @@ def _check_binding(binding, binding_path):
|
|||
.format(prop, binding_path))
|
||||
|
||||
ok_top = {"title", "description", "compatible", "properties", "#cells",
|
||||
"parent-bus", "child-bus", "parent", "child", "sub-node"}
|
||||
"parent-bus", "child-bus", "parent", "child", "child-binding",
|
||||
"sub-node"}
|
||||
|
||||
for prop in binding:
|
||||
if prop not in ok_top:
|
||||
|
@ -1471,7 +1490,19 @@ def _check_binding(binding, binding_path):
|
|||
|
||||
_check_binding_properties(binding, binding_path)
|
||||
|
||||
if "child-binding" in binding:
|
||||
if not isinstance(binding["child-binding"], dict):
|
||||
_err("malformed 'child-binding:' in {}, expected a binding "
|
||||
"(dictionary with keys/values)".format(binding_path))
|
||||
|
||||
_check_binding(binding["child-binding"], binding_path)
|
||||
|
||||
if "sub-node" in binding:
|
||||
_warn("'sub-node: properties: ...' in {} is deprecated and will be "
|
||||
"removed - please give a full binding for the child node in "
|
||||
"'child-binding:' instead (see binding-template.yaml)"
|
||||
.format(binding_path))
|
||||
|
||||
if binding["sub-node"].keys() != {"properties"}:
|
||||
_err("expected (just) 'properties:' in 'sub-node:' in {}"
|
||||
.format(binding_path))
|
||||
|
|
|
@ -307,24 +307,46 @@ def get_binding(node_path):
|
|||
if isinstance(compat, list):
|
||||
compat = compat[0]
|
||||
|
||||
parent_path = get_parent_path(node_path)
|
||||
parent_compat = get_compat(parent_path)
|
||||
# Support two levels of recursive 'child-binding:'. The new scripts support
|
||||
# any number of levels, but it gets a bit tricky to implement here, because
|
||||
# nodes don't store their bindings.
|
||||
|
||||
if parent_compat in bindings:
|
||||
parent_binding = bindings[parent_compat]
|
||||
# see if we're a sub-node
|
||||
if compat is None and 'sub-node' in parent_binding:
|
||||
return parent_binding['sub-node']
|
||||
parent_path = get_parent_path(node_path)
|
||||
pparent_path = get_parent_path(parent_path)
|
||||
|
||||
parent_compat = get_compat(parent_path)
|
||||
pparent_compat = get_compat(pparent_path) if pparent_path else None
|
||||
|
||||
if parent_compat in bindings or pparent_compat in bindings:
|
||||
if compat is None:
|
||||
# The node doesn't get a binding from 'compatible'. See if it gets
|
||||
# one via 'sub-node' or 'child-binding'.
|
||||
|
||||
parent_binding = bindings.get(parent_compat)
|
||||
if parent_binding:
|
||||
for sub_key in 'sub-node', 'child-binding':
|
||||
if sub_key in parent_binding:
|
||||
return parent_binding[sub_key]
|
||||
|
||||
# Look for 'child-binding: child-binding: ...' in grandparent node
|
||||
|
||||
pparent_binding = bindings.get(pparent_compat)
|
||||
if pparent_binding and 'child-binding' in pparent_binding:
|
||||
pp_child_binding = pparent_binding['child-binding']
|
||||
if 'child-binding' in pp_child_binding:
|
||||
return pp_child_binding['child-binding']
|
||||
|
||||
# look for a bus-specific binding
|
||||
|
||||
if 'child-bus' in parent_binding:
|
||||
bus = parent_binding['child-bus']
|
||||
return bus_bindings[bus][compat]
|
||||
parent_binding = bindings.get(parent_compat)
|
||||
if parent_binding:
|
||||
if 'child-bus' in parent_binding:
|
||||
bus = parent_binding['child-bus']
|
||||
return bus_bindings[bus][compat]
|
||||
|
||||
if 'child' in parent_binding and 'bus' in parent_binding['child']:
|
||||
bus = parent_binding['child']['bus']
|
||||
return bus_bindings[bus][compat]
|
||||
if 'child' in parent_binding and 'bus' in parent_binding['child']:
|
||||
bus = parent_binding['child']['bus']
|
||||
return bus_bindings[bus][compat]
|
||||
|
||||
# No bus-specific binding found, look in the main dict.
|
||||
if compat:
|
||||
|
|
22
scripts/dts/test-bindings/child-binding.yaml
Normal file
22
scripts/dts/test-bindings/child-binding.yaml
Normal file
|
@ -0,0 +1,22 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
title: child-binding test
|
||||
description: child-binding test
|
||||
|
||||
compatible: "child-binding"
|
||||
|
||||
child-binding:
|
||||
title: child node
|
||||
description: child node
|
||||
properties:
|
||||
child-prop:
|
||||
type: int
|
||||
required: true
|
||||
|
||||
child-binding:
|
||||
title: grandchild node
|
||||
description: grandchild node
|
||||
properties:
|
||||
grandchild-prop:
|
||||
type: int
|
||||
required: true
|
|
@ -22,3 +22,10 @@ properties:
|
|||
optional:
|
||||
type: int
|
||||
category: optional
|
||||
|
||||
# Deprecated older 'child-binding'-alike
|
||||
sub-node:
|
||||
properties:
|
||||
child-prop:
|
||||
type: int
|
||||
required: true
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
title: Sub-node test
|
||||
description: Sub-node test
|
||||
|
||||
compatible: "parent-with-sub-node"
|
||||
|
||||
sub-node:
|
||||
properties:
|
||||
foo:
|
||||
required: true
|
||||
type: int
|
||||
|
||||
bar:
|
||||
required: true
|
||||
type: int
|
|
@ -340,14 +340,20 @@
|
|||
};
|
||||
|
||||
//
|
||||
// Parent with 'sub-node:' in binding
|
||||
// Node with 'child-binding:' in binding (along with a recursive
|
||||
// 'child-binding:')
|
||||
//
|
||||
|
||||
parent-with-sub-node {
|
||||
compatible = "parent-with-sub-node";
|
||||
node {
|
||||
foo = <1>;
|
||||
bar = <2>;
|
||||
child-binding {
|
||||
compatible = "child-binding";
|
||||
child-1 {
|
||||
child-prop = <1>;
|
||||
grandchild {
|
||||
grandchild-prop = <2>;
|
||||
};
|
||||
};
|
||||
child-2 {
|
||||
child-prop = <3>;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -376,5 +382,8 @@
|
|||
compatible = "deprecated";
|
||||
required = <1>;
|
||||
required-2 = <2>;
|
||||
sub-node {
|
||||
child-prop = <3>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -116,14 +116,31 @@ def run():
|
|||
"test-bindings/device-on-bar-bus.yaml")
|
||||
|
||||
#
|
||||
# Test 'sub-node:' in binding
|
||||
# Test 'child-binding:'
|
||||
#
|
||||
|
||||
verify_streq(edt.get_dev("/parent-with-sub-node/node").description,
|
||||
"Sub-node test")
|
||||
child1 = edt.get_dev("/child-binding/child-1")
|
||||
child2 = edt.get_dev("/child-binding/child-2")
|
||||
grandchild = edt.get_dev("/child-binding/child-1/grandchild")
|
||||
|
||||
verify_streq(edt.get_dev("/parent-with-sub-node/node").props,
|
||||
"{'foo': <Property, name: foo, type: int, value: 1>, 'bar': <Property, name: bar, type: int, value: 2>}")
|
||||
verify_streq(child1.binding_path, "test-bindings/child-binding.yaml")
|
||||
verify_streq(child1.description, "child node")
|
||||
verify_streq(child1.props, "{'child-prop': <Property, name: child-prop, type: int, value: 1>}")
|
||||
|
||||
verify_streq(child2.binding_path, "test-bindings/child-binding.yaml")
|
||||
verify_streq(child2.description, "child node")
|
||||
verify_streq(child2.props, "{'child-prop': <Property, name: child-prop, type: int, value: 3>}")
|
||||
|
||||
verify_streq(grandchild.binding_path, "test-bindings/child-binding.yaml")
|
||||
verify_streq(grandchild.description, "grandchild node")
|
||||
verify_streq(grandchild.props, "{'grandchild-prop': <Property, name: grandchild-prop, type: int, value: 2>}")
|
||||
|
||||
#
|
||||
# Test deprecated 'sub-node:' key (replaced with 'child-binding:')
|
||||
#
|
||||
|
||||
verify_streq(edt.get_dev("/deprecated/sub-node").props,
|
||||
"{'child-prop': <Property, name: child-prop, type: int, value: 3>}")
|
||||
|
||||
#
|
||||
# Test Device.property (derived from DT and 'properties:' in the binding)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue