[PATCH v2 00/33] dm: Add dtoc implementation of device instantiation (part D)

By way of background, in May[1] and July[2] two 'tiny-dm' series were sent out, showing a possible way to reduce the overhead of driver model in SPL. The potential impact of that work was described in the cover letter[3].
While this was a successful demonstration, all but eliminating the overhead of driver model, it was not very practical, since it would have required substantial effort to adapt drivers used in SPL to what was effectively a parallel driver model.
Instead, after much discussion [4], it was decided to try to use the tiny-dm methods to adapt the existing of-platdata infrastructure. This is considerably more work up front but should result in much less effort for maintainers, along with a single, more consistent driver model.
Of the options mentioned in [3], the following are implemented (in dtoc only, not driver model) as part of this series (CS=reduces code size, DS=reduces data size):
CS - drop driver_bind() and create devices (struct udevice) at build-time CS - allocate all device- and uclass-private data at build-time
In addition the following was completed in an earlier series:
CS / DS - Combine req_seq and seq and calculate the new value at build-time
So this series updates dtoc to support generating devices and uclasses that are ready for use when SPL starts and don't need to be bound.
This 'build-time' instantiation helps to reduce the code-size overhead of driver model in SPL.
As part of this series, a new -i option is added to dtoc. This changes it to emit build-time-instantiated devices. Separate Kconfig options control instantiation and whether run-time binding is supported.
Several corner cases have come up in making this work on sandbox_spl and chromebook_coral. It is possible that others will come up in the future, but it should be possible to adapt things to address these, based on the work here.
This is part D of the overall effort. The final series includes the driver model implementation, as well as updating the documentation.
To find out what the new generated files look like, see the last four patches of this series.
It is available at u-boot-dm/prepd-working
[1] http://patchwork.ozlabs.org/project/uboot/list/?series=179128&state=* [2] http://patchwork.ozlabs.org/project/uboot/list/?series=187295&state=* [3] https://lists.denx.de/pipermail/u-boot/2020-July/418433.html [4] http://patchwork.ozlabs.org/project/uboot/patch/20200525093539.1.Ibf2d19439c...
Changes in v2: - New patch - New patch - New patch - Drop patches previously applied - Update cover letter - Fix the naming for uclass_plat_name so it is different from uclass_priv - Tidy up tabbing to make the code output line up better - Add a summary to the top of the generated file
Simon Glass (33): bootstage: Fix dependency for BOOTSTAGE_RECORD_COUNT dtoc: Scan drivers for available information dtoc: Save scan information across test runs dtoc: Ignore unwanted files when scanning for drivers dtoc: Collect priv/plat struct info from drivers dtoc: Support scanning of uclasses dtoc: Support scanning of structs in header files dtoc: Move test files into a test/ directory dtoc: Rename sandbox_i2c_test and sandbox_pmic_test dtoc: Add some extra properties to nodes dtoc: Make use of node properties dtoc: Process nodes to set up required properties dtoc: Track nodes which are actually used dtoc: Support tracking the phase of U-Boot Makefile: Pass the U-Boot phase to dtoc dtoc: Support headers needed for drivers dtoc: Process driver aliases along with drivers dtoc: Warn of duplicate drivers dtoc: Read aliases for uclasses dtoc: Detect drivers only at the start of start of line dtoc: Assign a sequence number to each node dtoc: Set up the uclasses that are used dtoc: Support processing the root node dtoc: Add an option for device instantiation dm: of-platadata: Add option for device instantiation dtoc: Add support for decl file dtoc: Don't generate platform data with instantiation sandbox: Make sandbox,emul more conventional sandbox: i2c: Rename driver names to work with of-platdata dtoc: Tidy up the list of supported phandle properties dtoc: Generate a summary in the dt-plat.c file dtoc: Generate uclass devices dtoc: Generate device instances
arch/sandbox/dts/sandbox.dtsi | 6 +- arch/sandbox/dts/test.dts | 4 +- common/Kconfig.boot | 3 + common/bootstage.c | 2 +- doc/driver-model/of-plat.rst | 3 +- doc/driver-model/pci-info.rst | 1 + drivers/i2c/i2c-emul-uclass.c | 4 +- drivers/misc/test_drv.c | 11 +- drivers/rtc/i2c_rtc_emul.c | 2 +- dts/Kconfig | 23 +- include/dm/device.h | 34 + include/dm/test.h | 5 + scripts/Makefile.spl | 6 +- test/dm/test-fdt.c | 6 +- tools/dtoc/dtb_platdata.py | 620 +++++++++- tools/dtoc/dtoc_test_scan_drivers.cxx | 1 - tools/dtoc/main.py | 9 +- tools/dtoc/src_scan.py | 597 +++++++++- tools/dtoc/{ => test}/dtoc_test.dts | 0 tools/dtoc/{ => test}/dtoc_test_add_prop.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr32.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr32_64.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr64.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr64_32.dts | 0 tools/dtoc/test/dtoc_test_alias_bad.dts | 58 + tools/dtoc/test/dtoc_test_alias_bad_path.dts | 58 + tools/dtoc/test/dtoc_test_alias_bad_uc.dts | 58 + tools/dtoc/{ => test}/dtoc_test_aliases.dts | 0 tools/dtoc/{ => test}/dtoc_test_bad_reg.dts | 0 tools/dtoc/{ => test}/dtoc_test_bad_reg2.dts | 0 .../{ => test}/dtoc_test_driver_alias.dts | 0 tools/dtoc/{ => test}/dtoc_test_empty.dts | 0 tools/dtoc/test/dtoc_test_inst.dts | 58 + .../{ => test}/dtoc_test_invalid_driver.dts | 0 tools/dtoc/{ => test}/dtoc_test_phandle.dts | 0 .../dtoc/{ => test}/dtoc_test_phandle_bad.dts | 0 .../{ => test}/dtoc_test_phandle_bad2.dts | 0 .../{ => test}/dtoc_test_phandle_cd_gpios.dts | 0 .../{ => test}/dtoc_test_phandle_reorder.dts | 0 .../{ => test}/dtoc_test_phandle_single.dts | 0 tools/dtoc/test/dtoc_test_scan_drivers.cxx | 5 + tools/dtoc/{ => test}/dtoc_test_simple.dts | 4 +- tools/dtoc/test_dtoc.py | 1023 ++++++++++++++++- tools/dtoc/test_fdt.py | 31 +- tools/dtoc/test_src_scan.py | 397 ++++++- 45 files changed, 2847 insertions(+), 182 deletions(-) delete mode 100644 tools/dtoc/dtoc_test_scan_drivers.cxx rename tools/dtoc/{ => test}/dtoc_test.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_add_prop.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr32.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr32_64.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr64.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr64_32.dts (100%) create mode 100644 tools/dtoc/test/dtoc_test_alias_bad.dts create mode 100644 tools/dtoc/test/dtoc_test_alias_bad_path.dts create mode 100644 tools/dtoc/test/dtoc_test_alias_bad_uc.dts rename tools/dtoc/{ => test}/dtoc_test_aliases.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_bad_reg.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_bad_reg2.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_driver_alias.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_empty.dts (100%) create mode 100644 tools/dtoc/test/dtoc_test_inst.dts rename tools/dtoc/{ => test}/dtoc_test_invalid_driver.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_bad.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_bad2.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_cd_gpios.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_reorder.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_single.dts (100%) create mode 100644 tools/dtoc/test/dtoc_test_scan_drivers.cxx rename tools/dtoc/{ => test}/dtoc_test_simple.dts (93%)

At present these three Kconfigs exist even when bootstage is not enabled. This is not necessary since bootstage.c is only built if BOOTSTAGE is enabled.
Make them conditional. Also fix up the overflow message to mention TPL.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
common/Kconfig.boot | 3 +++ common/bootstage.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/common/Kconfig.boot b/common/Kconfig.boot index 5eaabdfc27f..c3869f9b8a5 100644 --- a/common/Kconfig.boot +++ b/common/Kconfig.boot @@ -427,6 +427,7 @@ config BOOTSTAGE_REPORT
config BOOTSTAGE_RECORD_COUNT int "Number of boot stage records to store" + depends on BOOTSTAGE default 30 help This is the size of the bootstage record list and is the maximum @@ -434,6 +435,7 @@ config BOOTSTAGE_RECORD_COUNT
config SPL_BOOTSTAGE_RECORD_COUNT int "Number of boot stage records to store for SPL" + depends on SPL_BOOTSTAGE default 5 help This is the size of the bootstage record list and is the maximum @@ -441,6 +443,7 @@ config SPL_BOOTSTAGE_RECORD_COUNT
config TPL_BOOTSTAGE_RECORD_COUNT int "Number of boot stage records to store for TPL" + depends on TPL_BOOTSTAGE default 5 help This is the size of the bootstage record list and is the maximum diff --git a/common/bootstage.c b/common/bootstage.c index 5f87358fd85..ad2e4469af5 100644 --- a/common/bootstage.c +++ b/common/bootstage.c @@ -348,7 +348,7 @@ void bootstage_report(void) } if (data->rec_count > RECORD_COUNT) printf("Overflowed internal boot id table by %d entries\n" - "Please increase CONFIG_(SPL_)BOOTSTAGE_RECORD_COUNT\n", + "Please increase CONFIG_(SPL_TPL_)BOOTSTAGE_RECORD_COUNT\n", data->rec_count - RECORD_COUNT);
puts("\nAccumulated time:\n");

At present these three Kconfigs exist even when bootstage is not enabled. This is not necessary since bootstage.c is only built if BOOTSTAGE is enabled.
Make them conditional. Also fix up the overflow message to mention TPL.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
common/Kconfig.boot | 3 +++ common/bootstage.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-)
Applied to u-boot-dm, thanks!

At present we simply record the name of a driver parsed from its implementation file. We also need to get the uclass and a few other things so we can instantiate devices at build time. Add support for collecting this information. This requires parsing each driver file.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/src_scan.py | 194 ++++++++++++++++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 131 +++++++++++++++++++++++- 2 files changed, 311 insertions(+), 14 deletions(-)
diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index f63c9fc166e..095fb6d4766 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -54,15 +54,30 @@ class Driver:
Attributes: name: Name of driver. For U_BOOT_DRIVER(x) this is 'x' + fname: Filename where the driver was found + uclass_id: Name of uclass, e.g. 'UCLASS_I2C' + compat: Driver data for each compatible string: + key: Compatible string, e.g. 'rockchip,rk3288-grf' + value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None + fname: Filename where the driver was found + priv (str): struct name of the priv_auto member, e.g. 'serial_priv' """ - def __init__(self, name): + def __init__(self, name, fname): self.name = name + self.fname = fname + self.uclass_id = None + self.compat = None + self.priv = ''
def __eq__(self, other): - return self.name == other.name + return (self.name == other.name and + self.uclass_id == other.uclass_id and + self.compat == other.compat and + self.priv == other.priv)
def __repr__(self): - return "Driver(name='%s')" % self.name + return ("Driver(name='%s', uclass_id='%s', compat=%s, priv=%s)" % + (self.name, self.uclass_id, self.compat, self.priv))
class Scanner: @@ -81,6 +96,12 @@ class Scanner: _warning_disabled: true to disable warnings about driver names not found _drivers_additional (list or str): List of additional drivers to use during scanning + _of_match: Dict holding information about compatible strings + key: Name of struct udevice_id variable + value: Dict of compatible info in that variable: + key: Compatible string, e.g. 'rockchip,rk3288-grf' + value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None + _compat_to_driver: Maps compatible strings to Driver """ def __init__(self, basedir, warning_disabled, drivers_additional): """Set up a new Scanner @@ -94,6 +115,8 @@ class Scanner: self._driver_aliases = {} self._drivers_additional = drivers_additional or [] self._warning_disabled = warning_disabled + self._of_match = {} + self._compat_to_driver = {}
def get_normalized_compat_name(self, node): """Get a node's normalized compat name @@ -131,10 +154,163 @@ class Scanner:
return compat_list_c[0], compat_list_c[1:]
+ @classmethod + def _get_re_for_member(cls, member): + """_get_re_for_member: Get a compiled regular expression + + Args: + member (str): Struct member name, e.g. 'priv_auto' + + Returns: + re.Pattern: Compiled regular expression that parses: + + .member = sizeof(struct fred), + + and returns "fred" as group 1 + """ + return re.compile(r'^\s*.%s\s*=\s*sizeof(struct\s+(.*)),$' % member) + + def _parse_driver(self, fname, buff): + """Parse a C file to extract driver information contained within + + This parses U_BOOT_DRIVER() structs to obtain various pieces of useful + information. + + It updates the following members: + _drivers - updated with new Driver records for each driver found + in the file + _of_match - updated with each compatible string found in the file + _compat_to_driver - Maps compatible string to Driver + + Args: + fname (str): Filename being parsed (used for warnings) + buff (str): Contents of file + + Raises: + ValueError: Compatible variable is mentioned in .of_match in + U_BOOT_DRIVER() but not found in the file + """ + # Dict holding information about compatible strings collected in this + # function so far + # key: Name of struct udevice_id variable + # value: Dict of compatible info in that variable: + # key: Compatible string, e.g. 'rockchip,rk3288-grf' + # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None + of_match = {} + + # Dict holding driver information collected in this function so far + # key: Driver name (C name as in U_BOOT_DRIVER(xxx)) + # value: Driver + drivers = {} + + # Collect the driver info + driver = None + re_driver = re.compile(r'U_BOOT_DRIVER((.*))') + + # Collect the uclass ID, e.g. 'UCLASS_SPI' + re_id = re.compile(r'\s*.id\s*=\s*(UCLASS_[A-Z0-9_]+)') + + # Collect the compatible string, e.g. 'rockchip,rk3288-grf' + compat = None + re_compat = re.compile(r'{\s*.compatible\s*=\s*"(.*)"\s*' + r'(,\s*.data\s*=\s*(\S*))?\s*},') + + # This is a dict of compatible strings that were found: + # key: Compatible string, e.g. 'rockchip,rk3288-grf' + # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None + compat_dict = {} + + # Holds the var nane of the udevice_id list, e.g. + # 'rk3288_syscon_ids_noc' in + # static const struct udevice_id rk3288_syscon_ids_noc[] = { + ids_name = None + re_ids = re.compile(r'struct udevice_id (.*)[]\s*=') + + # Matches the references to the udevice_id list + re_of_match = re.compile( + r'.of_match\s*=\s*(of_match_ptr()?([a-z0-9_]+)())?,') + + # Matches the struct name for priv + re_priv = self._get_re_for_member('priv_auto') + + prefix = '' + for line in buff.splitlines(): + # Handle line continuation + if prefix: + line = prefix + line + prefix = '' + if line.endswith('\'): + prefix = line[:-1] + continue + + driver_match = re_driver.search(line) + + # If this line contains U_BOOT_DRIVER()... + if driver: + m_id = re_id.search(line) + m_of_match = re_of_match.search(line) + m_priv = re_priv.match(line) + if m_priv: + driver.priv = m_priv.group(1) + elif m_id: + driver.uclass_id = m_id.group(1) + elif m_of_match: + compat = m_of_match.group(2) + elif '};' in line: + if driver.uclass_id and compat: + if compat not in of_match: + raise ValueError( + "%s: Unknown compatible var '%s' (found: %s)" % + (fname, compat, ','.join(of_match.keys()))) + driver.compat = of_match[compat] + + # This needs to be deterministic, since a driver may + # have multiple compatible strings pointing to it. + # We record the one earliest in the alphabet so it + # will produce the same result on all machines. + for compat_id in of_match[compat]: + old = self._compat_to_driver.get(compat_id) + if not old or driver.name < old.name: + self._compat_to_driver[compat_id] = driver + drivers[driver.name] = driver + else: + # The driver does not have a uclass or compat string. + # The first is required but the second is not, so just + # ignore this. + pass + driver = None + ids_name = None + compat = None + compat_dict = {} + + elif ids_name: + compat_m = re_compat.search(line) + if compat_m: + compat_dict[compat_m.group(1)] = compat_m.group(3) + elif '};' in line: + of_match[ids_name] = compat_dict + ids_name = None + elif driver_match: + driver_name = driver_match.group(1) + driver = Driver(driver_name, fname) + else: + ids_m = re_ids.search(line) + if ids_m: + ids_name = ids_m.group(1) + + # Make the updates based on what we found + self._drivers.update(drivers) + self._of_match.update(of_match) + def scan_driver(self, fname): """Scan a driver file to build a list of driver names and aliases
- This procedure will populate self._drivers and self._driver_aliases + It updates the following members: + _drivers - updated with new Driver records for each driver found + in the file + _of_match - updated with each compatible string found in the file + _compat_to_driver - Maps compatible string to Driver + _driver_aliases - Maps alias names to driver name
Args fname: Driver filename to scan @@ -147,12 +323,10 @@ class Scanner: print("Skipping file '%s' due to unicode error" % fname) return
- # The following re will search for driver names declared as - # U_BOOT_DRIVER(driver_name) - drivers = re.findall(r'U_BOOT_DRIVER((.*))', buff) - - for driver in drivers: - self._drivers[driver] = Driver(driver) + # If this file has any U_BOOT_DRIVER() declarations, process it to + # obtain driver information + if 'U_BOOT_DRIVER' in buff: + self._parse_driver(fname, buff)
# The following re will search for driver aliases declared as # DM_DRIVER_ALIAS(alias, driver_name) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 7d686530d68..25e4866f201 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -17,6 +17,20 @@ from dtoc import src_scan from patman import test_util from patman import tools
+OUR_PATH = os.path.dirname(os.path.realpath(__file__)) + +class FakeNode: + """Fake Node object for testing""" + def __init__(self): + self.name = None + self.props = {} + +class FakeProp: + """Fake Prop object for testing""" + def __init__(self): + self.name = None + self.value = None + # This is a test so is allowed to access private things in the module it is # testing # pylint: disable=W0212 @@ -69,10 +83,22 @@ class TestSrcScan(unittest.TestCase):
def test_driver(self): """Test the Driver class""" - drv1 = src_scan.Driver('fred') - drv2 = src_scan.Driver('mary') - drv3 = src_scan.Driver('fred') - self.assertEqual("Driver(name='fred')", str(drv1)) + i2c = 'I2C_UCLASS' + compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', + 'rockchip,rk3288-srf': None} + drv1 = src_scan.Driver('fred', 'fred.c') + drv2 = src_scan.Driver('mary', 'mary.c') + drv3 = src_scan.Driver('fred', 'fred.c') + drv1.uclass_id = i2c + drv1.compat = compat + drv2.uclass_id = i2c + drv2.compat = compat + drv3.uclass_id = i2c + drv3.compat = compat + self.assertEqual( + "Driver(name='fred', uclass_id='I2C_UCLASS', " + "compat={'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', " + "'rockchip,rk3288-srf': None}, priv=)", str(drv1)) self.assertEqual(drv1, drv3) self.assertNotEqual(drv1, drv2) self.assertNotEqual(drv2, drv3) @@ -105,3 +131,100 @@ class TestSrcScan(unittest.TestCase): mocked.mock_calls[1]) finally: shutil.rmtree(indir) + + def test_scan(self): + """Test scanning of a driver""" + fname = os.path.join(OUR_PATH, '..', '..', 'drivers/i2c/tegra_i2c.c') + buff = tools.ReadFile(fname, False) + scan = src_scan.Scanner(None, False, None) + scan._parse_driver(fname, buff) + self.assertIn('i2c_tegra', scan._drivers) + drv = scan._drivers['i2c_tegra'] + self.assertEqual('i2c_tegra', drv.name) + self.assertEqual('UCLASS_I2C', drv.uclass_id) + self.assertEqual( + {'nvidia,tegra114-i2c': 'TYPE_114', + 'nvidia,tegra20-i2c': 'TYPE_STD', + 'nvidia,tegra20-i2c-dvc': 'TYPE_DVC'}, drv.compat) + self.assertEqual('i2c_bus', drv.priv) + self.assertEqual(1, len(scan._drivers)) + + def test_normalized_name(self): + """Test operation of get_normalized_compat_name()""" + prop = FakeProp() + prop.name = 'compatible' + prop.value = 'rockchip,rk3288-grf' + node = FakeNode() + node.props = {'compatible': prop} + scan = src_scan.Scanner(None, False, None) + with test_util.capture_sys_output() as (stdout, _): + name, aliases = scan.get_normalized_compat_name(node) + self.assertEqual('rockchip_rk3288_grf', name) + self.assertEqual([], aliases) + self.assertEqual( + 'WARNING: the driver rockchip_rk3288_grf was not found in the driver list', + stdout.getvalue().strip()) + + i2c = 'I2C_UCLASS' + compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', + 'rockchip,rk3288-srf': None} + drv = src_scan.Driver('fred', 'fred.c') + drv.uclass_id = i2c + drv.compat = compat + scan._drivers['rockchip_rk3288_grf'] = drv + + scan._driver_aliases['rockchip_rk3288_srf'] = 'rockchip_rk3288_grf' + + with test_util.capture_sys_output() as (stdout, _): + name, aliases = scan.get_normalized_compat_name(node) + self.assertEqual('', stdout.getvalue().strip()) + self.assertEqual('rockchip_rk3288_grf', name) + self.assertEqual([], aliases) + + prop.value = 'rockchip,rk3288-srf' + with test_util.capture_sys_output() as (stdout, _): + name, aliases = scan.get_normalized_compat_name(node) + self.assertEqual('', stdout.getvalue().strip()) + self.assertEqual('rockchip_rk3288_grf', name) + self.assertEqual(['rockchip_rk3288_srf'], aliases) + + def test_scan_errors(self): + """Test detection of scanning errors""" + buff = ''' +static const struct udevice_id tegra_i2c_ids2[] = { + { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, + { } +}; + +U_BOOT_DRIVER(i2c_tegra) = { + .name = "i2c_tegra", + .id = UCLASS_I2C, + .of_match = tegra_i2c_ids, +}; +''' + scan = src_scan.Scanner(None, False, None) + with self.assertRaises(ValueError) as exc: + scan._parse_driver('file.c', buff) + self.assertIn( + "file.c: Unknown compatible var 'tegra_i2c_ids' (found: tegra_i2c_ids2)", + str(exc.exception)) + + def test_of_match(self): + """Test detection of of_match_ptr() member""" + buff = ''' +static const struct udevice_id tegra_i2c_ids[] = { + { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, + { } +}; + +U_BOOT_DRIVER(i2c_tegra) = { + .name = "i2c_tegra", + .id = UCLASS_I2C, + .of_match = of_match_ptr(tegra_i2c_ids), +}; +''' + scan = src_scan.Scanner(None, False, None) + scan._parse_driver('file.c', buff) + self.assertIn('i2c_tegra', scan._drivers) + drv = scan._drivers['i2c_tegra'] + self.assertEqual('i2c_tegra', drv.name)

At present we simply record the name of a driver parsed from its implementation file. We also need to get the uclass and a few other things so we can instantiate devices at build time. Add support for collecting this information. This requires parsing each driver file.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/src_scan.py | 194 ++++++++++++++++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 131 +++++++++++++++++++++++- 2 files changed, 311 insertions(+), 14 deletions(-)
Applied to u-boot-dm, thanks!

At present most of the tests scan the U-Boot source tree as part of their run. This information does not change across tests, so we can save time by remembering it.
Add a way to set up this information and use it for each test, taking a copy first, so as not to mess up the original.
This reduces the run time from about 1.6 seconds to 1.5 seconds on my machine. For code coverage (which cannot run in parallel), it reduces from 33 seconds to 5.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 11 ++++++++--- tools/dtoc/main.py | 2 ++ tools/dtoc/test_dtoc.py | 40 +++++++++++++++++++++++++++++++------- 3 files changed, 43 insertions(+), 10 deletions(-)
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index b7abaed67ac..e9be5985c72 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -670,7 +670,8 @@ OUTPUT_FILES = {
def run_steps(args, dtb_file, include_disabled, output, output_dirs, - warning_disabled=False, drivers_additional=None, basedir=None): + warning_disabled=False, drivers_additional=None, basedir=None, + scan=None): """Run all the steps of the dtoc tool
Args: @@ -687,6 +688,9 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, scanning basedir (str): Base directory of U-Boot source code. Defaults to the grandparent of this file's directory + scan (src_src.Scanner): Scanner from a previous run. This can help speed + up tests. Use None for normal operation + Raises: ValueError: if args has no command, or an unknown command """ @@ -695,9 +699,10 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, if output and output_dirs and any(output_dirs): raise ValueError('Must specify either output or output_dirs, not both')
- scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional) + if not scan: + scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional) + scan.scan_drivers() plat = DtbPlatdata(scan, dtb_file, include_disabled) - scan.scan_drivers() plat.scan_dtb() plat.scan_tree() plat.scan_reg_sizes() diff --git a/tools/dtoc/main.py b/tools/dtoc/main.py index b0ad0f3952a..355b1e62773 100755 --- a/tools/dtoc/main.py +++ b/tools/dtoc/main.py @@ -53,6 +53,8 @@ def run_tests(processes, args): sys.argv = [sys.argv[0]] test_name = args and args[0] or None
+ test_dtoc.setup() + test_util.RunTestSuites( result, debug=True, verbosity=1, test_preserve_dirs=False, processes=processes, test_name=test_name, toolpath=[], diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index d961d67b8fc..6865d949a05 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -10,6 +10,7 @@ tool. """
import collections +import copy import glob import os import struct @@ -20,6 +21,7 @@ from dtb_platdata import tab_to from dtoc import dtb_platdata from dtoc import fdt from dtoc import fdt_util +from dtoc import src_scan from dtoc.src_scan import conv_name_to_c from dtoc.src_scan import get_compat_name from patman import test_util @@ -53,6 +55,9 @@ C_HEADER = '''/* #include <dt-structs.h> '''
+# Scanner saved from a previous run of the tests (to speed things up) +saved_scan = None + # This is a test so is allowed to access private things in the module it is # testing # pylint: disable=W0212 @@ -71,6 +76,19 @@ def get_dtb_file(dts_fname, capture_stderr=False): capture_stderr=capture_stderr)
+def setup(): + global saved_scan + + # Disable warnings so that calls to get_normalized_compat_name() will not + # output things. + saved_scan = src_scan.Scanner(None, True, False) + saved_scan.scan_drivers() + +def copy_scan(): + """Get a copy of saved_scan so that each test can start clean""" + return copy.deepcopy(saved_scan) + + class TestDtoc(unittest.TestCase): """Tests for dtoc""" @classmethod @@ -120,7 +138,8 @@ class TestDtoc(unittest.TestCase): dtb_file (str): Filename of .dtb file output (str): Filename of output file """ - dtb_platdata.run_steps(args, dtb_file, False, output, [], True) + dtb_platdata.run_steps(args, dtb_file, False, output, [], True, + None, None, scan=copy_scan())
def test_name(self): """Test conversion of device tree names to C identifiers""" @@ -175,7 +194,9 @@ class TestDtoc(unittest.TestCase): """Test output from a device tree file with no nodes""" dtb_file = get_dtb_file('dtoc_test_empty.dts') output = tools.GetOutputFilename('output') - self.run_test(['struct'], dtb_file, output) + + # Run this one without saved_scan to complete test coverage + dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], True) with open(output) as infile: lines = infile.read().splitlines() self.assertEqual(HEADER.splitlines(), lines) @@ -343,7 +364,8 @@ U_BOOT_DRVINFO(gpios_at_0) = { dtb_file = get_dtb_file('dtoc_test_invalid_driver.dts') output = tools.GetOutputFilename('output') with test_util.capture_sys_output() as _: - dtb_platdata.run_steps(['struct'], dtb_file, False, output, []) + dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], + scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(HEADER + ''' @@ -352,7 +374,8 @@ struct dtd_invalid { ''', data)
with test_util.capture_sys_output() as _: - dtb_platdata.run_steps(['platdata'], dtb_file, False, output, []) + dtb_platdata.run_steps(['platdata'], dtb_file, False, output, [], + scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' @@ -502,7 +525,8 @@ U_BOOT_DRVINFO(phandle_target) = { """Test that phandle targets are generated when unsing cd-gpios""" dtb_file = get_dtb_file('dtoc_test_phandle_cd_gpios.dts') output = tools.GetOutputFilename('output') - dtb_platdata.run_steps(['platdata'], dtb_file, False, output, [], True) + dtb_platdata.run_steps(['platdata'], dtb_file, False, output, [], True, + scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' @@ -903,7 +927,8 @@ U_BOOT_DRVINFO(spl_test2) = { def test_output_conflict(self): """Test a conflict between and output dirs and output file""" with self.assertRaises(ValueError) as exc: - dtb_platdata.run_steps(['all'], None, False, 'out', ['cdir'], True) + dtb_platdata.run_steps(['all'], None, False, 'out', ['cdir'], True, + scan=copy_scan()) self.assertIn("Must specify either output or output_dirs, not both", str(exc.exception))
@@ -919,7 +944,8 @@ U_BOOT_DRVINFO(spl_test2) = { fnames = glob.glob(outdir + '/*') self.assertEqual(2, len(fnames))
- dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], True) + dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], True, + scan=copy_scan()) fnames = glob.glob(outdir + '/*') self.assertEqual(4, len(fnames))

At present most of the tests scan the U-Boot source tree as part of their run. This information does not change across tests, so we can save time by remembering it.
Add a way to set up this information and use it for each test, taking a copy first, so as not to mess up the original.
This reduces the run time from about 1.6 seconds to 1.5 seconds on my machine. For code coverage (which cannot run in parallel), it reduces from 33 seconds to 5.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 11 ++++++++--- tools/dtoc/main.py | 2 ++ tools/dtoc/test_dtoc.py | 40 +++++++++++++++++++++++++++++++------- 3 files changed, 43 insertions(+), 10 deletions(-)
Applied to u-boot-dm, thanks!

We should ignore anything in the .git directory or any of the build-sandbox, etc. directories created by 'make check'. These can confuse dtoc. Update the code to ignore these.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/src_scan.py | 5 +++++ tools/dtoc/test_src_scan.py | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 095fb6d4766..761164a9c9a 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -345,6 +345,11 @@ class Scanner: This procedure will populate self._drivers and self._driver_aliases """ for (dirpath, _, filenames) in os.walk(self._basedir): + rel_path = dirpath[len(self._basedir):] + if rel_path.startswith('/'): + rel_path = rel_path[1:] + if rel_path.startswith('build') or rel_path.startswith('.git'): + continue for fname in filenames: if not fname.endswith('.c'): continue diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 25e4866f201..ada49fb7042 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -117,7 +117,9 @@ class TestSrcScan(unittest.TestCase):
fname_list = [] add_file('fname.c') + add_file('.git/ignoreme.c') add_file('dir/fname2.c') + add_file('build-sandbox/ignoreme2.c')
# Mock out scan_driver and check that it is called with the # expected files @@ -127,7 +129,8 @@ class TestSrcScan(unittest.TestCase): self.assertEqual(2, len(mocked.mock_calls)) self.assertEqual(mock.call(fname_list[0]), mocked.mock_calls[0]) - self.assertEqual(mock.call(fname_list[1]), + # .git file should be ignored + self.assertEqual(mock.call(fname_list[2]), mocked.mock_calls[1]) finally: shutil.rmtree(indir)

We should ignore anything in the .git directory or any of the build-sandbox, etc. directories created by 'make check'. These can confuse dtoc. Update the code to ignore these.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/src_scan.py | 5 +++++ tools/dtoc/test_src_scan.py | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-)
Applied to u-boot-dm, thanks!

In order to output variables to hold the priv/plat information used by each device, dtoc needs to know the struct for each. With this, it can declare this at build time:
u8 xxx_priv [sizeof(struct <name>)];
Collect the various struct names from the drivers.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/src_scan.py | 25 +++++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 761164a9c9a..ff3ab409e4b 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -61,6 +61,11 @@ class Driver: value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None fname: Filename where the driver was found priv (str): struct name of the priv_auto member, e.g. 'serial_priv' + plat (str): struct name of the plat_auto member, e.g. 'serial_plat' + child_priv (str): struct name of the per_child_auto member, + e.g. 'pci_child_priv' + child_plat (str): struct name of the per_child_plat_auto member, + e.g. 'pci_child_plat' """ def __init__(self, name, fname): self.name = name @@ -68,12 +73,16 @@ class Driver: self.uclass_id = None self.compat = None self.priv = '' + self.plat = '' + self.child_priv = '' + self.child_plat = ''
def __eq__(self, other): return (self.name == other.name and self.uclass_id == other.uclass_id and self.compat == other.compat and - self.priv == other.priv) + self.priv == other.priv and + self.plat == other.plat)
def __repr__(self): return ("Driver(name='%s', uclass_id='%s', compat=%s, priv=%s)" % @@ -230,8 +239,11 @@ class Scanner: re_of_match = re.compile( r'.of_match\s*=\s*(of_match_ptr()?([a-z0-9_]+)())?,')
- # Matches the struct name for priv + # Matches the struct name for priv, plat re_priv = self._get_re_for_member('priv_auto') + re_plat = self._get_re_for_member('plat_auto') + re_child_priv = self._get_re_for_member('per_child_auto') + re_child_plat = self._get_re_for_member('per_child_plat_auto')
prefix = '' for line in buff.splitlines(): @@ -250,8 +262,17 @@ class Scanner: m_id = re_id.search(line) m_of_match = re_of_match.search(line) m_priv = re_priv.match(line) + m_plat = re_plat.match(line) + m_cplat = re_child_plat.match(line) + m_cpriv = re_child_priv.match(line) if m_priv: driver.priv = m_priv.group(1) + elif m_plat: + driver.plat = m_plat.group(1) + elif m_cplat: + driver.child_plat = m_cplat.group(1) + elif m_cpriv: + driver.child_priv = m_cpriv.group(1) elif m_id: driver.uclass_id = m_id.group(1) elif m_of_match: diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index ada49fb7042..62dea2a9612 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -231,3 +231,35 @@ U_BOOT_DRIVER(i2c_tegra) = { self.assertIn('i2c_tegra', scan._drivers) drv = scan._drivers['i2c_tegra'] self.assertEqual('i2c_tegra', drv.name) + + def test_priv(self): + """Test collection of struct info from drivers""" + buff = ''' +static const struct udevice_id test_ids[] = { + { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, + { } +}; + +U_BOOT_DRIVER(testing) = { + .name = "testing", + .id = UCLASS_I2C, + .of_match = test_ids, + .priv_auto = sizeof(struct some_priv), + .plat_auto = sizeof(struct some_plat), + .per_child_auto = sizeof(struct some_cpriv), + .per_child_plat_auto = sizeof(struct some_cplat), +}; +''' + scan = src_scan.Scanner(None, False, None) + scan._parse_driver('file.c', buff) + self.assertIn('testing', scan._drivers) + drv = scan._drivers['testing'] + self.assertEqual('testing', drv.name) + self.assertEqual('UCLASS_I2C', drv.uclass_id) + self.assertEqual( + {'nvidia,tegra114-i2c': 'TYPE_114'}, drv.compat) + self.assertEqual('some_priv', drv.priv) + self.assertEqual('some_plat', drv.plat) + self.assertEqual('some_cpriv', drv.child_priv) + self.assertEqual('some_cplat', drv.child_plat) + self.assertEqual(1, len(scan._drivers))

In order to output variables to hold the priv/plat information used by each device, dtoc needs to know the struct for each. With this, it can declare this at build time:
u8 xxx_priv [sizeof(struct <name>)];
Collect the various struct names from the drivers.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/src_scan.py | 25 +++++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-)
Applied to u-boot-dm, thanks!

Uclasses can have per-device private / platform data so dtoc needs to scan these drivers. This allows it to find out the size of this data so it can be allocated a build time.
Add a parser for uclass information, similar to drivers. Keep a dict of the uclasses that were found.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/src_scan.py | 122 ++++++++++++++++++++++++++++++++++++ tools/dtoc/test_src_scan.py | 55 ++++++++++++++++ 2 files changed, 177 insertions(+)
diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index ff3ab409e4b..3245d02e09b 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -89,6 +89,43 @@ class Driver: (self.name, self.uclass_id, self.compat, self.priv))
+class UclassDriver: + """Holds information about a uclass driver + + Attributes: + name: Uclass name, e.g. 'i2c' if the driver is for UCLASS_I2C + uclass_id: Uclass ID, e.g. 'UCLASS_I2C' + priv: struct name of the private data, e.g. 'i2c_priv' + per_dev_priv (str): struct name of the priv_auto member, e.g. 'spi_info' + per_dev_plat (str): struct name of the plat_auto member, e.g. 'i2c_chip' + per_child_priv (str): struct name of the per_child_auto member, + e.g. 'pci_child_priv' + per_child_plat (str): struct name of the per_child_plat_auto member, + e.g. 'pci_child_plat' + """ + def __init__(self, name): + self.name = name + self.uclass_id = None + self.priv = '' + self.per_dev_priv = '' + self.per_dev_plat = '' + self.per_child_priv = '' + self.per_child_plat = '' + + def __eq__(self, other): + return (self.name == other.name and + self.uclass_id == other.uclass_id and + self.priv == other.priv) + + def __repr__(self): + return ("UclassDriver(name='%s', uclass_id='%s')" % + (self.name, self.uclass_id)) + + def __hash__(self): + # We can use the uclass ID since it is unique among uclasses + return hash(self.uclass_id) + + class Scanner: """Scanning of the U-Boot source tree
@@ -111,6 +148,9 @@ class Scanner: key: Compatible string, e.g. 'rockchip,rk3288-grf' value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None _compat_to_driver: Maps compatible strings to Driver + _uclass: Dict of uclass information + key: uclass name, e.g. 'UCLASS_I2C' + value: UClassDriver """ def __init__(self, basedir, warning_disabled, drivers_additional): """Set up a new Scanner @@ -126,6 +166,7 @@ class Scanner: self._warning_disabled = warning_disabled self._of_match = {} self._compat_to_driver = {} + self._uclass = {}
def get_normalized_compat_name(self, node): """Get a node's normalized compat name @@ -179,6 +220,85 @@ class Scanner: """ return re.compile(r'^\s*.%s\s*=\s*sizeof(struct\s+(.*)),$' % member)
+ def _parse_uclass_driver(self, fname, buff): + """Parse a C file to extract uclass driver information contained within + + This parses UCLASS_DRIVER() structs to obtain various pieces of useful + information. + + It updates the following member: + _uclass: Dict of uclass information + key: uclass name, e.g. 'UCLASS_I2C' + value: UClassDriver + + Args: + fname (str): Filename being parsed (used for warnings) + buff (str): Contents of file + """ + uc_drivers = {} + + # Collect the driver name and associated Driver + driver = None + re_driver = re.compile(r'UCLASS_DRIVER((.*))') + + # Collect the uclass ID, e.g. 'UCLASS_SPI' + re_id = re.compile(r'\s*.id\s*=\s*(UCLASS_[A-Z0-9_]+)') + + # Matches the header/size information for uclass-private data + re_priv = self._get_re_for_member('priv_auto') + + # Set up parsing for the auto members + re_per_device_priv = self._get_re_for_member('per_device_auto') + re_per_device_plat = self._get_re_for_member('per_device_plat_auto') + re_per_child_priv = self._get_re_for_member('per_child_auto') + re_per_child_plat = self._get_re_for_member('per_child_plat_auto') + + prefix = '' + for line in buff.splitlines(): + # Handle line continuation + if prefix: + line = prefix + line + prefix = '' + if line.endswith('\'): + prefix = line[:-1] + continue + + driver_match = re_driver.search(line) + + # If we have seen UCLASS_DRIVER()... + if driver: + m_id = re_id.search(line) + m_priv = re_priv.match(line) + m_per_dev_priv = re_per_device_priv.match(line) + m_per_dev_plat = re_per_device_plat.match(line) + m_per_child_priv = re_per_child_priv.match(line) + m_per_child_plat = re_per_child_plat.match(line) + if m_id: + driver.uclass_id = m_id.group(1) + elif m_priv: + driver.priv = m_priv.group(1) + elif m_per_dev_priv: + driver.per_dev_priv = m_per_dev_priv.group(1) + elif m_per_dev_plat: + driver.per_dev_plat = m_per_dev_plat.group(1) + elif m_per_child_priv: + driver.per_child_priv = m_per_child_priv.group(1) + elif m_per_child_plat: + driver.per_child_plat = m_per_child_plat.group(1) + elif '};' in line: + if not driver.uclass_id: + raise ValueError( + "%s: Cannot parse uclass ID in driver '%s'" % + (fname, driver.name)) + uc_drivers[driver.uclass_id] = driver + driver = None + + elif driver_match: + driver_name = driver_match.group(1) + driver = UclassDriver(driver_name) + + self._uclass.update(uc_drivers) + def _parse_driver(self, fname, buff): """Parse a C file to extract driver information contained within
@@ -348,6 +468,8 @@ class Scanner: # obtain driver information if 'U_BOOT_DRIVER' in buff: self._parse_driver(fname, buff) + if 'UCLASS_DRIVER' in buff: + self._parse_uclass_driver(fname, buff)
# The following re will search for driver aliases declared as # DM_DRIVER_ALIAS(alias, driver_name) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 62dea2a9612..641d6495de3 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -7,6 +7,7 @@ This includes unit tests for scanning of the source code """
+import copy import os import shutil import tempfile @@ -263,3 +264,57 @@ U_BOOT_DRIVER(testing) = { self.assertEqual('some_cpriv', drv.child_priv) self.assertEqual('some_cplat', drv.child_plat) self.assertEqual(1, len(scan._drivers)) + + def test_uclass_scan(self): + """Test collection of uclass-driver info""" + buff = ''' +UCLASS_DRIVER(i2c) = { + .id = UCLASS_I2C, + .name = "i2c", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .priv_auto = sizeof(struct some_priv), + .per_device_auto = sizeof(struct per_dev_priv), + .per_device_plat_auto = sizeof(struct per_dev_plat), + .per_child_auto = sizeof(struct per_child_priv), + .per_child_plat_auto = sizeof(struct per_child_plat), + .child_post_bind = i2c_child_post_bind, +}; + +''' + scan = src_scan.Scanner(None, False, None) + scan._parse_uclass_driver('file.c', buff) + self.assertIn('UCLASS_I2C', scan._uclass) + drv = scan._uclass['UCLASS_I2C'] + self.assertEqual('i2c', drv.name) + self.assertEqual('UCLASS_I2C', drv.uclass_id) + self.assertEqual('some_priv', drv.priv) + self.assertEqual('per_dev_priv', drv.per_dev_priv) + self.assertEqual('per_dev_plat', drv.per_dev_plat) + self.assertEqual('per_child_priv', drv.per_child_priv) + self.assertEqual('per_child_plat', drv.per_child_plat) + self.assertEqual(1, len(scan._uclass)) + + drv2 = copy.deepcopy(drv) + self.assertEqual(drv, drv2) + drv2.priv = 'other_priv' + self.assertNotEqual(drv, drv2) + + # The hashes only depend on the uclass ID, so should be equal + self.assertEqual(drv.__hash__(), drv2.__hash__()) + + self.assertEqual("UclassDriver(name='i2c', uclass_id='UCLASS_I2C')", + str(drv)) + + def test_uclass_scan_errors(self): + """Test detection of uclass scanning errors""" + buff = ''' +UCLASS_DRIVER(i2c) = { + .name = "i2c", +}; + +''' + scan = src_scan.Scanner(None, False, None) + with self.assertRaises(ValueError) as exc: + scan._parse_uclass_driver('file.c', buff) + self.assertIn("file.c: Cannot parse uclass ID in driver 'i2c'", + str(exc.exception))

Uclasses can have per-device private / platform data so dtoc needs to scan these drivers. This allows it to find out the size of this data so it can be allocated a build time.
Add a parser for uclass information, similar to drivers. Keep a dict of the uclasses that were found.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/src_scan.py | 122 ++++++++++++++++++++++++++++++++++++ tools/dtoc/test_src_scan.py | 55 ++++++++++++++++ 2 files changed, 177 insertions(+)
Applied to u-boot-dm, thanks!

Drivers can have private / platform data contained in structs and these struct definitions are generally kept in header files. In order to generate build-time devices, dtoc needs to generate code that declares the data contained in those structs. This generated code must include the relevant header file, to avoid a build error.
We need a way for dtoc to scan header files for struct definitions. Then, when it wants to generate code that uses a struct, it can make sure it includes the correct header file, first.
Add a parser for struct information, similar to drivers. Keep a dict of the structs that were found.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/src_scan.py | 86 +++++++++++++++++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 45 +++++++++++++++++++ 2 files changed, 128 insertions(+), 3 deletions(-)
diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 3245d02e09b..bf3e5de9b1e 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -126,6 +126,22 @@ class UclassDriver: return hash(self.uclass_id)
+class Struct: + """Holds information about a struct definition + + Attributes: + name: Struct name, e.g. 'fred' if the struct is 'struct fred' + fname: Filename containing the struct, in a format that C files can + include, e.g. 'asm/clk.h' + """ + def __init__(self, name, fname): + self.name = name + self.fname =fname + + def __repr__(self): + return ("Struct(name='%s', fname='%s')" % (self.name, self.fname)) + + class Scanner: """Scanning of the U-Boot source tree
@@ -151,6 +167,9 @@ class Scanner: _uclass: Dict of uclass information key: uclass name, e.g. 'UCLASS_I2C' value: UClassDriver + _structs: Dict of all structs found in U-Boot: + key: Name of struct + value: Struct object """ def __init__(self, basedir, warning_disabled, drivers_additional): """Set up a new Scanner @@ -167,6 +186,7 @@ class Scanner: self._of_match = {} self._compat_to_driver = {} self._uclass = {} + self._structs = {}
def get_normalized_compat_name(self, node): """Get a node's normalized compat name @@ -204,6 +224,41 @@ class Scanner:
return compat_list_c[0], compat_list_c[1:]
+ def _parse_structs(self, fname, buff): + """Parse a H file to extract struct definitions contained within + + This parses 'struct xx {' definitions to figure out what structs this + header defines. + + Args: + buff (str): Contents of file + fname (str): Filename (to use when printing errors) + """ + structs = {} + + re_struct = re.compile('^struct ([a-z0-9_]+) {$') + re_asm = re.compile('../arch/[a-z0-9]+/include/asm/(.*)') + prefix = '' + for line in buff.splitlines(): + # Handle line continuation + if prefix: + line = prefix + line + prefix = '' + if line.endswith('\'): + prefix = line[:-1] + continue + + m_struct = re_struct.match(line) + if m_struct: + name = m_struct.group(1) + include_dir = os.path.join(self._basedir, 'include') + rel_fname = os.path.relpath(fname, include_dir) + m_asm = re_asm.match(rel_fname) + if m_asm: + rel_fname = 'asm/' + m_asm.group(1) + structs[name] = Struct(name, rel_fname) + self._structs.update(structs) + @classmethod def _get_re_for_member(cls, member): """_get_re_for_member: Get a compiled regular expression @@ -482,6 +537,29 @@ class Scanner: continue self._driver_aliases[alias[1]] = alias[0]
+ def scan_header(self, fname): + """Scan a header file to build a list of struct definitions + + It updates the following members: + _structs - updated with new Struct records for each struct found + in the file + + Args + fname: header filename to scan + """ + with open(fname, encoding='utf-8') as inf: + try: + buff = inf.read() + except UnicodeDecodeError: + # This seems to happen on older Python versions + print("Skipping file '%s' due to unicode error" % fname) + return + + # If this file has any U_BOOT_DRIVER() declarations, process it to + # obtain driver information + if 'struct' in buff: + self._parse_structs(fname, buff) + def scan_drivers(self): """Scan the driver folders to build a list of driver names and aliases
@@ -494,9 +572,11 @@ class Scanner: if rel_path.startswith('build') or rel_path.startswith('.git'): continue for fname in filenames: - if not fname.endswith('.c'): - continue - self.scan_driver(dirpath + '/' + fname) + pathname = dirpath + '/' + fname + if fname.endswith('.c'): + self.scan_driver(pathname) + elif fname.endswith('.h'): + self.scan_header(pathname)
for fname in self._drivers_additional: if not isinstance(fname, str) or len(fname) == 0: diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 641d6495de3..a0b0e097eb2 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -318,3 +318,48 @@ UCLASS_DRIVER(i2c) = { scan._parse_uclass_driver('file.c', buff) self.assertIn("file.c: Cannot parse uclass ID in driver 'i2c'", str(exc.exception)) + + def test_struct_scan(self): + """Test collection of struct info""" + buff = ''' +/* some comment */ +struct some_struct1 { + struct i2c_msg *msgs; + uint nmsgs; +}; +''' + scan = src_scan.Scanner(None, False, None) + scan._basedir = os.path.join(OUR_PATH, '..', '..') + scan._parse_structs('arch/arm/include/asm/file.h', buff) + self.assertIn('some_struct1', scan._structs) + struc = scan._structs['some_struct1'] + self.assertEqual('some_struct1', struc.name) + self.assertEqual('asm/file.h', struc.fname) + + buff = ''' +/* another comment */ +struct another_struct { + int speed_hz; + int max_transaction_bytes; +}; +''' + scan._parse_structs('include/file2.h', buff) + self.assertIn('another_struct', scan._structs) + struc = scan._structs['another_struct'] + self.assertEqual('another_struct', struc.name) + self.assertEqual('file2.h', struc.fname) + + self.assertEqual(2, len(scan._structs)) + + self.assertEqual("Struct(name='another_struct', fname='file2.h')", + str(struc)) + + def test_struct_scan_errors(self): + """Test scanning a header file with an invalid unicode file""" + output = tools.GetOutputFilename('output.h') + tools.WriteFile(output, b'struct this is a test \x81 of bad unicode') + + scan = src_scan.Scanner(None, False, None) + with test_util.capture_sys_output() as (stdout, _): + scan.scan_header(output) + self.assertIn('due to unicode error', stdout.getvalue())

Drivers can have private / platform data contained in structs and these struct definitions are generally kept in header files. In order to generate build-time devices, dtoc needs to generate code that declares the data contained in those structs. This generated code must include the relevant header file, to avoid a build error.
We need a way for dtoc to scan header files for struct definitions. Then, when it wants to generate code that uses a struct, it can make sure it includes the correct header file, first.
Add a parser for struct information, similar to drivers. Keep a dict of the structs that were found.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/src_scan.py | 86 +++++++++++++++++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 45 +++++++++++++++++++ 2 files changed, 128 insertions(+), 3 deletions(-)
Applied to u-boot-dm, thanks!

It is confusing to have the test files in the same places as the implementation. Move them into a separate directory.
Add a helper function for test_dtoc, to avoid repeating the same path.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/{ => test}/dtoc_test.dts | 0 tools/dtoc/{ => test}/dtoc_test_add_prop.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr32.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr32_64.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr64.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr64_32.dts | 0 tools/dtoc/{ => test}/dtoc_test_aliases.dts | 0 tools/dtoc/{ => test}/dtoc_test_bad_reg.dts | 0 tools/dtoc/{ => test}/dtoc_test_bad_reg2.dts | 0 .../{ => test}/dtoc_test_driver_alias.dts | 0 tools/dtoc/{ => test}/dtoc_test_empty.dts | 0 .../{ => test}/dtoc_test_invalid_driver.dts | 0 tools/dtoc/{ => test}/dtoc_test_phandle.dts | 0 .../dtoc/{ => test}/dtoc_test_phandle_bad.dts | 0 .../{ => test}/dtoc_test_phandle_bad2.dts | 0 .../{ => test}/dtoc_test_phandle_cd_gpios.dts | 0 .../{ => test}/dtoc_test_phandle_reorder.dts | 0 .../{ => test}/dtoc_test_phandle_single.dts | 0 .../{ => test}/dtoc_test_scan_drivers.cxx | 0 tools/dtoc/{ => test}/dtoc_test_simple.dts | 0 tools/dtoc/test_dtoc.py | 2 +- tools/dtoc/test_fdt.py | 31 +++++++++++++------ tools/dtoc/test_src_scan.py | 3 +- 23 files changed, 24 insertions(+), 12 deletions(-) rename tools/dtoc/{ => test}/dtoc_test.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_add_prop.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr32.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr32_64.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr64.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr64_32.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_aliases.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_bad_reg.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_bad_reg2.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_driver_alias.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_empty.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_invalid_driver.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_bad.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_bad2.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_cd_gpios.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_reorder.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_single.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_scan_drivers.cxx (100%) rename tools/dtoc/{ => test}/dtoc_test_simple.dts (100%)
diff --git a/tools/dtoc/dtoc_test.dts b/tools/dtoc/test/dtoc_test.dts similarity index 100% rename from tools/dtoc/dtoc_test.dts rename to tools/dtoc/test/dtoc_test.dts diff --git a/tools/dtoc/dtoc_test_add_prop.dts b/tools/dtoc/test/dtoc_test_add_prop.dts similarity index 100% rename from tools/dtoc/dtoc_test_add_prop.dts rename to tools/dtoc/test/dtoc_test_add_prop.dts diff --git a/tools/dtoc/dtoc_test_addr32.dts b/tools/dtoc/test/dtoc_test_addr32.dts similarity index 100% rename from tools/dtoc/dtoc_test_addr32.dts rename to tools/dtoc/test/dtoc_test_addr32.dts diff --git a/tools/dtoc/dtoc_test_addr32_64.dts b/tools/dtoc/test/dtoc_test_addr32_64.dts similarity index 100% rename from tools/dtoc/dtoc_test_addr32_64.dts rename to tools/dtoc/test/dtoc_test_addr32_64.dts diff --git a/tools/dtoc/dtoc_test_addr64.dts b/tools/dtoc/test/dtoc_test_addr64.dts similarity index 100% rename from tools/dtoc/dtoc_test_addr64.dts rename to tools/dtoc/test/dtoc_test_addr64.dts diff --git a/tools/dtoc/dtoc_test_addr64_32.dts b/tools/dtoc/test/dtoc_test_addr64_32.dts similarity index 100% rename from tools/dtoc/dtoc_test_addr64_32.dts rename to tools/dtoc/test/dtoc_test_addr64_32.dts diff --git a/tools/dtoc/dtoc_test_aliases.dts b/tools/dtoc/test/dtoc_test_aliases.dts similarity index 100% rename from tools/dtoc/dtoc_test_aliases.dts rename to tools/dtoc/test/dtoc_test_aliases.dts diff --git a/tools/dtoc/dtoc_test_bad_reg.dts b/tools/dtoc/test/dtoc_test_bad_reg.dts similarity index 100% rename from tools/dtoc/dtoc_test_bad_reg.dts rename to tools/dtoc/test/dtoc_test_bad_reg.dts diff --git a/tools/dtoc/dtoc_test_bad_reg2.dts b/tools/dtoc/test/dtoc_test_bad_reg2.dts similarity index 100% rename from tools/dtoc/dtoc_test_bad_reg2.dts rename to tools/dtoc/test/dtoc_test_bad_reg2.dts diff --git a/tools/dtoc/dtoc_test_driver_alias.dts b/tools/dtoc/test/dtoc_test_driver_alias.dts similarity index 100% rename from tools/dtoc/dtoc_test_driver_alias.dts rename to tools/dtoc/test/dtoc_test_driver_alias.dts diff --git a/tools/dtoc/dtoc_test_empty.dts b/tools/dtoc/test/dtoc_test_empty.dts similarity index 100% rename from tools/dtoc/dtoc_test_empty.dts rename to tools/dtoc/test/dtoc_test_empty.dts diff --git a/tools/dtoc/dtoc_test_invalid_driver.dts b/tools/dtoc/test/dtoc_test_invalid_driver.dts similarity index 100% rename from tools/dtoc/dtoc_test_invalid_driver.dts rename to tools/dtoc/test/dtoc_test_invalid_driver.dts diff --git a/tools/dtoc/dtoc_test_phandle.dts b/tools/dtoc/test/dtoc_test_phandle.dts similarity index 100% rename from tools/dtoc/dtoc_test_phandle.dts rename to tools/dtoc/test/dtoc_test_phandle.dts diff --git a/tools/dtoc/dtoc_test_phandle_bad.dts b/tools/dtoc/test/dtoc_test_phandle_bad.dts similarity index 100% rename from tools/dtoc/dtoc_test_phandle_bad.dts rename to tools/dtoc/test/dtoc_test_phandle_bad.dts diff --git a/tools/dtoc/dtoc_test_phandle_bad2.dts b/tools/dtoc/test/dtoc_test_phandle_bad2.dts similarity index 100% rename from tools/dtoc/dtoc_test_phandle_bad2.dts rename to tools/dtoc/test/dtoc_test_phandle_bad2.dts diff --git a/tools/dtoc/dtoc_test_phandle_cd_gpios.dts b/tools/dtoc/test/dtoc_test_phandle_cd_gpios.dts similarity index 100% rename from tools/dtoc/dtoc_test_phandle_cd_gpios.dts rename to tools/dtoc/test/dtoc_test_phandle_cd_gpios.dts diff --git a/tools/dtoc/dtoc_test_phandle_reorder.dts b/tools/dtoc/test/dtoc_test_phandle_reorder.dts similarity index 100% rename from tools/dtoc/dtoc_test_phandle_reorder.dts rename to tools/dtoc/test/dtoc_test_phandle_reorder.dts diff --git a/tools/dtoc/dtoc_test_phandle_single.dts b/tools/dtoc/test/dtoc_test_phandle_single.dts similarity index 100% rename from tools/dtoc/dtoc_test_phandle_single.dts rename to tools/dtoc/test/dtoc_test_phandle_single.dts diff --git a/tools/dtoc/dtoc_test_scan_drivers.cxx b/tools/dtoc/test/dtoc_test_scan_drivers.cxx similarity index 100% rename from tools/dtoc/dtoc_test_scan_drivers.cxx rename to tools/dtoc/test/dtoc_test_scan_drivers.cxx diff --git a/tools/dtoc/dtoc_test_simple.dts b/tools/dtoc/test/dtoc_test_simple.dts similarity index 100% rename from tools/dtoc/dtoc_test_simple.dts rename to tools/dtoc/test/dtoc_test_simple.dts diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 6865d949a05..523f0a923eb 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -72,7 +72,7 @@ def get_dtb_file(dts_fname, capture_stderr=False): Returns: str: Filename of compiled file in output directory """ - return fdt_util.EnsureCompiled(os.path.join(OUR_PATH, dts_fname), + return fdt_util.EnsureCompiled(os.path.join(OUR_PATH, 'test', dts_fname), capture_stderr=capture_stderr)
diff --git a/tools/dtoc/test_fdt.py b/tools/dtoc/test_fdt.py index e8fbbd5d10a..1c3a8a2ab1e 100755 --- a/tools/dtoc/test_fdt.py +++ b/tools/dtoc/test_fdt.py @@ -48,6 +48,17 @@ def _GetPropertyValue(dtb, node, prop_name): data = dtb.GetContents()[offset:offset + len(prop.value)] return prop, [chr(x) for x in data]
+def find_dtb_file(dts_fname): + """Locate a test file in the test/ directory + + Args: + dts_fname (str): Filename to find, e.g. 'dtoc_test_simple.dts] + + Returns: + str: Path to the test filename + """ + return os.path.join('tools/dtoc/test', dts_fname) +
class TestFdt(unittest.TestCase): """Tests for the Fdt module @@ -64,7 +75,7 @@ class TestFdt(unittest.TestCase): tools.FinaliseOutputDir()
def setUp(self): - self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
def testFdt(self): """Test that we can open an Fdt""" @@ -141,7 +152,7 @@ class TestNode(unittest.TestCase): tools.FinaliseOutputDir()
def setUp(self): - self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) self.node = self.dtb.GetNode('/spl-test')
def testOffset(self): @@ -203,7 +214,7 @@ class TestNode(unittest.TestCase):
def testLookupPhandle(self): """Test looking up a single phandle""" - dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts') + dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts')) node = dtb.GetNode('/phandle-source2') prop = node.props['clocks'] target = dtb.GetNode('/phandle-target') @@ -222,7 +233,7 @@ class TestProp(unittest.TestCase): tools.FinaliseOutputDir()
def setUp(self): - self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) self.node = self.dtb.GetNode('/spl-test') self.fdt = self.dtb.GetFdtObj()
@@ -230,7 +241,7 @@ class TestProp(unittest.TestCase): self.assertEqual(None, self.dtb.GetNode('missing'))
def testPhandle(self): - dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts') + dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts')) node = dtb.GetNode('/phandle-source2') prop = node.props['clocks'] self.assertTrue(fdt32_to_cpu(prop.value) > 0) @@ -488,7 +499,7 @@ class TestFdtUtil(unittest.TestCase): tools.FinaliseOutputDir()
def setUp(self): - self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) self.node = self.dtb.GetNode('/spl-test')
def testGetInt(self): @@ -531,7 +542,7 @@ class TestFdtUtil(unittest.TestCase): str(e.exception))
def testGetPhandleList(self): - dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts') + dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts')) node = dtb.GetNode('/phandle-source2') self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks')) node = dtb.GetNode('/phandle-source') @@ -551,7 +562,7 @@ class TestFdtUtil(unittest.TestCase): self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0)) self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1))
- dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts') + dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts')) node1 = dtb2.GetNode('/test1') val = node1.props['reg'].value self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2)) @@ -565,7 +576,7 @@ class TestFdtUtil(unittest.TestCase):
def testEnsureCompiled(self): """Test a degenerate case of this function (file already compiled)""" - dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts') + dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts')) self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb))
def testEnsureCompiledTmpdir(self): @@ -574,7 +585,7 @@ class TestFdtUtil(unittest.TestCase): old_outdir = tools.outdir tools.outdir= None tmpdir = tempfile.mkdtemp(prefix='test_fdt.') - dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts', + dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'), tmpdir) self.assertEqual(tmpdir, os.path.dirname(dtb)) shutil.rmtree(tmpdir) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index a0b0e097eb2..a7eba3005e5 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -59,7 +59,8 @@ class TestSrcScan(unittest.TestCase): def test_additional(self): """Test with additional drivers to scan""" scan = src_scan.Scanner( - None, True, [None, '', 'tools/dtoc/dtoc_test_scan_drivers.cxx']) + None, True, + [None, '', 'tools/dtoc/test/dtoc_test_scan_drivers.cxx']) scan.scan_drivers() self.assertIn('sandbox_gpio_alias2', scan._driver_aliases) self.assertEqual('sandbox_gpio',

It is confusing to have the test files in the same places as the implementation. Move them into a separate directory.
Add a helper function for test_dtoc, to avoid repeating the same path.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/{ => test}/dtoc_test.dts | 0 tools/dtoc/{ => test}/dtoc_test_add_prop.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr32.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr32_64.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr64.dts | 0 tools/dtoc/{ => test}/dtoc_test_addr64_32.dts | 0 tools/dtoc/{ => test}/dtoc_test_aliases.dts | 0 tools/dtoc/{ => test}/dtoc_test_bad_reg.dts | 0 tools/dtoc/{ => test}/dtoc_test_bad_reg2.dts | 0 .../{ => test}/dtoc_test_driver_alias.dts | 0 tools/dtoc/{ => test}/dtoc_test_empty.dts | 0 .../{ => test}/dtoc_test_invalid_driver.dts | 0 tools/dtoc/{ => test}/dtoc_test_phandle.dts | 0 .../dtoc/{ => test}/dtoc_test_phandle_bad.dts | 0 .../{ => test}/dtoc_test_phandle_bad2.dts | 0 .../{ => test}/dtoc_test_phandle_cd_gpios.dts | 0 .../{ => test}/dtoc_test_phandle_reorder.dts | 0 .../{ => test}/dtoc_test_phandle_single.dts | 0 .../{ => test}/dtoc_test_scan_drivers.cxx | 0 tools/dtoc/{ => test}/dtoc_test_simple.dts | 0 tools/dtoc/test_dtoc.py | 2 +- tools/dtoc/test_fdt.py | 31 +++++++++++++------ tools/dtoc/test_src_scan.py | 3 +- 23 files changed, 24 insertions(+), 12 deletions(-) rename tools/dtoc/{ => test}/dtoc_test.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_add_prop.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr32.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr32_64.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr64.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_addr64_32.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_aliases.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_bad_reg.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_bad_reg2.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_driver_alias.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_empty.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_invalid_driver.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_bad.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_bad2.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_cd_gpios.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_reorder.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_phandle_single.dts (100%) rename tools/dtoc/{ => test}/dtoc_test_scan_drivers.cxx (100%) rename tools/dtoc/{ => test}/dtoc_test_simple.dts (100%)
Applied to u-boot-dm, thanks!

These have '_test' suffixes which are not present on the drivers in the source code. Drop the suffixes to avoid a mismatch when scanning.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/test/dtoc_test_simple.dts | 4 ++-- tools/dtoc/test_dtoc.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/tools/dtoc/test/dtoc_test_simple.dts b/tools/dtoc/test/dtoc_test_simple.dts index 1c87b891929..d8ab8613ee3 100644 --- a/tools/dtoc/test/dtoc_test_simple.dts +++ b/tools/dtoc/test/dtoc_test_simple.dts @@ -45,12 +45,12 @@ };
i2c@0 { - compatible = "sandbox,i2c-test"; + compatible = "sandbox,i2c"; u-boot,dm-pre-reloc; #address-cells = <1>; #size-cells = <0>; pmic@9 { - compatible = "sandbox,pmic-test"; + compatible = "sandbox,pmic"; u-boot,dm-pre-reloc; reg = <9>; low-power; diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 523f0a923eb..9049c2895f1 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -207,9 +207,9 @@ class TestDtoc(unittest.TestCase): self.assertEqual(C_HEADER.splitlines() + [''], lines)
struct_text = HEADER + ''' -struct dtd_sandbox_i2c_test { +struct dtd_sandbox_i2c { }; -struct dtd_sandbox_pmic_test { +struct dtd_sandbox_pmic { \tbool\t\tlow_power; \tfdt64_t\t\treg[2]; }; @@ -229,22 +229,22 @@ struct dtd_sandbox_spl_test {
platdata_text = C_HEADER + ''' /* Node /i2c@0 index 0 */ -static struct dtd_sandbox_i2c_test dtv_i2c_at_0 = { +static struct dtd_sandbox_i2c dtv_i2c_at_0 = { }; U_BOOT_DRVINFO(i2c_at_0) = { -\t.name\t\t= "sandbox_i2c_test", +\t.name\t\t= "sandbox_i2c", \t.plat\t= &dtv_i2c_at_0, \t.plat_size\t= sizeof(dtv_i2c_at_0), \t.parent_idx\t= -1, };
/* Node /i2c@0/pmic@9 index 1 */ -static struct dtd_sandbox_pmic_test dtv_pmic_at_9 = { +static struct dtd_sandbox_pmic dtv_pmic_at_9 = { \t.low_power\t\t= true, \t.reg\t\t\t= {0x9, 0x0}, }; U_BOOT_DRVINFO(pmic_at_9) = { -\t.name\t\t= "sandbox_pmic_test", +\t.name\t\t= "sandbox_pmic", \t.plat\t= &dtv_pmic_at_9, \t.plat_size\t= sizeof(dtv_pmic_at_9), \t.parent_idx\t= 0,

These have '_test' suffixes which are not present on the drivers in the source code. Drop the suffixes to avoid a mismatch when scanning.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/test/dtoc_test_simple.dts | 4 ++-- tools/dtoc/test_dtoc.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-)
Applied to u-boot-dm, thanks!

It is convenient to attach drivers, etc. to nodes so that we can use the Node object as the main data structure in this module.
Add a function which adds the new properties, along with documentation.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index e9be5985c72..8c36fbc68d2 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -354,8 +354,44 @@ class DtbPlatdata(): self.scan_node(self._fdt.GetRoot(), valid_nodes) self._valid_nodes = sorted(valid_nodes, key=lambda x: conv_name_to_c(x.name)) + + def prepare_nodes(self): + """Add extra properties to the nodes we are using + + The following properties are added for use by dtoc: + idx: Index number of this node (0=first, etc.) + struct_name: Name of the struct dtd used by this node + var_name: C name for this node + child_devs: List of child devices for this node, each a None + child_refs: Dict of references for each child: + key: Position in child list (-1=head, 0=first, 1=second, ... + n-1=last, n=head) + seq: Sequence number of the device (unique within its uclass), or + -1 not not known yet + dev_ref: Reference to this device, e.g. 'DM_DEVICE_REF(serial)' + driver: Driver record for this node, or None if not known + uclass: Uclass record for this node, or None if not known + uclass_seq: Position of this device within the uclass list (0=first, + n-1=last) + parent_seq: Position of this device within it siblings (0=first, + n-1=last) + parent_driver: Driver record of the node's parent, or None if none. + We don't use node.parent.driver since node.parent may not be in + the list of valid nodes + """ for idx, node in enumerate(self._valid_nodes): node.idx = idx + node.struct_name, _ = self._scan.get_normalized_compat_name(node) + node.var_name = conv_name_to_c(node.name) + node.child_devs = [] + node.child_refs = {} + node.seq = -1 + node.dev_ref = None + node.driver = None + node.uclass = None + node.uclass_seq = None + node.parent_seq = None + node.parent_driver = None
@staticmethod def get_num_cells(node): @@ -705,6 +741,7 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, plat = DtbPlatdata(scan, dtb_file, include_disabled) plat.scan_dtb() plat.scan_tree() + plat.prepare_nodes() plat.scan_reg_sizes() plat.setup_output_dirs(output_dirs) plat.scan_structs()

It is convenient to attach drivers, etc. to nodes so that we can use the Node object as the main data structure in this module.
Add a function which adds the new properties, along with documentation.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
Applied to u-boot-dm, thanks!

Now that we have these available, use them instead of recalculating things each time.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 45 ++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 26 deletions(-)
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 8c36fbc68d2..2ec22edfbf0 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -470,7 +470,6 @@ class DtbPlatdata(): """ structs = self._struct_data for node in self._valid_nodes: - node_name, _ = self._scan.get_normalized_compat_name(node) fields = {}
# Get a list of all the valid properties in this node. @@ -478,9 +477,9 @@ class DtbPlatdata(): if name not in PROP_IGNORE_LIST and name[0] != '#': fields[name] = copy.deepcopy(prop)
- # If we've seen this node_name before, update the existing struct. - if node_name in structs: - struct = structs[node_name] + # If we've seen this struct_name before, update the existing struct + if node.struct_name in structs: + struct = structs[node.struct_name] for name, prop in fields.items(): oldprop = struct.get(name) if oldprop: @@ -490,11 +489,10 @@ class DtbPlatdata():
# Otherwise store this as a new struct. else: - structs[node_name] = fields + structs[node.struct_name] = fields
for node in self._valid_nodes: - node_name, _ = self._scan.get_normalized_compat_name(node) - struct = structs[node_name] + struct = structs[node.struct_name] for name, prop in node.props.items(): if name not in PROP_IGNORE_LIST and name[0] != '#': prop.Widen(struct[name]) @@ -598,23 +596,22 @@ class DtbPlatdata(): self.buf(', '.join(vals[i:i + 8])) self.buf('}')
- def _declare_device(self, var_name, struct_name, node_parent): + def _declare_device(self, node): """Add a device declaration to the output
This declares a U_BOOT_DRVINFO() for the device being processed
Args: - var_name (str): C name for the node - struct_name (str): Name for the dt struct associated with the node - node_parent (Node): Parent of the node (or None if none) + node: Node to process """ - self.buf('U_BOOT_DRVINFO(%s) = {\n' % var_name) - self.buf('\t.name\t\t= "%s",\n' % struct_name) - self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, var_name)) - self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name)) + self.buf('U_BOOT_DRVINFO(%s) = {\n' % node.var_name) + self.buf('\t.name\t\t= "%s",\n' % node.struct_name) + self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, node.var_name)) + self.buf('\t.plat_size\t= sizeof(%s%s),\n' % + (VAL_PREFIX, node.var_name)) idx = -1 - if node_parent and node_parent in self._valid_nodes: - idx = node_parent.idx + if node.parent and node.parent in self._valid_nodes: + idx = node.parent.idx self.buf('\t.parent_idx\t= %d,\n' % idx) self.buf('};\n') self.buf('\n') @@ -638,16 +635,14 @@ class DtbPlatdata(): self.buf(get_value(prop.type, prop.value)) self.buf(',\n')
- def _output_values(self, var_name, struct_name, node): + def _output_values(self, node): """Output the definition of a device's struct values
Args: - var_name (str): C name for the node - struct_name (str): Name for the dt struct associated with the node - node (Node): Node being output + node (Node): Node to output """ self.buf('static struct %s%s %s%s = {\n' % - (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name)) + (STRUCT_PREFIX, node.struct_name, VAL_PREFIX, node.var_name)) for pname in sorted(node.props): self._output_prop(node, node.props[pname]) self.buf('};\n') @@ -658,12 +653,10 @@ class DtbPlatdata(): Args: node (fdt.Node): node to output """ - struct_name, _ = self._scan.get_normalized_compat_name(node) - var_name = conv_name_to_c(node.name) self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
- self._output_values(var_name, struct_name, node) - self._declare_device(var_name, struct_name, node.parent) + self._output_values(node) + self._declare_device(node)
self.out(''.join(self.get_buf()))

Now that we have these available, use them instead of recalculating things each time.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 45 ++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 26 deletions(-)
Applied to u-boot-dm, thanks!

Add logic to assign property values to nodes as required by dtoc. The references allow nodes to refer to each other in C code. The macros used by dtoc are not yet defined in driver model. They will be added along with the actual driver model implementation.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 37 +++++++++++++++++++ tools/dtoc/src_scan.py | 11 ++++++ tools/dtoc/test_dtoc.py | 76 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+)
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 2ec22edfbf0..ad71f703e52 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -647,6 +647,38 @@ class DtbPlatdata(): self._output_prop(node, node.props[pname]) self.buf('};\n')
+ def process_nodes(self, need_drivers): + nodes_to_output = list(self._valid_nodes) + + for node in nodes_to_output: + node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name + driver = self._scan.get_driver(node.struct_name) + if not driver: + if not need_drivers: + continue + raise ValueError("Cannot parse/find driver for '%s'" % + node.struct_name) + node.driver = driver + parent_driver = None + if node.parent in self._valid_nodes: + parent_driver = self._scan.get_driver(node.parent.struct_name) + if not parent_driver: + if not need_drivers: + continue + raise ValueError( + "Cannot parse/find parent driver '%s' for '%s'" % + (node.parent.struct_name, node.struct_name)) + node.parent_seq = len(node.parent.child_devs) + node.parent.child_devs.append(node) + node.parent.child_refs[node.parent_seq] = \ + '&%s->sibling_node' % node.dev_ref + node.parent_driver = parent_driver + + for node in nodes_to_output: + ref = '&%s->child_head' % node.dev_ref + node.child_refs[-1] = ref + node.child_refs[len(node.child_devs)] = ref + def output_node(self, node): """Output the C code for a node
@@ -731,6 +763,9 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, if not scan: scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional) scan.scan_drivers() + do_process = True + else: + do_process = False plat = DtbPlatdata(scan, dtb_file, include_disabled) plat.scan_dtb() plat.scan_tree() @@ -739,6 +774,8 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, plat.setup_output_dirs(output_dirs) plat.scan_structs() plat.scan_phandles() + if do_process: + plat.process_nodes(False)
cmds = args[0].split(',') if 'all' in cmds: diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index bf3e5de9b1e..504dac008d6 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -188,6 +188,17 @@ class Scanner: self._uclass = {} self._structs = {}
+ def get_driver(self, name): + """Get a driver given its name + + Args: + name (str): Driver name + + Returns: + Driver: Driver or None if not found + """ + return self._drivers.get(name) + def get_normalized_compat_name(self, node): """Get a node's normalized compat name
diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 9049c2895f1..3e98e363125 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -953,3 +953,79 @@ U_BOOT_DRVINFO(spl_test2) = { self.assertEqual( {'dt-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb'}, leafs) + + def setup_process_test(self): + """Set up a test of process_nodes() + + This uses saved_scan but returns a deep copy of it, so it is safe to + modify it in these tests + + Returns: + tuple: + DtbPlatdata: object to test + Scanner: scanner to use + """ + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + + # Take a copy before messing with it + scan = copy.deepcopy(saved_scan) + plat = dtb_platdata.DtbPlatdata(scan, dtb_file, False) + plat.scan_dtb() + plat.scan_tree() + plat.prepare_nodes() + return plat, scan + + def test_process_nodes(self): + """Test processing nodes to add various info""" + plat, scan = self.setup_process_test() + plat.process_nodes(True) + + i2c_node = plat._fdt.GetNode('/i2c@0') + pmic_node = plat._fdt.GetNode('/i2c@0/pmic@9') + pmic = scan._drivers['sandbox_pmic'] + i2c = scan._drivers['sandbox_i2c'] + self.assertEqual('DM_DEVICE_REF(pmic_at_9)', pmic_node.dev_ref) + self.assertEqual(pmic, pmic_node.driver) + self.assertEqual(i2c_node, pmic_node.parent) + self.assertEqual(i2c, pmic_node.parent_driver) + + # The pmic is the only child + self.assertEqual(pmic_node.parent_seq, 0) + self.assertEqual([pmic_node], i2c_node.child_devs) + + # Start and end of the list should be the child_head + ref = '&DM_DEVICE_REF(i2c_at_0)->child_head' + self.assertEqual( + {-1: ref, 0: '&DM_DEVICE_REF(pmic_at_9)->sibling_node', 1: ref}, + i2c_node.child_refs) + + def test_process_nodes_bad_parent(self): + # Pretend that i2c has a parent (the pmic) and delete that driver + plat, scan = self.setup_process_test() + + i2c_node = plat._fdt.GetNode('/i2c@0') + pmic_node = plat._fdt.GetNode('/i2c@0/pmic@9') + del scan._drivers['sandbox_pmic'] + i2c_node.parent = pmic_node + + # Process twice, the second time to generate an exception + plat.process_nodes(False) + with self.assertRaises(ValueError) as exc: + plat.process_nodes(True) + self.assertIn( + "Cannot parse/find parent driver 'sandbox_pmic' for 'sandbox_i2c", + str(exc.exception)) + + def test_process_nodes_bad_node(self): + plat, scan = self.setup_process_test() + + # Now remove the pmic driver + del scan._drivers['sandbox_pmic'] + + # Process twice, the second time to generate an exception + plat.process_nodes(False) + with self.assertRaises(ValueError) as exc: + plat.process_nodes(True) + self.assertIn("Cannot parse/find driver for 'sandbox_pmic", + str(exc.exception))

Add logic to assign property values to nodes as required by dtoc. The references allow nodes to refer to each other in C code. The macros used by dtoc are not yet defined in driver model. They will be added along with the actual driver model implementation.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 37 +++++++++++++++++++ tools/dtoc/src_scan.py | 11 ++++++ tools/dtoc/test_dtoc.py | 76 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+)
Applied to u-boot-dm, thanks!

Mark all nodes that are actually used, so we can perform extra checks on them.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 3 +++ tools/dtoc/src_scan.py | 25 ++++++++++++++++++++++--- tools/dtoc/test_dtoc.py | 11 +++++++++++ tools/dtoc/test_src_scan.py | 2 +- 4 files changed, 37 insertions(+), 4 deletions(-)
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index ad71f703e52..28669f31217 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -650,6 +650,9 @@ class DtbPlatdata(): def process_nodes(self, need_drivers): nodes_to_output = list(self._valid_nodes)
+ # Figure out which drivers we actually use + self._scan.mark_used(nodes_to_output) + for node in nodes_to_output: node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name driver = self._scan.get_driver(node.struct_name) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 504dac008d6..1a02d41063f 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -66,6 +66,7 @@ class Driver: e.g. 'pci_child_priv' child_plat (str): struct name of the per_child_plat_auto member, e.g. 'pci_child_plat' + used (bool): True if the driver is used by the structs being output """ def __init__(self, name, fname): self.name = name @@ -76,17 +77,19 @@ class Driver: self.plat = '' self.child_priv = '' self.child_plat = '' + self.used = False
def __eq__(self, other): return (self.name == other.name and self.uclass_id == other.uclass_id and self.compat == other.compat and self.priv == other.priv and - self.plat == other.plat) + self.plat == other.plat and + self.used == other.used)
def __repr__(self): - return ("Driver(name='%s', uclass_id='%s', compat=%s, priv=%s)" % - (self.name, self.uclass_id, self.compat, self.priv)) + return ("Driver(name='%s', used=%s, uclass_id='%s', compat=%s, priv=%s)" % + (self.name, self.used, self.uclass_id, self.compat, self.priv))
class UclassDriver: @@ -596,3 +599,19 @@ class Scanner: self.scan_driver(fname) else: self.scan_driver(self._basedir + '/' + fname) + + def mark_used(self, nodes): + """Mark the drivers associated with a list of nodes as 'used' + + This takes a list of nodes, finds the driver for each one and marks it + as used. + + Args: + nodes (list of None): Nodes that are in use + """ + # Figure out which drivers we actually use + for node in nodes: + struct_name, _ = self.get_normalized_compat_name(node) + driver = self._drivers.get(struct_name) + if driver: + driver.used = True diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 3e98e363125..d90ece205d7 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -1029,3 +1029,14 @@ U_BOOT_DRVINFO(spl_test2) = { plat.process_nodes(True) self.assertIn("Cannot parse/find driver for 'sandbox_pmic", str(exc.exception)) + + def test_process_nodes_used(self): + """Test processing nodes to add various info""" + plat, scan = self.setup_process_test() + plat.process_nodes(True) + + pmic = scan._drivers['sandbox_pmic'] + self.assertTrue(pmic.used) + + gpio = scan._drivers['sandbox_gpio'] + self.assertFalse(gpio.used) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index a7eba3005e5..ebdc12abc87 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -98,7 +98,7 @@ class TestSrcScan(unittest.TestCase): drv3.uclass_id = i2c drv3.compat = compat self.assertEqual( - "Driver(name='fred', uclass_id='I2C_UCLASS', " + "Driver(name='fred', used=False, uclass_id='I2C_UCLASS', " "compat={'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', " "'rockchip,rk3288-srf': None}, priv=)", str(drv1)) self.assertEqual(drv1, drv3)

Mark all nodes that are actually used, so we can perform extra checks on them.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 3 +++ tools/dtoc/src_scan.py | 25 ++++++++++++++++++++++--- tools/dtoc/test_dtoc.py | 11 +++++++++++ tools/dtoc/test_src_scan.py | 2 +- 4 files changed, 37 insertions(+), 4 deletions(-)
Applied to u-boot-dm, thanks!

U-Boot operates in several phases, typically TPL, SPL and U-Boot proper. The latter does not use dtoc.
In some rare cases different drivers are used for two phases. For example, in TPL it may not be necessary to use the full PCI subsystem, so a simple driver can be used instead.
This works in the build system simply by compiling in one driver or the other (e.g. PCI driver + uclass for SPL; simple_bus for TPL). But dtoc has no way of knowing which code is compiled in for which phase, since it does not inspect Makefiles or dependency graphs.
So to make this work for dtoc, we need to be able to explicitly mark drivers with their phase. This is done by adding an empty macro to the driver. Add support for this in dtoc.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
include/dm/device.h | 16 ++++++++++++++++ tools/dtoc/dtb_platdata.py | 7 +++++-- tools/dtoc/main.py | 5 ++++- tools/dtoc/src_scan.py | 12 +++++++++++- tools/dtoc/test_dtoc.py | 16 ++++++++-------- tools/dtoc/test_src_scan.py | 3 +++ 6 files changed, 47 insertions(+), 12 deletions(-)
diff --git a/include/dm/device.h b/include/dm/device.h index e665558444b..0086070b58b 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -345,6 +345,22 @@ struct driver { */ #define DM_DRIVER_ALIAS(__name, __alias)
+/** + * Declare a macro to indicate which phase of U-Boot this driver is fore. + * + * + * This macro produces no code but its information will be parsed by dtoc. The + * macro can be only be used once in a driver. Put it within the U_BOOT_DRIVER() + * declaration, e.g.: + * + * U_BOOT_DRIVER(cpu) = { + * .name = ... + * ... + * DM_PHASE(tpl) + * }; + */ +#define DM_PHASE(_phase) + /** * dev_get_plat() - Get the platform data for a device * diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 28669f31217..ef0454c8904 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -733,7 +733,7 @@ OUTPUT_FILES = { }
-def run_steps(args, dtb_file, include_disabled, output, output_dirs, +def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, warning_disabled=False, drivers_additional=None, basedir=None, scan=None): """Run all the steps of the dtoc tool @@ -746,6 +746,8 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, output_dirs (tuple of str): Directory to put C output files Directory to put H output files + phase: The phase of U-Boot that we are generating data for, e.g. 'spl' + or 'tpl'. None if not known warning_disabled (bool): True to avoid showing warnings about missing drivers drivers_additional (list): List of additional drivers to use during @@ -764,7 +766,8 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, raise ValueError('Must specify either output or output_dirs, not both')
if not scan: - scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional) + scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional, + phase) scan.scan_drivers() do_process = True else: diff --git a/tools/dtoc/main.py b/tools/dtoc/main.py index 355b1e62773..15a8ff167a9 100755 --- a/tools/dtoc/main.py +++ b/tools/dtoc/main.py @@ -85,6 +85,8 @@ parser.add_option('--include-disabled', action='store_true', help='Include disabled nodes') parser.add_option('-o', '--output', action='store', help='Select output filename') +parser.add_option('-p', '--phase', type=str, + help='set phase of U-Boot this invocation is for (spl/tpl)') parser.add_option('-P', '--processes', type=int, help='set number of processes to use for running tests') parser.add_option('-t', '--test', action='store_true', dest='test', @@ -104,4 +106,5 @@ elif options.test_coverage: else: dtb_platdata.run_steps(args, options.dtb_file, options.include_disabled, options.output, - [options.c_output_dir, options.h_output_dir]) + [options.c_output_dir, options.h_output_dir], + phase=options.phase) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 1a02d41063f..2699153016c 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -67,6 +67,7 @@ class Driver: child_plat (str): struct name of the per_child_plat_auto member, e.g. 'pci_child_plat' used (bool): True if the driver is used by the structs being output + phase (str): Which phase of U-Boot to use this driver """ def __init__(self, name, fname): self.name = name @@ -78,6 +79,7 @@ class Driver: self.child_priv = '' self.child_plat = '' self.used = False + self.phase = ''
def __eq__(self, other): return (self.name == other.name and @@ -173,8 +175,10 @@ class Scanner: _structs: Dict of all structs found in U-Boot: key: Name of struct value: Struct object + _phase: The phase of U-Boot that we are generating data for, e.g. 'spl' + or 'tpl'. None if not known """ - def __init__(self, basedir, warning_disabled, drivers_additional): + def __init__(self, basedir, warning_disabled, drivers_additional, phase=''): """Set up a new Scanner """ if not basedir: @@ -190,6 +194,7 @@ class Scanner: self._compat_to_driver = {} self._uclass = {} self._structs = {} + self._phase = phase
def get_driver(self, name): """Get a driver given its name @@ -428,6 +433,8 @@ class Scanner: re_of_match = re.compile( r'.of_match\s*=\s*(of_match_ptr()?([a-z0-9_]+)())?,')
+ re_phase = re.compile('^\s*DM_PHASE((.*)).*$') + # Matches the struct name for priv, plat re_priv = self._get_re_for_member('priv_auto') re_plat = self._get_re_for_member('plat_auto') @@ -454,6 +461,7 @@ class Scanner: m_plat = re_plat.match(line) m_cplat = re_child_plat.match(line) m_cpriv = re_child_priv.match(line) + m_phase = re_phase.match(line) if m_priv: driver.priv = m_priv.group(1) elif m_plat: @@ -466,6 +474,8 @@ class Scanner: driver.uclass_id = m_id.group(1) elif m_of_match: compat = m_of_match.group(2) + elif m_phase: + driver.phase = m_phase.group(1) elif '};' in line: if driver.uclass_id and compat: if compat not in of_match: diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index d90ece205d7..c1fafb656fb 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -138,8 +138,8 @@ class TestDtoc(unittest.TestCase): dtb_file (str): Filename of .dtb file output (str): Filename of output file """ - dtb_platdata.run_steps(args, dtb_file, False, output, [], True, - None, None, scan=copy_scan()) + dtb_platdata.run_steps(args, dtb_file, False, output, [], None, + warning_disabled=True, scan=copy_scan())
def test_name(self): """Test conversion of device tree names to C identifiers""" @@ -365,7 +365,7 @@ U_BOOT_DRVINFO(gpios_at_0) = { output = tools.GetOutputFilename('output') with test_util.capture_sys_output() as _: dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], - scan=copy_scan()) + None, scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(HEADER + ''' @@ -375,7 +375,7 @@ struct dtd_invalid {
with test_util.capture_sys_output() as _: dtb_platdata.run_steps(['platdata'], dtb_file, False, output, [], - scan=copy_scan()) + None, scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' @@ -927,8 +927,8 @@ U_BOOT_DRVINFO(spl_test2) = { def test_output_conflict(self): """Test a conflict between and output dirs and output file""" with self.assertRaises(ValueError) as exc: - dtb_platdata.run_steps(['all'], None, False, 'out', ['cdir'], True, - scan=copy_scan()) + dtb_platdata.run_steps(['all'], None, False, 'out', ['cdir'], None, + warning_disabled=True, scan=copy_scan()) self.assertIn("Must specify either output or output_dirs, not both", str(exc.exception))
@@ -944,8 +944,8 @@ U_BOOT_DRVINFO(spl_test2) = { fnames = glob.glob(outdir + '/*') self.assertEqual(2, len(fnames))
- dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], True, - scan=copy_scan()) + dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], None, + warning_disabled=True, scan=copy_scan()) fnames = glob.glob(outdir + '/*') self.assertEqual(4, len(fnames))
diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index ebdc12abc87..8d35b33c28a 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -233,6 +233,7 @@ U_BOOT_DRIVER(i2c_tegra) = { self.assertIn('i2c_tegra', scan._drivers) drv = scan._drivers['i2c_tegra'] self.assertEqual('i2c_tegra', drv.name) + self.assertEqual('', drv.phase)
def test_priv(self): """Test collection of struct info from drivers""" @@ -250,6 +251,7 @@ U_BOOT_DRIVER(testing) = { .plat_auto = sizeof(struct some_plat), .per_child_auto = sizeof(struct some_cpriv), .per_child_plat_auto = sizeof(struct some_cplat), + DM_PHASE(tpl) }; ''' scan = src_scan.Scanner(None, False, None) @@ -264,6 +266,7 @@ U_BOOT_DRIVER(testing) = { self.assertEqual('some_plat', drv.plat) self.assertEqual('some_cpriv', drv.child_priv) self.assertEqual('some_cplat', drv.child_plat) + self.assertEqual('tpl', drv.phase) self.assertEqual(1, len(scan._drivers))
def test_uclass_scan(self):

U-Boot operates in several phases, typically TPL, SPL and U-Boot proper. The latter does not use dtoc.
In some rare cases different drivers are used for two phases. For example, in TPL it may not be necessary to use the full PCI subsystem, so a simple driver can be used instead.
This works in the build system simply by compiling in one driver or the other (e.g. PCI driver + uclass for SPL; simple_bus for TPL). But dtoc has no way of knowing which code is compiled in for which phase, since it does not inspect Makefiles or dependency graphs.
So to make this work for dtoc, we need to be able to explicitly mark drivers with their phase. This is done by adding an empty macro to the driver. Add support for this in dtoc.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
include/dm/device.h | 16 ++++++++++++++++ tools/dtoc/dtb_platdata.py | 7 +++++-- tools/dtoc/main.py | 5 ++++- tools/dtoc/src_scan.py | 12 +++++++++++- tools/dtoc/test_dtoc.py | 16 ++++++++-------- tools/dtoc/test_src_scan.py | 3 +++ 6 files changed, 47 insertions(+), 12 deletions(-)
Applied to u-boot-dm, thanks!

Pass the U-Boot phase as a parameter so dtoc can use it. At present it is ether "spl" or "tpl".
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
scripts/Makefile.spl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index ea4e045769c..67641d0faea 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -309,7 +309,7 @@ $(obj)/$(SPL_BIN).dtb: $(obj)/dts/dt-$(SPL_NAME).dtb FORCE pythonpath = PYTHONPATH=scripts/dtc/pylibfdt
DTOC_ARGS := $(pythonpath) $(srctree)/tools/dtoc/dtoc \ - -d $(obj)/$(SPL_BIN).dtb + -d $(obj)/$(SPL_BIN).dtb -p $(SPL_NAME)
quiet_cmd_dtoc = DTOC $@ cmd_dtoc = $(DTOC_ARGS) -c $(obj)/dts -C include/generated all

Pass the U-Boot phase as a parameter so dtoc can use it. At present it is ether "spl" or "tpl".
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
scripts/Makefile.spl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Applied to u-boot-dm, thanks!

Typically dtoc can detect the header file needed for a driver by looking for the structs that it uses. For example, if a driver as a .priv_auto that uses 'struct serial_priv', then dtoc can search header files for the definition of that struct and use the file.
In some cases, enums are used in drivers, typically with the .data field of struct udevice_id. Since dtoc does not support searching for these, add a way to tell dtoc which header to use. This works as a macro included in the driver definition.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
include/dm/device.h | 18 ++++++++++++++++++ tools/dtoc/src_scan.py | 7 +++++++ tools/dtoc/test_src_scan.py | 4 ++++ 3 files changed, 29 insertions(+)
diff --git a/include/dm/device.h b/include/dm/device.h index 0086070b58b..1c52c9d3120 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -361,6 +361,24 @@ struct driver { */ #define DM_PHASE(_phase)
+/** + * Declare a macro to declare a header needed for a driver. Often the correct + * header can be found automatically, but only for struct declarations. For + * enums and #defines used in the driver declaration and declared in a different + * header from the structs, this macro must be used. + * + * This macro produces no code but its information will be parsed by dtoc. The + * macro can be used multiple times with different headers, for the same driver. + * Put it within the U_BOOT_DRIVER() declaration, e.g.: + * + * U_BOOT_DRIVER(cpu) = { + * .name = ... + * ... + * DM_HEADER(<asm/cpu.h>) + * }; + */ +#define DM_HEADER(_hdr) + /** * dev_get_plat() - Get the platform data for a device * diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 2699153016c..206b2b37583 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -68,6 +68,8 @@ class Driver: e.g. 'pci_child_plat' used (bool): True if the driver is used by the structs being output phase (str): Which phase of U-Boot to use this driver + headers (list): List of header files needed for this driver (each a str) + e.g. ['<asm/cpu.h>'] """ def __init__(self, name, fname): self.name = name @@ -80,6 +82,7 @@ class Driver: self.child_plat = '' self.used = False self.phase = '' + self.headers = []
def __eq__(self, other): return (self.name == other.name and @@ -434,6 +437,7 @@ class Scanner: r'.of_match\s*=\s*(of_match_ptr()?([a-z0-9_]+)())?,')
re_phase = re.compile('^\s*DM_PHASE((.*)).*$') + re_hdr = re.compile('^\s*DM_HEADER((.*)).*$')
# Matches the struct name for priv, plat re_priv = self._get_re_for_member('priv_auto') @@ -462,6 +466,7 @@ class Scanner: m_cplat = re_child_plat.match(line) m_cpriv = re_child_priv.match(line) m_phase = re_phase.match(line) + m_hdr = re_hdr.match(line) if m_priv: driver.priv = m_priv.group(1) elif m_plat: @@ -476,6 +481,8 @@ class Scanner: compat = m_of_match.group(2) elif m_phase: driver.phase = m_phase.group(1) + elif m_hdr: + driver.headers.append(m_hdr.group(1)) elif '};' in line: if driver.uclass_id and compat: if compat not in of_match: diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 8d35b33c28a..245b7302fd6 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -234,6 +234,7 @@ U_BOOT_DRIVER(i2c_tegra) = { drv = scan._drivers['i2c_tegra'] self.assertEqual('i2c_tegra', drv.name) self.assertEqual('', drv.phase) + self.assertEqual([], drv.headers)
def test_priv(self): """Test collection of struct info from drivers""" @@ -252,6 +253,8 @@ U_BOOT_DRIVER(testing) = { .per_child_auto = sizeof(struct some_cpriv), .per_child_plat_auto = sizeof(struct some_cplat), DM_PHASE(tpl) + DM_HEADER(<i2c.h>) + DM_HEADER(<asm/clk.h>) }; ''' scan = src_scan.Scanner(None, False, None) @@ -267,6 +270,7 @@ U_BOOT_DRIVER(testing) = { self.assertEqual('some_cpriv', drv.child_priv) self.assertEqual('some_cplat', drv.child_plat) self.assertEqual('tpl', drv.phase) + self.assertEqual(['<i2c.h>', '<asm/clk.h>'], drv.headers) self.assertEqual(1, len(scan._drivers))
def test_uclass_scan(self):

Typically dtoc can detect the header file needed for a driver by looking for the structs that it uses. For example, if a driver as a .priv_auto that uses 'struct serial_priv', then dtoc can search header files for the definition of that struct and use the file.
In some cases, enums are used in drivers, typically with the .data field of struct udevice_id. Since dtoc does not support searching for these, add a way to tell dtoc which header to use. This works as a macro included in the driver definition.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
include/dm/device.h | 18 ++++++++++++++++++ tools/dtoc/src_scan.py | 7 +++++++ tools/dtoc/test_src_scan.py | 4 ++++ 3 files changed, 29 insertions(+)
Applied to u-boot-dm, thanks!

Instead of using a separate step for this processing, handle it while scanning its associated driver. This allows us to drop the code coverage exception in this case.
Note that only files containing drivers are scanned by dtoc, so aliases declared in a file that doesn't hold a driver will not be noticed. It would be confusing to put them anywhere other than in the driver that they relate to, but update the documentation to say this explicitly, just in case.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
doc/driver-model/of-plat.rst | 3 ++- tools/dtoc/src_scan.py | 16 +++++----------- tools/dtoc/test/dtoc_test_scan_drivers.cxx | 4 ++++ 3 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/doc/driver-model/of-plat.rst b/doc/driver-model/of-plat.rst index 4ef2fe699a4..a5a6e46e3ec 100644 --- a/doc/driver-model/of-plat.rst +++ b/doc/driver-model/of-plat.rst @@ -183,7 +183,8 @@ each 'compatible' string.
In order to make this a bit more flexible DM_DRIVER_ALIAS macro can be used to declare an alias for a driver name, typically a 'compatible' string. -This macro produces no code, but it is by dtoc tool. +This macro produces no code, but it is by dtoc tool. It must be located in the +same file as its associated driver, ideally just after it.
The parent_idx is the index of the parent driver_info structure within its linker list (instantiated by the U_BOOT_DRVINFO() macro). This is used to support diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 206b2b37583..9d161a2cbc7 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -387,6 +387,7 @@ class Scanner: in the file _of_match - updated with each compatible string found in the file _compat_to_driver - Maps compatible string to Driver + _driver_aliases - Maps alias names to driver name
Args: fname (str): Filename being parsed (used for warnings) @@ -438,6 +439,7 @@ class Scanner:
re_phase = re.compile('^\s*DM_PHASE((.*)).*$') re_hdr = re.compile('^\s*DM_HEADER((.*)).*$') + re_alias = re.compile(r'DM_DRIVER_ALIAS(\s*(\w+)\s*,\s*(\w+)\s*)')
# Matches the struct name for priv, plat re_priv = self._get_re_for_member('priv_auto') @@ -522,8 +524,11 @@ class Scanner: driver = Driver(driver_name, fname) else: ids_m = re_ids.search(line) + m_alias = re_alias.match(line) if ids_m: ids_name = ids_m.group(1) + elif m_alias: + self._driver_aliases[m_alias[2]] = m_alias[1]
# Make the updates based on what we found self._drivers.update(drivers) @@ -557,17 +562,6 @@ class Scanner: if 'UCLASS_DRIVER' in buff: self._parse_uclass_driver(fname, buff)
- # The following re will search for driver aliases declared as - # DM_DRIVER_ALIAS(alias, driver_name) - driver_aliases = re.findall( - r'DM_DRIVER_ALIAS(\s*(\w+)\s*,\s*(\w+)\s*)', - buff) - - for alias in driver_aliases: # pragma: no cover - if len(alias) != 2: - continue - self._driver_aliases[alias[1]] = alias[0] - def scan_header(self, fname): """Scan a header file to build a list of struct definitions
diff --git a/tools/dtoc/test/dtoc_test_scan_drivers.cxx b/tools/dtoc/test/dtoc_test_scan_drivers.cxx index f448767670e..f370b8951d0 100644 --- a/tools/dtoc/test/dtoc_test_scan_drivers.cxx +++ b/tools/dtoc/test/dtoc_test_scan_drivers.cxx @@ -1 +1,5 @@ +/* Aliases must be in driver files */ +U_BOOT_DRIVER(sandbox_gpio) { +}; + DM_DRIVER_ALIAS(sandbox_gpio, sandbox_gpio_alias2)

Instead of using a separate step for this processing, handle it while scanning its associated driver. This allows us to drop the code coverage exception in this case.
Note that only files containing drivers are scanned by dtoc, so aliases declared in a file that doesn't hold a driver will not be noticed. It would be confusing to put them anywhere other than in the driver that they relate to, but update the documentation to say this explicitly, just in case.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
doc/driver-model/of-plat.rst | 3 ++- tools/dtoc/src_scan.py | 16 +++++----------- tools/dtoc/test/dtoc_test_scan_drivers.cxx | 4 ++++ 3 files changed, 11 insertions(+), 12 deletions(-)
Applied to u-boot-dm, thanks!

Hi Simon, Heiko,
With introduction of rockchip-nand-controller.c in Linux and recently rockchip_nfc.c in U-boot it offers a renewed opportunity to add support for MK808 with a rk3066 + NAND to boot from. rk3066 shares /arch/arm/dts/rk3xxx.dtsi with rk3188. The only 'usable' boot loader now is closed source with proprietary FTL.
As a spinoff to that discussion I've tried to compile for rk3188-radxarock as an example due to the lack of rk3066 support in next and enabling the new rockchip_nfc.c driver. I'm not familiar with Python/U-boot. Do you have an idea what this error below is about?
Kind regards,
Johan ===
python --version Python 2.7.13
===
From rock_defconfig:
CONFIG_SPL_OF_PLATDATA=y
===
git clone -b next https://source.denx.de/u-boot/custodians/u-boot-rockchip.git u-boot-next-20210619
make rock_defconfig
make CROSS_COMPILE=/usr/bin/arm-linux-gnueabi- menuconfig
[ ] Support for NAND controller on Rockchip SoCs (NEW)
Add to rock.h: #define CONFIG_SYS_MAX_NAND_DEVICE 1
make CROSS_COMPILE=/usr/bin/arm-linux-gnueabi- all
=== mkdir -p spl/dts/ FDTGREP spl/dts/dt-spl.dtb COPY spl/u-boot-spl.dtb CC spl/./lib/asm-offsets.s CC spl/./arch/arm/lib/asm-offsets.s DTOC spl/dts/dt-plat.c Traceback (most recent call last): File "./tools/dtoc/dtoc", line 112, in <module> options.phase, instantiate=options.instantiate) File "~/Downloads/u-boot-next-20210619/tools/dtoc/../dtoc/dtb_platdata.py", line 1180, in run_steps scan.scan_drivers() File "~/Downloads/u-boot-next-20210619/tools/dtoc/../dtoc/src_scan.py", line 649, in scan_drivers self.scan_driver(pathname) File "~/Downloads/u-boot-next-20210619/tools/dtoc/../dtoc/src_scan.py", line 608, in scan_driver self._parse_driver(fname, buff) File "~/Downloads/u-boot-next-20210619/tools/dtoc/../dtoc/src_scan.py", line 558, in _parse_driver self._driver_aliases[m_alias[2]] = m_alias[1] TypeError: '_sre.SRE_Match' object is not subscriptable scripts/Makefile.spl:352: recipe for target 'spl/dts/dt-plat.c' failed make[1]: *** [spl/dts/dt-plat.c] Error 1 Makefile:1977: recipe for target 'spl/u-boot-spl' failed make: *** [spl/u-boot-spl] Error 2
===
Changed: self._driver_aliases[m_alias[2]] = m_alias[1]
To: self._driver_aliases[m_alias.group(2)] = m_alias.group(1)
Is that a correct python change of mine?
===
After that it compiles normal with only this warning.
DTOC spl/dts/dt-plat.c WARNING: the driver rockchip_rk3188_grf was not found in the driver list WARNING: the driver rockchip_rk3188_pmu was not found in the driver list WARNING: the driver rockchip_rk3188_uart was not found in the driver list DTOC include/generated/dt-structs-gen.h WARNING: the driver rockchip_rk3188_grf was not found in the driver list WARNING: the driver rockchip_rk3188_pmu was not found in the driver list WARNING: the driver rockchip_rk3188_uart was not found in the driver list DTOC include/generated/dt-decl.h WARNING: the driver rockchip_rk3188_grf was not found in the driver list WARNING: the driver rockchip_rk3188_pmu was not found in the driver list WARNING: the driver rockchip_rk3188_uart was not found in the driver list
On 2/3/21 2:01 PM, Simon Glass wrote:
Instead of using a separate step for this processing, handle it while scanning its associated driver. This allows us to drop the code coverage exception in this case.
Note that only files containing drivers are scanned by dtoc, so aliases declared in a file that doesn't hold a driver will not be noticed. It would be confusing to put them anywhere other than in the driver that they relate to, but update the documentation to say this explicitly, just in case.
Signed-off-by: Simon Glass sjg@chromium.org Signed-off-by: Simon Glass sjg@chromium.org
(no changes since v1)
doc/driver-model/of-plat.rst | 3 ++- tools/dtoc/src_scan.py | 16 +++++----------- tools/dtoc/test/dtoc_test_scan_drivers.cxx | 4 ++++ 3 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/doc/driver-model/of-plat.rst b/doc/driver-model/of-plat.rst index 4ef2fe699a4..a5a6e46e3ec 100644 --- a/doc/driver-model/of-plat.rst +++ b/doc/driver-model/of-plat.rst @@ -183,7 +183,8 @@ each 'compatible' string.
In order to make this a bit more flexible DM_DRIVER_ALIAS macro can be used to declare an alias for a driver name, typically a 'compatible' string. -This macro produces no code, but it is by dtoc tool. +This macro produces no code, but it is by dtoc tool. It must be located in the +same file as its associated driver, ideally just after it.
The parent_idx is the index of the parent driver_info structure within its linker list (instantiated by the U_BOOT_DRVINFO() macro). This is used to support diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 206b2b37583..9d161a2cbc7 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -387,6 +387,7 @@ class Scanner: in the file _of_match - updated with each compatible string found in the file _compat_to_driver - Maps compatible string to Driver
_driver_aliases - Maps alias names to driver name Args: fname (str): Filename being parsed (used for warnings)
@@ -438,6 +439,7 @@ class Scanner:
re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$') re_hdr = re.compile('^\s*DM_HEADER\((.*)\).*$')
re_alias = re.compile(r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)') # Matches the struct name for priv, plat re_priv = self._get_re_for_member('priv_auto')
@@ -522,8 +524,11 @@ class Scanner: driver = Driver(driver_name, fname) else: ids_m = re_ids.search(line)
m_alias = re_alias.match(line) if ids_m: ids_name = ids_m.group(1)
elif m_alias:
self._driver_aliases[m_alias[2]] = m_alias[1] # Make the updates based on what we found self._drivers.update(drivers)
@@ -557,17 +562,6 @@ class Scanner: if 'UCLASS_DRIVER' in buff: self._parse_uclass_driver(fname, buff)
# The following re will search for driver aliases declared as
# DM_DRIVER_ALIAS(alias, driver_name)
driver_aliases = re.findall(
r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
buff)
for alias in driver_aliases: # pragma: no cover
if len(alias) != 2:
continue
self._driver_aliases[alias[1]] = alias[0]
- def scan_header(self, fname): """Scan a header file to build a list of struct definitions
diff --git a/tools/dtoc/test/dtoc_test_scan_drivers.cxx b/tools/dtoc/test/dtoc_test_scan_drivers.cxx index f448767670e..f370b8951d0 100644 --- a/tools/dtoc/test/dtoc_test_scan_drivers.cxx +++ b/tools/dtoc/test/dtoc_test_scan_drivers.cxx @@ -1 +1,5 @@ +/* Aliases must be in driver files */ +U_BOOT_DRIVER(sandbox_gpio) { +};
DM_DRIVER_ALIAS(sandbox_gpio, sandbox_gpio_alias2)

Hi Johan,
On Mon, 21 Jun 2021 at 12:39, Johan Jonker jbx6244@gmail.com wrote:
Hi Simon, Heiko,
With introduction of rockchip-nand-controller.c in Linux and recently rockchip_nfc.c in U-boot it offers a renewed opportunity to add support for MK808 with a rk3066 + NAND to boot from. rk3066 shares /arch/arm/dts/rk3xxx.dtsi with rk3188. The only 'usable' boot loader now is closed source with proprietary FTL.
As a spinoff to that discussion I've tried to compile for rk3188-radxarock as an example due to the lack of rk3066 support in next and enabling the new rockchip_nfc.c driver. I'm not familiar with Python/U-boot. Do you have an idea what this error below is about?
Kind regards,
Johan
python --version Python 2.7.13
===
From rock_defconfig: CONFIG_SPL_OF_PLATDATA=y
===
git clone -b next https://source.denx.de/u-boot/custodians/u-boot-rockchip.git u-boot-next-20210619
make rock_defconfig
make CROSS_COMPILE=/usr/bin/arm-linux-gnueabi- menuconfig
[ ] Support for NAND controller on Rockchip SoCs (NEW)
Add to rock.h: #define CONFIG_SYS_MAX_NAND_DEVICE 1
make CROSS_COMPILE=/usr/bin/arm-linux-gnueabi- all
=== mkdir -p spl/dts/ FDTGREP spl/dts/dt-spl.dtb COPY spl/u-boot-spl.dtb CC spl/./lib/asm-offsets.s CC spl/./arch/arm/lib/asm-offsets.s DTOC spl/dts/dt-plat.c Traceback (most recent call last): File "./tools/dtoc/dtoc", line 112, in <module> options.phase, instantiate=options.instantiate) File "~/Downloads/u-boot-next-20210619/tools/dtoc/../dtoc/dtb_platdata.py", line 1180, in run_steps scan.scan_drivers() File "~/Downloads/u-boot-next-20210619/tools/dtoc/../dtoc/src_scan.py", line 649, in scan_drivers self.scan_driver(pathname) File "~/Downloads/u-boot-next-20210619/tools/dtoc/../dtoc/src_scan.py", line 608, in scan_driver self._parse_driver(fname, buff) File "~/Downloads/u-boot-next-20210619/tools/dtoc/../dtoc/src_scan.py", line 558, in _parse_driver self._driver_aliases[m_alias[2]] = m_alias[1] TypeError: '_sre.SRE_Match' object is not subscriptable scripts/Makefile.spl:352: recipe for target 'spl/dts/dt-plat.c' failed make[1]: *** [spl/dts/dt-plat.c] Error 1 Makefile:1977: recipe for target 'spl/u-boot-spl' failed make: *** [spl/u-boot-spl] Error 2
===
Changed: self._driver_aliases[m_alias[2]] = m_alias[1]
To: self._driver_aliases[m_alias.group(2)] = m_alias.group(1)
Is that a correct python change of mine?
I think so, yes. I will take a look.
===
After that it compiles normal with only this warning.
DTOC spl/dts/dt-plat.c WARNING: the driver rockchip_rk3188_grf was not found in the driver list WARNING: the driver rockchip_rk3188_pmu was not found in the driver list WARNING: the driver rockchip_rk3188_uart was not found in the driver list DTOC include/generated/dt-structs-gen.h WARNING: the driver rockchip_rk3188_grf was not found in the driver list WARNING: the driver rockchip_rk3188_pmu was not found in the driver list WARNING: the driver rockchip_rk3188_uart was not found in the driver list DTOC include/generated/dt-decl.h WARNING: the driver rockchip_rk3188_grf was not found in the driver list WARNING: the driver rockchip_rk3188_pmu was not found in the driver list WARNING: the driver rockchip_rk3188_uart was not found in the driver list
OK that means you have the driver missing. It is not fatal unless OF_PLATDATA_INST is enabled.
[..]
Regards, Simon

Hi Johan,
On Sat, 26 Jun 2021 at 12:32, Simon Glass sjg@chromium.org wrote:
Hi Johan,
On Mon, 21 Jun 2021 at 12:39, Johan Jonker jbx6244@gmail.com wrote:
Hi Simon, Heiko,
With introduction of rockchip-nand-controller.c in Linux and recently rockchip_nfc.c in U-boot it offers a renewed opportunity to add support for MK808 with a rk3066 + NAND to boot from. rk3066 shares /arch/arm/dts/rk3xxx.dtsi with rk3188. The only 'usable' boot loader now is closed source with proprietary FTL.
As a spinoff to that discussion I've tried to compile for rk3188-radxarock as an example due to the lack of rk3066 support in next and enabling the new rockchip_nfc.c driver. I'm not familiar with Python/U-boot. Do you have an idea what this error below is about?
Kind regards,
Johan
python --version Python 2.7.13
===
From rock_defconfig: CONFIG_SPL_OF_PLATDATA=y
===
git clone -b next https://source.denx.de/u-boot/custodians/u-boot-rockchip.git u-boot-next-20210619
make rock_defconfig
make CROSS_COMPILE=/usr/bin/arm-linux-gnueabi- menuconfig
[ ] Support for NAND controller on Rockchip SoCs (NEW)
Add to rock.h: #define CONFIG_SYS_MAX_NAND_DEVICE 1
make CROSS_COMPILE=/usr/bin/arm-linux-gnueabi- all
=== mkdir -p spl/dts/ FDTGREP spl/dts/dt-spl.dtb COPY spl/u-boot-spl.dtb CC spl/./lib/asm-offsets.s CC spl/./arch/arm/lib/asm-offsets.s DTOC spl/dts/dt-plat.c Traceback (most recent call last): File "./tools/dtoc/dtoc", line 112, in <module> options.phase, instantiate=options.instantiate) File "~/Downloads/u-boot-next-20210619/tools/dtoc/../dtoc/dtb_platdata.py", line 1180, in run_steps scan.scan_drivers() File "~/Downloads/u-boot-next-20210619/tools/dtoc/../dtoc/src_scan.py", line 649, in scan_drivers self.scan_driver(pathname) File "~/Downloads/u-boot-next-20210619/tools/dtoc/../dtoc/src_scan.py", line 608, in scan_driver self._parse_driver(fname, buff) File "~/Downloads/u-boot-next-20210619/tools/dtoc/../dtoc/src_scan.py", line 558, in _parse_driver self._driver_aliases[m_alias[2]] = m_alias[1] TypeError: '_sre.SRE_Match' object is not subscriptable scripts/Makefile.spl:352: recipe for target 'spl/dts/dt-plat.c' failed make[1]: *** [spl/dts/dt-plat.c] Error 1 Makefile:1977: recipe for target 'spl/u-boot-spl' failed make: *** [spl/u-boot-spl] Error 2
===
Changed: self._driver_aliases[m_alias[2]] = m_alias[1]
To: self._driver_aliases[m_alias.group(2)] = m_alias.group(1)
Is that a correct python change of mine?
I think so, yes. I will take a look.
This was introduced in Python 3.6 so I suspect your Python is quite old. I will send a patch.
===
After that it compiles normal with only this warning.
DTOC spl/dts/dt-plat.c WARNING: the driver rockchip_rk3188_grf was not found in the driver list WARNING: the driver rockchip_rk3188_pmu was not found in the driver list WARNING: the driver rockchip_rk3188_uart was not found in the driver list DTOC include/generated/dt-structs-gen.h WARNING: the driver rockchip_rk3188_grf was not found in the driver list WARNING: the driver rockchip_rk3188_pmu was not found in the driver list WARNING: the driver rockchip_rk3188_uart was not found in the driver list DTOC include/generated/dt-decl.h WARNING: the driver rockchip_rk3188_grf was not found in the driver list WARNING: the driver rockchip_rk3188_pmu was not found in the driver list WARNING: the driver rockchip_rk3188_uart was not found in the driver list
OK that means you have the driver missing. It is not fatal unless OF_PLATDATA_INST is enabled.
I'll send a series to improve the warnings here. The problem is in the strange use of the .compatible memory in the rockchip driver, so I've updated dtoc to handle it but show a warning.
Regards, Simon

If drivers have the same name then we cannot distinguish them. This only matters if the driver is actually used by dtoc, but in that case, issue a warning.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/src_scan.py | 28 ++++++++++- tools/dtoc/test_src_scan.py | 95 +++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-)
diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 9d161a2cbc7..fb78536e003 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -70,6 +70,10 @@ class Driver: phase (str): Which phase of U-Boot to use this driver headers (list): List of header files needed for this driver (each a str) e.g. ['<asm/cpu.h>'] + dups (list): Driver objects with the same name as this one, that were + found after this one + warn_dups (bool): True if the duplicates are not distinguisble using + the phase """ def __init__(self, name, fname): self.name = name @@ -83,6 +87,8 @@ class Driver: self.used = False self.phase = '' self.headers = [] + self.dups = [] + self.warn_dups = False
def __eq__(self, other): return (self.name == other.name and @@ -531,7 +537,21 @@ class Scanner: self._driver_aliases[m_alias[2]] = m_alias[1]
# Make the updates based on what we found - self._drivers.update(drivers) + for driver in drivers.values(): + if driver.name in self._drivers: + orig = self._drivers[driver.name] + if self._phase: + # If the original driver matches our phase, use it + if orig.phase == self._phase: + orig.dups.append(driver) + continue + + # Otherwise use the new driver, which is assumed to match + else: + # We have no way of distinguishing them + driver.warn_dups = True + driver.dups.append(orig) + self._drivers[driver.name] = driver self._of_match.update(of_match)
def scan_driver(self, fname): @@ -617,6 +637,8 @@ class Scanner: This takes a list of nodes, finds the driver for each one and marks it as used.
+ If two used drivers have the same name, issue a warning. + Args: nodes (list of None): Nodes that are in use """ @@ -626,3 +648,7 @@ class Scanner: driver = self._drivers.get(struct_name) if driver: driver.used = True + if driver.dups and driver.warn_dups: + print("Warning: Duplicate driver name '%s' (orig=%s, dups=%s)" % + (driver.name, driver.fname, + ', '.join([drv.fname for drv in driver.dups]))) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 245b7302fd6..598ff256a60 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -371,3 +371,98 @@ struct another_struct { with test_util.capture_sys_output() as (stdout, _): scan.scan_header(output) self.assertIn('due to unicode error', stdout.getvalue()) + + def setup_dup_drivers(self, name, phase=''): + """Set up for a duplcate test + + Returns: + tuple: + Scanner to use + Driver record for first driver + Text of second driver declaration + Node for driver 1 + """ + driver1 = ''' +static const struct udevice_id test_ids[] = { + { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, + { } +}; + +U_BOOT_DRIVER(%s) = { + .name = "testing", + .id = UCLASS_I2C, + .of_match = test_ids, + %s +}; +''' % (name, 'DM_PHASE(%s)' % phase if phase else '') + driver2 = ''' +static const struct udevice_id test_ids[] = { + { .compatible = "nvidia,tegra114-dvc" }, + { } +}; + +U_BOOT_DRIVER(%s) = { + .name = "testing", + .id = UCLASS_RAM, + .of_match = test_ids, +}; +''' % name + scan = src_scan.Scanner(None, False, None, phase) + scan._parse_driver('file1.c', driver1) + self.assertIn(name, scan._drivers) + drv1 = scan._drivers[name] + + prop = FakeProp() + prop.name = 'compatible' + prop.value = 'nvidia,tegra114-i2c' + node = FakeNode() + node.name = 'testing' + node.props = {'compatible': prop} + + return scan, drv1, driver2, node + + def test_dup_drivers(self): + """Test handling of duplicate drivers""" + name = 'nvidia_tegra114_i2c' + scan, drv1, driver2, node = self.setup_dup_drivers(name) + self.assertEqual('', drv1.phase) + + # The driver should not have a duplicate yet + self.assertEqual([], drv1.dups) + + scan._parse_driver('file2.c', driver2) + + # The first driver should now be a duplicate of the second + drv2 = scan._drivers[name] + self.assertEqual('', drv2.phase) + self.assertEqual(1, len(drv2.dups)) + self.assertEqual([drv1], drv2.dups) + + # There is no way to distinguish them, so we should expect a warning + self.assertTrue(drv2.warn_dups) + + # We should see a warning + with test_util.capture_sys_output() as (stdout, _): + scan.mark_used([node]) + self.assertEqual( + "Warning: Duplicate driver name 'nvidia_tegra114_i2c' (orig=file2.c, dups=file1.c)", + stdout.getvalue().strip()) + + def test_dup_drivers_phase(self): + """Test handling of duplicate drivers but with different phases""" + name = 'nvidia_tegra114_i2c' + scan, drv1, driver2, node = self.setup_dup_drivers(name, 'spl') + scan._parse_driver('file2.c', driver2) + self.assertEqual('spl', drv1.phase) + + # The second driver should now be a duplicate of the second + self.assertEqual(1, len(drv1.dups)) + drv2 = drv1.dups[0] + + # The phase is different, so we should not warn of dups + self.assertFalse(drv1.warn_dups) + + # We should not see a warning + with test_util.capture_sys_output() as (stdout, _): + scan.mark_used([node]) + self.assertEqual('', stdout.getvalue().strip())

If drivers have the same name then we cannot distinguish them. This only matters if the driver is actually used by dtoc, but in that case, issue a warning.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/src_scan.py | 28 ++++++++++- tools/dtoc/test_src_scan.py | 95 +++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-)
Applied to u-boot-dm, thanks!

Scan the aliases in the device tree to establish the number of devices within each uclass, and the sequence number of each.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 28 ++++++++++ tools/dtoc/src_scan.py | 32 ++++++++++- tools/dtoc/test/dtoc_test_alias_bad.dts | 58 ++++++++++++++++++++ tools/dtoc/test/dtoc_test_alias_bad_path.dts | 58 ++++++++++++++++++++ tools/dtoc/test/dtoc_test_alias_bad_uc.dts | 58 ++++++++++++++++++++ tools/dtoc/test/dtoc_test_inst.dts | 58 ++++++++++++++++++++ tools/dtoc/test_dtoc.py | 56 ++++++++++++++++++- 7 files changed, 345 insertions(+), 3 deletions(-) create mode 100644 tools/dtoc/test/dtoc_test_alias_bad.dts create mode 100644 tools/dtoc/test/dtoc_test_alias_bad_path.dts create mode 100644 tools/dtoc/test/dtoc_test_alias_bad_uc.dts create mode 100644 tools/dtoc/test/dtoc_test_inst.dts
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index ef0454c8904..f6dcf47d490 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -647,6 +647,29 @@ class DtbPlatdata(): self._output_prop(node, node.props[pname]) self.buf('};\n')
+ def read_aliases(self): + """Read the aliases and attach the information to self._alias + + Raises: + ValueError: The alias path is not found + """ + alias_node = self._fdt.GetNode('/aliases') + if not alias_node: + return + re_num = re.compile('(^[a-z0-9-]+[a-z]+)([0-9]+)$') + for prop in alias_node.props.values(): + m_alias = re_num.match(prop.name) + if not m_alias: + raise ValueError("Cannot decode alias '%s'" % prop.name) + name, num = m_alias.groups() + node = self._fdt.GetNode(prop.value) + result = self._scan.add_uclass_alias(name, num, node) + if result is None: + raise ValueError("Alias '%s' path '%s' not found" % + (prop.name, prop.value)) + elif result is False: + print("Could not find uclass for alias '%s'" % prop.name) + def process_nodes(self, need_drivers): nodes_to_output = list(self._valid_nodes)
@@ -757,6 +780,9 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, scan (src_src.Scanner): Scanner from a previous run. This can help speed up tests. Use None for normal operation
+ Returns: + DtbPlatdata object + Raises: ValueError: if args has no command, or an unknown command """ @@ -782,6 +808,7 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, plat.scan_phandles() if do_process: plat.process_nodes(False) + plat.read_aliases()
cmds = args[0].split(',') if 'all' in cmds: @@ -796,3 +823,4 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, plat.out_header(outfile) outfile.method(plat) plat.finish_output() + return plat diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index fb78536e003..a2750321791 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -116,6 +116,13 @@ class UclassDriver: e.g. 'pci_child_priv' per_child_plat (str): struct name of the per_child_plat_auto member, e.g. 'pci_child_plat' + alias_num_to_node (dict): Aliases for this uclasses (for sequence + numbers) + key (int): Alias number, e.g. 2 for "pci2" + value (str): Node the alias points to + alias_path_to_num (dict): Convert a path to an alias number + key (str): Full path to node (e.g. '/soc/pci') + seq (int): Alias number, e.g. 2 for "pci2" """ def __init__(self, name): self.name = name @@ -125,6 +132,8 @@ class UclassDriver: self.per_dev_plat = '' self.per_child_priv = '' self.per_child_plat = '' + self.alias_num_to_node = {} + self.alias_path_to_num = {}
def __eq__(self, other): return (self.name == other.name and @@ -622,7 +631,6 @@ class Scanner: self.scan_driver(pathname) elif fname.endswith('.h'): self.scan_header(pathname) - for fname in self._drivers_additional: if not isinstance(fname, str) or len(fname) == 0: continue @@ -652,3 +660,25 @@ class Scanner: print("Warning: Duplicate driver name '%s' (orig=%s, dups=%s)" % (driver.name, driver.fname, ', '.join([drv.fname for drv in driver.dups]))) + + def add_uclass_alias(self, name, num, node): + """Add an alias to a uclass + + Args: + name: Name of uclass, e.g. 'i2c' + num: Alias number, e.g. 2 for alias 'i2c2' + node: Node the alias points to, or None if None + + Returns: + True if the node was added + False if the node was not added (uclass of that name not found) + None if the node could not be added because it was None + """ + for uclass in self._uclass.values(): + if uclass.name == name: + if node is None: + return None + uclass.alias_num_to_node[int(num)] = node + uclass.alias_path_to_num[node.path] = int(num) + return True + return False diff --git a/tools/dtoc/test/dtoc_test_alias_bad.dts b/tools/dtoc/test/dtoc_test_alias_bad.dts new file mode 100644 index 00000000000..d4f502ad0aa --- /dev/null +++ b/tools/dtoc/test/dtoc_test_alias_bad.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + aliases { + testbus2 = &bus2; + testfdt1 = &testfdt_1; + i2c4- = &i2c; + }; + + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + }; + + i2c: i2c { + u-boot,dm-pre-reloc; + compatible = "sandbox,i2c"; + intval = <3>; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; + }; + + bus2: some-bus { + #address-cells = <1>; + #size-cells = <0>; + compatible = "denx,u-boot-test-bus"; + reg = <3 1>; + ping-expect = <4>; + ping-add = <4>; + testfdt_1: test { + compatible = "denx,u-boot-fdt-test", "google,another-fdt-test"; + reg = <5>; + ping-expect = <5>; + ping-add = <5>; + }; + + test0 { + compatible = "google,another-fdt-test"; + }; + }; +}; diff --git a/tools/dtoc/test/dtoc_test_alias_bad_path.dts b/tools/dtoc/test/dtoc_test_alias_bad_path.dts new file mode 100644 index 00000000000..0beca4f0d03 --- /dev/null +++ b/tools/dtoc/test/dtoc_test_alias_bad_path.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + aliases { + testbus2 = &bus2; + testfdt1 = &testfdt_1; + i2c4 = "/does/not/exist"; + }; + + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + }; + + i2c: i2c { + u-boot,dm-pre-reloc; + compatible = "sandbox,i2c"; + intval = <3>; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; + }; + + bus2: some-bus { + #address-cells = <1>; + #size-cells = <0>; + compatible = "denx,u-boot-test-bus"; + reg = <3 1>; + ping-expect = <4>; + ping-add = <4>; + testfdt_1: test { + compatible = "denx,u-boot-fdt-test", "google,another-fdt-test"; + reg = <5>; + ping-expect = <5>; + ping-add = <5>; + }; + + test0 { + compatible = "google,another-fdt-test"; + }; + }; +}; diff --git a/tools/dtoc/test/dtoc_test_alias_bad_uc.dts b/tools/dtoc/test/dtoc_test_alias_bad_uc.dts new file mode 100644 index 00000000000..ae64f5b3b29 --- /dev/null +++ b/tools/dtoc/test/dtoc_test_alias_bad_uc.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + aliases { + testbus2 = &bus2; + testfdt1 = &testfdt_1; + other1 = &testfdt_1; + }; + + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + }; + + i2c: i2c { + u-boot,dm-pre-reloc; + compatible = "sandbox,i2c"; + intval = <3>; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; + }; + + bus2: some-bus { + #address-cells = <1>; + #size-cells = <0>; + compatible = "denx,u-boot-test-bus"; + reg = <3 1>; + ping-expect = <4>; + ping-add = <4>; + testfdt_1: test { + compatible = "denx,u-boot-fdt-test", "google,another-fdt-test"; + reg = <5>; + ping-expect = <5>; + ping-add = <5>; + }; + + test0 { + compatible = "google,another-fdt-test"; + }; + }; +}; diff --git a/tools/dtoc/test/dtoc_test_inst.dts b/tools/dtoc/test/dtoc_test_inst.dts new file mode 100644 index 00000000000..b8177fcef5f --- /dev/null +++ b/tools/dtoc/test/dtoc_test_inst.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + aliases { + testbus2 = &bus2; + testfdt1 = &testfdt_1; + i2c4 = &i2c; + }; + + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + }; + + i2c: i2c { + u-boot,dm-pre-reloc; + compatible = "sandbox,i2c"; + intval = <3>; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; + }; + + bus2: some-bus { + #address-cells = <1>; + #size-cells = <0>; + compatible = "denx,u-boot-test-bus"; + reg = <3 1>; + ping-expect = <4>; + ping-add = <4>; + testfdt_1: test { + compatible = "denx,u-boot-fdt-test", "google,another-fdt-test"; + reg = <5>; + ping-expect = <5>; + ping-add = <5>; + }; + + test0 { + compatible = "google,another-fdt-test"; + }; + }; +}; diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index c1fafb656fb..706cc39b3d5 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -137,9 +137,12 @@ class TestDtoc(unittest.TestCase): args (list of str): List of arguments for dtoc dtb_file (str): Filename of .dtb file output (str): Filename of output file + + Returns: + DtbPlatdata object """ - dtb_platdata.run_steps(args, dtb_file, False, output, [], None, - warning_disabled=True, scan=copy_scan()) + return dtb_platdata.run_steps(args, dtb_file, False, output, [], None, + warning_disabled=True, scan=copy_scan())
def test_name(self): """Test conversion of device tree names to C identifiers""" @@ -1040,3 +1043,52 @@ U_BOOT_DRVINFO(spl_test2) = {
gpio = scan._drivers['sandbox_gpio'] self.assertFalse(gpio.used) + + def test_alias_read(self): + """Test obtaining aliases""" + dtb_file = get_dtb_file('dtoc_test_inst.dts') + output = tools.GetOutputFilename('output') + plat = self.run_test(['struct'], dtb_file, output) + + scan = plat._scan + testfdt_node = plat._fdt.GetNode('/some-bus/test') + self.assertIn('UCLASS_TEST_FDT', scan._uclass) + uc = scan._uclass['UCLASS_TEST_FDT'] + self.assertEqual({1: testfdt_node}, uc.alias_num_to_node) + self.assertEqual({'/some-bus/test': 1}, uc.alias_path_to_num) + + # Try adding an alias that doesn't exist + self.assertFalse(scan.add_uclass_alias('fred', 3, None)) + + # Try adding an alias for a missing node + self.assertIsNone(scan.add_uclass_alias('testfdt', 3, None)) + + def test_alias_read_bad(self): + """Test invalid alias property name""" + dtb_file = get_dtb_file('dtoc_test_alias_bad.dts') + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as exc: + plat = self.run_test(['struct'], dtb_file, output) + self.assertIn("Cannot decode alias 'i2c4-'", str(exc.exception)) + + def test_alias_read_bad_path(self): + """Test alias pointing to a non-existent node""" + # This line may produce a warning, so capture it: + # Warning (alias_paths): /aliases:i2c4: aliases property is not a valid + # node (/does/not/exist) + dtb_file = get_dtb_file('dtoc_test_alias_bad_path.dts', True) + + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as exc: + plat = self.run_test(['struct'], dtb_file, output) + self.assertIn("Alias 'i2c4' path '/does/not/exist' not found", + str(exc.exception)) + + def test_alias_read_bad_uclass(self): + """Test alias for a uclass that doesn't exist""" + dtb_file = get_dtb_file('dtoc_test_alias_bad_uc.dts') + output = tools.GetOutputFilename('output') + with test_util.capture_sys_output() as (stdout, _): + plat = self.run_test(['struct'], dtb_file, output) + self.assertEqual("Could not find uclass for alias 'other1'", + stdout.getvalue().strip())

Scan the aliases in the device tree to establish the number of devices within each uclass, and the sequence number of each.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 28 ++++++++++ tools/dtoc/src_scan.py | 32 ++++++++++- tools/dtoc/test/dtoc_test_alias_bad.dts | 58 ++++++++++++++++++++ tools/dtoc/test/dtoc_test_alias_bad_path.dts | 58 ++++++++++++++++++++ tools/dtoc/test/dtoc_test_alias_bad_uc.dts | 58 ++++++++++++++++++++ tools/dtoc/test/dtoc_test_inst.dts | 58 ++++++++++++++++++++ tools/dtoc/test_dtoc.py | 56 ++++++++++++++++++- 7 files changed, 345 insertions(+), 3 deletions(-) create mode 100644 tools/dtoc/test/dtoc_test_alias_bad.dts create mode 100644 tools/dtoc/test/dtoc_test_alias_bad_path.dts create mode 100644 tools/dtoc/test/dtoc_test_alias_bad_uc.dts create mode 100644 tools/dtoc/test/dtoc_test_inst.dts
Applied to u-boot-dm, thanks!

If a driver declaration is included in a comment, dtoc currently gets confused. Update the parser to only consider declarations that begin at the start of a line. Since multi-line comments begin with an asterisk, this avoids the problem.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: - New patch
tools/dtoc/src_scan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index a2750321791..bb22b0b64ff 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -331,7 +331,7 @@ class Scanner:
# Collect the driver name and associated Driver driver = None - re_driver = re.compile(r'UCLASS_DRIVER((.*))') + re_driver = re.compile(r'^UCLASS_DRIVER((.*))')
# Collect the uclass ID, e.g. 'UCLASS_SPI' re_id = re.compile(r'\s*.id\s*=\s*(UCLASS_[A-Z0-9_]+)') @@ -427,7 +427,7 @@ class Scanner:
# Collect the driver info driver = None - re_driver = re.compile(r'U_BOOT_DRIVER((.*))') + re_driver = re.compile(r'^U_BOOT_DRIVER((.*))')
# Collect the uclass ID, e.g. 'UCLASS_SPI' re_id = re.compile(r'\s*.id\s*=\s*(UCLASS_[A-Z0-9_]+)')

If a driver declaration is included in a comment, dtoc currently gets confused. Update the parser to only consider declarations that begin at the start of a line. Since multi-line comments begin with an asterisk, this avoids the problem.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: - New patch
tools/dtoc/src_scan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
Applied to u-boot-dm, thanks!

Now that we have the alias information we can assign a sequence number to each device in the uclass. Store this in the node associated with each device.
This requires renaming the sandbox test drivers to have the right name. Note that test coverage is broken with this patch, but fixed in the next one.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
drivers/misc/test_drv.c | 6 +++-- test/dm/test-fdt.c | 6 ++--- tools/dtoc/dtb_platdata.py | 55 +++++++++++++++++++++++++++----------- tools/dtoc/test_dtoc.py | 6 +++++ 4 files changed, 53 insertions(+), 20 deletions(-)
diff --git a/drivers/misc/test_drv.c b/drivers/misc/test_drv.c index 7dd3de34c99..ac762fd9fea 100644 --- a/drivers/misc/test_drv.c +++ b/drivers/misc/test_drv.c @@ -85,7 +85,7 @@ static const struct udevice_id testbus_ids[] = { { } };
-U_BOOT_DRIVER(testbus_drv) = { +U_BOOT_DRIVER(denx_u_boot_test_bus) = { .name = "testbus_drv", .of_match = testbus_ids, .id = UCLASS_TEST_BUS, @@ -159,7 +159,9 @@ static const struct udevice_id testfdt_ids[] = { { } };
-U_BOOT_DRIVER(testfdt_drv) = { +DM_DRIVER_ALIAS(denx_u_boot_fdt_test, google_another_fdt_test) + +U_BOOT_DRIVER(denx_u_boot_fdt_test) = { .name = "testfdt_drv", .of_match = testfdt_ids, .id = UCLASS_TEST_FDT, diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index b5ac9bba24e..64f7fb0ccb2 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -329,7 +329,7 @@ static int dm_test_fdt_uclass_seq_more(struct unit_test_state *uts)
/* Check creating a device with an alias */ node = ofnode_path("/some-bus/c-test@1"); - ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(testfdt_drv), + ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test), "c-test@1", NULL, node, &dev)); ut_asserteq(12, dev_seq(dev)); ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 12, &dev)); @@ -349,11 +349,11 @@ static int dm_test_fdt_uclass_seq_more(struct unit_test_state *uts) * * So next available is 19 */ - ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(testfdt_drv), + ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test), "fred", NULL, ofnode_null(), &dev)); ut_asserteq(19, dev_seq(dev));
- ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(testfdt_drv), + ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test), "fred2", NULL, ofnode_null(), &dev)); ut_asserteq(20, dev_seq(dev));
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index f6dcf47d490..9e99c63ae70 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -136,8 +136,10 @@ class DtbPlatdata(): from the U-Boot source code _fdt: Fdt object, referencing the device tree _dtb_fname: Filename of the input device tree binary file - _valid_nodes: A list of Node object with compatible strings. The list - is ordered by conv_name_to_c(node.name) + _valid_nodes_unsorted: A list of Node object with compatible strings, + ordered by devicetree node order + _valid_nodes: A list of Node object with compatible strings, ordered by + conv_name_to_c(node.name) _include_disabled: true to include nodes marked status = "disabled" _outfile: The current output file (sys.stdout or a real file) _lines: Stashed list of output lines for outputting in the future @@ -155,6 +157,7 @@ class DtbPlatdata(): self._fdt = None self._dtb_fname = dtb_fname self._valid_nodes = None + self._valid_nodes_unsorted = None self._include_disabled = include_disabled self._outfile = None self._lines = [] @@ -324,34 +327,38 @@ class DtbPlatdata(): """ self._fdt = fdt.FdtScan(self._dtb_fname)
- def scan_node(self, root, valid_nodes): + def scan_node(self, node, valid_nodes): """Scan a node and subnodes to build a tree of node and phandle info
- This adds each node to self._valid_nodes. + This adds each subnode to self._valid_nodes if it is enabled and has a + compatible string.
Args: - root (Node): Root node for scan + node (Node): Node for scan for subnodes valid_nodes (list of Node): List of Node objects to add to """ - for node in root.subnodes: - if 'compatible' in node.props: - status = node.props.get('status') + for subnode in node.subnodes: + if 'compatible' in subnode.props: + status = subnode.props.get('status') if (not self._include_disabled and not status or status.value != 'disabled'): - valid_nodes.append(node) + valid_nodes.append(subnode)
# recurse to handle any subnodes - self.scan_node(node, valid_nodes) + self.scan_node(subnode, valid_nodes)
def scan_tree(self): """Scan the device tree for useful information
This fills in the following properties: - _valid_nodes: A list of nodes we wish to consider include in the - platform data + _valid_nodes_unsorted: A list of nodes we wish to consider include + in the platform data (in devicetree node order) + _valid_nodes: Sorted version of _valid_nodes_unsorted """ + root = self._fdt.GetRoot() valid_nodes = [] - self.scan_node(self._fdt.GetRoot(), valid_nodes) + self.scan_node(root, valid_nodes) + self._valid_nodes_unsorted = valid_nodes self._valid_nodes = sorted(valid_nodes, key=lambda x: conv_name_to_c(x.name))
@@ -670,6 +677,24 @@ class DtbPlatdata(): elif result is False: print("Could not find uclass for alias '%s'" % prop.name)
+ def assign_seq(self): + """Assign a sequence number to each node""" + for node in self._valid_nodes_unsorted: + if node.driver and node.seq == -1 and node.uclass: + uclass = node.uclass + num = uclass.alias_path_to_num.get(node.path) + if num is not None: + node.seq = num + else: + # Dynamically allocate the next available value after all + # existing ones + for seq in range(1000): + if seq not in uclass.alias_num_to_node: + break + node.seq = seq + uclass.alias_path_to_num[node.path] = seq + uclass.alias_num_to_node[seq] = node + def process_nodes(self, need_drivers): nodes_to_output = list(self._valid_nodes)
@@ -806,9 +831,9 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, plat.setup_output_dirs(output_dirs) plat.scan_structs() plat.scan_phandles() - if do_process: - plat.process_nodes(False) + plat.process_nodes(False) plat.read_aliases() + plat.assign_seq()
cmds = args[0].split(',') if 'all' in cmds: diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 706cc39b3d5..b4c0a042a9f 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -1092,3 +1092,9 @@ U_BOOT_DRVINFO(spl_test2) = { plat = self.run_test(['struct'], dtb_file, output) self.assertEqual("Could not find uclass for alias 'other1'", stdout.getvalue().strip()) + + def test_sequence(self): + """Test assignment of sequence numnbers""" + dtb_file = get_dtb_file('dtoc_test_inst.dts') + output = tools.GetOutputFilename('output') + plat = self.run_test(['struct'], dtb_file, output)

Now that we have the alias information we can assign a sequence number to each device in the uclass. Store this in the node associated with each device.
This requires renaming the sandbox test drivers to have the right name. Note that test coverage is broken with this patch, but fixed in the next one.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
drivers/misc/test_drv.c | 6 +++-- test/dm/test-fdt.c | 6 ++--- tools/dtoc/dtb_platdata.py | 55 +++++++++++++++++++++++++++----------- tools/dtoc/test_dtoc.py | 6 +++++ 4 files changed, 53 insertions(+), 20 deletions(-)
Applied to u-boot-dm, thanks!

We only care about uclasses that are actually used. This is determined by the drivers that use them. Check all the used drivers and build a list of 'valid' uclasses.
Also add references to the uclasses so we can generate C code that uses them. Attach a uclass to each valid driver.
For the tests, now that we have uclasses we must create an explicit test for the case where a node does not have one. This should only happen if the source code does not build, or the source-code scanning fails to find it.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 46 ++++++++++++++++++++++++------------- tools/dtoc/src_scan.py | 45 ++++++++++++++++++++++++++++++++++++ tools/dtoc/test_dtoc.py | 29 +++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 17 ++++++++++++++ 4 files changed, 119 insertions(+), 18 deletions(-)
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 9e99c63ae70..af21156659b 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -151,6 +151,8 @@ class DtbPlatdata(): key (str): Field name value: Prop object with field information _basedir (str): Base directory of source tree + _valid_uclasses (list of src_scan.Uclass): List of uclasses needed for + the selected devices (see _valid_node), in alphabetical order """ def __init__(self, scan, dtb_fname, include_disabled): self._scan = scan @@ -164,6 +166,7 @@ class DtbPlatdata(): self._dirnames = [None] * len(Ftype) self._struct_data = collections.OrderedDict() self._basedir = None + self._valid_uclasses = None
def setup_output_dirs(self, output_dirs): """Set up the output directories @@ -677,23 +680,12 @@ class DtbPlatdata(): elif result is False: print("Could not find uclass for alias '%s'" % prop.name)
- def assign_seq(self): + def assign_seqs(self): """Assign a sequence number to each node""" for node in self._valid_nodes_unsorted: - if node.driver and node.seq == -1 and node.uclass: - uclass = node.uclass - num = uclass.alias_path_to_num.get(node.path) - if num is not None: - node.seq = num - else: - # Dynamically allocate the next available value after all - # existing ones - for seq in range(1000): - if seq not in uclass.alias_num_to_node: - break - node.seq = seq - uclass.alias_path_to_num[node.path] = seq - uclass.alias_num_to_node[seq] = node + seq = self._scan.assign_seq(node) + if seq is not None: + node.seq = seq
def process_nodes(self, need_drivers): nodes_to_output = list(self._valid_nodes) @@ -710,6 +702,16 @@ class DtbPlatdata(): raise ValueError("Cannot parse/find driver for '%s'" % node.struct_name) node.driver = driver + uclass = self._scan._uclass.get(driver.uclass_id) + if not uclass: + raise ValueError("Cannot parse/find uclass '%s' for driver '%s'" % + (driver.uclass_id, node.struct_name)) + node.uclass = uclass + node.uclass_seq = len(node.uclass.devs) + node.uclass.devs.append(node) + uclass.node_refs[node.uclass_seq] = \ + '&%s->uclass_node' % node.dev_ref + parent_driver = None if node.parent in self._valid_nodes: parent_driver = self._scan.get_driver(node.parent.struct_name) @@ -730,6 +732,18 @@ class DtbPlatdata(): node.child_refs[-1] = ref node.child_refs[len(node.child_devs)] = ref
+ uclass_set = set() + for driver in self._scan._drivers.values(): + if driver.used and driver.uclass: + uclass_set.add(driver.uclass) + self._valid_uclasses = sorted(list(uclass_set), + key=lambda uc: uc.uclass_id) + + for seq, uclass in enumerate(uclass_set): + ref = '&DM_UCLASS_REF(%s)->dev_head' % uclass.name + uclass.node_refs[-1] = ref + uclass.node_refs[len(uclass.devs)] = ref + def output_node(self, node): """Output the C code for a node
@@ -833,7 +847,7 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, plat.scan_phandles() plat.process_nodes(False) plat.read_aliases() - plat.assign_seq() + plat.assign_seqs()
cmds = args[0].split(',') if 'all' in cmds: diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index bb22b0b64ff..8619206a8d4 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -74,6 +74,7 @@ class Driver: found after this one warn_dups (bool): True if the duplicates are not distinguisble using the phase + uclass (Uclass): uclass for this driver """ def __init__(self, name, fname): self.name = name @@ -89,6 +90,7 @@ class Driver: self.headers = [] self.dups = [] self.warn_dups = False + self.uclass = None
def __eq__(self, other): return (self.name == other.name and @@ -123,6 +125,10 @@ class UclassDriver: alias_path_to_num (dict): Convert a path to an alias number key (str): Full path to node (e.g. '/soc/pci') seq (int): Alias number, e.g. 2 for "pci2" + devs (list): List of devices in this uclass, each a Node + node_refs (dict): References in the linked list of devices: + key (int): Sequence number (0=first, n-1=last, -1=head, n=tail) + value (str): Reference to the device at that position """ def __init__(self, name): self.name = name @@ -134,6 +140,8 @@ class UclassDriver: self.per_child_plat = '' self.alias_num_to_node = {} self.alias_path_to_num = {} + self.devs = [] + self.node_refs = {}
def __eq__(self, other): return (self.name == other.name and @@ -639,6 +647,12 @@ class Scanner: else: self.scan_driver(self._basedir + '/' + fname)
+ # Get the uclass for each driver + # TODO: Can we just get the uclass for the ones we use, e.g. in + # mark_used()? + for driver in self._drivers.values(): + driver.uclass = self._uclass.get(driver.uclass_id) + def mark_used(self, nodes): """Mark the drivers associated with a list of nodes as 'used'
@@ -682,3 +696,34 @@ class Scanner: uclass.alias_path_to_num[node.path] = int(num) return True return False + + def assign_seq(self, node): + """Figure out the sequence number for a node + + This looks in the node's uclass and assigns a sequence number if needed, + based on the aliases and other nodes in that uclass. + + It updates the uclass alias_path_to_num and alias_num_to_node + + Args: + node (Node): Node object to look up + """ + if node.driver and node.seq == -1 and node.uclass: + uclass = node.uclass + num = uclass.alias_path_to_num.get(node.path) + if num is not None: + return num + else: + # Dynamically allocate the next available value after all + # existing ones + if uclass.alias_num_to_node: + start = max(uclass.alias_num_to_node.keys()) + else: + start = -1 + for seq in range(start + 1, 1000): + if seq not in uclass.alias_num_to_node: + break + uclass.alias_path_to_num[node.path] = seq + uclass.alias_num_to_node[seq] = node + return seq + return None diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index b4c0a042a9f..b077cf0e76d 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -141,6 +141,8 @@ class TestDtoc(unittest.TestCase): Returns: DtbPlatdata object """ + # Make a copy of the 'scan' object, since it includes uclasses and + # drivers, which get updated during execution. return dtb_platdata.run_steps(args, dtb_file, False, output, [], None, warning_disabled=True, scan=copy_scan())
@@ -1033,6 +1035,16 @@ U_BOOT_DRVINFO(spl_test2) = { self.assertIn("Cannot parse/find driver for 'sandbox_pmic", str(exc.exception))
+ def test_process_nodes_bad_uclass(self): + plat, scan = self.setup_process_test() + + self.assertIn('UCLASS_I2C', scan._uclass) + del scan._uclass['UCLASS_I2C'] + with self.assertRaises(ValueError) as exc: + plat.process_nodes(True) + self.assertIn("Cannot parse/find uclass 'UCLASS_I2C' for driver 'sandbox_i2c'", + str(exc.exception)) + def test_process_nodes_used(self): """Test processing nodes to add various info""" plat, scan = self.setup_process_test() @@ -1052,10 +1064,13 @@ U_BOOT_DRVINFO(spl_test2) = {
scan = plat._scan testfdt_node = plat._fdt.GetNode('/some-bus/test') + test0_node = plat._fdt.GetNode('/some-bus/test0') self.assertIn('UCLASS_TEST_FDT', scan._uclass) uc = scan._uclass['UCLASS_TEST_FDT'] - self.assertEqual({1: testfdt_node}, uc.alias_num_to_node) - self.assertEqual({'/some-bus/test': 1}, uc.alias_path_to_num) + self.assertEqual({1: testfdt_node, 2: test0_node}, + uc.alias_num_to_node) + self.assertEqual({'/some-bus/test': 1, '/some-bus/test0': 2}, + uc.alias_path_to_num)
# Try adding an alias that doesn't exist self.assertFalse(scan.add_uclass_alias('fred', 3, None)) @@ -1098,3 +1113,13 @@ U_BOOT_DRVINFO(spl_test2) = { dtb_file = get_dtb_file('dtoc_test_inst.dts') output = tools.GetOutputFilename('output') plat = self.run_test(['struct'], dtb_file, output) + + scan = plat._scan + testfdt = plat._fdt.GetNode('/some-bus/test') + self.assertEqual(1, testfdt.seq) + i2c = plat._fdt.GetNode('/i2c') + + # For now this uclass is not compiled in, so no sequence is assigned + self.assertEqual(4, i2c.seq) + spl = plat._fdt.GetNode('/spl-test') + self.assertEqual(0, spl.seq) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 598ff256a60..d32aa58400f 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -466,3 +466,20 @@ U_BOOT_DRIVER(%s) = { with test_util.capture_sys_output() as (stdout, _): scan.mark_used([node]) self.assertEqual('', stdout.getvalue().strip()) + + def test_sequence(self): + """Test assignment of sequence numnbers""" + scan = src_scan.Scanner(None, False, None, '') + node = FakeNode() + uc = src_scan.UclassDriver('UCLASS_I2C') + node.uclass = uc + node.driver = True + node.seq = -1 + node.path = 'mypath' + uc.alias_num_to_node[2] = node + + # This should assign 3 (after the 2 that exists) + seq = scan.assign_seq(node) + self.assertEqual(3, seq) + self.assertEqual({'mypath': 3}, uc.alias_path_to_num) + self.assertEqual({2: node, 3: node}, uc.alias_num_to_node)

We only care about uclasses that are actually used. This is determined by the drivers that use them. Check all the used drivers and build a list of 'valid' uclasses.
Also add references to the uclasses so we can generate C code that uses them. Attach a uclass to each valid driver.
For the tests, now that we have uclasses we must create an explicit test for the case where a node does not have one. This should only happen if the source code does not build, or the source-code scanning fails to find it.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 46 ++++++++++++++++++++++++------------- tools/dtoc/src_scan.py | 45 ++++++++++++++++++++++++++++++++++++ tools/dtoc/test_dtoc.py | 29 +++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 17 ++++++++++++++ 4 files changed, 119 insertions(+), 18 deletions(-)
Applied to u-boot-dm, thanks!

The device for the root node is normally bound by driver model on init. With devices being instantiated at build time, we must handle the root device also.
Add support for processing the root node, which may not have a compatible string.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 10 ++++++++-- tools/dtoc/src_scan.py | 39 ++++++++++++++++++++++--------------- tools/dtoc/test_dtoc.py | 23 ++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 7 +++++++ 4 files changed, 59 insertions(+), 20 deletions(-)
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index af21156659b..e08b92cf8a3 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -350,16 +350,22 @@ class DtbPlatdata(): # recurse to handle any subnodes self.scan_node(subnode, valid_nodes)
- def scan_tree(self): + def scan_tree(self, add_root): """Scan the device tree for useful information
This fills in the following properties: _valid_nodes_unsorted: A list of nodes we wish to consider include in the platform data (in devicetree node order) _valid_nodes: Sorted version of _valid_nodes_unsorted + + Args: + add_root: True to add the root node also (which wouldn't normally + be added as it may not have a compatible string) """ root = self._fdt.GetRoot() valid_nodes = [] + if add_root: + valid_nodes.append(root) self.scan_node(root, valid_nodes) self._valid_nodes_unsorted = valid_nodes self._valid_nodes = sorted(valid_nodes, @@ -839,7 +845,7 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, do_process = False plat = DtbPlatdata(scan, dtb_file, include_disabled) plat.scan_dtb() - plat.scan_tree() + plat.scan_tree(add_root=False) plat.prepare_nodes() plat.scan_reg_sizes() plat.setup_output_dirs(output_dirs) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 8619206a8d4..114212cfe2d 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -33,6 +33,8 @@ def conv_name_to_c(name): new = new.replace('-', '_') new = new.replace(',', '_') new = new.replace('.', '_') + if new == '/': + return 'root' return new
def get_compat_name(node): @@ -250,7 +252,10 @@ class Scanner: In case of no match found, the return will be the same as get_compat_name() """ - compat_list_c = get_compat_name(node) + if not node.parent: + compat_list_c = ['root_driver'] + else: + compat_list_c = get_compat_name(node)
for compat_c in compat_list_c: if not compat_c in self._drivers.keys(): @@ -509,21 +514,23 @@ class Scanner: elif m_hdr: driver.headers.append(m_hdr.group(1)) elif '};' in line: - if driver.uclass_id and compat: - if compat not in of_match: - raise ValueError( - "%s: Unknown compatible var '%s' (found: %s)" % - (fname, compat, ','.join(of_match.keys()))) - driver.compat = of_match[compat] - - # This needs to be deterministic, since a driver may - # have multiple compatible strings pointing to it. - # We record the one earliest in the alphabet so it - # will produce the same result on all machines. - for compat_id in of_match[compat]: - old = self._compat_to_driver.get(compat_id) - if not old or driver.name < old.name: - self._compat_to_driver[compat_id] = driver + is_root = driver.name == 'root_driver' + if driver.uclass_id and (compat or is_root): + if not is_root: + if compat not in of_match: + raise ValueError( + "%s: Unknown compatible var '%s' (found: %s)" % + (fname, compat, ','.join(of_match.keys()))) + driver.compat = of_match[compat] + + # This needs to be deterministic, since a driver may + # have multiple compatible strings pointing to it. + # We record the one earliest in the alphabet so it + # will produce the same result on all machines. + for compat_id in of_match[compat]: + old = self._compat_to_driver.get(compat_id) + if not old or driver.name < old.name: + self._compat_to_driver[compat_id] = driver drivers[driver.name] = driver else: # The driver does not have a uclass or compat string. diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index b077cf0e76d..ed8c7e47882 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -974,10 +974,10 @@ U_BOOT_DRVINFO(spl_test2) = { output = tools.GetOutputFilename('output')
# Take a copy before messing with it - scan = copy.deepcopy(saved_scan) + scan = copy_scan() plat = dtb_platdata.DtbPlatdata(scan, dtb_file, False) plat.scan_dtb() - plat.scan_tree() + plat.scan_tree(False) plat.prepare_nodes() return plat, scan
@@ -1123,3 +1123,22 @@ U_BOOT_DRVINFO(spl_test2) = { self.assertEqual(4, i2c.seq) spl = plat._fdt.GetNode('/spl-test') self.assertEqual(0, spl.seq) + + def test_process_root(self): + """Test assignment of sequence numnbers""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + + # Take a copy before messing with it + scan = copy_scan() + plat = dtb_platdata.DtbPlatdata(scan, dtb_file, False) + plat.scan_dtb() + root = plat._fdt.GetRoot() + + plat.scan_tree(False) + self.assertNotIn(root, plat._valid_nodes) + + plat.scan_tree(True) + self.assertIn(root, plat._valid_nodes) + self.assertEqual('root_driver', + scan.get_normalized_compat_name(root)[0]) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index d32aa58400f..0af86dcf0c3 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -161,6 +161,10 @@ class TestSrcScan(unittest.TestCase): prop.value = 'rockchip,rk3288-grf' node = FakeNode() node.props = {'compatible': prop} + + # get_normalized_compat_name() uses this to check for root node + node.parent = FakeNode() + scan = src_scan.Scanner(None, False, None) with test_util.capture_sys_output() as (stdout, _): name, aliases = scan.get_normalized_compat_name(node) @@ -419,6 +423,9 @@ U_BOOT_DRIVER(%s) = { node.name = 'testing' node.props = {'compatible': prop}
+ # get_normalized_compat_name() uses this to check for root node + node.parent = FakeNode() + return scan, drv1, driver2, node
def test_dup_drivers(self):

The device for the root node is normally bound by driver model on init. With devices being instantiated at build time, we must handle the root device also.
Add support for processing the root node, which may not have a compatible string.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 10 ++++++++-- tools/dtoc/src_scan.py | 39 ++++++++++++++++++++++--------------- tools/dtoc/test_dtoc.py | 23 ++++++++++++++++++++-- tools/dtoc/test_src_scan.py | 7 +++++++ 4 files changed, 59 insertions(+), 20 deletions(-)
Applied to u-boot-dm, thanks!

Add an option to instantiate devices at build time. For now this just parses the option and sets up a few parameters.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 17 +++++++++++------ tools/dtoc/main.py | 4 +++- tools/dtoc/test_dtoc.py | 37 ++++++++++++++++++++++--------------- 3 files changed, 36 insertions(+), 22 deletions(-)
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index e08b92cf8a3..46e2725a86c 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -153,8 +153,10 @@ class DtbPlatdata(): _basedir (str): Base directory of source tree _valid_uclasses (list of src_scan.Uclass): List of uclasses needed for the selected devices (see _valid_node), in alphabetical order + _instantiate: Instantiate devices so they don't need to be bound at + run-time """ - def __init__(self, scan, dtb_fname, include_disabled): + def __init__(self, scan, dtb_fname, include_disabled, instantiate=False): self._scan = scan self._fdt = None self._dtb_fname = dtb_fname @@ -167,6 +169,7 @@ class DtbPlatdata(): self._struct_data = collections.OrderedDict() self._basedir = None self._valid_uclasses = None + self._instantiate = instantiate
def setup_output_dirs(self, output_dirs): """Set up the output directories @@ -802,8 +805,8 @@ OUTPUT_FILES = {
def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, - warning_disabled=False, drivers_additional=None, basedir=None, - scan=None): + instantiate, warning_disabled=False, drivers_additional=None, + basedir=None, scan=None): """Run all the steps of the dtoc tool
Args: @@ -816,6 +819,8 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, Directory to put H output files phase: The phase of U-Boot that we are generating data for, e.g. 'spl' or 'tpl'. None if not known + instantiate: Instantiate devices so they don't need to be bound at + run-time warning_disabled (bool): True to avoid showing warnings about missing drivers drivers_additional (list): List of additional drivers to use during @@ -843,15 +848,15 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, do_process = True else: do_process = False - plat = DtbPlatdata(scan, dtb_file, include_disabled) + plat = DtbPlatdata(scan, dtb_file, include_disabled, instantiate) plat.scan_dtb() - plat.scan_tree(add_root=False) + plat.scan_tree(add_root=instantiate) plat.prepare_nodes() plat.scan_reg_sizes() plat.setup_output_dirs(output_dirs) plat.scan_structs() plat.scan_phandles() - plat.process_nodes(False) + plat.process_nodes(instantiate) plat.read_aliases() plat.assign_seqs()
diff --git a/tools/dtoc/main.py b/tools/dtoc/main.py index 15a8ff167a9..93706de89bf 100755 --- a/tools/dtoc/main.py +++ b/tools/dtoc/main.py @@ -81,6 +81,8 @@ parser.add_option('-C', '--h-output-dir', action='store', help='Select output directory for H files (defaults to --c-output-di)') parser.add_option('-d', '--dtb-file', action='store', help='Specify the .dtb input file') +parser.add_option('-i', '--instantiate', action='store_true', default=False, + help='Instantiate devices to avoid needing device_bind()') parser.add_option('--include-disabled', action='store_true', help='Include disabled nodes') parser.add_option('-o', '--output', action='store', @@ -107,4 +109,4 @@ else: dtb_platdata.run_steps(args, options.dtb_file, options.include_disabled, options.output, [options.c_output_dir, options.h_output_dir], - phase=options.phase) + options.phase, instantiate=options.instantiate) diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index ed8c7e47882..cbddd62424d 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -130,7 +130,7 @@ class TestDtoc(unittest.TestCase): self.assertEqual(expected, actual)
@staticmethod - def run_test(args, dtb_file, output): + def run_test(args, dtb_file, output, instantiate=False): """Run a test using dtoc
Args: @@ -143,8 +143,9 @@ class TestDtoc(unittest.TestCase): """ # Make a copy of the 'scan' object, since it includes uclasses and # drivers, which get updated during execution. - return dtb_platdata.run_steps(args, dtb_file, False, output, [], None, - warning_disabled=True, scan=copy_scan()) + return dtb_platdata.run_steps( + args, dtb_file, False, output, [], None, instantiate, + warning_disabled=True, scan=copy_scan())
def test_name(self): """Test conversion of device tree names to C identifiers""" @@ -201,7 +202,8 @@ class TestDtoc(unittest.TestCase): output = tools.GetOutputFilename('output')
# Run this one without saved_scan to complete test coverage - dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], True) + dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], None, + False) with open(output) as infile: lines = infile.read().splitlines() self.assertEqual(HEADER.splitlines(), lines) @@ -369,8 +371,9 @@ U_BOOT_DRVINFO(gpios_at_0) = { dtb_file = get_dtb_file('dtoc_test_invalid_driver.dts') output = tools.GetOutputFilename('output') with test_util.capture_sys_output() as _: - dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], - None, scan=copy_scan()) + dtb_platdata.run_steps( + ['struct'], dtb_file, False, output, [], None, False, + scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(HEADER + ''' @@ -379,8 +382,9 @@ struct dtd_invalid { ''', data)
with test_util.capture_sys_output() as _: - dtb_platdata.run_steps(['platdata'], dtb_file, False, output, [], - None, scan=copy_scan()) + dtb_platdata.run_steps( + ['platdata'], dtb_file, False, output, [], None, False, + scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' @@ -530,8 +534,9 @@ U_BOOT_DRVINFO(phandle_target) = { """Test that phandle targets are generated when unsing cd-gpios""" dtb_file = get_dtb_file('dtoc_test_phandle_cd_gpios.dts') output = tools.GetOutputFilename('output') - dtb_platdata.run_steps(['platdata'], dtb_file, False, output, [], True, - scan=copy_scan()) + dtb_platdata.run_steps( + ['platdata'], dtb_file, False, output, [], None, False, + warning_disabled=True, scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' @@ -925,15 +930,16 @@ U_BOOT_DRVINFO(spl_test2) = { dtb_file = get_dtb_file('dtoc_test_simple.dts') output = tools.GetOutputFilename('output') with self.assertRaises(ValueError) as exc: - self.run_test(['invalid-cmd'], dtb_file, output) + self.run_test(['invalid-cmd'], dtb_file, output, False) self.assertIn("Unknown command 'invalid-cmd': (use: platdata, struct)", str(exc.exception))
def test_output_conflict(self): """Test a conflict between and output dirs and output file""" with self.assertRaises(ValueError) as exc: - dtb_platdata.run_steps(['all'], None, False, 'out', ['cdir'], None, - warning_disabled=True, scan=copy_scan()) + dtb_platdata.run_steps( + ['all'], None, False, 'out', ['cdir'], None, False, + warning_disabled=True, scan=copy_scan()) self.assertIn("Must specify either output or output_dirs, not both", str(exc.exception))
@@ -949,8 +955,9 @@ U_BOOT_DRVINFO(spl_test2) = { fnames = glob.glob(outdir + '/*') self.assertEqual(2, len(fnames))
- dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], None, - warning_disabled=True, scan=copy_scan()) + dtb_platdata.run_steps( + ['all'], dtb_file, False, None, [outdir], None, False, + warning_disabled=True, scan=copy_scan()) fnames = glob.glob(outdir + '/*') self.assertEqual(4, len(fnames))

Add an option to instantiate devices at build time. For now this just parses the option and sets up a few parameters.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 17 +++++++++++------ tools/dtoc/main.py | 4 +++- tools/dtoc/test_dtoc.py | 37 ++++++++++++++++++++++--------------- 3 files changed, 36 insertions(+), 22 deletions(-)
Applied to u-boot-dm, thanks!

Add Kconfig options to support build-time device instantiation. When fully implemented, this will allow dtoc to create U-Boot devices (i.e. struct udevice records) at build time, thus reducing code space in SPL.
For now this defaults to off, but will be enabled when the rest of the implementation is in place.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
dts/Kconfig | 23 +++++++++++++++++++++-- scripts/Makefile.spl | 4 ++++ 2 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/dts/Kconfig b/dts/Kconfig index 71f50552e4f..e861ea48d01 100644 --- a/dts/Kconfig +++ b/dts/Kconfig @@ -355,15 +355,24 @@ config SPL_OF_PLATDATA compatible string, then adding platform data and U_BOOT_DRVINFO declarations for each node. See of-plat.txt for more information.
+if SPL_OF_PLATDATA + config SPL_OF_PLATDATA_PARENT bool "Support parent information in devices" - depends on SPL_OF_PLATDATA default y help Generally it is useful to be able to access the parent of a device with of-platdata. To save space this can be disabled, but in that case dev_get_parent() will always return NULL;
+config SPL_OF_PLATDATA_INST + bool "Declare devices at build time" + help + Declare devices as udevice instances so that they do not need to be + bound when U-Boot starts. This can save time and code space. + +endif + config TPL_OF_PLATDATA bool "Generate platform data for use in TPL" depends on TPL_OF_CONTROL @@ -385,13 +394,23 @@ config TPL_OF_PLATDATA compatible string, then adding platform data and U_BOOT_DRVINFO declarations for each node. See of-plat.txt for more information.
+if TPL_OF_PLATDATA + config TPL_OF_PLATDATA_PARENT bool "Support parent information in devices" - depends on TPL_OF_PLATDATA default y help Generally it is useful to be able to access the parent of a device with of-platdata. To save space this can be disabled, but in that case dev_get_parent() will always return NULL;
+config TPL_OF_PLATDATA_INST + bool "Declare devices at build time" + + help + Declare devices as udevice instances so that they do not need to be + bound when U-Boot starts. This can save time and code space. + +endif + endmenu diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index 67641d0faea..52609112ecd 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -311,6 +311,10 @@ pythonpath = PYTHONPATH=scripts/dtc/pylibfdt DTOC_ARGS := $(pythonpath) $(srctree)/tools/dtoc/dtoc \ -d $(obj)/$(SPL_BIN).dtb -p $(SPL_NAME)
+ifneq ($(CONFIG_$(SPL_TPL_)OF_PLATDATA_INST),) +DTOC_ARGS += -i +endif + quiet_cmd_dtoc = DTOC $@ cmd_dtoc = $(DTOC_ARGS) -c $(obj)/dts -C include/generated all

Add Kconfig options to support build-time device instantiation. When fully implemented, this will allow dtoc to create U-Boot devices (i.e. struct udevice records) at build time, thus reducing code space in SPL.
For now this defaults to off, but will be enabled when the rest of the implementation is in place.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
dts/Kconfig | 23 +++++++++++++++++++++-- scripts/Makefile.spl | 4 ++++ 2 files changed, 25 insertions(+), 2 deletions(-)
Applied to u-boot-dm, thanks!

Add an option to generate the declaration file, which declares all drivers and uclasses, so references can be used in the code.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 36 +++++++++++++++ tools/dtoc/test_dtoc.py | 91 +++++++++++++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 7 deletions(-)
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 46e2725a86c..040b7246787 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -689,6 +689,39 @@ class DtbPlatdata(): elif result is False: print("Could not find uclass for alias '%s'" % prop.name)
+ def generate_decl(self): + nodes_to_output = list(self._valid_nodes) + + self.buf('#include <dm/device-internal.h>\n') + self.buf('#include <dm/uclass-internal.h>\n') + self.buf('\n') + self.buf( + '/* driver declarations - these allow DM_DRIVER_GET() to be used */\n') + for node in nodes_to_output: + self.buf('DM_DRIVER_DECL(%s);\n' % node.struct_name); + self.buf('\n') + + if self._instantiate: + self.buf( + '/* device declarations - these allow DM_DEVICE_REF() to be used */\n') + for node in nodes_to_output: + self.buf('DM_DEVICE_DECL(%s);\n' % node.var_name) + self.buf('\n') + + uclass_list = self._valid_uclasses + + self.buf( + '/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */\n') + for uclass in uclass_list: + self.buf('DM_UCLASS_DRIVER_DECL(%s);\n' % uclass.name) + + if self._instantiate: + self.buf('\n') + self.buf('/* uclass declarations - needed for DM_UCLASS_REF() */\n') + for uclass in uclass_list: + self.buf('DM_UCLASS_DECL(%s);\n' % uclass.name) + self.out(''.join(self.get_buf())) + def assign_seqs(self): """Assign a sequence number to each node""" for node in self._valid_nodes_unsorted: @@ -794,6 +827,9 @@ class DtbPlatdata(): # key: Command used to generate this file # value: OutputFile for this command OUTPUT_FILES = { + 'decl': + OutputFile(Ftype.HEADER, 'dt-decl.h', DtbPlatdata.generate_decl, + 'Declares externs for all device/uclass instances'), 'struct': OutputFile(Ftype.HEADER, 'dt-structs-gen.h', DtbPlatdata.generate_structs, diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index cbddd62424d..a51a7f301ce 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -40,6 +40,14 @@ HEADER = '''/* #include <stdbool.h> #include <linux/libfdt.h>'''
+DECL_HEADER = '''/* + * DO NOT MODIFY + * + * Declares externs for all device/uclass instances. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ +''' + C_HEADER = '''/* * DO NOT MODIFY * @@ -213,6 +221,54 @@ class TestDtoc(unittest.TestCase): lines = infile.read().splitlines() self.assertEqual(C_HEADER.splitlines() + [''], lines)
+ decl_text = DECL_HEADER + ''' +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> + +/* driver declarations - these allow DM_DRIVER_GET() to be used */ +DM_DRIVER_DECL(sandbox_i2c); +DM_DRIVER_DECL(sandbox_pmic); +DM_DRIVER_DECL(sandbox_spl_test); +DM_DRIVER_DECL(sandbox_spl_test); +DM_DRIVER_DECL(sandbox_spl_test); + +/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */ +DM_UCLASS_DRIVER_DECL(i2c); +DM_UCLASS_DRIVER_DECL(misc); +DM_UCLASS_DRIVER_DECL(pmic); +''' + decl_text_inst = DECL_HEADER + ''' +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> + +/* 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(sandbox_spl_test); +DM_DRIVER_DECL(sandbox_spl_test); +DM_DRIVER_DECL(sandbox_spl_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(root); +DM_DEVICE_DECL(spl_test); +DM_DEVICE_DECL(spl_test2); +DM_DEVICE_DECL(spl_test3); + +/* 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); + +/* uclass declarations - needed for DM_UCLASS_REF() */ +DM_UCLASS_DECL(i2c); +DM_UCLASS_DECL(misc); +DM_UCLASS_DECL(pmic); +DM_UCLASS_DECL(root); +''' struct_text = HEADER + ''' struct dtd_sandbox_i2c { }; @@ -327,10 +383,17 @@ U_BOOT_DRVINFO(spl_test3) = {
self._check_strings(self.platdata_text, data)
+ self.run_test(['decl'], dtb_file, output) + with open(output) as infile: + data = infile.read() + + self._check_strings(self.decl_text, data) + # Try the 'all' command self.run_test(['all'], dtb_file, output) data = tools.ReadFile(output, binary=False) - self._check_strings(self.platdata_text + self.struct_text, data) + self._check_strings(self.decl_text + self.platdata_text + + self.struct_text, data)
def test_driver_alias(self): """Test output from a device tree file with a driver alias""" @@ -916,7 +979,8 @@ 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.platdata_text + self.struct_text, data) + self._check_strings(self.decl_text + self.platdata_text + + self.struct_text, data)
def test_no_command(self): """Test running dtoc without a command""" @@ -930,9 +994,10 @@ U_BOOT_DRVINFO(spl_test2) = { dtb_file = get_dtb_file('dtoc_test_simple.dts') output = tools.GetOutputFilename('output') with self.assertRaises(ValueError) as exc: - self.run_test(['invalid-cmd'], dtb_file, output, False) - self.assertIn("Unknown command 'invalid-cmd': (use: platdata, struct)", - str(exc.exception)) + self.run_test(['invalid-cmd'], dtb_file, output) + self.assertIn( + "Unknown command 'invalid-cmd': (use: decl, platdata, struct)", + str(exc.exception))
def test_output_conflict(self): """Test a conflict between and output dirs and output file""" @@ -959,11 +1024,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(4, len(fnames)) + self.assertEqual(5, 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-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb', + 'dt-decl.h'}, leafs)
def setup_process_test(self): @@ -1149,3 +1215,14 @@ U_BOOT_DRVINFO(spl_test2) = { self.assertIn(root, plat._valid_nodes) self.assertEqual('root_driver', scan.get_normalized_compat_name(root)[0]) + + def test_simple_inst(self): + """Test output from some simple nodes with instantiate enabled""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + + self.run_test(['decl'], dtb_file, output, True) + with open(output) as infile: + data = infile.read() + + self._check_strings(self.decl_text_inst, data)

Add an option to generate the declaration file, which declares all drivers and uclasses, so references can be used in the code.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 36 +++++++++++++++ tools/dtoc/test_dtoc.py | 91 +++++++++++++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 7 deletions(-)
Applied to u-boot-dm, thanks!

This file is not used when instantiating devices. Update dtoc to skip generating its contents and just add a comment instead.
Also it is useful to see the driver name and parent for each device. Update the file to show that information, to avoid updating the same tests twice.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 35 ++++++++++++++++++++++--- tools/dtoc/test_dtoc.py | 53 +++++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 13 deletions(-)
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 040b7246787..befe7c14901 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -786,19 +786,46 @@ class DtbPlatdata(): uclass.node_refs[-1] = ref uclass.node_refs[len(uclass.devs)] = ref
- def output_node(self, node): + def output_node_plat(self, node): """Output the C code for a node
Args: node (fdt.Node): node to output """ - self.buf('/* Node %s index %d */\n' % (node.path, node.idx)) + driver = node.driver + parent_driver = node.parent_driver + + line1 = 'Node %s index %d' % (node.path, node.idx) + if driver: + self.buf('/*\n') + self.buf(' * %s\n' % line1) + self.buf(' * driver %s parent %s\n' % (driver.name, + parent_driver.name if parent_driver else 'None')) + self.buf(' */\n') + else: + self.buf('/* %s */\n' % line1)
self._output_values(node) self._declare_device(node)
self.out(''.join(self.get_buf()))
+ def check_instantiate(self, require): + """Check if self._instantiate is set to the required value + + If not, this outputs a message into the current file + + Args: + require: True to require --instantiate, False to require that it not + be enabled + """ + if require != self._instantiate: + self.out( + '/* This file is not used: --instantiate was %senabled */\n' % + ('not ' if require else '')) + return False + return True + def generate_plat(self): """Generate device defintions for the platform data
@@ -809,6 +836,8 @@ class DtbPlatdata(): See the documentation in doc/driver-model/of-plat.rst for more information. """ + if not self.check_instantiate(False): + return self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n') self.out('#define DT_PLAT_C\n') self.out('\n') @@ -818,7 +847,7 @@ class DtbPlatdata(): self.out('\n')
for node in self._valid_nodes: - self.output_node(node) + self.output_node_plat(node)
self.out(''.join(self.get_buf()))
diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index a51a7f301ce..c6e33d34227 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -48,13 +48,15 @@ DECL_HEADER = '''/* */ '''
-C_HEADER = '''/* +C_HEADER_PRE = '''/* * DO NOT MODIFY * * Declares the U_BOOT_DRIVER() records and platform data. * This was generated by dtoc from a .dtb (device tree binary) file. */ +'''
+C_HEADER = C_HEADER_PRE + ''' /* Allow use of U_BOOT_DRVINFO() in this file */ #define DT_PLAT_C
@@ -289,9 +291,11 @@ struct dtd_sandbox_spl_test { \tconst char *\tstringval; }; ''' - platdata_text = C_HEADER + ''' -/* Node /i2c@0 index 0 */ +/* + * Node /i2c@0 index 0 + * driver sandbox_i2c parent None + */ static struct dtd_sandbox_i2c dtv_i2c_at_0 = { }; U_BOOT_DRVINFO(i2c_at_0) = { @@ -301,7 +305,10 @@ U_BOOT_DRVINFO(i2c_at_0) = { \t.parent_idx\t= -1, };
-/* Node /i2c@0/pmic@9 index 1 */ +/* + * Node /i2c@0/pmic@9 index 1 + * driver sandbox_pmic parent sandbox_i2c + */ static struct dtd_sandbox_pmic dtv_pmic_at_9 = { \t.low_power\t\t= true, \t.reg\t\t\t= {0x9, 0x0}, @@ -313,7 +320,10 @@ U_BOOT_DRVINFO(pmic_at_9) = { \t.parent_idx\t= 0, };
-/* Node /spl-test index 2 */ +/* + * Node /spl-test index 2 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test = { \t.boolval\t\t= true, \t.bytearray\t\t= {0x6, 0x0, 0x0}, @@ -333,7 +343,10 @@ U_BOOT_DRVINFO(spl_test) = { \t.parent_idx\t= -1, };
-/* Node /spl-test2 index 3 */ +/* + * Node /spl-test2 index 3 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test2 = { \t.acpi_name\t\t= "\\_SB.GPO0", \t.bytearray\t\t= {0x1, 0x23, 0x34}, @@ -352,7 +365,10 @@ U_BOOT_DRVINFO(spl_test2) = { \t.parent_idx\t= -1, };
-/* Node /spl-test3 index 4 */ +/* + * Node /spl-test3 index 4 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test3 = { \t.longbytearray\t\t= {0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, \t\t0x0}, @@ -414,7 +430,10 @@ struct dtd_sandbox_gpio { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' -/* Node /gpios@0 index 0 */ +/* + * Node /gpios@0 index 0 + * driver sandbox_gpio parent None + */ static struct dtd_sandbox_gpio dtv_gpios_at_0 = { \t.gpio_bank_name\t\t= "a", \t.gpio_controller\t= true, @@ -942,7 +961,10 @@ struct dtd_sandbox_spl_test { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' -/* Node /spl-test index 0 */ +/* + * Node /spl-test index 0 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test = { \t.intval\t\t\t= 0x1, }; @@ -953,7 +975,10 @@ U_BOOT_DRVINFO(spl_test) = { \t.parent_idx\t= -1, };
-/* Node /spl-test2 index 1 */ +/* + * Node /spl-test2 index 1 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test2 = { \t.intarray\t\t= 0x5, }; @@ -1226,3 +1251,11 @@ U_BOOT_DRVINFO(spl_test2) = { data = infile.read()
self._check_strings(self.decl_text_inst, data) + + self.run_test(['platdata'], dtb_file, output, True) + with open(output) as infile: + data = infile.read() + + self._check_strings(C_HEADER_PRE + ''' +/* This file is not used: --instantiate was enabled */ +''', data)

This file is not used when instantiating devices. Update dtoc to skip generating its contents and just add a comment instead.
Also it is useful to see the driver name and parent for each device. Update the file to show that information, to avoid updating the same tests twice.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 35 ++++++++++++++++++++++--- tools/dtoc/test_dtoc.py | 53 +++++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 13 deletions(-)
Applied to u-boot-dm, thanks!

At present this property is a phandle but does not have a #xxx-cells property to match it. Add one so that is works the same as gpio and clock phandles.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
arch/sandbox/dts/sandbox.dtsi | 6 +++++- doc/driver-model/pci-info.rst | 1 + 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index dc933f3bfc7..7455c99a739 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -101,15 +101,19 @@ };
i2c_emul: emul { + u-boot,dm-pre-reloc; reg = <0xff>; compatible = "sandbox,i2c-emul-parent"; emul_eeprom: emul-eeprom { compatible = "sandbox,i2c-eeprom"; sandbox,filename = "i2c.bin"; sandbox,size = <256>; + #emul-cells = <0>; }; emul0: emul0 { - compatible = "sandbox,i2c-rtc"; + u-boot,dm-pre-reloc; + compatible = "sandbox,i2c-rtc-emul"; + #emul-cells = <0>; }; }; }; diff --git a/doc/driver-model/pci-info.rst b/doc/driver-model/pci-info.rst index 8b9faa10663..251601a51e3 100644 --- a/doc/driver-model/pci-info.rst +++ b/doc/driver-model/pci-info.rst @@ -125,6 +125,7 @@ emulator driver. For example:: compatible = "sandbox,pci-emul-parent"; emul_1f: emul@1f,0 { compatible = "sandbox,swap-case"; + #emul-cells = <0>; }; };

At present this property is a phandle but does not have a #xxx-cells property to match it. Add one so that is works the same as gpio and clock phandles.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
arch/sandbox/dts/sandbox.dtsi | 6 +++++- doc/driver-model/pci-info.rst | 1 + 2 files changed, 6 insertions(+), 1 deletion(-)
Applied to u-boot-dm, thanks!

Some of these do not follow the rules. Make sure the driver name matches the compatible string in all cases.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: - New patch
arch/sandbox/dts/test.dts | 4 ++-- drivers/i2c/i2c-emul-uclass.c | 4 ++-- drivers/rtc/i2c_rtc_emul.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index e95f4631bf2..b8d3be543c9 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -558,10 +558,10 @@ sandbox,size = <256>; }; emul0: emul0 { - compatible = "sandbox,i2c-rtc"; + compatible = "sandbox,i2c-rtc-emul"; }; emul1: emull { - compatible = "sandbox,i2c-rtc"; + compatible = "sandbox,i2c-rtc-emul"; }; };
diff --git a/drivers/i2c/i2c-emul-uclass.c b/drivers/i2c/i2c-emul-uclass.c index 085b824e895..75d79883380 100644 --- a/drivers/i2c/i2c-emul-uclass.c +++ b/drivers/i2c/i2c-emul-uclass.c @@ -85,8 +85,8 @@ static const struct udevice_id i2c_emul_parent_ids[] = { { } };
-U_BOOT_DRIVER(i2c_emul_parent_drv) = { - .name = "i2c_emul_parent_drv", +U_BOOT_DRIVER(sandbox_i2c_emul_parent) = { + .name = "sandbox_i2c_emul_parent", .id = UCLASS_I2C_EMUL_PARENT, .of_match = i2c_emul_parent_ids, }; diff --git a/drivers/rtc/i2c_rtc_emul.c b/drivers/rtc/i2c_rtc_emul.c index f25b976e54d..fdc885c518b 100644 --- a/drivers/rtc/i2c_rtc_emul.c +++ b/drivers/rtc/i2c_rtc_emul.c @@ -223,7 +223,7 @@ static int sandbox_i2c_rtc_bind(struct udevice *dev) }
static const struct udevice_id sandbox_i2c_rtc_ids[] = { - { .compatible = "sandbox,i2c-rtc" }, + { .compatible = "sandbox,i2c-rtc-emul" }, { } };

Some of these do not follow the rules. Make sure the driver name matches the compatible string in all cases.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: - New patch
arch/sandbox/dts/test.dts | 4 ++-- drivers/i2c/i2c-emul-uclass.c | 4 ++-- drivers/rtc/i2c_rtc_emul.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-)
Applied to u-boot-dm, thanks!

For now dtoc only supports a hard-coded list of phandle properties, to avoid any situation where it makes a mistake in its determination.
Make this into a constant dict, recording both the phandle property name and the associated #cells property in the target node. This makes it easier to find and modify.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: - New patch
tools/dtoc/dtb_platdata.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-)
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index befe7c14901..ca2e55fa526 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -52,6 +52,20 @@ TYPE_NAMES = { STRUCT_PREFIX = 'dtd_' VAL_PREFIX = 'dtv_'
+# Properties which are considered to be phandles +# key: property name +# value: name of associated #cells property in the target node +# +# New phandle properties must be added here; otherwise they will come through as +# simple integers and finding devices by phandle will not work. +# Any property that ends with one of these (e.g. 'cd-gpios') will be considered +# a phandle property. +PHANDLE_PROPS = { + 'clocks': '#clock-cells', + 'gpios': '#gpio-cells', + 'sandbox,emul': '#emul-cells', + } + class Ftype(IntEnum): SOURCE, HEADER = range(2)
@@ -290,7 +304,11 @@ class DtbPlatdata(): ValueError: if the phandle cannot be parsed or the required property is not present """ - if prop.name in ['clocks', 'cd-gpios']: + cells_prop = None + for name, cprop in PHANDLE_PROPS.items(): + if prop.name.endswith(name): + cells_prop = cprop + if cells_prop: if not isinstance(prop.value, list): prop.value = [prop.value] val = prop.value @@ -310,14 +328,10 @@ class DtbPlatdata(): if not target: raise ValueError("Cannot parse '%s' in node '%s'" % (prop.name, node_name)) - cells = None - for prop_name in ['#clock-cells', '#gpio-cells']: - cells = target.props.get(prop_name) - if cells: - break + cells = target.props.get(cells_prop) if not cells: raise ValueError("Node '%s' has no cells property" % - (target.name)) + target.name) num_args = fdt_util.fdt32_to_cpu(cells.value) max_args = max(max_args, num_args) args.append(num_args)

For now dtoc only supports a hard-coded list of phandle properties, to avoid any situation where it makes a mistake in its determination.
Make this into a constant dict, recording both the phandle property name and the associated #cells property in the target node. This makes it easier to find and modify.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: - New patch
tools/dtoc/dtb_platdata.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-)
Applied to u-boot-dm, thanks!

Add a summary to the top of the generated code, to make it easier to see what the file contains.
Also add a tab to .plat so that its value lines up with the others.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 20 +++- tools/dtoc/test_dtoc.py | 184 ++++++++++++++++++++++++++++++------- 2 files changed, 169 insertions(+), 35 deletions(-)
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index ca2e55fa526..ab26c4adca5 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -639,7 +639,7 @@ class DtbPlatdata(): """ self.buf('U_BOOT_DRVINFO(%s) = {\n' % node.var_name) self.buf('\t.name\t\t= "%s",\n' % node.struct_name) - self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, node.var_name)) + self.buf('\t.plat\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name)) self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, node.var_name)) idx = -1 @@ -860,8 +860,22 @@ class DtbPlatdata(): self.out('#include <dt-structs.h>\n') self.out('\n')
- for node in self._valid_nodes: - self.output_node_plat(node) + if self._valid_nodes: + self.out('/*\n') + self.out( + " * driver_info declarations, ordered by 'struct driver_info' linker_list idx:\n") + self.out(' *\n') + self.out(' * idx %-20s %-s\n' % ('driver_info', 'driver')) + self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20)) + for node in self._valid_nodes: + self.out(' * %3d: %-20s %-s\n' % + (node.idx, node.var_name, node.struct_name)) + self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20)) + self.out(' */\n') + self.out('\n') + + for node in self._valid_nodes: + self.output_node_plat(node)
self.out(''.join(self.get_buf()))
diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index c6e33d34227..56d5c8d6b38 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -292,6 +292,19 @@ struct dtd_sandbox_spl_test { }; ''' platdata_text = C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: i2c_at_0 sandbox_i2c + * 1: pmic_at_9 sandbox_pmic + * 2: spl_test sandbox_spl_test + * 3: spl_test2 sandbox_spl_test + * 4: spl_test3 sandbox_spl_test + * --- -------------------- -------------------- + */ + /* * Node /i2c@0 index 0 * driver sandbox_i2c parent None @@ -300,7 +313,7 @@ static struct dtd_sandbox_i2c dtv_i2c_at_0 = { }; U_BOOT_DRVINFO(i2c_at_0) = { \t.name\t\t= "sandbox_i2c", -\t.plat\t= &dtv_i2c_at_0, +\t.plat\t\t= &dtv_i2c_at_0, \t.plat_size\t= sizeof(dtv_i2c_at_0), \t.parent_idx\t= -1, }; @@ -315,7 +328,7 @@ static struct dtd_sandbox_pmic dtv_pmic_at_9 = { }; U_BOOT_DRVINFO(pmic_at_9) = { \t.name\t\t= "sandbox_pmic", -\t.plat\t= &dtv_pmic_at_9, +\t.plat\t\t= &dtv_pmic_at_9, \t.plat_size\t= sizeof(dtv_pmic_at_9), \t.parent_idx\t= 0, }; @@ -338,7 +351,7 @@ static struct dtd_sandbox_spl_test dtv_spl_test = { }; U_BOOT_DRVINFO(spl_test) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test, +\t.plat\t\t= &dtv_spl_test, \t.plat_size\t= sizeof(dtv_spl_test), \t.parent_idx\t= -1, }; @@ -360,7 +373,7 @@ static struct dtd_sandbox_spl_test dtv_spl_test2 = { }; U_BOOT_DRVINFO(spl_test2) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test2, +\t.plat\t\t= &dtv_spl_test2, \t.plat_size\t= sizeof(dtv_spl_test2), \t.parent_idx\t= -1, }; @@ -376,7 +389,7 @@ static struct dtd_sandbox_spl_test dtv_spl_test3 = { }; U_BOOT_DRVINFO(spl_test3) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test3, +\t.plat\t\t= &dtv_spl_test3, \t.plat_size\t= sizeof(dtv_spl_test3), \t.parent_idx\t= -1, }; @@ -430,6 +443,15 @@ struct dtd_sandbox_gpio { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: gpios_at_0 sandbox_gpio + * --- -------------------- -------------------- + */ + /* * Node /gpios@0 index 0 * driver sandbox_gpio parent None @@ -441,7 +463,7 @@ static struct dtd_sandbox_gpio dtv_gpios_at_0 = { }; U_BOOT_DRVINFO(gpios_at_0) = { \t.name\t\t= "sandbox_gpio", -\t.plat\t= &dtv_gpios_at_0, +\t.plat\t\t= &dtv_gpios_at_0, \t.plat_size\t= sizeof(dtv_gpios_at_0), \t.parent_idx\t= -1, }; @@ -470,12 +492,21 @@ struct dtd_invalid { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: spl_test invalid + * --- -------------------- -------------------- + */ + /* Node /spl-test index 0 */ static struct dtd_invalid dtv_spl_test = { }; U_BOOT_DRVINFO(spl_test) = { \t.name\t\t= "invalid", -\t.plat\t= &dtv_spl_test, +\t.plat\t\t= &dtv_spl_test, \t.plat_size\t= sizeof(dtv_spl_test), \t.parent_idx\t= -1, }; @@ -502,13 +533,26 @@ struct dtd_target { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: phandle2_target target + * 1: phandle3_target target + * 2: phandle_source source + * 3: phandle_source2 source + * 4: phandle_target target + * --- -------------------- -------------------- + */ + /* Node /phandle2-target index 0 */ static struct dtd_target dtv_phandle2_target = { \t.intval\t\t\t= 0x1, }; U_BOOT_DRVINFO(phandle2_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle2_target, +\t.plat\t\t= &dtv_phandle2_target, \t.plat_size\t= sizeof(dtv_phandle2_target), \t.parent_idx\t= -1, }; @@ -519,7 +563,7 @@ static struct dtd_target dtv_phandle3_target = { }; U_BOOT_DRVINFO(phandle3_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle3_target, +\t.plat\t\t= &dtv_phandle3_target, \t.plat_size\t= sizeof(dtv_phandle3_target), \t.parent_idx\t= -1, }; @@ -534,7 +578,7 @@ static struct dtd_source dtv_phandle_source = { }; U_BOOT_DRVINFO(phandle_source) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source, +\t.plat\t\t= &dtv_phandle_source, \t.plat_size\t= sizeof(dtv_phandle_source), \t.parent_idx\t= -1, }; @@ -546,7 +590,7 @@ static struct dtd_source dtv_phandle_source2 = { }; U_BOOT_DRVINFO(phandle_source2) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source2, +\t.plat\t\t= &dtv_phandle_source2, \t.plat_size\t= sizeof(dtv_phandle_source2), \t.parent_idx\t= -1, }; @@ -557,7 +601,7 @@ static struct dtd_target dtv_phandle_target = { }; U_BOOT_DRVINFO(phandle_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle_target, +\t.plat\t\t= &dtv_phandle_target, \t.plat_size\t= sizeof(dtv_phandle_target), \t.parent_idx\t= -1, }; @@ -588,6 +632,16 @@ struct dtd_target { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: phandle_source2 source + * 1: phandle_target target + * --- -------------------- -------------------- + */ + /* Node /phandle-source2 index 0 */ static struct dtd_source dtv_phandle_source2 = { \t.clocks\t\t\t= { @@ -595,7 +649,7 @@ static struct dtd_source dtv_phandle_source2 = { }; U_BOOT_DRVINFO(phandle_source2) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source2, +\t.plat\t\t= &dtv_phandle_source2, \t.plat_size\t= sizeof(dtv_phandle_source2), \t.parent_idx\t= -1, }; @@ -605,7 +659,7 @@ static struct dtd_target dtv_phandle_target = { }; U_BOOT_DRVINFO(phandle_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle_target, +\t.plat\t\t= &dtv_phandle_target, \t.plat_size\t= sizeof(dtv_phandle_target), \t.parent_idx\t= -1, }; @@ -622,13 +676,26 @@ U_BOOT_DRVINFO(phandle_target) = { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: phandle2_target target + * 1: phandle3_target target + * 2: phandle_source source + * 3: phandle_source2 source + * 4: phandle_target target + * --- -------------------- -------------------- + */ + /* Node /phandle2-target index 0 */ static struct dtd_target dtv_phandle2_target = { \t.intval\t\t\t= 0x1, }; U_BOOT_DRVINFO(phandle2_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle2_target, +\t.plat\t\t= &dtv_phandle2_target, \t.plat_size\t= sizeof(dtv_phandle2_target), \t.parent_idx\t= -1, }; @@ -639,7 +706,7 @@ static struct dtd_target dtv_phandle3_target = { }; U_BOOT_DRVINFO(phandle3_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle3_target, +\t.plat\t\t= &dtv_phandle3_target, \t.plat_size\t= sizeof(dtv_phandle3_target), \t.parent_idx\t= -1, }; @@ -654,7 +721,7 @@ static struct dtd_source dtv_phandle_source = { }; U_BOOT_DRVINFO(phandle_source) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source, +\t.plat\t\t= &dtv_phandle_source, \t.plat_size\t= sizeof(dtv_phandle_source), \t.parent_idx\t= -1, }; @@ -666,7 +733,7 @@ static struct dtd_source dtv_phandle_source2 = { }; U_BOOT_DRVINFO(phandle_source2) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source2, +\t.plat\t\t= &dtv_phandle_source2, \t.plat_size\t= sizeof(dtv_phandle_source2), \t.parent_idx\t= -1, }; @@ -677,7 +744,7 @@ static struct dtd_target dtv_phandle_target = { }; U_BOOT_DRVINFO(phandle_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle_target, +\t.plat\t\t= &dtv_phandle_target, \t.plat_size\t= sizeof(dtv_phandle_target), \t.parent_idx\t= -1, }; @@ -727,13 +794,24 @@ struct dtd_test3 { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: test1 test1 + * 1: test2 test2 + * 2: test3 test3 + * --- -------------------- -------------------- + */ + /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x5678}, }; U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", -\t.plat\t= &dtv_test1, +\t.plat\t\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), \t.parent_idx\t= -1, }; @@ -744,7 +822,7 @@ static struct dtd_test2 dtv_test2 = { }; U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", -\t.plat\t= &dtv_test2, +\t.plat\t\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), \t.parent_idx\t= -1, }; @@ -755,7 +833,7 @@ static struct dtd_test3 dtv_test3 = { }; U_BOOT_DRVINFO(test3) = { \t.name\t\t= "test3", -\t.plat\t= &dtv_test3, +\t.plat\t\t= &dtv_test3, \t.plat_size\t= sizeof(dtv_test3), \t.parent_idx\t= -1, }; @@ -782,13 +860,23 @@ struct dtd_test2 { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: test1 test1 + * 1: test2 test2 + * --- -------------------- -------------------- + */ + /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x5678}, }; U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", -\t.plat\t= &dtv_test1, +\t.plat\t\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), \t.parent_idx\t= -1, }; @@ -799,7 +887,7 @@ static struct dtd_test2 dtv_test2 = { }; U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", -\t.plat\t= &dtv_test2, +\t.plat\t\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), \t.parent_idx\t= -1, }; @@ -829,13 +917,24 @@ struct dtd_test3 { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: test1 test1 + * 1: test2 test2 + * 2: test3 test3 + * --- -------------------- -------------------- + */ + /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x123400000000, 0x5678}, }; U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", -\t.plat\t= &dtv_test1, +\t.plat\t\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), \t.parent_idx\t= -1, }; @@ -846,7 +945,7 @@ static struct dtd_test2 dtv_test2 = { }; U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", -\t.plat\t= &dtv_test2, +\t.plat\t\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), \t.parent_idx\t= -1, }; @@ -857,7 +956,7 @@ static struct dtd_test3 dtv_test3 = { }; U_BOOT_DRVINFO(test3) = { \t.name\t\t= "test3", -\t.plat\t= &dtv_test3, +\t.plat\t\t= &dtv_test3, \t.plat_size\t= sizeof(dtv_test3), \t.parent_idx\t= -1, }; @@ -887,13 +986,24 @@ struct dtd_test3 { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: test1 test1 + * 1: test2 test2 + * 2: test3 test3 + * --- -------------------- -------------------- + */ + /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x567800000000}, }; U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", -\t.plat\t= &dtv_test1, +\t.plat\t\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), \t.parent_idx\t= -1, }; @@ -904,7 +1014,7 @@ static struct dtd_test2 dtv_test2 = { }; U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", -\t.plat\t= &dtv_test2, +\t.plat\t\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), \t.parent_idx\t= -1, }; @@ -915,7 +1025,7 @@ static struct dtd_test3 dtv_test3 = { }; U_BOOT_DRVINFO(test3) = { \t.name\t\t= "test3", -\t.plat\t= &dtv_test3, +\t.plat\t\t= &dtv_test3, \t.plat_size\t= sizeof(dtv_test3), \t.parent_idx\t= -1, }; @@ -961,6 +1071,16 @@ struct dtd_sandbox_spl_test { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: spl_test sandbox_spl_test + * 1: spl_test2 sandbox_spl_test + * --- -------------------- -------------------- + */ + /* * Node /spl-test index 0 * driver sandbox_spl_test parent None @@ -970,7 +1090,7 @@ static struct dtd_sandbox_spl_test dtv_spl_test = { }; U_BOOT_DRVINFO(spl_test) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test, +\t.plat\t\t= &dtv_spl_test, \t.plat_size\t= sizeof(dtv_spl_test), \t.parent_idx\t= -1, }; @@ -984,7 +1104,7 @@ static struct dtd_sandbox_spl_test dtv_spl_test2 = { }; U_BOOT_DRVINFO(spl_test2) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test2, +\t.plat\t\t= &dtv_spl_test2, \t.plat_size\t= sizeof(dtv_spl_test2), \t.parent_idx\t= -1, };

Add a summary to the top of the generated code, to make it easier to see what the file contains.
Also add a tab to .plat so that its value lines up with the others.
Signed-off-by: Simon Glass sjg@chromium.org ---
(no changes since v1)
tools/dtoc/dtb_platdata.py | 20 +++- tools/dtoc/test_dtoc.py | 184 ++++++++++++++++++++++++++++++------- 2 files changed, 169 insertions(+), 35 deletions(-)
Applied to u-boot-dm, thanks!

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 ---
(no changes since v1)
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 fe1cc2e278c..30f71edbd96 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 ab26c4adca5..6dadf37582f 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -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)'), }
diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 56d5c8d6b38..053d140664e 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 { @@ -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())

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 ---
(no changes since v1)
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(-)
Applied to u-boot-dm, thanks!

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.
Add another tab to the driver_info output so it lines up nicely like the device-instance output.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: - Drop patches previously applied - Update cover letter - Fix the naming for uclass_plat_name so it is different from uclass_priv - Tidy up tabbing to make the code output line up better - Add a summary to the top of the generated file
drivers/misc/test_drv.c | 4 + tools/dtoc/dtb_platdata.py | 168 ++++++++++++++++++- tools/dtoc/test_dtoc.py | 327 +++++++++++++++++++++++++++++++++++-- 3 files changed, 484 insertions(+), 15 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 6dadf37582f..f5b5ad5f71a 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -671,7 +671,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 + '_uc', node.var_name, 'plat') + 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\t= %s,\n' % plat_name) + else: + self.buf('\t.plat_\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name)) + if parent_plat_name: + self.buf('\t.parent_plat_\t= %s,\n' % parent_plat_name) + if uclass_plat_name: + self.buf('\t.uclass_plat_\t= %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\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: @@ -681,7 +778,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): @@ -731,10 +828,13 @@ class DtbPlatdata(): 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') + self.buf( + " * uclass declarations, ordered by 'struct uclass' linker_list idx:\n") uclass_list = self._valid_uclasses + for seq, uclass in enumerate(uclass_list): + self.buf(' * %3d: %s\n' % (seq, uclass.name)) + self.buf(' *\n') + self.buf(' * Sequence numbers allocated in each uclass:\n') for uclass in uclass_list: if uclass.alias_num_to_node: self.buf(' * %s: %s\n' % (uclass.name, uclass.uclass_id)) @@ -914,6 +1014,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
@@ -969,6 +1089,41 @@ 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') + + if self._valid_nodes: + self.out('/*\n') + self.out( + " * udevice declarations, ordered by 'struct udevice' linker_list position:\n") + self.out(' *\n') + self.out(' * idx %-20s %-s\n' % ('udevice', 'driver')) + self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20)) + for node in self._valid_nodes: + self.out(' * %3d: %-20s %-s\n' % + (node.idx, node.var_name, node.struct_name)) + self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20)) + self.out(' */\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 @@ -984,6 +1139,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 053d140664e..bb689f3b6e9 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -420,9 +420,14 @@ U_BOOT_DRVINFO(spl_test3) = { #include <dt-structs.h>
/* - * uclass declarations + * uclass declarations, ordered by 'struct uclass' linker_list idx: + * 0: i2c + * 1: misc + * 2: root + * 3: testbus + * 4: testfdt * - * Sequence numbers: + * Sequence numbers allocated in each uclass: * i2c: UCLASS_I2C * 4: /i2c * misc: UCLASS_MISC @@ -506,6 +511,300 @@ 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> + +/* + * udevice declarations, ordered by 'struct udevice' linker_list position: + * + * idx udevice driver + * --- -------------------- -------------------- + * 0: i2c sandbox_i2c + * 1: root root_driver + * 2: some_bus denx_u_boot_test_bus + * 3: spl_test sandbox_spl_test + * 4: spl_test3 sandbox_spl_test + * 5: test denx_u_boot_fdt_test + * 6: test0 denx_u_boot_fdt_test + * --- -------------------- -------------------- + */ + +/* + * Node /i2c index 0 + * driver sandbox_i2c parent root_driver +*/ +static struct dtd_sandbox_i2c dtv_i2c = { +\t.intval\t\t\t= 0x3, +}; + +#include <asm/i2c.h> +u8 _sandbox_i2c_priv_i2c[sizeof(struct sandbox_i2c_priv)] +\t__attribute__ ((section (".priv_data"))); +#include <i2c.h> +u8 _sandbox_i2c_uc_priv_i2c[sizeof(struct dm_i2c_bus)] +\t__attribute__ ((section (".priv_data"))); + +DM_DEVICE_INST(i2c) = { +\t.driver\t\t= DM_DRIVER_REF(sandbox_i2c), +\t.name\t\t= "sandbox_i2c", +\t.plat_\t\t= &dtv_i2c, +\t.priv_\t\t= _sandbox_i2c_priv_i2c, +\t.uclass\t\t= DM_UCLASS_REF(i2c), +\t.uclass_priv_ = _sandbox_i2c_uc_priv_i2c, +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(i2c)->dev_head, +\t\t.next = &DM_UCLASS_REF(i2c)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(i2c)->child_head, +\t\t.next = &DM_DEVICE_REF(i2c)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(root)->child_head, +\t\t.next = &DM_DEVICE_REF(some_bus)->sibling_node, +\t}, +\t.seq_ = 4, +}; + +/* + * Node / index 1 + * driver root_driver parent None +*/ +static struct dtd_root_driver dtv_root = { +}; + +DM_DEVICE_INST(root) = { +\t.driver\t\t= DM_DRIVER_REF(root_driver), +\t.name\t\t= "root_driver", +\t.plat_\t\t= &dtv_root, +\t.uclass\t\t= DM_UCLASS_REF(root), +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(root)->dev_head, +\t\t.next = &DM_UCLASS_REF(root)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test3)->sibling_node, +\t\t.next = &DM_DEVICE_REF(i2c)->sibling_node, +\t}, +\t.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"))) +\t_denx_u_boot_test_bus_plat_some_bus = { +\t.dtplat = { +\t\t.ping_add\t\t= 0x4, +\t\t.ping_expect\t\t= 0x4, +\t\t.reg\t\t\t= {0x3, 0x1}, +\t}, +}; +#include <dm/test.h> +u8 _denx_u_boot_test_bus_priv_some_bus[sizeof(struct dm_test_priv)] +\t__attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_test_bus_ucplat_some_bus[sizeof(struct dm_test_uclass_priv)] +\t__attribute__ ((section (".priv_data"))); +#include <test.h> + +DM_DEVICE_INST(some_bus) = { +\t.driver\t\t= DM_DRIVER_REF(denx_u_boot_test_bus), +\t.name\t\t= "denx_u_boot_test_bus", +\t.plat_\t\t= &_denx_u_boot_test_bus_plat_some_bus, +\t.uclass_plat_\t= _denx_u_boot_test_bus_ucplat_some_bus, +\t.driver_data\t= DM_TEST_TYPE_FIRST, +\t.priv_\t\t= _denx_u_boot_test_bus_priv_some_bus, +\t.uclass\t\t= DM_UCLASS_REF(testbus), +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(testbus)->dev_head, +\t\t.next = &DM_UCLASS_REF(testbus)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(test0)->sibling_node, +\t\t.next = &DM_DEVICE_REF(test)->sibling_node, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(i2c)->sibling_node, +\t\t.next = &DM_DEVICE_REF(spl_test)->sibling_node, +\t}, +\t.seq_ = 2, +}; + +/* + * Node /spl-test index 3 + * driver sandbox_spl_test parent root_driver +*/ +static struct dtd_sandbox_spl_test dtv_spl_test = { +\t.boolval\t\t= true, +\t.intval\t\t\t= 0x1, +}; + +DM_DEVICE_INST(spl_test) = { +\t.driver\t\t= DM_DRIVER_REF(sandbox_spl_test), +\t.name\t\t= "sandbox_spl_test", +\t.plat_\t\t= &dtv_spl_test, +\t.uclass\t\t= DM_UCLASS_REF(misc), +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(misc)->dev_head, +\t\t.next = &DM_DEVICE_REF(spl_test3)->uclass_node, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test)->child_head, +\t\t.next = &DM_DEVICE_REF(spl_test)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(some_bus)->sibling_node, +\t\t.next = &DM_DEVICE_REF(spl_test3)->sibling_node, +\t}, +\t.seq_ = 0, +}; + +/* + * Node /spl-test3 index 4 + * driver sandbox_spl_test parent root_driver +*/ +static struct dtd_sandbox_spl_test dtv_spl_test3 = { +\t.longbytearray\t\t= {0x90a0b0c, 0xd0e0f10}, +\t.stringarray\t\t= "one", +}; + +DM_DEVICE_INST(spl_test3) = { +\t.driver\t\t= DM_DRIVER_REF(sandbox_spl_test), +\t.name\t\t= "sandbox_spl_test", +\t.plat_\t\t= &dtv_spl_test3, +\t.uclass\t\t= DM_UCLASS_REF(misc), +\t.uclass_node\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test)->uclass_node, +\t\t.next = &DM_UCLASS_REF(misc)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test3)->child_head, +\t\t.next = &DM_DEVICE_REF(spl_test3)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test)->sibling_node, +\t\t.next = &DM_DEVICE_REF(root)->child_head, +\t}, +\t.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"))) +\t_denx_u_boot_fdt_test_plat_test = { +\t.dtplat = { +\t\t.ping_add\t\t= 0x5, +\t\t.ping_expect\t\t= 0x5, +\t\t.reg\t\t\t= {0x5, 0x0}, +\t}, +}; +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_priv_test[sizeof(struct dm_test_priv)] +\t__attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_parent_plat_test[sizeof(struct dm_test_parent_plat)] +\t__attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_parent_priv_test[sizeof(struct dm_test_parent_data)] +\t__attribute__ ((section (".priv_data"))); + +DM_DEVICE_INST(test) = { +\t.driver\t\t= DM_DRIVER_REF(denx_u_boot_fdt_test), +\t.name\t\t= "denx_u_boot_fdt_test", +\t.plat_\t\t= &_denx_u_boot_fdt_test_plat_test, +\t.parent_plat_\t= _denx_u_boot_fdt_test_parent_plat_test, +\t.driver_data\t= DM_TEST_TYPE_FIRST, +\t.parent\t\t= DM_DEVICE_REF(some_bus), +\t.priv_\t\t= _denx_u_boot_fdt_test_priv_test, +\t.uclass\t\t= DM_UCLASS_REF(testfdt), +\t.parent_priv_\t= _denx_u_boot_fdt_test_parent_priv_test, +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(testfdt)->dev_head, +\t\t.next = &DM_DEVICE_REF(test0)->uclass_node, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(test)->child_head, +\t\t.next = &DM_DEVICE_REF(test)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(some_bus)->child_head, +\t\t.next = &DM_DEVICE_REF(test0)->sibling_node, +\t}, +\t.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"))) +\t_denx_u_boot_fdt_test_plat_test0 = { +\t.dtplat = { +\t}, +}; +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_priv_test0[sizeof(struct dm_test_priv)] +\t__attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_parent_plat_test0[sizeof(struct dm_test_parent_plat)] +\t__attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_parent_priv_test0[sizeof(struct dm_test_parent_data)] +\t__attribute__ ((section (".priv_data"))); + +DM_DEVICE_INST(test0) = { +\t.driver\t\t= DM_DRIVER_REF(denx_u_boot_fdt_test), +\t.name\t\t= "denx_u_boot_fdt_test", +\t.plat_\t\t= &_denx_u_boot_fdt_test_plat_test0, +\t.parent_plat_\t= _denx_u_boot_fdt_test_parent_plat_test0, +\t.driver_data\t= DM_TEST_TYPE_SECOND, +\t.parent\t\t= DM_DEVICE_REF(some_bus), +\t.priv_\t\t= _denx_u_boot_fdt_test_priv_test0, +\t.uclass\t\t= DM_UCLASS_REF(testfdt), +\t.parent_priv_\t= _denx_u_boot_fdt_test_parent_priv_test0, +\t.uclass_node\t= { +\t\t.prev = &DM_DEVICE_REF(test)->uclass_node, +\t\t.next = &DM_UCLASS_REF(testfdt)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(test0)->child_head, +\t\t.next = &DM_DEVICE_REF(test0)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(test)->sibling_node, +\t\t.next = &DM_DEVICE_REF(some_bus)->child_head, +\t}, +\t.seq_ = 2, +}; + '''
def test_simple(self): @@ -533,8 +832,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""" @@ -1236,8 +1536,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""" @@ -1253,7 +1554,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): @@ -1281,12 +1582,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): @@ -1498,8 +1799,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')

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.
Add another tab to the driver_info output so it lines up nicely like the device-instance output.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v2: - Drop patches previously applied - Update cover letter - Fix the naming for uclass_plat_name so it is different from uclass_priv - Tidy up tabbing to make the code output line up better - Add a summary to the top of the generated file
drivers/misc/test_drv.c | 4 + tools/dtoc/dtb_platdata.py | 168 ++++++++++++++++++- tools/dtoc/test_dtoc.py | 327 +++++++++++++++++++++++++++++++++++-- 3 files changed, 484 insertions(+), 15 deletions(-)
Applied to u-boot-dm, thanks!
participants (2)
-
Johan Jonker
-
Simon Glass