[PATCH 49/49] dtoc: Generate device instances

Simon Glass sjg at chromium.org
Tue Dec 29 04:35:35 CET 2020


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 <sjg at 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



More information about the U-Boot mailing list