blob: af21156659bbe32c887d880c32e675aea3f98f15 [file] [log] [blame]
Simon Glass7581c012017-06-18 22:08:58 -06001#!/usr/bin/python
Tom Rini83d290c2018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Simon Glass7581c012017-06-18 22:08:58 -06003#
4# Copyright (C) 2017 Google, Inc
5# Written by Simon Glass <sjg@chromium.org>
6#
Simon Glass7581c012017-06-18 22:08:58 -06007
Simon Glass2be282c2017-06-18 22:08:59 -06008"""Device tree to platform data class
9
10This supports converting device tree data to C structures definitions and
11static data.
Simon Glass9b330382020-11-08 20:36:21 -070012
13See doc/driver-model/of-plat.rst for more informaiton
Simon Glass2be282c2017-06-18 22:08:59 -060014"""
15
Simon Glass8fed2eb2017-08-29 14:15:55 -060016import collections
Simon Glass7581c012017-06-18 22:08:58 -060017import copy
Simon Glassbe44f272020-12-28 20:34:51 -070018from enum import IntEnum
Walter Lozanodac82282020-07-03 08:07:17 -030019import os
20import re
Simon Glass2be282c2017-06-18 22:08:59 -060021import sys
Simon Glass7581c012017-06-18 22:08:58 -060022
Simon Glassbf776672020-04-17 18:09:04 -060023from dtoc import fdt
24from dtoc import fdt_util
Simon Glassa542a702020-12-28 20:35:06 -070025from dtoc import src_scan
26from dtoc.src_scan import conv_name_to_c
Simon Glass7581c012017-06-18 22:08:58 -060027
Simon Glass9b330382020-11-08 20:36:21 -070028# When we see these properties we ignore them - i.e. do not create a structure
29# member
Simon Glass7581c012017-06-18 22:08:58 -060030PROP_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
Simon Glass5ea9dcc2020-11-08 20:36:17 -070043# C type declarations for the types we support
Simon Glass7581c012017-06-18 22:08:58 -060044TYPE_NAMES = {
Simon Glass5ea9dcc2020-11-08 20:36:17 -070045 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',
Simon Glass2be282c2017-06-18 22:08:59 -060050}
Simon Glass7581c012017-06-18 22:08:58 -060051
52STRUCT_PREFIX = 'dtd_'
53VAL_PREFIX = 'dtv_'
54
Simon Glassbe44f272020-12-28 20:34:51 -070055class Ftype(IntEnum):
56 SOURCE, HEADER = range(2)
57
58
59# This holds information about each type of output file dtoc can create
60# type: Type of file (Ftype)
Simon Glassd1055d62020-12-28 20:35:00 -070061# fname: Filename excluding directory, e.g. 'dt-plat.c'
62# hdr_comment: Comment explaining the purpose of the file
63OutputFile = collections.namedtuple('OutputFile',
Simon Glassa7d5f962020-12-28 20:35:02 -070064 ['ftype', 'fname', 'method', 'hdr_comment'])
Simon Glassbe44f272020-12-28 20:34:51 -070065
Simon Glass8fed2eb2017-08-29 14:15:55 -060066# This holds information about a property which includes phandles.
67#
68# max_args: integer: Maximum number or arguments that any phandle uses (int).
69# args: Number of args for each phandle in the property. The total number of
70# phandles is len(args). This is a list of integers.
71PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
72
Simon Glass97136eb2020-10-03 09:25:19 -060073# Holds a single phandle link, allowing a C struct value to be assigned to point
74# to a device
75#
76# var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node')
77# dev_name: Name of device to assign to (e.g. 'clock')
78PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name'])
79
Simon Glass8fed2eb2017-08-29 14:15:55 -060080
Simon Glass2be282c2017-06-18 22:08:59 -060081def tab_to(num_tabs, line):
82 """Append tabs to a line of text to reach a tab stop.
Simon Glass7581c012017-06-18 22:08:58 -060083
Simon Glass2be282c2017-06-18 22:08:59 -060084 Args:
Simon Glass9b330382020-11-08 20:36:21 -070085 num_tabs (int): Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
86 line (str): Line of text to append to
Simon Glass2be282c2017-06-18 22:08:59 -060087
88 Returns:
Simon Glass9b330382020-11-08 20:36:21 -070089 str: line with the correct number of tabs appeneded. If the line already
Simon Glass2be282c2017-06-18 22:08:59 -060090 extends past that tab stop then a single space is appended.
91 """
92 if len(line) >= num_tabs * 8:
93 return line + ' '
94 return line + '\t' * (num_tabs - len(line) // 8)
95
Simon Glass56e0bbe2017-06-18 22:09:02 -060096def get_value(ftype, value):
97 """Get a value as a C expression
98
99 For integers this returns a byte-swapped (little-endian) hex string
100 For bytes this returns a hex string, e.g. 0x12
101 For strings this returns a literal string enclosed in quotes
102 For booleans this return 'true'
103
104 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700105 ftype (fdt.Type): Data type (fdt_util)
106 value (bytes): Data value, as a string of bytes
107
108 Returns:
109 str: String representation of the value
Simon Glass56e0bbe2017-06-18 22:09:02 -0600110 """
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700111 if ftype == fdt.Type.INT:
Simon Glassccc3da72020-12-23 08:11:19 -0700112 val = '%#x' % fdt_util.fdt32_to_cpu(value)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700113 elif ftype == fdt.Type.BYTE:
Simon Glass78128d52020-12-03 16:55:16 -0700114 char = value[0]
Simon Glassccc3da72020-12-23 08:11:19 -0700115 val = '%#x' % (ord(char) if isinstance(char, str) else char)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700116 elif ftype == fdt.Type.STRING:
Simon Glassf02d0eb2020-07-07 21:32:06 -0600117 # Handle evil ACPI backslashes by adding another backslash before them.
118 # So "\\_SB.GPO0" in the device tree effectively stays like that in C
Simon Glassccc3da72020-12-23 08:11:19 -0700119 val = '"%s"' % value.replace('\\', '\\\\')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700120 elif ftype == fdt.Type.BOOL:
Simon Glassccc3da72020-12-23 08:11:19 -0700121 val = 'true'
Simon Glass9b330382020-11-08 20:36:21 -0700122 else: # ftype == fdt.Type.INT64:
Simon Glassccc3da72020-12-23 08:11:19 -0700123 val = '%#x' % value
124 return val
Simon Glass56e0bbe2017-06-18 22:09:02 -0600125
Simon Glass56e0bbe2017-06-18 22:09:02 -0600126
Simon Glassccc3da72020-12-23 08:11:19 -0700127class DtbPlatdata():
Simon Glass7581c012017-06-18 22:08:58 -0600128 """Provide a means to convert device tree binary data to platform data
129
130 The output of this process is C structures which can be used in space-
131 constrained encvironments where the ~3KB code overhead of device tree
132 code is not affordable.
133
134 Properties:
Simon Glassa542a702020-12-28 20:35:06 -0700135 _scan: Scan object, for scanning and reporting on useful information
136 from the U-Boot source code
Simon Glass2be282c2017-06-18 22:08:59 -0600137 _fdt: Fdt object, referencing the device tree
Simon Glass7581c012017-06-18 22:08:58 -0600138 _dtb_fname: Filename of the input device tree binary file
Simon Glass074197a2021-02-03 06:01:09 -0700139 _valid_nodes_unsorted: A list of Node object with compatible strings,
140 ordered by devicetree node order
141 _valid_nodes: A list of Node object with compatible strings, ordered by
142 conv_name_to_c(node.name)
Simon Glasse36024b2017-06-18 22:09:01 -0600143 _include_disabled: true to include nodes marked status = "disabled"
Simon Glass7581c012017-06-18 22:08:58 -0600144 _outfile: The current output file (sys.stdout or a real file)
145 _lines: Stashed list of output lines for outputting in the future
Simon Glassbe44f272020-12-28 20:34:51 -0700146 _dirname: Directory to hold output files, or None for none (all files
147 go to stdout)
Simon Glassa7d5f962020-12-28 20:35:02 -0700148 _struct_data (dict): OrderedDict of dtplat structures to output
149 key (str): Node name, as a C identifier
150 value: dict containing structure fields:
151 key (str): Field name
152 value: Prop object with field information
Simon Glass1e0f3f42020-12-28 20:35:03 -0700153 _basedir (str): Base directory of source tree
Simon Glass337d6972021-02-03 06:01:10 -0700154 _valid_uclasses (list of src_scan.Uclass): List of uclasses needed for
155 the selected devices (see _valid_node), in alphabetical order
Simon Glass7581c012017-06-18 22:08:58 -0600156 """
Simon Glassa542a702020-12-28 20:35:06 -0700157 def __init__(self, scan, dtb_fname, include_disabled):
158 self._scan = scan
Simon Glass2be282c2017-06-18 22:08:59 -0600159 self._fdt = None
Simon Glass7581c012017-06-18 22:08:58 -0600160 self._dtb_fname = dtb_fname
161 self._valid_nodes = None
Simon Glass074197a2021-02-03 06:01:09 -0700162 self._valid_nodes_unsorted = None
Simon Glasse36024b2017-06-18 22:09:01 -0600163 self._include_disabled = include_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600164 self._outfile = None
165 self._lines = []
Simon Glassbe44f272020-12-28 20:34:51 -0700166 self._dirnames = [None] * len(Ftype)
Simon Glassa7d5f962020-12-28 20:35:02 -0700167 self._struct_data = collections.OrderedDict()
Simon Glass1e0f3f42020-12-28 20:35:03 -0700168 self._basedir = None
Simon Glass337d6972021-02-03 06:01:10 -0700169 self._valid_uclasses = None
Walter Lozanodac82282020-07-03 08:07:17 -0300170
Simon Glassbe44f272020-12-28 20:34:51 -0700171 def setup_output_dirs(self, output_dirs):
172 """Set up the output directories
173
174 This should be done before setup_output() is called
175
176 Args:
177 output_dirs (tuple of str):
178 Directory to use for C output files.
179 Use None to write files relative current directory
180 Directory to use for H output files.
181 Defaults to the C output dir
182 """
183 def process_dir(ftype, dirname):
184 if dirname:
185 os.makedirs(dirname, exist_ok=True)
186 self._dirnames[ftype] = dirname
187
188 if output_dirs:
189 c_dirname = output_dirs[0]
190 h_dirname = output_dirs[1] if len(output_dirs) > 1 else c_dirname
191 process_dir(Ftype.SOURCE, c_dirname)
192 process_dir(Ftype.HEADER, h_dirname)
193
194 def setup_output(self, ftype, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600195 """Set up the output destination
196
Simon Glass2be282c2017-06-18 22:08:59 -0600197 Once this is done, future calls to self.out() will output to this
Simon Glassbe44f272020-12-28 20:34:51 -0700198 file. The file used is as follows:
199
200 self._dirnames[ftype] is None: output to fname, or stdout if None
201 self._dirnames[ftype] is not None: output to fname in that directory
202
203 Calling this function multiple times will close the old file and open
204 the new one. If they are the same file, nothing happens and output will
205 continue to the same file.
Simon Glass7581c012017-06-18 22:08:58 -0600206
207 Args:
Simon Glassbe44f272020-12-28 20:34:51 -0700208 ftype (str): Type of file to create ('c' or 'h')
209 fname (str): Filename to send output to. If there is a directory in
210 self._dirnames for this file type, it will be put in that
211 directory
Simon Glass7581c012017-06-18 22:08:58 -0600212 """
Simon Glassbe44f272020-12-28 20:34:51 -0700213 dirname = self._dirnames[ftype]
214 if dirname:
215 pathname = os.path.join(dirname, fname)
216 if self._outfile:
217 self._outfile.close()
218 self._outfile = open(pathname, 'w')
219 elif fname:
220 if not self._outfile:
221 self._outfile = open(fname, 'w')
Simon Glassf62cea02020-12-28 20:34:48 -0700222 else:
223 self._outfile = sys.stdout
Simon Glass7581c012017-06-18 22:08:58 -0600224
Simon Glassbe44f272020-12-28 20:34:51 -0700225 def finish_output(self):
226 """Finish outputing to a file
227
228 This closes the output file, if one is in use
229 """
230 if self._outfile != sys.stdout:
231 self._outfile.close()
232
Simon Glass2be282c2017-06-18 22:08:59 -0600233 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600234 """Output a string to the output file
235
236 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700237 line (str): String to output
Simon Glass7581c012017-06-18 22:08:58 -0600238 """
Simon Glass2be282c2017-06-18 22:08:59 -0600239 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600240
Simon Glass2be282c2017-06-18 22:08:59 -0600241 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600242 """Buffer up a string to send later
243
244 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700245 line (str): String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600246 """
Simon Glass2be282c2017-06-18 22:08:59 -0600247 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600248
Simon Glass2be282c2017-06-18 22:08:59 -0600249 def get_buf(self):
Simon Glass7581c012017-06-18 22:08:58 -0600250 """Get the contents of the output buffer, and clear it
251
252 Returns:
Simon Glass9b330382020-11-08 20:36:21 -0700253 list(str): The output buffer, which is then cleared for future use
Simon Glass7581c012017-06-18 22:08:58 -0600254 """
255 lines = self._lines
256 self._lines = []
257 return lines
258
Simon Glassd1055d62020-12-28 20:35:00 -0700259 def out_header(self, outfile):
260 """Output a message indicating that this is an auto-generated file
261
262 Args:
263 outfile: OutputFile describing the file being generated
264 """
Simon Glassd5031142017-08-29 14:16:01 -0600265 self.out('''/*
266 * DO NOT MODIFY
267 *
Simon Glassd1055d62020-12-28 20:35:00 -0700268 * %s.
269 * This was generated by dtoc from a .dtb (device tree binary) file.
Simon Glassd5031142017-08-29 14:16:01 -0600270 */
271
Simon Glassd1055d62020-12-28 20:35:00 -0700272''' % outfile.hdr_comment)
Simon Glassd5031142017-08-29 14:16:01 -0600273
Simon Glass8fed2eb2017-08-29 14:15:55 -0600274 def get_phandle_argc(self, prop, node_name):
275 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600276
Simon Glass8fed2eb2017-08-29 14:15:55 -0600277 We have no reliable way of detecting whether a node uses a phandle
278 or not. As an interim measure, use a list of known property names.
Simon Glass2925c262017-08-29 14:15:54 -0600279
Simon Glass8fed2eb2017-08-29 14:15:55 -0600280 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700281 prop (fdt.Prop): Prop object to check
282 node_name (str): Node name, only used for raising an error
283 Returns:
284 int or None: Number of argument cells is this is a phandle,
285 else None
286 Raises:
287 ValueError: if the phandle cannot be parsed or the required property
288 is not present
Simon Glass8fed2eb2017-08-29 14:15:55 -0600289 """
Walter Lozanoad340172020-06-25 01:10:16 -0300290 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass760b7172018-07-06 10:27:31 -0600291 if not isinstance(prop.value, list):
292 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600293 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600294 i = 0
295
296 max_args = 0
297 args = []
298 while i < len(val):
299 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-07-06 10:27:31 -0600300 # If we get to the end of the list, stop. This can happen
301 # since some nodes have more phandles in the list than others,
302 # but we allocate enough space for the largest list. So those
303 # nodes with shorter lists end up with zeroes at the end.
304 if not phandle:
305 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600306 target = self._fdt.phandle_to_node.get(phandle)
307 if not target:
308 raise ValueError("Cannot parse '%s' in node '%s'" %
309 (prop.name, node_name))
Walter Lozanoad340172020-06-25 01:10:16 -0300310 cells = None
311 for prop_name in ['#clock-cells', '#gpio-cells']:
312 cells = target.props.get(prop_name)
313 if cells:
314 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600315 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300316 raise ValueError("Node '%s' has no cells property" %
Simon Glass9b330382020-11-08 20:36:21 -0700317 (target.name))
Simon Glass8fed2eb2017-08-29 14:15:55 -0600318 num_args = fdt_util.fdt32_to_cpu(cells.value)
319 max_args = max(max_args, num_args)
320 args.append(num_args)
321 i += 1 + num_args
322 return PhandleInfo(max_args, args)
323 return None
Simon Glass2925c262017-08-29 14:15:54 -0600324
Simon Glass2be282c2017-06-18 22:08:59 -0600325 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200326 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600327
Simon Glass2be282c2017-06-18 22:08:59 -0600328 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600329 device tree root node, and progress from there.
330 """
Simon Glass2be282c2017-06-18 22:08:59 -0600331 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600332
Simon Glass074197a2021-02-03 06:01:09 -0700333 def scan_node(self, node, valid_nodes):
Simon Glass2be282c2017-06-18 22:08:59 -0600334 """Scan a node and subnodes to build a tree of node and phandle info
335
Simon Glass074197a2021-02-03 06:01:09 -0700336 This adds each subnode to self._valid_nodes if it is enabled and has a
337 compatible string.
Simon Glass2be282c2017-06-18 22:08:59 -0600338
339 Args:
Simon Glass074197a2021-02-03 06:01:09 -0700340 node (Node): Node for scan for subnodes
Simon Glassccc3da72020-12-23 08:11:19 -0700341 valid_nodes (list of Node): List of Node objects to add to
Simon Glass2be282c2017-06-18 22:08:59 -0600342 """
Simon Glass074197a2021-02-03 06:01:09 -0700343 for subnode in node.subnodes:
344 if 'compatible' in subnode.props:
345 status = subnode.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600346 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600347 status.value != 'disabled'):
Simon Glass074197a2021-02-03 06:01:09 -0700348 valid_nodes.append(subnode)
Simon Glass7581c012017-06-18 22:08:58 -0600349
350 # recurse to handle any subnodes
Simon Glass074197a2021-02-03 06:01:09 -0700351 self.scan_node(subnode, valid_nodes)
Simon Glass7581c012017-06-18 22:08:58 -0600352
Simon Glass2be282c2017-06-18 22:08:59 -0600353 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600354 """Scan the device tree for useful information
355
356 This fills in the following properties:
Simon Glass074197a2021-02-03 06:01:09 -0700357 _valid_nodes_unsorted: A list of nodes we wish to consider include
358 in the platform data (in devicetree node order)
359 _valid_nodes: Sorted version of _valid_nodes_unsorted
Simon Glass7581c012017-06-18 22:08:58 -0600360 """
Simon Glass074197a2021-02-03 06:01:09 -0700361 root = self._fdt.GetRoot()
Simon Glass1b272732020-10-03 11:31:25 -0600362 valid_nodes = []
Simon Glass074197a2021-02-03 06:01:09 -0700363 self.scan_node(root, valid_nodes)
364 self._valid_nodes_unsorted = valid_nodes
Simon Glass1b272732020-10-03 11:31:25 -0600365 self._valid_nodes = sorted(valid_nodes,
366 key=lambda x: conv_name_to_c(x.name))
Simon Glass51d5d052021-02-03 06:00:58 -0700367
368 def prepare_nodes(self):
369 """Add extra properties to the nodes we are using
370
371 The following properties are added for use by dtoc:
372 idx: Index number of this node (0=first, etc.)
373 struct_name: Name of the struct dtd used by this node
374 var_name: C name for this node
375 child_devs: List of child devices for this node, each a None
376 child_refs: Dict of references for each child:
377 key: Position in child list (-1=head, 0=first, 1=second, ...
378 n-1=last, n=head)
379 seq: Sequence number of the device (unique within its uclass), or
380 -1 not not known yet
381 dev_ref: Reference to this device, e.g. 'DM_DEVICE_REF(serial)'
382 driver: Driver record for this node, or None if not known
383 uclass: Uclass record for this node, or None if not known
384 uclass_seq: Position of this device within the uclass list (0=first,
385 n-1=last)
386 parent_seq: Position of this device within it siblings (0=first,
387 n-1=last)
388 parent_driver: Driver record of the node's parent, or None if none.
389 We don't use node.parent.driver since node.parent may not be in
390 the list of valid nodes
391 """
Simon Glass1b272732020-10-03 11:31:25 -0600392 for idx, node in enumerate(self._valid_nodes):
393 node.idx = idx
Simon Glass51d5d052021-02-03 06:00:58 -0700394 node.struct_name, _ = self._scan.get_normalized_compat_name(node)
395 node.var_name = conv_name_to_c(node.name)
396 node.child_devs = []
397 node.child_refs = {}
398 node.seq = -1
399 node.dev_ref = None
400 node.driver = None
401 node.uclass = None
402 node.uclass_seq = None
403 node.parent_seq = None
404 node.parent_driver = None
Simon Glass7581c012017-06-18 22:08:58 -0600405
Simon Glassc20ee0e2017-08-29 14:15:50 -0600406 @staticmethod
407 def get_num_cells(node):
408 """Get the number of cells in addresses and sizes for this node
409
410 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700411 node (fdt.None): Node to check
Simon Glassc20ee0e2017-08-29 14:15:50 -0600412
413 Returns:
414 Tuple:
415 Number of address cells for this node
416 Number of size cells for this node
417 """
418 parent = node.parent
Simon Glass78128d52020-12-03 16:55:16 -0700419 num_addr, num_size = 2, 2
Simon Glassc20ee0e2017-08-29 14:15:50 -0600420 if parent:
Simon Glass78128d52020-12-03 16:55:16 -0700421 addr_prop = parent.props.get('#address-cells')
422 size_prop = parent.props.get('#size-cells')
423 if addr_prop:
424 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
425 if size_prop:
426 num_size = fdt_util.fdt32_to_cpu(size_prop.value)
427 return num_addr, num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600428
429 def scan_reg_sizes(self):
430 """Scan for 64-bit 'reg' properties and update the values
431
432 This finds 'reg' properties with 64-bit data and converts the value to
433 an array of 64-values. This allows it to be output in a way that the
434 C code can read.
435 """
436 for node in self._valid_nodes:
437 reg = node.props.get('reg')
438 if not reg:
439 continue
Simon Glass78128d52020-12-03 16:55:16 -0700440 num_addr, num_size = self.get_num_cells(node)
441 total = num_addr + num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600442
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700443 if reg.type != fdt.Type.INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600444 raise ValueError("Node '%s' reg property is not an int" %
445 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600446 if len(reg.value) % total:
Simon Glass9b330382020-11-08 20:36:21 -0700447 raise ValueError(
448 "Node '%s' reg property has %d cells "
449 'which is not a multiple of na + ns = %d + %d)' %
Simon Glass78128d52020-12-03 16:55:16 -0700450 (node.name, len(reg.value), num_addr, num_size))
451 reg.num_addr = num_addr
452 reg.num_size = num_size
453 if num_addr != 1 or num_size != 1:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700454 reg.type = fdt.Type.INT64
Simon Glassc20ee0e2017-08-29 14:15:50 -0600455 i = 0
456 new_value = []
457 val = reg.value
458 if not isinstance(val, list):
459 val = [val]
460 while i < len(val):
Simon Glass78128d52020-12-03 16:55:16 -0700461 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
462 i += num_addr
463 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
464 i += num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600465 new_value += [addr, size]
466 reg.value = new_value
467
Simon Glass2be282c2017-06-18 22:08:59 -0600468 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600469 """Scan the device tree building up the C structures we will use.
470
471 Build a dict keyed by C struct name containing a dict of Prop
472 object for each struct field (keyed by property name). Where the
473 same struct appears multiple times, try to use the 'widest'
474 property, i.e. the one with a type which can express all others.
475
476 Once the widest property is determined, all other properties are
477 updated to match that width.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600478
Simon Glassa7d5f962020-12-28 20:35:02 -0700479 The results are written to self._struct_data
Simon Glass7581c012017-06-18 22:08:58 -0600480 """
Simon Glassa7d5f962020-12-28 20:35:02 -0700481 structs = self._struct_data
Simon Glass7581c012017-06-18 22:08:58 -0600482 for node in self._valid_nodes:
Simon Glass7581c012017-06-18 22:08:58 -0600483 fields = {}
484
485 # Get a list of all the valid properties in this node.
486 for name, prop in node.props.items():
487 if name not in PROP_IGNORE_LIST and name[0] != '#':
488 fields[name] = copy.deepcopy(prop)
489
Simon Glasse525fea2021-02-03 06:00:59 -0700490 # If we've seen this struct_name before, update the existing struct
491 if node.struct_name in structs:
492 struct = structs[node.struct_name]
Simon Glass7581c012017-06-18 22:08:58 -0600493 for name, prop in fields.items():
494 oldprop = struct.get(name)
495 if oldprop:
496 oldprop.Widen(prop)
497 else:
498 struct[name] = prop
499
500 # Otherwise store this as a new struct.
501 else:
Simon Glasse525fea2021-02-03 06:00:59 -0700502 structs[node.struct_name] = fields
Simon Glass7581c012017-06-18 22:08:58 -0600503
Simon Glass7581c012017-06-18 22:08:58 -0600504 for node in self._valid_nodes:
Simon Glasse525fea2021-02-03 06:00:59 -0700505 struct = structs[node.struct_name]
Simon Glass7581c012017-06-18 22:08:58 -0600506 for name, prop in node.props.items():
507 if name not in PROP_IGNORE_LIST and name[0] != '#':
508 prop.Widen(struct[name])
Simon Glass7581c012017-06-18 22:08:58 -0600509
Simon Glass2be282c2017-06-18 22:08:59 -0600510 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600511 """Figure out what phandles each node uses
512
513 We need to be careful when outputing nodes that use phandles since
514 they must come after the declaration of the phandles in the C file.
515 Otherwise we get a compiler error since the phandle struct is not yet
516 declared.
517
518 This function adds to each node a list of phandle nodes that the node
519 depends on. This allows us to output things in the right order.
520 """
521 for node in self._valid_nodes:
522 node.phandles = set()
523 for pname, prop in node.props.items():
524 if pname in PROP_IGNORE_LIST or pname[0] == '#':
525 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600526 info = self.get_phandle_argc(prop, node.name)
527 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600528 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600529 pos = 0
530 for args in info.args:
531 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600532 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
533 target_node = self._fdt.phandle_to_node[phandle]
534 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600535 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600536
537
Simon Glassa7d5f962020-12-28 20:35:02 -0700538 def generate_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600539 """Generate struct defintions for the platform data
540
541 This writes out the body of a header file consisting of structure
542 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100543 doc/driver-model/of-plat.rst for more information.
Simon Glass7581c012017-06-18 22:08:58 -0600544 """
Simon Glassa7d5f962020-12-28 20:35:02 -0700545 structs = self._struct_data
Simon Glass2be282c2017-06-18 22:08:59 -0600546 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900547 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600548
549 # Output the struct definition
550 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600551 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600552 for pname in sorted(structs[name]):
553 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600554 info = self.get_phandle_argc(prop, structs[name])
555 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600556 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600557 struct_name = 'struct phandle_%d_arg' % info.max_args
558 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600559 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600560 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600561 else:
562 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600563 self.out('\t%s%s' % (tab_to(2, ptype),
564 conv_name_to_c(prop.name)))
565 if isinstance(prop.value, list):
566 self.out('[%d]' % len(prop.value))
567 self.out(';\n')
568 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600569
Simon Glassabf0c802020-12-23 08:11:20 -0700570 def _output_list(self, node, prop):
571 """Output the C code for a devicetree property that holds a list
572
573 Args:
574 node (fdt.Node): Node to output
575 prop (fdt.Prop): Prop to output
576 """
577 self.buf('{')
578 vals = []
579 # For phandles, output a reference to the platform data
580 # of the target node.
581 info = self.get_phandle_argc(prop, node.name)
582 if info:
583 # Process the list as pairs of (phandle, id)
584 pos = 0
585 for args in info.args:
586 phandle_cell = prop.value[pos]
587 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
588 target_node = self._fdt.phandle_to_node[phandle]
589 arg_values = []
590 for i in range(args):
591 arg_values.append(
592 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
593 pos += 1 + args
594 vals.append('\t{%d, {%s}}' % (target_node.idx,
595 ', '.join(arg_values)))
596 for val in vals:
597 self.buf('\n\t\t%s,' % val)
598 else:
599 for val in prop.value:
600 vals.append(get_value(prop.type, val))
601
602 # Put 8 values per line to avoid very long lines.
603 for i in range(0, len(vals), 8):
604 if i:
605 self.buf(',\n\t\t')
606 self.buf(', '.join(vals[i:i + 8]))
607 self.buf('}')
608
Simon Glasse525fea2021-02-03 06:00:59 -0700609 def _declare_device(self, node):
Simon Glass221ddc12020-12-23 08:11:21 -0700610 """Add a device declaration to the output
611
Simon Glass20e442a2020-12-28 20:34:54 -0700612 This declares a U_BOOT_DRVINFO() for the device being processed
Simon Glass221ddc12020-12-23 08:11:21 -0700613
614 Args:
Simon Glasse525fea2021-02-03 06:00:59 -0700615 node: Node to process
Simon Glass221ddc12020-12-23 08:11:21 -0700616 """
Simon Glasse525fea2021-02-03 06:00:59 -0700617 self.buf('U_BOOT_DRVINFO(%s) = {\n' % node.var_name)
618 self.buf('\t.name\t\t= "%s",\n' % node.struct_name)
619 self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
620 self.buf('\t.plat_size\t= sizeof(%s%s),\n' %
621 (VAL_PREFIX, node.var_name))
Simon Glass221ddc12020-12-23 08:11:21 -0700622 idx = -1
Simon Glasse525fea2021-02-03 06:00:59 -0700623 if node.parent and node.parent in self._valid_nodes:
624 idx = node.parent.idx
Simon Glass221ddc12020-12-23 08:11:21 -0700625 self.buf('\t.parent_idx\t= %d,\n' % idx)
626 self.buf('};\n')
627 self.buf('\n')
628
Simon Glass161dac12020-12-23 08:11:22 -0700629 def _output_prop(self, node, prop):
630 """Output a line containing the value of a struct member
631
632 Args:
633 node (Node): Node being output
634 prop (Prop): Prop object to output
635 """
636 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
637 return
638 member_name = conv_name_to_c(prop.name)
639 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
640
641 # Special handling for lists
642 if isinstance(prop.value, list):
643 self._output_list(node, prop)
644 else:
645 self.buf(get_value(prop.type, prop.value))
646 self.buf(',\n')
647
Simon Glasse525fea2021-02-03 06:00:59 -0700648 def _output_values(self, node):
Simon Glass161dac12020-12-23 08:11:22 -0700649 """Output the definition of a device's struct values
650
651 Args:
Simon Glasse525fea2021-02-03 06:00:59 -0700652 node (Node): Node to output
Simon Glass161dac12020-12-23 08:11:22 -0700653 """
654 self.buf('static struct %s%s %s%s = {\n' %
Simon Glasse525fea2021-02-03 06:00:59 -0700655 (STRUCT_PREFIX, node.struct_name, VAL_PREFIX, node.var_name))
Simon Glass161dac12020-12-23 08:11:22 -0700656 for pname in sorted(node.props):
657 self._output_prop(node, node.props[pname])
658 self.buf('};\n')
659
Simon Glass05953522021-02-03 06:01:07 -0700660 def read_aliases(self):
661 """Read the aliases and attach the information to self._alias
662
663 Raises:
664 ValueError: The alias path is not found
665 """
666 alias_node = self._fdt.GetNode('/aliases')
667 if not alias_node:
668 return
669 re_num = re.compile('(^[a-z0-9-]+[a-z]+)([0-9]+)$')
670 for prop in alias_node.props.values():
671 m_alias = re_num.match(prop.name)
672 if not m_alias:
673 raise ValueError("Cannot decode alias '%s'" % prop.name)
674 name, num = m_alias.groups()
675 node = self._fdt.GetNode(prop.value)
676 result = self._scan.add_uclass_alias(name, num, node)
677 if result is None:
678 raise ValueError("Alias '%s' path '%s' not found" %
679 (prop.name, prop.value))
680 elif result is False:
681 print("Could not find uclass for alias '%s'" % prop.name)
682
Simon Glass337d6972021-02-03 06:01:10 -0700683 def assign_seqs(self):
Simon Glass074197a2021-02-03 06:01:09 -0700684 """Assign a sequence number to each node"""
685 for node in self._valid_nodes_unsorted:
Simon Glass337d6972021-02-03 06:01:10 -0700686 seq = self._scan.assign_seq(node)
687 if seq is not None:
688 node.seq = seq
Simon Glass074197a2021-02-03 06:01:09 -0700689
Simon Glassfd471e22021-02-03 06:01:00 -0700690 def process_nodes(self, need_drivers):
691 nodes_to_output = list(self._valid_nodes)
692
Simon Glassb9319c42021-02-03 06:01:01 -0700693 # Figure out which drivers we actually use
694 self._scan.mark_used(nodes_to_output)
695
Simon Glassfd471e22021-02-03 06:01:00 -0700696 for node in nodes_to_output:
697 node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name
698 driver = self._scan.get_driver(node.struct_name)
699 if not driver:
700 if not need_drivers:
701 continue
702 raise ValueError("Cannot parse/find driver for '%s'" %
703 node.struct_name)
704 node.driver = driver
Simon Glass337d6972021-02-03 06:01:10 -0700705 uclass = self._scan._uclass.get(driver.uclass_id)
706 if not uclass:
707 raise ValueError("Cannot parse/find uclass '%s' for driver '%s'" %
708 (driver.uclass_id, node.struct_name))
709 node.uclass = uclass
710 node.uclass_seq = len(node.uclass.devs)
711 node.uclass.devs.append(node)
712 uclass.node_refs[node.uclass_seq] = \
713 '&%s->uclass_node' % node.dev_ref
714
Simon Glassfd471e22021-02-03 06:01:00 -0700715 parent_driver = None
716 if node.parent in self._valid_nodes:
717 parent_driver = self._scan.get_driver(node.parent.struct_name)
718 if not parent_driver:
719 if not need_drivers:
720 continue
721 raise ValueError(
722 "Cannot parse/find parent driver '%s' for '%s'" %
723 (node.parent.struct_name, node.struct_name))
724 node.parent_seq = len(node.parent.child_devs)
725 node.parent.child_devs.append(node)
726 node.parent.child_refs[node.parent_seq] = \
727 '&%s->sibling_node' % node.dev_ref
728 node.parent_driver = parent_driver
729
730 for node in nodes_to_output:
731 ref = '&%s->child_head' % node.dev_ref
732 node.child_refs[-1] = ref
733 node.child_refs[len(node.child_devs)] = ref
734
Simon Glass337d6972021-02-03 06:01:10 -0700735 uclass_set = set()
736 for driver in self._scan._drivers.values():
737 if driver.used and driver.uclass:
738 uclass_set.add(driver.uclass)
739 self._valid_uclasses = sorted(list(uclass_set),
740 key=lambda uc: uc.uclass_id)
741
742 for seq, uclass in enumerate(uclass_set):
743 ref = '&DM_UCLASS_REF(%s)->dev_head' % uclass.name
744 uclass.node_refs[-1] = ref
745 uclass.node_refs[len(uclass.devs)] = ref
746
Simon Glass2be282c2017-06-18 22:08:59 -0600747 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600748 """Output the C code for a node
749
750 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700751 node (fdt.Node): node to output
Simon Glass7581c012017-06-18 22:08:58 -0600752 """
Simon Glass1b272732020-10-03 11:31:25 -0600753 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Simon Glass7581c012017-06-18 22:08:58 -0600754
Simon Glasse525fea2021-02-03 06:00:59 -0700755 self._output_values(node)
756 self._declare_device(node)
Simon Glass7581c012017-06-18 22:08:58 -0600757
Simon Glass2be282c2017-06-18 22:08:59 -0600758 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600759
Simon Glassa7d5f962020-12-28 20:35:02 -0700760 def generate_plat(self):
Simon Glass7581c012017-06-18 22:08:58 -0600761 """Generate device defintions for the platform data
762
763 This writes out C platform data initialisation data and
Simon Glass20e442a2020-12-28 20:34:54 -0700764 U_BOOT_DRVINFO() declarations for each valid node. Where a node has
Simon Glass7581c012017-06-18 22:08:58 -0600765 multiple compatible strings, a #define is used to make them equivalent.
766
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100767 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600768 information.
769 """
Simon Glass20e442a2020-12-28 20:34:54 -0700770 self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
Simon Glassf31fa992020-12-28 20:35:01 -0700771 self.out('#define DT_PLAT_C\n')
Simon Glasscb43ac12020-10-03 11:31:41 -0600772 self.out('\n')
Simon Glass2be282c2017-06-18 22:08:59 -0600773 self.out('#include <common.h>\n')
774 self.out('#include <dm.h>\n')
775 self.out('#include <dt-structs.h>\n')
776 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600777
Simon Glass9eca08d2020-12-28 20:35:04 -0700778 for node in self._valid_nodes:
Simon Glass2be282c2017-06-18 22:08:59 -0600779 self.output_node(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600780
Walter Lozano51f12632020-06-25 01:10:13 -0300781 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600782
Simon Glass192c1112020-12-28 20:34:50 -0700783
Simon Glassbe44f272020-12-28 20:34:51 -0700784# Types of output file we understand
785# key: Command used to generate this file
786# value: OutputFile for this command
787OUTPUT_FILES = {
Simon Glassd1055d62020-12-28 20:35:00 -0700788 'struct':
789 OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
Simon Glassa7d5f962020-12-28 20:35:02 -0700790 DtbPlatdata.generate_structs,
Simon Glassd1055d62020-12-28 20:35:00 -0700791 'Defines the structs used to hold devicetree data'),
792 'platdata':
Simon Glassa7d5f962020-12-28 20:35:02 -0700793 OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
Simon Glassd1055d62020-12-28 20:35:00 -0700794 'Declares the U_BOOT_DRIVER() records and platform data'),
Simon Glassbe44f272020-12-28 20:34:51 -0700795 }
796
797
Simon Glassb00f0062021-02-03 06:01:02 -0700798def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase,
Simon Glassa32eb7d2021-02-03 06:00:51 -0700799 warning_disabled=False, drivers_additional=None, basedir=None,
800 scan=None):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600801 """Run all the steps of the dtoc tool
802
803 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700804 args (list): List of non-option arguments provided to the problem
805 dtb_file (str): Filename of dtb file to process
806 include_disabled (bool): True to include disabled nodes
Simon Glassf62cea02020-12-28 20:34:48 -0700807 output (str): Name of output file (None for stdout)
Simon Glass192c1112020-12-28 20:34:50 -0700808 output_dirs (tuple of str):
809 Directory to put C output files
810 Directory to put H output files
Simon Glassb00f0062021-02-03 06:01:02 -0700811 phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
812 or 'tpl'. None if not known
Simon Glass78128d52020-12-03 16:55:16 -0700813 warning_disabled (bool): True to avoid showing warnings about missing
814 drivers
Simon Glassccc3da72020-12-23 08:11:19 -0700815 drivers_additional (list): List of additional drivers to use during
Simon Glass78128d52020-12-03 16:55:16 -0700816 scanning
Simon Glass1e0f3f42020-12-28 20:35:03 -0700817 basedir (str): Base directory of U-Boot source code. Defaults to the
818 grandparent of this file's directory
Simon Glassa32eb7d2021-02-03 06:00:51 -0700819 scan (src_src.Scanner): Scanner from a previous run. This can help speed
820 up tests. Use None for normal operation
821
Simon Glass05953522021-02-03 06:01:07 -0700822 Returns:
823 DtbPlatdata object
824
Simon Glass9b330382020-11-08 20:36:21 -0700825 Raises:
826 ValueError: if args has no command, or an unknown command
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600827 """
828 if not args:
Simon Glassbe44f272020-12-28 20:34:51 -0700829 raise ValueError('Please specify a command: struct, platdata, all')
830 if output and output_dirs and any(output_dirs):
831 raise ValueError('Must specify either output or output_dirs, not both')
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600832
Simon Glassa32eb7d2021-02-03 06:00:51 -0700833 if not scan:
Simon Glassb00f0062021-02-03 06:01:02 -0700834 scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional,
835 phase)
Simon Glassa32eb7d2021-02-03 06:00:51 -0700836 scan.scan_drivers()
Simon Glassfd471e22021-02-03 06:01:00 -0700837 do_process = True
838 else:
839 do_process = False
Simon Glassa542a702020-12-28 20:35:06 -0700840 plat = DtbPlatdata(scan, dtb_file, include_disabled)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600841 plat.scan_dtb()
842 plat.scan_tree()
Simon Glass51d5d052021-02-03 06:00:58 -0700843 plat.prepare_nodes()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600844 plat.scan_reg_sizes()
Simon Glassbe44f272020-12-28 20:34:51 -0700845 plat.setup_output_dirs(output_dirs)
Simon Glassa7d5f962020-12-28 20:35:02 -0700846 plat.scan_structs()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600847 plat.scan_phandles()
Simon Glass074197a2021-02-03 06:01:09 -0700848 plat.process_nodes(False)
Simon Glass05953522021-02-03 06:01:07 -0700849 plat.read_aliases()
Simon Glass337d6972021-02-03 06:01:10 -0700850 plat.assign_seqs()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600851
Simon Glass10cbd3b2020-12-28 20:34:52 -0700852 cmds = args[0].split(',')
853 if 'all' in cmds:
854 cmds = sorted(OUTPUT_FILES.keys())
855 for cmd in cmds:
Simon Glassbe44f272020-12-28 20:34:51 -0700856 outfile = OUTPUT_FILES.get(cmd)
857 if not outfile:
858 raise ValueError("Unknown command '%s': (use: %s)" %
Simon Glass10cbd3b2020-12-28 20:34:52 -0700859 (cmd, ', '.join(sorted(OUTPUT_FILES.keys()))))
Simon Glassbe44f272020-12-28 20:34:51 -0700860 plat.setup_output(outfile.ftype,
861 outfile.fname if output_dirs else output)
Simon Glassd1055d62020-12-28 20:35:00 -0700862 plat.out_header(outfile)
Simon Glassa7d5f962020-12-28 20:35:02 -0700863 outfile.method(plat)
Simon Glassbe44f272020-12-28 20:34:51 -0700864 plat.finish_output()
Simon Glass05953522021-02-03 06:01:07 -0700865 return plat