dtoc: Generate uclass devices
authorSimon Glass <sjg@chromium.org>
Wed, 3 Feb 2021 13:01:20 +0000 (06:01 -0700)
committerSimon Glass <sjg@chromium.org>
Mon, 22 Mar 2021 06:23:27 +0000 (19:23 +1300)
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 <sjg@chromium.org>
drivers/misc/test_drv.c
include/dm/test.h
tools/dtoc/dtb_platdata.py
tools/dtoc/test_dtoc.py

index a2a77d3..f431a57 100644 (file)
@@ -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[] = {
index fe1cc2e..30f71ed 100644 (file)
@@ -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
  */
index ab26c4a..6dadf37 100644 (file)
@@ -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 <common.h>\n')
+        self.out('#include <dm.h>\n')
+        self.out('#include <dt-structs.h>\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)'),
     }
 
 
index 56d5c8d..053d140 100755 (executable)
@@ -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 <dt-structs.h>
 '''
 
+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 <common.h>
+#include <dm.h>
+#include <dt-structs.h>
+
+/*
+ * 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 <dm/test.h>
+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())