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