blob: 82671138a9a63c73cb7244f3e49f12dbede54c01 [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
Walter Lozanodac82282020-07-03 08:07:17 -030018import os
19import re
Simon Glass2be282c2017-06-18 22:08:59 -060020import sys
Simon Glass7581c012017-06-18 22:08:58 -060021
Simon Glassbf776672020-04-17 18:09:04 -060022from dtoc import fdt
23from dtoc import fdt_util
Simon Glass7581c012017-06-18 22:08:58 -060024
Simon Glass9b330382020-11-08 20:36:21 -070025# When we see these properties we ignore them - i.e. do not create a structure
26# member
Simon Glass7581c012017-06-18 22:08:58 -060027PROP_IGNORE_LIST = [
28 '#address-cells',
29 '#gpio-cells',
30 '#size-cells',
31 'compatible',
32 'linux,phandle',
33 "status",
34 'phandle',
35 'u-boot,dm-pre-reloc',
36 'u-boot,dm-tpl',
37 'u-boot,dm-spl',
38]
39
Simon Glass5ea9dcc2020-11-08 20:36:17 -070040# C type declarations for the types we support
Simon Glass7581c012017-06-18 22:08:58 -060041TYPE_NAMES = {
Simon Glass5ea9dcc2020-11-08 20:36:17 -070042 fdt.Type.INT: 'fdt32_t',
43 fdt.Type.BYTE: 'unsigned char',
44 fdt.Type.STRING: 'const char *',
45 fdt.Type.BOOL: 'bool',
46 fdt.Type.INT64: 'fdt64_t',
Simon Glass2be282c2017-06-18 22:08:59 -060047}
Simon Glass7581c012017-06-18 22:08:58 -060048
49STRUCT_PREFIX = 'dtd_'
50VAL_PREFIX = 'dtv_'
51
Simon Glass8fed2eb2017-08-29 14:15:55 -060052# This holds information about a property which includes phandles.
53#
54# max_args: integer: Maximum number or arguments that any phandle uses (int).
55# args: Number of args for each phandle in the property. The total number of
56# phandles is len(args). This is a list of integers.
57PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
58
Simon Glass97136eb2020-10-03 09:25:19 -060059# Holds a single phandle link, allowing a C struct value to be assigned to point
60# to a device
61#
62# var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node')
63# dev_name: Name of device to assign to (e.g. 'clock')
64PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name'])
65
Simon Glass8fed2eb2017-08-29 14:15:55 -060066
Simon Glass2be282c2017-06-18 22:08:59 -060067def conv_name_to_c(name):
Simon Glass7581c012017-06-18 22:08:58 -060068 """Convert a device-tree name to a C identifier
69
Simon Glass30107b02017-06-18 22:09:04 -060070 This uses multiple replace() calls instead of re.sub() since it is faster
71 (400ms for 1m calls versus 1000ms for the 're' version).
72
Simon Glass7581c012017-06-18 22:08:58 -060073 Args:
Simon Glass9b330382020-11-08 20:36:21 -070074 name (str): Name to convert
Simon Glass7581c012017-06-18 22:08:58 -060075 Return:
Simon Glass9b330382020-11-08 20:36:21 -070076 str: String containing the C version of this name
Simon Glass7581c012017-06-18 22:08:58 -060077 """
Simon Glass2be282c2017-06-18 22:08:59 -060078 new = name.replace('@', '_at_')
79 new = new.replace('-', '_')
80 new = new.replace(',', '_')
81 new = new.replace('.', '_')
Simon Glass2be282c2017-06-18 22:08:59 -060082 return new
Simon Glass7581c012017-06-18 22:08:58 -060083
Simon Glass2be282c2017-06-18 22:08:59 -060084def tab_to(num_tabs, line):
85 """Append tabs to a line of text to reach a tab stop.
Simon Glass7581c012017-06-18 22:08:58 -060086
Simon Glass2be282c2017-06-18 22:08:59 -060087 Args:
Simon Glass9b330382020-11-08 20:36:21 -070088 num_tabs (int): Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
89 line (str): Line of text to append to
Simon Glass2be282c2017-06-18 22:08:59 -060090
91 Returns:
Simon Glass9b330382020-11-08 20:36:21 -070092 str: line with the correct number of tabs appeneded. If the line already
Simon Glass2be282c2017-06-18 22:08:59 -060093 extends past that tab stop then a single space is appended.
94 """
95 if len(line) >= num_tabs * 8:
96 return line + ' '
97 return line + '\t' * (num_tabs - len(line) // 8)
98
Simon Glass56e0bbe2017-06-18 22:09:02 -060099def get_value(ftype, value):
100 """Get a value as a C expression
101
102 For integers this returns a byte-swapped (little-endian) hex string
103 For bytes this returns a hex string, e.g. 0x12
104 For strings this returns a literal string enclosed in quotes
105 For booleans this return 'true'
106
107 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700108 ftype (fdt.Type): Data type (fdt_util)
109 value (bytes): Data value, as a string of bytes
110
111 Returns:
112 str: String representation of the value
Simon Glass56e0bbe2017-06-18 22:09:02 -0600113 """
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700114 if ftype == fdt.Type.INT:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600115 return '%#x' % fdt_util.fdt32_to_cpu(value)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700116 elif ftype == fdt.Type.BYTE:
Simon Glass78128d52020-12-03 16:55:16 -0700117 char = value[0]
118 return '%#x' % (ord(char) if isinstance(char, str) else char)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700119 elif ftype == fdt.Type.STRING:
Simon Glassf02d0eb2020-07-07 21:32:06 -0600120 # Handle evil ACPI backslashes by adding another backslash before them.
121 # So "\\_SB.GPO0" in the device tree effectively stays like that in C
122 return '"%s"' % value.replace('\\', '\\\\')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700123 elif ftype == fdt.Type.BOOL:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600124 return 'true'
Simon Glass9b330382020-11-08 20:36:21 -0700125 else: # ftype == fdt.Type.INT64:
Simon Glassfbdfd222017-08-29 14:15:48 -0600126 return '%#x' % value
Simon Glass56e0bbe2017-06-18 22:09:02 -0600127
128def get_compat_name(node):
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300129 """Get the node's list of compatible string as a C identifiers
Simon Glass56e0bbe2017-06-18 22:09:02 -0600130
131 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700132 node (fdt.Node): Node object to check
Simon Glass56e0bbe2017-06-18 22:09:02 -0600133 Return:
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300134 List of C identifiers for all the compatible strings
Simon Glass56e0bbe2017-06-18 22:09:02 -0600135 """
136 compat = node.props['compatible'].value
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300137 if not isinstance(compat, list):
138 compat = [compat]
139 return [conv_name_to_c(c) for c in compat]
Simon Glass56e0bbe2017-06-18 22:09:02 -0600140
Simon Glass56e0bbe2017-06-18 22:09:02 -0600141
Simon Glass2be282c2017-06-18 22:08:59 -0600142class DtbPlatdata(object):
Simon Glass7581c012017-06-18 22:08:58 -0600143 """Provide a means to convert device tree binary data to platform data
144
145 The output of this process is C structures which can be used in space-
146 constrained encvironments where the ~3KB code overhead of device tree
147 code is not affordable.
148
149 Properties:
Simon Glass2be282c2017-06-18 22:08:59 -0600150 _fdt: Fdt object, referencing the device tree
Simon Glass7581c012017-06-18 22:08:58 -0600151 _dtb_fname: Filename of the input device tree binary file
Simon Glass1b272732020-10-03 11:31:25 -0600152 _valid_nodes: A list of Node object with compatible strings. The list
153 is ordered by conv_name_to_c(node.name)
Simon Glasse36024b2017-06-18 22:09:01 -0600154 _include_disabled: true to include nodes marked status = "disabled"
Simon Glass7581c012017-06-18 22:08:58 -0600155 _outfile: The current output file (sys.stdout or a real file)
Walter Lozano361e7332020-06-25 01:10:08 -0300156 _warning_disabled: true to disable warnings about driver names not found
Simon Glass7581c012017-06-18 22:08:58 -0600157 _lines: Stashed list of output lines for outputting in the future
Walter Lozanodac82282020-07-03 08:07:17 -0300158 _drivers: List of valid driver names found in drivers/
159 _driver_aliases: Dict that holds aliases for driver names
160 key: Driver alias declared with
161 U_BOOT_DRIVER_ALIAS(driver_alias, driver_name)
162 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300163 _drivers_additional: List of additional drivers to use during scanning
Simon Glass7581c012017-06-18 22:08:58 -0600164 """
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300165 def __init__(self, dtb_fname, include_disabled, warning_disabled,
Simon Glass78128d52020-12-03 16:55:16 -0700166 drivers_additional=None):
Simon Glass2be282c2017-06-18 22:08:59 -0600167 self._fdt = None
Simon Glass7581c012017-06-18 22:08:58 -0600168 self._dtb_fname = dtb_fname
169 self._valid_nodes = None
Simon Glasse36024b2017-06-18 22:09:01 -0600170 self._include_disabled = include_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600171 self._outfile = None
Walter Lozano361e7332020-06-25 01:10:08 -0300172 self._warning_disabled = warning_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600173 self._lines = []
Walter Lozanodac82282020-07-03 08:07:17 -0300174 self._drivers = []
175 self._driver_aliases = {}
Simon Glass78128d52020-12-03 16:55:16 -0700176 self._drivers_additional = drivers_additional or []
Walter Lozanodac82282020-07-03 08:07:17 -0300177
178 def get_normalized_compat_name(self, node):
179 """Get a node's normalized compat name
180
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300181 Returns a valid driver name by retrieving node's list of compatible
Walter Lozanodac82282020-07-03 08:07:17 -0300182 string as a C identifier and performing a check against _drivers
183 and a lookup in driver_aliases printing a warning in case of failure.
184
185 Args:
186 node: Node object to check
187 Return:
188 Tuple:
189 Driver name associated with the first compatible string
190 List of C identifiers for all the other compatible strings
191 (possibly empty)
192 In case of no match found, the return will be the same as
193 get_compat_name()
194 """
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300195 compat_list_c = get_compat_name(node)
Walter Lozanodac82282020-07-03 08:07:17 -0300196
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300197 for compat_c in compat_list_c:
198 if not compat_c in self._drivers:
199 compat_c = self._driver_aliases.get(compat_c)
200 if not compat_c:
201 continue
202
203 aliases_c = compat_list_c
204 if compat_c in aliases_c:
205 aliases_c.remove(compat_c)
206 return compat_c, aliases_c
207
208 if not self._warning_disabled:
209 print('WARNING: the driver %s was not found in the driver list'
210 % (compat_list_c[0]))
211
212 return compat_list_c[0], compat_list_c[1:]
Simon Glass7581c012017-06-18 22:08:58 -0600213
Simon Glass2be282c2017-06-18 22:08:59 -0600214 def setup_output(self, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600215 """Set up the output destination
216
Simon Glass2be282c2017-06-18 22:08:59 -0600217 Once this is done, future calls to self.out() will output to this
Simon Glass7581c012017-06-18 22:08:58 -0600218 file.
219
220 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700221 fname (str): Filename to send output to, or '-' for stdout
Simon Glass7581c012017-06-18 22:08:58 -0600222 """
223 if fname == '-':
224 self._outfile = sys.stdout
225 else:
226 self._outfile = open(fname, 'w')
227
Simon Glass2be282c2017-06-18 22:08:59 -0600228 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600229 """Output a string to the output file
230
231 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700232 line (str): String to output
Simon Glass7581c012017-06-18 22:08:58 -0600233 """
Simon Glass2be282c2017-06-18 22:08:59 -0600234 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600235
Simon Glass2be282c2017-06-18 22:08:59 -0600236 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600237 """Buffer up a string to send later
238
239 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700240 line (str): String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600241 """
Simon Glass2be282c2017-06-18 22:08:59 -0600242 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600243
Simon Glass2be282c2017-06-18 22:08:59 -0600244 def get_buf(self):
Simon Glass7581c012017-06-18 22:08:58 -0600245 """Get the contents of the output buffer, and clear it
246
247 Returns:
Simon Glass9b330382020-11-08 20:36:21 -0700248 list(str): The output buffer, which is then cleared for future use
Simon Glass7581c012017-06-18 22:08:58 -0600249 """
250 lines = self._lines
251 self._lines = []
252 return lines
253
Simon Glassd5031142017-08-29 14:16:01 -0600254 def out_header(self):
255 """Output a message indicating that this is an auto-generated file"""
256 self.out('''/*
257 * DO NOT MODIFY
258 *
259 * This file was generated by dtoc from a .dtb (device tree binary) file.
260 */
261
262''')
263
Simon Glass8fed2eb2017-08-29 14:15:55 -0600264 def get_phandle_argc(self, prop, node_name):
265 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600266
Simon Glass8fed2eb2017-08-29 14:15:55 -0600267 We have no reliable way of detecting whether a node uses a phandle
268 or not. As an interim measure, use a list of known property names.
Simon Glass2925c262017-08-29 14:15:54 -0600269
Simon Glass8fed2eb2017-08-29 14:15:55 -0600270 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700271 prop (fdt.Prop): Prop object to check
272 node_name (str): Node name, only used for raising an error
273 Returns:
274 int or None: Number of argument cells is this is a phandle,
275 else None
276 Raises:
277 ValueError: if the phandle cannot be parsed or the required property
278 is not present
Simon Glass8fed2eb2017-08-29 14:15:55 -0600279 """
Walter Lozanoad340172020-06-25 01:10:16 -0300280 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass760b7172018-07-06 10:27:31 -0600281 if not isinstance(prop.value, list):
282 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600283 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600284 i = 0
285
286 max_args = 0
287 args = []
288 while i < len(val):
289 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-07-06 10:27:31 -0600290 # If we get to the end of the list, stop. This can happen
291 # since some nodes have more phandles in the list than others,
292 # but we allocate enough space for the largest list. So those
293 # nodes with shorter lists end up with zeroes at the end.
294 if not phandle:
295 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600296 target = self._fdt.phandle_to_node.get(phandle)
297 if not target:
298 raise ValueError("Cannot parse '%s' in node '%s'" %
299 (prop.name, node_name))
Walter Lozanoad340172020-06-25 01:10:16 -0300300 cells = None
301 for prop_name in ['#clock-cells', '#gpio-cells']:
302 cells = target.props.get(prop_name)
303 if cells:
304 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600305 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300306 raise ValueError("Node '%s' has no cells property" %
Simon Glass9b330382020-11-08 20:36:21 -0700307 (target.name))
Simon Glass8fed2eb2017-08-29 14:15:55 -0600308 num_args = fdt_util.fdt32_to_cpu(cells.value)
309 max_args = max(max_args, num_args)
310 args.append(num_args)
311 i += 1 + num_args
312 return PhandleInfo(max_args, args)
313 return None
Simon Glass2925c262017-08-29 14:15:54 -0600314
Simon Glass78128d52020-12-03 16:55:16 -0700315 def scan_driver(self, fname):
Walter Lozanodac82282020-07-03 08:07:17 -0300316 """Scan a driver file to build a list of driver names and aliases
317
318 This procedure will populate self._drivers and self._driver_aliases
319
320 Args
Simon Glass78128d52020-12-03 16:55:16 -0700321 fname: Driver filename to scan
Walter Lozanodac82282020-07-03 08:07:17 -0300322 """
Simon Glass78128d52020-12-03 16:55:16 -0700323 with open(fname, encoding='utf-8') as inf:
Walter Lozanodac82282020-07-03 08:07:17 -0300324 try:
Simon Glass78128d52020-12-03 16:55:16 -0700325 buff = inf.read()
Walter Lozanodac82282020-07-03 08:07:17 -0300326 except UnicodeDecodeError:
327 # This seems to happen on older Python versions
Simon Glass78128d52020-12-03 16:55:16 -0700328 print("Skipping file '%s' due to unicode error" % fname)
Walter Lozanodac82282020-07-03 08:07:17 -0300329 return
330
331 # The following re will search for driver names declared as
332 # U_BOOT_DRIVER(driver_name)
333 drivers = re.findall('U_BOOT_DRIVER\((.*)\)', buff)
334
335 for driver in drivers:
336 self._drivers.append(driver)
337
338 # The following re will search for driver aliases declared as
339 # U_BOOT_DRIVER_ALIAS(alias, driver_name)
Simon Glass78128d52020-12-03 16:55:16 -0700340 driver_aliases = re.findall(
341 'U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
342 buff)
Walter Lozanodac82282020-07-03 08:07:17 -0300343
344 for alias in driver_aliases: # pragma: no cover
345 if len(alias) != 2:
346 continue
347 self._driver_aliases[alias[1]] = alias[0]
348
349 def scan_drivers(self):
350 """Scan the driver folders to build a list of driver names and aliases
351
352 This procedure will populate self._drivers and self._driver_aliases
353
354 """
355 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
356 if basedir == '':
357 basedir = './'
Simon Glass78128d52020-12-03 16:55:16 -0700358 for (dirpath, _, filenames) in os.walk(basedir):
359 for fname in filenames:
360 if not fname.endswith('.c'):
Walter Lozanodac82282020-07-03 08:07:17 -0300361 continue
Simon Glass78128d52020-12-03 16:55:16 -0700362 self.scan_driver(dirpath + '/' + fname)
Walter Lozanodac82282020-07-03 08:07:17 -0300363
Simon Glass78128d52020-12-03 16:55:16 -0700364 for fname in self._drivers_additional:
365 if not isinstance(fname, str) or len(fname) == 0:
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300366 continue
Simon Glass78128d52020-12-03 16:55:16 -0700367 if fname[0] == '/':
368 self.scan_driver(fname)
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300369 else:
Simon Glass78128d52020-12-03 16:55:16 -0700370 self.scan_driver(basedir + '/' + fname)
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300371
Simon Glass2be282c2017-06-18 22:08:59 -0600372 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200373 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600374
Simon Glass2be282c2017-06-18 22:08:59 -0600375 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600376 device tree root node, and progress from there.
377 """
Simon Glass2be282c2017-06-18 22:08:59 -0600378 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600379
Simon Glass1b272732020-10-03 11:31:25 -0600380 def scan_node(self, root, valid_nodes):
Simon Glass2be282c2017-06-18 22:08:59 -0600381 """Scan a node and subnodes to build a tree of node and phandle info
382
Simon Glass72ab7c52017-08-29 14:15:53 -0600383 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600384
385 Args:
386 root: Root node for scan
Simon Glass1b272732020-10-03 11:31:25 -0600387 valid_nodes: List of Node objects to add to
Simon Glass2be282c2017-06-18 22:08:59 -0600388 """
Simon Glass7581c012017-06-18 22:08:58 -0600389 for node in root.subnodes:
390 if 'compatible' in node.props:
391 status = node.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600392 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600393 status.value != 'disabled'):
Simon Glass1b272732020-10-03 11:31:25 -0600394 valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600395
396 # recurse to handle any subnodes
Simon Glass1b272732020-10-03 11:31:25 -0600397 self.scan_node(node, valid_nodes)
Simon Glass7581c012017-06-18 22:08:58 -0600398
Simon Glass2be282c2017-06-18 22:08:59 -0600399 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600400 """Scan the device tree for useful information
401
402 This fills in the following properties:
Simon Glass7581c012017-06-18 22:08:58 -0600403 _valid_nodes: A list of nodes we wish to consider include in the
404 platform data
405 """
Simon Glass1b272732020-10-03 11:31:25 -0600406 valid_nodes = []
407 self.scan_node(self._fdt.GetRoot(), valid_nodes)
408 self._valid_nodes = sorted(valid_nodes,
409 key=lambda x: conv_name_to_c(x.name))
410 for idx, node in enumerate(self._valid_nodes):
411 node.idx = idx
Simon Glass7581c012017-06-18 22:08:58 -0600412
Simon Glassc20ee0e2017-08-29 14:15:50 -0600413 @staticmethod
414 def get_num_cells(node):
415 """Get the number of cells in addresses and sizes for this node
416
417 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700418 node (fdt.None): Node to check
Simon Glassc20ee0e2017-08-29 14:15:50 -0600419
420 Returns:
421 Tuple:
422 Number of address cells for this node
423 Number of size cells for this node
424 """
425 parent = node.parent
Simon Glass78128d52020-12-03 16:55:16 -0700426 num_addr, num_size = 2, 2
Simon Glassc20ee0e2017-08-29 14:15:50 -0600427 if parent:
Simon Glass78128d52020-12-03 16:55:16 -0700428 addr_prop = parent.props.get('#address-cells')
429 size_prop = parent.props.get('#size-cells')
430 if addr_prop:
431 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
432 if size_prop:
433 num_size = fdt_util.fdt32_to_cpu(size_prop.value)
434 return num_addr, num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600435
436 def scan_reg_sizes(self):
437 """Scan for 64-bit 'reg' properties and update the values
438
439 This finds 'reg' properties with 64-bit data and converts the value to
440 an array of 64-values. This allows it to be output in a way that the
441 C code can read.
442 """
443 for node in self._valid_nodes:
444 reg = node.props.get('reg')
445 if not reg:
446 continue
Simon Glass78128d52020-12-03 16:55:16 -0700447 num_addr, num_size = self.get_num_cells(node)
448 total = num_addr + num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600449
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700450 if reg.type != fdt.Type.INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600451 raise ValueError("Node '%s' reg property is not an int" %
452 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600453 if len(reg.value) % total:
Simon Glass9b330382020-11-08 20:36:21 -0700454 raise ValueError(
455 "Node '%s' reg property has %d cells "
456 'which is not a multiple of na + ns = %d + %d)' %
Simon Glass78128d52020-12-03 16:55:16 -0700457 (node.name, len(reg.value), num_addr, num_size))
458 reg.num_addr = num_addr
459 reg.num_size = num_size
460 if num_addr != 1 or num_size != 1:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700461 reg.type = fdt.Type.INT64
Simon Glassc20ee0e2017-08-29 14:15:50 -0600462 i = 0
463 new_value = []
464 val = reg.value
465 if not isinstance(val, list):
466 val = [val]
467 while i < len(val):
Simon Glass78128d52020-12-03 16:55:16 -0700468 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
469 i += num_addr
470 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
471 i += num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600472 new_value += [addr, size]
473 reg.value = new_value
474
Simon Glass2be282c2017-06-18 22:08:59 -0600475 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600476 """Scan the device tree building up the C structures we will use.
477
478 Build a dict keyed by C struct name containing a dict of Prop
479 object for each struct field (keyed by property name). Where the
480 same struct appears multiple times, try to use the 'widest'
481 property, i.e. the one with a type which can express all others.
482
483 Once the widest property is determined, all other properties are
484 updated to match that width.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600485
486 Returns:
487 dict containing structures:
488 key (str): Node name, as a C identifier
489 value: dict containing structure fields:
490 key (str): Field name
491 value: Prop object with field information
Simon Glass7581c012017-06-18 22:08:58 -0600492 """
Simon Glass1b272732020-10-03 11:31:25 -0600493 structs = collections.OrderedDict()
Simon Glass7581c012017-06-18 22:08:58 -0600494 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300495 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600496 fields = {}
497
498 # Get a list of all the valid properties in this node.
499 for name, prop in node.props.items():
500 if name not in PROP_IGNORE_LIST and name[0] != '#':
501 fields[name] = copy.deepcopy(prop)
502
503 # If we've seen this node_name before, update the existing struct.
504 if node_name in structs:
505 struct = structs[node_name]
506 for name, prop in fields.items():
507 oldprop = struct.get(name)
508 if oldprop:
509 oldprop.Widen(prop)
510 else:
511 struct[name] = prop
512
513 # Otherwise store this as a new struct.
514 else:
515 structs[node_name] = fields
516
Simon Glass7581c012017-06-18 22:08:58 -0600517 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300518 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600519 struct = structs[node_name]
520 for name, prop in node.props.items():
521 if name not in PROP_IGNORE_LIST and name[0] != '#':
522 prop.Widen(struct[name])
Simon Glass7581c012017-06-18 22:08:58 -0600523
Simon Glass7581c012017-06-18 22:08:58 -0600524 return structs
525
Simon Glass2be282c2017-06-18 22:08:59 -0600526 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600527 """Figure out what phandles each node uses
528
529 We need to be careful when outputing nodes that use phandles since
530 they must come after the declaration of the phandles in the C file.
531 Otherwise we get a compiler error since the phandle struct is not yet
532 declared.
533
534 This function adds to each node a list of phandle nodes that the node
535 depends on. This allows us to output things in the right order.
536 """
537 for node in self._valid_nodes:
538 node.phandles = set()
539 for pname, prop in node.props.items():
540 if pname in PROP_IGNORE_LIST or pname[0] == '#':
541 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600542 info = self.get_phandle_argc(prop, node.name)
543 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600544 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600545 pos = 0
546 for args in info.args:
547 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600548 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
549 target_node = self._fdt.phandle_to_node[phandle]
550 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600551 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600552
553
Simon Glass2be282c2017-06-18 22:08:59 -0600554 def generate_structs(self, structs):
Simon Glass7581c012017-06-18 22:08:58 -0600555 """Generate struct defintions for the platform data
556
557 This writes out the body of a header file consisting of structure
558 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100559 doc/driver-model/of-plat.rst for more information.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600560
561 Args:
562 structs: dict containing structures:
563 key (str): Node name, as a C identifier
564 value: dict containing structure fields:
565 key (str): Field name
566 value: Prop object with field information
567
Simon Glass7581c012017-06-18 22:08:58 -0600568 """
Simon Glassd5031142017-08-29 14:16:01 -0600569 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600570 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900571 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600572
573 # Output the struct definition
574 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600575 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600576 for pname in sorted(structs[name]):
577 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600578 info = self.get_phandle_argc(prop, structs[name])
579 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600580 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600581 struct_name = 'struct phandle_%d_arg' % info.max_args
582 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600583 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600584 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600585 else:
586 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600587 self.out('\t%s%s' % (tab_to(2, ptype),
588 conv_name_to_c(prop.name)))
589 if isinstance(prop.value, list):
590 self.out('[%d]' % len(prop.value))
591 self.out(';\n')
592 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600593
Simon Glass2be282c2017-06-18 22:08:59 -0600594 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600595 """Output the C code for a node
596
597 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700598 node (fdt.Node): node to output
Simon Glass7581c012017-06-18 22:08:58 -0600599 """
Simon Glass26e408f2020-10-03 09:25:18 -0600600 def _output_list(node, prop):
601 """Output the C code for a devicetree property that holds a list
602
603 Args:
604 node (fdt.Node): Node to output
605 prop (fdt.Prop): Prop to output
606 """
607 self.buf('{')
608 vals = []
609 # For phandles, output a reference to the platform data
610 # of the target node.
611 info = self.get_phandle_argc(prop, node.name)
612 if info:
613 # Process the list as pairs of (phandle, id)
614 pos = 0
Simon Glass26e408f2020-10-03 09:25:18 -0600615 for args in info.args:
616 phandle_cell = prop.value[pos]
617 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
618 target_node = self._fdt.phandle_to_node[phandle]
Simon Glass26e408f2020-10-03 09:25:18 -0600619 arg_values = []
620 for i in range(args):
Simon Glass8a38abf2020-10-03 11:31:40 -0600621 arg_values.append(
622 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
Simon Glass26e408f2020-10-03 09:25:18 -0600623 pos += 1 + args
Simon Glass8a38abf2020-10-03 11:31:40 -0600624 vals.append('\t{%d, {%s}}' % (target_node.idx,
625 ', '.join(arg_values)))
Simon Glass26e408f2020-10-03 09:25:18 -0600626 for val in vals:
627 self.buf('\n\t\t%s,' % val)
628 else:
629 for val in prop.value:
630 vals.append(get_value(prop.type, val))
631
632 # Put 8 values per line to avoid very long lines.
633 for i in range(0, len(vals), 8):
634 if i:
635 self.buf(',\n\t\t')
636 self.buf(', '.join(vals[i:i + 8]))
637 self.buf('}')
638
Walter Lozanodac82282020-07-03 08:07:17 -0300639 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600640 var_name = conv_name_to_c(node.name)
Simon Glass1b272732020-10-03 11:31:25 -0600641 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Walter Lozano51f12632020-06-25 01:10:13 -0300642 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass2be282c2017-06-18 22:08:59 -0600643 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glass1953ce72019-05-17 22:00:32 -0600644 for pname in sorted(node.props):
645 prop = node.props[pname]
Simon Glass7581c012017-06-18 22:08:58 -0600646 if pname in PROP_IGNORE_LIST or pname[0] == '#':
647 continue
Simon Glass2be282c2017-06-18 22:08:59 -0600648 member_name = conv_name_to_c(prop.name)
649 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
Simon Glass7581c012017-06-18 22:08:58 -0600650
651 # Special handling for lists
Simon Glass2be282c2017-06-18 22:08:59 -0600652 if isinstance(prop.value, list):
Simon Glass26e408f2020-10-03 09:25:18 -0600653 _output_list(node, prop)
Simon Glass7581c012017-06-18 22:08:58 -0600654 else:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600655 self.buf(get_value(prop.type, prop.value))
Simon Glass2be282c2017-06-18 22:08:59 -0600656 self.buf(',\n')
657 self.buf('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600658
659 # Add a device declaration
Simon Glass2be282c2017-06-18 22:08:59 -0600660 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
661 self.buf('\t.name\t\t= "%s",\n' % struct_name)
Simon Glasscaa4daa2020-12-03 16:55:18 -0700662 self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, var_name))
Simon Glass4f500862020-12-03 16:55:19 -0700663 self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
Simon Glasse41651f2020-10-03 11:31:35 -0600664 idx = -1
665 if node.parent and node.parent in self._valid_nodes:
666 idx = node.parent.idx
667 self.buf('\t.parent_idx\t= %d,\n' % idx)
Simon Glass2be282c2017-06-18 22:08:59 -0600668 self.buf('};\n')
669 self.buf('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600670
Simon Glass2be282c2017-06-18 22:08:59 -0600671 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600672
Simon Glass2be282c2017-06-18 22:08:59 -0600673 def generate_tables(self):
Simon Glass7581c012017-06-18 22:08:58 -0600674 """Generate device defintions for the platform data
675
676 This writes out C platform data initialisation data and
677 U_BOOT_DEVICE() declarations for each valid node. Where a node has
678 multiple compatible strings, a #define is used to make them equivalent.
679
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100680 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600681 information.
682 """
Simon Glassd5031142017-08-29 14:16:01 -0600683 self.out_header()
Simon Glasscb43ac12020-10-03 11:31:41 -0600684 self.out('/* Allow use of U_BOOT_DEVICE() in this file */\n')
685 self.out('#define DT_PLATDATA_C\n')
686 self.out('\n')
Simon Glass2be282c2017-06-18 22:08:59 -0600687 self.out('#include <common.h>\n')
688 self.out('#include <dm.h>\n')
689 self.out('#include <dt-structs.h>\n')
690 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600691 nodes_to_output = list(self._valid_nodes)
692
693 # Keep outputing nodes until there is none left
694 while nodes_to_output:
695 node = nodes_to_output[0]
696 # Output all the node's dependencies first
697 for req_node in node.phandles:
698 if req_node in nodes_to_output:
Simon Glass2be282c2017-06-18 22:08:59 -0600699 self.output_node(req_node)
Simon Glass7581c012017-06-18 22:08:58 -0600700 nodes_to_output.remove(req_node)
Simon Glass2be282c2017-06-18 22:08:59 -0600701 self.output_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600702 nodes_to_output.remove(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600703
Walter Lozano51f12632020-06-25 01:10:13 -0300704 # Define dm_populate_phandle_data() which will add the linking between
705 # nodes using DM_GET_DEVICE
706 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
707 self.buf('void dm_populate_phandle_data(void) {\n')
Walter Lozano51f12632020-06-25 01:10:13 -0300708 self.buf('}\n')
709
710 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600711
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300712def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
Simon Glass78128d52020-12-03 16:55:16 -0700713 drivers_additional=None):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600714 """Run all the steps of the dtoc tool
715
716 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700717 args (list): List of non-option arguments provided to the problem
718 dtb_file (str): Filename of dtb file to process
719 include_disabled (bool): True to include disabled nodes
720 output (str): Name of output file
Simon Glass78128d52020-12-03 16:55:16 -0700721 warning_disabled (bool): True to avoid showing warnings about missing
722 drivers
723 _drivers_additional (list): List of additional drivers to use during
724 scanning
Simon Glass9b330382020-11-08 20:36:21 -0700725 Raises:
726 ValueError: if args has no command, or an unknown command
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600727 """
728 if not args:
729 raise ValueError('Please specify a command: struct, platdata')
730
Simon Glass78128d52020-12-03 16:55:16 -0700731 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled,
732 drivers_additional)
Walter Lozanodac82282020-07-03 08:07:17 -0300733 plat.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600734 plat.scan_dtb()
735 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600736 plat.scan_reg_sizes()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600737 plat.setup_output(output)
738 structs = plat.scan_structs()
739 plat.scan_phandles()
740
741 for cmd in args[0].split(','):
742 if cmd == 'struct':
743 plat.generate_structs(structs)
744 elif cmd == 'platdata':
745 plat.generate_tables()
746 else:
747 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
748 cmd)