From ea74c95103c66282b8c43e7893bdcd533cab220f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Feb 2021 06:01:20 -0700 Subject: [PATCH] dtoc: Generate uclass devices Add support for generating a file containing uclass instances. This avoids the need to create these at run time. Update a test uclass to include a 'priv_auto' member, to increase test coverage. Signed-off-by: Simon Glass --- drivers/misc/test_drv.c | 1 + include/dm/test.h | 5 ++ tools/dtoc/dtb_platdata.py | 93 +++++++++++++++++++++++++ tools/dtoc/test_dtoc.py | 164 +++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 250 insertions(+), 13 deletions(-) diff --git a/drivers/misc/test_drv.c b/drivers/misc/test_drv.c index a2a77d3..f431a57 100644 --- a/drivers/misc/test_drv.c +++ b/drivers/misc/test_drv.c @@ -205,6 +205,7 @@ UCLASS_DRIVER(testfdt) = { .name = "testfdt", .id = UCLASS_TEST_FDT, .flags = DM_UC_FLAG_SEQ_ALIAS, + .priv_auto = sizeof(struct dm_test_uc_priv), }; static const struct udevice_id testfdtm_ids[] = { diff --git a/include/dm/test.h b/include/dm/test.h index fe1cc2e..30f71ed 100644 --- a/include/dm/test.h +++ b/include/dm/test.h @@ -71,6 +71,11 @@ struct dm_test_priv { int uclass_postp; }; +/* struct dm_test_uc_priv - private data for the testdrv uclass */ +struct dm_test_uc_priv { + int dummy; +}; + /** * struct dm_test_perdev_class_priv - private per-device data for test uclass */ diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index ab26c4a..6dadf37 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -246,6 +246,7 @@ class DtbPlatdata(): """ if self._outfile != sys.stdout: self._outfile.close() + self._outfile = None def out(self, line): """Output a string to the output file @@ -649,6 +650,27 @@ class DtbPlatdata(): self.buf('};\n') self.buf('\n') + def prep_priv(self, struc, name, suffix, section='.priv_data'): + if not struc: + return None + var_name = '_%s%s' % (name, suffix) + hdr = self._scan._structs.get(struc) + if hdr: + self.buf('#include <%s>\n' % hdr.fname) + else: + print('Warning: Cannot find header file for struct %s' % struc) + attr = '__attribute__ ((section ("%s")))' % section + return var_name, struc, attr + + def alloc_priv(self, info, name, extra, suffix='_priv'): + result = self.prep_priv(info, name, suffix) + if not result: + return None + var_name, struc, section = result + self.buf('u8 %s_%s[sizeof(struct %s)]\n\t%s;\n' % + (var_name, extra, struc.strip(), section)) + return '%s_%s' % (var_name, extra) + def _output_prop(self, node, prop): """Output a line containing the value of a struct member @@ -680,6 +702,74 @@ class DtbPlatdata(): self._output_prop(node, node.props[pname]) self.buf('};\n') + def list_head(self, head_member, node_member, node_refs, var_name): + self.buf('\t.%s\t= {\n' % head_member) + if node_refs: + last = node_refs[-1].dev_ref + first = node_refs[0].dev_ref + member = node_member + else: + last = 'DM_DEVICE_REF(%s)' % var_name + first = last + member = head_member + self.buf('\t\t.prev = &%s->%s,\n' % (last, member)) + self.buf('\t\t.next = &%s->%s,\n' % (first, member)) + self.buf('\t},\n') + + def list_node(self, member, node_refs, seq): + self.buf('\t.%s\t= {\n' % member) + self.buf('\t\t.prev = %s,\n' % node_refs[seq - 1]) + self.buf('\t\t.next = %s,\n' % node_refs[seq + 1]) + self.buf('\t},\n') + + def generate_uclasses(self): + if not self.check_instantiate(True): + return + self.out('\n') + self.out('#include \n') + self.out('#include \n') + self.out('#include \n') + self.out('\n') + self.buf('/*\n') + self.buf(' * uclass declarations\n') + self.buf(' *\n') + self.buf(' * Sequence numbers:\n') + uclass_list = self._valid_uclasses + for uclass in uclass_list: + if uclass.alias_num_to_node: + self.buf(' * %s: %s\n' % (uclass.name, uclass.uclass_id)) + for seq, node in uclass.alias_num_to_node.items(): + self.buf(' * %d: %s\n' % (seq, node.path)) + self.buf(' */\n') + + uclass_node = {} + for seq, uclass in enumerate(uclass_list): + uclass_node[seq] = ('&DM_UCLASS_REF(%s)->sibling_node' % + uclass.name) + uclass_node[-1] = '&uclass_head' + uclass_node[len(uclass_list)] = '&uclass_head' + self.buf('\n') + self.buf('struct list_head %s = {\n' % 'uclass_head') + self.buf('\t.prev = %s,\n' % uclass_node[len(uclass_list) -1]) + self.buf('\t.next = %s,\n' % uclass_node[0]) + self.buf('};\n') + self.buf('\n') + + for seq, uclass in enumerate(uclass_list): + uc_drv = self._scan._uclass.get(uclass.uclass_id) + + priv_name = self.alloc_priv(uc_drv.priv, uc_drv.name, '') + + self.buf('DM_UCLASS_INST(%s) = {\n' % uclass.name) + if priv_name: + self.buf('\t.priv_\t\t= %s,\n' % priv_name) + self.buf('\t.uc_drv\t\t= DM_UCLASS_DRIVER_REF(%s),\n' % uclass.name) + self.list_node('sibling_node', uclass_node, seq) + self.list_head('dev_head', 'uclass_node', uc_drv.devs, None) + self.buf('};\n') + self.buf('\n') + self.out(''.join(self.get_buf())) + def read_aliases(self): """Read the aliases and attach the information to self._alias @@ -894,6 +984,9 @@ OUTPUT_FILES = { 'platdata': OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat, 'Declares the U_BOOT_DRIVER() records and platform data'), + 'uclass': + OutputFile(Ftype.SOURCE, 'dt-uclass.c', DtbPlatdata.generate_uclasses, + 'Declares the uclass instances (struct uclass)'), } diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 56d5c8d..053d140 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -16,6 +16,7 @@ import os import struct import unittest +from dtb_platdata import Ftype from dtb_platdata import get_value from dtb_platdata import tab_to from dtoc import dtb_platdata @@ -65,6 +66,18 @@ C_HEADER = C_HEADER_PRE + ''' #include ''' +UCLASS_HEADER_COMMON = '''/* + * DO NOT MODIFY + * + * Declares the uclass instances (struct uclass). + * This was generated by dtoc from a .dtb (device tree binary) file. + */ +''' + +UCLASS_HEADER = UCLASS_HEADER_COMMON + ''' +/* This file is not used: --instantiate was not enabled */ +''' + # Scanner saved from a previous run of the tests (to speed things up) saved_scan = None @@ -245,31 +258,35 @@ DM_UCLASS_DRIVER_DECL(pmic); /* driver declarations - these allow DM_DRIVER_GET() to be used */ DM_DRIVER_DECL(sandbox_i2c); -DM_DRIVER_DECL(sandbox_pmic); DM_DRIVER_DECL(root_driver); +DM_DRIVER_DECL(denx_u_boot_test_bus); DM_DRIVER_DECL(sandbox_spl_test); DM_DRIVER_DECL(sandbox_spl_test); -DM_DRIVER_DECL(sandbox_spl_test); +DM_DRIVER_DECL(denx_u_boot_fdt_test); +DM_DRIVER_DECL(denx_u_boot_fdt_test); /* device declarations - these allow DM_DEVICE_REF() to be used */ -DM_DEVICE_DECL(i2c_at_0); -DM_DEVICE_DECL(pmic_at_9); +DM_DEVICE_DECL(i2c); DM_DEVICE_DECL(root); +DM_DEVICE_DECL(some_bus); DM_DEVICE_DECL(spl_test); -DM_DEVICE_DECL(spl_test2); DM_DEVICE_DECL(spl_test3); +DM_DEVICE_DECL(test); +DM_DEVICE_DECL(test0); /* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */ DM_UCLASS_DRIVER_DECL(i2c); DM_UCLASS_DRIVER_DECL(misc); -DM_UCLASS_DRIVER_DECL(pmic); DM_UCLASS_DRIVER_DECL(root); +DM_UCLASS_DRIVER_DECL(testbus); +DM_UCLASS_DRIVER_DECL(testfdt); /* uclass declarations - needed for DM_UCLASS_REF() */ DM_UCLASS_DECL(i2c); DM_UCLASS_DECL(misc); -DM_UCLASS_DECL(pmic); DM_UCLASS_DECL(root); +DM_UCLASS_DECL(testbus); +DM_UCLASS_DECL(testfdt); ''' struct_text = HEADER + ''' struct dtd_sandbox_i2c { @@ -395,6 +412,101 @@ U_BOOT_DRVINFO(spl_test3) = { }; ''' + uclass_text = UCLASS_HEADER + uclass_text_inst = ''' + +#include +#include +#include + +/* + * uclass declarations + * + * Sequence numbers: + * i2c: UCLASS_I2C + * 4: /i2c + * misc: UCLASS_MISC + * 0: /spl-test + * 1: /spl-test3 + * root: UCLASS_ROOT + * 0: / + * testbus: UCLASS_TEST_BUS + * 2: /some-bus + * testfdt: UCLASS_TEST_FDT + * 1: /some-bus/test + * 2: /some-bus/test0 + */ + +struct list_head uclass_head = { + .prev = &DM_UCLASS_REF(testfdt)->sibling_node, + .next = &DM_UCLASS_REF(i2c)->sibling_node, +}; + +DM_UCLASS_INST(i2c) = { + .uc_drv = DM_UCLASS_DRIVER_REF(i2c), + .sibling_node = { + .prev = &uclass_head, + .next = &DM_UCLASS_REF(misc)->sibling_node, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(i2c)->uclass_node, + .next = &DM_DEVICE_REF(i2c)->uclass_node, + }, +}; + +DM_UCLASS_INST(misc) = { + .uc_drv = DM_UCLASS_DRIVER_REF(misc), + .sibling_node = { + .prev = &DM_UCLASS_REF(i2c)->sibling_node, + .next = &DM_UCLASS_REF(root)->sibling_node, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(spl_test3)->uclass_node, + .next = &DM_DEVICE_REF(spl_test)->uclass_node, + }, +}; + +DM_UCLASS_INST(root) = { + .uc_drv = DM_UCLASS_DRIVER_REF(root), + .sibling_node = { + .prev = &DM_UCLASS_REF(misc)->sibling_node, + .next = &DM_UCLASS_REF(testbus)->sibling_node, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(root)->uclass_node, + .next = &DM_DEVICE_REF(root)->uclass_node, + }, +}; + +DM_UCLASS_INST(testbus) = { + .uc_drv = DM_UCLASS_DRIVER_REF(testbus), + .sibling_node = { + .prev = &DM_UCLASS_REF(root)->sibling_node, + .next = &DM_UCLASS_REF(testfdt)->sibling_node, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(some_bus)->uclass_node, + .next = &DM_DEVICE_REF(some_bus)->uclass_node, + }, +}; + +#include +u8 _testfdt_priv_[sizeof(struct dm_test_uc_priv)] + __attribute__ ((section (".priv_data"))); +DM_UCLASS_INST(testfdt) = { + .priv_ = _testfdt_priv_, + .uc_drv = DM_UCLASS_DRIVER_REF(testfdt), + .sibling_node = { + .prev = &DM_UCLASS_REF(testbus)->sibling_node, + .next = &uclass_head, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(test0)->uclass_node, + .next = &DM_DEVICE_REF(test)->uclass_node, + }, +}; + +''' def test_simple(self): """Test output from some simple nodes with various types of data""" @@ -422,7 +534,7 @@ U_BOOT_DRVINFO(spl_test3) = { self.run_test(['all'], dtb_file, output) data = tools.ReadFile(output, binary=False) self._check_strings(self.decl_text + self.platdata_text + - self.struct_text, data) + self.struct_text + self.uclass_text, data) def test_driver_alias(self): """Test output from a device tree file with a driver alias""" @@ -1125,7 +1237,7 @@ U_BOOT_DRVINFO(spl_test2) = { self.run_test(['all'], dtb_file, output) data = tools.ReadFile(output, binary=False) self._check_strings(self.decl_text + self.platdata_text + - self.struct_text, data) + self.struct_text + self.uclass_text, data) def test_no_command(self): """Test running dtoc without a command""" @@ -1141,7 +1253,7 @@ U_BOOT_DRVINFO(spl_test2) = { with self.assertRaises(ValueError) as exc: self.run_test(['invalid-cmd'], dtb_file, output) self.assertIn( - "Unknown command 'invalid-cmd': (use: decl, platdata, struct)", + "Unknown command 'invalid-cmd': (use: decl, platdata, struct, uclass)", str(exc.exception)) def test_output_conflict(self): @@ -1169,12 +1281,12 @@ U_BOOT_DRVINFO(spl_test2) = { ['all'], dtb_file, False, None, [outdir], None, False, warning_disabled=True, scan=copy_scan()) fnames = glob.glob(outdir + '/*') - self.assertEqual(5, len(fnames)) + self.assertEqual(6, len(fnames)) leafs = set(os.path.basename(fname) for fname in fnames) self.assertEqual( {'dt-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb', - 'dt-decl.h'}, + 'dt-uclass.c', 'dt-decl.h'}, leafs) def setup_process_test(self): @@ -1363,7 +1475,7 @@ U_BOOT_DRVINFO(spl_test2) = { def test_simple_inst(self): """Test output from some simple nodes with instantiate enabled""" - dtb_file = get_dtb_file('dtoc_test_simple.dts') + dtb_file = get_dtb_file('dtoc_test_inst.dts') output = tools.GetOutputFilename('output') self.run_test(['decl'], dtb_file, output, True) @@ -1379,3 +1491,29 @@ U_BOOT_DRVINFO(spl_test2) = { self._check_strings(C_HEADER_PRE + ''' /* This file is not used: --instantiate was enabled */ ''', data) + + self.run_test(['uclass'], dtb_file, output, True) + with open(output) as infile: + data = infile.read() + + self._check_strings(UCLASS_HEADER_COMMON + self.uclass_text_inst, data) + + def test_inst_no_hdr(self): + """Test dealing with a struct that has no header""" + dtb_file = get_dtb_file('dtoc_test_inst.dts') + output = tools.GetOutputFilename('output') + + # Run it once to set everything up + plat = self.run_test(['decl'], dtb_file, output, True) + scan = plat._scan + + # Restart the output file and delete any record of the uclass' struct + plat.setup_output(Ftype.SOURCE, output) + del scan._structs['dm_test_uc_priv'] + + # Now generate the uclasses, which should provide a warning + with test_util.capture_sys_output() as (stdout, _): + plat.generate_uclasses() + self.assertEqual( + 'Warning: Cannot find header file for struct dm_test_uc_priv', + stdout.getvalue().strip()) -- 2.7.4