summaryrefslogtreecommitdiff
path: root/tools/dtoc
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2021-02-03 06:01:10 -0700
committerSimon Glass <sjg@chromium.org>2021-03-22 19:23:27 +1300
commit337d6972f5f5294971db52809f83c0454cb4e7ba (patch)
tree88828d958b7e3f7fbc92fb101e8e10acea276c9f /tools/dtoc
parent074197aadfef2cfe437ecc1fe057ef21f4eef0da (diff)
dtoc: Set up the uclasses that are used
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@chromium.org>
Diffstat (limited to 'tools/dtoc')
-rw-r--r--tools/dtoc/dtb_platdata.py46
-rw-r--r--tools/dtoc/src_scan.py45
-rwxr-xr-xtools/dtoc/test_dtoc.py29
-rw-r--r--tools/dtoc/test_src_scan.py17
4 files changed, 119 insertions, 18 deletions
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py
index 9e99c63ae7..af21156659 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 bb22b0b64f..8619206a8d 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 b4c0a042a9..b077cf0e76 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,13 @@ 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')
+
+ # For now this uclass is not compiled in, so no sequence is assigned
+ 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 598ff256a6..d32aa58400 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)