blob: b7abaed67acb2e302fcb51c6eca1490f542e74a4 [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))
357 for idx, node in enumerate(self._valid_nodes):
358 node.idx = idx
Simon Glass7581c012017-06-18 22:08:58 -0600359
Simon Glassc20ee0e2017-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 Glass9b330382020-11-08 20:36:21 -0700365 node (fdt.None): Node to check
Simon Glassc20ee0e2017-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 Glass78128d52020-12-03 16:55:16 -0700373 num_addr, num_size = 2, 2
Simon Glassc20ee0e2017-08-29 14:15:50 -0600374 if parent:
Simon Glass78128d52020-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 Glassc20ee0e2017-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 Glass78128d52020-12-03 16:55:16 -0700394 num_addr, num_size = self.get_num_cells(node)
395 total = num_addr + num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600396
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700397 if reg.type != fdt.Type.INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600398 raise ValueError("Node '%s' reg property is not an int" %
399 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600400 if len(reg.value) % total:
Simon Glass9b330382020-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 Glass78128d52020-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 Glass5ea9dcc2020-11-08 20:36:17 -0700408 reg.type = fdt.Type.INT64
Simon Glassc20ee0e2017-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 Glass78128d52020-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 Glassc20ee0e2017-08-29 14:15:50 -0600419 new_value += [addr, size]
420 reg.value = new_value
421
Simon Glass2be282c2017-06-18 22:08:59 -0600422 def scan_structs(self):
Simon Glass7581c012017-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 Glasse4fb5fa2020-10-03 11:31:24 -0600432
Simon Glassa7d5f962020-12-28 20:35:02 -0700433 The results are written to self._struct_data
Simon Glass7581c012017-06-18 22:08:58 -0600434 """
Simon Glassa7d5f962020-12-28 20:35:02 -0700435 structs = self._struct_data
Simon Glass7581c012017-06-18 22:08:58 -0600436 for node in self._valid_nodes:
Simon Glassa542a702020-12-28 20:35:06 -0700437 node_name, _ = self._scan.get_normalized_compat_name(node)
Simon Glass7581c012017-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 Glass7581c012017-06-18 22:08:58 -0600459 for node in self._valid_nodes:
Simon Glassa542a702020-12-28 20:35:06 -0700460 node_name, _ = self._scan.get_normalized_compat_name(node)
Simon Glass7581c012017-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 Glass7581c012017-06-18 22:08:58 -0600465
Simon Glass2be282c2017-06-18 22:08:59 -0600466 def scan_phandles(self):
Simon Glass7581c012017-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 Glass8fed2eb2017-08-29 14:15:55 -0600482 info = self.get_phandle_argc(prop, node.name)
483 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600484 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600485 pos = 0
486 for args in info.args:
487 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-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 Glass634eba42017-08-29 14:15:59 -0600491 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600492
493
Simon Glassa7d5f962020-12-28 20:35:02 -0700494 def generate_structs(self):
Simon Glass7581c012017-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 Schuchardt2799a692020-02-25 21:35:39 +0100499 doc/driver-model/of-plat.rst for more information.
Simon Glass7581c012017-06-18 22:08:58 -0600500 """
Simon Glassa7d5f962020-12-28 20:35:02 -0700501 structs = self._struct_data
Simon Glass2be282c2017-06-18 22:08:59 -0600502 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900503 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600504
505 # Output the struct definition
506 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600507 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600508 for pname in sorted(structs[name]):
509 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600510 info = self.get_phandle_argc(prop, structs[name])
511 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600512 # For phandles, include a reference to the target
Simon Glass0d154632017-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 Glass2be282c2017-06-18 22:08:59 -0600515 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600516 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600517 else:
518 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-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 Glass7581c012017-06-18 22:08:58 -0600525
Simon Glassabf0c802020-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
528
529 Args:
530 node (fdt.Node): Node to output
531 prop (fdt.Prop): Prop to output
532 """
533 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))
557
558 # 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('}')
564
Simon Glass221ddc12020-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 Glass20e442a2020-12-28 20:34:54 -0700568 This declares a U_BOOT_DRVINFO() for the device being processed
Simon Glass221ddc12020-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 Glass20e442a2020-12-28 20:34:54 -0700575 self.buf('U_BOOT_DRVINFO(%s) = {\n' % var_name)
Simon Glass221ddc12020-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 Glass161dac12020-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 Glass2be282c2017-06-18 22:08:59 -0600619 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600620 """Output the C code for a node
621
622 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700623 node (fdt.Node): node to output
Simon Glass7581c012017-06-18 22:08:58 -0600624 """
Simon Glassa542a702020-12-28 20:35:06 -0700625 struct_name, _ = self._scan.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600626 var_name = conv_name_to_c(node.name)
Simon Glass1b272732020-10-03 11:31:25 -0600627 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Simon Glass7581c012017-06-18 22:08:58 -0600628
Simon Glass161dac12020-12-23 08:11:22 -0700629 self._output_values(var_name, struct_name, node)
Simon Glass221ddc12020-12-23 08:11:21 -0700630 self._declare_device(var_name, struct_name, node.parent)
Simon Glass7581c012017-06-18 22:08:58 -0600631
Simon Glass2be282c2017-06-18 22:08:59 -0600632 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600633
Simon Glassa7d5f962020-12-28 20:35:02 -0700634 def generate_plat(self):
Simon Glass7581c012017-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 Glass20e442a2020-12-28 20:34:54 -0700638 U_BOOT_DRVINFO() declarations for each valid node. Where a node has
Simon Glass7581c012017-06-18 22:08:58 -0600639 multiple compatible strings, a #define is used to make them equivalent.
640
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100641 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600642 information.
643 """
Simon Glass20e442a2020-12-28 20:34:54 -0700644 self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
Simon Glassf31fa992020-12-28 20:35:01 -0700645 self.out('#define DT_PLAT_C\n')
Simon Glasscb43ac12020-10-03 11:31:41 -0600646 self.out('\n')
Simon Glass2be282c2017-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 Glass7581c012017-06-18 22:08:58 -0600651
Simon Glass9eca08d2020-12-28 20:35:04 -0700652 for node in self._valid_nodes:
Simon Glass2be282c2017-06-18 22:08:59 -0600653 self.output_node(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600654
Walter Lozano51f12632020-06-25 01:10:13 -0300655 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600656
Simon Glass192c1112020-12-28 20:34:50 -0700657
Simon Glassbe44f272020-12-28 20:34:51 -0700658# Types of output file we understand
659# key: Command used to generate this file
660# value: OutputFile for this command
661OUTPUT_FILES = {
Simon Glassd1055d62020-12-28 20:35:00 -0700662 'struct':
663 OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
Simon Glassa7d5f962020-12-28 20:35:02 -0700664 DtbPlatdata.generate_structs,
Simon Glassd1055d62020-12-28 20:35:00 -0700665 'Defines the structs used to hold devicetree data'),
666 'platdata':
Simon Glassa7d5f962020-12-28 20:35:02 -0700667 OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
Simon Glassd1055d62020-12-28 20:35:00 -0700668 'Declares the U_BOOT_DRIVER() records and platform data'),
Simon Glassbe44f272020-12-28 20:34:51 -0700669 }
670
671
Simon Glass192c1112020-12-28 20:34:50 -0700672def run_steps(args, dtb_file, include_disabled, output, output_dirs,
Simon Glass1e0f3f42020-12-28 20:35:03 -0700673 warning_disabled=False, drivers_additional=None, basedir=None):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600674 """Run all the steps of the dtoc tool
675
676 Args:
Simon Glass9b330382020-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 Glassf62cea02020-12-28 20:34:48 -0700680 output (str): Name of output file (None for stdout)
Simon Glass192c1112020-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 Glass78128d52020-12-03 16:55:16 -0700684 warning_disabled (bool): True to avoid showing warnings about missing
685 drivers
Simon Glassccc3da72020-12-23 08:11:19 -0700686 drivers_additional (list): List of additional drivers to use during
Simon Glass78128d52020-12-03 16:55:16 -0700687 scanning
Simon Glass1e0f3f42020-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 Glass9b330382020-11-08 20:36:21 -0700690 Raises:
691 ValueError: if args has no command, or an unknown command
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600692 """
693 if not args:
Simon Glassbe44f272020-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 Glassfa0ea5b2017-06-18 22:09:03 -0600697
Simon Glass10ea9c02020-12-28 20:35:07 -0700698 scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional)
Simon Glassa542a702020-12-28 20:35:06 -0700699 plat = DtbPlatdata(scan, dtb_file, include_disabled)
700 scan.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600701 plat.scan_dtb()
702 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600703 plat.scan_reg_sizes()
Simon Glassbe44f272020-12-28 20:34:51 -0700704 plat.setup_output_dirs(output_dirs)
Simon Glassa7d5f962020-12-28 20:35:02 -0700705 plat.scan_structs()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600706 plat.scan_phandles()
707
Simon Glass10cbd3b2020-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 Glassbe44f272020-12-28 20:34:51 -0700712 outfile = OUTPUT_FILES.get(cmd)
713 if not outfile:
714 raise ValueError("Unknown command '%s': (use: %s)" %
Simon Glass10cbd3b2020-12-28 20:34:52 -0700715 (cmd, ', '.join(sorted(OUTPUT_FILES.keys()))))
Simon Glassbe44f272020-12-28 20:34:51 -0700716 plat.setup_output(outfile.ftype,
717 outfile.fname if output_dirs else output)
Simon Glassd1055d62020-12-28 20:35:00 -0700718 plat.out_header(outfile)
Simon Glassa7d5f962020-12-28 20:35:02 -0700719 outfile.method(plat)
Simon Glassbe44f272020-12-28 20:34:51 -0700720 plat.finish_output()