blob: ebe5132e1435286734aba9cf8e6517269710e939 [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 Glass7581c012017-06-18 22:08:58 -060025
Simon Glass9b330382020-11-08 20:36:21 -070026# When we see these properties we ignore them - i.e. do not create a structure
27# member
Simon Glass7581c012017-06-18 22:08:58 -060028PROP_IGNORE_LIST = [
29 '#address-cells',
30 '#gpio-cells',
31 '#size-cells',
32 'compatible',
33 'linux,phandle',
34 "status",
35 'phandle',
36 'u-boot,dm-pre-reloc',
37 'u-boot,dm-tpl',
38 'u-boot,dm-spl',
39]
40
Simon Glass5ea9dcc2020-11-08 20:36:17 -070041# C type declarations for the types we support
Simon Glass7581c012017-06-18 22:08:58 -060042TYPE_NAMES = {
Simon Glass5ea9dcc2020-11-08 20:36:17 -070043 fdt.Type.INT: 'fdt32_t',
44 fdt.Type.BYTE: 'unsigned char',
45 fdt.Type.STRING: 'const char *',
46 fdt.Type.BOOL: 'bool',
47 fdt.Type.INT64: 'fdt64_t',
Simon Glass2be282c2017-06-18 22:08:59 -060048}
Simon Glass7581c012017-06-18 22:08:58 -060049
50STRUCT_PREFIX = 'dtd_'
51VAL_PREFIX = 'dtv_'
52
Simon Glassbe44f272020-12-28 20:34:51 -070053class Ftype(IntEnum):
54 SOURCE, HEADER = range(2)
55
56
57# This holds information about each type of output file dtoc can create
58# type: Type of file (Ftype)
59# fname: Filename excluding directory, e.g. 'dt-platdata.c'
60OutputFile = collections.namedtuple('OutputFile', ['ftype', 'fname'])
61
Simon Glass8fed2eb2017-08-29 14:15:55 -060062# This holds information about a property which includes phandles.
63#
64# max_args: integer: Maximum number or arguments that any phandle uses (int).
65# args: Number of args for each phandle in the property. The total number of
66# phandles is len(args). This is a list of integers.
67PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
68
Simon Glass97136eb2020-10-03 09:25:19 -060069# Holds a single phandle link, allowing a C struct value to be assigned to point
70# to a device
71#
72# var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node')
73# dev_name: Name of device to assign to (e.g. 'clock')
74PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name'])
75
Simon Glass8fed2eb2017-08-29 14:15:55 -060076
Simon Glass7d637c12020-12-23 08:11:23 -070077class Driver:
78 """Information about a driver in U-Boot
79
80 Attributes:
81 name: Name of driver. For U_BOOT_DRIVER(x) this is 'x'
82 """
83 def __init__(self, name):
84 self.name = name
85
86 def __eq__(self, other):
87 return self.name == other.name
88
89 def __repr__(self):
90 return "Driver(name='%s')" % self.name
91
92
Simon Glass2be282c2017-06-18 22:08:59 -060093def conv_name_to_c(name):
Simon Glass7581c012017-06-18 22:08:58 -060094 """Convert a device-tree name to a C identifier
95
Simon Glass30107b02017-06-18 22:09:04 -060096 This uses multiple replace() calls instead of re.sub() since it is faster
97 (400ms for 1m calls versus 1000ms for the 're' version).
98
Simon Glass7581c012017-06-18 22:08:58 -060099 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700100 name (str): Name to convert
Simon Glass7581c012017-06-18 22:08:58 -0600101 Return:
Simon Glass9b330382020-11-08 20:36:21 -0700102 str: String containing the C version of this name
Simon Glass7581c012017-06-18 22:08:58 -0600103 """
Simon Glass2be282c2017-06-18 22:08:59 -0600104 new = name.replace('@', '_at_')
105 new = new.replace('-', '_')
106 new = new.replace(',', '_')
107 new = new.replace('.', '_')
Simon Glass2be282c2017-06-18 22:08:59 -0600108 return new
Simon Glass7581c012017-06-18 22:08:58 -0600109
Simon Glass2be282c2017-06-18 22:08:59 -0600110def tab_to(num_tabs, line):
111 """Append tabs to a line of text to reach a tab stop.
Simon Glass7581c012017-06-18 22:08:58 -0600112
Simon Glass2be282c2017-06-18 22:08:59 -0600113 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700114 num_tabs (int): Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
115 line (str): Line of text to append to
Simon Glass2be282c2017-06-18 22:08:59 -0600116
117 Returns:
Simon Glass9b330382020-11-08 20:36:21 -0700118 str: line with the correct number of tabs appeneded. If the line already
Simon Glass2be282c2017-06-18 22:08:59 -0600119 extends past that tab stop then a single space is appended.
120 """
121 if len(line) >= num_tabs * 8:
122 return line + ' '
123 return line + '\t' * (num_tabs - len(line) // 8)
124
Simon Glass56e0bbe2017-06-18 22:09:02 -0600125def get_value(ftype, value):
126 """Get a value as a C expression
127
128 For integers this returns a byte-swapped (little-endian) hex string
129 For bytes this returns a hex string, e.g. 0x12
130 For strings this returns a literal string enclosed in quotes
131 For booleans this return 'true'
132
133 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700134 ftype (fdt.Type): Data type (fdt_util)
135 value (bytes): Data value, as a string of bytes
136
137 Returns:
138 str: String representation of the value
Simon Glass56e0bbe2017-06-18 22:09:02 -0600139 """
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700140 if ftype == fdt.Type.INT:
Simon Glassccc3da72020-12-23 08:11:19 -0700141 val = '%#x' % fdt_util.fdt32_to_cpu(value)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700142 elif ftype == fdt.Type.BYTE:
Simon Glass78128d52020-12-03 16:55:16 -0700143 char = value[0]
Simon Glassccc3da72020-12-23 08:11:19 -0700144 val = '%#x' % (ord(char) if isinstance(char, str) else char)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700145 elif ftype == fdt.Type.STRING:
Simon Glassf02d0eb2020-07-07 21:32:06 -0600146 # Handle evil ACPI backslashes by adding another backslash before them.
147 # So "\\_SB.GPO0" in the device tree effectively stays like that in C
Simon Glassccc3da72020-12-23 08:11:19 -0700148 val = '"%s"' % value.replace('\\', '\\\\')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700149 elif ftype == fdt.Type.BOOL:
Simon Glassccc3da72020-12-23 08:11:19 -0700150 val = 'true'
Simon Glass9b330382020-11-08 20:36:21 -0700151 else: # ftype == fdt.Type.INT64:
Simon Glassccc3da72020-12-23 08:11:19 -0700152 val = '%#x' % value
153 return val
Simon Glass56e0bbe2017-06-18 22:09:02 -0600154
155def get_compat_name(node):
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300156 """Get the node's list of compatible string as a C identifiers
Simon Glass56e0bbe2017-06-18 22:09:02 -0600157
158 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700159 node (fdt.Node): Node object to check
Simon Glass56e0bbe2017-06-18 22:09:02 -0600160 Return:
Simon Glassccc3da72020-12-23 08:11:19 -0700161 list of str: List of C identifiers for all the compatible strings
Simon Glass56e0bbe2017-06-18 22:09:02 -0600162 """
163 compat = node.props['compatible'].value
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300164 if not isinstance(compat, list):
165 compat = [compat]
166 return [conv_name_to_c(c) for c in compat]
Simon Glass56e0bbe2017-06-18 22:09:02 -0600167
Simon Glass56e0bbe2017-06-18 22:09:02 -0600168
Simon Glassccc3da72020-12-23 08:11:19 -0700169class DtbPlatdata():
Simon Glass7581c012017-06-18 22:08:58 -0600170 """Provide a means to convert device tree binary data to platform data
171
172 The output of this process is C structures which can be used in space-
173 constrained encvironments where the ~3KB code overhead of device tree
174 code is not affordable.
175
176 Properties:
Simon Glass2be282c2017-06-18 22:08:59 -0600177 _fdt: Fdt object, referencing the device tree
Simon Glass7581c012017-06-18 22:08:58 -0600178 _dtb_fname: Filename of the input device tree binary file
Simon Glass1b272732020-10-03 11:31:25 -0600179 _valid_nodes: A list of Node object with compatible strings. The list
180 is ordered by conv_name_to_c(node.name)
Simon Glasse36024b2017-06-18 22:09:01 -0600181 _include_disabled: true to include nodes marked status = "disabled"
Simon Glass7581c012017-06-18 22:08:58 -0600182 _outfile: The current output file (sys.stdout or a real file)
Walter Lozano361e7332020-06-25 01:10:08 -0300183 _warning_disabled: true to disable warnings about driver names not found
Simon Glass7581c012017-06-18 22:08:58 -0600184 _lines: Stashed list of output lines for outputting in the future
Simon Glass7d637c12020-12-23 08:11:23 -0700185 _drivers: Dict of valid driver names found in drivers/
186 key: Driver name
187 value: Driver for that driver
Walter Lozanodac82282020-07-03 08:07:17 -0300188 _driver_aliases: Dict that holds aliases for driver names
189 key: Driver alias declared with
190 U_BOOT_DRIVER_ALIAS(driver_alias, driver_name)
191 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300192 _drivers_additional: List of additional drivers to use during scanning
Simon Glassbe44f272020-12-28 20:34:51 -0700193 _dirname: Directory to hold output files, or None for none (all files
194 go to stdout)
Simon Glass7581c012017-06-18 22:08:58 -0600195 """
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300196 def __init__(self, dtb_fname, include_disabled, warning_disabled,
Simon Glass78128d52020-12-03 16:55:16 -0700197 drivers_additional=None):
Simon Glass2be282c2017-06-18 22:08:59 -0600198 self._fdt = None
Simon Glass7581c012017-06-18 22:08:58 -0600199 self._dtb_fname = dtb_fname
200 self._valid_nodes = None
Simon Glasse36024b2017-06-18 22:09:01 -0600201 self._include_disabled = include_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600202 self._outfile = None
Walter Lozano361e7332020-06-25 01:10:08 -0300203 self._warning_disabled = warning_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600204 self._lines = []
Simon Glass7d637c12020-12-23 08:11:23 -0700205 self._drivers = {}
Walter Lozanodac82282020-07-03 08:07:17 -0300206 self._driver_aliases = {}
Simon Glass78128d52020-12-03 16:55:16 -0700207 self._drivers_additional = drivers_additional or []
Simon Glassbe44f272020-12-28 20:34:51 -0700208 self._dirnames = [None] * len(Ftype)
Walter Lozanodac82282020-07-03 08:07:17 -0300209
210 def get_normalized_compat_name(self, node):
211 """Get a node's normalized compat name
212
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300213 Returns a valid driver name by retrieving node's list of compatible
Walter Lozanodac82282020-07-03 08:07:17 -0300214 string as a C identifier and performing a check against _drivers
215 and a lookup in driver_aliases printing a warning in case of failure.
216
217 Args:
Simon Glassccc3da72020-12-23 08:11:19 -0700218 node (Node): Node object to check
Walter Lozanodac82282020-07-03 08:07:17 -0300219 Return:
220 Tuple:
221 Driver name associated with the first compatible string
222 List of C identifiers for all the other compatible strings
223 (possibly empty)
224 In case of no match found, the return will be the same as
225 get_compat_name()
226 """
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300227 compat_list_c = get_compat_name(node)
Walter Lozanodac82282020-07-03 08:07:17 -0300228
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300229 for compat_c in compat_list_c:
Simon Glass7d637c12020-12-23 08:11:23 -0700230 if not compat_c in self._drivers.keys():
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300231 compat_c = self._driver_aliases.get(compat_c)
232 if not compat_c:
233 continue
234
235 aliases_c = compat_list_c
236 if compat_c in aliases_c:
237 aliases_c.remove(compat_c)
238 return compat_c, aliases_c
239
240 if not self._warning_disabled:
241 print('WARNING: the driver %s was not found in the driver list'
242 % (compat_list_c[0]))
243
244 return compat_list_c[0], compat_list_c[1:]
Simon Glass7581c012017-06-18 22:08:58 -0600245
Simon Glassbe44f272020-12-28 20:34:51 -0700246 def setup_output_dirs(self, output_dirs):
247 """Set up the output directories
248
249 This should be done before setup_output() is called
250
251 Args:
252 output_dirs (tuple of str):
253 Directory to use for C output files.
254 Use None to write files relative current directory
255 Directory to use for H output files.
256 Defaults to the C output dir
257 """
258 def process_dir(ftype, dirname):
259 if dirname:
260 os.makedirs(dirname, exist_ok=True)
261 self._dirnames[ftype] = dirname
262
263 if output_dirs:
264 c_dirname = output_dirs[0]
265 h_dirname = output_dirs[1] if len(output_dirs) > 1 else c_dirname
266 process_dir(Ftype.SOURCE, c_dirname)
267 process_dir(Ftype.HEADER, h_dirname)
268
269 def setup_output(self, ftype, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600270 """Set up the output destination
271
Simon Glass2be282c2017-06-18 22:08:59 -0600272 Once this is done, future calls to self.out() will output to this
Simon Glassbe44f272020-12-28 20:34:51 -0700273 file. The file used is as follows:
274
275 self._dirnames[ftype] is None: output to fname, or stdout if None
276 self._dirnames[ftype] is not None: output to fname in that directory
277
278 Calling this function multiple times will close the old file and open
279 the new one. If they are the same file, nothing happens and output will
280 continue to the same file.
Simon Glass7581c012017-06-18 22:08:58 -0600281
282 Args:
Simon Glassbe44f272020-12-28 20:34:51 -0700283 ftype (str): Type of file to create ('c' or 'h')
284 fname (str): Filename to send output to. If there is a directory in
285 self._dirnames for this file type, it will be put in that
286 directory
Simon Glass7581c012017-06-18 22:08:58 -0600287 """
Simon Glassbe44f272020-12-28 20:34:51 -0700288 dirname = self._dirnames[ftype]
289 if dirname:
290 pathname = os.path.join(dirname, fname)
291 if self._outfile:
292 self._outfile.close()
293 self._outfile = open(pathname, 'w')
294 elif fname:
295 if not self._outfile:
296 self._outfile = open(fname, 'w')
Simon Glassf62cea02020-12-28 20:34:48 -0700297 else:
298 self._outfile = sys.stdout
Simon Glass7581c012017-06-18 22:08:58 -0600299
Simon Glassbe44f272020-12-28 20:34:51 -0700300 def finish_output(self):
301 """Finish outputing to a file
302
303 This closes the output file, if one is in use
304 """
305 if self._outfile != sys.stdout:
306 self._outfile.close()
307
Simon Glass2be282c2017-06-18 22:08:59 -0600308 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600309 """Output a string to the output file
310
311 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700312 line (str): String to output
Simon Glass7581c012017-06-18 22:08:58 -0600313 """
Simon Glass2be282c2017-06-18 22:08:59 -0600314 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600315
Simon Glass2be282c2017-06-18 22:08:59 -0600316 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600317 """Buffer up a string to send later
318
319 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700320 line (str): String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600321 """
Simon Glass2be282c2017-06-18 22:08:59 -0600322 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600323
Simon Glass2be282c2017-06-18 22:08:59 -0600324 def get_buf(self):
Simon Glass7581c012017-06-18 22:08:58 -0600325 """Get the contents of the output buffer, and clear it
326
327 Returns:
Simon Glass9b330382020-11-08 20:36:21 -0700328 list(str): The output buffer, which is then cleared for future use
Simon Glass7581c012017-06-18 22:08:58 -0600329 """
330 lines = self._lines
331 self._lines = []
332 return lines
333
Simon Glassd5031142017-08-29 14:16:01 -0600334 def out_header(self):
335 """Output a message indicating that this is an auto-generated file"""
336 self.out('''/*
337 * DO NOT MODIFY
338 *
339 * This file was generated by dtoc from a .dtb (device tree binary) file.
340 */
341
342''')
343
Simon Glass8fed2eb2017-08-29 14:15:55 -0600344 def get_phandle_argc(self, prop, node_name):
345 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600346
Simon Glass8fed2eb2017-08-29 14:15:55 -0600347 We have no reliable way of detecting whether a node uses a phandle
348 or not. As an interim measure, use a list of known property names.
Simon Glass2925c262017-08-29 14:15:54 -0600349
Simon Glass8fed2eb2017-08-29 14:15:55 -0600350 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700351 prop (fdt.Prop): Prop object to check
352 node_name (str): Node name, only used for raising an error
353 Returns:
354 int or None: Number of argument cells is this is a phandle,
355 else None
356 Raises:
357 ValueError: if the phandle cannot be parsed or the required property
358 is not present
Simon Glass8fed2eb2017-08-29 14:15:55 -0600359 """
Walter Lozanoad340172020-06-25 01:10:16 -0300360 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass760b7172018-07-06 10:27:31 -0600361 if not isinstance(prop.value, list):
362 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600363 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600364 i = 0
365
366 max_args = 0
367 args = []
368 while i < len(val):
369 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-07-06 10:27:31 -0600370 # If we get to the end of the list, stop. This can happen
371 # since some nodes have more phandles in the list than others,
372 # but we allocate enough space for the largest list. So those
373 # nodes with shorter lists end up with zeroes at the end.
374 if not phandle:
375 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600376 target = self._fdt.phandle_to_node.get(phandle)
377 if not target:
378 raise ValueError("Cannot parse '%s' in node '%s'" %
379 (prop.name, node_name))
Walter Lozanoad340172020-06-25 01:10:16 -0300380 cells = None
381 for prop_name in ['#clock-cells', '#gpio-cells']:
382 cells = target.props.get(prop_name)
383 if cells:
384 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600385 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300386 raise ValueError("Node '%s' has no cells property" %
Simon Glass9b330382020-11-08 20:36:21 -0700387 (target.name))
Simon Glass8fed2eb2017-08-29 14:15:55 -0600388 num_args = fdt_util.fdt32_to_cpu(cells.value)
389 max_args = max(max_args, num_args)
390 args.append(num_args)
391 i += 1 + num_args
392 return PhandleInfo(max_args, args)
393 return None
Simon Glass2925c262017-08-29 14:15:54 -0600394
Simon Glass78128d52020-12-03 16:55:16 -0700395 def scan_driver(self, fname):
Walter Lozanodac82282020-07-03 08:07:17 -0300396 """Scan a driver file to build a list of driver names and aliases
397
398 This procedure will populate self._drivers and self._driver_aliases
399
400 Args
Simon Glass78128d52020-12-03 16:55:16 -0700401 fname: Driver filename to scan
Walter Lozanodac82282020-07-03 08:07:17 -0300402 """
Simon Glass78128d52020-12-03 16:55:16 -0700403 with open(fname, encoding='utf-8') as inf:
Walter Lozanodac82282020-07-03 08:07:17 -0300404 try:
Simon Glass78128d52020-12-03 16:55:16 -0700405 buff = inf.read()
Walter Lozanodac82282020-07-03 08:07:17 -0300406 except UnicodeDecodeError:
407 # This seems to happen on older Python versions
Simon Glass78128d52020-12-03 16:55:16 -0700408 print("Skipping file '%s' due to unicode error" % fname)
Walter Lozanodac82282020-07-03 08:07:17 -0300409 return
410
411 # The following re will search for driver names declared as
412 # U_BOOT_DRIVER(driver_name)
Simon Glassccc3da72020-12-23 08:11:19 -0700413 drivers = re.findall(r'U_BOOT_DRIVER\((.*)\)', buff)
Walter Lozanodac82282020-07-03 08:07:17 -0300414
415 for driver in drivers:
Simon Glass7d637c12020-12-23 08:11:23 -0700416 self._drivers[driver] = Driver(driver)
Walter Lozanodac82282020-07-03 08:07:17 -0300417
418 # The following re will search for driver aliases declared as
419 # U_BOOT_DRIVER_ALIAS(alias, driver_name)
Simon Glass78128d52020-12-03 16:55:16 -0700420 driver_aliases = re.findall(
Simon Glassccc3da72020-12-23 08:11:19 -0700421 r'U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
Simon Glass78128d52020-12-03 16:55:16 -0700422 buff)
Walter Lozanodac82282020-07-03 08:07:17 -0300423
424 for alias in driver_aliases: # pragma: no cover
425 if len(alias) != 2:
426 continue
427 self._driver_aliases[alias[1]] = alias[0]
428
429 def scan_drivers(self):
430 """Scan the driver folders to build a list of driver names and aliases
431
432 This procedure will populate self._drivers and self._driver_aliases
433
434 """
435 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
436 if basedir == '':
437 basedir = './'
Simon Glass78128d52020-12-03 16:55:16 -0700438 for (dirpath, _, filenames) in os.walk(basedir):
439 for fname in filenames:
440 if not fname.endswith('.c'):
Walter Lozanodac82282020-07-03 08:07:17 -0300441 continue
Simon Glass78128d52020-12-03 16:55:16 -0700442 self.scan_driver(dirpath + '/' + fname)
Walter Lozanodac82282020-07-03 08:07:17 -0300443
Simon Glass78128d52020-12-03 16:55:16 -0700444 for fname in self._drivers_additional:
445 if not isinstance(fname, str) or len(fname) == 0:
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300446 continue
Simon Glass78128d52020-12-03 16:55:16 -0700447 if fname[0] == '/':
448 self.scan_driver(fname)
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300449 else:
Simon Glass78128d52020-12-03 16:55:16 -0700450 self.scan_driver(basedir + '/' + fname)
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300451
Simon Glass2be282c2017-06-18 22:08:59 -0600452 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200453 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600454
Simon Glass2be282c2017-06-18 22:08:59 -0600455 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600456 device tree root node, and progress from there.
457 """
Simon Glass2be282c2017-06-18 22:08:59 -0600458 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600459
Simon Glass1b272732020-10-03 11:31:25 -0600460 def scan_node(self, root, valid_nodes):
Simon Glass2be282c2017-06-18 22:08:59 -0600461 """Scan a node and subnodes to build a tree of node and phandle info
462
Simon Glass72ab7c52017-08-29 14:15:53 -0600463 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600464
465 Args:
Simon Glassccc3da72020-12-23 08:11:19 -0700466 root (Node): Root node for scan
467 valid_nodes (list of Node): List of Node objects to add to
Simon Glass2be282c2017-06-18 22:08:59 -0600468 """
Simon Glass7581c012017-06-18 22:08:58 -0600469 for node in root.subnodes:
470 if 'compatible' in node.props:
471 status = node.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600472 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600473 status.value != 'disabled'):
Simon Glass1b272732020-10-03 11:31:25 -0600474 valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600475
476 # recurse to handle any subnodes
Simon Glass1b272732020-10-03 11:31:25 -0600477 self.scan_node(node, valid_nodes)
Simon Glass7581c012017-06-18 22:08:58 -0600478
Simon Glass2be282c2017-06-18 22:08:59 -0600479 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600480 """Scan the device tree for useful information
481
482 This fills in the following properties:
Simon Glass7581c012017-06-18 22:08:58 -0600483 _valid_nodes: A list of nodes we wish to consider include in the
484 platform data
485 """
Simon Glass1b272732020-10-03 11:31:25 -0600486 valid_nodes = []
487 self.scan_node(self._fdt.GetRoot(), valid_nodes)
488 self._valid_nodes = sorted(valid_nodes,
489 key=lambda x: conv_name_to_c(x.name))
490 for idx, node in enumerate(self._valid_nodes):
491 node.idx = idx
Simon Glass7581c012017-06-18 22:08:58 -0600492
Simon Glassc20ee0e2017-08-29 14:15:50 -0600493 @staticmethod
494 def get_num_cells(node):
495 """Get the number of cells in addresses and sizes for this node
496
497 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700498 node (fdt.None): Node to check
Simon Glassc20ee0e2017-08-29 14:15:50 -0600499
500 Returns:
501 Tuple:
502 Number of address cells for this node
503 Number of size cells for this node
504 """
505 parent = node.parent
Simon Glass78128d52020-12-03 16:55:16 -0700506 num_addr, num_size = 2, 2
Simon Glassc20ee0e2017-08-29 14:15:50 -0600507 if parent:
Simon Glass78128d52020-12-03 16:55:16 -0700508 addr_prop = parent.props.get('#address-cells')
509 size_prop = parent.props.get('#size-cells')
510 if addr_prop:
511 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
512 if size_prop:
513 num_size = fdt_util.fdt32_to_cpu(size_prop.value)
514 return num_addr, num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600515
516 def scan_reg_sizes(self):
517 """Scan for 64-bit 'reg' properties and update the values
518
519 This finds 'reg' properties with 64-bit data and converts the value to
520 an array of 64-values. This allows it to be output in a way that the
521 C code can read.
522 """
523 for node in self._valid_nodes:
524 reg = node.props.get('reg')
525 if not reg:
526 continue
Simon Glass78128d52020-12-03 16:55:16 -0700527 num_addr, num_size = self.get_num_cells(node)
528 total = num_addr + num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600529
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700530 if reg.type != fdt.Type.INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600531 raise ValueError("Node '%s' reg property is not an int" %
532 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600533 if len(reg.value) % total:
Simon Glass9b330382020-11-08 20:36:21 -0700534 raise ValueError(
535 "Node '%s' reg property has %d cells "
536 'which is not a multiple of na + ns = %d + %d)' %
Simon Glass78128d52020-12-03 16:55:16 -0700537 (node.name, len(reg.value), num_addr, num_size))
538 reg.num_addr = num_addr
539 reg.num_size = num_size
540 if num_addr != 1 or num_size != 1:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700541 reg.type = fdt.Type.INT64
Simon Glassc20ee0e2017-08-29 14:15:50 -0600542 i = 0
543 new_value = []
544 val = reg.value
545 if not isinstance(val, list):
546 val = [val]
547 while i < len(val):
Simon Glass78128d52020-12-03 16:55:16 -0700548 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
549 i += num_addr
550 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
551 i += num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600552 new_value += [addr, size]
553 reg.value = new_value
554
Simon Glass2be282c2017-06-18 22:08:59 -0600555 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600556 """Scan the device tree building up the C structures we will use.
557
558 Build a dict keyed by C struct name containing a dict of Prop
559 object for each struct field (keyed by property name). Where the
560 same struct appears multiple times, try to use the 'widest'
561 property, i.e. the one with a type which can express all others.
562
563 Once the widest property is determined, all other properties are
564 updated to match that width.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600565
566 Returns:
Simon Glassccc3da72020-12-23 08:11:19 -0700567 dict of dict: dict containing structures:
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600568 key (str): Node name, as a C identifier
569 value: dict containing structure fields:
570 key (str): Field name
571 value: Prop object with field information
Simon Glass7581c012017-06-18 22:08:58 -0600572 """
Simon Glass1b272732020-10-03 11:31:25 -0600573 structs = collections.OrderedDict()
Simon Glass7581c012017-06-18 22:08:58 -0600574 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300575 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600576 fields = {}
577
578 # Get a list of all the valid properties in this node.
579 for name, prop in node.props.items():
580 if name not in PROP_IGNORE_LIST and name[0] != '#':
581 fields[name] = copy.deepcopy(prop)
582
583 # If we've seen this node_name before, update the existing struct.
584 if node_name in structs:
585 struct = structs[node_name]
586 for name, prop in fields.items():
587 oldprop = struct.get(name)
588 if oldprop:
589 oldprop.Widen(prop)
590 else:
591 struct[name] = prop
592
593 # Otherwise store this as a new struct.
594 else:
595 structs[node_name] = fields
596
Simon Glass7581c012017-06-18 22:08:58 -0600597 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300598 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600599 struct = structs[node_name]
600 for name, prop in node.props.items():
601 if name not in PROP_IGNORE_LIST and name[0] != '#':
602 prop.Widen(struct[name])
Simon Glass7581c012017-06-18 22:08:58 -0600603
Simon Glass7581c012017-06-18 22:08:58 -0600604 return structs
605
Simon Glass2be282c2017-06-18 22:08:59 -0600606 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600607 """Figure out what phandles each node uses
608
609 We need to be careful when outputing nodes that use phandles since
610 they must come after the declaration of the phandles in the C file.
611 Otherwise we get a compiler error since the phandle struct is not yet
612 declared.
613
614 This function adds to each node a list of phandle nodes that the node
615 depends on. This allows us to output things in the right order.
616 """
617 for node in self._valid_nodes:
618 node.phandles = set()
619 for pname, prop in node.props.items():
620 if pname in PROP_IGNORE_LIST or pname[0] == '#':
621 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600622 info = self.get_phandle_argc(prop, node.name)
623 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600624 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600625 pos = 0
626 for args in info.args:
627 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600628 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
629 target_node = self._fdt.phandle_to_node[phandle]
630 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600631 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600632
633
Simon Glass2be282c2017-06-18 22:08:59 -0600634 def generate_structs(self, structs):
Simon Glass7581c012017-06-18 22:08:58 -0600635 """Generate struct defintions for the platform data
636
637 This writes out the body of a header file consisting of structure
638 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100639 doc/driver-model/of-plat.rst for more information.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600640
641 Args:
Simon Glassccc3da72020-12-23 08:11:19 -0700642 structs (dict): dict containing structures:
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600643 key (str): Node name, as a C identifier
644 value: dict containing structure fields:
645 key (str): Field name
646 value: Prop object with field information
647
Simon Glass7581c012017-06-18 22:08:58 -0600648 """
Simon Glassd5031142017-08-29 14:16:01 -0600649 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600650 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900651 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600652
653 # Output the struct definition
654 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600655 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600656 for pname in sorted(structs[name]):
657 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600658 info = self.get_phandle_argc(prop, structs[name])
659 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600660 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600661 struct_name = 'struct phandle_%d_arg' % info.max_args
662 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600663 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600664 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600665 else:
666 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600667 self.out('\t%s%s' % (tab_to(2, ptype),
668 conv_name_to_c(prop.name)))
669 if isinstance(prop.value, list):
670 self.out('[%d]' % len(prop.value))
671 self.out(';\n')
672 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600673
Simon Glassabf0c802020-12-23 08:11:20 -0700674 def _output_list(self, node, prop):
675 """Output the C code for a devicetree property that holds a list
676
677 Args:
678 node (fdt.Node): Node to output
679 prop (fdt.Prop): Prop to output
680 """
681 self.buf('{')
682 vals = []
683 # For phandles, output a reference to the platform data
684 # of the target node.
685 info = self.get_phandle_argc(prop, node.name)
686 if info:
687 # Process the list as pairs of (phandle, id)
688 pos = 0
689 for args in info.args:
690 phandle_cell = prop.value[pos]
691 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
692 target_node = self._fdt.phandle_to_node[phandle]
693 arg_values = []
694 for i in range(args):
695 arg_values.append(
696 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
697 pos += 1 + args
698 vals.append('\t{%d, {%s}}' % (target_node.idx,
699 ', '.join(arg_values)))
700 for val in vals:
701 self.buf('\n\t\t%s,' % val)
702 else:
703 for val in prop.value:
704 vals.append(get_value(prop.type, val))
705
706 # Put 8 values per line to avoid very long lines.
707 for i in range(0, len(vals), 8):
708 if i:
709 self.buf(',\n\t\t')
710 self.buf(', '.join(vals[i:i + 8]))
711 self.buf('}')
712
Simon Glass221ddc12020-12-23 08:11:21 -0700713 def _declare_device(self, var_name, struct_name, node_parent):
714 """Add a device declaration to the output
715
Simon Glass20e442a2020-12-28 20:34:54 -0700716 This declares a U_BOOT_DRVINFO() for the device being processed
Simon Glass221ddc12020-12-23 08:11:21 -0700717
718 Args:
719 var_name (str): C name for the node
720 struct_name (str): Name for the dt struct associated with the node
721 node_parent (Node): Parent of the node (or None if none)
722 """
Simon Glass20e442a2020-12-28 20:34:54 -0700723 self.buf('U_BOOT_DRVINFO(%s) = {\n' % var_name)
Simon Glass221ddc12020-12-23 08:11:21 -0700724 self.buf('\t.name\t\t= "%s",\n' % struct_name)
725 self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, var_name))
726 self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
727 idx = -1
728 if node_parent and node_parent in self._valid_nodes:
729 idx = node_parent.idx
730 self.buf('\t.parent_idx\t= %d,\n' % idx)
731 self.buf('};\n')
732 self.buf('\n')
733
Simon Glass161dac12020-12-23 08:11:22 -0700734 def _output_prop(self, node, prop):
735 """Output a line containing the value of a struct member
736
737 Args:
738 node (Node): Node being output
739 prop (Prop): Prop object to output
740 """
741 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
742 return
743 member_name = conv_name_to_c(prop.name)
744 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
745
746 # Special handling for lists
747 if isinstance(prop.value, list):
748 self._output_list(node, prop)
749 else:
750 self.buf(get_value(prop.type, prop.value))
751 self.buf(',\n')
752
753 def _output_values(self, var_name, struct_name, node):
754 """Output the definition of a device's struct values
755
756 Args:
757 var_name (str): C name for the node
758 struct_name (str): Name for the dt struct associated with the node
759 node (Node): Node being output
760 """
761 self.buf('static struct %s%s %s%s = {\n' %
762 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
763 for pname in sorted(node.props):
764 self._output_prop(node, node.props[pname])
765 self.buf('};\n')
766
Simon Glass2be282c2017-06-18 22:08:59 -0600767 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600768 """Output the C code for a node
769
770 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700771 node (fdt.Node): node to output
Simon Glass7581c012017-06-18 22:08:58 -0600772 """
Walter Lozanodac82282020-07-03 08:07:17 -0300773 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600774 var_name = conv_name_to_c(node.name)
Simon Glass1b272732020-10-03 11:31:25 -0600775 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Simon Glass7581c012017-06-18 22:08:58 -0600776
Simon Glass161dac12020-12-23 08:11:22 -0700777 self._output_values(var_name, struct_name, node)
Simon Glass221ddc12020-12-23 08:11:21 -0700778 self._declare_device(var_name, struct_name, node.parent)
Simon Glass7581c012017-06-18 22:08:58 -0600779
Simon Glass2be282c2017-06-18 22:08:59 -0600780 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600781
Simon Glass2be282c2017-06-18 22:08:59 -0600782 def generate_tables(self):
Simon Glass7581c012017-06-18 22:08:58 -0600783 """Generate device defintions for the platform data
784
785 This writes out C platform data initialisation data and
Simon Glass20e442a2020-12-28 20:34:54 -0700786 U_BOOT_DRVINFO() declarations for each valid node. Where a node has
Simon Glass7581c012017-06-18 22:08:58 -0600787 multiple compatible strings, a #define is used to make them equivalent.
788
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100789 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600790 information.
791 """
Simon Glassd5031142017-08-29 14:16:01 -0600792 self.out_header()
Simon Glass20e442a2020-12-28 20:34:54 -0700793 self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
Simon Glasscb43ac12020-10-03 11:31:41 -0600794 self.out('#define DT_PLATDATA_C\n')
795 self.out('\n')
Simon Glass2be282c2017-06-18 22:08:59 -0600796 self.out('#include <common.h>\n')
797 self.out('#include <dm.h>\n')
798 self.out('#include <dt-structs.h>\n')
799 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600800 nodes_to_output = list(self._valid_nodes)
801
802 # Keep outputing nodes until there is none left
803 while nodes_to_output:
804 node = nodes_to_output[0]
805 # Output all the node's dependencies first
806 for req_node in node.phandles:
807 if req_node in nodes_to_output:
Simon Glass2be282c2017-06-18 22:08:59 -0600808 self.output_node(req_node)
Simon Glass7581c012017-06-18 22:08:58 -0600809 nodes_to_output.remove(req_node)
Simon Glass2be282c2017-06-18 22:08:59 -0600810 self.output_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600811 nodes_to_output.remove(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600812
Walter Lozano51f12632020-06-25 01:10:13 -0300813 # Define dm_populate_phandle_data() which will add the linking between
814 # nodes using DM_GET_DEVICE
815 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
816 self.buf('void dm_populate_phandle_data(void) {\n')
Walter Lozano51f12632020-06-25 01:10:13 -0300817 self.buf('}\n')
818
819 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600820
Simon Glass192c1112020-12-28 20:34:50 -0700821
Simon Glassbe44f272020-12-28 20:34:51 -0700822# Types of output file we understand
823# key: Command used to generate this file
824# value: OutputFile for this command
825OUTPUT_FILES = {
826 'struct': OutputFile(Ftype.HEADER, 'dt-structs-gen.h'),
827 'platdata': OutputFile(Ftype.SOURCE, 'dt-platdata.c'),
828 }
829
830
Simon Glass192c1112020-12-28 20:34:50 -0700831def run_steps(args, dtb_file, include_disabled, output, output_dirs,
832 warning_disabled=False, drivers_additional=None):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600833 """Run all the steps of the dtoc tool
834
835 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700836 args (list): List of non-option arguments provided to the problem
837 dtb_file (str): Filename of dtb file to process
838 include_disabled (bool): True to include disabled nodes
Simon Glassf62cea02020-12-28 20:34:48 -0700839 output (str): Name of output file (None for stdout)
Simon Glass192c1112020-12-28 20:34:50 -0700840 output_dirs (tuple of str):
841 Directory to put C output files
842 Directory to put H output files
Simon Glass78128d52020-12-03 16:55:16 -0700843 warning_disabled (bool): True to avoid showing warnings about missing
844 drivers
Simon Glassccc3da72020-12-23 08:11:19 -0700845 drivers_additional (list): List of additional drivers to use during
Simon Glass78128d52020-12-03 16:55:16 -0700846 scanning
Simon Glass9b330382020-11-08 20:36:21 -0700847 Raises:
848 ValueError: if args has no command, or an unknown command
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600849 """
850 if not args:
Simon Glassbe44f272020-12-28 20:34:51 -0700851 raise ValueError('Please specify a command: struct, platdata, all')
852 if output and output_dirs and any(output_dirs):
853 raise ValueError('Must specify either output or output_dirs, not both')
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600854
Simon Glass78128d52020-12-03 16:55:16 -0700855 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled,
856 drivers_additional)
Walter Lozanodac82282020-07-03 08:07:17 -0300857 plat.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600858 plat.scan_dtb()
859 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600860 plat.scan_reg_sizes()
Simon Glassbe44f272020-12-28 20:34:51 -0700861 plat.setup_output_dirs(output_dirs)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600862 structs = plat.scan_structs()
863 plat.scan_phandles()
864
Simon Glass10cbd3b2020-12-28 20:34:52 -0700865 cmds = args[0].split(',')
866 if 'all' in cmds:
867 cmds = sorted(OUTPUT_FILES.keys())
868 for cmd in cmds:
Simon Glassbe44f272020-12-28 20:34:51 -0700869 outfile = OUTPUT_FILES.get(cmd)
870 if not outfile:
871 raise ValueError("Unknown command '%s': (use: %s)" %
Simon Glass10cbd3b2020-12-28 20:34:52 -0700872 (cmd, ', '.join(sorted(OUTPUT_FILES.keys()))))
Simon Glassbe44f272020-12-28 20:34:51 -0700873 plat.setup_output(outfile.ftype,
874 outfile.fname if output_dirs else output)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600875 if cmd == 'struct':
876 plat.generate_structs(structs)
877 elif cmd == 'platdata':
878 plat.generate_tables()
Simon Glassbe44f272020-12-28 20:34:51 -0700879 plat.finish_output()