dts: edtlib: Turn Node.instance_no into EDT.compat2enabled

Add an EDT.compat2enabled attribute that maps compatibles to enabled
devicetree nodes that implement them. For example,
EDT.compat2enabled["foo"] is a list of all enabled nodes with "foo" in
the 'compatible' property.

The old Node.instance_no functionality can be implemented in terms of
EDT.compat2enabled, so remove Node.instance_no. EDT.compat2enabled is
more flexible and easier to understand.

Simplify main() in gen_defines.py by using EDT.compat2enabled to
generate the DT_COMPAT_<compatible> existence macros. The behavior is
slightly different now, as DT_COMPAT_<compatible> is generated for
enabled nodes that don't have a binding as well, but that might be an
improvement overall. It probably doesn't hurt at least.

EDT.compat2enabled simplifies the implementation of the new
$(dt_compat_get_str) preprocessor function in
https://github.com/zephyrproject-rtos/zephyr/pull/21560. That was the
original motivation.

Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
This commit is contained in:
Ulf Magnusson 2020-01-28 04:46:12 +01:00 committed by Kumar Gala
commit 88db84b89b
4 changed files with 75 additions and 34 deletions

View file

@ -65,7 +65,7 @@ a .dts file to parse and a list of paths to directories containing bindings.
# - Please use ""-quoted strings instead of ''-quoted strings, just to make # - Please use ""-quoted strings instead of ''-quoted strings, just to make
# things consistent (''-quoting is more common otherwise in Python) # things consistent (''-quoting is more common otherwise in Python)
from collections import OrderedDict from collections import OrderedDict, defaultdict
import os import os
import re import re
import sys import sys
@ -96,6 +96,25 @@ class EDT:
nodes: nodes:
A list of Node objects for the nodes that appear in the devicetree A list of Node objects for the nodes that appear in the devicetree
compat2enabled:
A collections.defaultdict that maps each 'compatible' string that appears
on some enabled Node to a list of enabled Nodes.
For example, edt.compat2enabled["bar"] would include the 'foo' and 'bar'
nodes below.
foo {
compatible = "bar";
status = "okay";
...
};
bar {
compatible = "foo", "bar", "baz";
status = "okay";
...
};
dts_path: dts_path:
The .dts path passed to __init__() The .dts path passed to __init__()
@ -128,6 +147,7 @@ class EDT:
self._init_compat2binding(bindings_dirs) self._init_compat2binding(bindings_dirs)
self._init_nodes() self._init_nodes()
self._init_compat2enabled()
self._define_order() self._define_order()
@ -443,7 +463,6 @@ class EDT:
node.bus_node = node._bus_node() node.bus_node = node._bus_node()
node._init_binding() node._init_binding()
node._init_regs() node._init_regs()
node._set_instance_no()
self.nodes.append(node) self.nodes.append(node)
self._node2enode[dt_node] = node self._node2enode[dt_node] = node
@ -456,6 +475,15 @@ class EDT:
node._init_interrupts() node._init_interrupts()
node._init_pinctrls() node._init_pinctrls()
def _init_compat2enabled(self):
# Creates self.compat2enabled
self.compat2enabled = defaultdict(list)
for node in self.nodes:
if node.enabled:
for compat in node.compats:
self.compat2enabled[compat].append(node)
def _check_binding(self, binding, binding_path): def _check_binding(self, binding, binding_path):
# Does sanity checking on 'binding'. Only takes 'self' for the sake of # Does sanity checking on 'binding'. Only takes 'self' for the sake of
# self._warn(). # self._warn().
@ -689,16 +717,6 @@ class Node:
read_only: read_only:
True if the node has a 'read-only' property, and False otherwise True if the node has a 'read-only' property, and False otherwise
instance_no:
Dictionary that maps each 'compatible' string for the node to a unique
index among all nodes that have that 'compatible' string.
As an example, 'instance_no["foo,led"] == 3' can be read as "this is the
fourth foo,led node".
Only enabled nodes (status != "disabled") are counted. 'instance_no' is
meaningless for disabled nodes.
matching_compat: matching_compat:
The 'compatible' string for the binding that matched the node, or None if The 'compatible' string for the binding that matched the node, or None if
the node has no binding the node has no binding
@ -1297,17 +1315,6 @@ class Node:
return OrderedDict(zip(cell_names, data_list)) return OrderedDict(zip(cell_names, data_list))
def _set_instance_no(self):
# Initializes self.instance_no
self.instance_no = {}
for compat in self.compats:
self.instance_no[compat] = 0
for other_node in self.edt.nodes:
if compat in other_node.compats and other_node.enabled:
self.instance_no[compat] += 1
class Register: class Register:
""" """

View file

@ -42,8 +42,6 @@ def main():
write_top_comment(edt) write_top_comment(edt)
active_compats = set()
for node in edt.nodes: for node in edt.nodes:
if node.enabled and node.matching_compat: if node.enabled and node.matching_compat:
# Skip 'fixed-partitions' devices since they are handled by # Skip 'fixed-partitions' devices since they are handled by
@ -60,10 +58,8 @@ def main():
write_bus(node) write_bus(node)
write_existence_flags(node) write_existence_flags(node)
active_compats.update(node.compats) out_comment("Compatibles appearing on enabled nodes")
for compat in sorted(edt.compat2enabled):
out_comment("Active compatibles (mentioned in DTS + binding found)")
for compat in sorted(active_compats):
#define DT_COMPAT_<COMPAT> 1 #define DT_COMPAT_<COMPAT> 1
out(f"COMPAT_{str2ident(compat)}", 1) out(f"COMPAT_{str2ident(compat)}", 1)
@ -272,12 +268,13 @@ def write_bus(node):
def write_existence_flags(node): def write_existence_flags(node):
# Generate #defines of the form # Generate #defines of the form
# #
# #define DT_INST_<INSTANCE>_<COMPAT> 1 # #define DT_INST_<instance no.>_<compatible string> 1
# #
# These are flags for which devices exist. # for enabled nodes. These are flags for which devices exist.
for compat in node.compats: for compat in node.compats:
out(f"INST_{node.instance_no[compat]}_{str2ident(compat)}", 1) instance_no = node.edt.compat2enabled[compat].index(node)
out(f"INST_{instance_no}_{str2ident(compat)}", 1)
def node_ident(node): def node_ident(node):
@ -337,8 +334,11 @@ def node_instance_aliases(node):
# This is a list since a node can have multiple 'compatible' strings, each # This is a list since a node can have multiple 'compatible' strings, each
# with their own instance number. # with their own instance number.
return [f"INST_{node.instance_no[compat]}_{str2ident(compat)}" res = []
for compat in node.compats] for compat in node.compats:
instance_no = node.edt.compat2enabled[compat].index(node)
res.append(f"INST_{instance_no}_{str2ident(compat)}")
return res
def write_addr_size(edt, prop_name, prefix): def write_addr_size(edt, prop_name, prefix):

View file

@ -294,6 +294,31 @@
default-not-used = <234>; default-not-used = <234>;
}; };
//
// For testing EDT.compat2enabled
//
compat2enabled {
foo-1 {
status = "okay";
compatible = "compat2enabled";
};
foo-disabled {
status = "disabled";
compatible = "compat2enabled";
};
foo-2 {
// No 'status', which is also treated as enabled
compatible = "compat2enabled";
};
// Should not create an entry in compat2enabled, since all nodes
// with the compatible are disabled
bar {
status = "disabled";
compatible = "compat2enabled-disabled";
};
};
// //
// For testing 'bus:' and 'on-bus:' // For testing 'bus:' and 'on-bus:'
// //

View file

@ -164,6 +164,15 @@ warning: "#cells:" in test-bindings/deprecated.yaml is deprecated and will be re
verify_streq(edt.get_node("/deprecated/sub-node").props, verify_streq(edt.get_node("/deprecated/sub-node").props,
"OrderedDict([('foos', <Property, name: foos, type: phandle-array, value: [<ControllerAndData, controller: <Node /deprecated in 'test.dts', binding test-bindings/deprecated.yaml>, data: OrderedDict([('foo', 1), ('bar', 2)])>]>)])") "OrderedDict([('foos', <Property, name: foos, type: phandle-array, value: [<ControllerAndData, controller: <Node /deprecated in 'test.dts', binding test-bindings/deprecated.yaml>, data: OrderedDict([('foo', 1), ('bar', 2)])>]>)])")
#
# Test EDT.compat2enabled
#
verify_streq(edt.compat2enabled["compat2enabled"], "[<Node /compat2enabled/foo-1 in 'test.dts', no binding>, <Node /compat2enabled/foo-2 in 'test.dts', no binding>]")
if "compat2enabled-disabled" in edt.compat2enabled:
fail("'compat2enabled-disabled' should not appear in edt.compat2enabled")
# #
# Test Node.props (derived from DT and 'properties:' in the binding) # Test Node.props (derived from DT and 'properties:' in the binding)
# #