uboot/tools/dtoc/dtb_platdata.py
<<
>>
Prefs
   1#!/usr/bin/python
   2# SPDX-License-Identifier: GPL-2.0+
   3#
   4# Copyright (C) 2017 Google, Inc
   5# Written by Simon Glass <sjg@chromium.org>
   6#
   7
   8"""Device tree to platform data class
   9
  10This supports converting device tree data to C structures definitions and
  11static data.
  12
  13See doc/driver-model/of-plat.rst for more informaiton
  14"""
  15
  16import collections
  17import copy
  18from enum import IntEnum
  19import os
  20import re
  21import sys
  22
  23from dtoc import fdt
  24from dtoc import fdt_util
  25from dtoc import src_scan
  26from dtoc.src_scan import conv_name_to_c
  27
  28# When we see these properties we ignore them - i.e. do not create a structure
  29# member
  30PROP_IGNORE_LIST = [
  31    '#address-cells',
  32    '#gpio-cells',
  33    '#size-cells',
  34    'compatible',
  35    'linux,phandle',
  36    "status",
  37    'phandle',
  38    'u-boot,dm-pre-reloc',
  39    'u-boot,dm-tpl',
  40    'u-boot,dm-spl',
  41]
  42
  43# C type declarations for the types we support
  44TYPE_NAMES = {
  45    fdt.Type.INT: 'fdt32_t',
  46    fdt.Type.BYTE: 'unsigned char',
  47    fdt.Type.STRING: 'const char *',
  48    fdt.Type.BOOL: 'bool',
  49    fdt.Type.INT64: 'fdt64_t',
  50}
  51
  52STRUCT_PREFIX = 'dtd_'
  53VAL_PREFIX = 'dtv_'
  54
  55# Properties which are considered to be phandles
  56#    key: property name
  57#    value: name of associated #cells property in the target node
  58#
  59# New phandle properties must be added here; otherwise they will come through as
  60# simple integers and finding devices by phandle will not work.
  61# Any property that ends with one of these (e.g. 'cd-gpios') will be considered
  62# a phandle property.
  63PHANDLE_PROPS = {
  64    'clocks': '#clock-cells',
  65    'interrupts-extended': '#interrupt-cells',
  66    'gpios': '#gpio-cells',
  67    'sandbox,emul': '#emul-cells',
  68    }
  69
  70class Ftype(IntEnum):
  71    SOURCE, HEADER = range(2)
  72
  73
  74# This holds information about each type of output file dtoc can create
  75# ftype: Type of file (Ftype)
  76# fname: Filename excluding directory, e.g. 'dt-plat.c'
  77# hdr_comment: Comment explaining the purpose of the file
  78OutputFile = collections.namedtuple('OutputFile',
  79                                    ['ftype', 'fname', 'method', 'hdr_comment'])
  80
  81# This holds information about a property which includes phandles.
  82#
  83# max_args: integer: Maximum number or arguments that any phandle uses (int).
  84# args: Number of args for each phandle in the property. The total number of
  85#     phandles is len(args). This is a list of integers.
  86PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
  87
  88# Holds a single phandle link, allowing a C struct value to be assigned to point
  89# to a device
  90#
  91# var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node')
  92# dev_name: Name of device to assign to (e.g. 'clock')
  93PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name'])
  94
  95
  96def tab_to(num_tabs, line):
  97    """Append tabs to a line of text to reach a tab stop.
  98
  99    Args:
 100        num_tabs (int): Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
 101        line (str): Line of text to append to
 102
 103    Returns:
 104        str: line with the correct number of tabs appeneded. If the line already
 105        extends past that tab stop then a single space is appended.
 106    """
 107    if len(line) >= num_tabs * 8:
 108        return line + ' '
 109    return line + '\t' * (num_tabs - len(line) // 8)
 110
 111def get_value(ftype, value):
 112    """Get a value as a C expression
 113
 114    For integers this returns a byte-swapped (little-endian) hex string
 115    For bytes this returns a hex string, e.g. 0x12
 116    For strings this returns a literal string enclosed in quotes
 117    For booleans this return 'true'
 118
 119    Args:
 120        ftype (fdt.Type): Data type (fdt_util)
 121        value (bytes): Data value, as a string of bytes
 122
 123    Returns:
 124        str: String representation of the value
 125    """
 126    if ftype == fdt.Type.INT:
 127        val = '%#x' % fdt_util.fdt32_to_cpu(value)
 128    elif ftype == fdt.Type.BYTE:
 129        char = value[0]
 130        val = '%#x' % (ord(char) if isinstance(char, str) else char)
 131    elif ftype == fdt.Type.STRING:
 132        # Handle evil ACPI backslashes by adding another backslash before them.
 133        # So "\\_SB.GPO0" in the device tree effectively stays like that in C
 134        val = '"%s"' % value.replace('\\', '\\\\')
 135    elif ftype == fdt.Type.BOOL:
 136        val = 'true'
 137    else:  # ftype == fdt.Type.INT64:
 138        val = '%#x' % value
 139    return val
 140
 141
 142class DtbPlatdata():
 143    """Provide a means to convert device tree binary data to platform data
 144
 145    The output of this process is C structures which can be used in space-
 146    constrained encvironments where the ~3KB code overhead of device tree
 147    code is not affordable.
 148
 149    Properties:
 150        _scan: Scan object, for scanning and reporting on useful information
 151            from the U-Boot source code
 152        _fdt: Fdt object, referencing the device tree
 153        _dtb_fname: Filename of the input device tree binary file
 154        _valid_nodes_unsorted: A list of Node object with compatible strings,
 155            ordered by devicetree node order
 156        _valid_nodes: A list of Node object with compatible strings, ordered by
 157            conv_name_to_c(node.name)
 158        _include_disabled: true to include nodes marked status = "disabled"
 159        _outfile: The current output file (sys.stdout or a real file)
 160        _lines: Stashed list of output lines for outputting in the future
 161        _dirname: Directory to hold output files, or None for none (all files
 162            go to stdout)
 163        _struct_data (dict): OrderedDict of dtplat structures to output
 164            key (str): Node name, as a C identifier
 165                    value: dict containing structure fields:
 166                        key (str): Field name
 167                        value: Prop object with field information
 168        _basedir (str): Base directory of source tree
 169        _valid_uclasses (list of src_scan.Uclass): List of uclasses needed for
 170            the selected devices (see _valid_node), in alphabetical order
 171        _instantiate: Instantiate devices so they don't need to be bound at
 172            run-time
 173    """
 174    def __init__(self, scan, dtb_fname, include_disabled, instantiate=False):
 175        self._scan = scan
 176        self._fdt = None
 177        self._dtb_fname = dtb_fname
 178        self._valid_nodes = None
 179        self._valid_nodes_unsorted = None
 180        self._include_disabled = include_disabled
 181        self._outfile = None
 182        self._lines = []
 183        self._dirnames = [None] * len(Ftype)
 184        self._struct_data = collections.OrderedDict()
 185        self._basedir = None
 186        self._valid_uclasses = None
 187        self._instantiate = instantiate
 188
 189    def setup_output_dirs(self, output_dirs):
 190        """Set up the output directories
 191
 192        This should be done before setup_output() is called
 193
 194        Args:
 195            output_dirs (tuple of str):
 196                Directory to use for C output files.
 197                    Use None to write files relative current directory
 198                Directory to use for H output files.
 199                    Defaults to the C output dir
 200        """
 201        def process_dir(ftype, dirname):
 202            if dirname:
 203                os.makedirs(dirname, exist_ok=True)
 204                self._dirnames[ftype] = dirname
 205
 206        if output_dirs:
 207            c_dirname = output_dirs[0]
 208            h_dirname = output_dirs[1] if len(output_dirs) > 1 else c_dirname
 209            process_dir(Ftype.SOURCE, c_dirname)
 210            process_dir(Ftype.HEADER, h_dirname)
 211
 212    def setup_output(self, ftype, fname):
 213        """Set up the output destination
 214
 215        Once this is done, future calls to self.out() will output to this
 216        file. The file used is as follows:
 217
 218        self._dirnames[ftype] is None: output to fname, or stdout if None
 219        self._dirnames[ftype] is not None: output to fname in that directory
 220
 221        Calling this function multiple times will close the old file and open
 222        the new one. If they are the same file, nothing happens and output will
 223        continue to the same file.
 224
 225        Args:
 226            ftype (str): Type of file to create ('c' or 'h')
 227            fname (str): Filename to send output to. If there is a directory in
 228                self._dirnames for this file type, it will be put in that
 229                directory
 230        """
 231        dirname = self._dirnames[ftype]
 232        if dirname:
 233            pathname = os.path.join(dirname, fname)
 234            if self._outfile:
 235                self._outfile.close()
 236            self._outfile = open(pathname, 'w')
 237        elif fname:
 238            if not self._outfile:
 239                self._outfile = open(fname, 'w')
 240        else:
 241            self._outfile = sys.stdout
 242
 243    def finish_output(self):
 244        """Finish outputing to a file
 245
 246        This closes the output file, if one is in use
 247        """
 248        if self._outfile != sys.stdout:
 249            self._outfile.close()
 250            self._outfile = None
 251
 252    def out(self, line):
 253        """Output a string to the output file
 254
 255        Args:
 256            line (str): String to output
 257        """
 258        self._outfile.write(line)
 259
 260    def buf(self, line):
 261        """Buffer up a string to send later
 262
 263        Args:
 264            line (str): String to add to our 'buffer' list
 265        """
 266        self._lines.append(line)
 267
 268    def get_buf(self):
 269        """Get the contents of the output buffer, and clear it
 270
 271        Returns:
 272            list(str): The output buffer, which is then cleared for future use
 273        """
 274        lines = self._lines
 275        self._lines = []
 276        return lines
 277
 278    def out_header(self, outfile):
 279        """Output a message indicating that this is an auto-generated file
 280
 281        Args:
 282            outfile: OutputFile describing the file being generated
 283        """
 284        self.out('''/*
 285 * DO NOT MODIFY
 286 *
 287 * %s.
 288 * This was generated by dtoc from a .dtb (device tree binary) file.
 289 */
 290
 291''' % outfile.hdr_comment)
 292
 293    def get_phandle_argc(self, prop, node_name):
 294        """Check if a node contains phandles
 295
 296        We have no reliable way of detecting whether a node uses a phandle
 297        or not. As an interim measure, use a list of known property names.
 298
 299        Args:
 300            prop (fdt.Prop): Prop object to check
 301            node_name (str): Node name, only used for raising an error
 302        Returns:
 303            int or None: Number of argument cells is this is a phandle,
 304                else None
 305        Raises:
 306            ValueError: if the phandle cannot be parsed or the required property
 307                is not present
 308        """
 309        cells_prop = None
 310        for name, cprop in PHANDLE_PROPS.items():
 311            if prop.name.endswith(name):
 312                cells_prop = cprop
 313        if cells_prop:
 314            if not isinstance(prop.value, list):
 315                prop.value = [prop.value]
 316            val = prop.value
 317            i = 0
 318
 319            max_args = 0
 320            args = []
 321            while i < len(val):
 322                phandle = fdt_util.fdt32_to_cpu(val[i])
 323                # If we get to the end of the list, stop. This can happen
 324                # since some nodes have more phandles in the list than others,
 325                # but we allocate enough space for the largest list. So those
 326                # nodes with shorter lists end up with zeroes at the end.
 327                if not phandle:
 328                    break
 329                target = self._fdt.phandle_to_node.get(phandle)
 330                if not target:
 331                    raise ValueError("Cannot parse '%s' in node '%s'" %
 332                                     (prop.name, node_name))
 333                cells = target.props.get(cells_prop)
 334                if not cells:
 335                    raise ValueError("Node '%s' has no cells property" %
 336                                     target.name)
 337                num_args = fdt_util.fdt32_to_cpu(cells.value)
 338                max_args = max(max_args, num_args)
 339                args.append(num_args)
 340                i += 1 + num_args
 341            return PhandleInfo(max_args, args)
 342        return None
 343
 344    def scan_dtb(self):
 345        """Scan the device tree to obtain a tree of nodes and properties
 346
 347        Once this is done, self._fdt.GetRoot() can be called to obtain the
 348        device tree root node, and progress from there.
 349        """
 350        self._fdt = fdt.FdtScan(self._dtb_fname)
 351
 352    def scan_node(self, node, valid_nodes):
 353        """Scan a node and subnodes to build a tree of node and phandle info
 354
 355        This adds each subnode to self._valid_nodes if it is enabled and has a
 356        compatible string.
 357
 358        Args:
 359            node (Node): Node for scan for subnodes
 360            valid_nodes (list of Node): List of Node objects to add to
 361        """
 362        for subnode in node.subnodes:
 363            if 'compatible' in subnode.props:
 364                status = subnode.props.get('status')
 365                if (not self._include_disabled and not status or
 366                        status.value != 'disabled'):
 367                    valid_nodes.append(subnode)
 368
 369            # recurse to handle any subnodes
 370            self.scan_node(subnode, valid_nodes)
 371
 372    def scan_tree(self, add_root):
 373        """Scan the device tree for useful information
 374
 375        This fills in the following properties:
 376            _valid_nodes_unsorted: A list of nodes we wish to consider include
 377                in the platform data (in devicetree node order)
 378            _valid_nodes: Sorted version of _valid_nodes_unsorted
 379
 380        Args:
 381            add_root: True to add the root node also (which wouldn't normally
 382                be added as it may not have a compatible string)
 383        """
 384        root = self._fdt.GetRoot()
 385        valid_nodes = []
 386        if add_root:
 387            valid_nodes.append(root)
 388        self.scan_node(root, valid_nodes)
 389        self._valid_nodes_unsorted = valid_nodes
 390        self._valid_nodes = sorted(valid_nodes,
 391                                   key=lambda x: conv_name_to_c(x.name))
 392
 393    def prepare_nodes(self):
 394        """Add extra properties to the nodes we are using
 395
 396        The following properties are added for use by dtoc:
 397            idx: Index number of this node (0=first, etc.)
 398            struct_name: Name of the struct dtd used by this node
 399            var_name: C name for this node
 400            child_devs: List of child devices for this node, each a None
 401            child_refs: Dict of references for each child:
 402                key: Position in child list (-1=head, 0=first, 1=second, ...
 403                                             n-1=last, n=head)
 404            seq: Sequence number of the device (unique within its uclass), or
 405                -1 not not known yet
 406            dev_ref: Reference to this device, e.g. 'DM_DEVICE_REF(serial)'
 407            driver: Driver record for this node, or None if not known
 408            uclass: Uclass record for this node, or None if not known
 409            uclass_seq: Position of this device within the uclass list (0=first,
 410                n-1=last)
 411            parent_seq: Position of this device within it siblings (0=first,
 412                n-1=last)
 413            parent_driver: Driver record of the node's parent, or None if none.
 414                We don't use node.parent.driver since node.parent may not be in
 415                the list of valid nodes
 416        """
 417        for idx, node in enumerate(self._valid_nodes):
 418            node.idx = idx
 419            node.struct_name, _ = self._scan.get_normalized_compat_name(node)
 420            node.var_name = conv_name_to_c(node.name)
 421            node.child_devs = []
 422            node.child_refs = {}
 423            node.seq = -1
 424            node.dev_ref = None
 425            node.driver = None
 426            node.uclass = None
 427            node.uclass_seq = None
 428            node.parent_seq = None
 429            node.parent_driver = None
 430
 431    @staticmethod
 432    def get_num_cells(node):
 433        """Get the number of cells in addresses and sizes for this node
 434
 435        Args:
 436            node (fdt.None): Node to check
 437
 438        Returns:
 439            Tuple:
 440                Number of address cells for this node
 441                Number of size cells for this node
 442        """
 443        parent = node.parent
 444        if parent and not parent.props:
 445            raise ValueError("Parent node '%s' has no properties - do you need u-boot,dm-spl or similar?" %
 446                             parent.path)
 447        num_addr, num_size = 2, 2
 448        if parent:
 449            addr_prop = parent.props.get('#address-cells')
 450            size_prop = parent.props.get('#size-cells')
 451            if addr_prop:
 452                num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
 453            if size_prop:
 454                num_size = fdt_util.fdt32_to_cpu(size_prop.value)
 455        return num_addr, num_size
 456
 457    def scan_reg_sizes(self):
 458        """Scan for 64-bit 'reg' properties and update the values
 459
 460        This finds 'reg' properties with 64-bit data and converts the value to
 461        an array of 64-values. This allows it to be output in a way that the
 462        C code can read.
 463        """
 464        for node in self._valid_nodes:
 465            reg = node.props.get('reg')
 466            if not reg:
 467                continue
 468            num_addr, num_size = self.get_num_cells(node)
 469            total = num_addr + num_size
 470
 471            if reg.type != fdt.Type.INT:
 472                raise ValueError("Node '%s' reg property is not an int" %
 473                                 node.name)
 474            if not isinstance(reg.value, list):
 475                reg.value = [reg.value]
 476            if len(reg.value) % total:
 477                raise ValueError(
 478                    "Node '%s' (parent '%s') reg property has %d cells "
 479                    'which is not a multiple of na + ns = %d + %d)' %
 480                    (node.name, node.parent.name, len(reg.value), num_addr,
 481                     num_size))
 482            reg.num_addr = num_addr
 483            reg.num_size = num_size
 484            if num_addr > 1 or num_size > 1:
 485                reg.type = fdt.Type.INT64
 486                i = 0
 487                new_value = []
 488                val = reg.value
 489                while i < len(val):
 490                    addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
 491                    i += num_addr
 492                    size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
 493                    i += num_size
 494                    new_value += [addr, size]
 495                reg.value = new_value
 496
 497    def scan_structs(self):
 498        """Scan the device tree building up the C structures we will use.
 499
 500        Build a dict keyed by C struct name containing a dict of Prop
 501        object for each struct field (keyed by property name). Where the
 502        same struct appears multiple times, try to use the 'widest'
 503        property, i.e. the one with a type which can express all others.
 504
 505        Once the widest property is determined, all other properties are
 506        updated to match that width.
 507
 508        The results are written to self._struct_data
 509        """
 510        structs = self._struct_data
 511        for node in self._valid_nodes:
 512            fields = {}
 513
 514            # Get a list of all the valid properties in this node.
 515            for name, prop in node.props.items():
 516                if name not in PROP_IGNORE_LIST and name[0] != '#':
 517                    fields[name] = copy.deepcopy(prop)
 518
 519            # If we've seen this struct_name before, update the existing struct
 520            if node.struct_name in structs:
 521                struct = structs[node.struct_name]
 522                for name, prop in fields.items():
 523                    oldprop = struct.get(name)
 524                    if oldprop:
 525                        oldprop.Widen(prop)
 526                    else:
 527                        struct[name] = prop
 528
 529            # Otherwise store this as a new struct.
 530            else:
 531                structs[node.struct_name] = fields
 532
 533        for node in self._valid_nodes:
 534            struct = structs[node.struct_name]
 535            for name, prop in node.props.items():
 536                if name not in PROP_IGNORE_LIST and name[0] != '#':
 537                    prop.Widen(struct[name])
 538
 539    def scan_phandles(self):
 540        """Figure out what phandles each node uses
 541
 542        We need to be careful when outputing nodes that use phandles since
 543        they must come after the declaration of the phandles in the C file.
 544        Otherwise we get a compiler error since the phandle struct is not yet
 545        declared.
 546
 547        This function adds to each node a list of phandle nodes that the node
 548        depends on. This allows us to output things in the right order.
 549        """
 550        for node in self._valid_nodes:
 551            node.phandles = set()
 552            for pname, prop in node.props.items():
 553                if pname in PROP_IGNORE_LIST or pname[0] == '#':
 554                    continue
 555                info = self.get_phandle_argc(prop, node.name)
 556                if info:
 557                    # Process the list as pairs of (phandle, id)
 558                    pos = 0
 559                    for args in info.args:
 560                        phandle_cell = prop.value[pos]
 561                        phandle = fdt_util.fdt32_to_cpu(phandle_cell)
 562                        target_node = self._fdt.phandle_to_node[phandle]
 563                        node.phandles.add(target_node)
 564                        pos += 1 + args
 565
 566
 567    def generate_structs(self):
 568        """Generate struct defintions for the platform data
 569
 570        This writes out the body of a header file consisting of structure
 571        definitions for node in self._valid_nodes. See the documentation in
 572        doc/driver-model/of-plat.rst for more information.
 573        """
 574        structs = self._struct_data
 575        self.out('#include <stdbool.h>\n')
 576        self.out('#include <linux/libfdt.h>\n')
 577
 578        # Output the struct definition
 579        for name in sorted(structs):
 580            self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
 581            for pname in sorted(structs[name]):
 582                prop = structs[name][pname]
 583                info = self.get_phandle_argc(prop, structs[name])
 584                if info:
 585                    # For phandles, include a reference to the target
 586                    struct_name = 'struct phandle_%d_arg' % info.max_args
 587                    self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
 588                                             conv_name_to_c(prop.name),
 589                                             len(info.args)))
 590                else:
 591                    ptype = TYPE_NAMES[prop.type]
 592                    self.out('\t%s%s' % (tab_to(2, ptype),
 593                                         conv_name_to_c(prop.name)))
 594                    if isinstance(prop.value, list):
 595                        self.out('[%d]' % len(prop.value))
 596                self.out(';\n')
 597            self.out('};\n')
 598
 599    def _output_list(self, node, prop):
 600        """Output the C code for a devicetree property that holds a list
 601
 602        Args:
 603            node (fdt.Node): Node to output
 604            prop (fdt.Prop): Prop to output
 605        """
 606        self.buf('{')
 607        vals = []
 608        # For phandles, output a reference to the platform data
 609        # of the target node.
 610        info = self.get_phandle_argc(prop, node.name)
 611        if info:
 612            # Process the list as pairs of (phandle, id)
 613            pos = 0
 614            for args in info.args:
 615                phandle_cell = prop.value[pos]
 616                phandle = fdt_util.fdt32_to_cpu(phandle_cell)
 617                target_node = self._fdt.phandle_to_node[phandle]
 618                arg_values = []
 619                for i in range(args):
 620                    arg_values.append(
 621                        str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
 622                pos += 1 + args
 623                vals.append('\t{%d, {%s}}' % (target_node.idx,
 624                                              ', '.join(arg_values)))
 625            for val in vals:
 626                self.buf('\n\t\t%s,' % val)
 627        else:
 628            for val in prop.value:
 629                vals.append(get_value(prop.type, val))
 630
 631            # Put 8 values per line to avoid very long lines.
 632            for i in range(0, len(vals), 8):
 633                if i:
 634                    self.buf(',\n\t\t')
 635                self.buf(', '.join(vals[i:i + 8]))
 636        self.buf('}')
 637
 638    def _declare_device(self, node):
 639        """Add a device declaration to the output
 640
 641        This declares a U_BOOT_DRVINFO() for the device being processed
 642
 643        Args:
 644            node: Node to process
 645        """
 646        self.buf('U_BOOT_DRVINFO(%s) = {\n' % node.var_name)
 647        self.buf('\t.name\t\t= "%s",\n' % node.struct_name)
 648        self.buf('\t.plat\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
 649        self.buf('\t.plat_size\t= sizeof(%s%s),\n' %
 650                 (VAL_PREFIX, node.var_name))
 651        idx = -1
 652        if node.parent and node.parent in self._valid_nodes:
 653            idx = node.parent.idx
 654        self.buf('\t.parent_idx\t= %d,\n' % idx)
 655        self.buf('};\n')
 656        self.buf('\n')
 657
 658    def prep_priv(self, struc, name, suffix, section='.priv_data'):
 659        if not struc:
 660            return None
 661        var_name = '_%s%s' % (name, suffix)
 662        hdr = self._scan._structs.get(struc)
 663        if hdr:
 664            self.buf('#include <%s>\n' % hdr.fname)
 665        else:
 666            print('Warning: Cannot find header file for struct %s' % struc)
 667        attr = '__attribute__ ((section ("%s")))' % section
 668        return var_name, struc, attr
 669
 670    def alloc_priv(self, info, name, extra, suffix='_priv'):
 671        result = self.prep_priv(info, name, suffix)
 672        if not result:
 673            return None
 674        var_name, struc, section = result
 675        self.buf('u8 %s_%s[sizeof(struct %s)]\n\t%s;\n' %
 676                 (var_name, extra, struc.strip(), section))
 677        return '%s_%s' % (var_name, extra)
 678
 679    def alloc_plat(self, info, name, extra, node):
 680        result = self.prep_priv(info, name, '_plat')
 681        if not result:
 682            return None
 683        var_name, struc, section = result
 684        self.buf('struct %s %s\n\t%s_%s = {\n' %
 685                 (struc.strip(), section, var_name, extra))
 686        self.buf('\t.dtplat = {\n')
 687        for pname in sorted(node.props):
 688            self._output_prop(node, node.props[pname], 2)
 689        self.buf('\t},\n')
 690        self.buf('};\n')
 691        return '&%s_%s' % (var_name, extra)
 692
 693    def _declare_device_inst(self, node, parent_driver):
 694        """Add a device instance declaration to the output
 695
 696        This declares a DM_DEVICE_INST() for the device being processed
 697
 698        Args:
 699            node: Node to output
 700        """
 701        driver = node.driver
 702        uclass = node.uclass
 703        self.buf('\n')
 704        num_lines = len(self._lines)
 705        plat_name = self.alloc_plat(driver.plat, driver.name, node.var_name,
 706                                    node)
 707        priv_name = self.alloc_priv(driver.priv, driver.name, node.var_name)
 708        parent_plat_name = None
 709        parent_priv_name = None
 710        if parent_driver:
 711            # TODO: deal with uclass providing these values
 712            parent_plat_name = self.alloc_priv(
 713                parent_driver.child_plat, driver.name, node.var_name,
 714                '_parent_plat')
 715            parent_priv_name = self.alloc_priv(
 716                parent_driver.child_priv, driver.name, node.var_name,
 717                '_parent_priv')
 718        uclass_plat_name = self.alloc_priv(
 719            uclass.per_dev_plat, driver.name + '_uc', node.var_name, 'plat')
 720        uclass_priv_name = self.alloc_priv(uclass.per_dev_priv,
 721                                           driver.name + '_uc', node.var_name)
 722        for hdr in driver.headers:
 723            self.buf('#include %s\n' % hdr)
 724
 725        # Add a blank line if we emitted any stuff above, for readability
 726        if num_lines != len(self._lines):
 727            self.buf('\n')
 728
 729        self.buf('DM_DEVICE_INST(%s) = {\n' % node.var_name)
 730        self.buf('\t.driver\t\t= DM_DRIVER_REF(%s),\n' % node.struct_name)
 731        self.buf('\t.name\t\t= "%s",\n' % node.struct_name)
 732        if plat_name:
 733            self.buf('\t.plat_\t\t= %s,\n' % plat_name)
 734        else:
 735            self.buf('\t.plat_\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
 736        if parent_plat_name:
 737            self.buf('\t.parent_plat_\t= %s,\n' % parent_plat_name)
 738        if uclass_plat_name:
 739            self.buf('\t.uclass_plat_\t= %s,\n' % uclass_plat_name)
 740        driver_date = None
 741
 742        if node != self._fdt.GetRoot():
 743            compat_list = node.props['compatible'].value
 744            if not isinstance(compat_list, list):
 745                compat_list = [compat_list]
 746            for compat in compat_list:
 747                driver_data = driver.compat.get(compat)
 748                if driver_data:
 749                    self.buf('\t.driver_data\t= %s,\n' % driver_data)
 750                    break
 751
 752        if node.parent and node.parent.parent:
 753            if node.parent not in self._valid_nodes:
 754                # This might indicate that the parent node is not in the
 755                # SPL/TPL devicetree but the child is. For example if we are
 756                # dealing with of-platdata in TPL, the parent has a
 757                # u-boot,dm-tpl tag but the child has u-boot,dm-pre-reloc. In
 758                # this case the child node exists in TPL but the parent does
 759                # not.
 760                raise ValueError("Node '%s' requires parent node '%s' but it is not in the valid list" %
 761                                 (node.path, node.parent.path))
 762            self.buf('\t.parent\t\t= DM_DEVICE_REF(%s),\n' %
 763                     node.parent.var_name)
 764        if priv_name:
 765            self.buf('\t.priv_\t\t= %s,\n' % priv_name)
 766        self.buf('\t.uclass\t\t= DM_UCLASS_REF(%s),\n' % uclass.name)
 767
 768        if uclass_priv_name:
 769            self.buf('\t.uclass_priv_ = %s,\n' % uclass_priv_name)
 770        if parent_priv_name:
 771            self.buf('\t.parent_priv_\t= %s,\n' % parent_priv_name)
 772        self.list_node('uclass_node', uclass.node_refs, node.uclass_seq)
 773        self.list_head('child_head', 'sibling_node', node.child_devs, node.var_name)
 774        if node.parent in self._valid_nodes:
 775            self.list_node('sibling_node', node.parent.child_refs,
 776                           node.parent_seq)
 777        # flags is left as 0
 778
 779        self.buf('\t.seq_ = %d,\n' % node.seq)
 780
 781        self.buf('};\n')
 782        self.buf('\n')
 783        return parent_plat_name
 784
 785    def _output_prop(self, node, prop, tabs=1):
 786        """Output a line containing the value of a struct member
 787
 788        Args:
 789            node (Node): Node being output
 790            prop (Prop): Prop object to output
 791        """
 792        if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
 793            return
 794        member_name = conv_name_to_c(prop.name)
 795        self.buf('%s%s= ' % ('\t' * tabs, tab_to(3, '.' + member_name)))
 796
 797        # Special handling for lists
 798        if isinstance(prop.value, list):
 799            self._output_list(node, prop)
 800        else:
 801            self.buf(get_value(prop.type, prop.value))
 802        self.buf(',\n')
 803
 804    def _output_values(self, node):
 805        """Output the definition of a device's struct values
 806
 807        Args:
 808            node (Node): Node to output
 809        """
 810        self.buf('static struct %s%s %s%s = {\n' %
 811                 (STRUCT_PREFIX, node.struct_name, VAL_PREFIX, node.var_name))
 812        for pname in sorted(node.props):
 813            self._output_prop(node, node.props[pname])
 814        self.buf('};\n')
 815
 816    def list_head(self, head_member, node_member, node_refs, var_name):
 817        self.buf('\t.%s\t= {\n' % head_member)
 818        if node_refs:
 819            last = node_refs[-1].dev_ref
 820            first = node_refs[0].dev_ref
 821            member = node_member
 822        else:
 823            last = 'DM_DEVICE_REF(%s)' % var_name
 824            first = last
 825            member = head_member
 826        self.buf('\t\t.prev = &%s->%s,\n' % (last, member))
 827        self.buf('\t\t.next = &%s->%s,\n' % (first, member))
 828        self.buf('\t},\n')
 829
 830    def list_node(self, member, node_refs, seq):
 831        self.buf('\t.%s\t= {\n' % member)
 832        self.buf('\t\t.prev = %s,\n' % node_refs[seq - 1])
 833        self.buf('\t\t.next = %s,\n' % node_refs[seq + 1])
 834        self.buf('\t},\n')
 835
 836    def generate_uclasses(self):
 837        self.out('\n')
 838        self.out('#include <common.h>\n')
 839        self.out('#include <dm.h>\n')
 840        self.out('#include <dt-structs.h>\n')
 841        self.out('\n')
 842        self.buf('/*\n')
 843        self.buf(
 844            " * uclass declarations, ordered by 'struct uclass' linker_list idx:\n")
 845        uclass_list = self._valid_uclasses
 846        for seq, uclass in enumerate(uclass_list):
 847            self.buf(' * %3d: %s\n' % (seq, uclass.name))
 848        self.buf(' *\n')
 849        self.buf(' * Sequence numbers allocated in each uclass:\n')
 850        for uclass in uclass_list:
 851            if uclass.alias_num_to_node:
 852                self.buf(' * %s: %s\n' % (uclass.name, uclass.uclass_id))
 853                for seq, node in uclass.alias_num_to_node.items():
 854                    self.buf(' *    %d: %s\n' % (seq, node.path))
 855        self.buf(' */\n')
 856
 857        uclass_node = {}
 858        for seq, uclass in enumerate(uclass_list):
 859            uclass_node[seq] = ('&DM_UCLASS_REF(%s)->sibling_node' %
 860                                uclass.name)
 861        uclass_node[-1] = '&uclass_head'
 862        uclass_node[len(uclass_list)] = '&uclass_head'
 863        self.buf('\n')
 864        self.buf('struct list_head %s = {\n' % 'uclass_head')
 865        self.buf('\t.prev = %s,\n' % uclass_node[len(uclass_list) -1])
 866        self.buf('\t.next = %s,\n' % uclass_node[0])
 867        self.buf('};\n')
 868        self.buf('\n')
 869
 870        for seq, uclass in enumerate(uclass_list):
 871            uc_drv = self._scan._uclass.get(uclass.uclass_id)
 872
 873            priv_name = self.alloc_priv(uc_drv.priv, uc_drv.name, '')
 874
 875            self.buf('DM_UCLASS_INST(%s) = {\n' % uclass.name)
 876            if priv_name:
 877                self.buf('\t.priv_\t\t= %s,\n' % priv_name)
 878            self.buf('\t.uc_drv\t\t= DM_UCLASS_DRIVER_REF(%s),\n' % uclass.name)
 879            self.list_node('sibling_node', uclass_node, seq)
 880            self.list_head('dev_head', 'uclass_node', uc_drv.devs, None)
 881            self.buf('};\n')
 882            self.buf('\n')
 883        self.out(''.join(self.get_buf()))
 884
 885    def read_aliases(self):
 886        """Read the aliases and attach the information to self._alias
 887
 888        Raises:
 889            ValueError: The alias path is not found
 890        """
 891        alias_node = self._fdt.GetNode('/aliases')
 892        if not alias_node:
 893            return
 894        re_num = re.compile('(^[a-z0-9-]+[a-z]+)([0-9]+)$')
 895        for prop in alias_node.props.values():
 896            m_alias = re_num.match(prop.name)
 897            if not m_alias:
 898                raise ValueError("Cannot decode alias '%s'" % prop.name)
 899            name, num = m_alias.groups()
 900            node = self._fdt.GetNode(prop.value)
 901            result = self._scan.add_uclass_alias(name, num, node)
 902            if result is None:
 903                raise ValueError("Alias '%s' path '%s' not found" %
 904                                 (prop.name, prop.value))
 905            elif result is False:
 906                print("Could not find uclass for alias '%s'" % prop.name)
 907
 908    def generate_decl(self):
 909        nodes_to_output = list(self._valid_nodes)
 910
 911        self.buf('#include <dm/device-internal.h>\n')
 912        self.buf('#include <dm/uclass-internal.h>\n')
 913        self.buf('\n')
 914        self.buf(
 915            '/* driver declarations - these allow DM_DRIVER_GET() to be used */\n')
 916        for node in nodes_to_output:
 917            self.buf('extern U_BOOT_DRIVER(%s);\n' % node.struct_name);
 918        self.buf('\n')
 919
 920        if self._instantiate:
 921            self.buf(
 922                '/* device declarations - these allow DM_DEVICE_REF() to be used */\n')
 923            for node in nodes_to_output:
 924                self.buf('extern DM_DEVICE_INST(%s);\n' % node.var_name)
 925            self.buf('\n')
 926
 927        uclass_list = self._valid_uclasses
 928
 929        self.buf(
 930            '/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */\n')
 931        for uclass in uclass_list:
 932            self.buf('extern UCLASS_DRIVER(%s);\n' % uclass.name)
 933
 934        if self._instantiate:
 935            self.buf('\n')
 936            self.buf('/* uclass declarations - needed for DM_UCLASS_REF() */\n')
 937            for uclass in uclass_list:
 938                self.buf('extern DM_UCLASS_INST(%s);\n' % uclass.name)
 939        self.out(''.join(self.get_buf()))
 940
 941    def assign_seqs(self):
 942        """Assign a sequence number to each node"""
 943        for node in self._valid_nodes_unsorted:
 944            seq = self._scan.assign_seq(node)
 945            if seq is not None:
 946                node.seq = seq
 947
 948    def process_nodes(self, need_drivers):
 949        nodes_to_output = list(self._valid_nodes)
 950
 951        # Figure out which drivers we actually use
 952        self._scan.mark_used(nodes_to_output)
 953
 954        for node in nodes_to_output:
 955            node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name
 956            driver = self._scan.get_driver(node.struct_name)
 957            if not driver:
 958                if not need_drivers:
 959                    continue
 960                raise ValueError("Cannot parse/find driver for '%s'" %
 961                                 node.struct_name)
 962            node.driver = driver
 963            uclass = self._scan._uclass.get(driver.uclass_id)
 964            if not uclass:
 965                raise ValueError("Cannot parse/find uclass '%s' for driver '%s'" %
 966                                (driver.uclass_id, node.struct_name))
 967            node.uclass = uclass
 968            node.uclass_seq = len(node.uclass.devs)
 969            node.uclass.devs.append(node)
 970            uclass.node_refs[node.uclass_seq] = \
 971                '&%s->uclass_node' % node.dev_ref
 972
 973            parent_driver = None
 974            if node.parent in self._valid_nodes:
 975                parent_driver = self._scan.get_driver(node.parent.struct_name)
 976                if not parent_driver:
 977                    if not need_drivers:
 978                        continue
 979                    raise ValueError(
 980                        "Cannot parse/find parent driver '%s' for '%s'" %
 981                        (node.parent.struct_name, node.struct_name))
 982                node.parent_seq = len(node.parent.child_devs)
 983                node.parent.child_devs.append(node)
 984                node.parent.child_refs[node.parent_seq] = \
 985                    '&%s->sibling_node' % node.dev_ref
 986                node.parent_driver = parent_driver
 987
 988        for node in nodes_to_output:
 989            ref = '&%s->child_head' % node.dev_ref
 990            node.child_refs[-1] = ref
 991            node.child_refs[len(node.child_devs)] = ref
 992
 993        uclass_set = set()
 994        for driver in self._scan._drivers.values():
 995            if driver.used and driver.uclass:
 996                uclass_set.add(driver.uclass)
 997        self._valid_uclasses = sorted(list(uclass_set),
 998                                      key=lambda uc: uc.uclass_id)
 999
1000        for seq, uclass in enumerate(uclass_set):
1001            ref = '&DM_UCLASS_REF(%s)->dev_head' % uclass.name
1002            uclass.node_refs[-1] = ref
1003            uclass.node_refs[len(uclass.devs)] = ref
1004
1005    def output_node_plat(self, node):
1006        """Output the C code for a node
1007
1008        Args:
1009            node (fdt.Node): node to output
1010        """
1011        driver = node.driver
1012        parent_driver = node.parent_driver
1013
1014        line1 = 'Node %s index %d' % (node.path, node.idx)
1015        if driver:
1016            self.buf('/*\n')
1017            self.buf(' * %s\n' % line1)
1018            self.buf(' * driver %s parent %s\n' % (driver.name,
1019                parent_driver.name if parent_driver else 'None'))
1020            self.buf(' */\n')
1021        else:
1022            self.buf('/* %s */\n' % line1)
1023
1024        self._output_values(node)
1025        self._declare_device(node)
1026
1027        self.out(''.join(self.get_buf()))
1028
1029    def output_node_instance(self, node):
1030        """Output the C code for a node
1031
1032        Args:
1033            node (fdt.Node): node to output
1034        """
1035        parent_driver = node.parent_driver
1036
1037        self.buf('/*\n')
1038        self.buf(' * Node %s index %d\n' % (node.path, node.idx))
1039        self.buf(' * driver %s parent %s\n' % (node.driver.name,
1040                 parent_driver.name if parent_driver else 'None'))
1041        self.buf('*/\n')
1042
1043        if not node.driver.plat:
1044            self._output_values(node)
1045        self._declare_device_inst(node, parent_driver)
1046
1047        self.out(''.join(self.get_buf()))
1048
1049    def generate_plat(self):
1050        """Generate device defintions for the platform data
1051
1052        This writes out C platform data initialisation data and
1053        U_BOOT_DRVINFO() declarations for each valid node. Where a node has
1054        multiple compatible strings, a #define is used to make them equivalent.
1055
1056        See the documentation in doc/driver-model/of-plat.rst for more
1057        information.
1058        """
1059        self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
1060        self.out('#define DT_PLAT_C\n')
1061        self.out('\n')
1062        self.out('#include <common.h>\n')
1063        self.out('#include <dm.h>\n')
1064        self.out('#include <dt-structs.h>\n')
1065        self.out('\n')
1066
1067        if self._valid_nodes:
1068            self.out('/*\n')
1069            self.out(
1070                " * driver_info declarations, ordered by 'struct driver_info' linker_list idx:\n")
1071            self.out(' *\n')
1072            self.out(' * idx  %-20s %-s\n' % ('driver_info', 'driver'))
1073            self.out(' * ---  %-20s %-s\n' % ('-' * 20, '-' * 20))
1074            for node in self._valid_nodes:
1075                self.out(' * %3d: %-20s %-s\n' %
1076                        (node.idx, node.var_name, node.struct_name))
1077            self.out(' * ---  %-20s %-s\n' % ('-' * 20, '-' * 20))
1078            self.out(' */\n')
1079            self.out('\n')
1080
1081            for node in self._valid_nodes:
1082                self.output_node_plat(node)
1083
1084        self.out(''.join(self.get_buf()))
1085
1086    def generate_device(self):
1087        """Generate device instances
1088
1089        This writes out DM_DEVICE_INST() records for each device in the
1090        build.
1091
1092        See the documentation in doc/driver-model/of-plat.rst for more
1093        information.
1094        """
1095        self.out('#include <common.h>\n')
1096        self.out('#include <dm.h>\n')
1097        self.out('#include <dt-structs.h>\n')
1098        self.out('\n')
1099
1100        if self._valid_nodes:
1101            self.out('/*\n')
1102            self.out(
1103                " * udevice declarations, ordered by 'struct udevice' linker_list position:\n")
1104            self.out(' *\n')
1105            self.out(' * idx  %-20s %-s\n' % ('udevice', 'driver'))
1106            self.out(' * ---  %-20s %-s\n' % ('-' * 20, '-' * 20))
1107            for node in self._valid_nodes:
1108                self.out(' * %3d: %-20s %-s\n' %
1109                        (node.idx, node.var_name, node.struct_name))
1110            self.out(' * ---  %-20s %-s\n' % ('-' * 20, '-' * 20))
1111            self.out(' */\n')
1112            self.out('\n')
1113
1114            for node in self._valid_nodes:
1115                self.output_node_instance(node)
1116
1117        self.out(''.join(self.get_buf()))
1118
1119
1120# Types of output file we understand
1121# key: Command used to generate this file
1122# value: OutputFile for this command
1123OUTPUT_FILES_COMMON = {
1124    'decl':
1125        OutputFile(Ftype.HEADER, 'dt-decl.h', DtbPlatdata.generate_decl,
1126                   'Declares externs for all device/uclass instances'),
1127    'struct':
1128        OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
1129                   DtbPlatdata.generate_structs,
1130                   'Defines the structs used to hold devicetree data'),
1131    }
1132
1133# File generated without instantiate
1134OUTPUT_FILES_NOINST = {
1135    'platdata':
1136        OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
1137                   'Declares the U_BOOT_DRIVER() records and platform data'),
1138    }
1139
1140# File generated with instantiate
1141OUTPUT_FILES_INST = {
1142    'device':
1143        OutputFile(Ftype.SOURCE, 'dt-device.c', DtbPlatdata.generate_device,
1144                   'Declares the DM_DEVICE_INST() records'),
1145    'uclass':
1146        OutputFile(Ftype.SOURCE, 'dt-uclass.c', DtbPlatdata.generate_uclasses,
1147                   'Declares the uclass instances (struct uclass)'),
1148    }
1149
1150
1151def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase,
1152              instantiate, warning_disabled=False, drivers_additional=None,
1153              basedir=None, scan=None):
1154    """Run all the steps of the dtoc tool
1155
1156    Args:
1157        args (list): List of non-option arguments provided to the problem
1158        dtb_file (str): Filename of dtb file to process
1159        include_disabled (bool): True to include disabled nodes
1160        output (str): Name of output file (None for stdout)
1161        output_dirs (tuple of str):
1162            Directory to put C output files
1163            Directory to put H output files
1164        phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
1165             or 'tpl'. None if not known
1166        instantiate: Instantiate devices so they don't need to be bound at
1167            run-time
1168        warning_disabled (bool): True to avoid showing warnings about missing
1169            drivers
1170        drivers_additional (list): List of additional drivers to use during
1171            scanning
1172        basedir (str): Base directory of U-Boot source code. Defaults to the
1173            grandparent of this file's directory
1174        scan (src_src.Scanner): Scanner from a previous run. This can help speed
1175            up tests. Use None for normal operation
1176
1177    Returns:
1178        DtbPlatdata object
1179
1180    Raises:
1181        ValueError: if args has no command, or an unknown command
1182    """
1183    if not args:
1184        raise ValueError('Please specify a command: struct, platdata, all')
1185    if output and output_dirs and any(output_dirs):
1186        raise ValueError('Must specify either output or output_dirs, not both')
1187
1188    if not scan:
1189        scan = src_scan.Scanner(basedir, drivers_additional, phase)
1190        scan.scan_drivers()
1191        do_process = True
1192    else:
1193        do_process = False
1194    plat = DtbPlatdata(scan, dtb_file, include_disabled, instantiate)
1195    plat.scan_dtb()
1196    plat.scan_tree(add_root=instantiate)
1197    plat.prepare_nodes()
1198    plat.scan_reg_sizes()
1199    plat.setup_output_dirs(output_dirs)
1200    plat.scan_structs()
1201    plat.scan_phandles()
1202    plat.process_nodes(instantiate)
1203    plat.read_aliases()
1204    plat.assign_seqs()
1205
1206    # Figure out what output files we plan to generate
1207    output_files = dict(OUTPUT_FILES_COMMON)
1208    if instantiate:
1209        output_files.update(OUTPUT_FILES_INST)
1210    else:
1211        output_files.update(OUTPUT_FILES_NOINST)
1212
1213    cmds = args[0].split(',')
1214    if 'all' in cmds:
1215        cmds = sorted(output_files.keys())
1216    for cmd in cmds:
1217        outfile = output_files.get(cmd)
1218        if not outfile:
1219            raise ValueError("Unknown command '%s': (use: %s)" %
1220                             (cmd, ', '.join(sorted(output_files.keys()))))
1221        plat.setup_output(outfile.ftype,
1222                          outfile.fname if output_dirs else output)
1223        plat.out_header(outfile)
1224        outfile.method(plat)
1225    plat.finish_output()
1226
1227    if not warning_disabled:
1228        scan.show_warnings()
1229    return plat
1230