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    'bootph-all',
39    'bootph-pre-sram',
40    'bootph-pre-ram',
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 bootph-pre-ram 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                # bootph-pre-sram tag but the child has bootph-all. 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 <dm.h>\n')
839        self.out('#include <dt-structs.h>\n')
840        self.out('\n')
841        self.buf('/*\n')
842        self.buf(
843            " * uclass declarations, ordered by 'struct uclass' linker_list idx:\n")
844        uclass_list = self._valid_uclasses
845        for seq, uclass in enumerate(uclass_list):
846            self.buf(' * %3d: %s\n' % (seq, uclass.name))
847        self.buf(' *\n')
848        self.buf(' * Sequence numbers allocated in each uclass:\n')
849        for uclass in uclass_list:
850            if uclass.alias_num_to_node:
851                self.buf(' * %s: %s\n' % (uclass.name, uclass.uclass_id))
852                for seq, node in uclass.alias_num_to_node.items():
853                    self.buf(' *    %d: %s\n' % (seq, node.path))
854        self.buf(' */\n')
855
856        uclass_node = {}
857        for seq, uclass in enumerate(uclass_list):
858            uclass_node[seq] = ('&DM_UCLASS_REF(%s)->sibling_node' %
859                                uclass.name)
860        uclass_node[-1] = '&uclass_head'
861        uclass_node[len(uclass_list)] = '&uclass_head'
862        self.buf('\n')
863        self.buf('struct list_head %s = {\n' % 'uclass_head')
864        self.buf('\t.prev = %s,\n' % uclass_node[len(uclass_list) -1])
865        self.buf('\t.next = %s,\n' % uclass_node[0])
866        self.buf('};\n')
867        self.buf('\n')
868
869        for seq, uclass in enumerate(uclass_list):
870            uc_drv = self._scan._uclass.get(uclass.uclass_id)
871
872            priv_name = self.alloc_priv(uc_drv.priv, uc_drv.name, '')
873
874            self.buf('DM_UCLASS_INST(%s) = {\n' % uclass.name)
875            if priv_name:
876                self.buf('\t.priv_\t\t= %s,\n' % priv_name)
877            self.buf('\t.uc_drv\t\t= DM_UCLASS_DRIVER_REF(%s),\n' % uclass.name)
878            self.list_node('sibling_node', uclass_node, seq)
879            self.list_head('dev_head', 'uclass_node', uc_drv.devs, None)
880            self.buf('};\n')
881            self.buf('\n')
882        self.out(''.join(self.get_buf()))
883
884    def read_aliases(self):
885        """Read the aliases and attach the information to self._alias
886
887        Raises:
888            ValueError: The alias path is not found
889        """
890        alias_node = self._fdt.GetNode('/aliases')
891        if not alias_node:
892            return
893        re_num = re.compile('(^[a-z0-9-]+[a-z]+)([0-9]+)$')
894        for prop in alias_node.props.values():
895            m_alias = re_num.match(prop.name)
896            if not m_alias:
897                raise ValueError("Cannot decode alias '%s'" % prop.name)
898            name, num = m_alias.groups()
899            node = self._fdt.GetNode(prop.value)
900            result = self._scan.add_uclass_alias(name, num, node)
901            if result is None:
902                raise ValueError("Alias '%s' path '%s' not found" %
903                                 (prop.name, prop.value))
904            elif result is False:
905                print("Could not find uclass for alias '%s'" % prop.name)
906
907    def generate_decl(self):
908        nodes_to_output = list(self._valid_nodes)
909
910        self.buf('#include <dm/device-internal.h>\n')
911        self.buf('#include <dm/uclass-internal.h>\n')
912        self.buf('\n')
913        self.buf(
914            '/* driver declarations - these allow DM_DRIVER_GET() to be used */\n')
915        for node in nodes_to_output:
916            self.buf('extern U_BOOT_DRIVER(%s);\n' % node.struct_name);
917        self.buf('\n')
918
919        if self._instantiate:
920            self.buf(
921                '/* device declarations - these allow DM_DEVICE_REF() to be used */\n')
922            for node in nodes_to_output:
923                self.buf('extern DM_DEVICE_INST(%s);\n' % node.var_name)
924            self.buf('\n')
925
926        uclass_list = self._valid_uclasses
927
928        self.buf(
929            '/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */\n')
930        for uclass in uclass_list:
931            self.buf('extern UCLASS_DRIVER(%s);\n' % uclass.name)
932
933        if self._instantiate:
934            self.buf('\n')
935            self.buf('/* uclass declarations - needed for DM_UCLASS_REF() */\n')
936            for uclass in uclass_list:
937                self.buf('extern DM_UCLASS_INST(%s);\n' % uclass.name)
938        self.out(''.join(self.get_buf()))
939
940    def assign_seqs(self):
941        """Assign a sequence number to each node"""
942        for node in self._valid_nodes_unsorted:
943            seq = self._scan.assign_seq(node)
944            if seq is not None:
945                node.seq = seq
946
947    def process_nodes(self, need_drivers):
948        nodes_to_output = list(self._valid_nodes)
949
950        # Figure out which drivers we actually use
951        self._scan.mark_used(nodes_to_output)
952
953        for node in nodes_to_output:
954            node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name
955            driver = self._scan.get_driver(node.struct_name)
956            if not driver:
957                if not need_drivers:
958                    continue
959                raise ValueError("Cannot parse/find driver for '%s'" %
960                                 node.struct_name)
961            node.driver = driver
962            uclass = self._scan._uclass.get(driver.uclass_id)
963            if not uclass:
964                raise ValueError("Cannot parse/find uclass '%s' for driver '%s'" %
965                                (driver.uclass_id, node.struct_name))
966            node.uclass = uclass
967            node.uclass_seq = len(node.uclass.devs)
968            node.uclass.devs.append(node)
969            uclass.node_refs[node.uclass_seq] = \
970                '&%s->uclass_node' % node.dev_ref
971
972            parent_driver = None
973            if node.parent in self._valid_nodes:
974                parent_driver = self._scan.get_driver(node.parent.struct_name)
975                if not parent_driver:
976                    if not need_drivers:
977                        continue
978                    raise ValueError(
979                        "Cannot parse/find parent driver '%s' for '%s'" %
980                        (node.parent.struct_name, node.struct_name))
981                node.parent_seq = len(node.parent.child_devs)
982                node.parent.child_devs.append(node)
983                node.parent.child_refs[node.parent_seq] = \
984                    '&%s->sibling_node' % node.dev_ref
985                node.parent_driver = parent_driver
986
987        for node in nodes_to_output:
988            ref = '&%s->child_head' % node.dev_ref
989            node.child_refs[-1] = ref
990            node.child_refs[len(node.child_devs)] = ref
991
992        uclass_set = set()
993        for driver in self._scan._drivers.values():
994            if driver.used and driver.uclass:
995                uclass_set.add(driver.uclass)
996        self._valid_uclasses = sorted(list(uclass_set),
997                                      key=lambda uc: uc.uclass_id)
998
999        for seq, uclass in enumerate(uclass_set):
1000            ref = '&DM_UCLASS_REF(%s)->dev_head' % uclass.name
1001            uclass.node_refs[-1] = ref
1002            uclass.node_refs[len(uclass.devs)] = ref
1003
1004    def output_node_plat(self, node):
1005        """Output the C code for a node
1006
1007        Args:
1008            node (fdt.Node): node to output
1009        """
1010        driver = node.driver
1011        parent_driver = node.parent_driver
1012
1013        line1 = 'Node %s index %d' % (node.path, node.idx)
1014        if driver:
1015            self.buf('/*\n')
1016            self.buf(' * %s\n' % line1)
1017            self.buf(' * driver %s parent %s\n' % (driver.name,
1018                parent_driver.name if parent_driver else 'None'))
1019            self.buf(' */\n')
1020        else:
1021            self.buf('/* %s */\n' % line1)
1022
1023        self._output_values(node)
1024        self._declare_device(node)
1025
1026        self.out(''.join(self.get_buf()))
1027
1028    def output_node_instance(self, node):
1029        """Output the C code for a node
1030
1031        Args:
1032            node (fdt.Node): node to output
1033        """
1034        parent_driver = node.parent_driver
1035
1036        self.buf('/*\n')
1037        self.buf(' * Node %s index %d\n' % (node.path, node.idx))
1038        self.buf(' * driver %s parent %s\n' % (node.driver.name,
1039                 parent_driver.name if parent_driver else 'None'))
1040        self.buf('*/\n')
1041
1042        if not node.driver.plat:
1043            self._output_values(node)
1044        self._declare_device_inst(node, parent_driver)
1045
1046        self.out(''.join(self.get_buf()))
1047
1048    def generate_plat(self):
1049        """Generate device defintions for the platform data
1050
1051        This writes out C platform data initialisation data and
1052        U_BOOT_DRVINFO() declarations for each valid node. Where a node has
1053        multiple compatible strings, a #define is used to make them equivalent.
1054
1055        See the documentation in doc/driver-model/of-plat.rst for more
1056        information.
1057        """
1058        self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
1059        self.out('#define DT_PLAT_C\n')
1060        self.out('\n')
1061        self.out('#include <dm.h>\n')
1062        self.out('#include <dt-structs.h>\n')
1063        self.out('\n')
1064
1065        if self._valid_nodes:
1066            self.out('/*\n')
1067            self.out(
1068                " * driver_info declarations, ordered by 'struct driver_info' linker_list idx:\n")
1069            self.out(' *\n')
1070            self.out(' * idx  %-20s %-s\n' % ('driver_info', 'driver'))
1071            self.out(' * ---  %-20s %-s\n' % ('-' * 20, '-' * 20))
1072            for node in self._valid_nodes:
1073                self.out(' * %3d: %-20s %-s\n' %
1074                        (node.idx, node.var_name, node.struct_name))
1075            self.out(' * ---  %-20s %-s\n' % ('-' * 20, '-' * 20))
1076            self.out(' */\n')
1077            self.out('\n')
1078
1079            for node in self._valid_nodes:
1080                self.output_node_plat(node)
1081
1082        self.out(''.join(self.get_buf()))
1083
1084    def generate_device(self):
1085        """Generate device instances
1086
1087        This writes out DM_DEVICE_INST() records for each device in the
1088        build.
1089
1090        See the documentation in doc/driver-model/of-plat.rst for more
1091        information.
1092        """
1093        self.out('#include <dm.h>\n')
1094        self.out('#include <dt-structs.h>\n')
1095        self.out('\n')
1096
1097        if self._valid_nodes:
1098            self.out('/*\n')
1099            self.out(
1100                " * udevice declarations, ordered by 'struct udevice' linker_list position:\n")
1101            self.out(' *\n')
1102            self.out(' * idx  %-20s %-s\n' % ('udevice', 'driver'))
1103            self.out(' * ---  %-20s %-s\n' % ('-' * 20, '-' * 20))
1104            for node in self._valid_nodes:
1105                self.out(' * %3d: %-20s %-s\n' %
1106                        (node.idx, node.var_name, node.struct_name))
1107            self.out(' * ---  %-20s %-s\n' % ('-' * 20, '-' * 20))
1108            self.out(' */\n')
1109            self.out('\n')
1110
1111            for node in self._valid_nodes:
1112                self.output_node_instance(node)
1113
1114        self.out(''.join(self.get_buf()))
1115
1116
1117# Types of output file we understand
1118# key: Command used to generate this file
1119# value: OutputFile for this command
1120OUTPUT_FILES_COMMON = {
1121    'decl':
1122        OutputFile(Ftype.HEADER, 'dt-decl.h', DtbPlatdata.generate_decl,
1123                   'Declares externs for all device/uclass instances'),
1124    'struct':
1125        OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
1126                   DtbPlatdata.generate_structs,
1127                   'Defines the structs used to hold devicetree data'),
1128    }
1129
1130# File generated without instantiate
1131OUTPUT_FILES_NOINST = {
1132    'platdata':
1133        OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
1134                   'Declares the U_BOOT_DRIVER() records and platform data'),
1135    }
1136
1137# File generated with instantiate
1138OUTPUT_FILES_INST = {
1139    'device':
1140        OutputFile(Ftype.SOURCE, 'dt-device.c', DtbPlatdata.generate_device,
1141                   'Declares the DM_DEVICE_INST() records'),
1142    'uclass':
1143        OutputFile(Ftype.SOURCE, 'dt-uclass.c', DtbPlatdata.generate_uclasses,
1144                   'Declares the uclass instances (struct uclass)'),
1145    }
1146
1147
1148def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase,
1149              instantiate, warning_disabled=False, drivers_additional=None,
1150              basedir=None, scan=None):
1151    """Run all the steps of the dtoc tool
1152
1153    Args:
1154        args (list): List of non-option arguments provided to the problem
1155        dtb_file (str): Filename of dtb file to process
1156        include_disabled (bool): True to include disabled nodes
1157        output (str): Name of output file (None for stdout)
1158        output_dirs (tuple of str):
1159            Directory to put C output files
1160            Directory to put H output files
1161        phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
1162             or 'tpl'. None if not known
1163        instantiate: Instantiate devices so they don't need to be bound at
1164            run-time
1165        warning_disabled (bool): True to avoid showing warnings about missing
1166            drivers
1167        drivers_additional (list): List of additional drivers to use during
1168            scanning
1169        basedir (str): Base directory of U-Boot source code. Defaults to the
1170            grandparent of this file's directory
1171        scan (src_src.Scanner): Scanner from a previous run. This can help speed
1172            up tests. Use None for normal operation
1173
1174    Returns:
1175        DtbPlatdata object
1176
1177    Raises:
1178        ValueError: if args has no command, or an unknown command
1179    """
1180    if not args:
1181        raise ValueError('Please specify a command: struct, platdata, all')
1182    if output and output_dirs and any(output_dirs):
1183        raise ValueError('Must specify either output or output_dirs, not both')
1184
1185    if not scan:
1186        scan = src_scan.Scanner(basedir, drivers_additional, phase)
1187        scan.scan_drivers()
1188        do_process = True
1189    else:
1190        do_process = False
1191    plat = DtbPlatdata(scan, dtb_file, include_disabled, instantiate)
1192    plat.scan_dtb()
1193    plat.scan_tree(add_root=instantiate)
1194    plat.prepare_nodes()
1195    plat.scan_reg_sizes()
1196    plat.setup_output_dirs(output_dirs)
1197    plat.scan_structs()
1198    plat.scan_phandles()
1199    plat.process_nodes(instantiate)
1200    plat.read_aliases()
1201    plat.assign_seqs()
1202
1203    # Figure out what output files we plan to generate
1204    output_files = dict(OUTPUT_FILES_COMMON)
1205    if instantiate:
1206        output_files.update(OUTPUT_FILES_INST)
1207    else:
1208        output_files.update(OUTPUT_FILES_NOINST)
1209
1210    cmds = args[0].split(',')
1211    if 'all' in cmds:
1212        cmds = sorted(output_files.keys())
1213    for cmd in cmds:
1214        outfile = output_files.get(cmd)
1215        if not outfile:
1216            raise ValueError("Unknown command '%s': (use: %s)" %
1217                             (cmd, ', '.join(sorted(output_files.keys()))))
1218        plat.setup_output(outfile.ftype,
1219                          outfile.fname if output_dirs else output)
1220        plat.out_header(outfile)
1221        outfile.method(plat)
1222    plat.finish_output()
1223
1224    if not warning_disabled:
1225        scan.show_warnings()
1226    return plat
1227