blob: b7abaed67acb2e302fcb51c6eca1490f542e74a4 [file] [log] [blame]
Simon Glassd570dec2017-06-18 22:08:58 -06001#!/usr/bin/python
Tom Rini10e47792018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Simon Glassd570dec2017-06-18 22:08:58 -06003#
4# Copyright (C) 2017 Google, Inc
5# Written by Simon Glass <sjg@chromium.org>
6#
Simon Glassd570dec2017-06-18 22:08:58 -06007
Simon Glass1f730c82017-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 Glass94ee3bd2020-11-08 20:36:21 -070012
13See doc/driver-model/of-plat.rst for more informaiton
Simon Glass1f730c82017-06-18 22:08:59 -060014"""
15
Simon Glassec3b5e42017-08-29 14:15:55 -060016import collections
Simon Glassd570dec2017-06-18 22:08:58 -060017import copy
Simon Glassc3a310a82020-12-28 20:34:51 -070018from enum import IntEnum
Walter Lozanoe675d962020-07-03 08:07:17 -030019import os
20import re
Simon Glass1f730c82017-06-18 22:08:59 -060021import sys
Simon Glassd570dec2017-06-18 22:08:58 -060022
Simon Glassa997ea52020-04-17 18:09:04 -060023from dtoc import fdt
24from dtoc import fdt_util
Simon Glass9065bc92020-12-28 20:35:06 -070025from dtoc import src_scan
26from dtoc.src_scan import conv_name_to_c
Simon Glassd570dec2017-06-18 22:08:58 -060027
Simon Glass94ee3bd2020-11-08 20:36:21 -070028# When we see these properties we ignore them - i.e. do not create a structure
29# member
Simon Glassd570dec2017-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 Glassc9a032c2020-11-08 20:36:17 -070043# C type declarations for the types we support
Simon Glassd570dec2017-06-18 22:08:58 -060044TYPE_NAMES = {
Simon Glassc9a032c2020-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 Glass1f730c82017-06-18 22:08:59 -060050}
Simon Glassd570dec2017-06-18 22:08:58 -060051
52STRUCT_PREFIX = 'dtd_'
53VAL_PREFIX = 'dtv_'
54
Simon Glassc3a310a82020-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 Glass6b208842020-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 Glass55526782020-12-28 20:35:02 -070064 ['ftype', 'fname', 'method', 'hdr_comment'])
Simon Glassc3a310a82020-12-28 20:34:51 -070065
Simon Glassec3b5e42017-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 Glassc1622112020-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 Glassec3b5e42017-08-29 14:15:55 -060080
Simon Glass1f730c82017-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 Glassd570dec2017-06-18 22:08:58 -060083
Simon Glass1f730c82017-06-18 22:08:59 -060084 Args:
Simon Glass94ee3bd2020-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 Glass1f730c82017-06-18 22:08:59 -060087
88 Returns:
Simon Glass94ee3bd2020-11-08 20:36:21 -070089 str: line with the correct number of tabs appeneded. If the line already
Simon Glass1f730c82017-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 Glass7a2add12017-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 Glass94ee3bd2020-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 Glass7a2add12017-06-18 22:09:02 -0600110 """
Simon Glassc9a032c2020-11-08 20:36:17 -0700111 if ftype == fdt.Type.INT:
Simon Glass27511232020-12-23 08:11:19 -0700112 val = '%#x' % fdt_util.fdt32_to_cpu(value)
Simon Glassc9a032c2020-11-08 20:36:17 -0700113 elif ftype == fdt.Type.BYTE:
Simon Glass93daa012020-12-03 16:55:16 -0700114 char = value[0]
Simon Glass27511232020-12-23 08:11:19 -0700115 val = '%#x' % (ord(char) if isinstance(char, str) else char)
Simon Glassc9a032c2020-11-08 20:36:17 -0700116 elif ftype == fdt.Type.STRING:
Simon Glass7f5e2262020-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 Glass27511232020-12-23 08:11:19 -0700119 val = '"%s"' % value.replace('\\', '\\\\')
Simon Glassc9a032c2020-11-08 20:36:17 -0700120 elif ftype == fdt.Type.BOOL:
Simon Glass27511232020-12-23 08:11:19 -0700121 val = 'true'
Simon Glass94ee3bd2020-11-08 20:36:21 -0700122 else: # ftype == fdt.Type.INT64:
Simon Glass27511232020-12-23 08:11:19 -0700123 val = '%#x' % value
124 return val
Simon Glass7a2add12017-06-18 22:09:02 -0600125
Simon Glass7a2add12017-06-18 22:09:02 -0600126
Simon Glass27511232020-12-23 08:11:19 -0700127class DtbPlatdata():
Simon Glassd570dec2017-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 Glass9065bc92020-12-28 20:35:06 -0700135 _scan: Scan object, for scanning and reporting on useful information
136 from the U-Boot source code
Simon Glass1f730c82017-06-18 22:08:59 -0600137 _fdt: Fdt object, referencing the device tree
Simon Glassd570dec2017-06-18 22:08:58 -0600138 _dtb_fname: Filename of the input device tree binary file
Simon Glass192f8132020-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 Glasseab3f622017-06-18 22:09:01 -0600141 _include_disabled: true to include nodes marked status = "disabled"
Simon Glassd570dec2017-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 Glassc3a310a82020-12-28 20:34:51 -0700144 _dirname: Directory to hold output files, or None for none (all files
145 go to stdout)
Simon Glass55526782020-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 Glass4f2059b2020-12-28 20:35:03 -0700151 _basedir (str): Base directory of source tree
Simon Glassd570dec2017-06-18 22:08:58 -0600152 """
Simon Glass9065bc92020-12-28 20:35:06 -0700153 def __init__(self, scan, dtb_fname, include_disabled):
154 self._scan = scan
Simon Glass1f730c82017-06-18 22:08:59 -0600155 self._fdt = None
Simon Glassd570dec2017-06-18 22:08:58 -0600156 self._dtb_fname = dtb_fname
157 self._valid_nodes = None
Simon Glasseab3f622017-06-18 22:09:01 -0600158 self._include_disabled = include_disabled
Simon Glassd570dec2017-06-18 22:08:58 -0600159 self._outfile = None
160 self._lines = []
Simon Glassc3a310a82020-12-28 20:34:51 -0700161 self._dirnames = [None] * len(Ftype)
Simon Glass55526782020-12-28 20:35:02 -0700162 self._struct_data = collections.OrderedDict()
Simon Glass4f2059b2020-12-28 20:35:03 -0700163 self._basedir = None
Walter Lozanoe675d962020-07-03 08:07:17 -0300164
Simon Glassc3a310a82020-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 Glassd570dec2017-06-18 22:08:58 -0600189 """Set up the output destination
190
Simon Glass1f730c82017-06-18 22:08:59 -0600191 Once this is done, future calls to self.out() will output to this
Simon Glassc3a310a82020-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 Glassd570dec2017-06-18 22:08:58 -0600200
201 Args:
Simon Glassc3a310a82020-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 Glassd570dec2017-06-18 22:08:58 -0600206 """
Simon Glassc3a310a82020-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 Glass6ca0c7a2020-12-28 20:34:48 -0700216 else:
217 self._outfile = sys.stdout
Simon Glassd570dec2017-06-18 22:08:58 -0600218
Simon Glassc3a310a82020-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 Glass1f730c82017-06-18 22:08:59 -0600227 def out(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600228 """Output a string to the output file
229
230 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700231 line (str): String to output
Simon Glassd570dec2017-06-18 22:08:58 -0600232 """
Simon Glass1f730c82017-06-18 22:08:59 -0600233 self._outfile.write(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600234
Simon Glass1f730c82017-06-18 22:08:59 -0600235 def buf(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600236 """Buffer up a string to send later
237
238 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700239 line (str): String to add to our 'buffer' list
Simon Glassd570dec2017-06-18 22:08:58 -0600240 """
Simon Glass1f730c82017-06-18 22:08:59 -0600241 self._lines.append(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600242
Simon Glass1f730c82017-06-18 22:08:59 -0600243 def get_buf(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600244 """Get the contents of the output buffer, and clear it
245
246 Returns:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700247 list(str): The output buffer, which is then cleared for future use
Simon Glassd570dec2017-06-18 22:08:58 -0600248 """
249 lines = self._lines
250 self._lines = []
251 return lines
252
Simon Glass6b208842020-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 Glass68d32c72017-08-29 14:16:01 -0600259 self.out('''/*
260 * DO NOT MODIFY
261 *
Simon Glass6b208842020-12-28 20:35:00 -0700262 * %s.
263 * This was generated by dtoc from a .dtb (device tree binary) file.
Simon Glass68d32c72017-08-29 14:16:01 -0600264 */
265
Simon Glass6b208842020-12-28 20:35:00 -0700266''' % outfile.hdr_comment)
Simon Glass68d32c72017-08-29 14:16:01 -0600267
Simon Glassec3b5e42017-08-29 14:15:55 -0600268 def get_phandle_argc(self, prop, node_name):
269 """Check if a node contains phandles
Simon Glassce5621d2017-08-29 14:15:54 -0600270
Simon Glassec3b5e42017-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.
273
274 Args:
Simon Glass94ee3bd2020-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 Glassec3b5e42017-08-29 14:15:55 -0600283 """
Walter Lozano179f0b62020-06-25 01:10:16 -0300284 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass609e2b12018-07-06 10:27:31 -0600285 if not isinstance(prop.value, list):
286 prop.value = [prop.value]
Simon Glassec3b5e42017-08-29 14:15:55 -0600287 val = prop.value
Simon Glassec3b5e42017-08-29 14:15:55 -0600288 i = 0
Simon Glassce5621d2017-08-29 14:15:54 -0600289
Simon Glassec3b5e42017-08-29 14:15:55 -0600290 max_args = 0
291 args = []
292 while i < len(val):
293 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass609e2b12018-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 Glassec3b5e42017-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 Lozano179f0b62020-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 Glassec3b5e42017-08-29 14:15:55 -0600309 if not cells:
Walter Lozano179f0b62020-06-25 01:10:16 -0300310 raise ValueError("Node '%s' has no cells property" %
Simon Glass94ee3bd2020-11-08 20:36:21 -0700311 (target.name))
Simon Glassec3b5e42017-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 Glassce5621d2017-08-29 14:15:54 -0600318
Simon Glass1f730c82017-06-18 22:08:59 -0600319 def scan_dtb(self):
Anatolij Gustschinda707d42017-08-18 17:58:51 +0200320 """Scan the device tree to obtain a tree of nodes and properties
Simon Glassd570dec2017-06-18 22:08:58 -0600321
Simon Glass1f730c82017-06-18 22:08:59 -0600322 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glassd570dec2017-06-18 22:08:58 -0600323 device tree root node, and progress from there.
324 """
Simon Glass1f730c82017-06-18 22:08:59 -0600325 self._fdt = fdt.FdtScan(self._dtb_fname)
326
Simon Glass192f8132020-10-03 11:31:25 -0600327 def scan_node(self, root, valid_nodes):
Simon Glass1f730c82017-06-18 22:08:59 -0600328 """Scan a node and subnodes to build a tree of node and phandle info
Simon Glassd570dec2017-06-18 22:08:58 -0600329
Simon Glass17ffd622017-08-29 14:15:53 -0600330 This adds each node to self._valid_nodes.
Simon Glass1f730c82017-06-18 22:08:59 -0600331
332 Args:
Simon Glass27511232020-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 Glass1f730c82017-06-18 22:08:59 -0600335 """
Simon Glassd570dec2017-06-18 22:08:58 -0600336 for node in root.subnodes:
337 if 'compatible' in node.props:
338 status = node.props.get('status')
Simon Glasseab3f622017-06-18 22:09:01 -0600339 if (not self._include_disabled and not status or
Simon Glass1f730c82017-06-18 22:08:59 -0600340 status.value != 'disabled'):
Simon Glass192f8132020-10-03 11:31:25 -0600341 valid_nodes.append(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600342
343 # recurse to handle any subnodes
Simon Glass192f8132020-10-03 11:31:25 -0600344 self.scan_node(node, valid_nodes)
Simon Glassd570dec2017-06-18 22:08:58 -0600345
Simon Glass1f730c82017-06-18 22:08:59 -0600346 def scan_tree(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600347 """Scan the device tree for useful information
348
349 This fills in the following properties:
Simon Glassd570dec2017-06-18 22:08:58 -0600350 _valid_nodes: A list of nodes we wish to consider include in the
351 platform data
352 """
Simon Glass192f8132020-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))
357 for idx, node in enumerate(self._valid_nodes):
358 node.idx = idx
Simon Glassd570dec2017-06-18 22:08:58 -0600359
Simon Glass1b1fe412017-08-29 14:15:50 -0600360 @staticmethod
361 def get_num_cells(node):
362 """Get the number of cells in addresses and sizes for this node
363
364 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700365 node (fdt.None): Node to check
Simon Glass1b1fe412017-08-29 14:15:50 -0600366
367 Returns:
368 Tuple:
369 Number of address cells for this node
370 Number of size cells for this node
371 """
372 parent = node.parent
Simon Glass93daa012020-12-03 16:55:16 -0700373 num_addr, num_size = 2, 2
Simon Glass1b1fe412017-08-29 14:15:50 -0600374 if parent:
Simon Glass93daa012020-12-03 16:55:16 -0700375 addr_prop = parent.props.get('#address-cells')
376 size_prop = parent.props.get('#size-cells')
377 if addr_prop:
378 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
379 if size_prop:
380 num_size = fdt_util.fdt32_to_cpu(size_prop.value)
381 return num_addr, num_size
Simon Glass1b1fe412017-08-29 14:15:50 -0600382
383 def scan_reg_sizes(self):
384 """Scan for 64-bit 'reg' properties and update the values
385
386 This finds 'reg' properties with 64-bit data and converts the value to
387 an array of 64-values. This allows it to be output in a way that the
388 C code can read.
389 """
390 for node in self._valid_nodes:
391 reg = node.props.get('reg')
392 if not reg:
393 continue
Simon Glass93daa012020-12-03 16:55:16 -0700394 num_addr, num_size = self.get_num_cells(node)
395 total = num_addr + num_size
Simon Glass1b1fe412017-08-29 14:15:50 -0600396
Simon Glassc9a032c2020-11-08 20:36:17 -0700397 if reg.type != fdt.Type.INT:
Simon Glassc38fee042018-07-06 10:27:32 -0600398 raise ValueError("Node '%s' reg property is not an int" %
399 node.name)
Simon Glass1b1fe412017-08-29 14:15:50 -0600400 if len(reg.value) % total:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700401 raise ValueError(
402 "Node '%s' reg property has %d cells "
403 'which is not a multiple of na + ns = %d + %d)' %
Simon Glass93daa012020-12-03 16:55:16 -0700404 (node.name, len(reg.value), num_addr, num_size))
405 reg.num_addr = num_addr
406 reg.num_size = num_size
407 if num_addr != 1 or num_size != 1:
Simon Glassc9a032c2020-11-08 20:36:17 -0700408 reg.type = fdt.Type.INT64
Simon Glass1b1fe412017-08-29 14:15:50 -0600409 i = 0
410 new_value = []
411 val = reg.value
412 if not isinstance(val, list):
413 val = [val]
414 while i < len(val):
Simon Glass93daa012020-12-03 16:55:16 -0700415 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
416 i += num_addr
417 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
418 i += num_size
Simon Glass1b1fe412017-08-29 14:15:50 -0600419 new_value += [addr, size]
420 reg.value = new_value
421
Simon Glass1f730c82017-06-18 22:08:59 -0600422 def scan_structs(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600423 """Scan the device tree building up the C structures we will use.
424
425 Build a dict keyed by C struct name containing a dict of Prop
426 object for each struct field (keyed by property name). Where the
427 same struct appears multiple times, try to use the 'widest'
428 property, i.e. the one with a type which can express all others.
429
430 Once the widest property is determined, all other properties are
431 updated to match that width.
Simon Glass941f8f02020-10-03 11:31:24 -0600432
Simon Glass55526782020-12-28 20:35:02 -0700433 The results are written to self._struct_data
Simon Glassd570dec2017-06-18 22:08:58 -0600434 """
Simon Glass55526782020-12-28 20:35:02 -0700435 structs = self._struct_data
Simon Glassd570dec2017-06-18 22:08:58 -0600436 for node in self._valid_nodes:
Simon Glass9065bc92020-12-28 20:35:06 -0700437 node_name, _ = self._scan.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600438 fields = {}
439
440 # Get a list of all the valid properties in this node.
441 for name, prop in node.props.items():
442 if name not in PROP_IGNORE_LIST and name[0] != '#':
443 fields[name] = copy.deepcopy(prop)
444
445 # If we've seen this node_name before, update the existing struct.
446 if node_name in structs:
447 struct = structs[node_name]
448 for name, prop in fields.items():
449 oldprop = struct.get(name)
450 if oldprop:
451 oldprop.Widen(prop)
452 else:
453 struct[name] = prop
454
455 # Otherwise store this as a new struct.
456 else:
457 structs[node_name] = fields
458
Simon Glassd570dec2017-06-18 22:08:58 -0600459 for node in self._valid_nodes:
Simon Glass9065bc92020-12-28 20:35:06 -0700460 node_name, _ = self._scan.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600461 struct = structs[node_name]
462 for name, prop in node.props.items():
463 if name not in PROP_IGNORE_LIST and name[0] != '#':
464 prop.Widen(struct[name])
Simon Glassd570dec2017-06-18 22:08:58 -0600465
Simon Glass1f730c82017-06-18 22:08:59 -0600466 def scan_phandles(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600467 """Figure out what phandles each node uses
468
469 We need to be careful when outputing nodes that use phandles since
470 they must come after the declaration of the phandles in the C file.
471 Otherwise we get a compiler error since the phandle struct is not yet
472 declared.
473
474 This function adds to each node a list of phandle nodes that the node
475 depends on. This allows us to output things in the right order.
476 """
477 for node in self._valid_nodes:
478 node.phandles = set()
479 for pname, prop in node.props.items():
480 if pname in PROP_IGNORE_LIST or pname[0] == '#':
481 continue
Simon Glassec3b5e42017-08-29 14:15:55 -0600482 info = self.get_phandle_argc(prop, node.name)
483 if info:
Simon Glassec3b5e42017-08-29 14:15:55 -0600484 # Process the list as pairs of (phandle, id)
Simon Glass3deeb472017-08-29 14:15:59 -0600485 pos = 0
486 for args in info.args:
487 phandle_cell = prop.value[pos]
Simon Glassec3b5e42017-08-29 14:15:55 -0600488 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
489 target_node = self._fdt.phandle_to_node[phandle]
490 node.phandles.add(target_node)
Simon Glass3deeb472017-08-29 14:15:59 -0600491 pos += 1 + args
Simon Glassd570dec2017-06-18 22:08:58 -0600492
493
Simon Glass55526782020-12-28 20:35:02 -0700494 def generate_structs(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600495 """Generate struct defintions for the platform data
496
497 This writes out the body of a header file consisting of structure
498 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +0100499 doc/driver-model/of-plat.rst for more information.
Simon Glassd570dec2017-06-18 22:08:58 -0600500 """
Simon Glass55526782020-12-28 20:35:02 -0700501 structs = self._struct_data
Simon Glass1f730c82017-06-18 22:08:59 -0600502 self.out('#include <stdbool.h>\n')
Masahiro Yamada75f82d02018-03-05 01:20:11 +0900503 self.out('#include <linux/libfdt.h>\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600504
505 # Output the struct definition
506 for name in sorted(structs):
Simon Glass1f730c82017-06-18 22:08:59 -0600507 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glassd570dec2017-06-18 22:08:58 -0600508 for pname in sorted(structs[name]):
509 prop = structs[name][pname]
Simon Glassec3b5e42017-08-29 14:15:55 -0600510 info = self.get_phandle_argc(prop, structs[name])
511 if info:
Simon Glassd570dec2017-06-18 22:08:58 -0600512 # For phandles, include a reference to the target
Simon Glasse94414b2017-08-29 14:15:56 -0600513 struct_name = 'struct phandle_%d_arg' % info.max_args
514 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass1f730c82017-06-18 22:08:59 -0600515 conv_name_to_c(prop.name),
Simon Glass3deeb472017-08-29 14:15:59 -0600516 len(info.args)))
Simon Glassd570dec2017-06-18 22:08:58 -0600517 else:
518 ptype = TYPE_NAMES[prop.type]
Simon Glass1f730c82017-06-18 22:08:59 -0600519 self.out('\t%s%s' % (tab_to(2, ptype),
520 conv_name_to_c(prop.name)))
521 if isinstance(prop.value, list):
522 self.out('[%d]' % len(prop.value))
523 self.out(';\n')
524 self.out('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600525
Simon Glass9d98b6e2020-12-23 08:11:20 -0700526 def _output_list(self, node, prop):
527 """Output the C code for a devicetree property that holds a list
Simon Glassd570dec2017-06-18 22:08:58 -0600528
529 Args:
Simon Glass9d98b6e2020-12-23 08:11:20 -0700530 node (fdt.Node): Node to output
531 prop (fdt.Prop): Prop to output
Simon Glassd570dec2017-06-18 22:08:58 -0600532 """
Simon Glass9d98b6e2020-12-23 08:11:20 -0700533 self.buf('{')
534 vals = []
535 # For phandles, output a reference to the platform data
536 # of the target node.
537 info = self.get_phandle_argc(prop, node.name)
538 if info:
539 # Process the list as pairs of (phandle, id)
540 pos = 0
541 for args in info.args:
542 phandle_cell = prop.value[pos]
543 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
544 target_node = self._fdt.phandle_to_node[phandle]
545 arg_values = []
546 for i in range(args):
547 arg_values.append(
548 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
549 pos += 1 + args
550 vals.append('\t{%d, {%s}}' % (target_node.idx,
551 ', '.join(arg_values)))
552 for val in vals:
553 self.buf('\n\t\t%s,' % val)
554 else:
555 for val in prop.value:
556 vals.append(get_value(prop.type, val))
Simon Glassbc9e2682020-10-03 09:25:18 -0600557
Simon Glass9d98b6e2020-12-23 08:11:20 -0700558 # Put 8 values per line to avoid very long lines.
559 for i in range(0, len(vals), 8):
560 if i:
561 self.buf(',\n\t\t')
562 self.buf(', '.join(vals[i:i + 8]))
563 self.buf('}')
Simon Glassbc9e2682020-10-03 09:25:18 -0600564
Simon Glass9fdd0c32020-12-23 08:11:21 -0700565 def _declare_device(self, var_name, struct_name, node_parent):
566 """Add a device declaration to the output
567
Simon Glass1d8364a2020-12-28 20:34:54 -0700568 This declares a U_BOOT_DRVINFO() for the device being processed
Simon Glass9fdd0c32020-12-23 08:11:21 -0700569
570 Args:
571 var_name (str): C name for the node
572 struct_name (str): Name for the dt struct associated with the node
573 node_parent (Node): Parent of the node (or None if none)
574 """
Simon Glass1d8364a2020-12-28 20:34:54 -0700575 self.buf('U_BOOT_DRVINFO(%s) = {\n' % var_name)
Simon Glass9fdd0c32020-12-23 08:11:21 -0700576 self.buf('\t.name\t\t= "%s",\n' % struct_name)
577 self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, var_name))
578 self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
579 idx = -1
580 if node_parent and node_parent in self._valid_nodes:
581 idx = node_parent.idx
582 self.buf('\t.parent_idx\t= %d,\n' % idx)
583 self.buf('};\n')
584 self.buf('\n')
585
Simon Glass9829eea2020-12-23 08:11:22 -0700586 def _output_prop(self, node, prop):
587 """Output a line containing the value of a struct member
588
589 Args:
590 node (Node): Node being output
591 prop (Prop): Prop object to output
592 """
593 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
594 return
595 member_name = conv_name_to_c(prop.name)
596 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
597
598 # Special handling for lists
599 if isinstance(prop.value, list):
600 self._output_list(node, prop)
601 else:
602 self.buf(get_value(prop.type, prop.value))
603 self.buf(',\n')
604
605 def _output_values(self, var_name, struct_name, node):
606 """Output the definition of a device's struct values
607
608 Args:
609 var_name (str): C name for the node
610 struct_name (str): Name for the dt struct associated with the node
611 node (Node): Node being output
612 """
613 self.buf('static struct %s%s %s%s = {\n' %
614 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
615 for pname in sorted(node.props):
616 self._output_prop(node, node.props[pname])
617 self.buf('};\n')
618
Simon Glass9d98b6e2020-12-23 08:11:20 -0700619 def output_node(self, node):
620 """Output the C code for a node
Simon Glassbc9e2682020-10-03 09:25:18 -0600621
Simon Glass9d98b6e2020-12-23 08:11:20 -0700622 Args:
623 node (fdt.Node): node to output
624 """
Simon Glass9065bc92020-12-28 20:35:06 -0700625 struct_name, _ = self._scan.get_normalized_compat_name(node)
Simon Glass1f730c82017-06-18 22:08:59 -0600626 var_name = conv_name_to_c(node.name)
Simon Glass192f8132020-10-03 11:31:25 -0600627 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Simon Glassd570dec2017-06-18 22:08:58 -0600628
Simon Glass9829eea2020-12-23 08:11:22 -0700629 self._output_values(var_name, struct_name, node)
Simon Glass9fdd0c32020-12-23 08:11:21 -0700630 self._declare_device(var_name, struct_name, node.parent)
Simon Glassd570dec2017-06-18 22:08:58 -0600631
Simon Glass1f730c82017-06-18 22:08:59 -0600632 self.out(''.join(self.get_buf()))
Simon Glassd570dec2017-06-18 22:08:58 -0600633
Simon Glass55526782020-12-28 20:35:02 -0700634 def generate_plat(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600635 """Generate device defintions for the platform data
636
637 This writes out C platform data initialisation data and
Simon Glass1d8364a2020-12-28 20:34:54 -0700638 U_BOOT_DRVINFO() declarations for each valid node. Where a node has
Simon Glassd570dec2017-06-18 22:08:58 -0600639 multiple compatible strings, a #define is used to make them equivalent.
640
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +0100641 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glassd570dec2017-06-18 22:08:58 -0600642 information.
643 """
Simon Glass1d8364a2020-12-28 20:34:54 -0700644 self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
Simon Glassbeddd7a2020-12-28 20:35:01 -0700645 self.out('#define DT_PLAT_C\n')
Simon Glass4c73d7b2020-10-03 11:31:41 -0600646 self.out('\n')
Simon Glass1f730c82017-06-18 22:08:59 -0600647 self.out('#include <common.h>\n')
648 self.out('#include <dm.h>\n')
649 self.out('#include <dt-structs.h>\n')
650 self.out('\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600651
Simon Glass16382ce2020-12-28 20:35:04 -0700652 for node in self._valid_nodes:
Simon Glass1f730c82017-06-18 22:08:59 -0600653 self.output_node(node)
Simon Glass3fa797a2017-06-18 22:09:03 -0600654
Walter Lozanodc5b4372020-06-25 01:10:13 -0300655 self.out(''.join(self.get_buf()))
Simon Glass3fa797a2017-06-18 22:09:03 -0600656
Simon Glassc3a310a82020-12-28 20:34:51 -0700657
658# Types of output file we understand
659# key: Command used to generate this file
660# value: OutputFile for this command
661OUTPUT_FILES = {
Simon Glass6b208842020-12-28 20:35:00 -0700662 'struct':
663 OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
Simon Glass55526782020-12-28 20:35:02 -0700664 DtbPlatdata.generate_structs,
Simon Glass6b208842020-12-28 20:35:00 -0700665 'Defines the structs used to hold devicetree data'),
666 'platdata':
Simon Glass55526782020-12-28 20:35:02 -0700667 OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
Simon Glass6b208842020-12-28 20:35:00 -0700668 'Declares the U_BOOT_DRIVER() records and platform data'),
Simon Glassc3a310a82020-12-28 20:34:51 -0700669 }
670
Simon Glass6a65d8a2020-12-28 20:34:50 -0700671
672def run_steps(args, dtb_file, include_disabled, output, output_dirs,
Simon Glass4f2059b2020-12-28 20:35:03 -0700673 warning_disabled=False, drivers_additional=None, basedir=None):
Simon Glass3fa797a2017-06-18 22:09:03 -0600674 """Run all the steps of the dtoc tool
675
676 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700677 args (list): List of non-option arguments provided to the problem
678 dtb_file (str): Filename of dtb file to process
679 include_disabled (bool): True to include disabled nodes
Simon Glass6ca0c7a2020-12-28 20:34:48 -0700680 output (str): Name of output file (None for stdout)
Simon Glass6a65d8a2020-12-28 20:34:50 -0700681 output_dirs (tuple of str):
682 Directory to put C output files
683 Directory to put H output files
Simon Glass93daa012020-12-03 16:55:16 -0700684 warning_disabled (bool): True to avoid showing warnings about missing
685 drivers
Simon Glass27511232020-12-23 08:11:19 -0700686 drivers_additional (list): List of additional drivers to use during
Simon Glass93daa012020-12-03 16:55:16 -0700687 scanning
Simon Glass4f2059b2020-12-28 20:35:03 -0700688 basedir (str): Base directory of U-Boot source code. Defaults to the
689 grandparent of this file's directory
Simon Glass94ee3bd2020-11-08 20:36:21 -0700690 Raises:
691 ValueError: if args has no command, or an unknown command
Simon Glass3fa797a2017-06-18 22:09:03 -0600692 """
693 if not args:
Simon Glassc3a310a82020-12-28 20:34:51 -0700694 raise ValueError('Please specify a command: struct, platdata, all')
695 if output and output_dirs and any(output_dirs):
696 raise ValueError('Must specify either output or output_dirs, not both')
Simon Glass3fa797a2017-06-18 22:09:03 -0600697
Simon Glassdf692c32020-12-28 20:35:07 -0700698 scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional)
Simon Glass9065bc92020-12-28 20:35:06 -0700699 plat = DtbPlatdata(scan, dtb_file, include_disabled)
700 scan.scan_drivers()
Simon Glass3fa797a2017-06-18 22:09:03 -0600701 plat.scan_dtb()
702 plat.scan_tree()
Simon Glass1b1fe412017-08-29 14:15:50 -0600703 plat.scan_reg_sizes()
Simon Glassc3a310a82020-12-28 20:34:51 -0700704 plat.setup_output_dirs(output_dirs)
Simon Glass55526782020-12-28 20:35:02 -0700705 plat.scan_structs()
Simon Glass3fa797a2017-06-18 22:09:03 -0600706 plat.scan_phandles()
707
Simon Glass4e8e8462020-12-28 20:34:52 -0700708 cmds = args[0].split(',')
709 if 'all' in cmds:
710 cmds = sorted(OUTPUT_FILES.keys())
711 for cmd in cmds:
Simon Glassc3a310a82020-12-28 20:34:51 -0700712 outfile = OUTPUT_FILES.get(cmd)
713 if not outfile:
714 raise ValueError("Unknown command '%s': (use: %s)" %
Simon Glass4e8e8462020-12-28 20:34:52 -0700715 (cmd, ', '.join(sorted(OUTPUT_FILES.keys()))))
Simon Glassc3a310a82020-12-28 20:34:51 -0700716 plat.setup_output(outfile.ftype,
717 outfile.fname if output_dirs else output)
Simon Glass6b208842020-12-28 20:35:00 -0700718 plat.out_header(outfile)
Simon Glass55526782020-12-28 20:35:02 -0700719 outfile.method(plat)
Simon Glassc3a310a82020-12-28 20:34:51 -0700720 plat.finish_output()