blob: 5b1bb7e5fd9ea82115cdbd34f1a50e4682784b0c [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
Walter Lozanoe675d962020-07-03 08:07:17 -030018import os
19import re
Simon Glass1f730c82017-06-18 22:08:59 -060020import sys
Simon Glassd570dec2017-06-18 22:08:58 -060021
Simon Glassa997ea52020-04-17 18:09:04 -060022from dtoc import fdt
23from dtoc import fdt_util
Simon Glassd570dec2017-06-18 22:08:58 -060024
Simon Glass94ee3bd2020-11-08 20:36:21 -070025# When we see these properties we ignore them - i.e. do not create a structure
26# member
Simon Glassd570dec2017-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 Glassc9a032c2020-11-08 20:36:17 -070040# C type declarations for the types we support
Simon Glassd570dec2017-06-18 22:08:58 -060041TYPE_NAMES = {
Simon Glassc9a032c2020-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 Glass1f730c82017-06-18 22:08:59 -060047}
Simon Glassd570dec2017-06-18 22:08:58 -060048
49STRUCT_PREFIX = 'dtd_'
50VAL_PREFIX = 'dtv_'
51
Simon Glassec3b5e42017-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 Glassc1622112020-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 Glassec3b5e42017-08-29 14:15:55 -060066
Simon Glassb42ed512020-12-23 08:11:23 -070067class Driver:
68 """Information about a driver in U-Boot
69
70 Attributes:
71 name: Name of driver. For U_BOOT_DRIVER(x) this is 'x'
72 """
73 def __init__(self, name):
74 self.name = name
75
76 def __eq__(self, other):
77 return self.name == other.name
78
79 def __repr__(self):
80 return "Driver(name='%s')" % self.name
81
82
Simon Glass1f730c82017-06-18 22:08:59 -060083def conv_name_to_c(name):
Simon Glassd570dec2017-06-18 22:08:58 -060084 """Convert a device-tree name to a C identifier
85
Simon Glass2e0bf3f2017-06-18 22:09:04 -060086 This uses multiple replace() calls instead of re.sub() since it is faster
87 (400ms for 1m calls versus 1000ms for the 're' version).
88
Simon Glassd570dec2017-06-18 22:08:58 -060089 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -070090 name (str): Name to convert
Simon Glassd570dec2017-06-18 22:08:58 -060091 Return:
Simon Glass94ee3bd2020-11-08 20:36:21 -070092 str: String containing the C version of this name
Simon Glassd570dec2017-06-18 22:08:58 -060093 """
Simon Glass1f730c82017-06-18 22:08:59 -060094 new = name.replace('@', '_at_')
95 new = new.replace('-', '_')
96 new = new.replace(',', '_')
97 new = new.replace('.', '_')
Simon Glass1f730c82017-06-18 22:08:59 -060098 return new
Simon Glassd570dec2017-06-18 22:08:58 -060099
Simon Glass1f730c82017-06-18 22:08:59 -0600100def tab_to(num_tabs, line):
101 """Append tabs to a line of text to reach a tab stop.
Simon Glassd570dec2017-06-18 22:08:58 -0600102
Simon Glass1f730c82017-06-18 22:08:59 -0600103 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700104 num_tabs (int): Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
105 line (str): Line of text to append to
Simon Glass1f730c82017-06-18 22:08:59 -0600106
107 Returns:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700108 str: line with the correct number of tabs appeneded. If the line already
Simon Glass1f730c82017-06-18 22:08:59 -0600109 extends past that tab stop then a single space is appended.
110 """
111 if len(line) >= num_tabs * 8:
112 return line + ' '
113 return line + '\t' * (num_tabs - len(line) // 8)
114
Simon Glass7a2add12017-06-18 22:09:02 -0600115def get_value(ftype, value):
116 """Get a value as a C expression
117
118 For integers this returns a byte-swapped (little-endian) hex string
119 For bytes this returns a hex string, e.g. 0x12
120 For strings this returns a literal string enclosed in quotes
121 For booleans this return 'true'
122
123 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700124 ftype (fdt.Type): Data type (fdt_util)
125 value (bytes): Data value, as a string of bytes
126
127 Returns:
128 str: String representation of the value
Simon Glass7a2add12017-06-18 22:09:02 -0600129 """
Simon Glassc9a032c2020-11-08 20:36:17 -0700130 if ftype == fdt.Type.INT:
Simon Glass27511232020-12-23 08:11:19 -0700131 val = '%#x' % fdt_util.fdt32_to_cpu(value)
Simon Glassc9a032c2020-11-08 20:36:17 -0700132 elif ftype == fdt.Type.BYTE:
Simon Glass93daa012020-12-03 16:55:16 -0700133 char = value[0]
Simon Glass27511232020-12-23 08:11:19 -0700134 val = '%#x' % (ord(char) if isinstance(char, str) else char)
Simon Glassc9a032c2020-11-08 20:36:17 -0700135 elif ftype == fdt.Type.STRING:
Simon Glass7f5e2262020-07-07 21:32:06 -0600136 # Handle evil ACPI backslashes by adding another backslash before them.
137 # So "\\_SB.GPO0" in the device tree effectively stays like that in C
Simon Glass27511232020-12-23 08:11:19 -0700138 val = '"%s"' % value.replace('\\', '\\\\')
Simon Glassc9a032c2020-11-08 20:36:17 -0700139 elif ftype == fdt.Type.BOOL:
Simon Glass27511232020-12-23 08:11:19 -0700140 val = 'true'
Simon Glass94ee3bd2020-11-08 20:36:21 -0700141 else: # ftype == fdt.Type.INT64:
Simon Glass27511232020-12-23 08:11:19 -0700142 val = '%#x' % value
143 return val
Simon Glass7a2add12017-06-18 22:09:02 -0600144
145def get_compat_name(node):
Walter Lozano5fe734c2020-07-23 00:22:03 -0300146 """Get the node's list of compatible string as a C identifiers
Simon Glass7a2add12017-06-18 22:09:02 -0600147
148 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700149 node (fdt.Node): Node object to check
Simon Glass7a2add12017-06-18 22:09:02 -0600150 Return:
Simon Glass27511232020-12-23 08:11:19 -0700151 list of str: List of C identifiers for all the compatible strings
Simon Glass7a2add12017-06-18 22:09:02 -0600152 """
153 compat = node.props['compatible'].value
Walter Lozano5fe734c2020-07-23 00:22:03 -0300154 if not isinstance(compat, list):
155 compat = [compat]
156 return [conv_name_to_c(c) for c in compat]
Simon Glass7a2add12017-06-18 22:09:02 -0600157
Simon Glass7a2add12017-06-18 22:09:02 -0600158
Simon Glass27511232020-12-23 08:11:19 -0700159class DtbPlatdata():
Simon Glassd570dec2017-06-18 22:08:58 -0600160 """Provide a means to convert device tree binary data to platform data
161
162 The output of this process is C structures which can be used in space-
163 constrained encvironments where the ~3KB code overhead of device tree
164 code is not affordable.
165
166 Properties:
Simon Glass1f730c82017-06-18 22:08:59 -0600167 _fdt: Fdt object, referencing the device tree
Simon Glassd570dec2017-06-18 22:08:58 -0600168 _dtb_fname: Filename of the input device tree binary file
Simon Glass192f8132020-10-03 11:31:25 -0600169 _valid_nodes: A list of Node object with compatible strings. The list
170 is ordered by conv_name_to_c(node.name)
Simon Glasseab3f622017-06-18 22:09:01 -0600171 _include_disabled: true to include nodes marked status = "disabled"
Simon Glassd570dec2017-06-18 22:08:58 -0600172 _outfile: The current output file (sys.stdout or a real file)
Walter Lozanoa324e412020-06-25 01:10:08 -0300173 _warning_disabled: true to disable warnings about driver names not found
Simon Glassd570dec2017-06-18 22:08:58 -0600174 _lines: Stashed list of output lines for outputting in the future
Simon Glassb42ed512020-12-23 08:11:23 -0700175 _drivers: Dict of valid driver names found in drivers/
176 key: Driver name
177 value: Driver for that driver
Walter Lozanoe675d962020-07-03 08:07:17 -0300178 _driver_aliases: Dict that holds aliases for driver names
179 key: Driver alias declared with
180 U_BOOT_DRIVER_ALIAS(driver_alias, driver_name)
181 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Walter Lozanod82062b2020-07-28 19:06:23 -0300182 _drivers_additional: List of additional drivers to use during scanning
Simon Glassd570dec2017-06-18 22:08:58 -0600183 """
Walter Lozanod82062b2020-07-28 19:06:23 -0300184 def __init__(self, dtb_fname, include_disabled, warning_disabled,
Simon Glass93daa012020-12-03 16:55:16 -0700185 drivers_additional=None):
Simon Glass1f730c82017-06-18 22:08:59 -0600186 self._fdt = None
Simon Glassd570dec2017-06-18 22:08:58 -0600187 self._dtb_fname = dtb_fname
188 self._valid_nodes = None
Simon Glasseab3f622017-06-18 22:09:01 -0600189 self._include_disabled = include_disabled
Simon Glassd570dec2017-06-18 22:08:58 -0600190 self._outfile = None
Walter Lozanoa324e412020-06-25 01:10:08 -0300191 self._warning_disabled = warning_disabled
Simon Glassd570dec2017-06-18 22:08:58 -0600192 self._lines = []
Simon Glassb42ed512020-12-23 08:11:23 -0700193 self._drivers = {}
Walter Lozanoe675d962020-07-03 08:07:17 -0300194 self._driver_aliases = {}
Simon Glass93daa012020-12-03 16:55:16 -0700195 self._drivers_additional = drivers_additional or []
Walter Lozanoe675d962020-07-03 08:07:17 -0300196
197 def get_normalized_compat_name(self, node):
198 """Get a node's normalized compat name
199
Walter Lozano5fe734c2020-07-23 00:22:03 -0300200 Returns a valid driver name by retrieving node's list of compatible
Walter Lozanoe675d962020-07-03 08:07:17 -0300201 string as a C identifier and performing a check against _drivers
202 and a lookup in driver_aliases printing a warning in case of failure.
203
204 Args:
Simon Glass27511232020-12-23 08:11:19 -0700205 node (Node): Node object to check
Walter Lozanoe675d962020-07-03 08:07:17 -0300206 Return:
207 Tuple:
208 Driver name associated with the first compatible string
209 List of C identifiers for all the other compatible strings
210 (possibly empty)
211 In case of no match found, the return will be the same as
212 get_compat_name()
213 """
Walter Lozano5fe734c2020-07-23 00:22:03 -0300214 compat_list_c = get_compat_name(node)
215
216 for compat_c in compat_list_c:
Simon Glassb42ed512020-12-23 08:11:23 -0700217 if not compat_c in self._drivers.keys():
Walter Lozano5fe734c2020-07-23 00:22:03 -0300218 compat_c = self._driver_aliases.get(compat_c)
219 if not compat_c:
220 continue
221
222 aliases_c = compat_list_c
223 if compat_c in aliases_c:
224 aliases_c.remove(compat_c)
225 return compat_c, aliases_c
226
227 if not self._warning_disabled:
228 print('WARNING: the driver %s was not found in the driver list'
229 % (compat_list_c[0]))
Walter Lozanoe675d962020-07-03 08:07:17 -0300230
Walter Lozano5fe734c2020-07-23 00:22:03 -0300231 return compat_list_c[0], compat_list_c[1:]
Simon Glassd570dec2017-06-18 22:08:58 -0600232
Simon Glass1f730c82017-06-18 22:08:59 -0600233 def setup_output(self, fname):
Simon Glassd570dec2017-06-18 22:08:58 -0600234 """Set up the output destination
235
Simon Glass1f730c82017-06-18 22:08:59 -0600236 Once this is done, future calls to self.out() will output to this
Simon Glassd570dec2017-06-18 22:08:58 -0600237 file.
238
239 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700240 fname (str): Filename to send output to, or '-' for stdout
Simon Glassd570dec2017-06-18 22:08:58 -0600241 """
242 if fname == '-':
243 self._outfile = sys.stdout
244 else:
245 self._outfile = open(fname, 'w')
246
Simon Glass1f730c82017-06-18 22:08:59 -0600247 def out(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600248 """Output a string to the output file
249
250 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700251 line (str): String to output
Simon Glassd570dec2017-06-18 22:08:58 -0600252 """
Simon Glass1f730c82017-06-18 22:08:59 -0600253 self._outfile.write(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600254
Simon Glass1f730c82017-06-18 22:08:59 -0600255 def buf(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600256 """Buffer up a string to send later
257
258 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700259 line (str): String to add to our 'buffer' list
Simon Glassd570dec2017-06-18 22:08:58 -0600260 """
Simon Glass1f730c82017-06-18 22:08:59 -0600261 self._lines.append(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600262
Simon Glass1f730c82017-06-18 22:08:59 -0600263 def get_buf(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600264 """Get the contents of the output buffer, and clear it
265
266 Returns:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700267 list(str): The output buffer, which is then cleared for future use
Simon Glassd570dec2017-06-18 22:08:58 -0600268 """
269 lines = self._lines
270 self._lines = []
271 return lines
272
Simon Glass68d32c72017-08-29 14:16:01 -0600273 def out_header(self):
274 """Output a message indicating that this is an auto-generated file"""
275 self.out('''/*
276 * DO NOT MODIFY
277 *
278 * This file was generated by dtoc from a .dtb (device tree binary) file.
279 */
280
281''')
282
Simon Glassec3b5e42017-08-29 14:15:55 -0600283 def get_phandle_argc(self, prop, node_name):
284 """Check if a node contains phandles
Simon Glassce5621d2017-08-29 14:15:54 -0600285
Simon Glassec3b5e42017-08-29 14:15:55 -0600286 We have no reliable way of detecting whether a node uses a phandle
287 or not. As an interim measure, use a list of known property names.
288
289 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700290 prop (fdt.Prop): Prop object to check
291 node_name (str): Node name, only used for raising an error
292 Returns:
293 int or None: Number of argument cells is this is a phandle,
294 else None
295 Raises:
296 ValueError: if the phandle cannot be parsed or the required property
297 is not present
Simon Glassec3b5e42017-08-29 14:15:55 -0600298 """
Walter Lozano179f0b62020-06-25 01:10:16 -0300299 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass609e2b12018-07-06 10:27:31 -0600300 if not isinstance(prop.value, list):
301 prop.value = [prop.value]
Simon Glassec3b5e42017-08-29 14:15:55 -0600302 val = prop.value
Simon Glassec3b5e42017-08-29 14:15:55 -0600303 i = 0
Simon Glassce5621d2017-08-29 14:15:54 -0600304
Simon Glassec3b5e42017-08-29 14:15:55 -0600305 max_args = 0
306 args = []
307 while i < len(val):
308 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass609e2b12018-07-06 10:27:31 -0600309 # If we get to the end of the list, stop. This can happen
310 # since some nodes have more phandles in the list than others,
311 # but we allocate enough space for the largest list. So those
312 # nodes with shorter lists end up with zeroes at the end.
313 if not phandle:
314 break
Simon Glassec3b5e42017-08-29 14:15:55 -0600315 target = self._fdt.phandle_to_node.get(phandle)
316 if not target:
317 raise ValueError("Cannot parse '%s' in node '%s'" %
318 (prop.name, node_name))
Walter Lozano179f0b62020-06-25 01:10:16 -0300319 cells = None
320 for prop_name in ['#clock-cells', '#gpio-cells']:
321 cells = target.props.get(prop_name)
322 if cells:
323 break
Simon Glassec3b5e42017-08-29 14:15:55 -0600324 if not cells:
Walter Lozano179f0b62020-06-25 01:10:16 -0300325 raise ValueError("Node '%s' has no cells property" %
Simon Glass94ee3bd2020-11-08 20:36:21 -0700326 (target.name))
Simon Glassec3b5e42017-08-29 14:15:55 -0600327 num_args = fdt_util.fdt32_to_cpu(cells.value)
328 max_args = max(max_args, num_args)
329 args.append(num_args)
330 i += 1 + num_args
331 return PhandleInfo(max_args, args)
332 return None
Simon Glassce5621d2017-08-29 14:15:54 -0600333
Simon Glass93daa012020-12-03 16:55:16 -0700334 def scan_driver(self, fname):
Walter Lozanoe675d962020-07-03 08:07:17 -0300335 """Scan a driver file to build a list of driver names and aliases
336
337 This procedure will populate self._drivers and self._driver_aliases
338
339 Args
Simon Glass93daa012020-12-03 16:55:16 -0700340 fname: Driver filename to scan
Walter Lozanoe675d962020-07-03 08:07:17 -0300341 """
Simon Glass93daa012020-12-03 16:55:16 -0700342 with open(fname, encoding='utf-8') as inf:
Walter Lozanoe675d962020-07-03 08:07:17 -0300343 try:
Simon Glass93daa012020-12-03 16:55:16 -0700344 buff = inf.read()
Walter Lozanoe675d962020-07-03 08:07:17 -0300345 except UnicodeDecodeError:
346 # This seems to happen on older Python versions
Simon Glass93daa012020-12-03 16:55:16 -0700347 print("Skipping file '%s' due to unicode error" % fname)
Walter Lozanoe675d962020-07-03 08:07:17 -0300348 return
349
350 # The following re will search for driver names declared as
351 # U_BOOT_DRIVER(driver_name)
Simon Glass27511232020-12-23 08:11:19 -0700352 drivers = re.findall(r'U_BOOT_DRIVER\((.*)\)', buff)
Walter Lozanoe675d962020-07-03 08:07:17 -0300353
354 for driver in drivers:
Simon Glassb42ed512020-12-23 08:11:23 -0700355 self._drivers[driver] = Driver(driver)
Walter Lozanoe675d962020-07-03 08:07:17 -0300356
357 # The following re will search for driver aliases declared as
358 # U_BOOT_DRIVER_ALIAS(alias, driver_name)
Simon Glass93daa012020-12-03 16:55:16 -0700359 driver_aliases = re.findall(
Simon Glass27511232020-12-23 08:11:19 -0700360 r'U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
Simon Glass93daa012020-12-03 16:55:16 -0700361 buff)
Walter Lozanoe675d962020-07-03 08:07:17 -0300362
363 for alias in driver_aliases: # pragma: no cover
364 if len(alias) != 2:
365 continue
366 self._driver_aliases[alias[1]] = alias[0]
367
368 def scan_drivers(self):
369 """Scan the driver folders to build a list of driver names and aliases
370
371 This procedure will populate self._drivers and self._driver_aliases
372
373 """
374 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
375 if basedir == '':
376 basedir = './'
Simon Glass93daa012020-12-03 16:55:16 -0700377 for (dirpath, _, filenames) in os.walk(basedir):
378 for fname in filenames:
379 if not fname.endswith('.c'):
Walter Lozanoe675d962020-07-03 08:07:17 -0300380 continue
Simon Glass93daa012020-12-03 16:55:16 -0700381 self.scan_driver(dirpath + '/' + fname)
Walter Lozanoe675d962020-07-03 08:07:17 -0300382
Simon Glass93daa012020-12-03 16:55:16 -0700383 for fname in self._drivers_additional:
384 if not isinstance(fname, str) or len(fname) == 0:
Walter Lozanod82062b2020-07-28 19:06:23 -0300385 continue
Simon Glass93daa012020-12-03 16:55:16 -0700386 if fname[0] == '/':
387 self.scan_driver(fname)
Walter Lozanod82062b2020-07-28 19:06:23 -0300388 else:
Simon Glass93daa012020-12-03 16:55:16 -0700389 self.scan_driver(basedir + '/' + fname)
Walter Lozanod82062b2020-07-28 19:06:23 -0300390
Simon Glass1f730c82017-06-18 22:08:59 -0600391 def scan_dtb(self):
Anatolij Gustschinda707d42017-08-18 17:58:51 +0200392 """Scan the device tree to obtain a tree of nodes and properties
Simon Glassd570dec2017-06-18 22:08:58 -0600393
Simon Glass1f730c82017-06-18 22:08:59 -0600394 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glassd570dec2017-06-18 22:08:58 -0600395 device tree root node, and progress from there.
396 """
Simon Glass1f730c82017-06-18 22:08:59 -0600397 self._fdt = fdt.FdtScan(self._dtb_fname)
398
Simon Glass192f8132020-10-03 11:31:25 -0600399 def scan_node(self, root, valid_nodes):
Simon Glass1f730c82017-06-18 22:08:59 -0600400 """Scan a node and subnodes to build a tree of node and phandle info
Simon Glassd570dec2017-06-18 22:08:58 -0600401
Simon Glass17ffd622017-08-29 14:15:53 -0600402 This adds each node to self._valid_nodes.
Simon Glass1f730c82017-06-18 22:08:59 -0600403
404 Args:
Simon Glass27511232020-12-23 08:11:19 -0700405 root (Node): Root node for scan
406 valid_nodes (list of Node): List of Node objects to add to
Simon Glass1f730c82017-06-18 22:08:59 -0600407 """
Simon Glassd570dec2017-06-18 22:08:58 -0600408 for node in root.subnodes:
409 if 'compatible' in node.props:
410 status = node.props.get('status')
Simon Glasseab3f622017-06-18 22:09:01 -0600411 if (not self._include_disabled and not status or
Simon Glass1f730c82017-06-18 22:08:59 -0600412 status.value != 'disabled'):
Simon Glass192f8132020-10-03 11:31:25 -0600413 valid_nodes.append(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600414
415 # recurse to handle any subnodes
Simon Glass192f8132020-10-03 11:31:25 -0600416 self.scan_node(node, valid_nodes)
Simon Glassd570dec2017-06-18 22:08:58 -0600417
Simon Glass1f730c82017-06-18 22:08:59 -0600418 def scan_tree(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600419 """Scan the device tree for useful information
420
421 This fills in the following properties:
Simon Glassd570dec2017-06-18 22:08:58 -0600422 _valid_nodes: A list of nodes we wish to consider include in the
423 platform data
424 """
Simon Glass192f8132020-10-03 11:31:25 -0600425 valid_nodes = []
426 self.scan_node(self._fdt.GetRoot(), valid_nodes)
427 self._valid_nodes = sorted(valid_nodes,
428 key=lambda x: conv_name_to_c(x.name))
429 for idx, node in enumerate(self._valid_nodes):
430 node.idx = idx
Simon Glassd570dec2017-06-18 22:08:58 -0600431
Simon Glass1b1fe412017-08-29 14:15:50 -0600432 @staticmethod
433 def get_num_cells(node):
434 """Get the number of cells in addresses and sizes for this node
435
436 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700437 node (fdt.None): Node to check
Simon Glass1b1fe412017-08-29 14:15:50 -0600438
439 Returns:
440 Tuple:
441 Number of address cells for this node
442 Number of size cells for this node
443 """
444 parent = node.parent
Simon Glass93daa012020-12-03 16:55:16 -0700445 num_addr, num_size = 2, 2
Simon Glass1b1fe412017-08-29 14:15:50 -0600446 if parent:
Simon Glass93daa012020-12-03 16:55:16 -0700447 addr_prop = parent.props.get('#address-cells')
448 size_prop = parent.props.get('#size-cells')
449 if addr_prop:
450 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
451 if size_prop:
452 num_size = fdt_util.fdt32_to_cpu(size_prop.value)
453 return num_addr, num_size
Simon Glass1b1fe412017-08-29 14:15:50 -0600454
455 def scan_reg_sizes(self):
456 """Scan for 64-bit 'reg' properties and update the values
457
458 This finds 'reg' properties with 64-bit data and converts the value to
459 an array of 64-values. This allows it to be output in a way that the
460 C code can read.
461 """
462 for node in self._valid_nodes:
463 reg = node.props.get('reg')
464 if not reg:
465 continue
Simon Glass93daa012020-12-03 16:55:16 -0700466 num_addr, num_size = self.get_num_cells(node)
467 total = num_addr + num_size
Simon Glass1b1fe412017-08-29 14:15:50 -0600468
Simon Glassc9a032c2020-11-08 20:36:17 -0700469 if reg.type != fdt.Type.INT:
Simon Glassc38fee042018-07-06 10:27:32 -0600470 raise ValueError("Node '%s' reg property is not an int" %
471 node.name)
Simon Glass1b1fe412017-08-29 14:15:50 -0600472 if len(reg.value) % total:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700473 raise ValueError(
474 "Node '%s' reg property has %d cells "
475 'which is not a multiple of na + ns = %d + %d)' %
Simon Glass93daa012020-12-03 16:55:16 -0700476 (node.name, len(reg.value), num_addr, num_size))
477 reg.num_addr = num_addr
478 reg.num_size = num_size
479 if num_addr != 1 or num_size != 1:
Simon Glassc9a032c2020-11-08 20:36:17 -0700480 reg.type = fdt.Type.INT64
Simon Glass1b1fe412017-08-29 14:15:50 -0600481 i = 0
482 new_value = []
483 val = reg.value
484 if not isinstance(val, list):
485 val = [val]
486 while i < len(val):
Simon Glass93daa012020-12-03 16:55:16 -0700487 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
488 i += num_addr
489 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
490 i += num_size
Simon Glass1b1fe412017-08-29 14:15:50 -0600491 new_value += [addr, size]
492 reg.value = new_value
493
Simon Glass1f730c82017-06-18 22:08:59 -0600494 def scan_structs(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600495 """Scan the device tree building up the C structures we will use.
496
497 Build a dict keyed by C struct name containing a dict of Prop
498 object for each struct field (keyed by property name). Where the
499 same struct appears multiple times, try to use the 'widest'
500 property, i.e. the one with a type which can express all others.
501
502 Once the widest property is determined, all other properties are
503 updated to match that width.
Simon Glass941f8f02020-10-03 11:31:24 -0600504
505 Returns:
Simon Glass27511232020-12-23 08:11:19 -0700506 dict of dict: dict containing structures:
Simon Glass941f8f02020-10-03 11:31:24 -0600507 key (str): Node name, as a C identifier
508 value: dict containing structure fields:
509 key (str): Field name
510 value: Prop object with field information
Simon Glassd570dec2017-06-18 22:08:58 -0600511 """
Simon Glass192f8132020-10-03 11:31:25 -0600512 structs = collections.OrderedDict()
Simon Glassd570dec2017-06-18 22:08:58 -0600513 for node in self._valid_nodes:
Walter Lozanoe675d962020-07-03 08:07:17 -0300514 node_name, _ = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600515 fields = {}
516
517 # Get a list of all the valid properties in this node.
518 for name, prop in node.props.items():
519 if name not in PROP_IGNORE_LIST and name[0] != '#':
520 fields[name] = copy.deepcopy(prop)
521
522 # If we've seen this node_name before, update the existing struct.
523 if node_name in structs:
524 struct = structs[node_name]
525 for name, prop in fields.items():
526 oldprop = struct.get(name)
527 if oldprop:
528 oldprop.Widen(prop)
529 else:
530 struct[name] = prop
531
532 # Otherwise store this as a new struct.
533 else:
534 structs[node_name] = fields
535
Simon Glassd570dec2017-06-18 22:08:58 -0600536 for node in self._valid_nodes:
Walter Lozanoe675d962020-07-03 08:07:17 -0300537 node_name, _ = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600538 struct = structs[node_name]
539 for name, prop in node.props.items():
540 if name not in PROP_IGNORE_LIST and name[0] != '#':
541 prop.Widen(struct[name])
Simon Glassd570dec2017-06-18 22:08:58 -0600542
Simon Glassd570dec2017-06-18 22:08:58 -0600543 return structs
544
Simon Glass1f730c82017-06-18 22:08:59 -0600545 def scan_phandles(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600546 """Figure out what phandles each node uses
547
548 We need to be careful when outputing nodes that use phandles since
549 they must come after the declaration of the phandles in the C file.
550 Otherwise we get a compiler error since the phandle struct is not yet
551 declared.
552
553 This function adds to each node a list of phandle nodes that the node
554 depends on. This allows us to output things in the right order.
555 """
556 for node in self._valid_nodes:
557 node.phandles = set()
558 for pname, prop in node.props.items():
559 if pname in PROP_IGNORE_LIST or pname[0] == '#':
560 continue
Simon Glassec3b5e42017-08-29 14:15:55 -0600561 info = self.get_phandle_argc(prop, node.name)
562 if info:
Simon Glassec3b5e42017-08-29 14:15:55 -0600563 # Process the list as pairs of (phandle, id)
Simon Glass3deeb472017-08-29 14:15:59 -0600564 pos = 0
565 for args in info.args:
566 phandle_cell = prop.value[pos]
Simon Glassec3b5e42017-08-29 14:15:55 -0600567 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
568 target_node = self._fdt.phandle_to_node[phandle]
569 node.phandles.add(target_node)
Simon Glass3deeb472017-08-29 14:15:59 -0600570 pos += 1 + args
Simon Glassd570dec2017-06-18 22:08:58 -0600571
572
Simon Glass1f730c82017-06-18 22:08:59 -0600573 def generate_structs(self, structs):
Simon Glassd570dec2017-06-18 22:08:58 -0600574 """Generate struct defintions for the platform data
575
576 This writes out the body of a header file consisting of structure
577 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +0100578 doc/driver-model/of-plat.rst for more information.
Simon Glass941f8f02020-10-03 11:31:24 -0600579
580 Args:
Simon Glass27511232020-12-23 08:11:19 -0700581 structs (dict): dict containing structures:
Simon Glass941f8f02020-10-03 11:31:24 -0600582 key (str): Node name, as a C identifier
583 value: dict containing structure fields:
584 key (str): Field name
585 value: Prop object with field information
586
Simon Glassd570dec2017-06-18 22:08:58 -0600587 """
Simon Glass68d32c72017-08-29 14:16:01 -0600588 self.out_header()
Simon Glass1f730c82017-06-18 22:08:59 -0600589 self.out('#include <stdbool.h>\n')
Masahiro Yamada75f82d02018-03-05 01:20:11 +0900590 self.out('#include <linux/libfdt.h>\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600591
592 # Output the struct definition
593 for name in sorted(structs):
Simon Glass1f730c82017-06-18 22:08:59 -0600594 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glassd570dec2017-06-18 22:08:58 -0600595 for pname in sorted(structs[name]):
596 prop = structs[name][pname]
Simon Glassec3b5e42017-08-29 14:15:55 -0600597 info = self.get_phandle_argc(prop, structs[name])
598 if info:
Simon Glassd570dec2017-06-18 22:08:58 -0600599 # For phandles, include a reference to the target
Simon Glasse94414b2017-08-29 14:15:56 -0600600 struct_name = 'struct phandle_%d_arg' % info.max_args
601 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass1f730c82017-06-18 22:08:59 -0600602 conv_name_to_c(prop.name),
Simon Glass3deeb472017-08-29 14:15:59 -0600603 len(info.args)))
Simon Glassd570dec2017-06-18 22:08:58 -0600604 else:
605 ptype = TYPE_NAMES[prop.type]
Simon Glass1f730c82017-06-18 22:08:59 -0600606 self.out('\t%s%s' % (tab_to(2, ptype),
607 conv_name_to_c(prop.name)))
608 if isinstance(prop.value, list):
609 self.out('[%d]' % len(prop.value))
610 self.out(';\n')
611 self.out('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600612
Simon Glass9d98b6e2020-12-23 08:11:20 -0700613 def _output_list(self, node, prop):
614 """Output the C code for a devicetree property that holds a list
Simon Glassd570dec2017-06-18 22:08:58 -0600615
616 Args:
Simon Glass9d98b6e2020-12-23 08:11:20 -0700617 node (fdt.Node): Node to output
618 prop (fdt.Prop): Prop to output
Simon Glassd570dec2017-06-18 22:08:58 -0600619 """
Simon Glass9d98b6e2020-12-23 08:11:20 -0700620 self.buf('{')
621 vals = []
622 # For phandles, output a reference to the platform data
623 # of the target node.
624 info = self.get_phandle_argc(prop, node.name)
625 if info:
626 # Process the list as pairs of (phandle, id)
627 pos = 0
628 for args in info.args:
629 phandle_cell = prop.value[pos]
630 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
631 target_node = self._fdt.phandle_to_node[phandle]
632 arg_values = []
633 for i in range(args):
634 arg_values.append(
635 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
636 pos += 1 + args
637 vals.append('\t{%d, {%s}}' % (target_node.idx,
638 ', '.join(arg_values)))
639 for val in vals:
640 self.buf('\n\t\t%s,' % val)
641 else:
642 for val in prop.value:
643 vals.append(get_value(prop.type, val))
Simon Glassbc9e2682020-10-03 09:25:18 -0600644
Simon Glass9d98b6e2020-12-23 08:11:20 -0700645 # Put 8 values per line to avoid very long lines.
646 for i in range(0, len(vals), 8):
647 if i:
648 self.buf(',\n\t\t')
649 self.buf(', '.join(vals[i:i + 8]))
650 self.buf('}')
Simon Glassbc9e2682020-10-03 09:25:18 -0600651
Simon Glass9fdd0c32020-12-23 08:11:21 -0700652 def _declare_device(self, var_name, struct_name, node_parent):
653 """Add a device declaration to the output
654
655 This declares a U_BOOT_DEVICE() for the device being processed
656
657 Args:
658 var_name (str): C name for the node
659 struct_name (str): Name for the dt struct associated with the node
660 node_parent (Node): Parent of the node (or None if none)
661 """
662 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
663 self.buf('\t.name\t\t= "%s",\n' % struct_name)
664 self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, var_name))
665 self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
666 idx = -1
667 if node_parent and node_parent in self._valid_nodes:
668 idx = node_parent.idx
669 self.buf('\t.parent_idx\t= %d,\n' % idx)
670 self.buf('};\n')
671 self.buf('\n')
672
Simon Glass9829eea2020-12-23 08:11:22 -0700673 def _output_prop(self, node, prop):
674 """Output a line containing the value of a struct member
675
676 Args:
677 node (Node): Node being output
678 prop (Prop): Prop object to output
679 """
680 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
681 return
682 member_name = conv_name_to_c(prop.name)
683 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
684
685 # Special handling for lists
686 if isinstance(prop.value, list):
687 self._output_list(node, prop)
688 else:
689 self.buf(get_value(prop.type, prop.value))
690 self.buf(',\n')
691
692 def _output_values(self, var_name, struct_name, node):
693 """Output the definition of a device's struct values
694
695 Args:
696 var_name (str): C name for the node
697 struct_name (str): Name for the dt struct associated with the node
698 node (Node): Node being output
699 """
700 self.buf('static struct %s%s %s%s = {\n' %
701 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
702 for pname in sorted(node.props):
703 self._output_prop(node, node.props[pname])
704 self.buf('};\n')
705
Simon Glass9d98b6e2020-12-23 08:11:20 -0700706 def output_node(self, node):
707 """Output the C code for a node
Simon Glassbc9e2682020-10-03 09:25:18 -0600708
Simon Glass9d98b6e2020-12-23 08:11:20 -0700709 Args:
710 node (fdt.Node): node to output
711 """
Walter Lozanoe675d962020-07-03 08:07:17 -0300712 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass1f730c82017-06-18 22:08:59 -0600713 var_name = conv_name_to_c(node.name)
Simon Glass192f8132020-10-03 11:31:25 -0600714 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Simon Glassd570dec2017-06-18 22:08:58 -0600715
Simon Glass9829eea2020-12-23 08:11:22 -0700716 self._output_values(var_name, struct_name, node)
Simon Glass9fdd0c32020-12-23 08:11:21 -0700717 self._declare_device(var_name, struct_name, node.parent)
Simon Glassd570dec2017-06-18 22:08:58 -0600718
Simon Glass1f730c82017-06-18 22:08:59 -0600719 self.out(''.join(self.get_buf()))
Simon Glassd570dec2017-06-18 22:08:58 -0600720
Simon Glass1f730c82017-06-18 22:08:59 -0600721 def generate_tables(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600722 """Generate device defintions for the platform data
723
724 This writes out C platform data initialisation data and
725 U_BOOT_DEVICE() declarations for each valid node. Where a node has
726 multiple compatible strings, a #define is used to make them equivalent.
727
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +0100728 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glassd570dec2017-06-18 22:08:58 -0600729 information.
730 """
Simon Glass68d32c72017-08-29 14:16:01 -0600731 self.out_header()
Simon Glass4c73d7b2020-10-03 11:31:41 -0600732 self.out('/* Allow use of U_BOOT_DEVICE() in this file */\n')
733 self.out('#define DT_PLATDATA_C\n')
734 self.out('\n')
Simon Glass1f730c82017-06-18 22:08:59 -0600735 self.out('#include <common.h>\n')
736 self.out('#include <dm.h>\n')
737 self.out('#include <dt-structs.h>\n')
738 self.out('\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600739 nodes_to_output = list(self._valid_nodes)
740
741 # Keep outputing nodes until there is none left
742 while nodes_to_output:
743 node = nodes_to_output[0]
744 # Output all the node's dependencies first
745 for req_node in node.phandles:
746 if req_node in nodes_to_output:
Simon Glass1f730c82017-06-18 22:08:59 -0600747 self.output_node(req_node)
Simon Glassd570dec2017-06-18 22:08:58 -0600748 nodes_to_output.remove(req_node)
Simon Glass1f730c82017-06-18 22:08:59 -0600749 self.output_node(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600750 nodes_to_output.remove(node)
Simon Glass3fa797a2017-06-18 22:09:03 -0600751
Walter Lozanodc5b4372020-06-25 01:10:13 -0300752 # Define dm_populate_phandle_data() which will add the linking between
753 # nodes using DM_GET_DEVICE
754 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
755 self.buf('void dm_populate_phandle_data(void) {\n')
Walter Lozanodc5b4372020-06-25 01:10:13 -0300756 self.buf('}\n')
757
758 self.out(''.join(self.get_buf()))
Simon Glass3fa797a2017-06-18 22:09:03 -0600759
Walter Lozanod82062b2020-07-28 19:06:23 -0300760def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
Simon Glass93daa012020-12-03 16:55:16 -0700761 drivers_additional=None):
Simon Glass3fa797a2017-06-18 22:09:03 -0600762 """Run all the steps of the dtoc tool
763
764 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700765 args (list): List of non-option arguments provided to the problem
766 dtb_file (str): Filename of dtb file to process
767 include_disabled (bool): True to include disabled nodes
768 output (str): Name of output file
Simon Glass93daa012020-12-03 16:55:16 -0700769 warning_disabled (bool): True to avoid showing warnings about missing
770 drivers
Simon Glass27511232020-12-23 08:11:19 -0700771 drivers_additional (list): List of additional drivers to use during
Simon Glass93daa012020-12-03 16:55:16 -0700772 scanning
Simon Glass94ee3bd2020-11-08 20:36:21 -0700773 Raises:
774 ValueError: if args has no command, or an unknown command
Simon Glass3fa797a2017-06-18 22:09:03 -0600775 """
776 if not args:
777 raise ValueError('Please specify a command: struct, platdata')
778
Simon Glass93daa012020-12-03 16:55:16 -0700779 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled,
780 drivers_additional)
Walter Lozanoe675d962020-07-03 08:07:17 -0300781 plat.scan_drivers()
Simon Glass3fa797a2017-06-18 22:09:03 -0600782 plat.scan_dtb()
783 plat.scan_tree()
Simon Glass1b1fe412017-08-29 14:15:50 -0600784 plat.scan_reg_sizes()
Simon Glass3fa797a2017-06-18 22:09:03 -0600785 plat.setup_output(output)
786 structs = plat.scan_structs()
787 plat.scan_phandles()
788
789 for cmd in args[0].split(','):
790 if cmd == 'struct':
791 plat.generate_structs(structs)
792 elif cmd == 'platdata':
793 plat.generate_tables()
794 else:
795 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
796 cmd)