diff options
Diffstat (limited to 'roms/u-boot/tools/dtoc/dtb_platdata.py')
-rw-r--r-- | roms/u-boot/tools/dtoc/dtb_platdata.py | 1219 |
1 files changed, 1219 insertions, 0 deletions
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 |