blob: ad71f703e52af70f28a94f9a796d8c6c105f7b8a [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 Glass1b272732020-10-03 11:31:25 -0600139 _valid_nodes: A list of Node object with compatible strings. The list
140 is ordered by conv_name_to_c(node.name)
Simon Glasse36024b2017-06-18 22:09:01 -0600141 _include_disabled: true to include nodes marked status = "disabled"
Simon Glass7581c012017-06-18 22:08:58 -0600142 _outfile: The current output file (sys.stdout or a real file)
143 _lines: Stashed list of output lines for outputting in the future
Simon Glassbe44f272020-12-28 20:34:51 -0700144 _dirname: Directory to hold output files, or None for none (all files
145 go to stdout)
Simon Glassa7d5f962020-12-28 20:35:02 -0700146 _struct_data (dict): OrderedDict of dtplat structures to output
147 key (str): Node name, as a C identifier
148 value: dict containing structure fields:
149 key (str): Field name
150 value: Prop object with field information
Simon Glass1e0f3f42020-12-28 20:35:03 -0700151 _basedir (str): Base directory of source tree
Simon Glass7581c012017-06-18 22:08:58 -0600152 """
Simon Glassa542a702020-12-28 20:35:06 -0700153 def __init__(self, scan, dtb_fname, include_disabled):
154 self._scan = scan
Simon Glass2be282c2017-06-18 22:08:59 -0600155 self._fdt = None
Simon Glass7581c012017-06-18 22:08:58 -0600156 self._dtb_fname = dtb_fname
157 self._valid_nodes = None
Simon Glasse36024b2017-06-18 22:09:01 -0600158 self._include_disabled = include_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600159 self._outfile = None
160 self._lines = []
Simon Glassbe44f272020-12-28 20:34:51 -0700161 self._dirnames = [None] * len(Ftype)
Simon Glassa7d5f962020-12-28 20:35:02 -0700162 self._struct_data = collections.OrderedDict()
Simon Glass1e0f3f42020-12-28 20:35:03 -0700163 self._basedir = None
Walter Lozanodac82282020-07-03 08:07:17 -0300164
Simon Glassbe44f272020-12-28 20:34:51 -0700165 def setup_output_dirs(self, output_dirs):
166 """Set up the output directories
167
168 This should be done before setup_output() is called
169
170 Args:
171 output_dirs (tuple of str):
172 Directory to use for C output files.
173 Use None to write files relative current directory
174 Directory to use for H output files.
175 Defaults to the C output dir
176 """
177 def process_dir(ftype, dirname):
178 if dirname:
179 os.makedirs(dirname, exist_ok=True)
180 self._dirnames[ftype] = dirname
181
182 if output_dirs:
183 c_dirname = output_dirs[0]
184 h_dirname = output_dirs[1] if len(output_dirs) > 1 else c_dirname
185 process_dir(Ftype.SOURCE, c_dirname)
186 process_dir(Ftype.HEADER, h_dirname)
187
188 def setup_output(self, ftype, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600189 """Set up the output destination
190
Simon Glass2be282c2017-06-18 22:08:59 -0600191 Once this is done, future calls to self.out() will output to this
Simon Glassbe44f272020-12-28 20:34:51 -0700192 file. The file used is as follows:
193
194 self._dirnames[ftype] is None: output to fname, or stdout if None
195 self._dirnames[ftype] is not None: output to fname in that directory
196
197 Calling this function multiple times will close the old file and open
198 the new one. If they are the same file, nothing happens and output will
199 continue to the same file.
Simon Glass7581c012017-06-18 22:08:58 -0600200
201 Args:
Simon Glassbe44f272020-12-28 20:34:51 -0700202 ftype (str): Type of file to create ('c' or 'h')
203 fname (str): Filename to send output to. If there is a directory in
204 self._dirnames for this file type, it will be put in that
205 directory
Simon Glass7581c012017-06-18 22:08:58 -0600206 """
Simon Glassbe44f272020-12-28 20:34:51 -0700207 dirname = self._dirnames[ftype]
208 if dirname:
209 pathname = os.path.join(dirname, fname)
210 if self._outfile:
211 self._outfile.close()
212 self._outfile = open(pathname, 'w')
213 elif fname:
214 if not self._outfile:
215 self._outfile = open(fname, 'w')
Simon Glassf62cea02020-12-28 20:34:48 -0700216 else:
217 self._outfile = sys.stdout
Simon Glass7581c012017-06-18 22:08:58 -0600218
Simon Glassbe44f272020-12-28 20:34:51 -0700219 def finish_output(self):
220 """Finish outputing to a file
221
222 This closes the output file, if one is in use
223 """
224 if self._outfile != sys.stdout:
225 self._outfile.close()
226
Simon Glass2be282c2017-06-18 22:08:59 -0600227 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600228 """Output a string to the output file
229
230 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700231 line (str): String to output
Simon Glass7581c012017-06-18 22:08:58 -0600232 """
Simon Glass2be282c2017-06-18 22:08:59 -0600233 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600234
Simon Glass2be282c2017-06-18 22:08:59 -0600235 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600236 """Buffer up a string to send later
237
238 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700239 line (str): String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600240 """
Simon Glass2be282c2017-06-18 22:08:59 -0600241 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600242
Simon Glass2be282c2017-06-18 22:08:59 -0600243 def get_buf(self):
Simon Glass7581c012017-06-18 22:08:58 -0600244 """Get the contents of the output buffer, and clear it
245
246 Returns:
Simon Glass9b330382020-11-08 20:36:21 -0700247 list(str): The output buffer, which is then cleared for future use
Simon Glass7581c012017-06-18 22:08:58 -0600248 """
249 lines = self._lines
250 self._lines = []
251 return lines
252
Simon Glassd1055d62020-12-28 20:35:00 -0700253 def out_header(self, outfile):
254 """Output a message indicating that this is an auto-generated file
255
256 Args:
257 outfile: OutputFile describing the file being generated
258 """
Simon Glassd5031142017-08-29 14:16:01 -0600259 self.out('''/*
260 * DO NOT MODIFY
261 *
Simon Glassd1055d62020-12-28 20:35:00 -0700262 * %s.
263 * This was generated by dtoc from a .dtb (device tree binary) file.
Simon Glassd5031142017-08-29 14:16:01 -0600264 */
265
Simon Glassd1055d62020-12-28 20:35:00 -0700266''' % outfile.hdr_comment)
Simon Glassd5031142017-08-29 14:16:01 -0600267
Simon Glass8fed2eb2017-08-29 14:15:55 -0600268 def get_phandle_argc(self, prop, node_name):
269 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600270
Simon Glass8fed2eb2017-08-29 14:15:55 -0600271 We have no reliable way of detecting whether a node uses a phandle
272 or not. As an interim measure, use a list of known property names.
Simon Glass2925c262017-08-29 14:15:54 -0600273
Simon Glass8fed2eb2017-08-29 14:15:55 -0600274 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700275 prop (fdt.Prop): Prop object to check
276 node_name (str): Node name, only used for raising an error
277 Returns:
278 int or None: Number of argument cells is this is a phandle,
279 else None
280 Raises:
281 ValueError: if the phandle cannot be parsed or the required property
282 is not present
Simon Glass8fed2eb2017-08-29 14:15:55 -0600283 """
Walter Lozanoad340172020-06-25 01:10:16 -0300284 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass760b7172018-07-06 10:27:31 -0600285 if not isinstance(prop.value, list):
286 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600287 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600288 i = 0
289
290 max_args = 0
291 args = []
292 while i < len(val):
293 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-07-06 10:27:31 -0600294 # If we get to the end of the list, stop. This can happen
295 # since some nodes have more phandles in the list than others,
296 # but we allocate enough space for the largest list. So those
297 # nodes with shorter lists end up with zeroes at the end.
298 if not phandle:
299 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600300 target = self._fdt.phandle_to_node.get(phandle)
301 if not target:
302 raise ValueError("Cannot parse '%s' in node '%s'" %
303 (prop.name, node_name))
Walter Lozanoad340172020-06-25 01:10:16 -0300304 cells = None
305 for prop_name in ['#clock-cells', '#gpio-cells']:
306 cells = target.props.get(prop_name)
307 if cells:
308 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600309 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300310 raise ValueError("Node '%s' has no cells property" %
Simon Glass9b330382020-11-08 20:36:21 -0700311 (target.name))
Simon Glass8fed2eb2017-08-29 14:15:55 -0600312 num_args = fdt_util.fdt32_to_cpu(cells.value)
313 max_args = max(max_args, num_args)
314 args.append(num_args)
315 i += 1 + num_args
316 return PhandleInfo(max_args, args)
317 return None
Simon Glass2925c262017-08-29 14:15:54 -0600318
Simon Glass2be282c2017-06-18 22:08:59 -0600319 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200320 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600321
Simon Glass2be282c2017-06-18 22:08:59 -0600322 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600323 device tree root node, and progress from there.
324 """
Simon Glass2be282c2017-06-18 22:08:59 -0600325 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600326
Simon Glass1b272732020-10-03 11:31:25 -0600327 def scan_node(self, root, valid_nodes):
Simon Glass2be282c2017-06-18 22:08:59 -0600328 """Scan a node and subnodes to build a tree of node and phandle info
329
Simon Glass72ab7c52017-08-29 14:15:53 -0600330 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600331
332 Args:
Simon Glassccc3da72020-12-23 08:11:19 -0700333 root (Node): Root node for scan
334 valid_nodes (list of Node): List of Node objects to add to
Simon Glass2be282c2017-06-18 22:08:59 -0600335 """
Simon Glass7581c012017-06-18 22:08:58 -0600336 for node in root.subnodes:
337 if 'compatible' in node.props:
338 status = node.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600339 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600340 status.value != 'disabled'):
Simon Glass1b272732020-10-03 11:31:25 -0600341 valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600342
343 # recurse to handle any subnodes
Simon Glass1b272732020-10-03 11:31:25 -0600344 self.scan_node(node, valid_nodes)
Simon Glass7581c012017-06-18 22:08:58 -0600345
Simon Glass2be282c2017-06-18 22:08:59 -0600346 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600347 """Scan the device tree for useful information
348
349 This fills in the following properties:
Simon Glass7581c012017-06-18 22:08:58 -0600350 _valid_nodes: A list of nodes we wish to consider include in the
351 platform data
352 """
Simon Glass1b272732020-10-03 11:31:25 -0600353 valid_nodes = []
354 self.scan_node(self._fdt.GetRoot(), valid_nodes)
355 self._valid_nodes = sorted(valid_nodes,
356 key=lambda x: conv_name_to_c(x.name))
Simon Glass51d5d052021-02-03 06:00:58 -0700357
358 def prepare_nodes(self):
359 """Add extra properties to the nodes we are using
360
361 The following properties are added for use by dtoc:
362 idx: Index number of this node (0=first, etc.)
363 struct_name: Name of the struct dtd used by this node
364 var_name: C name for this node
365 child_devs: List of child devices for this node, each a None
366 child_refs: Dict of references for each child:
367 key: Position in child list (-1=head, 0=first, 1=second, ...
368 n-1=last, n=head)
369 seq: Sequence number of the device (unique within its uclass), or
370 -1 not not known yet
371 dev_ref: Reference to this device, e.g. 'DM_DEVICE_REF(serial)'
372 driver: Driver record for this node, or None if not known
373 uclass: Uclass record for this node, or None if not known
374 uclass_seq: Position of this device within the uclass list (0=first,
375 n-1=last)
376 parent_seq: Position of this device within it siblings (0=first,
377 n-1=last)
378 parent_driver: Driver record of the node's parent, or None if none.
379 We don't use node.parent.driver since node.parent may not be in
380 the list of valid nodes
381 """
Simon Glass1b272732020-10-03 11:31:25 -0600382 for idx, node in enumerate(self._valid_nodes):
383 node.idx = idx
Simon Glass51d5d052021-02-03 06:00:58 -0700384 node.struct_name, _ = self._scan.get_normalized_compat_name(node)
385 node.var_name = conv_name_to_c(node.name)
386 node.child_devs = []
387 node.child_refs = {}
388 node.seq = -1
389 node.dev_ref = None
390 node.driver = None
391 node.uclass = None
392 node.uclass_seq = None
393 node.parent_seq = None
394 node.parent_driver = None
Simon Glass7581c012017-06-18 22:08:58 -0600395
Simon Glassc20ee0e2017-08-29 14:15:50 -0600396 @staticmethod
397 def get_num_cells(node):
398 """Get the number of cells in addresses and sizes for this node
399
400 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700401 node (fdt.None): Node to check
Simon Glassc20ee0e2017-08-29 14:15:50 -0600402
403 Returns:
404 Tuple:
405 Number of address cells for this node
406 Number of size cells for this node
407 """
408 parent = node.parent
Simon Glass78128d52020-12-03 16:55:16 -0700409 num_addr, num_size = 2, 2
Simon Glassc20ee0e2017-08-29 14:15:50 -0600410 if parent:
Simon Glass78128d52020-12-03 16:55:16 -0700411 addr_prop = parent.props.get('#address-cells')
412 size_prop = parent.props.get('#size-cells')
413 if addr_prop:
414 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
415 if size_prop:
416 num_size = fdt_util.fdt32_to_cpu(size_prop.value)
417 return num_addr, num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600418
419 def scan_reg_sizes(self):
420 """Scan for 64-bit 'reg' properties and update the values
421
422 This finds 'reg' properties with 64-bit data and converts the value to
423 an array of 64-values. This allows it to be output in a way that the
424 C code can read.
425 """
426 for node in self._valid_nodes:
427 reg = node.props.get('reg')
428 if not reg:
429 continue
Simon Glass78128d52020-12-03 16:55:16 -0700430 num_addr, num_size = self.get_num_cells(node)
431 total = num_addr + num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600432
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700433 if reg.type != fdt.Type.INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600434 raise ValueError("Node '%s' reg property is not an int" %
435 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600436 if len(reg.value) % total:
Simon Glass9b330382020-11-08 20:36:21 -0700437 raise ValueError(
438 "Node '%s' reg property has %d cells "
439 'which is not a multiple of na + ns = %d + %d)' %
Simon Glass78128d52020-12-03 16:55:16 -0700440 (node.name, len(reg.value), num_addr, num_size))
441 reg.num_addr = num_addr
442 reg.num_size = num_size
443 if num_addr != 1 or num_size != 1:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700444 reg.type = fdt.Type.INT64
Simon Glassc20ee0e2017-08-29 14:15:50 -0600445 i = 0
446 new_value = []
447 val = reg.value
448 if not isinstance(val, list):
449 val = [val]
450 while i < len(val):
Simon Glass78128d52020-12-03 16:55:16 -0700451 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
452 i += num_addr
453 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
454 i += num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600455 new_value += [addr, size]
456 reg.value = new_value
457
Simon Glass2be282c2017-06-18 22:08:59 -0600458 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600459 """Scan the device tree building up the C structures we will use.
460
461 Build a dict keyed by C struct name containing a dict of Prop
462 object for each struct field (keyed by property name). Where the
463 same struct appears multiple times, try to use the 'widest'
464 property, i.e. the one with a type which can express all others.
465
466 Once the widest property is determined, all other properties are
467 updated to match that width.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600468
Simon Glassa7d5f962020-12-28 20:35:02 -0700469 The results are written to self._struct_data
Simon Glass7581c012017-06-18 22:08:58 -0600470 """
Simon Glassa7d5f962020-12-28 20:35:02 -0700471 structs = self._struct_data
Simon Glass7581c012017-06-18 22:08:58 -0600472 for node in self._valid_nodes:
Simon Glass7581c012017-06-18 22:08:58 -0600473 fields = {}
474
475 # Get a list of all the valid properties in this node.
476 for name, prop in node.props.items():
477 if name not in PROP_IGNORE_LIST and name[0] != '#':
478 fields[name] = copy.deepcopy(prop)
479
Simon Glasse525fea2021-02-03 06:00:59 -0700480 # If we've seen this struct_name before, update the existing struct
481 if node.struct_name in structs:
482 struct = structs[node.struct_name]
Simon Glass7581c012017-06-18 22:08:58 -0600483 for name, prop in fields.items():
484 oldprop = struct.get(name)
485 if oldprop:
486 oldprop.Widen(prop)
487 else:
488 struct[name] = prop
489
490 # Otherwise store this as a new struct.
491 else:
Simon Glasse525fea2021-02-03 06:00:59 -0700492 structs[node.struct_name] = fields
Simon Glass7581c012017-06-18 22:08:58 -0600493
Simon Glass7581c012017-06-18 22:08:58 -0600494 for node in self._valid_nodes:
Simon Glasse525fea2021-02-03 06:00:59 -0700495 struct = structs[node.struct_name]
Simon Glass7581c012017-06-18 22:08:58 -0600496 for name, prop in node.props.items():
497 if name not in PROP_IGNORE_LIST and name[0] != '#':
498 prop.Widen(struct[name])
Simon Glass7581c012017-06-18 22:08:58 -0600499
Simon Glass2be282c2017-06-18 22:08:59 -0600500 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600501 """Figure out what phandles each node uses
502
503 We need to be careful when outputing nodes that use phandles since
504 they must come after the declaration of the phandles in the C file.
505 Otherwise we get a compiler error since the phandle struct is not yet
506 declared.
507
508 This function adds to each node a list of phandle nodes that the node
509 depends on. This allows us to output things in the right order.
510 """
511 for node in self._valid_nodes:
512 node.phandles = set()
513 for pname, prop in node.props.items():
514 if pname in PROP_IGNORE_LIST or pname[0] == '#':
515 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600516 info = self.get_phandle_argc(prop, node.name)
517 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600518 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600519 pos = 0
520 for args in info.args:
521 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600522 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
523 target_node = self._fdt.phandle_to_node[phandle]
524 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600525 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600526
527
Simon Glassa7d5f962020-12-28 20:35:02 -0700528 def generate_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600529 """Generate struct defintions for the platform data
530
531 This writes out the body of a header file consisting of structure
532 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100533 doc/driver-model/of-plat.rst for more information.
Simon Glass7581c012017-06-18 22:08:58 -0600534 """
Simon Glassa7d5f962020-12-28 20:35:02 -0700535 structs = self._struct_data
Simon Glass2be282c2017-06-18 22:08:59 -0600536 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900537 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600538
539 # Output the struct definition
540 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600541 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600542 for pname in sorted(structs[name]):
543 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600544 info = self.get_phandle_argc(prop, structs[name])
545 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600546 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600547 struct_name = 'struct phandle_%d_arg' % info.max_args
548 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600549 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600550 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600551 else:
552 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600553 self.out('\t%s%s' % (tab_to(2, ptype),
554 conv_name_to_c(prop.name)))
555 if isinstance(prop.value, list):
556 self.out('[%d]' % len(prop.value))
557 self.out(';\n')
558 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600559
Simon Glassabf0c802020-12-23 08:11:20 -0700560 def _output_list(self, node, prop):
561 """Output the C code for a devicetree property that holds a list
562
563 Args:
564 node (fdt.Node): Node to output
565 prop (fdt.Prop): Prop to output
566 """
567 self.buf('{')
568 vals = []
569 # For phandles, output a reference to the platform data
570 # of the target node.
571 info = self.get_phandle_argc(prop, node.name)
572 if info:
573 # Process the list as pairs of (phandle, id)
574 pos = 0
575 for args in info.args:
576 phandle_cell = prop.value[pos]
577 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
578 target_node = self._fdt.phandle_to_node[phandle]
579 arg_values = []
580 for i in range(args):
581 arg_values.append(
582 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
583 pos += 1 + args
584 vals.append('\t{%d, {%s}}' % (target_node.idx,
585 ', '.join(arg_values)))
586 for val in vals:
587 self.buf('\n\t\t%s,' % val)
588 else:
589 for val in prop.value:
590 vals.append(get_value(prop.type, val))
591
592 # Put 8 values per line to avoid very long lines.
593 for i in range(0, len(vals), 8):
594 if i:
595 self.buf(',\n\t\t')
596 self.buf(', '.join(vals[i:i + 8]))
597 self.buf('}')
598
Simon Glasse525fea2021-02-03 06:00:59 -0700599 def _declare_device(self, node):
Simon Glass221ddc12020-12-23 08:11:21 -0700600 """Add a device declaration to the output
601
Simon Glass20e442a2020-12-28 20:34:54 -0700602 This declares a U_BOOT_DRVINFO() for the device being processed
Simon Glass221ddc12020-12-23 08:11:21 -0700603
604 Args:
Simon Glasse525fea2021-02-03 06:00:59 -0700605 node: Node to process
Simon Glass221ddc12020-12-23 08:11:21 -0700606 """
Simon Glasse525fea2021-02-03 06:00:59 -0700607 self.buf('U_BOOT_DRVINFO(%s) = {\n' % node.var_name)
608 self.buf('\t.name\t\t= "%s",\n' % node.struct_name)
609 self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
610 self.buf('\t.plat_size\t= sizeof(%s%s),\n' %
611 (VAL_PREFIX, node.var_name))
Simon Glass221ddc12020-12-23 08:11:21 -0700612 idx = -1
Simon Glasse525fea2021-02-03 06:00:59 -0700613 if node.parent and node.parent in self._valid_nodes:
614 idx = node.parent.idx
Simon Glass221ddc12020-12-23 08:11:21 -0700615 self.buf('\t.parent_idx\t= %d,\n' % idx)
616 self.buf('};\n')
617 self.buf('\n')
618
Simon Glass161dac12020-12-23 08:11:22 -0700619 def _output_prop(self, node, prop):
620 """Output a line containing the value of a struct member
621
622 Args:
623 node (Node): Node being output
624 prop (Prop): Prop object to output
625 """
626 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
627 return
628 member_name = conv_name_to_c(prop.name)
629 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
630
631 # Special handling for lists
632 if isinstance(prop.value, list):
633 self._output_list(node, prop)
634 else:
635 self.buf(get_value(prop.type, prop.value))
636 self.buf(',\n')
637
Simon Glasse525fea2021-02-03 06:00:59 -0700638 def _output_values(self, node):
Simon Glass161dac12020-12-23 08:11:22 -0700639 """Output the definition of a device's struct values
640
641 Args:
Simon Glasse525fea2021-02-03 06:00:59 -0700642 node (Node): Node to output
Simon Glass161dac12020-12-23 08:11:22 -0700643 """
644 self.buf('static struct %s%s %s%s = {\n' %
Simon Glasse525fea2021-02-03 06:00:59 -0700645 (STRUCT_PREFIX, node.struct_name, VAL_PREFIX, node.var_name))
Simon Glass161dac12020-12-23 08:11:22 -0700646 for pname in sorted(node.props):
647 self._output_prop(node, node.props[pname])
648 self.buf('};\n')
649
Simon Glassfd471e22021-02-03 06:01:00 -0700650 def process_nodes(self, need_drivers):
651 nodes_to_output = list(self._valid_nodes)
652
653 for node in nodes_to_output:
654 node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name
655 driver = self._scan.get_driver(node.struct_name)
656 if not driver:
657 if not need_drivers:
658 continue
659 raise ValueError("Cannot parse/find driver for '%s'" %
660 node.struct_name)
661 node.driver = driver
662 parent_driver = None
663 if node.parent in self._valid_nodes:
664 parent_driver = self._scan.get_driver(node.parent.struct_name)
665 if not parent_driver:
666 if not need_drivers:
667 continue
668 raise ValueError(
669 "Cannot parse/find parent driver '%s' for '%s'" %
670 (node.parent.struct_name, node.struct_name))
671 node.parent_seq = len(node.parent.child_devs)
672 node.parent.child_devs.append(node)
673 node.parent.child_refs[node.parent_seq] = \
674 '&%s->sibling_node' % node.dev_ref
675 node.parent_driver = parent_driver
676
677 for node in nodes_to_output:
678 ref = '&%s->child_head' % node.dev_ref
679 node.child_refs[-1] = ref
680 node.child_refs[len(node.child_devs)] = ref
681
Simon Glass2be282c2017-06-18 22:08:59 -0600682 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600683 """Output the C code for a node
684
685 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700686 node (fdt.Node): node to output
Simon Glass7581c012017-06-18 22:08:58 -0600687 """
Simon Glass1b272732020-10-03 11:31:25 -0600688 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Simon Glass7581c012017-06-18 22:08:58 -0600689
Simon Glasse525fea2021-02-03 06:00:59 -0700690 self._output_values(node)
691 self._declare_device(node)
Simon Glass7581c012017-06-18 22:08:58 -0600692
Simon Glass2be282c2017-06-18 22:08:59 -0600693 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600694
Simon Glassa7d5f962020-12-28 20:35:02 -0700695 def generate_plat(self):
Simon Glass7581c012017-06-18 22:08:58 -0600696 """Generate device defintions for the platform data
697
698 This writes out C platform data initialisation data and
Simon Glass20e442a2020-12-28 20:34:54 -0700699 U_BOOT_DRVINFO() declarations for each valid node. Where a node has
Simon Glass7581c012017-06-18 22:08:58 -0600700 multiple compatible strings, a #define is used to make them equivalent.
701
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100702 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600703 information.
704 """
Simon Glass20e442a2020-12-28 20:34:54 -0700705 self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
Simon Glassf31fa992020-12-28 20:35:01 -0700706 self.out('#define DT_PLAT_C\n')
Simon Glasscb43ac12020-10-03 11:31:41 -0600707 self.out('\n')
Simon Glass2be282c2017-06-18 22:08:59 -0600708 self.out('#include <common.h>\n')
709 self.out('#include <dm.h>\n')
710 self.out('#include <dt-structs.h>\n')
711 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600712
Simon Glass9eca08d2020-12-28 20:35:04 -0700713 for node in self._valid_nodes:
Simon Glass2be282c2017-06-18 22:08:59 -0600714 self.output_node(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600715
Walter Lozano51f12632020-06-25 01:10:13 -0300716 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600717
Simon Glass192c1112020-12-28 20:34:50 -0700718
Simon Glassbe44f272020-12-28 20:34:51 -0700719# Types of output file we understand
720# key: Command used to generate this file
721# value: OutputFile for this command
722OUTPUT_FILES = {
Simon Glassd1055d62020-12-28 20:35:00 -0700723 'struct':
724 OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
Simon Glassa7d5f962020-12-28 20:35:02 -0700725 DtbPlatdata.generate_structs,
Simon Glassd1055d62020-12-28 20:35:00 -0700726 'Defines the structs used to hold devicetree data'),
727 'platdata':
Simon Glassa7d5f962020-12-28 20:35:02 -0700728 OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
Simon Glassd1055d62020-12-28 20:35:00 -0700729 'Declares the U_BOOT_DRIVER() records and platform data'),
Simon Glassbe44f272020-12-28 20:34:51 -0700730 }
731
732
Simon Glass192c1112020-12-28 20:34:50 -0700733def run_steps(args, dtb_file, include_disabled, output, output_dirs,
Simon Glassa32eb7d2021-02-03 06:00:51 -0700734 warning_disabled=False, drivers_additional=None, basedir=None,
735 scan=None):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600736 """Run all the steps of the dtoc tool
737
738 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700739 args (list): List of non-option arguments provided to the problem
740 dtb_file (str): Filename of dtb file to process
741 include_disabled (bool): True to include disabled nodes
Simon Glassf62cea02020-12-28 20:34:48 -0700742 output (str): Name of output file (None for stdout)
Simon Glass192c1112020-12-28 20:34:50 -0700743 output_dirs (tuple of str):
744 Directory to put C output files
745 Directory to put H output files
Simon Glass78128d52020-12-03 16:55:16 -0700746 warning_disabled (bool): True to avoid showing warnings about missing
747 drivers
Simon Glassccc3da72020-12-23 08:11:19 -0700748 drivers_additional (list): List of additional drivers to use during
Simon Glass78128d52020-12-03 16:55:16 -0700749 scanning
Simon Glass1e0f3f42020-12-28 20:35:03 -0700750 basedir (str): Base directory of U-Boot source code. Defaults to the
751 grandparent of this file's directory
Simon Glassa32eb7d2021-02-03 06:00:51 -0700752 scan (src_src.Scanner): Scanner from a previous run. This can help speed
753 up tests. Use None for normal operation
754
Simon Glass9b330382020-11-08 20:36:21 -0700755 Raises:
756 ValueError: if args has no command, or an unknown command
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600757 """
758 if not args:
Simon Glassbe44f272020-12-28 20:34:51 -0700759 raise ValueError('Please specify a command: struct, platdata, all')
760 if output and output_dirs and any(output_dirs):
761 raise ValueError('Must specify either output or output_dirs, not both')
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600762
Simon Glassa32eb7d2021-02-03 06:00:51 -0700763 if not scan:
764 scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional)
765 scan.scan_drivers()
Simon Glassfd471e22021-02-03 06:01:00 -0700766 do_process = True
767 else:
768 do_process = False
Simon Glassa542a702020-12-28 20:35:06 -0700769 plat = DtbPlatdata(scan, dtb_file, include_disabled)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600770 plat.scan_dtb()
771 plat.scan_tree()
Simon Glass51d5d052021-02-03 06:00:58 -0700772 plat.prepare_nodes()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600773 plat.scan_reg_sizes()
Simon Glassbe44f272020-12-28 20:34:51 -0700774 plat.setup_output_dirs(output_dirs)
Simon Glassa7d5f962020-12-28 20:35:02 -0700775 plat.scan_structs()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600776 plat.scan_phandles()
Simon Glassfd471e22021-02-03 06:01:00 -0700777 if do_process:
778 plat.process_nodes(False)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600779
Simon Glass10cbd3b2020-12-28 20:34:52 -0700780 cmds = args[0].split(',')
781 if 'all' in cmds:
782 cmds = sorted(OUTPUT_FILES.keys())
783 for cmd in cmds:
Simon Glassbe44f272020-12-28 20:34:51 -0700784 outfile = OUTPUT_FILES.get(cmd)
785 if not outfile:
786 raise ValueError("Unknown command '%s': (use: %s)" %
Simon Glass10cbd3b2020-12-28 20:34:52 -0700787 (cmd, ', '.join(sorted(OUTPUT_FILES.keys()))))
Simon Glassbe44f272020-12-28 20:34:51 -0700788 plat.setup_output(outfile.ftype,
789 outfile.fname if output_dirs else output)
Simon Glassd1055d62020-12-28 20:35:00 -0700790 plat.out_header(outfile)
Simon Glassa7d5f962020-12-28 20:35:02 -0700791 outfile.method(plat)
Simon Glassbe44f272020-12-28 20:34:51 -0700792 plat.finish_output()