Add support for generating a file containing udevice instances. This avoids the need to create these at run time.
Update a test uclass to include a 'per_device_plat_auto' member, to increase test coverage. Signed-off-by: Simon Glass <s...@chromium.org> --- drivers/misc/test_drv.c | 4 + tools/dtoc/dtb_platdata.py | 145 +++++++++++++++++- tools/dtoc/test_dtoc.py | 303 ++++++++++++++++++++++++++++++++++++- 3 files changed, 442 insertions(+), 10 deletions(-) diff --git a/drivers/misc/test_drv.c b/drivers/misc/test_drv.c index c516d6a262d..a6dd013e319 100644 --- a/drivers/misc/test_drv.c +++ b/drivers/misc/test_drv.c @@ -97,6 +97,7 @@ U_BOOT_DRIVER(denx_u_boot_test_bus) = { .per_child_plat_auto = sizeof(struct dm_test_parent_plat), .child_pre_probe = testbus_child_pre_probe, .child_post_remove = testbus_child_post_remove, + DM_HEADER(<test.h>) }; UCLASS_DRIVER(testbus) = { @@ -105,6 +106,9 @@ UCLASS_DRIVER(testbus) = { .flags = DM_UC_FLAG_SEQ_ALIAS, .child_pre_probe = testbus_child_pre_probe_uclass, .child_post_probe = testbus_child_post_probe_uclass, + + /* This is for dtoc testing only */ + .per_device_plat_auto = sizeof(struct dm_test_uclass_priv), }; static int testfdt_drv_ping(struct udevice *dev, int pingval, int *pingret) diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 069f1295a0c..4b56be681b3 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -657,7 +657,104 @@ class DtbPlatdata(): (var_name, extra, struc.strip(), section)) return '%s_%s' % (var_name, extra) - def _output_prop(self, node, prop): + def alloc_plat(self, info, name, extra, node): + result = self.prep_priv(info, name, '_plat') + if not result: + return None + var_name, struc, section = result + self.buf('struct %s %s\n\t%s_%s = {\n' % + (struc.strip(), section, var_name, extra)) + self.buf('\t.dtplat = {\n') + for pname in sorted(node.props): + self._output_prop(node, node.props[pname], 2) + self.buf('\t},\n') + self.buf('};\n') + return '&%s_%s' % (var_name, extra) + + def _declare_device_inst(self, node, parent_driver): + """Add a device instance declaration to the output + + This declares a DM_DEVICE_INST() for the device being processed + + Args: + node: Node to output + """ + driver = node.driver + uclass = node.uclass + self.buf('\n') + num_lines = len(self._lines) + plat_name = self.alloc_plat(driver.plat, driver.name, node.var_name, + node) + priv_name = self.alloc_priv(driver.priv, driver.name, node.var_name) + parent_plat_name = None + parent_priv_name = None + if parent_driver: + # TODO: deal with uclass providing these values + parent_plat_name = self.alloc_priv( + parent_driver.child_plat, driver.name, node.var_name, + '_parent_plat') + parent_priv_name = self.alloc_priv( + parent_driver.child_priv, driver.name, node.var_name, + '_parent_priv') + uclass_plat_name = self.alloc_priv(uclass.per_dev_plat, driver.name, + node.var_name) + uclass_priv_name = self.alloc_priv(uclass.per_dev_priv, + driver.name + '_uc', node.var_name) + for hdr in driver.headers: + self.buf('#include %s\n' % hdr) + + # Add a blank line if we emitted any stuff above, for readability + if num_lines != len(self._lines): + self.buf('\n') + + self.buf('DM_DEVICE_INST(%s) = {\n' % node.var_name) + self.buf('\t.driver\t\t= DM_DRIVER_REF(%s),\n' % node.struct_name) + self.buf('\t.name\t\t= "%s",\n' % node.struct_name) + if plat_name: + self.buf('\t.plat_\t= %s,\n' % plat_name) + else: + self.buf('\t.plat_\t= &%s%s,\n' % (VAL_PREFIX, node.var_name)) + if parent_plat_name: + self.buf('\t.parent_plat_ = %s,\n' % parent_plat_name) + if uclass_plat_name: + self.buf('\t.uclass_plat_ = %s,\n' % uclass_plat_name) + driver_date = None + + if node != self._fdt.GetRoot(): + compat_list = node.props['compatible'].value + if not isinstance(compat_list, list): + compat_list = [compat_list] + for compat in compat_list: + driver_data = driver.compat.get(compat) + if driver_data: + self.buf('\t.driver_data\t= %s,\n' % driver_data) + break + + if node.parent and node.parent.parent: + self.buf('\t.parent\t\t= DM_DEVICE_REF(%s),\n' % + node.parent.var_name) + if priv_name: + self.buf('\t.priv_\t\t= %s,\n' % priv_name) + self.buf('\t.uclass\t= DM_UCLASS_REF(%s),\n' % uclass.name) + + if uclass_priv_name: + self.buf('\t.uclass_priv_ = %s,\n' % uclass_priv_name) + if parent_priv_name: + self.buf('\t.parent_priv_\t= %s,\n' % parent_priv_name) + self.list_node('uclass_node', uclass.node_refs, node.uclass_seq) + self.list_head('child_head', 'sibling_node', node.child_devs, node.var_name) + if node.parent in self._valid_nodes: + self.list_node('sibling_node', node.parent.child_refs, + node.parent_seq) + # flags is left as 0 + + self.buf('\t.seq_ = %d,\n' % node.seq) + + self.buf('};\n') + self.buf('\n') + return parent_plat_name + + def _output_prop(self, node, prop, tabs=1): """Output a line containing the value of a struct member Args: @@ -667,7 +764,7 @@ class DtbPlatdata(): if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#': return member_name = conv_name_to_c(prop.name) - self.buf('\t%s= ' % tab_to(3, '.' + member_name)) + self.buf('%s%s= ' % ('\t' * tabs, tab_to(3, '.' + member_name))) # Special handling for lists if isinstance(prop.value, list): @@ -900,6 +997,26 @@ class DtbPlatdata(): self.out(''.join(self.get_buf())) + def output_node_instance(self, node): + """Output the C code for a node + + Args: + node (fdt.Node): node to output + """ + parent_driver = node.parent_driver + + self.buf('/*\n') + self.buf(' * Node %s index %d\n' % (node.path, node.idx)) + self.buf(' * driver %s parent %s\n' % (node.driver.name, + parent_driver.name if parent_driver else 'None')) + self.buf('*/\n') + + if not node.driver.plat: + self._output_values(node) + self._declare_device_inst(node, parent_driver) + + self.out(''.join(self.get_buf())) + def check_instantiate(self, require): """Check if self._instantiate is set to the required value @@ -941,6 +1058,27 @@ class DtbPlatdata(): self.out(''.join(self.get_buf())) + def generate_device(self): + """Generate device instances + + This writes out DM_DEVICE_INST() records for each device in the + build. + + See the documentation in doc/driver-model/of-plat.rst for more + information. + """ + if not self.check_instantiate(True): + return + self.out('#include <common.h>\n') + self.out('#include <dm.h>\n') + self.out('#include <dt-structs.h>\n') + self.out('\n') + + for node in self._valid_nodes: + self.output_node_instance(node) + + self.out(''.join(self.get_buf())) + # Types of output file we understand # key: Command used to generate this file @@ -956,6 +1094,9 @@ OUTPUT_FILES = { 'platdata': OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat, 'Declares the U_BOOT_DRIVER() records and platform data'), + 'device': + OutputFile(Ftype.SOURCE, 'dt-device.c', DtbPlatdata.generate_device, + 'Declares the DM_DEVICE_INST() records'), '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 352753a1e3a..e6211bc37dc 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -493,6 +493,285 @@ DM_UCLASS_INST(testfdt) = { }, }; +''' + device_text = '''/* + * DO NOT MODIFY + * + * Declares the DM_DEVICE_INST() records. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ + +/* This file is not used: --instantiate was not enabled */ +''' + device_text_inst = '''/* + * DO NOT MODIFY + * + * Declares the DM_DEVICE_INST() records. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ + +#include <common.h> +#include <dm.h> +#include <dt-structs.h> + +/* + * Node /i2c index 0 + * driver sandbox_i2c parent root_driver +*/ +static struct dtd_sandbox_i2c dtv_i2c = { + .intval = 0x3, +}; + +#include <asm/i2c.h> +u8 _sandbox_i2c_priv_i2c[sizeof(struct sandbox_i2c_priv)] + __attribute__ ((section (".priv_data"))); +#include <i2c.h> +u8 _sandbox_i2c_uc_priv_i2c[sizeof(struct dm_i2c_bus)] + __attribute__ ((section (".priv_data"))); + +DM_DEVICE_INST(i2c) = { + .driver = DM_DRIVER_REF(sandbox_i2c), + .name = "sandbox_i2c", + .plat_ = &dtv_i2c, + .priv_ = _sandbox_i2c_priv_i2c, + .uclass = DM_UCLASS_REF(i2c), + .uclass_priv_ = _sandbox_i2c_uc_priv_i2c, + .uclass_node = { + .prev = &DM_UCLASS_REF(i2c)->dev_head, + .next = &DM_UCLASS_REF(i2c)->dev_head, + }, + .child_head = { + .prev = &DM_DEVICE_REF(i2c)->child_head, + .next = &DM_DEVICE_REF(i2c)->child_head, + }, + .sibling_node = { + .prev = &DM_DEVICE_REF(root)->child_head, + .next = &DM_DEVICE_REF(some_bus)->sibling_node, + }, + .seq_ = 4, +}; + +/* + * Node / index 1 + * driver root_driver parent None +*/ +static struct dtd_root_driver dtv_root = { +}; + +DM_DEVICE_INST(root) = { + .driver = DM_DRIVER_REF(root_driver), + .name = "root_driver", + .plat_ = &dtv_root, + .uclass = DM_UCLASS_REF(root), + .uclass_node = { + .prev = &DM_UCLASS_REF(root)->dev_head, + .next = &DM_UCLASS_REF(root)->dev_head, + }, + .child_head = { + .prev = &DM_DEVICE_REF(spl_test3)->sibling_node, + .next = &DM_DEVICE_REF(i2c)->sibling_node, + }, + .seq_ = 0, +}; + +/* + * Node /some-bus index 2 + * driver denx_u_boot_test_bus parent root_driver +*/ + +#include <dm/test.h> +struct dm_test_pdata __attribute__ ((section (".priv_data"))) + _denx_u_boot_test_bus_plat_some_bus = { + .dtplat = { + .ping_add = 0x4, + .ping_expect = 0x4, + .reg = {0x3, 0x1}, + }, +}; +#include <dm/test.h> +u8 _denx_u_boot_test_bus_priv_some_bus[sizeof(struct dm_test_priv)] + __attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_test_bus_priv_some_bus[sizeof(struct dm_test_uclass_priv)] + __attribute__ ((section (".priv_data"))); +#include <test.h> + +DM_DEVICE_INST(some_bus) = { + .driver = DM_DRIVER_REF(denx_u_boot_test_bus), + .name = "denx_u_boot_test_bus", + .plat_ = &_denx_u_boot_test_bus_plat_some_bus, + .uclass_plat_ = _denx_u_boot_test_bus_priv_some_bus, + .driver_data = DM_TEST_TYPE_FIRST, + .priv_ = _denx_u_boot_test_bus_priv_some_bus, + .uclass = DM_UCLASS_REF(testbus), + .uclass_node = { + .prev = &DM_UCLASS_REF(testbus)->dev_head, + .next = &DM_UCLASS_REF(testbus)->dev_head, + }, + .child_head = { + .prev = &DM_DEVICE_REF(test0)->sibling_node, + .next = &DM_DEVICE_REF(test)->sibling_node, + }, + .sibling_node = { + .prev = &DM_DEVICE_REF(i2c)->sibling_node, + .next = &DM_DEVICE_REF(spl_test)->sibling_node, + }, + .seq_ = 2, +}; + +/* + * Node /spl-test index 3 + * driver sandbox_spl_test parent root_driver +*/ +static struct dtd_sandbox_spl_test dtv_spl_test = { + .boolval = true, + .intval = 0x1, +}; + +DM_DEVICE_INST(spl_test) = { + .driver = DM_DRIVER_REF(sandbox_spl_test), + .name = "sandbox_spl_test", + .plat_ = &dtv_spl_test, + .uclass = DM_UCLASS_REF(misc), + .uclass_node = { + .prev = &DM_UCLASS_REF(misc)->dev_head, + .next = &DM_DEVICE_REF(spl_test3)->uclass_node, + }, + .child_head = { + .prev = &DM_DEVICE_REF(spl_test)->child_head, + .next = &DM_DEVICE_REF(spl_test)->child_head, + }, + .sibling_node = { + .prev = &DM_DEVICE_REF(some_bus)->sibling_node, + .next = &DM_DEVICE_REF(spl_test3)->sibling_node, + }, + .seq_ = 0, +}; + +/* + * Node /spl-test3 index 4 + * driver sandbox_spl_test parent root_driver +*/ +static struct dtd_sandbox_spl_test dtv_spl_test3 = { + .longbytearray = {0x90a0b0c, 0xd0e0f10}, + .stringarray = "one", +}; + +DM_DEVICE_INST(spl_test3) = { + .driver = DM_DRIVER_REF(sandbox_spl_test), + .name = "sandbox_spl_test", + .plat_ = &dtv_spl_test3, + .uclass = DM_UCLASS_REF(misc), + .uclass_node = { + .prev = &DM_DEVICE_REF(spl_test)->uclass_node, + .next = &DM_UCLASS_REF(misc)->dev_head, + }, + .child_head = { + .prev = &DM_DEVICE_REF(spl_test3)->child_head, + .next = &DM_DEVICE_REF(spl_test3)->child_head, + }, + .sibling_node = { + .prev = &DM_DEVICE_REF(spl_test)->sibling_node, + .next = &DM_DEVICE_REF(root)->child_head, + }, + .seq_ = 1, +}; + +/* + * Node /some-bus/test index 5 + * driver denx_u_boot_fdt_test parent denx_u_boot_test_bus +*/ + +#include <dm/test.h> +struct dm_test_pdata __attribute__ ((section (".priv_data"))) + _denx_u_boot_fdt_test_plat_test = { + .dtplat = { + .ping_add = 0x5, + .ping_expect = 0x5, + .reg = {0x5, 0x0}, + }, +}; +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_priv_test[sizeof(struct dm_test_priv)] + __attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_parent_plat_test[sizeof(struct dm_test_parent_plat)] + __attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_parent_priv_test[sizeof(struct dm_test_parent_data)] + __attribute__ ((section (".priv_data"))); + +DM_DEVICE_INST(test) = { + .driver = DM_DRIVER_REF(denx_u_boot_fdt_test), + .name = "denx_u_boot_fdt_test", + .plat_ = &_denx_u_boot_fdt_test_plat_test, + .parent_plat_ = _denx_u_boot_fdt_test_parent_plat_test, + .driver_data = DM_TEST_TYPE_FIRST, + .parent = DM_DEVICE_REF(some_bus), + .priv_ = _denx_u_boot_fdt_test_priv_test, + .uclass = DM_UCLASS_REF(testfdt), + .parent_priv_ = _denx_u_boot_fdt_test_parent_priv_test, + .uclass_node = { + .prev = &DM_UCLASS_REF(testfdt)->dev_head, + .next = &DM_DEVICE_REF(test0)->uclass_node, + }, + .child_head = { + .prev = &DM_DEVICE_REF(test)->child_head, + .next = &DM_DEVICE_REF(test)->child_head, + }, + .sibling_node = { + .prev = &DM_DEVICE_REF(some_bus)->child_head, + .next = &DM_DEVICE_REF(test0)->sibling_node, + }, + .seq_ = 1, +}; + +/* + * Node /some-bus/test0 index 6 + * driver denx_u_boot_fdt_test parent denx_u_boot_test_bus +*/ + +#include <dm/test.h> +struct dm_test_pdata __attribute__ ((section (".priv_data"))) + _denx_u_boot_fdt_test_plat_test0 = { + .dtplat = { + }, +}; +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_priv_test0[sizeof(struct dm_test_priv)] + __attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_parent_plat_test0[sizeof(struct dm_test_parent_plat)] + __attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_parent_priv_test0[sizeof(struct dm_test_parent_data)] + __attribute__ ((section (".priv_data"))); + +DM_DEVICE_INST(test0) = { + .driver = DM_DRIVER_REF(denx_u_boot_fdt_test), + .name = "denx_u_boot_fdt_test", + .plat_ = &_denx_u_boot_fdt_test_plat_test0, + .parent_plat_ = _denx_u_boot_fdt_test_parent_plat_test0, + .driver_data = DM_TEST_TYPE_SECOND, + .parent = DM_DEVICE_REF(some_bus), + .priv_ = _denx_u_boot_fdt_test_priv_test0, + .uclass = DM_UCLASS_REF(testfdt), + .parent_priv_ = _denx_u_boot_fdt_test_parent_priv_test0, + .uclass_node = { + .prev = &DM_DEVICE_REF(test)->uclass_node, + .next = &DM_UCLASS_REF(testfdt)->dev_head, + }, + .child_head = { + .prev = &DM_DEVICE_REF(test0)->child_head, + .next = &DM_DEVICE_REF(test0)->child_head, + }, + .sibling_node = { + .prev = &DM_DEVICE_REF(test)->sibling_node, + .next = &DM_DEVICE_REF(some_bus)->child_head, + }, + .seq_ = 2, +}; + ''' def test_simple(self): @@ -520,8 +799,9 @@ DM_UCLASS_INST(testfdt) = { # Try the 'all' command 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 + self.uclass_text, data) + self._check_strings( + self.decl_text + self.device_text + self.platdata_text + + self.struct_text + self.uclass_text, data) def test_driver_alias(self): """Test output from a device tree file with a driver alias""" @@ -1116,8 +1396,9 @@ U_BOOT_DRVINFO(spl_test2) = { output = tools.GetOutputFilename('output') 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 + self.uclass_text, data) + self._check_strings( + self.decl_text + self.device_text + self.platdata_text + + self.struct_text + self.uclass_text, data) def test_no_command(self): """Test running dtoc without a command""" @@ -1133,7 +1414,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, uclass)", + "Unknown command 'invalid-cmd': (use: decl, device, platdata, struct, uclass)", str(exc.exception)) def test_output_conflict(self): @@ -1161,12 +1442,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(6, len(fnames)) + self.assertEqual(7, 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-uclass.c', 'dt-decl.h'}, + 'dt-uclass.c', 'dt-decl.h', 'dt-device.c'}, leafs) def setup_process_test(self): @@ -1376,8 +1657,14 @@ U_BOOT_DRVINFO(spl_test2) = { self._check_strings(UCLASS_HEADER_COMMON + self.uclass_text_inst, data) + self.run_test(['device'], dtb_file, output, True) + with open(output) as infile: + data = infile.read() + + self._check_strings(self.device_text_inst, data) + def test_inst_no_hdr(self): - """Test dealing with a struct that has no header""" + """Test dealing with a struct tsssshat has no header""" dtb_file = get_dtb_file('dtoc_test_inst.dts') output = tools.GetOutputFilename('output') -- 2.29.2.729.g45daf8777d-goog