blob: 1d89f77a00bd49baf6c11c47d6bd2971b1fb61da [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 Glassccc3da72020-12-23 08:11:19 -0700115 val = '%#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]
Simon Glassccc3da72020-12-23 08:11:19 -0700118 val = '%#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
Simon Glassccc3da72020-12-23 08:11:19 -0700122 val = '"%s"' % value.replace('\\', '\\\\')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700123 elif ftype == fdt.Type.BOOL:
Simon Glassccc3da72020-12-23 08:11:19 -0700124 val = 'true'
Simon Glass9b330382020-11-08 20:36:21 -0700125 else: # ftype == fdt.Type.INT64:
Simon Glassccc3da72020-12-23 08:11:19 -0700126 val = '%#x' % value
127 return val
Simon Glass56e0bbe2017-06-18 22:09:02 -0600128
129def get_compat_name(node):
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300130 """Get the node's list of compatible string as a C identifiers
Simon Glass56e0bbe2017-06-18 22:09:02 -0600131
132 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700133 node (fdt.Node): Node object to check
Simon Glass56e0bbe2017-06-18 22:09:02 -0600134 Return:
Simon Glassccc3da72020-12-23 08:11:19 -0700135 list of str: List of C identifiers for all the compatible strings
Simon Glass56e0bbe2017-06-18 22:09:02 -0600136 """
137 compat = node.props['compatible'].value
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300138 if not isinstance(compat, list):
139 compat = [compat]
140 return [conv_name_to_c(c) for c in compat]
Simon Glass56e0bbe2017-06-18 22:09:02 -0600141
Simon Glass56e0bbe2017-06-18 22:09:02 -0600142
Simon Glassccc3da72020-12-23 08:11:19 -0700143class DtbPlatdata():
Simon Glass7581c012017-06-18 22:08:58 -0600144 """Provide a means to convert device tree binary data to platform data
145
146 The output of this process is C structures which can be used in space-
147 constrained encvironments where the ~3KB code overhead of device tree
148 code is not affordable.
149
150 Properties:
Simon Glass2be282c2017-06-18 22:08:59 -0600151 _fdt: Fdt object, referencing the device tree
Simon Glass7581c012017-06-18 22:08:58 -0600152 _dtb_fname: Filename of the input device tree binary file
Simon Glass1b272732020-10-03 11:31:25 -0600153 _valid_nodes: A list of Node object with compatible strings. The list
154 is ordered by conv_name_to_c(node.name)
Simon Glasse36024b2017-06-18 22:09:01 -0600155 _include_disabled: true to include nodes marked status = "disabled"
Simon Glass7581c012017-06-18 22:08:58 -0600156 _outfile: The current output file (sys.stdout or a real file)
Walter Lozano361e7332020-06-25 01:10:08 -0300157 _warning_disabled: true to disable warnings about driver names not found
Simon Glass7581c012017-06-18 22:08:58 -0600158 _lines: Stashed list of output lines for outputting in the future
Walter Lozanodac82282020-07-03 08:07:17 -0300159 _drivers: List of valid driver names found in drivers/
160 _driver_aliases: Dict that holds aliases for driver names
161 key: Driver alias declared with
162 U_BOOT_DRIVER_ALIAS(driver_alias, driver_name)
163 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300164 _drivers_additional: List of additional drivers to use during scanning
Simon Glass7581c012017-06-18 22:08:58 -0600165 """
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300166 def __init__(self, dtb_fname, include_disabled, warning_disabled,
Simon Glass78128d52020-12-03 16:55:16 -0700167 drivers_additional=None):
Simon Glass2be282c2017-06-18 22:08:59 -0600168 self._fdt = None
Simon Glass7581c012017-06-18 22:08:58 -0600169 self._dtb_fname = dtb_fname
170 self._valid_nodes = None
Simon Glasse36024b2017-06-18 22:09:01 -0600171 self._include_disabled = include_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600172 self._outfile = None
Walter Lozano361e7332020-06-25 01:10:08 -0300173 self._warning_disabled = warning_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600174 self._lines = []
Walter Lozanodac82282020-07-03 08:07:17 -0300175 self._drivers = []
176 self._driver_aliases = {}
Simon Glass78128d52020-12-03 16:55:16 -0700177 self._drivers_additional = drivers_additional or []
Walter Lozanodac82282020-07-03 08:07:17 -0300178
179 def get_normalized_compat_name(self, node):
180 """Get a node's normalized compat name
181
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300182 Returns a valid driver name by retrieving node's list of compatible
Walter Lozanodac82282020-07-03 08:07:17 -0300183 string as a C identifier and performing a check against _drivers
184 and a lookup in driver_aliases printing a warning in case of failure.
185
186 Args:
Simon Glassccc3da72020-12-23 08:11:19 -0700187 node (Node): Node object to check
Walter Lozanodac82282020-07-03 08:07:17 -0300188 Return:
189 Tuple:
190 Driver name associated with the first compatible string
191 List of C identifiers for all the other compatible strings
192 (possibly empty)
193 In case of no match found, the return will be the same as
194 get_compat_name()
195 """
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300196 compat_list_c = get_compat_name(node)
Walter Lozanodac82282020-07-03 08:07:17 -0300197
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300198 for compat_c in compat_list_c:
199 if not compat_c in self._drivers:
200 compat_c = self._driver_aliases.get(compat_c)
201 if not compat_c:
202 continue
203
204 aliases_c = compat_list_c
205 if compat_c in aliases_c:
206 aliases_c.remove(compat_c)
207 return compat_c, aliases_c
208
209 if not self._warning_disabled:
210 print('WARNING: the driver %s was not found in the driver list'
211 % (compat_list_c[0]))
212
213 return compat_list_c[0], compat_list_c[1:]
Simon Glass7581c012017-06-18 22:08:58 -0600214
Simon Glass2be282c2017-06-18 22:08:59 -0600215 def setup_output(self, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600216 """Set up the output destination
217
Simon Glass2be282c2017-06-18 22:08:59 -0600218 Once this is done, future calls to self.out() will output to this
Simon Glass7581c012017-06-18 22:08:58 -0600219 file.
220
221 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700222 fname (str): Filename to send output to, or '-' for stdout
Simon Glass7581c012017-06-18 22:08:58 -0600223 """
224 if fname == '-':
225 self._outfile = sys.stdout
226 else:
227 self._outfile = open(fname, 'w')
228
Simon Glass2be282c2017-06-18 22:08:59 -0600229 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600230 """Output a string to the output file
231
232 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700233 line (str): String to output
Simon Glass7581c012017-06-18 22:08:58 -0600234 """
Simon Glass2be282c2017-06-18 22:08:59 -0600235 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600236
Simon Glass2be282c2017-06-18 22:08:59 -0600237 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600238 """Buffer up a string to send later
239
240 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700241 line (str): String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600242 """
Simon Glass2be282c2017-06-18 22:08:59 -0600243 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600244
Simon Glass2be282c2017-06-18 22:08:59 -0600245 def get_buf(self):
Simon Glass7581c012017-06-18 22:08:58 -0600246 """Get the contents of the output buffer, and clear it
247
248 Returns:
Simon Glass9b330382020-11-08 20:36:21 -0700249 list(str): The output buffer, which is then cleared for future use
Simon Glass7581c012017-06-18 22:08:58 -0600250 """
251 lines = self._lines
252 self._lines = []
253 return lines
254
Simon Glassd5031142017-08-29 14:16:01 -0600255 def out_header(self):
256 """Output a message indicating that this is an auto-generated file"""
257 self.out('''/*
258 * DO NOT MODIFY
259 *
260 * This file was generated by dtoc from a .dtb (device tree binary) file.
261 */
262
263''')
264
Simon Glass8fed2eb2017-08-29 14:15:55 -0600265 def get_phandle_argc(self, prop, node_name):
266 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600267
Simon Glass8fed2eb2017-08-29 14:15:55 -0600268 We have no reliable way of detecting whether a node uses a phandle
269 or not. As an interim measure, use a list of known property names.
Simon Glass2925c262017-08-29 14:15:54 -0600270
Simon Glass8fed2eb2017-08-29 14:15:55 -0600271 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700272 prop (fdt.Prop): Prop object to check
273 node_name (str): Node name, only used for raising an error
274 Returns:
275 int or None: Number of argument cells is this is a phandle,
276 else None
277 Raises:
278 ValueError: if the phandle cannot be parsed or the required property
279 is not present
Simon Glass8fed2eb2017-08-29 14:15:55 -0600280 """
Walter Lozanoad340172020-06-25 01:10:16 -0300281 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass760b7172018-07-06 10:27:31 -0600282 if not isinstance(prop.value, list):
283 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600284 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600285 i = 0
286
287 max_args = 0
288 args = []
289 while i < len(val):
290 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-07-06 10:27:31 -0600291 # If we get to the end of the list, stop. This can happen
292 # since some nodes have more phandles in the list than others,
293 # but we allocate enough space for the largest list. So those
294 # nodes with shorter lists end up with zeroes at the end.
295 if not phandle:
296 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600297 target = self._fdt.phandle_to_node.get(phandle)
298 if not target:
299 raise ValueError("Cannot parse '%s' in node '%s'" %
300 (prop.name, node_name))
Walter Lozanoad340172020-06-25 01:10:16 -0300301 cells = None
302 for prop_name in ['#clock-cells', '#gpio-cells']:
303 cells = target.props.get(prop_name)
304 if cells:
305 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600306 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300307 raise ValueError("Node '%s' has no cells property" %
Simon Glass9b330382020-11-08 20:36:21 -0700308 (target.name))
Simon Glass8fed2eb2017-08-29 14:15:55 -0600309 num_args = fdt_util.fdt32_to_cpu(cells.value)
310 max_args = max(max_args, num_args)
311 args.append(num_args)
312 i += 1 + num_args
313 return PhandleInfo(max_args, args)
314 return None
Simon Glass2925c262017-08-29 14:15:54 -0600315
Simon Glass78128d52020-12-03 16:55:16 -0700316 def scan_driver(self, fname):
Walter Lozanodac82282020-07-03 08:07:17 -0300317 """Scan a driver file to build a list of driver names and aliases
318
319 This procedure will populate self._drivers and self._driver_aliases
320
321 Args
Simon Glass78128d52020-12-03 16:55:16 -0700322 fname: Driver filename to scan
Walter Lozanodac82282020-07-03 08:07:17 -0300323 """
Simon Glass78128d52020-12-03 16:55:16 -0700324 with open(fname, encoding='utf-8') as inf:
Walter Lozanodac82282020-07-03 08:07:17 -0300325 try:
Simon Glass78128d52020-12-03 16:55:16 -0700326 buff = inf.read()
Walter Lozanodac82282020-07-03 08:07:17 -0300327 except UnicodeDecodeError:
328 # This seems to happen on older Python versions
Simon Glass78128d52020-12-03 16:55:16 -0700329 print("Skipping file '%s' due to unicode error" % fname)
Walter Lozanodac82282020-07-03 08:07:17 -0300330 return
331
332 # The following re will search for driver names declared as
333 # U_BOOT_DRIVER(driver_name)
Simon Glassccc3da72020-12-23 08:11:19 -0700334 drivers = re.findall(r'U_BOOT_DRIVER\((.*)\)', buff)
Walter Lozanodac82282020-07-03 08:07:17 -0300335
336 for driver in drivers:
337 self._drivers.append(driver)
338
339 # The following re will search for driver aliases declared as
340 # U_BOOT_DRIVER_ALIAS(alias, driver_name)
Simon Glass78128d52020-12-03 16:55:16 -0700341 driver_aliases = re.findall(
Simon Glassccc3da72020-12-23 08:11:19 -0700342 r'U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
Simon Glass78128d52020-12-03 16:55:16 -0700343 buff)
Walter Lozanodac82282020-07-03 08:07:17 -0300344
345 for alias in driver_aliases: # pragma: no cover
346 if len(alias) != 2:
347 continue
348 self._driver_aliases[alias[1]] = alias[0]
349
350 def scan_drivers(self):
351 """Scan the driver folders to build a list of driver names and aliases
352
353 This procedure will populate self._drivers and self._driver_aliases
354
355 """
356 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
357 if basedir == '':
358 basedir = './'
Simon Glass78128d52020-12-03 16:55:16 -0700359 for (dirpath, _, filenames) in os.walk(basedir):
360 for fname in filenames:
361 if not fname.endswith('.c'):
Walter Lozanodac82282020-07-03 08:07:17 -0300362 continue
Simon Glass78128d52020-12-03 16:55:16 -0700363 self.scan_driver(dirpath + '/' + fname)
Walter Lozanodac82282020-07-03 08:07:17 -0300364
Simon Glass78128d52020-12-03 16:55:16 -0700365 for fname in self._drivers_additional:
366 if not isinstance(fname, str) or len(fname) == 0:
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300367 continue
Simon Glass78128d52020-12-03 16:55:16 -0700368 if fname[0] == '/':
369 self.scan_driver(fname)
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300370 else:
Simon Glass78128d52020-12-03 16:55:16 -0700371 self.scan_driver(basedir + '/' + fname)
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300372
Simon Glass2be282c2017-06-18 22:08:59 -0600373 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200374 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600375
Simon Glass2be282c2017-06-18 22:08:59 -0600376 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600377 device tree root node, and progress from there.
378 """
Simon Glass2be282c2017-06-18 22:08:59 -0600379 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600380
Simon Glass1b272732020-10-03 11:31:25 -0600381 def scan_node(self, root, valid_nodes):
Simon Glass2be282c2017-06-18 22:08:59 -0600382 """Scan a node and subnodes to build a tree of node and phandle info
383
Simon Glass72ab7c52017-08-29 14:15:53 -0600384 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600385
386 Args:
Simon Glassccc3da72020-12-23 08:11:19 -0700387 root (Node): Root node for scan
388 valid_nodes (list of Node): List of Node objects to add to
Simon Glass2be282c2017-06-18 22:08:59 -0600389 """
Simon Glass7581c012017-06-18 22:08:58 -0600390 for node in root.subnodes:
391 if 'compatible' in node.props:
392 status = node.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600393 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600394 status.value != 'disabled'):
Simon Glass1b272732020-10-03 11:31:25 -0600395 valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600396
397 # recurse to handle any subnodes
Simon Glass1b272732020-10-03 11:31:25 -0600398 self.scan_node(node, valid_nodes)
Simon Glass7581c012017-06-18 22:08:58 -0600399
Simon Glass2be282c2017-06-18 22:08:59 -0600400 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600401 """Scan the device tree for useful information
402
403 This fills in the following properties:
Simon Glass7581c012017-06-18 22:08:58 -0600404 _valid_nodes: A list of nodes we wish to consider include in the
405 platform data
406 """
Simon Glass1b272732020-10-03 11:31:25 -0600407 valid_nodes = []
408 self.scan_node(self._fdt.GetRoot(), valid_nodes)
409 self._valid_nodes = sorted(valid_nodes,
410 key=lambda x: conv_name_to_c(x.name))
411 for idx, node in enumerate(self._valid_nodes):
412 node.idx = idx
Simon Glass7581c012017-06-18 22:08:58 -0600413
Simon Glassc20ee0e2017-08-29 14:15:50 -0600414 @staticmethod
415 def get_num_cells(node):
416 """Get the number of cells in addresses and sizes for this node
417
418 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700419 node (fdt.None): Node to check
Simon Glassc20ee0e2017-08-29 14:15:50 -0600420
421 Returns:
422 Tuple:
423 Number of address cells for this node
424 Number of size cells for this node
425 """
426 parent = node.parent
Simon Glass78128d52020-12-03 16:55:16 -0700427 num_addr, num_size = 2, 2
Simon Glassc20ee0e2017-08-29 14:15:50 -0600428 if parent:
Simon Glass78128d52020-12-03 16:55:16 -0700429 addr_prop = parent.props.get('#address-cells')
430 size_prop = parent.props.get('#size-cells')
431 if addr_prop:
432 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
433 if size_prop:
434 num_size = fdt_util.fdt32_to_cpu(size_prop.value)
435 return num_addr, num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600436
437 def scan_reg_sizes(self):
438 """Scan for 64-bit 'reg' properties and update the values
439
440 This finds 'reg' properties with 64-bit data and converts the value to
441 an array of 64-values. This allows it to be output in a way that the
442 C code can read.
443 """
444 for node in self._valid_nodes:
445 reg = node.props.get('reg')
446 if not reg:
447 continue
Simon Glass78128d52020-12-03 16:55:16 -0700448 num_addr, num_size = self.get_num_cells(node)
449 total = num_addr + num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600450
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700451 if reg.type != fdt.Type.INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600452 raise ValueError("Node '%s' reg property is not an int" %
453 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600454 if len(reg.value) % total:
Simon Glass9b330382020-11-08 20:36:21 -0700455 raise ValueError(
456 "Node '%s' reg property has %d cells "
457 'which is not a multiple of na + ns = %d + %d)' %
Simon Glass78128d52020-12-03 16:55:16 -0700458 (node.name, len(reg.value), num_addr, num_size))
459 reg.num_addr = num_addr
460 reg.num_size = num_size
461 if num_addr != 1 or num_size != 1:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700462 reg.type = fdt.Type.INT64
Simon Glassc20ee0e2017-08-29 14:15:50 -0600463 i = 0
464 new_value = []
465 val = reg.value
466 if not isinstance(val, list):
467 val = [val]
468 while i < len(val):
Simon Glass78128d52020-12-03 16:55:16 -0700469 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
470 i += num_addr
471 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
472 i += num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600473 new_value += [addr, size]
474 reg.value = new_value
475
Simon Glass2be282c2017-06-18 22:08:59 -0600476 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600477 """Scan the device tree building up the C structures we will use.
478
479 Build a dict keyed by C struct name containing a dict of Prop
480 object for each struct field (keyed by property name). Where the
481 same struct appears multiple times, try to use the 'widest'
482 property, i.e. the one with a type which can express all others.
483
484 Once the widest property is determined, all other properties are
485 updated to match that width.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600486
487 Returns:
Simon Glassccc3da72020-12-23 08:11:19 -0700488 dict of dict: dict containing structures:
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600489 key (str): Node name, as a C identifier
490 value: dict containing structure fields:
491 key (str): Field name
492 value: Prop object with field information
Simon Glass7581c012017-06-18 22:08:58 -0600493 """
Simon Glass1b272732020-10-03 11:31:25 -0600494 structs = collections.OrderedDict()
Simon Glass7581c012017-06-18 22:08:58 -0600495 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300496 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600497 fields = {}
498
499 # Get a list of all the valid properties in this node.
500 for name, prop in node.props.items():
501 if name not in PROP_IGNORE_LIST and name[0] != '#':
502 fields[name] = copy.deepcopy(prop)
503
504 # If we've seen this node_name before, update the existing struct.
505 if node_name in structs:
506 struct = structs[node_name]
507 for name, prop in fields.items():
508 oldprop = struct.get(name)
509 if oldprop:
510 oldprop.Widen(prop)
511 else:
512 struct[name] = prop
513
514 # Otherwise store this as a new struct.
515 else:
516 structs[node_name] = fields
517
Simon Glass7581c012017-06-18 22:08:58 -0600518 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300519 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600520 struct = structs[node_name]
521 for name, prop in node.props.items():
522 if name not in PROP_IGNORE_LIST and name[0] != '#':
523 prop.Widen(struct[name])
Simon Glass7581c012017-06-18 22:08:58 -0600524
Simon Glass7581c012017-06-18 22:08:58 -0600525 return structs
526
Simon Glass2be282c2017-06-18 22:08:59 -0600527 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600528 """Figure out what phandles each node uses
529
530 We need to be careful when outputing nodes that use phandles since
531 they must come after the declaration of the phandles in the C file.
532 Otherwise we get a compiler error since the phandle struct is not yet
533 declared.
534
535 This function adds to each node a list of phandle nodes that the node
536 depends on. This allows us to output things in the right order.
537 """
538 for node in self._valid_nodes:
539 node.phandles = set()
540 for pname, prop in node.props.items():
541 if pname in PROP_IGNORE_LIST or pname[0] == '#':
542 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600543 info = self.get_phandle_argc(prop, node.name)
544 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600545 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600546 pos = 0
547 for args in info.args:
548 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600549 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
550 target_node = self._fdt.phandle_to_node[phandle]
551 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600552 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600553
554
Simon Glass2be282c2017-06-18 22:08:59 -0600555 def generate_structs(self, structs):
Simon Glass7581c012017-06-18 22:08:58 -0600556 """Generate struct defintions for the platform data
557
558 This writes out the body of a header file consisting of structure
559 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100560 doc/driver-model/of-plat.rst for more information.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600561
562 Args:
Simon Glassccc3da72020-12-23 08:11:19 -0700563 structs (dict): dict containing structures:
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600564 key (str): Node name, as a C identifier
565 value: dict containing structure fields:
566 key (str): Field name
567 value: Prop object with field information
568
Simon Glass7581c012017-06-18 22:08:58 -0600569 """
Simon Glassd5031142017-08-29 14:16:01 -0600570 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600571 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900572 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600573
574 # Output the struct definition
575 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600576 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600577 for pname in sorted(structs[name]):
578 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600579 info = self.get_phandle_argc(prop, structs[name])
580 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600581 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600582 struct_name = 'struct phandle_%d_arg' % info.max_args
583 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600584 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600585 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600586 else:
587 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600588 self.out('\t%s%s' % (tab_to(2, ptype),
589 conv_name_to_c(prop.name)))
590 if isinstance(prop.value, list):
591 self.out('[%d]' % len(prop.value))
592 self.out(';\n')
593 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600594
Simon Glassabf0c802020-12-23 08:11:20 -0700595 def _output_list(self, node, prop):
596 """Output the C code for a devicetree property that holds a list
597
598 Args:
599 node (fdt.Node): Node to output
600 prop (fdt.Prop): Prop to output
601 """
602 self.buf('{')
603 vals = []
604 # For phandles, output a reference to the platform data
605 # of the target node.
606 info = self.get_phandle_argc(prop, node.name)
607 if info:
608 # Process the list as pairs of (phandle, id)
609 pos = 0
610 for args in info.args:
611 phandle_cell = prop.value[pos]
612 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
613 target_node = self._fdt.phandle_to_node[phandle]
614 arg_values = []
615 for i in range(args):
616 arg_values.append(
617 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
618 pos += 1 + args
619 vals.append('\t{%d, {%s}}' % (target_node.idx,
620 ', '.join(arg_values)))
621 for val in vals:
622 self.buf('\n\t\t%s,' % val)
623 else:
624 for val in prop.value:
625 vals.append(get_value(prop.type, val))
626
627 # Put 8 values per line to avoid very long lines.
628 for i in range(0, len(vals), 8):
629 if i:
630 self.buf(',\n\t\t')
631 self.buf(', '.join(vals[i:i + 8]))
632 self.buf('}')
633
Simon Glass221ddc12020-12-23 08:11:21 -0700634 def _declare_device(self, var_name, struct_name, node_parent):
635 """Add a device declaration to the output
636
637 This declares a U_BOOT_DEVICE() for the device being processed
638
639 Args:
640 var_name (str): C name for the node
641 struct_name (str): Name for the dt struct associated with the node
642 node_parent (Node): Parent of the node (or None if none)
643 """
644 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
645 self.buf('\t.name\t\t= "%s",\n' % struct_name)
646 self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, var_name))
647 self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
648 idx = -1
649 if node_parent and node_parent in self._valid_nodes:
650 idx = node_parent.idx
651 self.buf('\t.parent_idx\t= %d,\n' % idx)
652 self.buf('};\n')
653 self.buf('\n')
654
Simon Glass161dac12020-12-23 08:11:22 -0700655 def _output_prop(self, node, prop):
656 """Output a line containing the value of a struct member
657
658 Args:
659 node (Node): Node being output
660 prop (Prop): Prop object to output
661 """
662 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
663 return
664 member_name = conv_name_to_c(prop.name)
665 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
666
667 # Special handling for lists
668 if isinstance(prop.value, list):
669 self._output_list(node, prop)
670 else:
671 self.buf(get_value(prop.type, prop.value))
672 self.buf(',\n')
673
674 def _output_values(self, var_name, struct_name, node):
675 """Output the definition of a device's struct values
676
677 Args:
678 var_name (str): C name for the node
679 struct_name (str): Name for the dt struct associated with the node
680 node (Node): Node being output
681 """
682 self.buf('static struct %s%s %s%s = {\n' %
683 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
684 for pname in sorted(node.props):
685 self._output_prop(node, node.props[pname])
686 self.buf('};\n')
687
Simon Glass2be282c2017-06-18 22:08:59 -0600688 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600689 """Output the C code for a node
690
691 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700692 node (fdt.Node): node to output
Simon Glass7581c012017-06-18 22:08:58 -0600693 """
Walter Lozanodac82282020-07-03 08:07:17 -0300694 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600695 var_name = conv_name_to_c(node.name)
Simon Glass1b272732020-10-03 11:31:25 -0600696 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Simon Glass7581c012017-06-18 22:08:58 -0600697
Simon Glass161dac12020-12-23 08:11:22 -0700698 self._output_values(var_name, struct_name, node)
Simon Glass221ddc12020-12-23 08:11:21 -0700699 self._declare_device(var_name, struct_name, node.parent)
Simon Glass7581c012017-06-18 22:08:58 -0600700
Simon Glass2be282c2017-06-18 22:08:59 -0600701 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600702
Simon Glass2be282c2017-06-18 22:08:59 -0600703 def generate_tables(self):
Simon Glass7581c012017-06-18 22:08:58 -0600704 """Generate device defintions for the platform data
705
706 This writes out C platform data initialisation data and
707 U_BOOT_DEVICE() declarations for each valid node. Where a node has
708 multiple compatible strings, a #define is used to make them equivalent.
709
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100710 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600711 information.
712 """
Simon Glassd5031142017-08-29 14:16:01 -0600713 self.out_header()
Simon Glasscb43ac12020-10-03 11:31:41 -0600714 self.out('/* Allow use of U_BOOT_DEVICE() in this file */\n')
715 self.out('#define DT_PLATDATA_C\n')
716 self.out('\n')
Simon Glass2be282c2017-06-18 22:08:59 -0600717 self.out('#include <common.h>\n')
718 self.out('#include <dm.h>\n')
719 self.out('#include <dt-structs.h>\n')
720 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600721 nodes_to_output = list(self._valid_nodes)
722
723 # Keep outputing nodes until there is none left
724 while nodes_to_output:
725 node = nodes_to_output[0]
726 # Output all the node's dependencies first
727 for req_node in node.phandles:
728 if req_node in nodes_to_output:
Simon Glass2be282c2017-06-18 22:08:59 -0600729 self.output_node(req_node)
Simon Glass7581c012017-06-18 22:08:58 -0600730 nodes_to_output.remove(req_node)
Simon Glass2be282c2017-06-18 22:08:59 -0600731 self.output_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600732 nodes_to_output.remove(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600733
Walter Lozano51f12632020-06-25 01:10:13 -0300734 # Define dm_populate_phandle_data() which will add the linking between
735 # nodes using DM_GET_DEVICE
736 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
737 self.buf('void dm_populate_phandle_data(void) {\n')
Walter Lozano51f12632020-06-25 01:10:13 -0300738 self.buf('}\n')
739
740 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600741
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300742def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
Simon Glass78128d52020-12-03 16:55:16 -0700743 drivers_additional=None):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600744 """Run all the steps of the dtoc tool
745
746 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700747 args (list): List of non-option arguments provided to the problem
748 dtb_file (str): Filename of dtb file to process
749 include_disabled (bool): True to include disabled nodes
750 output (str): Name of output file
Simon Glass78128d52020-12-03 16:55:16 -0700751 warning_disabled (bool): True to avoid showing warnings about missing
752 drivers
Simon Glassccc3da72020-12-23 08:11:19 -0700753 drivers_additional (list): List of additional drivers to use during
Simon Glass78128d52020-12-03 16:55:16 -0700754 scanning
Simon Glass9b330382020-11-08 20:36:21 -0700755 Raises:
756 ValueError: if args has no command, or an unknown command
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600757 """
758 if not args:
759 raise ValueError('Please specify a command: struct, platdata')
760
Simon Glass78128d52020-12-03 16:55:16 -0700761 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled,
762 drivers_additional)
Walter Lozanodac82282020-07-03 08:07:17 -0300763 plat.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600764 plat.scan_dtb()
765 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600766 plat.scan_reg_sizes()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600767 plat.setup_output(output)
768 structs = plat.scan_structs()
769 plat.scan_phandles()
770
771 for cmd in args[0].split(','):
772 if cmd == 'struct':
773 plat.generate_structs(structs)
774 elif cmd == 'platdata':
775 plat.generate_tables()
776 else:
777 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
778 cmd)