From fd471e2ce14342e7186cf9f95a82ce55a9bea6e4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:00 -0700 Subject: [PATCH] dtoc: Process nodes to set up required properties Add logic to assign property values to nodes as required by dtoc. The references allow nodes to refer to each other in C code. The macros used by dtoc are not yet defined in driver model. They will be added along with the actual driver model implementation. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 37 +++++++++++++++++++ tools/dtoc/src_scan.py | 11 ++++++ tools/dtoc/test_dtoc.py | 76 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 2ec22edfbf..ad71f703e5 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -647,6 +647,38 @@ class DtbPlatdata(): self._output_prop(node, node.props[pname]) self.buf('};\n') + def process_nodes(self, need_drivers): + nodes_to_output = list(self._valid_nodes) + + for node in nodes_to_output: + node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name + driver = self._scan.get_driver(node.struct_name) + if not driver: + if not need_drivers: + continue + raise ValueError("Cannot parse/find driver for '%s'" % + node.struct_name) + node.driver = driver + parent_driver = None + if node.parent in self._valid_nodes: + parent_driver = self._scan.get_driver(node.parent.struct_name) + if not parent_driver: + if not need_drivers: + continue + raise ValueError( + "Cannot parse/find parent driver '%s' for '%s'" % + (node.parent.struct_name, node.struct_name)) + node.parent_seq = len(node.parent.child_devs) + node.parent.child_devs.append(node) + node.parent.child_refs[node.parent_seq] = \ + '&%s->sibling_node' % node.dev_ref + node.parent_driver = parent_driver + + for node in nodes_to_output: + ref = '&%s->child_head' % node.dev_ref + node.child_refs[-1] = ref + node.child_refs[len(node.child_devs)] = ref + def output_node(self, node): """Output the C code for a node @@ -731,6 +763,9 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, if not scan: scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional) scan.scan_drivers() + do_process = True + else: + do_process = False plat = DtbPlatdata(scan, dtb_file, include_disabled) plat.scan_dtb() plat.scan_tree() @@ -739,6 +774,8 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, plat.setup_output_dirs(output_dirs) plat.scan_structs() plat.scan_phandles() + if do_process: + plat.process_nodes(False) cmds = args[0].split(',') if 'all' in cmds: diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index bf3e5de9b1..504dac008d 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -188,6 +188,17 @@ class Scanner: self._uclass = {} self._structs = {} + def get_driver(self, name): + """Get a driver given its name + + Args: + name (str): Driver name + + Returns: + Driver: Driver or None if not found + """ + return self._drivers.get(name) + def get_normalized_compat_name(self, node): """Get a node's normalized compat name diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 9049c2895f..3e98e36312 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -953,3 +953,79 @@ U_BOOT_DRVINFO(spl_test2) = { self.assertEqual( {'dt-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb'}, leafs) + + def setup_process_test(self): + """Set up a test of process_nodes() + + This uses saved_scan but returns a deep copy of it, so it is safe to + modify it in these tests + + Returns: + tuple: + DtbPlatdata: object to test + Scanner: scanner to use + """ + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + + # Take a copy before messing with it + scan = copy.deepcopy(saved_scan) + plat = dtb_platdata.DtbPlatdata(scan, dtb_file, False) + plat.scan_dtb() + plat.scan_tree() + plat.prepare_nodes() + return plat, scan + + def test_process_nodes(self): + """Test processing nodes to add various info""" + plat, scan = self.setup_process_test() + plat.process_nodes(True) + + i2c_node = plat._fdt.GetNode('/i2c@0') + pmic_node = plat._fdt.GetNode('/i2c@0/pmic@9') + pmic = scan._drivers['sandbox_pmic'] + i2c = scan._drivers['sandbox_i2c'] + self.assertEqual('DM_DEVICE_REF(pmic_at_9)', pmic_node.dev_ref) + self.assertEqual(pmic, pmic_node.driver) + self.assertEqual(i2c_node, pmic_node.parent) + self.assertEqual(i2c, pmic_node.parent_driver) + + # The pmic is the only child + self.assertEqual(pmic_node.parent_seq, 0) + self.assertEqual([pmic_node], i2c_node.child_devs) + + # Start and end of the list should be the child_head + ref = '&DM_DEVICE_REF(i2c_at_0)->child_head' + self.assertEqual( + {-1: ref, 0: '&DM_DEVICE_REF(pmic_at_9)->sibling_node', 1: ref}, + i2c_node.child_refs) + + def test_process_nodes_bad_parent(self): + # Pretend that i2c has a parent (the pmic) and delete that driver + plat, scan = self.setup_process_test() + + i2c_node = plat._fdt.GetNode('/i2c@0') + pmic_node = plat._fdt.GetNode('/i2c@0/pmic@9') + del scan._drivers['sandbox_pmic'] + i2c_node.parent = pmic_node + + # Process twice, the second time to generate an exception + plat.process_nodes(False) + with self.assertRaises(ValueError) as exc: + plat.process_nodes(True) + self.assertIn( + "Cannot parse/find parent driver 'sandbox_pmic' for 'sandbox_i2c", + str(exc.exception)) + + def test_process_nodes_bad_node(self): + plat, scan = self.setup_process_test() + + # Now remove the pmic driver + del scan._drivers['sandbox_pmic'] + + # Process twice, the second time to generate an exception + plat.process_nodes(False) + with self.assertRaises(ValueError) as exc: + plat.process_nodes(True) + self.assertIn("Cannot parse/find driver for 'sandbox_pmic", + str(exc.exception)) -- 2.39.5