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 <sjg@chromium.org>
This commit is contained in:
Simon Glass 2021-02-03 06:01:20 -07:00
parent 9763e4eb93
commit ea74c95103
4 changed files with 250 additions and 13 deletions

View 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[] = {

View 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
*/

View 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)'),
}

View File

@ -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 {
@ -394,6 +411,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):
@ -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())