[PATCH 48/49] dtoc: Generate uclass devices
Simon Glass
sjg at chromium.org
Tue Dec 29 04:35:34 CET 2020
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 at chromium.org>
---
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 ac762fd9fea..c516d6a262d 100644
--- a/drivers/misc/test_drv.c
+++ b/drivers/misc/test_drv.c
@@ -204,6 +204,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 6ac6672cd6f..c37085e9264 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 befe7c14901..069f1295a0c 100644
--- a/tools/dtoc/dtb_platdata.py
+++ b/tools/dtoc/dtb_platdata.py
@@ -232,6 +232,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
@@ -635,6 +636,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
@@ -666,6 +688,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
@@ -866,6 +956,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 1381c7eded3..352753a1e3a 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 <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 {
@@ -381,6 +398,101 @@ U_BOOT_DRVINFO(spl_test3) = {
\t.parent_idx\t= -1,
};
+'''
+ 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):
@@ -409,7 +521,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"""
@@ -1005,7 +1117,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"""
@@ -1021,7 +1133,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):
@@ -1049,12 +1161,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):
@@ -1241,7 +1353,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)
@@ -1257,3 +1369,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.29.2.729.g45daf8777d-goog
More information about the U-Boot
mailing list