blob: 8c36fbc68d23eb0b555a66475688476a73658f86 [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 Glassa542a702020-12-28 20:35:06 -0700473 node_name, _ = self._scan.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600474 fields = {}
475
476 # Get a list of all the valid properties in this node.
477 for name, prop in node.props.items():
478 if name not in PROP_IGNORE_LIST and name[0] != '#':
479 fields[name] = copy.deepcopy(prop)
480
481 # If we've seen this node_name before, update the existing struct.
482 if node_name in structs:
483 struct = structs[node_name]
484 for name, prop in fields.items():
485 oldprop = struct.get(name)
486 if oldprop:
487 oldprop.Widen(prop)
488 else:
489 struct[name] = prop
490
491 # Otherwise store this as a new struct.
492 else:
493 structs[node_name] = fields
494
Simon Glass7581c012017-06-18 22:08:58 -0600495 for node in self._valid_nodes:
Simon Glassa542a702020-12-28 20:35:06 -0700496 node_name, _ = self._scan.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600497 struct = structs[node_name]
498 for name, prop in node.props.items():
499 if name not in PROP_IGNORE_LIST and name[0] != '#':
500 prop.Widen(struct[name])
Simon Glass7581c012017-06-18 22:08:58 -0600501
Simon Glass2be282c2017-06-18 22:08:59 -0600502 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600503 """Figure out what phandles each node uses
504
505 We need to be careful when outputing nodes that use phandles since
506 they must come after the declaration of the phandles in the C file.
507 Otherwise we get a compiler error since the phandle struct is not yet
508 declared.
509
510 This function adds to each node a list of phandle nodes that the node
511 depends on. This allows us to output things in the right order.
512 """
513 for node in self._valid_nodes:
514 node.phandles = set()
515 for pname, prop in node.props.items():
516 if pname in PROP_IGNORE_LIST or pname[0] == '#':
517 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600518 info = self.get_phandle_argc(prop, node.name)
519 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600520 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600521 pos = 0
522 for args in info.args:
523 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600524 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
525 target_node = self._fdt.phandle_to_node[phandle]
526 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600527 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600528
529
Simon Glassa7d5f962020-12-28 20:35:02 -0700530 def generate_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600531 """Generate struct defintions for the platform data
532
533 This writes out the body of a header file consisting of structure
534 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100535 doc/driver-model/of-plat.rst for more information.
Simon Glass7581c012017-06-18 22:08:58 -0600536 """
Simon Glassa7d5f962020-12-28 20:35:02 -0700537 structs = self._struct_data
Simon Glass2be282c2017-06-18 22:08:59 -0600538 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900539 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600540
541 # Output the struct definition
542 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600543 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600544 for pname in sorted(structs[name]):
545 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600546 info = self.get_phandle_argc(prop, structs[name])
547 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600548 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600549 struct_name = 'struct phandle_%d_arg' % info.max_args
550 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600551 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600552 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600553 else:
554 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600555 self.out('\t%s%s' % (tab_to(2, ptype),
556 conv_name_to_c(prop.name)))
557 if isinstance(prop.value, list):
558 self.out('[%d]' % len(prop.value))
559 self.out(';\n')
560 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600561
Simon Glassabf0c802020-12-23 08:11:20 -0700562 def _output_list(self, node, prop):
563 """Output the C code for a devicetree property that holds a list
564
565 Args:
566 node (fdt.Node): Node to output
567 prop (fdt.Prop): Prop to output
568 """
569 self.buf('{')
570 vals = []
571 # For phandles, output a reference to the platform data
572 # of the target node.
573 info = self.get_phandle_argc(prop, node.name)
574 if info:
575 # Process the list as pairs of (phandle, id)
576 pos = 0
577 for args in info.args:
578 phandle_cell = prop.value[pos]
579 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
580 target_node = self._fdt.phandle_to_node[phandle]
581 arg_values = []
582 for i in range(args):
583 arg_values.append(
584 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
585 pos += 1 + args
586 vals.append('\t{%d, {%s}}' % (target_node.idx,
587 ', '.join(arg_values)))
588 for val in vals:
589 self.buf('\n\t\t%s,' % val)
590 else:
591 for val in prop.value:
592 vals.append(get_value(prop.type, val))
593
594 # Put 8 values per line to avoid very long lines.
595 for i in range(0, len(vals), 8):
596 if i:
597 self.buf(',\n\t\t')
598 self.buf(', '.join(vals[i:i + 8]))
599 self.buf('}')
600
Simon Glass221ddc12020-12-23 08:11:21 -0700601 def _declare_device(self, var_name, struct_name, node_parent):
602 """Add a device declaration to the output
603
Simon Glass20e442a2020-12-28 20:34:54 -0700604 This declares a U_BOOT_DRVINFO() for the device being processed
Simon Glass221ddc12020-12-23 08:11:21 -0700605
606 Args:
607 var_name (str): C name for the node
608 struct_name (str): Name for the dt struct associated with the node
609 node_parent (Node): Parent of the node (or None if none)
610 """
Simon Glass20e442a2020-12-28 20:34:54 -0700611 self.buf('U_BOOT_DRVINFO(%s) = {\n' % var_name)
Simon Glass221ddc12020-12-23 08:11:21 -0700612 self.buf('\t.name\t\t= "%s",\n' % struct_name)
613 self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, var_name))
614 self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
615 idx = -1
616 if node_parent and node_parent in self._valid_nodes:
617 idx = node_parent.idx
618 self.buf('\t.parent_idx\t= %d,\n' % idx)
619 self.buf('};\n')
620 self.buf('\n')
621
Simon Glass161dac12020-12-23 08:11:22 -0700622 def _output_prop(self, node, prop):
623 """Output a line containing the value of a struct member
624
625 Args:
626 node (Node): Node being output
627 prop (Prop): Prop object to output
628 """
629 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
630 return
631 member_name = conv_name_to_c(prop.name)
632 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
633
634 # Special handling for lists
635 if isinstance(prop.value, list):
636 self._output_list(node, prop)
637 else:
638 self.buf(get_value(prop.type, prop.value))
639 self.buf(',\n')
640
641 def _output_values(self, var_name, struct_name, node):
642 """Output the definition of a device's struct values
643
644 Args:
645 var_name (str): C name for the node
646 struct_name (str): Name for the dt struct associated with the node
647 node (Node): Node being output
648 """
649 self.buf('static struct %s%s %s%s = {\n' %
650 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
651 for pname in sorted(node.props):
652 self._output_prop(node, node.props[pname])
653 self.buf('};\n')
654
Simon Glass2be282c2017-06-18 22:08:59 -0600655 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600656 """Output the C code for a node
657
658 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700659 node (fdt.Node): node to output
Simon Glass7581c012017-06-18 22:08:58 -0600660 """
Simon Glassa542a702020-12-28 20:35:06 -0700661 struct_name, _ = self._scan.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600662 var_name = conv_name_to_c(node.name)
Simon Glass1b272732020-10-03 11:31:25 -0600663 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Simon Glass7581c012017-06-18 22:08:58 -0600664
Simon Glass161dac12020-12-23 08:11:22 -0700665 self._output_values(var_name, struct_name, node)
Simon Glass221ddc12020-12-23 08:11:21 -0700666 self._declare_device(var_name, struct_name, node.parent)
Simon Glass7581c012017-06-18 22:08:58 -0600667
Simon Glass2be282c2017-06-18 22:08:59 -0600668 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600669
Simon Glassa7d5f962020-12-28 20:35:02 -0700670 def generate_plat(self):
Simon Glass7581c012017-06-18 22:08:58 -0600671 """Generate device defintions for the platform data
672
673 This writes out C platform data initialisation data and
Simon Glass20e442a2020-12-28 20:34:54 -0700674 U_BOOT_DRVINFO() declarations for each valid node. Where a node has
Simon Glass7581c012017-06-18 22:08:58 -0600675 multiple compatible strings, a #define is used to make them equivalent.
676
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100677 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600678 information.
679 """
Simon Glass20e442a2020-12-28 20:34:54 -0700680 self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
Simon Glassf31fa992020-12-28 20:35:01 -0700681 self.out('#define DT_PLAT_C\n')
Simon Glasscb43ac12020-10-03 11:31:41 -0600682 self.out('\n')
Simon Glass2be282c2017-06-18 22:08:59 -0600683 self.out('#include <common.h>\n')
684 self.out('#include <dm.h>\n')
685 self.out('#include <dt-structs.h>\n')
686 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600687
Simon Glass9eca08d2020-12-28 20:35:04 -0700688 for node in self._valid_nodes:
Simon Glass2be282c2017-06-18 22:08:59 -0600689 self.output_node(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600690
Walter Lozano51f12632020-06-25 01:10:13 -0300691 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600692
Simon Glass192c1112020-12-28 20:34:50 -0700693
Simon Glassbe44f272020-12-28 20:34:51 -0700694# Types of output file we understand
695# key: Command used to generate this file
696# value: OutputFile for this command
697OUTPUT_FILES = {
Simon Glassd1055d62020-12-28 20:35:00 -0700698 'struct':
699 OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
Simon Glassa7d5f962020-12-28 20:35:02 -0700700 DtbPlatdata.generate_structs,
Simon Glassd1055d62020-12-28 20:35:00 -0700701 'Defines the structs used to hold devicetree data'),
702 'platdata':
Simon Glassa7d5f962020-12-28 20:35:02 -0700703 OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
Simon Glassd1055d62020-12-28 20:35:00 -0700704 'Declares the U_BOOT_DRIVER() records and platform data'),
Simon Glassbe44f272020-12-28 20:34:51 -0700705 }
706
707
Simon Glass192c1112020-12-28 20:34:50 -0700708def run_steps(args, dtb_file, include_disabled, output, output_dirs,
Simon Glassa32eb7d2021-02-03 06:00:51 -0700709 warning_disabled=False, drivers_additional=None, basedir=None,
710 scan=None):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600711 """Run all the steps of the dtoc tool
712
713 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700714 args (list): List of non-option arguments provided to the problem
715 dtb_file (str): Filename of dtb file to process
716 include_disabled (bool): True to include disabled nodes
Simon Glassf62cea02020-12-28 20:34:48 -0700717 output (str): Name of output file (None for stdout)
Simon Glass192c1112020-12-28 20:34:50 -0700718 output_dirs (tuple of str):
719 Directory to put C output files
720 Directory to put H output files
Simon Glass78128d52020-12-03 16:55:16 -0700721 warning_disabled (bool): True to avoid showing warnings about missing
722 drivers
Simon Glassccc3da72020-12-23 08:11:19 -0700723 drivers_additional (list): List of additional drivers to use during
Simon Glass78128d52020-12-03 16:55:16 -0700724 scanning
Simon Glass1e0f3f42020-12-28 20:35:03 -0700725 basedir (str): Base directory of U-Boot source code. Defaults to the
726 grandparent of this file's directory
Simon Glassa32eb7d2021-02-03 06:00:51 -0700727 scan (src_src.Scanner): Scanner from a previous run. This can help speed
728 up tests. Use None for normal operation
729
Simon Glass9b330382020-11-08 20:36:21 -0700730 Raises:
731 ValueError: if args has no command, or an unknown command
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600732 """
733 if not args:
Simon Glassbe44f272020-12-28 20:34:51 -0700734 raise ValueError('Please specify a command: struct, platdata, all')
735 if output and output_dirs and any(output_dirs):
736 raise ValueError('Must specify either output or output_dirs, not both')
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600737
Simon Glassa32eb7d2021-02-03 06:00:51 -0700738 if not scan:
739 scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional)
740 scan.scan_drivers()
Simon Glassa542a702020-12-28 20:35:06 -0700741 plat = DtbPlatdata(scan, dtb_file, include_disabled)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600742 plat.scan_dtb()
743 plat.scan_tree()
Simon Glass51d5d052021-02-03 06:00:58 -0700744 plat.prepare_nodes()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600745 plat.scan_reg_sizes()
Simon Glassbe44f272020-12-28 20:34:51 -0700746 plat.setup_output_dirs(output_dirs)
Simon Glassa7d5f962020-12-28 20:35:02 -0700747 plat.scan_structs()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600748 plat.scan_phandles()
749
Simon Glass10cbd3b2020-12-28 20:34:52 -0700750 cmds = args[0].split(',')
751 if 'all' in cmds:
752 cmds = sorted(OUTPUT_FILES.keys())
753 for cmd in cmds:
Simon Glassbe44f272020-12-28 20:34:51 -0700754 outfile = OUTPUT_FILES.get(cmd)
755 if not outfile:
756 raise ValueError("Unknown command '%s': (use: %s)" %
Simon Glass10cbd3b2020-12-28 20:34:52 -0700757 (cmd, ', '.join(sorted(OUTPUT_FILES.keys()))))
Simon Glassbe44f272020-12-28 20:34:51 -0700758 plat.setup_output(outfile.ftype,
759 outfile.fname if output_dirs else output)
Simon Glassd1055d62020-12-28 20:35:00 -0700760 plat.out_header(outfile)
Simon Glassa7d5f962020-12-28 20:35:02 -0700761 outfile.method(plat)
Simon Glassbe44f272020-12-28 20:34:51 -0700762 plat.finish_output()