blob: e08b92cf8a3fd9bc5a7767945963f1a02ba42f54 [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 Glass50aae3e2021-02-03 06:01:11 -0700353 def scan_tree(self, add_root):
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 Glass50aae3e2021-02-03 06:01:11 -0700360
361 Args:
362 add_root: True to add the root node also (which wouldn't normally
363 be added as it may not have a compatible string)
Simon Glass7581c012017-06-18 22:08:58 -0600364 """
Simon Glass074197a2021-02-03 06:01:09 -0700365 root = self._fdt.GetRoot()
Simon Glass1b272732020-10-03 11:31:25 -0600366 valid_nodes = []
Simon Glass50aae3e2021-02-03 06:01:11 -0700367 if add_root:
368 valid_nodes.append(root)
Simon Glass074197a2021-02-03 06:01:09 -0700369 self.scan_node(root, valid_nodes)
370 self._valid_nodes_unsorted = valid_nodes
Simon Glass1b272732020-10-03 11:31:25 -0600371 self._valid_nodes = sorted(valid_nodes,
372 key=lambda x: conv_name_to_c(x.name))
Simon Glass51d5d052021-02-03 06:00:58 -0700373
374 def prepare_nodes(self):
375 """Add extra properties to the nodes we are using
376
377 The following properties are added for use by dtoc:
378 idx: Index number of this node (0=first, etc.)
379 struct_name: Name of the struct dtd used by this node
380 var_name: C name for this node
381 child_devs: List of child devices for this node, each a None
382 child_refs: Dict of references for each child:
383 key: Position in child list (-1=head, 0=first, 1=second, ...
384 n-1=last, n=head)
385 seq: Sequence number of the device (unique within its uclass), or
386 -1 not not known yet
387 dev_ref: Reference to this device, e.g. 'DM_DEVICE_REF(serial)'
388 driver: Driver record for this node, or None if not known
389 uclass: Uclass record for this node, or None if not known
390 uclass_seq: Position of this device within the uclass list (0=first,
391 n-1=last)
392 parent_seq: Position of this device within it siblings (0=first,
393 n-1=last)
394 parent_driver: Driver record of the node's parent, or None if none.
395 We don't use node.parent.driver since node.parent may not be in
396 the list of valid nodes
397 """
Simon Glass1b272732020-10-03 11:31:25 -0600398 for idx, node in enumerate(self._valid_nodes):
399 node.idx = idx
Simon Glass51d5d052021-02-03 06:00:58 -0700400 node.struct_name, _ = self._scan.get_normalized_compat_name(node)
401 node.var_name = conv_name_to_c(node.name)
402 node.child_devs = []
403 node.child_refs = {}
404 node.seq = -1
405 node.dev_ref = None
406 node.driver = None
407 node.uclass = None
408 node.uclass_seq = None
409 node.parent_seq = None
410 node.parent_driver = None
Simon Glass7581c012017-06-18 22:08:58 -0600411
Simon Glassc20ee0e2017-08-29 14:15:50 -0600412 @staticmethod
413 def get_num_cells(node):
414 """Get the number of cells in addresses and sizes for this node
415
416 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700417 node (fdt.None): Node to check
Simon Glassc20ee0e2017-08-29 14:15:50 -0600418
419 Returns:
420 Tuple:
421 Number of address cells for this node
422 Number of size cells for this node
423 """
424 parent = node.parent
Simon Glass78128d52020-12-03 16:55:16 -0700425 num_addr, num_size = 2, 2
Simon Glassc20ee0e2017-08-29 14:15:50 -0600426 if parent:
Simon Glass78128d52020-12-03 16:55:16 -0700427 addr_prop = parent.props.get('#address-cells')
428 size_prop = parent.props.get('#size-cells')
429 if addr_prop:
430 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
431 if size_prop:
432 num_size = fdt_util.fdt32_to_cpu(size_prop.value)
433 return num_addr, num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600434
435 def scan_reg_sizes(self):
436 """Scan for 64-bit 'reg' properties and update the values
437
438 This finds 'reg' properties with 64-bit data and converts the value to
439 an array of 64-values. This allows it to be output in a way that the
440 C code can read.
441 """
442 for node in self._valid_nodes:
443 reg = node.props.get('reg')
444 if not reg:
445 continue
Simon Glass78128d52020-12-03 16:55:16 -0700446 num_addr, num_size = self.get_num_cells(node)
447 total = num_addr + num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600448
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700449 if reg.type != fdt.Type.INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600450 raise ValueError("Node '%s' reg property is not an int" %
451 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600452 if len(reg.value) % total:
Simon Glass9b330382020-11-08 20:36:21 -0700453 raise ValueError(
454 "Node '%s' reg property has %d cells "
455 'which is not a multiple of na + ns = %d + %d)' %
Simon Glass78128d52020-12-03 16:55:16 -0700456 (node.name, len(reg.value), num_addr, num_size))
457 reg.num_addr = num_addr
458 reg.num_size = num_size
459 if num_addr != 1 or num_size != 1:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700460 reg.type = fdt.Type.INT64
Simon Glassc20ee0e2017-08-29 14:15:50 -0600461 i = 0
462 new_value = []
463 val = reg.value
464 if not isinstance(val, list):
465 val = [val]
466 while i < len(val):
Simon Glass78128d52020-12-03 16:55:16 -0700467 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
468 i += num_addr
469 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
470 i += num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600471 new_value += [addr, size]
472 reg.value = new_value
473
Simon Glass2be282c2017-06-18 22:08:59 -0600474 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600475 """Scan the device tree building up the C structures we will use.
476
477 Build a dict keyed by C struct name containing a dict of Prop
478 object for each struct field (keyed by property name). Where the
479 same struct appears multiple times, try to use the 'widest'
480 property, i.e. the one with a type which can express all others.
481
482 Once the widest property is determined, all other properties are
483 updated to match that width.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600484
Simon Glassa7d5f962020-12-28 20:35:02 -0700485 The results are written to self._struct_data
Simon Glass7581c012017-06-18 22:08:58 -0600486 """
Simon Glassa7d5f962020-12-28 20:35:02 -0700487 structs = self._struct_data
Simon Glass7581c012017-06-18 22:08:58 -0600488 for node in self._valid_nodes:
Simon Glass7581c012017-06-18 22:08:58 -0600489 fields = {}
490
491 # Get a list of all the valid properties in this node.
492 for name, prop in node.props.items():
493 if name not in PROP_IGNORE_LIST and name[0] != '#':
494 fields[name] = copy.deepcopy(prop)
495
Simon Glasse525fea2021-02-03 06:00:59 -0700496 # If we've seen this struct_name before, update the existing struct
497 if node.struct_name in structs:
498 struct = structs[node.struct_name]
Simon Glass7581c012017-06-18 22:08:58 -0600499 for name, prop in fields.items():
500 oldprop = struct.get(name)
501 if oldprop:
502 oldprop.Widen(prop)
503 else:
504 struct[name] = prop
505
506 # Otherwise store this as a new struct.
507 else:
Simon Glasse525fea2021-02-03 06:00:59 -0700508 structs[node.struct_name] = fields
Simon Glass7581c012017-06-18 22:08:58 -0600509
Simon Glass7581c012017-06-18 22:08:58 -0600510 for node in self._valid_nodes:
Simon Glasse525fea2021-02-03 06:00:59 -0700511 struct = structs[node.struct_name]
Simon Glass7581c012017-06-18 22:08:58 -0600512 for name, prop in node.props.items():
513 if name not in PROP_IGNORE_LIST and name[0] != '#':
514 prop.Widen(struct[name])
Simon Glass7581c012017-06-18 22:08:58 -0600515
Simon Glass2be282c2017-06-18 22:08:59 -0600516 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600517 """Figure out what phandles each node uses
518
519 We need to be careful when outputing nodes that use phandles since
520 they must come after the declaration of the phandles in the C file.
521 Otherwise we get a compiler error since the phandle struct is not yet
522 declared.
523
524 This function adds to each node a list of phandle nodes that the node
525 depends on. This allows us to output things in the right order.
526 """
527 for node in self._valid_nodes:
528 node.phandles = set()
529 for pname, prop in node.props.items():
530 if pname in PROP_IGNORE_LIST or pname[0] == '#':
531 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600532 info = self.get_phandle_argc(prop, node.name)
533 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600534 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600535 pos = 0
536 for args in info.args:
537 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600538 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
539 target_node = self._fdt.phandle_to_node[phandle]
540 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600541 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600542
543
Simon Glassa7d5f962020-12-28 20:35:02 -0700544 def generate_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600545 """Generate struct defintions for the platform data
546
547 This writes out the body of a header file consisting of structure
548 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100549 doc/driver-model/of-plat.rst for more information.
Simon Glass7581c012017-06-18 22:08:58 -0600550 """
Simon Glassa7d5f962020-12-28 20:35:02 -0700551 structs = self._struct_data
Simon Glass2be282c2017-06-18 22:08:59 -0600552 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900553 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600554
555 # Output the struct definition
556 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600557 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600558 for pname in sorted(structs[name]):
559 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600560 info = self.get_phandle_argc(prop, structs[name])
561 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600562 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600563 struct_name = 'struct phandle_%d_arg' % info.max_args
564 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600565 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600566 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600567 else:
568 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600569 self.out('\t%s%s' % (tab_to(2, ptype),
570 conv_name_to_c(prop.name)))
571 if isinstance(prop.value, list):
572 self.out('[%d]' % len(prop.value))
573 self.out(';\n')
574 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600575
Simon Glassabf0c802020-12-23 08:11:20 -0700576 def _output_list(self, node, prop):
577 """Output the C code for a devicetree property that holds a list
578
579 Args:
580 node (fdt.Node): Node to output
581 prop (fdt.Prop): Prop to output
582 """
583 self.buf('{')
584 vals = []
585 # For phandles, output a reference to the platform data
586 # of the target node.
587 info = self.get_phandle_argc(prop, node.name)
588 if info:
589 # Process the list as pairs of (phandle, id)
590 pos = 0
591 for args in info.args:
592 phandle_cell = prop.value[pos]
593 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
594 target_node = self._fdt.phandle_to_node[phandle]
595 arg_values = []
596 for i in range(args):
597 arg_values.append(
598 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
599 pos += 1 + args
600 vals.append('\t{%d, {%s}}' % (target_node.idx,
601 ', '.join(arg_values)))
602 for val in vals:
603 self.buf('\n\t\t%s,' % val)
604 else:
605 for val in prop.value:
606 vals.append(get_value(prop.type, val))
607
608 # Put 8 values per line to avoid very long lines.
609 for i in range(0, len(vals), 8):
610 if i:
611 self.buf(',\n\t\t')
612 self.buf(', '.join(vals[i:i + 8]))
613 self.buf('}')
614
Simon Glasse525fea2021-02-03 06:00:59 -0700615 def _declare_device(self, node):
Simon Glass221ddc12020-12-23 08:11:21 -0700616 """Add a device declaration to the output
617
Simon Glass20e442a2020-12-28 20:34:54 -0700618 This declares a U_BOOT_DRVINFO() for the device being processed
Simon Glass221ddc12020-12-23 08:11:21 -0700619
620 Args:
Simon Glasse525fea2021-02-03 06:00:59 -0700621 node: Node to process
Simon Glass221ddc12020-12-23 08:11:21 -0700622 """
Simon Glasse525fea2021-02-03 06:00:59 -0700623 self.buf('U_BOOT_DRVINFO(%s) = {\n' % node.var_name)
624 self.buf('\t.name\t\t= "%s",\n' % node.struct_name)
625 self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
626 self.buf('\t.plat_size\t= sizeof(%s%s),\n' %
627 (VAL_PREFIX, node.var_name))
Simon Glass221ddc12020-12-23 08:11:21 -0700628 idx = -1
Simon Glasse525fea2021-02-03 06:00:59 -0700629 if node.parent and node.parent in self._valid_nodes:
630 idx = node.parent.idx
Simon Glass221ddc12020-12-23 08:11:21 -0700631 self.buf('\t.parent_idx\t= %d,\n' % idx)
632 self.buf('};\n')
633 self.buf('\n')
634
Simon Glass161dac12020-12-23 08:11:22 -0700635 def _output_prop(self, node, prop):
636 """Output a line containing the value of a struct member
637
638 Args:
639 node (Node): Node being output
640 prop (Prop): Prop object to output
641 """
642 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
643 return
644 member_name = conv_name_to_c(prop.name)
645 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
646
647 # Special handling for lists
648 if isinstance(prop.value, list):
649 self._output_list(node, prop)
650 else:
651 self.buf(get_value(prop.type, prop.value))
652 self.buf(',\n')
653
Simon Glasse525fea2021-02-03 06:00:59 -0700654 def _output_values(self, node):
Simon Glass161dac12020-12-23 08:11:22 -0700655 """Output the definition of a device's struct values
656
657 Args:
Simon Glasse525fea2021-02-03 06:00:59 -0700658 node (Node): Node to output
Simon Glass161dac12020-12-23 08:11:22 -0700659 """
660 self.buf('static struct %s%s %s%s = {\n' %
Simon Glasse525fea2021-02-03 06:00:59 -0700661 (STRUCT_PREFIX, node.struct_name, VAL_PREFIX, node.var_name))
Simon Glass161dac12020-12-23 08:11:22 -0700662 for pname in sorted(node.props):
663 self._output_prop(node, node.props[pname])
664 self.buf('};\n')
665
Simon Glass05953522021-02-03 06:01:07 -0700666 def read_aliases(self):
667 """Read the aliases and attach the information to self._alias
668
669 Raises:
670 ValueError: The alias path is not found
671 """
672 alias_node = self._fdt.GetNode('/aliases')
673 if not alias_node:
674 return
675 re_num = re.compile('(^[a-z0-9-]+[a-z]+)([0-9]+)$')
676 for prop in alias_node.props.values():
677 m_alias = re_num.match(prop.name)
678 if not m_alias:
679 raise ValueError("Cannot decode alias '%s'" % prop.name)
680 name, num = m_alias.groups()
681 node = self._fdt.GetNode(prop.value)
682 result = self._scan.add_uclass_alias(name, num, node)
683 if result is None:
684 raise ValueError("Alias '%s' path '%s' not found" %
685 (prop.name, prop.value))
686 elif result is False:
687 print("Could not find uclass for alias '%s'" % prop.name)
688
Simon Glass337d6972021-02-03 06:01:10 -0700689 def assign_seqs(self):
Simon Glass074197a2021-02-03 06:01:09 -0700690 """Assign a sequence number to each node"""
691 for node in self._valid_nodes_unsorted:
Simon Glass337d6972021-02-03 06:01:10 -0700692 seq = self._scan.assign_seq(node)
693 if seq is not None:
694 node.seq = seq
Simon Glass074197a2021-02-03 06:01:09 -0700695
Simon Glassfd471e22021-02-03 06:01:00 -0700696 def process_nodes(self, need_drivers):
697 nodes_to_output = list(self._valid_nodes)
698
Simon Glassb9319c42021-02-03 06:01:01 -0700699 # Figure out which drivers we actually use
700 self._scan.mark_used(nodes_to_output)
701
Simon Glassfd471e22021-02-03 06:01:00 -0700702 for node in nodes_to_output:
703 node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name
704 driver = self._scan.get_driver(node.struct_name)
705 if not driver:
706 if not need_drivers:
707 continue
708 raise ValueError("Cannot parse/find driver for '%s'" %
709 node.struct_name)
710 node.driver = driver
Simon Glass337d6972021-02-03 06:01:10 -0700711 uclass = self._scan._uclass.get(driver.uclass_id)
712 if not uclass:
713 raise ValueError("Cannot parse/find uclass '%s' for driver '%s'" %
714 (driver.uclass_id, node.struct_name))
715 node.uclass = uclass
716 node.uclass_seq = len(node.uclass.devs)
717 node.uclass.devs.append(node)
718 uclass.node_refs[node.uclass_seq] = \
719 '&%s->uclass_node' % node.dev_ref
720
Simon Glassfd471e22021-02-03 06:01:00 -0700721 parent_driver = None
722 if node.parent in self._valid_nodes:
723 parent_driver = self._scan.get_driver(node.parent.struct_name)
724 if not parent_driver:
725 if not need_drivers:
726 continue
727 raise ValueError(
728 "Cannot parse/find parent driver '%s' for '%s'" %
729 (node.parent.struct_name, node.struct_name))
730 node.parent_seq = len(node.parent.child_devs)
731 node.parent.child_devs.append(node)
732 node.parent.child_refs[node.parent_seq] = \
733 '&%s->sibling_node' % node.dev_ref
734 node.parent_driver = parent_driver
735
736 for node in nodes_to_output:
737 ref = '&%s->child_head' % node.dev_ref
738 node.child_refs[-1] = ref
739 node.child_refs[len(node.child_devs)] = ref
740
Simon Glass337d6972021-02-03 06:01:10 -0700741 uclass_set = set()
742 for driver in self._scan._drivers.values():
743 if driver.used and driver.uclass:
744 uclass_set.add(driver.uclass)
745 self._valid_uclasses = sorted(list(uclass_set),
746 key=lambda uc: uc.uclass_id)
747
748 for seq, uclass in enumerate(uclass_set):
749 ref = '&DM_UCLASS_REF(%s)->dev_head' % uclass.name
750 uclass.node_refs[-1] = ref
751 uclass.node_refs[len(uclass.devs)] = ref
752
Simon Glass2be282c2017-06-18 22:08:59 -0600753 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600754 """Output the C code for a node
755
756 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700757 node (fdt.Node): node to output
Simon Glass7581c012017-06-18 22:08:58 -0600758 """
Simon Glass1b272732020-10-03 11:31:25 -0600759 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Simon Glass7581c012017-06-18 22:08:58 -0600760
Simon Glasse525fea2021-02-03 06:00:59 -0700761 self._output_values(node)
762 self._declare_device(node)
Simon Glass7581c012017-06-18 22:08:58 -0600763
Simon Glass2be282c2017-06-18 22:08:59 -0600764 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600765
Simon Glassa7d5f962020-12-28 20:35:02 -0700766 def generate_plat(self):
Simon Glass7581c012017-06-18 22:08:58 -0600767 """Generate device defintions for the platform data
768
769 This writes out C platform data initialisation data and
Simon Glass20e442a2020-12-28 20:34:54 -0700770 U_BOOT_DRVINFO() declarations for each valid node. Where a node has
Simon Glass7581c012017-06-18 22:08:58 -0600771 multiple compatible strings, a #define is used to make them equivalent.
772
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100773 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600774 information.
775 """
Simon Glass20e442a2020-12-28 20:34:54 -0700776 self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
Simon Glassf31fa992020-12-28 20:35:01 -0700777 self.out('#define DT_PLAT_C\n')
Simon Glasscb43ac12020-10-03 11:31:41 -0600778 self.out('\n')
Simon Glass2be282c2017-06-18 22:08:59 -0600779 self.out('#include <common.h>\n')
780 self.out('#include <dm.h>\n')
781 self.out('#include <dt-structs.h>\n')
782 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600783
Simon Glass9eca08d2020-12-28 20:35:04 -0700784 for node in self._valid_nodes:
Simon Glass2be282c2017-06-18 22:08:59 -0600785 self.output_node(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600786
Walter Lozano51f12632020-06-25 01:10:13 -0300787 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600788
Simon Glass192c1112020-12-28 20:34:50 -0700789
Simon Glassbe44f272020-12-28 20:34:51 -0700790# Types of output file we understand
791# key: Command used to generate this file
792# value: OutputFile for this command
793OUTPUT_FILES = {
Simon Glassd1055d62020-12-28 20:35:00 -0700794 'struct':
795 OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
Simon Glassa7d5f962020-12-28 20:35:02 -0700796 DtbPlatdata.generate_structs,
Simon Glassd1055d62020-12-28 20:35:00 -0700797 'Defines the structs used to hold devicetree data'),
798 'platdata':
Simon Glassa7d5f962020-12-28 20:35:02 -0700799 OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
Simon Glassd1055d62020-12-28 20:35:00 -0700800 'Declares the U_BOOT_DRIVER() records and platform data'),
Simon Glassbe44f272020-12-28 20:34:51 -0700801 }
802
803
Simon Glassb00f0062021-02-03 06:01:02 -0700804def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase,
Simon Glassa32eb7d2021-02-03 06:00:51 -0700805 warning_disabled=False, drivers_additional=None, basedir=None,
806 scan=None):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600807 """Run all the steps of the dtoc tool
808
809 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700810 args (list): List of non-option arguments provided to the problem
811 dtb_file (str): Filename of dtb file to process
812 include_disabled (bool): True to include disabled nodes
Simon Glassf62cea02020-12-28 20:34:48 -0700813 output (str): Name of output file (None for stdout)
Simon Glass192c1112020-12-28 20:34:50 -0700814 output_dirs (tuple of str):
815 Directory to put C output files
816 Directory to put H output files
Simon Glassb00f0062021-02-03 06:01:02 -0700817 phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
818 or 'tpl'. None if not known
Simon Glass78128d52020-12-03 16:55:16 -0700819 warning_disabled (bool): True to avoid showing warnings about missing
820 drivers
Simon Glassccc3da72020-12-23 08:11:19 -0700821 drivers_additional (list): List of additional drivers to use during
Simon Glass78128d52020-12-03 16:55:16 -0700822 scanning
Simon Glass1e0f3f42020-12-28 20:35:03 -0700823 basedir (str): Base directory of U-Boot source code. Defaults to the
824 grandparent of this file's directory
Simon Glassa32eb7d2021-02-03 06:00:51 -0700825 scan (src_src.Scanner): Scanner from a previous run. This can help speed
826 up tests. Use None for normal operation
827
Simon Glass05953522021-02-03 06:01:07 -0700828 Returns:
829 DtbPlatdata object
830
Simon Glass9b330382020-11-08 20:36:21 -0700831 Raises:
832 ValueError: if args has no command, or an unknown command
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600833 """
834 if not args:
Simon Glassbe44f272020-12-28 20:34:51 -0700835 raise ValueError('Please specify a command: struct, platdata, all')
836 if output and output_dirs and any(output_dirs):
837 raise ValueError('Must specify either output or output_dirs, not both')
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600838
Simon Glassa32eb7d2021-02-03 06:00:51 -0700839 if not scan:
Simon Glassb00f0062021-02-03 06:01:02 -0700840 scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional,
841 phase)
Simon Glassa32eb7d2021-02-03 06:00:51 -0700842 scan.scan_drivers()
Simon Glassfd471e22021-02-03 06:01:00 -0700843 do_process = True
844 else:
845 do_process = False
Simon Glassa542a702020-12-28 20:35:06 -0700846 plat = DtbPlatdata(scan, dtb_file, include_disabled)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600847 plat.scan_dtb()
Simon Glass50aae3e2021-02-03 06:01:11 -0700848 plat.scan_tree(add_root=False)
Simon Glass51d5d052021-02-03 06:00:58 -0700849 plat.prepare_nodes()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600850 plat.scan_reg_sizes()
Simon Glassbe44f272020-12-28 20:34:51 -0700851 plat.setup_output_dirs(output_dirs)
Simon Glassa7d5f962020-12-28 20:35:02 -0700852 plat.scan_structs()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600853 plat.scan_phandles()
Simon Glass074197a2021-02-03 06:01:09 -0700854 plat.process_nodes(False)
Simon Glass05953522021-02-03 06:01:07 -0700855 plat.read_aliases()
Simon Glass337d6972021-02-03 06:01:10 -0700856 plat.assign_seqs()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600857
Simon Glass10cbd3b2020-12-28 20:34:52 -0700858 cmds = args[0].split(',')
859 if 'all' in cmds:
860 cmds = sorted(OUTPUT_FILES.keys())
861 for cmd in cmds:
Simon Glassbe44f272020-12-28 20:34:51 -0700862 outfile = OUTPUT_FILES.get(cmd)
863 if not outfile:
864 raise ValueError("Unknown command '%s': (use: %s)" %
Simon Glass10cbd3b2020-12-28 20:34:52 -0700865 (cmd, ', '.join(sorted(OUTPUT_FILES.keys()))))
Simon Glassbe44f272020-12-28 20:34:51 -0700866 plat.setup_output(outfile.ftype,
867 outfile.fname if output_dirs else output)
Simon Glassd1055d62020-12-28 20:35:00 -0700868 plat.out_header(outfile)
Simon Glassa7d5f962020-12-28 20:35:02 -0700869 outfile.method(plat)
Simon Glassbe44f272020-12-28 20:34:51 -0700870 plat.finish_output()
Simon Glass05953522021-02-03 06:01:07 -0700871 return plat