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:
parent
9763e4eb93
commit
ea74c95103
|
@ -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[] = {
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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)'),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
Loading…
Reference in New Issue