diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/tools/dtoc | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/tools/dtoc')
38 files changed, 6894 insertions, 0 deletions
diff --git a/roms/u-boot/tools/dtoc/.gitignore b/roms/u-boot/tools/dtoc/.gitignore new file mode 100644 index 000000000..0d20b6487 --- /dev/null +++ b/roms/u-boot/tools/dtoc/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/roms/u-boot/tools/dtoc/dtb_platdata.py b/roms/u-boot/tools/dtoc/dtb_platdata.py new file mode 100644 index 000000000..2d42480a9 --- /dev/null +++ b/roms/u-boot/tools/dtoc/dtb_platdata.py @@ -0,0 +1,1219 @@ +#!/usr/bin/python +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2017 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +"""Device tree to platform data class + +This supports converting device tree data to C structures definitions and +static data. + +See doc/driver-model/of-plat.rst for more informaiton +""" + +import collections +import copy +from enum import IntEnum +import os +import re +import sys + +from dtoc import fdt +from dtoc import fdt_util +from dtoc import src_scan +from dtoc.src_scan import conv_name_to_c + +# When we see these properties we ignore them - i.e. do not create a structure +# member +PROP_IGNORE_LIST = [ + '#address-cells', + '#gpio-cells', + '#size-cells', + 'compatible', + 'linux,phandle', + "status", + 'phandle', + 'u-boot,dm-pre-reloc', + 'u-boot,dm-tpl', + 'u-boot,dm-spl', +] + +# C type declarations for the types we support +TYPE_NAMES = { + fdt.Type.INT: 'fdt32_t', + fdt.Type.BYTE: 'unsigned char', + fdt.Type.STRING: 'const char *', + fdt.Type.BOOL: 'bool', + fdt.Type.INT64: 'fdt64_t', +} + +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) + + +# This holds information about each type of output file dtoc can create +# type: Type of file (Ftype) +# fname: Filename excluding directory, e.g. 'dt-plat.c' +# hdr_comment: Comment explaining the purpose of the file +OutputFile = collections.namedtuple('OutputFile', + ['ftype', 'fname', 'method', 'hdr_comment']) + +# This holds information about a property which includes phandles. +# +# max_args: integer: Maximum number or arguments that any phandle uses (int). +# args: Number of args for each phandle in the property. The total number of +# phandles is len(args). This is a list of integers. +PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args']) + +# Holds a single phandle link, allowing a C struct value to be assigned to point +# to a device +# +# var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node') +# dev_name: Name of device to assign to (e.g. 'clock') +PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name']) + + +def tab_to(num_tabs, line): + """Append tabs to a line of text to reach a tab stop. + + Args: + num_tabs (int): Tab stop to obtain (0 = column 0, 1 = column 8, etc.) + line (str): Line of text to append to + + Returns: + str: line with the correct number of tabs appeneded. If the line already + extends past that tab stop then a single space is appended. + """ + if len(line) >= num_tabs * 8: + return line + ' ' + return line + '\t' * (num_tabs - len(line) // 8) + +def get_value(ftype, value): + """Get a value as a C expression + + For integers this returns a byte-swapped (little-endian) hex string + For bytes this returns a hex string, e.g. 0x12 + For strings this returns a literal string enclosed in quotes + For booleans this return 'true' + + Args: + ftype (fdt.Type): Data type (fdt_util) + value (bytes): Data value, as a string of bytes + + Returns: + str: String representation of the value + """ + if ftype == fdt.Type.INT: + val = '%#x' % fdt_util.fdt32_to_cpu(value) + elif ftype == fdt.Type.BYTE: + char = value[0] + val = '%#x' % (ord(char) if isinstance(char, str) else char) + elif ftype == fdt.Type.STRING: + # Handle evil ACPI backslashes by adding another backslash before them. + # So "\\_SB.GPO0" in the device tree effectively stays like that in C + val = '"%s"' % value.replace('\\', '\\\\') + elif ftype == fdt.Type.BOOL: + val = 'true' + else: # ftype == fdt.Type.INT64: + val = '%#x' % value + return val + + +class DtbPlatdata(): + """Provide a means to convert device tree binary data to platform data + + The output of this process is C structures which can be used in space- + constrained encvironments where the ~3KB code overhead of device tree + code is not affordable. + + Properties: + _scan: Scan object, for scanning and reporting on useful information + 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_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 + _dirname: Directory to hold output files, or None for none (all files + go to stdout) + _struct_data (dict): OrderedDict of dtplat structures to output + key (str): Node name, as a C identifier + value: dict containing structure fields: + 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 + _instantiate: Instantiate devices so they don't need to be bound at + run-time + """ + def __init__(self, scan, dtb_fname, include_disabled, instantiate=False): + self._scan = scan + 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 = [] + self._dirnames = [None] * len(Ftype) + 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 + + This should be done before setup_output() is called + + Args: + output_dirs (tuple of str): + Directory to use for C output files. + Use None to write files relative current directory + Directory to use for H output files. + Defaults to the C output dir + """ + def process_dir(ftype, dirname): + if dirname: + os.makedirs(dirname, exist_ok=True) + self._dirnames[ftype] = dirname + + if output_dirs: + c_dirname = output_dirs[0] + h_dirname = output_dirs[1] if len(output_dirs) > 1 else c_dirname + process_dir(Ftype.SOURCE, c_dirname) + process_dir(Ftype.HEADER, h_dirname) + + def setup_output(self, ftype, fname): + """Set up the output destination + + Once this is done, future calls to self.out() will output to this + file. The file used is as follows: + + self._dirnames[ftype] is None: output to fname, or stdout if None + self._dirnames[ftype] is not None: output to fname in that directory + + Calling this function multiple times will close the old file and open + the new one. If they are the same file, nothing happens and output will + continue to the same file. + + Args: + ftype (str): Type of file to create ('c' or 'h') + fname (str): Filename to send output to. If there is a directory in + self._dirnames for this file type, it will be put in that + directory + """ + dirname = self._dirnames[ftype] + if dirname: + pathname = os.path.join(dirname, fname) + if self._outfile: + self._outfile.close() + self._outfile = open(pathname, 'w') + elif fname: + if not self._outfile: + self._outfile = open(fname, 'w') + else: + self._outfile = sys.stdout + + def finish_output(self): + """Finish outputing to a file + + This closes the output file, if one is in use + """ + if self._outfile != sys.stdout: + self._outfile.close() + self._outfile = None + + def out(self, line): + """Output a string to the output file + + Args: + line (str): String to output + """ + self._outfile.write(line) + + def buf(self, line): + """Buffer up a string to send later + + Args: + line (str): String to add to our 'buffer' list + """ + self._lines.append(line) + + def get_buf(self): + """Get the contents of the output buffer, and clear it + + Returns: + list(str): The output buffer, which is then cleared for future use + """ + lines = self._lines + self._lines = [] + return lines + + def out_header(self, outfile): + """Output a message indicating that this is an auto-generated file + + Args: + outfile: OutputFile describing the file being generated + """ + self.out('''/* + * DO NOT MODIFY + * + * %s. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ + +''' % outfile.hdr_comment) + + def get_phandle_argc(self, prop, node_name): + """Check if a node contains phandles + + We have no reliable way of detecting whether a node uses a phandle + or not. As an interim measure, use a list of known property names. + + Args: + prop (fdt.Prop): Prop object to check + node_name (str): Node name, only used for raising an error + Returns: + int or None: Number of argument cells is this is a phandle, + else None + Raises: + ValueError: if the phandle cannot be parsed or the required property + is not present + """ + 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 + i = 0 + + max_args = 0 + args = [] + while i < len(val): + phandle = fdt_util.fdt32_to_cpu(val[i]) + # If we get to the end of the list, stop. This can happen + # since some nodes have more phandles in the list than others, + # but we allocate enough space for the largest list. So those + # nodes with shorter lists end up with zeroes at the end. + if not phandle: + break + target = self._fdt.phandle_to_node.get(phandle) + if not target: + raise ValueError("Cannot parse '%s' in node '%s'" % + (prop.name, node_name)) + cells = target.props.get(cells_prop) + if not cells: + raise ValueError("Node '%s' has no cells property" % + target.name) + num_args = fdt_util.fdt32_to_cpu(cells.value) + max_args = max(max_args, num_args) + args.append(num_args) + i += 1 + num_args + return PhandleInfo(max_args, args) + return None + + def scan_dtb(self): + """Scan the device tree to obtain a tree of nodes and properties + + Once this is done, self._fdt.GetRoot() can be called to obtain the + device tree root node, and progress from there. + """ + self._fdt = fdt.FdtScan(self._dtb_fname) + + def scan_node(self, node, valid_nodes): + """Scan a node and subnodes to build a tree of node and phandle info + + This adds each subnode to self._valid_nodes if it is enabled and has a + compatible string. + + Args: + node (Node): Node for scan for subnodes + valid_nodes (list of Node): List of Node objects to add to + """ + 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(subnode) + + # recurse to handle any subnodes + self.scan_node(subnode, valid_nodes) + + 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, + 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): + """Get the number of cells in addresses and sizes for this node + + Args: + node (fdt.None): Node to check + + Returns: + Tuple: + Number of address cells for this node + Number of size cells for this node + """ + parent = node.parent + if parent and not parent.props: + raise ValueError("Parent node '%s' has no properties - do you need u-boot,dm-spl or similar?" % + parent.path) + num_addr, num_size = 2, 2 + if parent: + addr_prop = parent.props.get('#address-cells') + size_prop = parent.props.get('#size-cells') + if addr_prop: + num_addr = fdt_util.fdt32_to_cpu(addr_prop.value) + if size_prop: + num_size = fdt_util.fdt32_to_cpu(size_prop.value) + return num_addr, num_size + + def scan_reg_sizes(self): + """Scan for 64-bit 'reg' properties and update the values + + This finds 'reg' properties with 64-bit data and converts the value to + an array of 64-values. This allows it to be output in a way that the + C code can read. + """ + for node in self._valid_nodes: + reg = node.props.get('reg') + if not reg: + continue + num_addr, num_size = self.get_num_cells(node) + total = num_addr + num_size + + if reg.type != fdt.Type.INT: + raise ValueError("Node '%s' reg property is not an int" % + node.name) + if not isinstance(reg.value, list): + reg.value = [reg.value] + if len(reg.value) % total: + raise ValueError( + "Node '%s' (parent '%s') reg property has %d cells " + 'which is not a multiple of na + ns = %d + %d)' % + (node.name, node.parent.name, len(reg.value), num_addr, + num_size)) + reg.num_addr = num_addr + reg.num_size = num_size + if num_addr > 1 or num_size > 1: + reg.type = fdt.Type.INT64 + i = 0 + new_value = [] + val = reg.value + while i < len(val): + addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr) + i += num_addr + size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size) + i += num_size + new_value += [addr, size] + reg.value = new_value + + def scan_structs(self): + """Scan the device tree building up the C structures we will use. + + Build a dict keyed by C struct name containing a dict of Prop + object for each struct field (keyed by property name). Where the + same struct appears multiple times, try to use the 'widest' + property, i.e. the one with a type which can express all others. + + Once the widest property is determined, all other properties are + updated to match that width. + + The results are written to self._struct_data + """ + structs = self._struct_data + for node in self._valid_nodes: + fields = {} + + # Get a list of all the valid properties in this node. + for name, prop in node.props.items(): + if name not in PROP_IGNORE_LIST and name[0] != '#': + fields[name] = copy.deepcopy(prop) + + # 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: + oldprop.Widen(prop) + else: + struct[name] = prop + + # Otherwise store this as a new struct. + else: + structs[node.struct_name] = fields + + for node in self._valid_nodes: + 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]) + + def scan_phandles(self): + """Figure out what phandles each node uses + + We need to be careful when outputing nodes that use phandles since + they must come after the declaration of the phandles in the C file. + Otherwise we get a compiler error since the phandle struct is not yet + declared. + + This function adds to each node a list of phandle nodes that the node + depends on. This allows us to output things in the right order. + """ + for node in self._valid_nodes: + node.phandles = set() + for pname, prop in node.props.items(): + if pname in PROP_IGNORE_LIST or pname[0] == '#': + continue + info = self.get_phandle_argc(prop, node.name) + if info: + # Process the list as pairs of (phandle, id) + pos = 0 + for args in info.args: + phandle_cell = prop.value[pos] + phandle = fdt_util.fdt32_to_cpu(phandle_cell) + target_node = self._fdt.phandle_to_node[phandle] + node.phandles.add(target_node) + pos += 1 + args + + + def generate_structs(self): + """Generate struct defintions for the platform data + + This writes out the body of a header file consisting of structure + definitions for node in self._valid_nodes. See the documentation in + doc/driver-model/of-plat.rst for more information. + """ + structs = self._struct_data + self.out('#include <stdbool.h>\n') + self.out('#include <linux/libfdt.h>\n') + + # Output the struct definition + for name in sorted(structs): + self.out('struct %s%s {\n' % (STRUCT_PREFIX, name)) + for pname in sorted(structs[name]): + prop = structs[name][pname] + info = self.get_phandle_argc(prop, structs[name]) + if info: + # For phandles, include a reference to the target + struct_name = 'struct phandle_%d_arg' % info.max_args + self.out('\t%s%s[%d]' % (tab_to(2, struct_name), + conv_name_to_c(prop.name), + len(info.args))) + else: + ptype = TYPE_NAMES[prop.type] + self.out('\t%s%s' % (tab_to(2, ptype), + conv_name_to_c(prop.name))) + if isinstance(prop.value, list): + self.out('[%d]' % len(prop.value)) + self.out(';\n') + self.out('};\n') + + def _output_list(self, node, prop): + """Output the C code for a devicetree property that holds a list + + Args: + node (fdt.Node): Node to output + prop (fdt.Prop): Prop to output + """ + self.buf('{') + vals = [] + # For phandles, output a reference to the platform data + # of the target node. + info = self.get_phandle_argc(prop, node.name) + if info: + # Process the list as pairs of (phandle, id) + pos = 0 + for args in info.args: + phandle_cell = prop.value[pos] + phandle = fdt_util.fdt32_to_cpu(phandle_cell) + target_node = self._fdt.phandle_to_node[phandle] + arg_values = [] + for i in range(args): + arg_values.append( + str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i]))) + pos += 1 + args + vals.append('\t{%d, {%s}}' % (target_node.idx, + ', '.join(arg_values))) + for val in vals: + self.buf('\n\t\t%s,' % val) + else: + for val in prop.value: + vals.append(get_value(prop.type, val)) + + # Put 8 values per line to avoid very long lines. + for i in range(0, len(vals), 8): + if i: + self.buf(',\n\t\t') + self.buf(', '.join(vals[i:i + 8])) + self.buf('}') + + def _declare_device(self, node): + """Add a device declaration to the output + + This declares a U_BOOT_DRVINFO() for the device being processed + + Args: + node: Node to process + """ + 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\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 + self.buf('\t.parent_idx\t= %d,\n' % idx) + 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 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: + node (Node): Node being output + prop (Prop): Prop object to output + """ + if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#': + return + member_name = conv_name_to_c(prop.name) + self.buf('%s%s= ' % ('\t' * tabs, tab_to(3, '.' + member_name))) + + # Special handling for lists + if isinstance(prop.value, list): + self._output_list(node, prop) + else: + self.buf(get_value(prop.type, prop.value)) + self.buf(',\n') + + def _output_values(self, node): + """Output the definition of a device's struct values + + Args: + node (Node): Node to output + """ + self.buf('static struct %s%s %s%s = {\n' % + (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') + + 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): + 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, 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)) + 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 + + 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 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('extern U_BOOT_DRIVER(%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('extern DM_DEVICE_INST(%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('extern UCLASS_DRIVER(%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('extern DM_UCLASS_INST(%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: + 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) + + # 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) + if not driver: + if not need_drivers: + continue + 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) + 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 + + 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_plat(self, node): + """Output the C code for a node + + Args: + node (fdt.Node): node to output + """ + 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 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 generate_plat(self): + """Generate device defintions for the platform data + + This writes out C platform data initialisation data and + U_BOOT_DRVINFO() declarations for each valid node. Where a node has + multiple compatible strings, a #define is used to make them equivalent. + + See the documentation in doc/driver-model/of-plat.rst for more + information. + """ + self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n') + self.out('#define DT_PLAT_C\n') + 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') + + 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())) + + 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. + """ + 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 +# value: OutputFile for this command +OUTPUT_FILES_COMMON = { + '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, + 'Defines the structs used to hold devicetree data'), + } + +# File generated without instantiate +OUTPUT_FILES_NOINST = { + 'platdata': + OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat, + 'Declares the U_BOOT_DRIVER() records and platform data'), + } + +# File generated with instantiate +OUTPUT_FILES_INST = { + '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)'), + } + + +def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, + instantiate, warning_disabled=False, drivers_additional=None, + basedir=None, scan=None): + """Run all the steps of the dtoc tool + + Args: + args (list): List of non-option arguments provided to the problem + dtb_file (str): Filename of dtb file to process + include_disabled (bool): True to include disabled nodes + output (str): Name of output file (None for stdout) + 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 + 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 + 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 + + Returns: + DtbPlatdata object + + Raises: + ValueError: if args has no command, or an unknown command + """ + if not args: + raise ValueError('Please specify a command: struct, platdata, all') + if output and output_dirs and any(output_dirs): + raise ValueError('Must specify either output or output_dirs, not both') + + if not scan: + scan = src_scan.Scanner(basedir, drivers_additional, phase) + scan.scan_drivers() + do_process = True + else: + do_process = False + plat = DtbPlatdata(scan, dtb_file, include_disabled, instantiate) + plat.scan_dtb() + 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(instantiate) + plat.read_aliases() + plat.assign_seqs() + + # Figure out what output files we plan to generate + output_files = dict(OUTPUT_FILES_COMMON) + if instantiate: + output_files.update(OUTPUT_FILES_INST) + else: + output_files.update(OUTPUT_FILES_NOINST) + + cmds = args[0].split(',') + if 'all' in cmds: + cmds = sorted(output_files.keys()) + for cmd in cmds: + outfile = output_files.get(cmd) + if not outfile: + raise ValueError("Unknown command '%s': (use: %s)" % + (cmd, ', '.join(sorted(output_files.keys())))) + plat.setup_output(outfile.ftype, + outfile.fname if output_dirs else output) + plat.out_header(outfile) + outfile.method(plat) + plat.finish_output() + + if not warning_disabled: + scan.show_warnings() + return plat diff --git a/roms/u-boot/tools/dtoc/dtoc b/roms/u-boot/tools/dtoc/dtoc new file mode 120000 index 000000000..11a5d8e18 --- /dev/null +++ b/roms/u-boot/tools/dtoc/dtoc @@ -0,0 +1 @@ +main.py
\ No newline at end of file diff --git a/roms/u-boot/tools/dtoc/fdt.py b/roms/u-boot/tools/dtoc/fdt.py new file mode 100644 index 000000000..3996971e3 --- /dev/null +++ b/roms/u-boot/tools/dtoc/fdt.py @@ -0,0 +1,804 @@ +#!/usr/bin/python +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +from enum import IntEnum +import struct +import sys + +from dtoc import fdt_util +import libfdt +from libfdt import QUIET_NOTFOUND +from patman import tools + +# This deals with a device tree, presenting it as an assortment of Node and +# Prop objects, representing nodes and properties, respectively. This file +# contains the base classes and defines the high-level API. You can use +# FdtScan() as a convenience function to create and scan an Fdt. + +# This implementation uses a libfdt Python library to access the device tree, +# so it is fairly efficient. + +# A list of types we support +class Type(IntEnum): + (BYTE, INT, STRING, BOOL, INT64) = range(5) + + def is_wider_than(self, other): + """Check if another type is 'wider' than this one + + A wider type is one that holds more information than an earlier one, + similar to the concept of type-widening in C. + + This uses a simple arithmetic comparison, since type values are in order + from narrowest (BYTE) to widest (INT64). + + Args: + other: Other type to compare against + + Return: + True if the other type is wider + """ + return self.value > other.value + +def CheckErr(errnum, msg): + if errnum: + raise ValueError('Error %d: %s: %s' % + (errnum, libfdt.fdt_strerror(errnum), msg)) + + +def BytesToValue(data): + """Converts a string of bytes into a type and value + + Args: + A bytes value (which on Python 2 is an alias for str) + + Return: + A tuple: + Type of data + Data, either a single element or a list of elements. Each element + is one of: + Type.STRING: str/bytes value from the property + Type.INT: a byte-swapped integer stored as a 4-byte str/bytes + Type.BYTE: a byte stored as a single-byte str/bytes + """ + data = bytes(data) + size = len(data) + strings = data.split(b'\0') + is_string = True + count = len(strings) - 1 + if count > 0 and not len(strings[-1]): + for string in strings[:-1]: + if not string: + is_string = False + break + for ch in string: + if ch < 32 or ch > 127: + is_string = False + break + else: + is_string = False + if is_string: + if count == 1: + return Type.STRING, strings[0].decode() + else: + return Type.STRING, [s.decode() for s in strings[:-1]] + if size % 4: + if size == 1: + return Type.BYTE, chr(data[0]) + else: + return Type.BYTE, [chr(ch) for ch in list(data)] + val = [] + for i in range(0, size, 4): + val.append(data[i:i + 4]) + if size == 4: + return Type.INT, val[0] + else: + return Type.INT, val + + +class Prop: + """A device tree property + + Properties: + node: Node containing this property + offset: Offset of the property (None if still to be synced) + name: Property name (as per the device tree) + value: Property value as a string of bytes, or a list of strings of + bytes + type: Value type + """ + def __init__(self, node, offset, name, data): + self._node = node + self._offset = offset + self.name = name + self.value = None + self.bytes = bytes(data) + self.dirty = offset is None + if not data: + self.type = Type.BOOL + self.value = True + return + self.type, self.value = BytesToValue(bytes(data)) + + def RefreshOffset(self, poffset): + self._offset = poffset + + def Widen(self, newprop): + """Figure out which property type is more general + + Given a current property and a new property, this function returns the + one that is less specific as to type. The less specific property will + be ble to represent the data in the more specific property. This is + used for things like: + + node1 { + compatible = "fred"; + value = <1>; + }; + node1 { + compatible = "fred"; + value = <1 2>; + }; + + He we want to use an int array for 'value'. The first property + suggests that a single int is enough, but the second one shows that + it is not. Calling this function with these two propertes would + update the current property to be like the second, since it is less + specific. + """ + if self.type.is_wider_than(newprop.type): + if self.type == Type.INT and newprop.type == Type.BYTE: + if type(self.value) == list: + new_value = [] + for val in self.value: + new_value += [chr(by) for by in val] + else: + new_value = [chr(by) for by in self.value] + self.value = new_value + self.type = newprop.type + + if type(newprop.value) == list and type(self.value) != list: + self.value = [self.value] + + if type(self.value) == list and len(newprop.value) > len(self.value): + val = self.GetEmpty(self.type) + while len(self.value) < len(newprop.value): + self.value.append(val) + + @classmethod + def GetEmpty(self, type): + """Get an empty / zero value of the given type + + Returns: + A single value of the given type + """ + if type == Type.BYTE: + return chr(0) + elif type == Type.INT: + return struct.pack('>I', 0); + elif type == Type.STRING: + return '' + else: + return True + + def GetOffset(self): + """Get the offset of a property + + Returns: + The offset of the property (struct fdt_property) within the file + """ + self._node._fdt.CheckCache() + return self._node._fdt.GetStructOffset(self._offset) + + def SetInt(self, val): + """Set the integer value of the property + + The device tree is marked dirty so that the value will be written to + the block on the next sync. + + Args: + val: Integer value (32-bit, single cell) + """ + self.bytes = struct.pack('>I', val); + self.value = self.bytes + self.type = Type.INT + self.dirty = True + + def SetData(self, bytes): + """Set the value of a property as bytes + + Args: + bytes: New property value to set + """ + self.bytes = bytes + self.type, self.value = BytesToValue(bytes) + self.dirty = True + + def Sync(self, auto_resize=False): + """Sync property changes back to the device tree + + This updates the device tree blob with any changes to this property + since the last sync. + + Args: + auto_resize: Resize the device tree automatically if it does not + have enough space for the update + + Raises: + FdtException if auto_resize is False and there is not enough space + """ + if self.dirty: + node = self._node + fdt_obj = node._fdt._fdt_obj + node_name = fdt_obj.get_name(node._offset) + if node_name and node_name != node.name: + raise ValueError("Internal error, node '%s' name mismatch '%s'" % + (node.path, node_name)) + + if auto_resize: + while fdt_obj.setprop(node.Offset(), self.name, self.bytes, + (libfdt.NOSPACE,)) == -libfdt.NOSPACE: + fdt_obj.resize(fdt_obj.totalsize() + 1024 + + len(self.bytes)) + fdt_obj.setprop(node.Offset(), self.name, self.bytes) + else: + fdt_obj.setprop(node.Offset(), self.name, self.bytes) + self.dirty = False + + +class Node: + """A device tree node + + Properties: + parent: Parent Node + offset: Integer offset in the device tree (None if to be synced) + name: Device tree node tname + path: Full path to node, along with the node name itself + _fdt: Device tree object + subnodes: A list of subnodes for this node, each a Node object + props: A dict of properties for this node, each a Prop object. + Keyed by property name + """ + def __init__(self, fdt, parent, offset, name, path): + self._fdt = fdt + self.parent = parent + self._offset = offset + self.name = name + self.path = path + self.subnodes = [] + self.props = {} + + def GetFdt(self): + """Get the Fdt object for this node + + Returns: + Fdt object + """ + return self._fdt + + def FindNode(self, name): + """Find a node given its name + + Args: + name: Node name to look for + Returns: + Node object if found, else None + """ + for subnode in self.subnodes: + if subnode.name == name: + return subnode + return None + + def Offset(self): + """Returns the offset of a node, after checking the cache + + This should be used instead of self._offset directly, to ensure that + the cache does not contain invalid offsets. + """ + self._fdt.CheckCache() + return self._offset + + def Scan(self): + """Scan a node's properties and subnodes + + This fills in the props and subnodes properties, recursively + searching into subnodes so that the entire tree is built. + """ + fdt_obj = self._fdt._fdt_obj + self.props = self._fdt.GetProps(self) + phandle = fdt_obj.get_phandle(self.Offset()) + if phandle: + self._fdt.phandle_to_node[phandle] = self + + offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND) + while offset >= 0: + sep = '' if self.path[-1] == '/' else '/' + name = fdt_obj.get_name(offset) + path = self.path + sep + name + node = Node(self._fdt, self, offset, name, path) + self.subnodes.append(node) + + node.Scan() + offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND) + + def Refresh(self, my_offset): + """Fix up the _offset for each node, recursively + + Note: This does not take account of property offsets - these will not + be updated. + """ + fdt_obj = self._fdt._fdt_obj + if self._offset != my_offset: + self._offset = my_offset + name = fdt_obj.get_name(self._offset) + if name and self.name != name: + raise ValueError("Internal error, node '%s' name mismatch '%s'" % + (self.path, name)) + + offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND) + for subnode in self.subnodes: + if subnode.name != fdt_obj.get_name(offset): + raise ValueError('Internal error, node name mismatch %s != %s' % + (subnode.name, fdt_obj.get_name(offset))) + subnode.Refresh(offset) + offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND) + if offset != -libfdt.FDT_ERR_NOTFOUND: + raise ValueError('Internal error, offset == %d' % offset) + + poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND) + while poffset >= 0: + p = fdt_obj.get_property_by_offset(poffset) + prop = self.props.get(p.name) + if not prop: + raise ValueError("Internal error, node '%s' property '%s' missing, " + 'offset %d' % (self.path, p.name, poffset)) + prop.RefreshOffset(poffset) + poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND) + + def DeleteProp(self, prop_name): + """Delete a property of a node + + The property is deleted and the offset cache is invalidated. + + Args: + prop_name: Name of the property to delete + Raises: + ValueError if the property does not exist + """ + CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name), + "Node '%s': delete property: '%s'" % (self.path, prop_name)) + del self.props[prop_name] + self._fdt.Invalidate() + + def AddZeroProp(self, prop_name): + """Add a new property to the device tree with an integer value of 0. + + Args: + prop_name: Name of property + """ + self.props[prop_name] = Prop(self, None, prop_name, + tools.GetBytes(0, 4)) + + def AddEmptyProp(self, prop_name, len): + """Add a property with a fixed data size, for filling in later + + The device tree is marked dirty so that the value will be written to + the blob on the next sync. + + Args: + prop_name: Name of property + len: Length of data in property + """ + value = tools.GetBytes(0, len) + self.props[prop_name] = Prop(self, None, prop_name, value) + + def _CheckProp(self, prop_name): + """Check if a property is present + + Args: + prop_name: Name of property + + Returns: + self + + Raises: + ValueError if the property is missing + """ + if prop_name not in self.props: + raise ValueError("Fdt '%s', node '%s': Missing property '%s'" % + (self._fdt._fname, self.path, prop_name)) + return self + + def SetInt(self, prop_name, val): + """Update an integer property int the device tree. + + This is not allowed to change the size of the FDT. + + The device tree is marked dirty so that the value will be written to + the blob on the next sync. + + Args: + prop_name: Name of property + val: Value to set + """ + self._CheckProp(prop_name).props[prop_name].SetInt(val) + + def SetData(self, prop_name, val): + """Set the data value of a property + + The device tree is marked dirty so that the value will be written to + the blob on the next sync. + + Args: + prop_name: Name of property to set + val: Data value to set + """ + self._CheckProp(prop_name).props[prop_name].SetData(val) + + def SetString(self, prop_name, val): + """Set the string value of a property + + The device tree is marked dirty so that the value will be written to + the blob on the next sync. + + Args: + prop_name: Name of property to set + val: String value to set (will be \0-terminated in DT) + """ + if type(val) == str: + val = val.encode('utf-8') + self._CheckProp(prop_name).props[prop_name].SetData(val + b'\0') + + def AddData(self, prop_name, val): + """Add a new property to a node + + The device tree is marked dirty so that the value will be written to + the blob on the next sync. + + Args: + prop_name: Name of property to add + val: Bytes value of property + + Returns: + Prop added + """ + prop = Prop(self, None, prop_name, val) + self.props[prop_name] = prop + return prop + + def AddString(self, prop_name, val): + """Add a new string property to a node + + The device tree is marked dirty so that the value will be written to + the blob on the next sync. + + Args: + prop_name: Name of property to add + val: String value of property + + Returns: + Prop added + """ + val = bytes(val, 'utf-8') + return self.AddData(prop_name, val + b'\0') + + def AddInt(self, prop_name, val): + """Add a new integer property to a node + + The device tree is marked dirty so that the value will be written to + the blob on the next sync. + + Args: + prop_name: Name of property to add + val: Integer value of property + + Returns: + Prop added + """ + return self.AddData(prop_name, struct.pack('>I', val)) + + def AddSubnode(self, name): + """Add a new subnode to the node + + Args: + name: name of node to add + + Returns: + New subnode that was created + """ + path = self.path + '/' + name + subnode = Node(self._fdt, self, None, name, path) + self.subnodes.append(subnode) + return subnode + + def Sync(self, auto_resize=False): + """Sync node changes back to the device tree + + This updates the device tree blob with any changes to this node and its + subnodes since the last sync. + + Args: + auto_resize: Resize the device tree automatically if it does not + have enough space for the update + + Returns: + True if the node had to be added, False if it already existed + + Raises: + FdtException if auto_resize is False and there is not enough space + """ + added = False + if self._offset is None: + # The subnode doesn't exist yet, so add it + fdt_obj = self._fdt._fdt_obj + if auto_resize: + while True: + offset = fdt_obj.add_subnode(self.parent._offset, self.name, + (libfdt.NOSPACE,)) + if offset != -libfdt.NOSPACE: + break + fdt_obj.resize(fdt_obj.totalsize() + 1024) + else: + offset = fdt_obj.add_subnode(self.parent._offset, self.name) + self._offset = offset + added = True + + # Sync the existing subnodes first, so that we can rely on the offsets + # being correct. As soon as we add new subnodes, it pushes all the + # existing subnodes up. + for node in reversed(self.subnodes): + if node._offset is not None: + node.Sync(auto_resize) + + # Sync subnodes in reverse so that we get the expected order. Each + # new node goes at the start of the subnode list. This avoids an O(n^2) + # rescan of node offsets. + num_added = 0 + for node in reversed(self.subnodes): + if node.Sync(auto_resize): + num_added += 1 + if num_added: + # Reorder our list of nodes to put the new ones first, since that's + # what libfdt does + old_count = len(self.subnodes) - num_added + subnodes = self.subnodes[old_count:] + self.subnodes[:old_count] + self.subnodes = subnodes + + # Sync properties now, whose offsets should not have been disturbed, + # since properties come before subnodes. This is done after all the + # subnode processing above, since updating properties can disturb the + # offsets of those subnodes. + # Properties are synced in reverse order, with new properties added + # before existing properties are synced. This ensures that the offsets + # of earlier properties are not disturbed. + # Note that new properties will have an offset of None here, which + # Python cannot sort against int. So use a large value instead so that + # new properties are added first. + prop_list = sorted(self.props.values(), + key=lambda prop: prop._offset or 1 << 31, + reverse=True) + for prop in prop_list: + prop.Sync(auto_resize) + return added + + +class Fdt: + """Provides simple access to a flat device tree blob using libfdts. + + Properties: + fname: Filename of fdt + _root: Root of device tree (a Node object) + name: Helpful name for this Fdt for the user (useful when creating the + DT from data rather than a file) + """ + def __init__(self, fname): + self._fname = fname + self._cached_offsets = False + self.phandle_to_node = {} + self.name = '' + if self._fname: + self.name = self._fname + self._fname = fdt_util.EnsureCompiled(self._fname) + + with open(self._fname, 'rb') as fd: + self._fdt_obj = libfdt.Fdt(fd.read()) + + @staticmethod + def FromData(data, name=''): + """Create a new Fdt object from the given data + + Args: + data: Device-tree data blob + name: Helpful name for this Fdt for the user + + Returns: + Fdt object containing the data + """ + fdt = Fdt(None) + fdt._fdt_obj = libfdt.Fdt(bytes(data)) + fdt.name = name + return fdt + + def LookupPhandle(self, phandle): + """Look up a phandle + + Args: + phandle: Phandle to look up (int) + + Returns: + Node object the phandle points to + """ + return self.phandle_to_node.get(phandle) + + def Scan(self, root='/'): + """Scan a device tree, building up a tree of Node objects + + This fills in the self._root property + + Args: + root: Ignored + + TODO(sjg@chromium.org): Implement the 'root' parameter + """ + self._cached_offsets = True + self._root = self.Node(self, None, 0, '/', '/') + self._root.Scan() + + def GetRoot(self): + """Get the root Node of the device tree + + Returns: + The root Node object + """ + return self._root + + def GetNode(self, path): + """Look up a node from its path + + Args: + path: Path to look up, e.g. '/microcode/update@0' + Returns: + Node object, or None if not found + """ + node = self._root + parts = path.split('/') + if len(parts) < 2: + return None + if len(parts) == 2 and parts[1] == '': + return node + for part in parts[1:]: + node = node.FindNode(part) + if not node: + return None + return node + + def Flush(self): + """Flush device tree changes back to the file + + If the device tree has changed in memory, write it back to the file. + """ + with open(self._fname, 'wb') as fd: + fd.write(self._fdt_obj.as_bytearray()) + + def Sync(self, auto_resize=False): + """Make sure any DT changes are written to the blob + + Args: + auto_resize: Resize the device tree automatically if it does not + have enough space for the update + + Raises: + FdtException if auto_resize is False and there is not enough space + """ + self.CheckCache() + self._root.Sync(auto_resize) + self.Refresh() + + def Pack(self): + """Pack the device tree down to its minimum size + + When nodes and properties shrink or are deleted, wasted space can + build up in the device tree binary. + """ + CheckErr(self._fdt_obj.pack(), 'pack') + self.Refresh() + + def GetContents(self): + """Get the contents of the FDT + + Returns: + The FDT contents as a string of bytes + """ + return bytes(self._fdt_obj.as_bytearray()) + + def GetFdtObj(self): + """Get the contents of the FDT + + Returns: + The FDT contents as a libfdt.Fdt object + """ + return self._fdt_obj + + def GetProps(self, node): + """Get all properties from a node. + + Args: + node: Full path to node name to look in. + + Returns: + A dictionary containing all the properties, indexed by node name. + The entries are Prop objects. + + Raises: + ValueError: if the node does not exist. + """ + props_dict = {} + poffset = self._fdt_obj.first_property_offset(node._offset, + QUIET_NOTFOUND) + while poffset >= 0: + p = self._fdt_obj.get_property_by_offset(poffset) + prop = Prop(node, poffset, p.name, p) + props_dict[prop.name] = prop + + poffset = self._fdt_obj.next_property_offset(poffset, + QUIET_NOTFOUND) + return props_dict + + def Invalidate(self): + """Mark our offset cache as invalid""" + self._cached_offsets = False + + def CheckCache(self): + """Refresh the offset cache if needed""" + if self._cached_offsets: + return + self.Refresh() + + def Refresh(self): + """Refresh the offset cache""" + self._root.Refresh(0) + self._cached_offsets = True + + def GetStructOffset(self, offset): + """Get the file offset of a given struct offset + + Args: + offset: Offset within the 'struct' region of the device tree + Returns: + Position of @offset within the device tree binary + """ + return self._fdt_obj.off_dt_struct() + offset + + @classmethod + def Node(self, fdt, parent, offset, name, path): + """Create a new node + + This is used by Fdt.Scan() to create a new node using the correct + class. + + Args: + fdt: Fdt object + parent: Parent node, or None if this is the root node + offset: Offset of node + name: Node name + path: Full path to node + """ + node = Node(fdt, parent, offset, name, path) + return node + + def GetFilename(self): + """Get the filename of the device tree + + Returns: + String filename + """ + return self._fname + +def FdtScan(fname): + """Returns a new Fdt object""" + dtb = Fdt(fname) + dtb.Scan() + return dtb diff --git a/roms/u-boot/tools/dtoc/fdt_util.py b/roms/u-boot/tools/dtoc/fdt_util.py new file mode 100644 index 000000000..37e96b986 --- /dev/null +++ b/roms/u-boot/tools/dtoc/fdt_util.py @@ -0,0 +1,207 @@ +#!/usr/bin/python +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +# Utility functions for reading from a device tree. Once the upstream pylibfdt +# implementation advances far enough, we should be able to drop these. + +import os +import struct +import sys +import tempfile + +from patman import command +from patman import tools + +def fdt32_to_cpu(val): + """Convert a device tree cell to an integer + + Args: + Value to convert (4-character string representing the cell value) + + Return: + A native-endian integer value + """ + return struct.unpack('>I', val)[0] + +def fdt_cells_to_cpu(val, cells): + """Convert one or two cells to a long integer + + Args: + Value to convert (array of one or more 4-character strings) + + Return: + A native-endian integer value + """ + if not cells: + return 0 + out = int(fdt32_to_cpu(val[0])) + if cells == 2: + out = out << 32 | fdt32_to_cpu(val[1]) + return out + +def EnsureCompiled(fname, tmpdir=None, capture_stderr=False): + """Compile an fdt .dts source file into a .dtb binary blob if needed. + + Args: + fname: Filename (if .dts it will be compiled). It not it will be + left alone + tmpdir: Temporary directory for output files, or None to use the + tools-module output directory + + Returns: + Filename of resulting .dtb file + """ + _, ext = os.path.splitext(fname) + if ext != '.dts': + return fname + + if tmpdir: + dts_input = os.path.join(tmpdir, 'source.dts') + dtb_output = os.path.join(tmpdir, 'source.dtb') + else: + dts_input = tools.GetOutputFilename('source.dts') + dtb_output = tools.GetOutputFilename('source.dtb') + + search_paths = [os.path.join(os.getcwd(), 'include')] + root, _ = os.path.splitext(fname) + cc, args = tools.GetTargetCompileTool('cc') + args += ['-E', '-P', '-x', 'assembler-with-cpp', '-D__ASSEMBLY__'] + args += ['-Ulinux'] + for path in search_paths: + args.extend(['-I', path]) + args += ['-o', dts_input, fname] + command.Run(cc, *args) + + # If we don't have a directory, put it in the tools tempdir + search_list = [] + for path in search_paths: + search_list.extend(['-i', path]) + dtc, args = tools.GetTargetCompileTool('dtc') + args += ['-I', 'dts', '-o', dtb_output, '-O', 'dtb', + '-W', 'no-unit_address_vs_reg'] + args.extend(search_list) + args.append(dts_input) + command.Run(dtc, *args, capture_stderr=capture_stderr) + return dtb_output + +def GetInt(node, propname, default=None): + """Get an integer from a property + + Args: + node: Node object to read from + propname: property name to read + default: Default value to use if the node/property do not exist + + Returns: + Integer value read, or default if none + """ + prop = node.props.get(propname) + if not prop: + return default + if isinstance(prop.value, list): + raise ValueError("Node '%s' property '%s' has list value: expecting " + "a single integer" % (node.name, propname)) + value = fdt32_to_cpu(prop.value) + return value + +def GetString(node, propname, default=None): + """Get a string from a property + + Args: + node: Node object to read from + propname: property name to read + default: Default value to use if the node/property do not exist + + Returns: + String value read, or default if none + """ + prop = node.props.get(propname) + if not prop: + return default + value = prop.value + if isinstance(value, list): + raise ValueError("Node '%s' property '%s' has list value: expecting " + "a single string" % (node.name, propname)) + return value + +def GetBool(node, propname, default=False): + """Get an boolean from a property + + Args: + node: Node object to read from + propname: property name to read + default: Default value to use if the node/property do not exist + + Returns: + Boolean value read, or default if none (if you set this to True the + function will always return True) + """ + if propname in node.props: + return True + return default + +def GetByte(node, propname, default=None): + """Get an byte from a property + + Args: + node: Node object to read from + propname: property name to read + default: Default value to use if the node/property do not exist + + Returns: + Byte value read, or default if none + """ + prop = node.props.get(propname) + if not prop: + return default + value = prop.value + if isinstance(value, list): + raise ValueError("Node '%s' property '%s' has list value: expecting " + "a single byte" % (node.name, propname)) + if len(value) != 1: + raise ValueError("Node '%s' property '%s' has length %d, expecting %d" % + (node.name, propname, len(value), 1)) + return ord(value[0]) + +def GetPhandleList(node, propname): + """Get a list of phandles from a property + + Args: + node: Node object to read from + propname: property name to read + + Returns: + List of phandles read, each an integer + """ + prop = node.props.get(propname) + if not prop: + return None + value = prop.value + if not isinstance(value, list): + value = [value] + return [fdt32_to_cpu(v) for v in value] + +def GetDatatype(node, propname, datatype): + """Get a value of a given type from a property + + Args: + node: Node object to read from + propname: property name to read + datatype: Type to read (str or int) + + Returns: + value read, or None if none + + Raises: + ValueError if datatype is not str or int + """ + if datatype == str: + return GetString(node, propname) + elif datatype == int: + return GetInt(node, propname) + raise ValueError("fdt_util internal error: Unknown data type '%s'" % + datatype) diff --git a/roms/u-boot/tools/dtoc/main.py b/roms/u-boot/tools/dtoc/main.py new file mode 100755 index 000000000..93706de89 --- /dev/null +++ b/roms/u-boot/tools/dtoc/main.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +"""Device tree to C tool + +This tool converts a device tree binary file (.dtb) into two C files. The +indent is to allow a C program to access data from the device tree without +having to link against libfdt. By putting the data from the device tree into +C structures, normal C code can be used. This helps to reduce the size of the +compiled program. + +Dtoc produces several output files - see OUTPUT_FILES in dtb_platdata.py + +This tool is used in U-Boot to provide device tree data to SPL without +increasing the code size of SPL. This supports the CONFIG_SPL_OF_PLATDATA +options. For more information about the use of this options and tool please +see doc/driver-model/of-plat.rst +""" + +from optparse import OptionParser +import os +import sys +import unittest + +# Bring in the patman libraries +our_path = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(our_path, '..')) + +# Bring in the libfdt module +sys.path.insert(0, 'scripts/dtc/pylibfdt') +sys.path.insert(0, os.path.join(our_path, + '../../build-sandbox_spl/scripts/dtc/pylibfdt')) + +from dtoc import dtb_platdata +from patman import test_util + +def run_tests(processes, args): + """Run all the test we have for dtoc + + Args: + processes: Number of processes to use to run tests (None=same as #CPUs) + args: List of positional args provided to dtoc. This can hold a test + name to execute (as in 'dtoc -t test_empty_file', for example) + """ + from dtoc import test_src_scan + from dtoc import test_dtoc + + result = unittest.TestResult() + 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=[], + test_class_list=[test_dtoc.TestDtoc,test_src_scan.TestSrcScan]) + + return test_util.ReportResult('binman', test_name, result) + +def RunTestCoverage(): + """Run the tests and check that we get 100% coverage""" + sys.argv = [sys.argv[0]] + test_util.RunTestCoverage('tools/dtoc/dtoc', '/main.py', + ['tools/patman/*.py', '*/fdt*', '*test*'], options.build_dir) + + +if __name__ != '__main__': + sys.exit(1) + +parser = OptionParser() +parser.add_option('-B', '--build-dir', type='string', default='b', + help='Directory containing the build output') +parser.add_option('-c', '--c-output-dir', action='store', + help='Select output directory for C files') +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', + 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', + default=False, help='run tests') +parser.add_option('-T', '--test-coverage', action='store_true', + default=False, help='run tests and check for 100% coverage') +(options, args) = parser.parse_args() + +# Run our meagre tests +if options.test: + ret_code = run_tests(options.processes, args) + sys.exit(ret_code) + +elif options.test_coverage: + RunTestCoverage() + +else: + dtb_platdata.run_steps(args, options.dtb_file, options.include_disabled, + options.output, + [options.c_output_dir, options.h_output_dir], + options.phase, instantiate=options.instantiate) diff --git a/roms/u-boot/tools/dtoc/setup.py b/roms/u-boot/tools/dtoc/setup.py new file mode 100644 index 000000000..5e092fe08 --- /dev/null +++ b/roms/u-boot/tools/dtoc/setup.py @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0+ + +from distutils.core import setup +setup(name='dtoc', + version='1.0', + license='GPL-2.0+', + scripts=['dtoc'], + packages=['dtoc'], + package_dir={'dtoc': ''}, + package_data={'dtoc': ['README']}, + classifiers=['Environment :: Console', + 'Topic :: Software Development :: Embedded Systems']) diff --git a/roms/u-boot/tools/dtoc/src_scan.py b/roms/u-boot/tools/dtoc/src_scan.py new file mode 100644 index 000000000..2db96884c --- /dev/null +++ b/roms/u-boot/tools/dtoc/src_scan.py @@ -0,0 +1,739 @@ +#!/usr/bin/python +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2017 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +"""Scanning of U-Boot source for drivers and structs + +This scans the source tree to find out things about all instances of +U_BOOT_DRIVER(), UCLASS_DRIVER and all struct declarations in header files. + +See doc/driver-model/of-plat.rst for more informaiton +""" + +import os +import re +import sys + + +def conv_name_to_c(name): + """Convert a device-tree name to a C identifier + + This uses multiple replace() calls instead of re.sub() since it is faster + (400ms for 1m calls versus 1000ms for the 're' version). + + Args: + name (str): Name to convert + Return: + str: String containing the C version of this name + """ + new = name.replace('@', '_at_') + new = new.replace('-', '_') + new = new.replace(',', '_') + new = new.replace('.', '_') + if new == '/': + return 'root' + return new + +def get_compat_name(node): + """Get the node's list of compatible string as a C identifiers + + Args: + node (fdt.Node): Node object to check + Return: + list of str: List of C identifiers for all the compatible strings + """ + compat = node.props['compatible'].value + if not isinstance(compat, list): + compat = [compat] + return [conv_name_to_c(c) for c in compat] + + +class Driver: + """Information about a driver in U-Boot + + 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' + 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' + 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>'] + 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 + uclass (Uclass): uclass for this driver + """ + def __init__(self, name, fname): + self.name = name + self.fname = fname + self.uclass_id = None + self.compat = None + self.priv = '' + self.plat = '' + self.child_priv = '' + self.child_plat = '' + self.used = False + self.phase = '' + self.headers = [] + self.dups = [] + self.warn_dups = False + self.uclass = None + + 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 and + self.used == other.used) + + def __repr__(self): + 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: + """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' + 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" + 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 + self.uclass_id = None + self.priv = '' + self.per_dev_priv = '' + self.per_dev_plat = '' + self.per_child_priv = '' + 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 + 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 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 + + Properties: + _basedir (str): Base directory of U-Boot source code. Defaults to the + grandparent of this file's directory + _drivers: Dict of valid driver names found in drivers/ + key: Driver name + value: Driver for that driver + _driver_aliases: Dict that holds aliases for driver names + key: Driver alias declared with + DM_DRIVER_ALIAS(driver_alias, driver_name) + value: Driver name declared with U_BOOT_DRIVER(driver_name) + _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 + _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 + _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, drivers_additional, phase=''): + """Set up a new Scanner + """ + if not basedir: + basedir = sys.argv[0].replace('tools/dtoc/dtoc', '') + if basedir == '': + basedir = './' + self._basedir = basedir + self._drivers = {} + self._driver_aliases = {} + self._drivers_additional = drivers_additional or [] + self._missing_drivers = set() + self._of_match = {} + self._compat_to_driver = {} + self._uclass = {} + self._structs = {} + self._phase = phase + + 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 + + Returns a valid driver name by retrieving node's list of compatible + string as a C identifier and performing a check against _drivers + and a lookup in driver_aliases printing a warning in case of failure. + + Args: + node (Node): Node object to check + Return: + Tuple: + Driver name associated with the first compatible string + List of C identifiers for all the other compatible strings + (possibly empty) + In case of no match found, the return will be the same as + get_compat_name() + """ + 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(): + compat_c = self._driver_aliases.get(compat_c) + if not compat_c: + continue + + aliases_c = compat_list_c + if compat_c in aliases_c: + aliases_c.remove(compat_c) + return compat_c, aliases_c + + self._missing_drivers.add(compat_list_c[0]) + + 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 + + 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_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 + + 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 + _driver_aliases - Maps alias names to driver name + + 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_]+)(\))?,') + + 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') + 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(): + # 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) + 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) + m_hdr = re_hdr.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: + 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: + 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. + # 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) + 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 + 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 show_warnings(self): + """Show any warnings that have been collected""" + for name in sorted(list(self._missing_drivers)): + print('WARNING: the driver %s was not found in the driver list' + % name) + + def scan_driver(self, fname): + """Scan a driver file to build a list of driver names and 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 + """ + 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 'U_BOOT_DRIVER' in buff: + self._parse_driver(fname, buff) + if 'UCLASS_DRIVER' in buff: + self._parse_uclass_driver(fname, buff) + + 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 + + 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: + 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: + continue + if fname[0] == '/': + self.scan_driver(fname) + 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' + + 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 + """ + # 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 + 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]))) + + 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 + + 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/roms/u-boot/tools/dtoc/test/dtoc_test.dts b/roms/u-boot/tools/dtoc/test/dtoc_test.dts new file mode 100644 index 000000000..b2259483a --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + + /dts-v1/; + +/ { +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_add_prop.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_add_prop.dts new file mode 100644 index 000000000..fa296e555 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_add_prop.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2018 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + intval = <1>; + }; + + spl-test2 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + intarray = <5>; + }; +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_addr32.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_addr32.dts new file mode 100644 index 000000000..239045497 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_addr32.dts @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + + /dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + test1 { + u-boot,dm-pre-reloc; + compatible = "test1"; + reg = <0x1234 0x5678>; + }; + + test2 { + u-boot,dm-pre-reloc; + compatible = "test2"; + reg = <0x12345678 0x98765432 2 3>; + }; + +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_addr32_64.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_addr32_64.dts new file mode 100644 index 000000000..7599d5b0a --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_addr32_64.dts @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <2>; + + test1 { + u-boot,dm-pre-reloc; + compatible = "test1"; + reg = <0x1234 0x5678 0x0>; + }; + + test2 { + u-boot,dm-pre-reloc; + compatible = "test2"; + reg = <0x12345678 0x98765432 0x10987654>; + }; + + test3 { + u-boot,dm-pre-reloc; + compatible = "test3"; + reg = <0x12345678 0x98765432 0x10987654 2 0 3>; + }; + +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_addr64.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_addr64.dts new file mode 100644 index 000000000..263d25138 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_addr64.dts @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + + /dts-v1/; + +/ { + #address-cells = <2>; + #size-cells = <2>; + + test1 { + u-boot,dm-pre-reloc; + compatible = "test1"; + reg = /bits/ 64 <0x1234 0x5678>; + }; + + test2 { + u-boot,dm-pre-reloc; + compatible = "test2"; + reg = /bits/ 64 <0x1234567890123456 0x9876543210987654>; + }; + + test3 { + u-boot,dm-pre-reloc; + compatible = "test3"; + reg = /bits/ 64 <0x1234567890123456 0x9876543210987654 2 3>; + }; + +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_addr64_32.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_addr64_32.dts new file mode 100644 index 000000000..85e4f5fda --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_addr64_32.dts @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <2>; + #size-cells = <1>; + + test1 { + u-boot,dm-pre-reloc; + compatible = "test1"; + reg = <0x1234 0x0 0x5678>; + }; + + test2 { + u-boot,dm-pre-reloc; + compatible = "test2"; + reg = <0x12345678 0x90123456 0x98765432>; + }; + + test3 { + u-boot,dm-pre-reloc; + compatible = "test3"; + reg = <0x12345678 0x90123456 0x98765432 0 2 3>; + }; + +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_alias_bad.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_alias_bad.dts new file mode 100644 index 000000000..d4f502ad0 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/dtoc/test/dtoc_test_alias_bad_path.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_alias_bad_path.dts new file mode 100644 index 000000000..0beca4f0d --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/dtoc/test/dtoc_test_alias_bad_uc.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_alias_bad_uc.dts new file mode 100644 index 000000000..ae64f5b3b --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/dtoc/test/dtoc_test_aliases.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_aliases.dts new file mode 100644 index 000000000..ae3371686 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_aliases.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + + /dts-v1/; + +/ { + spl-test { + u-boot,dm-pre-reloc; + compatible = "compat1", "compat2.1-fred", "compat3"; + intval = <1>; + }; + + spl-test2 { + u-boot,dm-pre-reloc; + compatible = "compat1", "simple_bus"; + intval = <1>; + }; +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_bad_reg.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_bad_reg.dts new file mode 100644 index 000000000..1312acb61 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_bad_reg.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2018 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + spl-test { + compatible = "test"; + reg = "fre"; + }; +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_bad_reg2.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_bad_reg2.dts new file mode 100644 index 000000000..3e9efa43a --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_bad_reg2.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2018 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + spl-test { + compatible = "test"; + reg = <1 2 3>; + }; +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_driver_alias.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_driver_alias.dts new file mode 100644 index 000000000..da7973b2e --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_driver_alias.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2020 Collabora Ltd. + */ + +/dts-v1/; + +/ { + gpio_a: gpios@0 { + u-boot,dm-pre-reloc; + gpio-controller; + compatible = "sandbox_gpio_alias"; + #gpio-cells = <1>; + gpio-bank-name = "a"; + sandbox,gpio-count = <20>; + }; + +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_empty.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_empty.dts new file mode 100644 index 000000000..b2259483a --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_empty.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + + /dts-v1/; + +/ { +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_inst.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_inst.dts new file mode 100644 index 000000000..b8177fcef --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/dtoc/test/dtoc_test_invalid_driver.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_invalid_driver.dts new file mode 100644 index 000000000..914ac3e89 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_invalid_driver.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + spl-test { + u-boot,dm-pre-reloc; + compatible = "invalid"; + }; +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_noprops.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_noprops.dts new file mode 100644 index 000000000..e6fdd11b8 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_noprops.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + i2c@0 { + pmic@9 { + compatible = "sandbox,pmic"; + u-boot,dm-pre-reloc; + reg = <9>; + low-power; + }; + }; +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_phandle.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_phandle.dts new file mode 100644 index 000000000..a71acffc6 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_phandle.dts @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + + /dts-v1/; + +/ { + phandle: phandle-target { + u-boot,dm-pre-reloc; + compatible = "target"; + intval = <0>; + #clock-cells = <0>; + }; + + phandle_1: phandle2-target { + u-boot,dm-pre-reloc; + compatible = "target"; + intval = <1>; + #clock-cells = <1>; + }; + phandle_2: phandle3-target { + u-boot,dm-pre-reloc; + compatible = "target"; + intval = <2>; + #clock-cells = <2>; + }; + + phandle-source { + u-boot,dm-pre-reloc; + compatible = "source"; + clocks = <&phandle &phandle_1 11 &phandle_2 12 13 &phandle>; + }; + + phandle-source2 { + u-boot,dm-pre-reloc; + compatible = "source"; + clocks = <&phandle>; + }; +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_bad.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_bad.dts new file mode 100644 index 000000000..a3ddc5958 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_bad.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2018 Google, Inc + */ + +/dts-v1/; + +/ { + phandle-source { + u-boot,dm-pre-reloc; + compatible = "source"; + clocks = <20>; /* Invalid phandle */ + }; +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_bad2.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_bad2.dts new file mode 100644 index 000000000..fe25f565f --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_bad2.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2018 Google, Inc + */ + +/dts-v1/; + +/ { + phandle: phandle-target { + u-boot,dm-pre-reloc; + compatible = "target"; + intval = <0>; + }; + + phandle-source2 { + u-boot,dm-pre-reloc; + compatible = "source"; + clocks = <&phandle>; + }; +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_cd_gpios.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_cd_gpios.dts new file mode 100644 index 000000000..241743e73 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_cd_gpios.dts @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2020 Collabora Ltd. + */ + +/dts-v1/; + +/ { + phandle: phandle-target { + u-boot,dm-pre-reloc; + compatible = "target"; + intval = <0>; + #gpio-cells = <0>; + }; + + phandle_1: phandle2-target { + u-boot,dm-pre-reloc; + compatible = "target"; + intval = <1>; + #gpio-cells = <1>; + }; + phandle_2: phandle3-target { + u-boot,dm-pre-reloc; + compatible = "target"; + intval = <2>; + #gpio-cells = <2>; + }; + + phandle-source { + u-boot,dm-pre-reloc; + compatible = "source"; + cd-gpios = <&phandle &phandle_1 11 &phandle_2 12 13 &phandle>; + }; + + phandle-source2 { + u-boot,dm-pre-reloc; + compatible = "source"; + cd-gpios = <&phandle>; + }; +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_reorder.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_reorder.dts new file mode 100644 index 000000000..aa71d56f2 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_reorder.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2018 Google, Inc + */ + +/dts-v1/; + +/ { + + phandle-source2 { + u-boot,dm-pre-reloc; + compatible = "source"; + clocks = <&phandle>; + }; + + phandle: phandle-target { + u-boot,dm-pre-reloc; + compatible = "target"; + #clock-cells = <0>; + }; +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_single.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_single.dts new file mode 100644 index 000000000..aacd0b15f --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_phandle_single.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2018 Google, Inc + */ + +/dts-v1/; + +/ { + phandle: phandle-target { + u-boot,dm-pre-reloc; + compatible = "target"; + intval = <0>; + #clock-cells = <0>; + }; + + phandle-source2 { + u-boot,dm-pre-reloc; + compatible = "source"; + clocks = <&phandle>; + }; +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_scan_drivers.cxx b/roms/u-boot/tools/dtoc/test/dtoc_test_scan_drivers.cxx new file mode 100644 index 000000000..f370b8951 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_scan_drivers.cxx @@ -0,0 +1,5 @@ +/* Aliases must be in driver files */ +U_BOOT_DRIVER(sandbox_gpio) { +}; + +DM_DRIVER_ALIAS(sandbox_gpio, sandbox_gpio_alias2) diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_simple.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_simple.dts new file mode 100644 index 000000000..b5c1274bb --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_simple.dts @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + + /dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + intarray = <2 3 4>; + byteval = [05]; + bytearray = [06]; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10 11]; + stringval = "message"; + stringarray = "multi-word", "message"; + notstring = [20 21 22 10 00]; + }; + + spl-test2 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + intval = <3>; + intarray = <5>; + byteval = [08]; + bytearray = [01 23 34]; + longbytearray = [09 0a 0b 0c]; + stringval = "message2"; + stringarray = "another", "multi-word", "message"; + acpi-name = "\\_SB.GPO0"; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; + }; + + i2c@0 { + compatible = "sandbox,i2c"; + u-boot,dm-pre-reloc; + #address-cells = <1>; + #size-cells = <0>; + pmic@9 { + compatible = "sandbox,pmic"; + u-boot,dm-pre-reloc; + reg = <9>; + low-power; + }; + }; + + orig-node { + orig = <1 23 4>; + }; +}; diff --git a/roms/u-boot/tools/dtoc/test/dtoc_test_single_reg.dts b/roms/u-boot/tools/dtoc/test/dtoc_test_single_reg.dts new file mode 100644 index 000000000..804b67855 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test/dtoc_test_single_reg.dts @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + i2c@0 { + compatible = "sandbox,i2c"; + u-boot,dm-pre-reloc; + #address-cells = <1>; + #size-cells = <0>; + pmic@9 { + compatible = "sandbox,pmic"; + u-boot,dm-pre-reloc; + reg = <9>; + low-power; + + gpio { + compatible = "sandbox,gpio"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/dtoc/test_dtoc.py b/roms/u-boot/tools/dtoc/test_dtoc.py new file mode 100755 index 000000000..0b2805fee --- /dev/null +++ b/roms/u-boot/tools/dtoc/test_dtoc.py @@ -0,0 +1,1832 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2012 The Chromium OS Authors. +# + +"""Tests for the dtb_platdata module + +This includes unit tests for some functions and functional tests for the dtoc +tool. +""" + +import collections +import copy +import glob +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 +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 +from patman import tools + +OUR_PATH = os.path.dirname(os.path.realpath(__file__)) + + +HEADER = '''/* + * DO NOT MODIFY + * + * Defines the structs used to hold devicetree data. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ + +#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_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 + +#include <common.h> +#include <dm.h> +#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. + */ +''' + +# 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 + +def get_dtb_file(dts_fname, capture_stderr=False): + """Compile a .dts file to a .dtb + + Args: + dts_fname (str): Filename of .dts file in the current directory + capture_stderr (bool): True to capture and discard stderr output + + Returns: + str: Filename of compiled file in output directory + """ + return fdt_util.EnsureCompiled(os.path.join(OUR_PATH, 'test', dts_fname), + 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, 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 + def setUpClass(cls): + tools.PrepareOutputDir(None) + cls.maxDiff = None + + @classmethod + def tearDownClass(cls): + tools.FinaliseOutputDir() + + @staticmethod + def _write_python_string(fname, data): + """Write a string with tabs expanded as done in this Python file + + Args: + fname (str): Filename to write to + data (str): Raw string to convert + """ + data = data.replace('\t', '\\t') + with open(fname, 'w') as fout: + fout.write(data) + + def _check_strings(self, expected, actual): + """Check that a string matches its expected value + + If the strings do not match, they are written to the /tmp directory in + the same Python format as is used here in the test. This allows for + easy comparison and update of the tests. + + Args: + expected (str): Expected string + actual (str): Actual string + """ + if expected != actual: + self._write_python_string('/tmp/binman.expected', expected) + self._write_python_string('/tmp/binman.actual', actual) + print('Failures written to /tmp/binman.{expected,actual}') + self.assertEqual(expected, actual) + + @staticmethod + def run_test(args, dtb_file, output, instantiate=False): + """Run a test using dtoc + + Args: + 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 + """ + # 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, instantiate, + warning_disabled=True, scan=copy_scan()) + + def test_name(self): + """Test conversion of device tree names to C identifiers""" + self.assertEqual('serial_at_0x12', conv_name_to_c('serial@0x12')) + self.assertEqual('vendor_clock_frequency', + conv_name_to_c('vendor,clock-frequency')) + self.assertEqual('rockchip_rk3399_sdhci_5_1', + conv_name_to_c('rockchip,rk3399-sdhci-5.1')) + + def test_tab_to(self): + """Test operation of tab_to() function""" + self.assertEqual('fred ', tab_to(0, 'fred')) + self.assertEqual('fred\t', tab_to(1, 'fred')) + self.assertEqual('fred was here ', tab_to(1, 'fred was here')) + self.assertEqual('fred was here\t\t', tab_to(3, 'fred was here')) + self.assertEqual('exactly8 ', tab_to(1, 'exactly8')) + self.assertEqual('exactly8\t', tab_to(2, 'exactly8')) + + def test_get_value(self): + """Test operation of get_value() function""" + self.assertEqual('0x45', + get_value(fdt.Type.INT, struct.pack('>I', 0x45))) + self.assertEqual('0x45', + get_value(fdt.Type.BYTE, struct.pack('<I', 0x45))) + self.assertEqual('0x0', + get_value(fdt.Type.BYTE, struct.pack('>I', 0x45))) + self.assertEqual('"test"', get_value(fdt.Type.STRING, 'test')) + self.assertEqual('true', get_value(fdt.Type.BOOL, None)) + + def test_get_compat_name(self): + """Test operation of get_compat_name() function""" + Prop = collections.namedtuple('Prop', ['value']) + Node = collections.namedtuple('Node', ['props']) + + prop = Prop(['rockchip,rk3399-sdhci-5.1', 'arasan,sdhci-5.1']) + node = Node({'compatible': prop}) + self.assertEqual((['rockchip_rk3399_sdhci_5_1', 'arasan_sdhci_5_1']), + get_compat_name(node)) + + prop = Prop(['rockchip,rk3399-sdhci-5.1']) + node = Node({'compatible': prop}) + self.assertEqual((['rockchip_rk3399_sdhci_5_1']), + get_compat_name(node)) + + prop = Prop(['rockchip,rk3399-sdhci-5.1', 'arasan,sdhci-5.1', 'third']) + node = Node({'compatible': prop}) + self.assertEqual((['rockchip_rk3399_sdhci_5_1', + 'arasan_sdhci_5_1', 'third']), + get_compat_name(node)) + + def test_empty_file(self): + """Test output from a device tree file with no nodes""" + dtb_file = get_dtb_file('dtoc_test_empty.dts') + output = tools.GetOutputFilename('output') + + # Run this one without saved_scan to complete test coverage + 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) + + self.run_test(['platdata'], dtb_file, output) + with open(output) as infile: + 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 */ +extern U_BOOT_DRIVER(sandbox_i2c); +extern U_BOOT_DRIVER(sandbox_pmic); +extern U_BOOT_DRIVER(sandbox_spl_test); +extern U_BOOT_DRIVER(sandbox_spl_test); +extern U_BOOT_DRIVER(sandbox_spl_test); + +/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */ +extern UCLASS_DRIVER(i2c); +extern UCLASS_DRIVER(misc); +extern UCLASS_DRIVER(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 */ +extern U_BOOT_DRIVER(sandbox_i2c); +extern U_BOOT_DRIVER(root_driver); +extern U_BOOT_DRIVER(denx_u_boot_test_bus); +extern U_BOOT_DRIVER(sandbox_spl_test); +extern U_BOOT_DRIVER(sandbox_spl_test); +extern U_BOOT_DRIVER(denx_u_boot_fdt_test); +extern U_BOOT_DRIVER(denx_u_boot_fdt_test); + +/* device declarations - these allow DM_DEVICE_REF() to be used */ +extern DM_DEVICE_INST(i2c); +extern DM_DEVICE_INST(root); +extern DM_DEVICE_INST(some_bus); +extern DM_DEVICE_INST(spl_test); +extern DM_DEVICE_INST(spl_test3); +extern DM_DEVICE_INST(test); +extern DM_DEVICE_INST(test0); + +/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */ +extern UCLASS_DRIVER(i2c); +extern UCLASS_DRIVER(misc); +extern UCLASS_DRIVER(root); +extern UCLASS_DRIVER(testbus); +extern UCLASS_DRIVER(testfdt); + +/* uclass declarations - needed for DM_UCLASS_REF() */ +extern DM_UCLASS_INST(i2c); +extern DM_UCLASS_INST(misc); +extern DM_UCLASS_INST(root); +extern DM_UCLASS_INST(testbus); +extern DM_UCLASS_INST(testfdt); +''' + struct_text = HEADER + ''' +struct dtd_sandbox_i2c { +}; +struct dtd_sandbox_pmic { +\tbool\t\tlow_power; +\tfdt32_t\t\treg[1]; +}; +struct dtd_sandbox_spl_test { +\tconst char * acpi_name; +\tbool\t\tboolval; +\tunsigned char\tbytearray[3]; +\tunsigned char\tbyteval; +\tfdt32_t\t\tintarray[4]; +\tfdt32_t\t\tintval; +\tunsigned char\tlongbytearray[9]; +\tunsigned char\tnotstring[5]; +\tconst char *\tstringarray[3]; +\tconst char *\tstringval; +}; +''' + 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 + */ +static struct dtd_sandbox_i2c dtv_i2c_at_0 = { +}; +U_BOOT_DRVINFO(i2c_at_0) = { +\t.name\t\t= "sandbox_i2c", +\t.plat\t\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 + * 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}, +}; +U_BOOT_DRVINFO(pmic_at_9) = { +\t.name\t\t= "sandbox_pmic", +\t.plat\t\t= &dtv_pmic_at_9, +\t.plat_size\t= sizeof(dtv_pmic_at_9), +\t.parent_idx\t= 0, +}; + +/* + * 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}, +\t.byteval\t\t= 0x5, +\t.intarray\t\t= {0x2, 0x3, 0x4, 0x0}, +\t.intval\t\t\t= 0x1, +\t.longbytearray\t\t= {0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, +\t\t0x11}, +\t.notstring\t\t= {0x20, 0x21, 0x22, 0x10, 0x0}, +\t.stringarray\t\t= {"multi-word", "message", ""}, +\t.stringval\t\t= "message", +}; +U_BOOT_DRVINFO(spl_test) = { +\t.name\t\t= "sandbox_spl_test", +\t.plat\t\t= &dtv_spl_test, +\t.plat_size\t= sizeof(dtv_spl_test), +\t.parent_idx\t= -1, +}; + +/* + * 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}, +\t.byteval\t\t= 0x8, +\t.intarray\t\t= {0x5, 0x0, 0x0, 0x0}, +\t.intval\t\t\t= 0x3, +\t.longbytearray\t\t= {0x9, 0xa, 0xb, 0xc, 0x0, 0x0, 0x0, 0x0, +\t\t0x0}, +\t.stringarray\t\t= {"another", "multi-word", "message"}, +\t.stringval\t\t= "message2", +}; +U_BOOT_DRVINFO(spl_test2) = { +\t.name\t\t= "sandbox_spl_test", +\t.plat\t\t= &dtv_spl_test2, +\t.plat_size\t= sizeof(dtv_spl_test2), +\t.parent_idx\t= -1, +}; + +/* + * 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}, +\t.stringarray\t\t= {"one", "", ""}, +}; +U_BOOT_DRVINFO(spl_test3) = { +\t.name\t\t= "sandbox_spl_test", +\t.plat\t\t= &dtv_spl_test3, +\t.plat_size\t= sizeof(dtv_spl_test3), +\t.parent_idx\t= -1, +}; + +''' + uclass_text_inst = ''' + +#include <common.h> +#include <dm.h> +#include <dt-structs.h> + +/* + * uclass declarations, ordered by 'struct uclass' linker_list idx: + * 0: i2c + * 1: misc + * 2: root + * 3: testbus + * 4: testfdt + * + * Sequence numbers allocated in each uclass: + * 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, + }, +}; + +''' + 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}, +\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): + """Test output from some simple nodes with various types of data""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + self.run_test(['struct'], dtb_file, output) + with open(output) as infile: + data = infile.read() + + self._check_strings(self.struct_text, data) + + self.run_test(['platdata'], dtb_file, output) + with open(output) as infile: + data = infile.read() + + 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.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""" + dtb_file = get_dtb_file('dtoc_test_driver_alias.dts') + output = tools.GetOutputFilename('output') + self.run_test(['struct'], dtb_file, output) + with open(output) as infile: + data = infile.read() + self._check_strings(HEADER + ''' +struct dtd_sandbox_gpio { +\tconst char *\tgpio_bank_name; +\tbool\t\tgpio_controller; +\tfdt32_t\t\tsandbox_gpio_count; +}; +''', data) + + self.run_test(['platdata'], dtb_file, output) + 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 + */ +static struct dtd_sandbox_gpio dtv_gpios_at_0 = { +\t.gpio_bank_name\t\t= "a", +\t.gpio_controller\t= true, +\t.sandbox_gpio_count\t= 0x14, +}; +U_BOOT_DRVINFO(gpios_at_0) = { +\t.name\t\t= "sandbox_gpio", +\t.plat\t\t= &dtv_gpios_at_0, +\t.plat_size\t= sizeof(dtv_gpios_at_0), +\t.parent_idx\t= -1, +}; + +''', data) + + def test_invalid_driver(self): + """Test output from a device tree file with an invalid driver""" + 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, False, + scan=copy_scan()) + with open(output) as infile: + data = infile.read() + self._check_strings(HEADER + ''' +struct dtd_invalid { +}; +''', data) + + with test_util.capture_sys_output() as _: + 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 + ''' +/* + * 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\t= &dtv_spl_test, +\t.plat_size\t= sizeof(dtv_spl_test), +\t.parent_idx\t= -1, +}; + +''', data) + + def test_phandle(self): + """Test output from a node containing a phandle reference""" + dtb_file = get_dtb_file('dtoc_test_phandle.dts') + output = tools.GetOutputFilename('output') + self.run_test(['struct'], dtb_file, output) + with open(output) as infile: + data = infile.read() + self._check_strings(HEADER + ''' +struct dtd_source { +\tstruct phandle_2_arg clocks[4]; +}; +struct dtd_target { +\tfdt32_t\t\tintval; +}; +''', data) + + self.run_test(['platdata'], dtb_file, output) + 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\t= &dtv_phandle2_target, +\t.plat_size\t= sizeof(dtv_phandle2_target), +\t.parent_idx\t= -1, +}; + +/* Node /phandle3-target index 1 */ +static struct dtd_target dtv_phandle3_target = { +\t.intval\t\t\t= 0x2, +}; +U_BOOT_DRVINFO(phandle3_target) = { +\t.name\t\t= "target", +\t.plat\t\t= &dtv_phandle3_target, +\t.plat_size\t= sizeof(dtv_phandle3_target), +\t.parent_idx\t= -1, +}; + +/* Node /phandle-source index 2 */ +static struct dtd_source dtv_phandle_source = { +\t.clocks\t\t\t= { +\t\t\t{4, {}}, +\t\t\t{0, {11}}, +\t\t\t{1, {12, 13}}, +\t\t\t{4, {}},}, +}; +U_BOOT_DRVINFO(phandle_source) = { +\t.name\t\t= "source", +\t.plat\t\t= &dtv_phandle_source, +\t.plat_size\t= sizeof(dtv_phandle_source), +\t.parent_idx\t= -1, +}; + +/* Node /phandle-source2 index 3 */ +static struct dtd_source dtv_phandle_source2 = { +\t.clocks\t\t\t= { +\t\t\t{4, {}},}, +}; +U_BOOT_DRVINFO(phandle_source2) = { +\t.name\t\t= "source", +\t.plat\t\t= &dtv_phandle_source2, +\t.plat_size\t= sizeof(dtv_phandle_source2), +\t.parent_idx\t= -1, +}; + +/* Node /phandle-target index 4 */ +static struct dtd_target dtv_phandle_target = { +\t.intval\t\t\t= 0x0, +}; +U_BOOT_DRVINFO(phandle_target) = { +\t.name\t\t= "target", +\t.plat\t\t= &dtv_phandle_target, +\t.plat_size\t= sizeof(dtv_phandle_target), +\t.parent_idx\t= -1, +}; + +''', data) + + def test_phandle_single(self): + """Test output from a node containing a phandle reference""" + dtb_file = get_dtb_file('dtoc_test_phandle_single.dts') + output = tools.GetOutputFilename('output') + self.run_test(['struct'], dtb_file, output) + with open(output) as infile: + data = infile.read() + self._check_strings(HEADER + ''' +struct dtd_source { +\tstruct phandle_0_arg clocks[1]; +}; +struct dtd_target { +\tfdt32_t\t\tintval; +}; +''', data) + + def test_phandle_reorder(self): + """Test that phandle targets are generated before their references""" + dtb_file = get_dtb_file('dtoc_test_phandle_reorder.dts') + output = tools.GetOutputFilename('output') + self.run_test(['platdata'], dtb_file, output) + 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= { +\t\t\t{1, {}},}, +}; +U_BOOT_DRVINFO(phandle_source2) = { +\t.name\t\t= "source", +\t.plat\t\t= &dtv_phandle_source2, +\t.plat_size\t= sizeof(dtv_phandle_source2), +\t.parent_idx\t= -1, +}; + +/* Node /phandle-target index 1 */ +static struct dtd_target dtv_phandle_target = { +}; +U_BOOT_DRVINFO(phandle_target) = { +\t.name\t\t= "target", +\t.plat\t\t= &dtv_phandle_target, +\t.plat_size\t= sizeof(dtv_phandle_target), +\t.parent_idx\t= -1, +}; + +''', data) + + def test_phandle_cd_gpio(self): + """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, [], None, False, + warning_disabled=True, scan=copy_scan()) + 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\t= &dtv_phandle2_target, +\t.plat_size\t= sizeof(dtv_phandle2_target), +\t.parent_idx\t= -1, +}; + +/* Node /phandle3-target index 1 */ +static struct dtd_target dtv_phandle3_target = { +\t.intval\t\t\t= 0x2, +}; +U_BOOT_DRVINFO(phandle3_target) = { +\t.name\t\t= "target", +\t.plat\t\t= &dtv_phandle3_target, +\t.plat_size\t= sizeof(dtv_phandle3_target), +\t.parent_idx\t= -1, +}; + +/* Node /phandle-source index 2 */ +static struct dtd_source dtv_phandle_source = { +\t.cd_gpios\t\t= { +\t\t\t{4, {}}, +\t\t\t{0, {11}}, +\t\t\t{1, {12, 13}}, +\t\t\t{4, {}},}, +}; +U_BOOT_DRVINFO(phandle_source) = { +\t.name\t\t= "source", +\t.plat\t\t= &dtv_phandle_source, +\t.plat_size\t= sizeof(dtv_phandle_source), +\t.parent_idx\t= -1, +}; + +/* Node /phandle-source2 index 3 */ +static struct dtd_source dtv_phandle_source2 = { +\t.cd_gpios\t\t= { +\t\t\t{4, {}},}, +}; +U_BOOT_DRVINFO(phandle_source2) = { +\t.name\t\t= "source", +\t.plat\t\t= &dtv_phandle_source2, +\t.plat_size\t= sizeof(dtv_phandle_source2), +\t.parent_idx\t= -1, +}; + +/* Node /phandle-target index 4 */ +static struct dtd_target dtv_phandle_target = { +\t.intval\t\t\t= 0x0, +}; +U_BOOT_DRVINFO(phandle_target) = { +\t.name\t\t= "target", +\t.plat\t\t= &dtv_phandle_target, +\t.plat_size\t= sizeof(dtv_phandle_target), +\t.parent_idx\t= -1, +}; + +''', data) + + def test_phandle_bad(self): + """Test a node containing an invalid phandle fails""" + dtb_file = get_dtb_file('dtoc_test_phandle_bad.dts', + capture_stderr=True) + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as exc: + self.run_test(['struct'], dtb_file, output) + self.assertIn("Cannot parse 'clocks' in node 'phandle-source'", + str(exc.exception)) + + def test_phandle_bad2(self): + """Test a phandle target missing its #*-cells property""" + dtb_file = get_dtb_file('dtoc_test_phandle_bad2.dts', + capture_stderr=True) + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as exc: + self.run_test(['struct'], dtb_file, output) + self.assertIn("Node 'phandle-target' has no cells property", + str(exc.exception)) + + def test_addresses64(self): + """Test output from a node with a 'reg' property with na=2, ns=2""" + dtb_file = get_dtb_file('dtoc_test_addr64.dts') + output = tools.GetOutputFilename('output') + self.run_test(['struct'], dtb_file, output) + with open(output) as infile: + data = infile.read() + self._check_strings(HEADER + ''' +struct dtd_test1 { +\tfdt64_t\t\treg[2]; +}; +struct dtd_test2 { +\tfdt64_t\t\treg[2]; +}; +struct dtd_test3 { +\tfdt64_t\t\treg[4]; +}; +''', data) + + self.run_test(['platdata'], dtb_file, output) + 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\t= &dtv_test1, +\t.plat_size\t= sizeof(dtv_test1), +\t.parent_idx\t= -1, +}; + +/* Node /test2 index 1 */ +static struct dtd_test2 dtv_test2 = { +\t.reg\t\t\t= {0x1234567890123456, 0x9876543210987654}, +}; +U_BOOT_DRVINFO(test2) = { +\t.name\t\t= "test2", +\t.plat\t\t= &dtv_test2, +\t.plat_size\t= sizeof(dtv_test2), +\t.parent_idx\t= -1, +}; + +/* Node /test3 index 2 */ +static struct dtd_test3 dtv_test3 = { +\t.reg\t\t\t= {0x1234567890123456, 0x9876543210987654, 0x2, 0x3}, +}; +U_BOOT_DRVINFO(test3) = { +\t.name\t\t= "test3", +\t.plat\t\t= &dtv_test3, +\t.plat_size\t= sizeof(dtv_test3), +\t.parent_idx\t= -1, +}; + +''', data) + + def test_addresses32(self): + """Test output from a node with a 'reg' property with na=1, ns=1""" + dtb_file = get_dtb_file('dtoc_test_addr32.dts') + output = tools.GetOutputFilename('output') + self.run_test(['struct'], dtb_file, output) + with open(output) as infile: + data = infile.read() + self._check_strings(HEADER + ''' +struct dtd_test1 { +\tfdt32_t\t\treg[2]; +}; +struct dtd_test2 { +\tfdt32_t\t\treg[4]; +}; +''', data) + + self.run_test(['platdata'], dtb_file, output) + 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\t= &dtv_test1, +\t.plat_size\t= sizeof(dtv_test1), +\t.parent_idx\t= -1, +}; + +/* Node /test2 index 1 */ +static struct dtd_test2 dtv_test2 = { +\t.reg\t\t\t= {0x12345678, 0x98765432, 0x2, 0x3}, +}; +U_BOOT_DRVINFO(test2) = { +\t.name\t\t= "test2", +\t.plat\t\t= &dtv_test2, +\t.plat_size\t= sizeof(dtv_test2), +\t.parent_idx\t= -1, +}; + +''', data) + + def test_addresses64_32(self): + """Test output from a node with a 'reg' property with na=2, ns=1""" + dtb_file = get_dtb_file('dtoc_test_addr64_32.dts') + output = tools.GetOutputFilename('output') + self.run_test(['struct'], dtb_file, output) + with open(output) as infile: + data = infile.read() + self._check_strings(HEADER + ''' +struct dtd_test1 { +\tfdt64_t\t\treg[2]; +}; +struct dtd_test2 { +\tfdt64_t\t\treg[2]; +}; +struct dtd_test3 { +\tfdt64_t\t\treg[4]; +}; +''', data) + + self.run_test(['platdata'], dtb_file, output) + 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\t= &dtv_test1, +\t.plat_size\t= sizeof(dtv_test1), +\t.parent_idx\t= -1, +}; + +/* Node /test2 index 1 */ +static struct dtd_test2 dtv_test2 = { +\t.reg\t\t\t= {0x1234567890123456, 0x98765432}, +}; +U_BOOT_DRVINFO(test2) = { +\t.name\t\t= "test2", +\t.plat\t\t= &dtv_test2, +\t.plat_size\t= sizeof(dtv_test2), +\t.parent_idx\t= -1, +}; + +/* Node /test3 index 2 */ +static struct dtd_test3 dtv_test3 = { +\t.reg\t\t\t= {0x1234567890123456, 0x98765432, 0x2, 0x3}, +}; +U_BOOT_DRVINFO(test3) = { +\t.name\t\t= "test3", +\t.plat\t\t= &dtv_test3, +\t.plat_size\t= sizeof(dtv_test3), +\t.parent_idx\t= -1, +}; + +''', data) + + def test_addresses32_64(self): + """Test output from a node with a 'reg' property with na=1, ns=2""" + dtb_file = get_dtb_file('dtoc_test_addr32_64.dts') + output = tools.GetOutputFilename('output') + self.run_test(['struct'], dtb_file, output) + with open(output) as infile: + data = infile.read() + self._check_strings(HEADER + ''' +struct dtd_test1 { +\tfdt64_t\t\treg[2]; +}; +struct dtd_test2 { +\tfdt64_t\t\treg[2]; +}; +struct dtd_test3 { +\tfdt64_t\t\treg[4]; +}; +''', data) + + self.run_test(['platdata'], dtb_file, output) + 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\t= &dtv_test1, +\t.plat_size\t= sizeof(dtv_test1), +\t.parent_idx\t= -1, +}; + +/* Node /test2 index 1 */ +static struct dtd_test2 dtv_test2 = { +\t.reg\t\t\t= {0x12345678, 0x9876543210987654}, +}; +U_BOOT_DRVINFO(test2) = { +\t.name\t\t= "test2", +\t.plat\t\t= &dtv_test2, +\t.plat_size\t= sizeof(dtv_test2), +\t.parent_idx\t= -1, +}; + +/* Node /test3 index 2 */ +static struct dtd_test3 dtv_test3 = { +\t.reg\t\t\t= {0x12345678, 0x9876543210987654, 0x2, 0x3}, +}; +U_BOOT_DRVINFO(test3) = { +\t.name\t\t= "test3", +\t.plat\t\t= &dtv_test3, +\t.plat_size\t= sizeof(dtv_test3), +\t.parent_idx\t= -1, +}; + +''', data) + + def test_bad_reg(self): + """Test that a reg property with an invalid type generates an error""" + # Capture stderr since dtc will emit warnings for this file + dtb_file = get_dtb_file('dtoc_test_bad_reg.dts', capture_stderr=True) + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as exc: + self.run_test(['struct'], dtb_file, output) + self.assertIn("Node 'spl-test' reg property is not an int", + str(exc.exception)) + + def test_bad_reg2(self): + """Test that a reg property with an invalid cell count is detected""" + # Capture stderr since dtc will emit warnings for this file + dtb_file = get_dtb_file('dtoc_test_bad_reg2.dts', capture_stderr=True) + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as exc: + self.run_test(['struct'], dtb_file, output) + self.assertIn( + "Node 'spl-test' (parent '/') reg property has 3 cells which is not a multiple of na + ns = 1 + 1)", + str(exc.exception)) + + def test_add_prop(self): + """Test that a subequent node can add a new property to a struct""" + dtb_file = get_dtb_file('dtoc_test_add_prop.dts') + output = tools.GetOutputFilename('output') + self.run_test(['struct'], dtb_file, output) + with open(output) as infile: + data = infile.read() + self._check_strings(HEADER + ''' +struct dtd_sandbox_spl_test { +\tfdt32_t\t\tintarray; +\tfdt32_t\t\tintval; +}; +''', data) + + self.run_test(['platdata'], dtb_file, output) + 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 + */ +static struct dtd_sandbox_spl_test dtv_spl_test = { +\t.intval\t\t\t= 0x1, +}; +U_BOOT_DRVINFO(spl_test) = { +\t.name\t\t= "sandbox_spl_test", +\t.plat\t\t= &dtv_spl_test, +\t.plat_size\t= sizeof(dtv_spl_test), +\t.parent_idx\t= -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, +}; +U_BOOT_DRVINFO(spl_test2) = { +\t.name\t\t= "sandbox_spl_test", +\t.plat\t\t= &dtv_spl_test2, +\t.plat_size\t= sizeof(dtv_spl_test2), +\t.parent_idx\t= -1, +}; + +''', data) + + def test_stdout(self): + """Test output to stdout""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + with test_util.capture_sys_output() as (stdout, _): + self.run_test(['struct'], dtb_file, None) + self._check_strings(self.struct_text, stdout.getvalue()) + + def test_multi_to_file(self): + """Test output of multiple pieces to a single file""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + 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, data) + + def test_no_command(self): + """Test running dtoc without a command""" + with self.assertRaises(ValueError) as exc: + self.run_test([], '', '') + self.assertIn("Please specify a command: struct, platdata", + str(exc.exception)) + + def test_bad_command(self): + """Test running dtoc with an invalid command""" + 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.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""" + with self.assertRaises(ValueError) as exc: + 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)) + + def check_output_dirs(self, instantiate): + # Remove the directory so that files from other tests are not there + tools._RemoveOutputDir() + tools.PrepareOutputDir(None) + + # This should create the .dts and .dtb in the output directory + dtb_file = get_dtb_file('dtoc_test_simple.dts') + outdir = tools.GetOutputDir() + fnames = glob.glob(outdir + '/*') + self.assertEqual(2, len(fnames)) + + dtb_platdata.run_steps( + ['all'], dtb_file, False, None, [outdir], None, instantiate, + warning_disabled=True, scan=copy_scan()) + fnames = glob.glob(outdir + '/*') + return fnames + + def test_output_dirs(self): + """Test outputting files to a directory""" + fnames = self.check_output_dirs(False) + 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-decl.h'}, + leafs) + + def test_output_dirs_inst(self): + """Test outputting files to a directory with instantiation""" + fnames = self.check_output_dirs(True) + self.assertEqual(6, len(fnames)) + + leafs = set(os.path.basename(fname) for fname in fnames) + self.assertEqual( + {'dt-structs-gen.h', 'source.dts', 'source.dtb', + 'dt-uclass.c', 'dt-decl.h', 'dt-device.c'}, + 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_scan() + plat = dtb_platdata.DtbPlatdata(scan, dtb_file, False) + plat.scan_dtb() + plat.scan_tree(False) + 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)) + + 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() + plat.process_nodes(True) + + pmic = scan._drivers['sandbox_pmic'] + self.assertTrue(pmic.used) + + 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') + 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, 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)) + + # 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()) + + 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) + + 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) + + 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]) + + def test_simple_inst(self): + """Test output from some simple nodes with instantiate enabled""" + dtb_file = get_dtb_file('dtoc_test_inst.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) + + 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) + + 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 tsssshat 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()) + + def test_missing_props(self): + """Test detection of a parent node with no properties""" + dtb_file = get_dtb_file('dtoc_test_noprops.dts', capture_stderr=True) + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as exc: + self.run_test(['struct'], dtb_file, output) + self.assertIn("Parent node '/i2c@0' has no properties - do you need", + str(exc.exception)) + + def test_single_reg(self): + """Test detection of a parent node with no properties""" + dtb_file = get_dtb_file('dtoc_test_single_reg.dts') + output = tools.GetOutputFilename('output') + self.run_test(['struct'], dtb_file, output) diff --git a/roms/u-boot/tools/dtoc/test_fdt b/roms/u-boot/tools/dtoc/test_fdt new file mode 120000 index 000000000..7c3b23031 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test_fdt @@ -0,0 +1 @@ +test_fdt.py
\ No newline at end of file diff --git a/roms/u-boot/tools/dtoc/test_fdt.py b/roms/u-boot/tools/dtoc/test_fdt.py new file mode 100755 index 000000000..856392b1b --- /dev/null +++ b/roms/u-boot/tools/dtoc/test_fdt.py @@ -0,0 +1,696 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +from optparse import OptionParser +import glob +import os +import shutil +import sys +import tempfile +import unittest + +# Bring in the patman libraries +our_path = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(1, os.path.join(our_path, '..')) + +from dtoc import fdt +from dtoc import fdt_util +from dtoc.fdt_util import fdt32_to_cpu +from fdt import Type, BytesToValue +import libfdt +from patman import command +from patman import test_util +from patman import tools + +def _GetPropertyValue(dtb, node, prop_name): + """Low-level function to get the property value based on its offset + + This looks directly in the device tree at the property's offset to find + its value. It is useful as a check that the property is in the correct + place. + + Args: + node: Node to look in + prop_name: Property name to find + + Returns: + Tuple: + Prop object found + Value of property as a string (found using property offset) + """ + prop = node.props[prop_name] + + # Add 12, which is sizeof(struct fdt_property), to get to start of data + offset = prop.GetOffset() + 12 + 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 + + This includes unit tests for some functions and functional tests for the fdt + module. + """ + @classmethod + def setUpClass(cls): + tools.PrepareOutputDir(None) + + @classmethod + def tearDownClass(cls): + tools.FinaliseOutputDir() + + def setUp(self): + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) + + def testFdt(self): + """Test that we can open an Fdt""" + self.dtb.Scan() + root = self.dtb.GetRoot() + self.assertTrue(isinstance(root, fdt.Node)) + + def testGetNode(self): + """Test the GetNode() method""" + node = self.dtb.GetNode('/spl-test') + self.assertTrue(isinstance(node, fdt.Node)) + + node = self.dtb.GetNode('/i2c@0/pmic@9') + self.assertTrue(isinstance(node, fdt.Node)) + self.assertEqual('pmic@9', node.name) + self.assertIsNone(self.dtb.GetNode('/i2c@0/pmic@9/missing')) + + node = self.dtb.GetNode('/') + self.assertTrue(isinstance(node, fdt.Node)) + self.assertEqual(0, node.Offset()) + + def testFlush(self): + """Check that we can flush the device tree out to its file""" + fname = self.dtb._fname + with open(fname, 'rb') as fd: + data = fd.read() + os.remove(fname) + with self.assertRaises(IOError): + open(fname, 'rb') + self.dtb.Flush() + with open(fname, 'rb') as fd: + data = fd.read() + + def testPack(self): + """Test that packing a device tree works""" + self.dtb.Pack() + + def testGetFdt(self): + """Tetst that we can access the raw device-tree data""" + self.assertTrue(isinstance(self.dtb.GetContents(), bytearray)) + + def testGetProps(self): + """Tests obtaining a list of properties""" + node = self.dtb.GetNode('/spl-test') + props = self.dtb.GetProps(node) + self.assertEqual(['boolval', 'bytearray', 'byteval', 'compatible', + 'intarray', 'intval', 'longbytearray', 'notstring', + 'stringarray', 'stringval', 'u-boot,dm-pre-reloc'], + sorted(props.keys())) + + def testCheckError(self): + """Tests the ChecKError() function""" + with self.assertRaises(ValueError) as e: + fdt.CheckErr(-libfdt.NOTFOUND, 'hello') + self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception)) + + def testGetFdt(self): + node = self.dtb.GetNode('/spl-test') + self.assertEqual(self.dtb, node.GetFdt()) + + def testBytesToValue(self): + self.assertEqual(BytesToValue(b'this\0is\0'), + (Type.STRING, ['this', 'is'])) + +class TestNode(unittest.TestCase): + """Test operation of the Node class""" + + @classmethod + def setUpClass(cls): + tools.PrepareOutputDir(None) + + @classmethod + def tearDownClass(cls): + tools.FinaliseOutputDir() + + def setUp(self): + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) + self.node = self.dtb.GetNode('/spl-test') + self.fdt = self.dtb.GetFdtObj() + + def testOffset(self): + """Tests that we can obtain the offset of a node""" + self.assertTrue(self.node.Offset() > 0) + + def testDelete(self): + """Tests that we can delete a property""" + node2 = self.dtb.GetNode('/spl-test2') + offset1 = node2.Offset() + self.node.DeleteProp('intval') + offset2 = node2.Offset() + self.assertTrue(offset2 < offset1) + self.node.DeleteProp('intarray') + offset3 = node2.Offset() + self.assertTrue(offset3 < offset2) + with self.assertRaises(libfdt.FdtException): + self.node.DeleteProp('missing') + + def testDeleteGetOffset(self): + """Test that property offset update when properties are deleted""" + self.node.DeleteProp('intval') + prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray') + self.assertEqual(prop.value, value) + + def testFindNode(self): + """Tests that we can find a node using the FindNode() functoin""" + node = self.dtb.GetRoot().FindNode('i2c@0') + self.assertEqual('i2c@0', node.name) + subnode = node.FindNode('pmic@9') + self.assertEqual('pmic@9', subnode.name) + self.assertEqual(None, node.FindNode('missing')) + + def testRefreshMissingNode(self): + """Test refreshing offsets when an extra node is present in dtb""" + # Delete it from our tables, not the device tree + del self.dtb._root.subnodes[-1] + with self.assertRaises(ValueError) as e: + self.dtb.Refresh() + self.assertIn('Internal error, offset', str(e.exception)) + + def testRefreshExtraNode(self): + """Test refreshing offsets when an expected node is missing""" + # Delete it from the device tre, not our tables + self.fdt.del_node(self.node.Offset()) + with self.assertRaises(ValueError) as e: + self.dtb.Refresh() + self.assertIn('Internal error, node name mismatch ' + 'spl-test != spl-test2', str(e.exception)) + + def testRefreshMissingProp(self): + """Test refreshing offsets when an extra property is present in dtb""" + # Delete it from our tables, not the device tree + del self.node.props['notstring'] + with self.assertRaises(ValueError) as e: + self.dtb.Refresh() + self.assertIn("Internal error, node '/spl-test' property 'notstring' missing, offset ", + str(e.exception)) + + def testLookupPhandle(self): + """Test looking up a single phandle""" + dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts')) + node = dtb.GetNode('/phandle-source2') + prop = node.props['clocks'] + target = dtb.GetNode('/phandle-target') + self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value))) + + def testAddNodeSpace(self): + """Test adding a single node when out of space""" + self.fdt.pack() + self.node.AddSubnode('subnode') + with self.assertRaises(libfdt.FdtException) as e: + self.dtb.Sync(auto_resize=False) + self.assertIn('FDT_ERR_NOSPACE', str(e.exception)) + + self.dtb.Sync(auto_resize=True) + offset = self.fdt.path_offset('/spl-test/subnode') + self.assertTrue(offset > 0) + + def testAddNodes(self): + """Test adding various subnode and properies""" + node = self.dtb.GetNode('/i2c@0') + + # Add one more node next to the pmic one + sn1 = node.AddSubnode('node-one') + sn1.AddInt('integer-a', 12) + sn1.AddInt('integer-b', 23) + + # Sync so that everything is clean + self.dtb.Sync(auto_resize=True) + + # Add two subnodes next to pmic and node-one + sn2 = node.AddSubnode('node-two') + sn2.AddInt('integer-2a', 34) + sn2.AddInt('integer-2b', 45) + + sn3 = node.AddSubnode('node-three') + sn3.AddInt('integer-3', 123) + + # Add a property to the node after i2c@0 to check that this is not + # disturbed by adding a subnode to i2c@0 + orig_node = self.dtb.GetNode('/orig-node') + orig_node.AddInt('integer-4', 456) + + # Add a property to the pmic node to check that pmic properties are not + # disturbed + pmic = self.dtb.GetNode('/i2c@0/pmic@9') + pmic.AddInt('integer-5', 567) + + self.dtb.Sync(auto_resize=True) + + def testRefreshNameMismatch(self): + """Test name mismatch when syncing nodes and properties""" + prop = self.node.AddInt('integer-a', 12) + + wrong_offset = self.dtb.GetNode('/i2c@0')._offset + self.node._offset = wrong_offset + with self.assertRaises(ValueError) as e: + self.dtb.Sync() + self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'", + str(e.exception)) + + with self.assertRaises(ValueError) as e: + self.node.Refresh(wrong_offset) + self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'", + str(e.exception)) + + +class TestProp(unittest.TestCase): + """Test operation of the Prop class""" + + @classmethod + def setUpClass(cls): + tools.PrepareOutputDir(None) + + @classmethod + def tearDownClass(cls): + tools.FinaliseOutputDir() + + def setUp(self): + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) + self.node = self.dtb.GetNode('/spl-test') + self.fdt = self.dtb.GetFdtObj() + + def testMissingNode(self): + self.assertEqual(None, self.dtb.GetNode('missing')) + + def testPhandle(self): + 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) + + def _ConvertProp(self, prop_name): + """Helper function to look up a property in self.node and return it + + Args: + Property name to find + + Return fdt.Prop object for this property + """ + p = self.fdt.getprop(self.node.Offset(), prop_name) + return fdt.Prop(self.node, -1, prop_name, p) + + def testMakeProp(self): + """Test we can convert all the the types that are supported""" + prop = self._ConvertProp('boolval') + self.assertEqual(Type.BOOL, prop.type) + self.assertEqual(True, prop.value) + + prop = self._ConvertProp('intval') + self.assertEqual(Type.INT, prop.type) + self.assertEqual(1, fdt32_to_cpu(prop.value)) + + prop = self._ConvertProp('intarray') + self.assertEqual(Type.INT, prop.type) + val = [fdt32_to_cpu(val) for val in prop.value] + self.assertEqual([2, 3, 4], val) + + prop = self._ConvertProp('byteval') + self.assertEqual(Type.BYTE, prop.type) + self.assertEqual(5, ord(prop.value)) + + prop = self._ConvertProp('longbytearray') + self.assertEqual(Type.BYTE, prop.type) + val = [ord(val) for val in prop.value] + self.assertEqual([9, 10, 11, 12, 13, 14, 15, 16, 17], val) + + prop = self._ConvertProp('stringval') + self.assertEqual(Type.STRING, prop.type) + self.assertEqual('message', prop.value) + + prop = self._ConvertProp('stringarray') + self.assertEqual(Type.STRING, prop.type) + self.assertEqual(['multi-word', 'message'], prop.value) + + prop = self._ConvertProp('notstring') + self.assertEqual(Type.BYTE, prop.type) + val = [ord(val) for val in prop.value] + self.assertEqual([0x20, 0x21, 0x22, 0x10, 0], val) + + def testGetEmpty(self): + """Tests the GetEmpty() function for the various supported types""" + self.assertEqual(True, fdt.Prop.GetEmpty(Type.BOOL)) + self.assertEqual(chr(0), fdt.Prop.GetEmpty(Type.BYTE)) + self.assertEqual(tools.GetBytes(0, 4), fdt.Prop.GetEmpty(Type.INT)) + self.assertEqual('', fdt.Prop.GetEmpty(Type.STRING)) + + def testGetOffset(self): + """Test we can get the offset of a property""" + prop, value = _GetPropertyValue(self.dtb, self.node, 'longbytearray') + self.assertEqual(prop.value, value) + + def testWiden(self): + """Test widening of values""" + node2 = self.dtb.GetNode('/spl-test2') + node3 = self.dtb.GetNode('/spl-test3') + prop = self.node.props['intval'] + + # No action + prop2 = node2.props['intval'] + prop.Widen(prop2) + self.assertEqual(Type.INT, prop.type) + self.assertEqual(1, fdt32_to_cpu(prop.value)) + + # Convert singla value to array + prop2 = self.node.props['intarray'] + prop.Widen(prop2) + self.assertEqual(Type.INT, prop.type) + self.assertTrue(isinstance(prop.value, list)) + + # A 4-byte array looks like a single integer. When widened by a longer + # byte array, it should turn into an array. + prop = self.node.props['longbytearray'] + prop2 = node2.props['longbytearray'] + prop3 = node3.props['longbytearray'] + self.assertFalse(isinstance(prop2.value, list)) + self.assertEqual(4, len(prop2.value)) + self.assertEqual(b'\x09\x0a\x0b\x0c', prop2.value) + prop2.Widen(prop) + self.assertTrue(isinstance(prop2.value, list)) + self.assertEqual(9, len(prop2.value)) + self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\0', + '\0', '\0', '\0', '\0'], prop2.value) + prop3.Widen(prop) + self.assertTrue(isinstance(prop3.value, list)) + self.assertEqual(9, len(prop3.value)) + self.assertEqual(['\x09', '\x0a', '\x0b', '\x0c', '\x0d', + '\x0e', '\x0f', '\x10', '\0'], prop3.value) + + # Similarly for a string array + prop = self.node.props['stringval'] + prop2 = node2.props['stringarray'] + self.assertFalse(isinstance(prop.value, list)) + self.assertEqual(7, len(prop.value)) + prop.Widen(prop2) + self.assertTrue(isinstance(prop.value, list)) + self.assertEqual(3, len(prop.value)) + + # Enlarging an existing array + prop = self.node.props['stringarray'] + prop2 = node2.props['stringarray'] + self.assertTrue(isinstance(prop.value, list)) + self.assertEqual(2, len(prop.value)) + prop.Widen(prop2) + self.assertTrue(isinstance(prop.value, list)) + self.assertEqual(3, len(prop.value)) + + def testAdd(self): + """Test adding properties""" + self.fdt.pack() + # This function should automatically expand the device tree + self.node.AddZeroProp('one') + self.node.AddZeroProp('two') + self.node.AddZeroProp('three') + self.dtb.Sync(auto_resize=True) + + # Updating existing properties should be OK, since the device-tree size + # does not change + self.fdt.pack() + self.node.SetInt('one', 1) + self.node.SetInt('two', 2) + self.node.SetInt('three', 3) + self.dtb.Sync(auto_resize=False) + + # This should fail since it would need to increase the device-tree size + self.node.AddZeroProp('four') + with self.assertRaises(libfdt.FdtException) as e: + self.dtb.Sync(auto_resize=False) + self.assertIn('FDT_ERR_NOSPACE', str(e.exception)) + self.dtb.Sync(auto_resize=True) + + def testAddMore(self): + """Test various other methods for adding and setting properties""" + self.node.AddZeroProp('one') + self.dtb.Sync(auto_resize=True) + data = self.fdt.getprop(self.node.Offset(), 'one') + self.assertEqual(0, fdt32_to_cpu(data)) + + self.node.SetInt('one', 1) + self.dtb.Sync(auto_resize=False) + data = self.fdt.getprop(self.node.Offset(), 'one') + self.assertEqual(1, fdt32_to_cpu(data)) + + val = 1234 + self.node.AddInt('integer', val) + self.dtb.Sync(auto_resize=True) + data = self.fdt.getprop(self.node.Offset(), 'integer') + self.assertEqual(val, fdt32_to_cpu(data)) + + val = '123' + chr(0) + '456' + self.node.AddString('string', val) + self.dtb.Sync(auto_resize=True) + data = self.fdt.getprop(self.node.Offset(), 'string') + self.assertEqual(tools.ToBytes(val) + b'\0', data) + + self.fdt.pack() + self.node.SetString('string', val + 'x') + with self.assertRaises(libfdt.FdtException) as e: + self.dtb.Sync(auto_resize=False) + self.assertIn('FDT_ERR_NOSPACE', str(e.exception)) + self.node.SetString('string', val[:-1]) + + prop = self.node.props['string'] + prop.SetData(tools.ToBytes(val)) + self.dtb.Sync(auto_resize=False) + data = self.fdt.getprop(self.node.Offset(), 'string') + self.assertEqual(tools.ToBytes(val), data) + + self.node.AddEmptyProp('empty', 5) + self.dtb.Sync(auto_resize=True) + prop = self.node.props['empty'] + prop.SetData(tools.ToBytes(val)) + self.dtb.Sync(auto_resize=False) + data = self.fdt.getprop(self.node.Offset(), 'empty') + self.assertEqual(tools.ToBytes(val), data) + + self.node.SetData('empty', b'123') + self.assertEqual(b'123', prop.bytes) + + # Trying adding a lot of data at once + self.node.AddData('data', tools.GetBytes(65, 20000)) + self.dtb.Sync(auto_resize=True) + + def testFromData(self): + dtb2 = fdt.Fdt.FromData(self.dtb.GetContents()) + self.assertEqual(dtb2.GetContents(), self.dtb.GetContents()) + + self.node.AddEmptyProp('empty', 5) + self.dtb.Sync(auto_resize=True) + self.assertTrue(dtb2.GetContents() != self.dtb.GetContents()) + + def testMissingSetInt(self): + """Test handling of a missing property with SetInt""" + with self.assertRaises(ValueError) as e: + self.node.SetInt('one', 1) + self.assertIn("node '/spl-test': Missing property 'one'", + str(e.exception)) + + def testMissingSetData(self): + """Test handling of a missing property with SetData""" + with self.assertRaises(ValueError) as e: + self.node.SetData('one', b'data') + self.assertIn("node '/spl-test': Missing property 'one'", + str(e.exception)) + + def testMissingSetString(self): + """Test handling of a missing property with SetString""" + with self.assertRaises(ValueError) as e: + self.node.SetString('one', 1) + self.assertIn("node '/spl-test': Missing property 'one'", + str(e.exception)) + + def testGetFilename(self): + """Test the dtb filename can be provided""" + self.assertEqual(tools.GetOutputFilename('source.dtb'), + self.dtb.GetFilename()) + + +class TestFdtUtil(unittest.TestCase): + """Tests for the fdt_util module + + This module will likely be mostly replaced at some point, once upstream + libfdt has better Python support. For now, this provides tests for current + functionality. + """ + @classmethod + def setUpClass(cls): + tools.PrepareOutputDir(None) + + @classmethod + def tearDownClass(cls): + tools.FinaliseOutputDir() + + def setUp(self): + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) + self.node = self.dtb.GetNode('/spl-test') + + def testGetInt(self): + self.assertEqual(1, fdt_util.GetInt(self.node, 'intval')) + self.assertEqual(3, fdt_util.GetInt(self.node, 'missing', 3)) + + with self.assertRaises(ValueError) as e: + self.assertEqual(3, fdt_util.GetInt(self.node, 'intarray')) + self.assertIn("property 'intarray' has list value: expecting a single " + 'integer', str(e.exception)) + + def testGetString(self): + self.assertEqual('message', fdt_util.GetString(self.node, 'stringval')) + self.assertEqual('test', fdt_util.GetString(self.node, 'missing', + 'test')) + + with self.assertRaises(ValueError) as e: + self.assertEqual(3, fdt_util.GetString(self.node, 'stringarray')) + self.assertIn("property 'stringarray' has list value: expecting a " + 'single string', str(e.exception)) + + def testGetBool(self): + self.assertEqual(True, fdt_util.GetBool(self.node, 'boolval')) + self.assertEqual(False, fdt_util.GetBool(self.node, 'missing')) + self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True)) + self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False)) + + def testGetByte(self): + self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval')) + self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3)) + + with self.assertRaises(ValueError) as e: + fdt_util.GetByte(self.node, 'longbytearray') + self.assertIn("property 'longbytearray' has list value: expecting a " + 'single byte', str(e.exception)) + + with self.assertRaises(ValueError) as e: + fdt_util.GetByte(self.node, 'intval') + self.assertIn("property 'intval' has length 4, expecting 1", + str(e.exception)) + + def testGetPhandleList(self): + 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') + self.assertEqual([1, 2, 11, 3, 12, 13, 1], + fdt_util.GetPhandleList(node, 'clocks')) + self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing')) + + def testGetDataType(self): + self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int)) + self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval', + str)) + with self.assertRaises(ValueError) as e: + self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval', + bool)) + def testFdtCellsToCpu(self): + val = self.node.props['intarray'].value + 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(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)) + + node2 = dtb2.GetNode('/test2') + val = node2.props['reg'].value + self.assertEqual(0x1234567890123456, fdt_util.fdt_cells_to_cpu(val, 2)) + self.assertEqual(0x9876543210987654, fdt_util.fdt_cells_to_cpu(val[2:], + 2)) + self.assertEqual(0x12345678, fdt_util.fdt_cells_to_cpu(val, 1)) + + def testEnsureCompiled(self): + """Test a degenerate case of this function (file already compiled)""" + dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts')) + self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb)) + + def testEnsureCompiledTmpdir(self): + """Test providing a temporary directory""" + try: + old_outdir = tools.outdir + tools.outdir= None + tmpdir = tempfile.mkdtemp(prefix='test_fdt.') + dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'), + tmpdir) + self.assertEqual(tmpdir, os.path.dirname(dtb)) + shutil.rmtree(tmpdir) + finally: + tools.outdir= old_outdir + + +def RunTestCoverage(): + """Run the tests and check that we get 100% coverage""" + test_util.RunTestCoverage('tools/dtoc/test_fdt.py', None, + ['tools/patman/*.py', '*test_fdt.py'], options.build_dir) + + +def RunTests(args): + """Run all the test we have for the fdt model + + Args: + args: List of positional args provided to fdt. This can hold a test + name to execute (as in 'fdt -t testFdt', for example) + """ + result = unittest.TestResult() + sys.argv = [sys.argv[0]] + test_name = args and args[0] or None + for module in (TestFdt, TestNode, TestProp, TestFdtUtil): + if test_name: + try: + suite = unittest.TestLoader().loadTestsFromName(test_name, module) + except AttributeError: + continue + else: + suite = unittest.TestLoader().loadTestsFromTestCase(module) + suite.run(result) + + print(result) + for _, err in result.errors: + print(err) + for _, err in result.failures: + print(err) + +if __name__ != '__main__': + sys.exit(1) + +parser = OptionParser() +parser.add_option('-B', '--build-dir', type='string', default='b', + help='Directory containing the build output') +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', + default=False, help='run tests') +parser.add_option('-T', '--test-coverage', action='store_true', + default=False, help='run tests and check for 100% coverage') +(options, args) = parser.parse_args() + +# Run our meagre tests +if options.test: + RunTests(args) +elif options.test_coverage: + RunTestCoverage() diff --git a/roms/u-boot/tools/dtoc/test_src_scan.py b/roms/u-boot/tools/dtoc/test_src_scan.py new file mode 100644 index 000000000..d6da03849 --- /dev/null +++ b/roms/u-boot/tools/dtoc/test_src_scan.py @@ -0,0 +1,492 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2020 Google LLC +# + +"""Tests for the src_scan module + +This includes unit tests for scanning of the source code +""" + +import copy +import os +import shutil +import tempfile +import unittest +from unittest import mock + +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 + +class TestSrcScan(unittest.TestCase): + """Tests for src_scan""" + @classmethod + def setUpClass(cls): + tools.PrepareOutputDir(None) + + @classmethod + def tearDownClass(cls): + tools.FinaliseOutputDir() + + def test_simple(self): + """Simple test of scanning drivers""" + scan = src_scan.Scanner(None, None) + scan.scan_drivers() + self.assertIn('sandbox_gpio', scan._drivers) + self.assertIn('sandbox_gpio_alias', scan._driver_aliases) + self.assertEqual('sandbox_gpio', + scan._driver_aliases['sandbox_gpio_alias']) + self.assertNotIn('sandbox_gpio_alias2', scan._driver_aliases) + + def test_additional(self): + """Test with additional drivers to scan""" + scan = src_scan.Scanner( + None, [None, '', 'tools/dtoc/test/dtoc_test_scan_drivers.cxx']) + scan.scan_drivers() + self.assertIn('sandbox_gpio_alias2', scan._driver_aliases) + self.assertEqual('sandbox_gpio', + scan._driver_aliases['sandbox_gpio_alias2']) + + def test_unicode_error(self): + """Test running dtoc with an invalid unicode file + + To be able to perform this test without adding a weird text file which + would produce issues when using checkpatch.pl or patman, generate the + file at runtime and then process it. + """ + driver_fn = '/tmp/' + next(tempfile._get_candidate_names()) + with open(driver_fn, 'wb+') as fout: + fout.write(b'\x81') + + scan = src_scan.Scanner(None, [driver_fn]) + with test_util.capture_sys_output() as (stdout, _): + scan.scan_drivers() + self.assertRegex(stdout.getvalue(), + r"Skipping file '.*' due to unicode error\s*") + + def test_driver(self): + """Test the Driver class""" + 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', used=False, 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) + + def test_scan_dirs(self): + """Test scanning of source directories""" + def add_file(fname): + pathname = os.path.join(indir, fname) + dirname = os.path.dirname(pathname) + os.makedirs(dirname, exist_ok=True) + tools.WriteFile(pathname, '', binary=False) + fname_list.append(pathname) + + try: + indir = tempfile.mkdtemp(prefix='dtoc.') + + 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 + with mock.patch.object(src_scan.Scanner, "scan_driver") as mocked: + scan = src_scan.Scanner(indir, None) + scan.scan_drivers() + self.assertEqual(2, len(mocked.mock_calls)) + self.assertEqual(mock.call(fname_list[0]), + mocked.mock_calls[0]) + # .git file should be ignored + self.assertEqual(mock.call(fname_list[2]), + 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, 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} + + # get_normalized_compat_name() uses this to check for root node + node.parent = FakeNode() + + scan = src_scan.Scanner(None, 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(1, len(scan._missing_drivers)) + self.assertEqual({'rockchip_rk3288_grf'}, scan._missing_drivers) + #'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, 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, None) + scan._parse_driver('file.c', buff) + self.assertIn('i2c_tegra', scan._drivers) + 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""" + 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), + DM_PHASE(tpl) + DM_HEADER(<i2c.h>) + DM_HEADER(<asm/clk.h>) +}; +''' + scan = src_scan.Scanner(None, 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('tpl', drv.phase) + self.assertEqual(['<i2c.h>', '<asm/clk.h>'], drv.headers) + 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, 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, 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)) + + 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, 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, None) + 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, 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} + + # get_normalized_compat_name() uses this to check for root node + node.parent = FakeNode() + + 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()) + + def test_sequence(self): + """Test assignment of sequence numnbers""" + scan = src_scan.Scanner(None, 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) |