[PATCH 42/49] dtoc: Set up the uclasses that are used
Simon Glass
sjg at chromium.org
Tue Dec 29 04:35:28 CET 2020
We only care about uclasses that are actually used. This is determined by
the drivers that use them. Check all the used drivers and build a list of
'valid' uclasses.
Also add references to the uclasses so we can generate C code that uses
them. Attach a uclass to each valid driver.
For the tests, now that we have uclasses we must create an explicit test
for the case where a node does not have one. This should only happen if
the source code does not build, or the source-code scanning fails to find
it.
Signed-off-by: Simon Glass <sjg at chromium.org>
---
tools/dtoc/dtb_platdata.py | 46 ++++++++++++++++++++++++-------------
tools/dtoc/src_scan.py | 45 ++++++++++++++++++++++++++++++++++++
tools/dtoc/test_dtoc.py | 27 ++++++++++++++++++++--
tools/dtoc/test_src_scan.py | 17 ++++++++++++++
4 files changed, 117 insertions(+), 18 deletions(-)
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py
index 9e99c63ae70..af21156659b 100644
--- a/tools/dtoc/dtb_platdata.py
+++ b/tools/dtoc/dtb_platdata.py
@@ -151,6 +151,8 @@ class DtbPlatdata():
key (str): Field name
value: Prop object with field information
_basedir (str): Base directory of source tree
+ _valid_uclasses (list of src_scan.Uclass): List of uclasses needed for
+ the selected devices (see _valid_node), in alphabetical order
"""
def __init__(self, scan, dtb_fname, include_disabled):
self._scan = scan
@@ -164,6 +166,7 @@ class DtbPlatdata():
self._dirnames = [None] * len(Ftype)
self._struct_data = collections.OrderedDict()
self._basedir = None
+ self._valid_uclasses = None
def setup_output_dirs(self, output_dirs):
"""Set up the output directories
@@ -677,23 +680,12 @@ class DtbPlatdata():
elif result is False:
print("Could not find uclass for alias '%s'" % prop.name)
- def assign_seq(self):
+ def assign_seqs(self):
"""Assign a sequence number to each node"""
for node in self._valid_nodes_unsorted:
- if node.driver and node.seq == -1 and node.uclass:
- uclass = node.uclass
- num = uclass.alias_path_to_num.get(node.path)
- if num is not None:
- node.seq = num
- else:
- # Dynamically allocate the next available value after all
- # existing ones
- for seq in range(1000):
- if seq not in uclass.alias_num_to_node:
- break
- node.seq = seq
- uclass.alias_path_to_num[node.path] = seq
- uclass.alias_num_to_node[seq] = node
+ seq = self._scan.assign_seq(node)
+ if seq is not None:
+ node.seq = seq
def process_nodes(self, need_drivers):
nodes_to_output = list(self._valid_nodes)
@@ -710,6 +702,16 @@ class DtbPlatdata():
raise ValueError("Cannot parse/find driver for '%s'" %
node.struct_name)
node.driver = driver
+ uclass = self._scan._uclass.get(driver.uclass_id)
+ if not uclass:
+ raise ValueError("Cannot parse/find uclass '%s' for driver '%s'" %
+ (driver.uclass_id, node.struct_name))
+ node.uclass = uclass
+ node.uclass_seq = len(node.uclass.devs)
+ node.uclass.devs.append(node)
+ uclass.node_refs[node.uclass_seq] = \
+ '&%s->uclass_node' % node.dev_ref
+
parent_driver = None
if node.parent in self._valid_nodes:
parent_driver = self._scan.get_driver(node.parent.struct_name)
@@ -730,6 +732,18 @@ class DtbPlatdata():
node.child_refs[-1] = ref
node.child_refs[len(node.child_devs)] = ref
+ uclass_set = set()
+ for driver in self._scan._drivers.values():
+ if driver.used and driver.uclass:
+ uclass_set.add(driver.uclass)
+ self._valid_uclasses = sorted(list(uclass_set),
+ key=lambda uc: uc.uclass_id)
+
+ for seq, uclass in enumerate(uclass_set):
+ ref = '&DM_UCLASS_REF(%s)->dev_head' % uclass.name
+ uclass.node_refs[-1] = ref
+ uclass.node_refs[len(uclass.devs)] = ref
+
def output_node(self, node):
"""Output the C code for a node
@@ -833,7 +847,7 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase,
plat.scan_phandles()
plat.process_nodes(False)
plat.read_aliases()
- plat.assign_seq()
+ plat.assign_seqs()
cmds = args[0].split(',')
if 'all' in cmds:
diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py
index a2750321791..07bfb35195f 100644
--- a/tools/dtoc/src_scan.py
+++ b/tools/dtoc/src_scan.py
@@ -74,6 +74,7 @@ class Driver:
found after this one
warn_dups (bool): True if the duplicates are not distinguisble using
the phase
+ uclass (Uclass): uclass for this driver
"""
def __init__(self, name, fname):
self.name = name
@@ -89,6 +90,7 @@ class Driver:
self.headers = []
self.dups = []
self.warn_dups = False
+ self.uclass = None
def __eq__(self, other):
return (self.name == other.name and
@@ -123,6 +125,10 @@ class UclassDriver:
alias_path_to_num (dict): Convert a path to an alias number
key (str): Full path to node (e.g. '/soc/pci')
seq (int): Alias number, e.g. 2 for "pci2"
+ devs (list): List of devices in this uclass, each a Node
+ node_refs (dict): References in the linked list of devices:
+ key (int): Sequence number (0=first, n-1=last, -1=head, n=tail)
+ value (str): Reference to the device at that position
"""
def __init__(self, name):
self.name = name
@@ -134,6 +140,8 @@ class UclassDriver:
self.per_child_plat = ''
self.alias_num_to_node = {}
self.alias_path_to_num = {}
+ self.devs = []
+ self.node_refs = {}
def __eq__(self, other):
return (self.name == other.name and
@@ -639,6 +647,12 @@ class Scanner:
else:
self.scan_driver(self._basedir + '/' + fname)
+ # Get the uclass for each driver
+ # TODO: Can we just get the uclass for the ones we use, e.g. in
+ # mark_used()?
+ for driver in self._drivers.values():
+ driver.uclass = self._uclass.get(driver.uclass_id)
+
def mark_used(self, nodes):
"""Mark the drivers associated with a list of nodes as 'used'
@@ -682,3 +696,34 @@ class Scanner:
uclass.alias_path_to_num[node.path] = int(num)
return True
return False
+
+ def assign_seq(self, node):
+ """Figure out the sequence number for a node
+
+ This looks in the node's uclass and assigns a sequence number if needed,
+ based on the aliases and other nodes in that uclass.
+
+ It updates the uclass alias_path_to_num and alias_num_to_node
+
+ Args:
+ node (Node): Node object to look up
+ """
+ if node.driver and node.seq == -1 and node.uclass:
+ uclass = node.uclass
+ num = uclass.alias_path_to_num.get(node.path)
+ if num is not None:
+ return num
+ else:
+ # Dynamically allocate the next available value after all
+ # existing ones
+ if uclass.alias_num_to_node:
+ start = max(uclass.alias_num_to_node.keys())
+ else:
+ start = -1
+ for seq in range(start + 1, 1000):
+ if seq not in uclass.alias_num_to_node:
+ break
+ uclass.alias_path_to_num[node.path] = seq
+ uclass.alias_num_to_node[seq] = node
+ return seq
+ return None
diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py
index b4c0a042a9f..c182966f552 100755
--- a/tools/dtoc/test_dtoc.py
+++ b/tools/dtoc/test_dtoc.py
@@ -141,6 +141,8 @@ class TestDtoc(unittest.TestCase):
Returns:
DtbPlatdata object
"""
+ # Make a copy of the 'scan' object, since it includes uclasses and
+ # drivers, which get updated during execution.
return dtb_platdata.run_steps(args, dtb_file, False, output, [], None,
warning_disabled=True, scan=copy_scan())
@@ -1033,6 +1035,16 @@ U_BOOT_DRVINFO(spl_test2) = {
self.assertIn("Cannot parse/find driver for 'sandbox_pmic",
str(exc.exception))
+ def test_process_nodes_bad_uclass(self):
+ plat, scan = self.setup_process_test()
+
+ self.assertIn('UCLASS_I2C', scan._uclass)
+ del scan._uclass['UCLASS_I2C']
+ with self.assertRaises(ValueError) as exc:
+ plat.process_nodes(True)
+ self.assertIn("Cannot parse/find uclass 'UCLASS_I2C' for driver 'sandbox_i2c'",
+ str(exc.exception))
+
def test_process_nodes_used(self):
"""Test processing nodes to add various info"""
plat, scan = self.setup_process_test()
@@ -1052,10 +1064,13 @@ U_BOOT_DRVINFO(spl_test2) = {
scan = plat._scan
testfdt_node = plat._fdt.GetNode('/some-bus/test')
+ test0_node = plat._fdt.GetNode('/some-bus/test0')
self.assertIn('UCLASS_TEST_FDT', scan._uclass)
uc = scan._uclass['UCLASS_TEST_FDT']
- self.assertEqual({1: testfdt_node}, uc.alias_num_to_node)
- self.assertEqual({'/some-bus/test': 1}, uc.alias_path_to_num)
+ self.assertEqual({1: testfdt_node, 2: test0_node},
+ uc.alias_num_to_node)
+ self.assertEqual({'/some-bus/test': 1, '/some-bus/test0': 2},
+ uc.alias_path_to_num)
# Try adding an alias that doesn't exist
self.assertFalse(scan.add_uclass_alias('fred', 3, None))
@@ -1098,3 +1113,11 @@ U_BOOT_DRVINFO(spl_test2) = {
dtb_file = get_dtb_file('dtoc_test_inst.dts')
output = tools.GetOutputFilename('output')
plat = self.run_test(['struct'], dtb_file, output)
+
+ scan = plat._scan
+ testfdt = plat._fdt.GetNode('/some-bus/test')
+ self.assertEqual(1, testfdt.seq)
+ i2c = plat._fdt.GetNode('/i2c')
+ self.assertEqual(4, i2c.seq)
+ spl = plat._fdt.GetNode('/spl-test')
+ self.assertEqual(0, spl.seq)
diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py
index 598ff256a60..d32aa58400f 100644
--- a/tools/dtoc/test_src_scan.py
+++ b/tools/dtoc/test_src_scan.py
@@ -466,3 +466,20 @@ U_BOOT_DRIVER(%s) = {
with test_util.capture_sys_output() as (stdout, _):
scan.mark_used([node])
self.assertEqual('', stdout.getvalue().strip())
+
+ def test_sequence(self):
+ """Test assignment of sequence numnbers"""
+ scan = src_scan.Scanner(None, False, None, '')
+ node = FakeNode()
+ uc = src_scan.UclassDriver('UCLASS_I2C')
+ node.uclass = uc
+ node.driver = True
+ node.seq = -1
+ node.path = 'mypath'
+ uc.alias_num_to_node[2] = node
+
+ # This should assign 3 (after the 2 that exists)
+ seq = scan.assign_seq(node)
+ self.assertEqual(3, seq)
+ self.assertEqual({'mypath': 3}, uc.alias_path_to_num)
+ self.assertEqual({2: node, 3: node}, uc.alias_num_to_node)
--
2.29.2.729.g45daf8777d-goog
More information about the U-Boot
mailing list