blob: 9e99c63ae70a304ddcaacd985701bac561f0817b [file] [log] [blame]
Simon Glass7581c012017-06-18 22:08:58 -06001#!/usr/bin/python
Tom Rini83d290c2018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Simon Glass7581c012017-06-18 22:08:58 -06003#
4# Copyright (C) 2017 Google, Inc
5# Written by Simon Glass <sjg@chromium.org>
6#
Simon Glass7581c012017-06-18 22:08:58 -06007
Simon Glass2be282c2017-06-18 22:08:59 -06008"""Device tree to platform data class
9
10This supports converting device tree data to C structures definitions and
11static data.
Simon Glass9b330382020-11-08 20:36:21 -070012
13See doc/driver-model/of-plat.rst for more informaiton
Simon Glass2be282c2017-06-18 22:08:59 -060014"""
15
Simon Glass8fed2eb2017-08-29 14:15:55 -060016import collections
Simon Glass7581c012017-06-18 22:08:58 -060017import copy
Simon Glassbe44f272020-12-28 20:34:51 -070018from enum import IntEnum
Walter Lozanodac82282020-07-03 08:07:17 -030019import os
20import re
Simon Glass2be282c2017-06-18 22:08:59 -060021import sys
Simon Glass7581c012017-06-18 22:08:58 -060022
Simon Glassbf776672020-04-17 18:09:04 -060023from dtoc import fdt
24from dtoc import fdt_util
Simon Glassa542a702020-12-28 20:35:06 -070025from dtoc import src_scan
26from dtoc.src_scan import conv_name_to_c
Simon Glass7581c012017-06-18 22:08:58 -060027
Simon Glass9b330382020-11-08 20:36:21 -070028# When we see these properties we ignore them - i.e. do not create a structure
29# member
Simon Glass7581c012017-06-18 22:08:58 -060030PROP_IGNORE_LIST = [
31 '#address-cells',
32 '#gpio-cells',
33 '#size-cells',
34 'compatible',
35 'linux,phandle',
36 "status",
37 'phandle',
38 'u-boot,dm-pre-reloc',
39 'u-boot,dm-tpl',
40 'u-boot,dm-spl',
41]
42
Simon Glass5ea9dcc2020-11-08 20:36:17 -070043# C type declarations for the types we support
Simon Glass7581c012017-06-18 22:08:58 -060044TYPE_NAMES = {
Simon Glass5ea9dcc2020-11-08 20:36:17 -070045 fdt.Type.INT: 'fdt32_t',
46 fdt.Type.BYTE: 'unsigned char',
47 fdt.Type.STRING: 'const char *',
48 fdt.Type.BOOL: 'bool',
49 fdt.Type.INT64: 'fdt64_t',
Simon Glass2be282c2017-06-18 22:08:59 -060050}
Simon Glass7581c012017-06-18 22:08:58 -060051
52STRUCT_PREFIX = 'dtd_'
53VAL_PREFIX = 'dtv_'
54
Simon Glassbe44f272020-12-28 20:34:51 -070055class Ftype(IntEnum):
56 SOURCE, HEADER = range(2)
57
58
59# This holds information about each type of output file dtoc can create
60# type: Type of file (Ftype)
Simon Glassd1055d62020-12-28 20:35:00 -070061# fname: Filename excluding directory, e.g. 'dt-plat.c'
62# hdr_comment: Comment explaining the purpose of the file
63OutputFile = collections.namedtuple('OutputFile',
Simon Glassa7d5f962020-12-28 20:35:02 -070064 ['ftype', 'fname', 'method', 'hdr_comment'])
Simon Glassbe44f272020-12-28 20:34:51 -070065
Simon Glass8fed2eb2017-08-29 14:15:55 -060066# This holds information about a property which includes phandles.
67#
68# max_args: integer: Maximum number or arguments that any phandle uses (int).
69# args: Number of args for each phandle in the property. The total number of
70# phandles is len(args). This is a list of integers.
71PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
72
Simon Glass97136eb2020-10-03 09:25:19 -060073# Holds a single phandle link, allowing a C struct value to be assigned to point
74# to a device
75#
76# var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node')
77# dev_name: Name of device to assign to (e.g. 'clock')
78PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name'])
79
Simon Glass8fed2eb2017-08-29 14:15:55 -060080
Simon Glass2be282c2017-06-18 22:08:59 -060081def tab_to(num_tabs, line):
82 """Append tabs to a line of text to reach a tab stop.
Simon Glass7581c012017-06-18 22:08:58 -060083
Simon Glass2be282c2017-06-18 22:08:59 -060084 Args:
Simon Glass9b330382020-11-08 20:36:21 -070085 num_tabs (int): Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
86 line (str): Line of text to append to
Simon Glass2be282c2017-06-18 22:08:59 -060087
88 Returns:
Simon Glass9b330382020-11-08 20:36:21 -070089 str: line with the correct number of tabs appeneded. If the line already
Simon Glass2be282c2017-06-18 22:08:59 -060090 extends past that tab stop then a single space is appended.
91 """
92 if len(line) >= num_tabs * 8:
93 return line + ' '
94 return line + '\t' * (num_tabs - len(line) // 8)
95
Simon Glass56e0bbe2017-06-18 22:09:02 -060096def get_value(ftype, value):
97 """Get a value as a C expression
98
99 For integers this returns a byte-swapped (little-endian) hex string
100 For bytes this returns a hex string, e.g. 0x12
101 For strings this returns a literal string enclosed in quotes
102 For booleans this return 'true'
103
104 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700105 ftype (fdt.Type): Data type (fdt_util)
106 value (bytes): Data value, as a string of bytes
107
108 Returns:
109 str: String representation of the value
Simon Glass56e0bbe2017-06-18 22:09:02 -0600110 """
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700111 if ftype == fdt.Type.INT:
Simon Glassccc3da72020-12-23 08:11:19 -0700112 val = '%#x' % fdt_util.fdt32_to_cpu(value)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700113 elif ftype == fdt.Type.BYTE:
Simon Glass78128d52020-12-03 16:55:16 -0700114 char = value[0]
Simon Glassccc3da72020-12-23 08:11:19 -0700115 val = '%#x' % (ord(char) if isinstance(char, str) else char)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700116 elif ftype == fdt.Type.STRING:
Simon Glassf02d0eb2020-07-07 21:32:06 -0600117 # Handle evil ACPI backslashes by adding another backslash before them.
118 # So "\\_SB.GPO0" in the device tree effectively stays like that in C
Simon Glassccc3da72020-12-23 08:11:19 -0700119 val = '"%s"' % value.replace('\\', '\\\\')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700120 elif ftype == fdt.Type.BOOL:
Simon Glassccc3da72020-12-23 08:11:19 -0700121 val = 'true'
Simon Glass9b330382020-11-08 20:36:21 -0700122 else: # ftype == fdt.Type.INT64:
Simon Glassccc3da72020-12-23 08:11:19 -0700123 val = '%#x' % value
124 return val
Simon Glass56e0bbe2017-06-18 22:09:02 -0600125
Simon Glass56e0bbe2017-06-18 22:09:02 -0600126
Simon Glassccc3da72020-12-23 08:11:19 -0700127class DtbPlatdata():
Simon Glass7581c012017-06-18 22:08:58 -0600128 """Provide a means to convert device tree binary data to platform data
129
130 The output of this process is C structures which can be used in space-
131 constrained encvironments where the ~3KB code overhead of device tree
132 code is not affordable.
133
134 Properties:
Simon Glassa542a702020-12-28 20:35:06 -0700135 _scan: Scan object, for scanning and reporting on useful information
136 from the U-Boot source code
Simon Glass2be282c2017-06-18 22:08:59 -0600137 _fdt: Fdt object, referencing the device tree
Simon Glass7581c012017-06-18 22:08:58 -0600138 _dtb_fname: Filename of the input device tree binary file
Simon Glass074197a2021-02-03 06:01:09 -0700139 _valid_nodes_unsorted: A list of Node object with compatible strings,
140 ordered by devicetree node order
141 _valid_nodes: A list of Node object with compatible strings, ordered by
142 conv_name_to_c(node.name)
Simon Glasse36024b2017-06-18 22:09:01 -0600143 _include_disabled: true to include nodes marked status = "disabled"
Simon Glass7581c012017-06-18 22:08:58 -0600144 _outfile: The current output file (sys.stdout or a real file)
145 _lines: Stashed list of output lines for outputting in the future
Simon Glassbe44f272020-12-28 20:34:51 -0700146 _dirname: Directory to hold output files, or None for none (all files
147 go to stdout)
Simon Glassa7d5f962020-12-28 20:35:02 -0700148 _struct_data (dict): OrderedDict of dtplat structures to output
149 key (str): Node name, as a C identifier
150 value: dict containing structure fields:
151 key (str): Field name
152 value: Prop object with field information
Simon Glass1e0f3f42020-12-28 20:35:03 -0700153 _basedir (str): Base directory of source tree
Simon Glass7581c012017-06-18 22:08:58 -0600154 """
Simon Glassa542a702020-12-28 20:35:06 -0700155 def __init__(self, scan, dtb_fname, include_disabled):
156 self._scan = scan
Simon Glass2be282c2017-06-18 22:08:59 -0600157 self._fdt = None
Simon Glass7581c012017-06-18 22:08:58 -0600158 self._dtb_fname = dtb_fname
159 self._valid_nodes = None
Simon Glass074197a2021-02-03 06:01:09 -0700160 self._valid_nodes_unsorted = None
Simon Glasse36024b2017-06-18 22:09:01 -0600161 self._include_disabled = include_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600162 self._outfile = None
163 self._lines = []
Simon Glassbe44f272020-12-28 20:34:51 -0700164 self._dirnames = [None] * len(Ftype)
Simon Glassa7d5f962020-12-28 20:35:02 -0700165 self._struct_data = collections.OrderedDict()
Simon Glass1e0f3f42020-12-28 20:35:03 -0700166 self._basedir = None
Walter Lozanodac82282020-07-03 08:07:17 -0300167
Simon Glassbe44f272020-12-28 20:34:51 -0700168 def setup_output_dirs(self, output_dirs):
169 """Set up the output directories
170
171 This should be done before setup_output() is called
172
173 Args:
174 output_dirs (tuple of str):
175 Directory to use for C output files.
176 Use None to write files relative current directory
177 Directory to use for H output files.
178 Defaults to the C output dir
179 """
180 def process_dir(ftype, dirname):
181 if dirname:
182 os.makedirs(dirname, exist_ok=True)
183 self._dirnames[ftype] = dirname
184
185 if output_dirs:
186 c_dirname = output_dirs[0]
187 h_dirname = output_dirs[1] if len(output_dirs) > 1 else c_dirname
188 process_dir(Ftype.SOURCE, c_dirname)
189 process_dir(Ftype.HEADER, h_dirname)
190
191 def setup_output(self, ftype, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600192 """Set up the output destination
193
Simon Glass2be282c2017-06-18 22:08:59 -0600194 Once this is done, future calls to self.out() will output to this
Simon Glassbe44f272020-12-28 20:34:51 -0700195 file. The file used is as follows:
196
197 self._dirnames[ftype] is None: output to fname, or stdout if None
198 self._dirnames[ftype] is not None: output to fname in that directory
199
200 Calling this function multiple times will close the old file and open
201 the new one. If they are the same file, nothing happens and output will
202 continue to the same file.
Simon Glass7581c012017-06-18 22:08:58 -0600203
204 Args:
Simon Glassbe44f272020-12-28 20:34:51 -0700205 ftype (str): Type of file to create ('c' or 'h')
206 fname (str): Filename to send output to. If there is a directory in
207 self._dirnames for this file type, it will be put in that
208 directory
Simon Glass7581c012017-06-18 22:08:58 -0600209 """
Simon Glassbe44f272020-12-28 20:34:51 -0700210 dirname = self._dirnames[ftype]
211 if dirname:
212 pathname = os.path.join(dirname, fname)
213 if self._outfile:
214 self._outfile.close()
215 self._outfile = open(pathname, 'w')
216 elif fname:
217 if not self._outfile:
218 self._outfile = open(fname, 'w')
Simon Glassf62cea02020-12-28 20:34:48 -0700219 else:
220 self._outfile = sys.stdout
Simon Glass7581c012017-06-18 22:08:58 -0600221
Simon Glassbe44f272020-12-28 20:34:51 -0700222 def finish_output(self):
223 """Finish outputing to a file
224
225 This closes the output file, if one is in use
226 """
227 if self._outfile != sys.stdout:
228 self._outfile.close()
229
Simon Glass2be282c2017-06-18 22:08:59 -0600230 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600231 """Output a string to the output file
232
233 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700234 line (str): String to output
Simon Glass7581c012017-06-18 22:08:58 -0600235 """
Simon Glass2be282c2017-06-18 22:08:59 -0600236 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600237
Simon Glass2be282c2017-06-18 22:08:59 -0600238 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600239 """Buffer up a string to send later
240
241 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700242 line (str): String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600243 """
Simon Glass2be282c2017-06-18 22:08:59 -0600244 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600245
Simon Glass2be282c2017-06-18 22:08:59 -0600246 def get_buf(self):
Simon Glass7581c012017-06-18 22:08:58 -0600247 """Get the contents of the output buffer, and clear it
248
249 Returns:
Simon Glass9b330382020-11-08 20:36:21 -0700250 list(str): The output buffer, which is then cleared for future use
Simon Glass7581c012017-06-18 22:08:58 -0600251 """
252 lines = self._lines
253 self._lines = []
254 return lines
255
Simon Glassd1055d62020-12-28 20:35:00 -0700256 def out_header(self, outfile):
257 """Output a message indicating that this is an auto-generated file
258
259 Args:
260 outfile: OutputFile describing the file being generated
261 """
Simon Glassd5031142017-08-29 14:16:01 -0600262 self.out('''/*
263 * DO NOT MODIFY
264 *
Simon Glassd1055d62020-12-28 20:35:00 -0700265 * %s.
266 * This was generated by dtoc from a .dtb (device tree binary) file.
Simon Glassd5031142017-08-29 14:16:01 -0600267 */
268
Simon Glassd1055d62020-12-28 20:35:00 -0700269''' % outfile.hdr_comment)
Simon Glassd5031142017-08-29 14:16:01 -0600270
Simon Glass8fed2eb2017-08-29 14:15:55 -0600271 def get_phandle_argc(self, prop, node_name):
272 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600273
Simon Glass8fed2eb2017-08-29 14:15:55 -0600274 We have no reliable way of detecting whether a node uses a phandle
275 or not. As an interim measure, use a list of known property names.
Simon Glass2925c262017-08-29 14:15:54 -0600276
Simon Glass8fed2eb2017-08-29 14:15:55 -0600277 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700278 prop (fdt.Prop): Prop object to check
279 node_name (str): Node name, only used for raising an error
280 Returns:
281 int or None: Number of argument cells is this is a phandle,
282 else None
283 Raises:
284 ValueError: if the phandle cannot be parsed or the required property
285 is not present
Simon Glass8fed2eb2017-08-29 14:15:55 -0600286 """
Walter Lozanoad340172020-06-25 01:10:16 -0300287 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass760b7172018-07-06 10:27:31 -0600288 if not isinstance(prop.value, list):
289 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600290 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600291 i = 0
292
293 max_args = 0
294 args = []
295 while i < len(val):
296 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-07-06 10:27:31 -0600297 # If we get to the end of the list, stop. This can happen
298 # since some nodes have more phandles in the list than others,
299 # but we allocate enough space for the largest list. So those
300 # nodes with shorter lists end up with zeroes at the end.
301 if not phandle:
302 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600303 target = self._fdt.phandle_to_node.get(phandle)
304 if not target:
305 raise ValueError("Cannot parse '%s' in node '%s'" %
306 (prop.name, node_name))
Walter Lozanoad340172020-06-25 01:10:16 -0300307 cells = None
308 for prop_name in ['#clock-cells', '#gpio-cells']:
309 cells = target.props.get(prop_name)
310 if cells:
311 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600312 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300313 raise ValueError("Node '%s' has no cells property" %
Simon Glass9b330382020-11-08 20:36:21 -0700314 (target.name))
Simon Glass8fed2eb2017-08-29 14:15:55 -0600315 num_args = fdt_util.fdt32_to_cpu(cells.value)
316 max_args = max(max_args, num_args)
317 args.append(num_args)
318 i += 1 + num_args
319 return PhandleInfo(max_args, args)
320 return None
Simon Glass2925c262017-08-29 14:15:54 -0600321
Simon Glass2be282c2017-06-18 22:08:59 -0600322 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200323 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600324
Simon Glass2be282c2017-06-18 22:08:59 -0600325 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600326 device tree root node, and progress from there.
327 """
Simon Glass2be282c2017-06-18 22:08:59 -0600328 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600329
Simon Glass074197a2021-02-03 06:01:09 -0700330 def scan_node(self, node, valid_nodes):
Simon Glass2be282c2017-06-18 22:08:59 -0600331 """Scan a node and subnodes to build a tree of node and phandle info
332
Simon Glass074197a2021-02-03 06:01:09 -0700333 This adds each subnode to self._valid_nodes if it is enabled and has a
334 compatible string.
Simon Glass2be282c2017-06-18 22:08:59 -0600335
336 Args:
Simon Glass074197a2021-02-03 06:01:09 -0700337 node (Node): Node for scan for subnodes
Simon Glassccc3da72020-12-23 08:11:19 -0700338 valid_nodes (list of Node): List of Node objects to add to
Simon Glass2be282c2017-06-18 22:08:59 -0600339 """
Simon Glass074197a2021-02-03 06:01:09 -0700340 for subnode in node.subnodes:
341 if 'compatible' in subnode.props:
342 status = subnode.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600343 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600344 status.value != 'disabled'):
Simon Glass074197a2021-02-03 06:01:09 -0700345 valid_nodes.append(subnode)
Simon Glass7581c012017-06-18 22:08:58 -0600346
347 # recurse to handle any subnodes
Simon Glass074197a2021-02-03 06:01:09 -0700348 self.scan_node(subnode, valid_nodes)
Simon Glass7581c012017-06-18 22:08:58 -0600349
Simon Glass2be282c2017-06-18 22:08:59 -0600350 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600351 """Scan the device tree for useful information
352
353 This fills in the following properties:
Simon Glass074197a2021-02-03 06:01:09 -0700354 _valid_nodes_unsorted: A list of nodes we wish to consider include
355 in the platform data (in devicetree node order)
356 _valid_nodes: Sorted version of _valid_nodes_unsorted
Simon Glass7581c012017-06-18 22:08:58 -0600357 """
Simon Glass074197a2021-02-03 06:01:09 -0700358 root = self._fdt.GetRoot()
Simon Glass1b272732020-10-03 11:31:25 -0600359 valid_nodes = []
Simon Glass074197a2021-02-03 06:01:09 -0700360 self.scan_node(root, valid_nodes)
361 self._valid_nodes_unsorted = valid_nodes
Simon Glass1b272732020-10-03 11:31:25 -0600362 self._valid_nodes = sorted(valid_nodes,
363 key=lambda x: conv_name_to_c(x.name))
Simon Glass51d5d052021-02-03 06:00:58 -0700364
365 def prepare_nodes(self):
366 """Add extra properties to the nodes we are using
367
368 The following properties are added for use by dtoc:
369 idx: Index number of this node (0=first, etc.)
370 struct_name: Name of the struct dtd used by this node
371 var_name: C name for this node
372 child_devs: List of child devices for this node, each a None
373 child_refs: Dict of references for each child:
374 key: Position in child list (-1=head, 0=first, 1=second, ...
375 n-1=last, n=head)
376 seq: Sequence number of the device (unique within its uclass), or
377 -1 not not known yet
378 dev_ref: Reference to this device, e.g. 'DM_DEVICE_REF(serial)'
379 driver: Driver record for this node, or None if not known
380 uclass: Uclass record for this node, or None if not known
381 uclass_seq: Position of this device within the uclass list (0=first,
382 n-1=last)
383 parent_seq: Position of this device within it siblings (0=first,
384 n-1=last)
385 parent_driver: Driver record of the node's parent, or None if none.
386 We don't use node.parent.driver since node.parent may not be in
387 the list of valid nodes
388 """
Simon Glass1b272732020-10-03 11:31:25 -0600389 for idx, node in enumerate(self._valid_nodes):
390 node.idx = idx
Simon Glass51d5d052021-02-03 06:00:58 -0700391 node.struct_name, _ = self._scan.get_normalized_compat_name(node)
392 node.var_name = conv_name_to_c(node.name)
393 node.child_devs = []
394 node.child_refs = {}
395 node.seq = -1
396 node.dev_ref = None
397 node.driver = None
398 node.uclass = None
399 node.uclass_seq = None
400 node.parent_seq = None
401 node.parent_driver = None
Simon Glass7581c012017-06-18 22:08:58 -0600402
Simon Glassc20ee0e2017-08-29 14:15:50 -0600403 @staticmethod
404 def get_num_cells(node):
405 """Get the number of cells in addresses and sizes for this node
406
407 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700408 node (fdt.None): Node to check
Simon Glassc20ee0e2017-08-29 14:15:50 -0600409
410 Returns:
411 Tuple:
412 Number of address cells for this node
413 Number of size cells for this node
414 """
415 parent = node.parent
Simon Glass78128d52020-12-03 16:55:16 -0700416 num_addr, num_size = 2, 2
Simon Glassc20ee0e2017-08-29 14:15:50 -0600417 if parent:
Simon Glass78128d52020-12-03 16:55:16 -0700418 addr_prop = parent.props.get('#address-cells')
419 size_prop = parent.props.get('#size-cells')
420 if addr_prop:
421 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
422 if size_prop:
423 num_size = fdt_util.fdt32_to_cpu(size_prop.value)
424 return num_addr, num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600425
426 def scan_reg_sizes(self):
427 """Scan for 64-bit 'reg' properties and update the values
428
429 This finds 'reg' properties with 64-bit data and converts the value to
430 an array of 64-values. This allows it to be output in a way that the
431 C code can read.
432 """
433 for node in self._valid_nodes:
434 reg = node.props.get('reg')
435 if not reg:
436 continue
Simon Glass78128d52020-12-03 16:55:16 -0700437 num_addr, num_size = self.get_num_cells(node)
438 total = num_addr + num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600439
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700440 if reg.type != fdt.Type.INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600441 raise ValueError("Node '%s' reg property is not an int" %
442 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600443 if len(reg.value) % total:
Simon Glass9b330382020-11-08 20:36:21 -0700444 raise ValueError(
445 "Node '%s' reg property has %d cells "
446 'which is not a multiple of na + ns = %d + %d)' %
Simon Glass78128d52020-12-03 16:55:16 -0700447 (node.name, len(reg.value), num_addr, num_size))
448 reg.num_addr = num_addr
449 reg.num_size = num_size
450 if num_addr != 1 or num_size != 1:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700451 reg.type = fdt.Type.INT64
Simon Glassc20ee0e2017-08-29 14:15:50 -0600452 i = 0
453 new_value = []
454 val = reg.value
455 if not isinstance(val, list):
456 val = [val]
457 while i < len(val):
Simon Glass78128d52020-12-03 16:55:16 -0700458 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
459 i += num_addr
460 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
461 i += num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600462 new_value += [addr, size]
463 reg.value = new_value
464
Simon Glass2be282c2017-06-18 22:08:59 -0600465 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600466 """Scan the device tree building up the C structures we will use.
467
468 Build a dict keyed by C struct name containing a dict of Prop
469 object for each struct field (keyed by property name). Where the
470 same struct appears multiple times, try to use the 'widest'
471 property, i.e. the one with a type which can express all others.
472
473 Once the widest property is determined, all other properties are
474 updated to match that width.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600475
Simon Glassa7d5f962020-12-28 20:35:02 -0700476 The results are written to self._struct_data
Simon Glass7581c012017-06-18 22:08:58 -0600477 """
Simon Glassa7d5f962020-12-28 20:35:02 -0700478 structs = self._struct_data
Simon Glass7581c012017-06-18 22:08:58 -0600479 for node in self._valid_nodes:
Simon Glass7581c012017-06-18 22:08:58 -0600480 fields = {}
481
482 # Get a list of all the valid properties in this node.
483 for name, prop in node.props.items():
484 if name not in PROP_IGNORE_LIST and name[0] != '#':
485 fields[name] = copy.deepcopy(prop)
486
Simon Glasse525fea2021-02-03 06:00:59 -0700487 # If we've seen this struct_name before, update the existing struct
488 if node.struct_name in structs:
489 struct = structs[node.struct_name]
Simon Glass7581c012017-06-18 22:08:58 -0600490 for name, prop in fields.items():
491 oldprop = struct.get(name)
492 if oldprop:
493 oldprop.Widen(prop)
494 else:
495 struct[name] = prop
496
497 # Otherwise store this as a new struct.
498 else:
Simon Glasse525fea2021-02-03 06:00:59 -0700499 structs[node.struct_name] = fields
Simon Glass7581c012017-06-18 22:08:58 -0600500
Simon Glass7581c012017-06-18 22:08:58 -0600501 for node in self._valid_nodes:
Simon Glasse525fea2021-02-03 06:00:59 -0700502 struct = structs[node.struct_name]
Simon Glass7581c012017-06-18 22:08:58 -0600503 for name, prop in node.props.items():
504 if name not in PROP_IGNORE_LIST and name[0] != '#':
505 prop.Widen(struct[name])
Simon Glass7581c012017-06-18 22:08:58 -0600506
Simon Glass2be282c2017-06-18 22:08:59 -0600507 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600508 """Figure out what phandles each node uses
509
510 We need to be careful when outputing nodes that use phandles since
511 they must come after the declaration of the phandles in the C file.
512 Otherwise we get a compiler error since the phandle struct is not yet
513 declared.
514
515 This function adds to each node a list of phandle nodes that the node
516 depends on. This allows us to output things in the right order.
517 """
518 for node in self._valid_nodes:
519 node.phandles = set()
520 for pname, prop in node.props.items():
521 if pname in PROP_IGNORE_LIST or pname[0] == '#':
522 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600523 info = self.get_phandle_argc(prop, node.name)
524 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600525 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600526 pos = 0
527 for args in info.args:
528 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600529 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
530 target_node = self._fdt.phandle_to_node[phandle]
531 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600532 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600533
534
Simon Glassa7d5f962020-12-28 20:35:02 -0700535 def generate_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600536 """Generate struct defintions for the platform data
537
538 This writes out the body of a header file consisting of structure
539 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100540 doc/driver-model/of-plat.rst for more information.
Simon Glass7581c012017-06-18 22:08:58 -0600541 """
Simon Glassa7d5f962020-12-28 20:35:02 -0700542 structs = self._struct_data
Simon Glass2be282c2017-06-18 22:08:59 -0600543 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900544 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600545
546 # Output the struct definition
547 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600548 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600549 for pname in sorted(structs[name]):
550 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600551 info = self.get_phandle_argc(prop, structs[name])
552 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600553 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600554 struct_name = 'struct phandle_%d_arg' % info.max_args
555 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600556 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600557 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600558 else:
559 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600560 self.out('\t%s%s' % (tab_to(2, ptype),
561 conv_name_to_c(prop.name)))
562 if isinstance(prop.value, list):
563 self.out('[%d]' % len(prop.value))
564 self.out(';\n')
565 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600566
Simon Glassabf0c802020-12-23 08:11:20 -0700567 def _output_list(self, node, prop):
568 """Output the C code for a devicetree property that holds a list
569
570 Args:
571 node (fdt.Node): Node to output
572 prop (fdt.Prop): Prop to output
573 """
574 self.buf('{')
575 vals = []
576 # For phandles, output a reference to the platform data
577 # of the target node.
578 info = self.get_phandle_argc(prop, node.name)
579 if info:
580 # Process the list as pairs of (phandle, id)
581 pos = 0
582 for args in info.args:
583 phandle_cell = prop.value[pos]
584 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
585 target_node = self._fdt.phandle_to_node[phandle]
586 arg_values = []
587 for i in range(args):
588 arg_values.append(
589 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
590 pos += 1 + args
591 vals.append('\t{%d, {%s}}' % (target_node.idx,
592 ', '.join(arg_values)))
593 for val in vals:
594 self.buf('\n\t\t%s,' % val)
595 else:
596 for val in prop.value:
597 vals.append(get_value(prop.type, val))
598
599 # Put 8 values per line to avoid very long lines.
600 for i in range(0, len(vals), 8):
601 if i:
602 self.buf(',\n\t\t')
603 self.buf(', '.join(vals[i:i + 8]))
604 self.buf('}')
605
Simon Glasse525fea2021-02-03 06:00:59 -0700606 def _declare_device(self, node):
Simon Glass221ddc12020-12-23 08:11:21 -0700607 """Add a device declaration to the output
608
Simon Glass20e442a2020-12-28 20:34:54 -0700609 This declares a U_BOOT_DRVINFO() for the device being processed
Simon Glass221ddc12020-12-23 08:11:21 -0700610
611 Args:
Simon Glasse525fea2021-02-03 06:00:59 -0700612 node: Node to process
Simon Glass221ddc12020-12-23 08:11:21 -0700613 """
Simon Glasse525fea2021-02-03 06:00:59 -0700614 self.buf('U_BOOT_DRVINFO(%s) = {\n' % node.var_name)
615 self.buf('\t.name\t\t= "%s",\n' % node.struct_name)
616 self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
617 self.buf('\t.plat_size\t= sizeof(%s%s),\n' %
618 (VAL_PREFIX, node.var_name))
Simon Glass221ddc12020-12-23 08:11:21 -0700619 idx = -1
Simon Glasse525fea2021-02-03 06:00:59 -0700620 if node.parent and node.parent in self._valid_nodes:
621 idx = node.parent.idx
Simon Glass221ddc12020-12-23 08:11:21 -0700622 self.buf('\t.parent_idx\t= %d,\n' % idx)
623 self.buf('};\n')
624 self.buf('\n')
625
Simon Glass161dac12020-12-23 08:11:22 -0700626 def _output_prop(self, node, prop):
627 """Output a line containing the value of a struct member
628
629 Args:
630 node (Node): Node being output
631 prop (Prop): Prop object to output
632 """
633 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
634 return
635 member_name = conv_name_to_c(prop.name)
636 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
637
638 # Special handling for lists
639 if isinstance(prop.value, list):
640 self._output_list(node, prop)
641 else:
642 self.buf(get_value(prop.type, prop.value))
643 self.buf(',\n')
644
Simon Glasse525fea2021-02-03 06:00:59 -0700645 def _output_values(self, node):
Simon Glass161dac12020-12-23 08:11:22 -0700646 """Output the definition of a device's struct values
647
648 Args:
Simon Glasse525fea2021-02-03 06:00:59 -0700649 node (Node): Node to output
Simon Glass161dac12020-12-23 08:11:22 -0700650 """
651 self.buf('static struct %s%s %s%s = {\n' %
Simon Glasse525fea2021-02-03 06:00:59 -0700652 (STRUCT_PREFIX, node.struct_name, VAL_PREFIX, node.var_name))
Simon Glass161dac12020-12-23 08:11:22 -0700653 for pname in sorted(node.props):
654 self._output_prop(node, node.props[pname])
655 self.buf('};\n')
656
Simon Glass05953522021-02-03 06:01:07 -0700657 def read_aliases(self):
658 """Read the aliases and attach the information to self._alias
659
660 Raises:
661 ValueError: The alias path is not found
662 """
663 alias_node = self._fdt.GetNode('/aliases')
664 if not alias_node:
665 return
666 re_num = re.compile('(^[a-z0-9-]+[a-z]+)([0-9]+)$')
667 for prop in alias_node.props.values():
668 m_alias = re_num.match(prop.name)
669 if not m_alias:
670 raise ValueError("Cannot decode alias '%s'" % prop.name)
671 name, num = m_alias.groups()
672 node = self._fdt.GetNode(prop.value)
673 result = self._scan.add_uclass_alias(name, num, node)
674 if result is None:
675 raise ValueError("Alias '%s' path '%s' not found" %
676 (prop.name, prop.value))
677 elif result is False:
678 print("Could not find uclass for alias '%s'" % prop.name)
679
Simon Glass074197a2021-02-03 06:01:09 -0700680 def assign_seq(self):
681 """Assign a sequence number to each node"""
682 for node in self._valid_nodes_unsorted:
683 if node.driver and node.seq == -1 and node.uclass:
684 uclass = node.uclass
685 num = uclass.alias_path_to_num.get(node.path)
686 if num is not None:
687 node.seq = num
688 else:
689 # Dynamically allocate the next available value after all
690 # existing ones
691 for seq in range(1000):
692 if seq not in uclass.alias_num_to_node:
693 break
694 node.seq = seq
695 uclass.alias_path_to_num[node.path] = seq
696 uclass.alias_num_to_node[seq] = node
697
Simon Glassfd471e22021-02-03 06:01:00 -0700698 def process_nodes(self, need_drivers):
699 nodes_to_output = list(self._valid_nodes)
700
Simon Glassb9319c42021-02-03 06:01:01 -0700701 # Figure out which drivers we actually use
702 self._scan.mark_used(nodes_to_output)
703
Simon Glassfd471e22021-02-03 06:01:00 -0700704 for node in nodes_to_output:
705 node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name
706 driver = self._scan.get_driver(node.struct_name)
707 if not driver:
708 if not need_drivers:
709 continue
710 raise ValueError("Cannot parse/find driver for '%s'" %
711 node.struct_name)
712 node.driver = driver
713 parent_driver = None
714 if node.parent in self._valid_nodes:
715 parent_driver = self._scan.get_driver(node.parent.struct_name)
716 if not parent_driver:
717 if not need_drivers:
718 continue
719 raise ValueError(
720 "Cannot parse/find parent driver '%s' for '%s'" %
721 (node.parent.struct_name, node.struct_name))
722 node.parent_seq = len(node.parent.child_devs)
723 node.parent.child_devs.append(node)
724 node.parent.child_refs[node.parent_seq] = \
725 '&%s->sibling_node' % node.dev_ref
726 node.parent_driver = parent_driver
727
728 for node in nodes_to_output:
729 ref = '&%s->child_head' % node.dev_ref
730 node.child_refs[-1] = ref
731 node.child_refs[len(node.child_devs)] = ref
732
Simon Glass2be282c2017-06-18 22:08:59 -0600733 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600734 """Output the C code for a node
735
736 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700737 node (fdt.Node): node to output
Simon Glass7581c012017-06-18 22:08:58 -0600738 """
Simon Glass1b272732020-10-03 11:31:25 -0600739 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Simon Glass7581c012017-06-18 22:08:58 -0600740
Simon Glasse525fea2021-02-03 06:00:59 -0700741 self._output_values(node)
742 self._declare_device(node)
Simon Glass7581c012017-06-18 22:08:58 -0600743
Simon Glass2be282c2017-06-18 22:08:59 -0600744 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600745
Simon Glassa7d5f962020-12-28 20:35:02 -0700746 def generate_plat(self):
Simon Glass7581c012017-06-18 22:08:58 -0600747 """Generate device defintions for the platform data
748
749 This writes out C platform data initialisation data and
Simon Glass20e442a2020-12-28 20:34:54 -0700750 U_BOOT_DRVINFO() declarations for each valid node. Where a node has
Simon Glass7581c012017-06-18 22:08:58 -0600751 multiple compatible strings, a #define is used to make them equivalent.
752
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100753 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600754 information.
755 """
Simon Glass20e442a2020-12-28 20:34:54 -0700756 self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
Simon Glassf31fa992020-12-28 20:35:01 -0700757 self.out('#define DT_PLAT_C\n')
Simon Glasscb43ac12020-10-03 11:31:41 -0600758 self.out('\n')
Simon Glass2be282c2017-06-18 22:08:59 -0600759 self.out('#include <common.h>\n')
760 self.out('#include <dm.h>\n')
761 self.out('#include <dt-structs.h>\n')
762 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600763
Simon Glass9eca08d2020-12-28 20:35:04 -0700764 for node in self._valid_nodes:
Simon Glass2be282c2017-06-18 22:08:59 -0600765 self.output_node(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600766
Walter Lozano51f12632020-06-25 01:10:13 -0300767 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600768
Simon Glass192c1112020-12-28 20:34:50 -0700769
Simon Glassbe44f272020-12-28 20:34:51 -0700770# Types of output file we understand
771# key: Command used to generate this file
772# value: OutputFile for this command
773OUTPUT_FILES = {
Simon Glassd1055d62020-12-28 20:35:00 -0700774 'struct':
775 OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
Simon Glassa7d5f962020-12-28 20:35:02 -0700776 DtbPlatdata.generate_structs,
Simon Glassd1055d62020-12-28 20:35:00 -0700777 'Defines the structs used to hold devicetree data'),
778 'platdata':
Simon Glassa7d5f962020-12-28 20:35:02 -0700779 OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
Simon Glassd1055d62020-12-28 20:35:00 -0700780 'Declares the U_BOOT_DRIVER() records and platform data'),
Simon Glassbe44f272020-12-28 20:34:51 -0700781 }
782
783
Simon Glassb00f0062021-02-03 06:01:02 -0700784def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase,
Simon Glassa32eb7d2021-02-03 06:00:51 -0700785 warning_disabled=False, drivers_additional=None, basedir=None,
786 scan=None):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600787 """Run all the steps of the dtoc tool
788
789 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700790 args (list): List of non-option arguments provided to the problem
791 dtb_file (str): Filename of dtb file to process
792 include_disabled (bool): True to include disabled nodes
Simon Glassf62cea02020-12-28 20:34:48 -0700793 output (str): Name of output file (None for stdout)
Simon Glass192c1112020-12-28 20:34:50 -0700794 output_dirs (tuple of str):
795 Directory to put C output files
796 Directory to put H output files
Simon Glassb00f0062021-02-03 06:01:02 -0700797 phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
798 or 'tpl'. None if not known
Simon Glass78128d52020-12-03 16:55:16 -0700799 warning_disabled (bool): True to avoid showing warnings about missing
800 drivers
Simon Glassccc3da72020-12-23 08:11:19 -0700801 drivers_additional (list): List of additional drivers to use during
Simon Glass78128d52020-12-03 16:55:16 -0700802 scanning
Simon Glass1e0f3f42020-12-28 20:35:03 -0700803 basedir (str): Base directory of U-Boot source code. Defaults to the
804 grandparent of this file's directory
Simon Glassa32eb7d2021-02-03 06:00:51 -0700805 scan (src_src.Scanner): Scanner from a previous run. This can help speed
806 up tests. Use None for normal operation
807
Simon Glass05953522021-02-03 06:01:07 -0700808 Returns:
809 DtbPlatdata object
810
Simon Glass9b330382020-11-08 20:36:21 -0700811 Raises:
812 ValueError: if args has no command, or an unknown command
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600813 """
814 if not args:
Simon Glassbe44f272020-12-28 20:34:51 -0700815 raise ValueError('Please specify a command: struct, platdata, all')
816 if output and output_dirs and any(output_dirs):
817 raise ValueError('Must specify either output or output_dirs, not both')
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600818
Simon Glassa32eb7d2021-02-03 06:00:51 -0700819 if not scan:
Simon Glassb00f0062021-02-03 06:01:02 -0700820 scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional,
821 phase)
Simon Glassa32eb7d2021-02-03 06:00:51 -0700822 scan.scan_drivers()
Simon Glassfd471e22021-02-03 06:01:00 -0700823 do_process = True
824 else:
825 do_process = False
Simon Glassa542a702020-12-28 20:35:06 -0700826 plat = DtbPlatdata(scan, dtb_file, include_disabled)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600827 plat.scan_dtb()
828 plat.scan_tree()
Simon Glass51d5d052021-02-03 06:00:58 -0700829 plat.prepare_nodes()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600830 plat.scan_reg_sizes()
Simon Glassbe44f272020-12-28 20:34:51 -0700831 plat.setup_output_dirs(output_dirs)
Simon Glassa7d5f962020-12-28 20:35:02 -0700832 plat.scan_structs()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600833 plat.scan_phandles()
Simon Glass074197a2021-02-03 06:01:09 -0700834 plat.process_nodes(False)
Simon Glass05953522021-02-03 06:01:07 -0700835 plat.read_aliases()
Simon Glass074197a2021-02-03 06:01:09 -0700836 plat.assign_seq()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600837
Simon Glass10cbd3b2020-12-28 20:34:52 -0700838 cmds = args[0].split(',')
839 if 'all' in cmds:
840 cmds = sorted(OUTPUT_FILES.keys())
841 for cmd in cmds:
Simon Glassbe44f272020-12-28 20:34:51 -0700842 outfile = OUTPUT_FILES.get(cmd)
843 if not outfile:
844 raise ValueError("Unknown command '%s': (use: %s)" %
Simon Glass10cbd3b2020-12-28 20:34:52 -0700845 (cmd, ', '.join(sorted(OUTPUT_FILES.keys()))))
Simon Glassbe44f272020-12-28 20:34:51 -0700846 plat.setup_output(outfile.ftype,
847 outfile.fname if output_dirs else output)
Simon Glassd1055d62020-12-28 20:35:00 -0700848 plat.out_header(outfile)
Simon Glassa7d5f962020-12-28 20:35:02 -0700849 outfile.method(plat)
Simon Glassbe44f272020-12-28 20:34:51 -0700850 plat.finish_output()
Simon Glass05953522021-02-03 06:01:07 -0700851 return plat