blob: 6b10eabafb9e8931d554add38b27f207783100d8 [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 Glassfc0056e2020-11-08 20:36:18 -0700117 ch = value[0]
Simon Glass9b330382020-11-08 20:36:21 -0700118 return '%#x' % (ord(ch) if isinstance(ch, str) else ch)
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,
166 drivers_additional=[]):
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 = {}
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300176 self._drivers_additional = drivers_additional
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
Walter Lozanodac82282020-07-03 08:07:17 -0300315 def scan_driver(self, fn):
316 """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
321 fn: Driver filename to scan
322 """
323 with open(fn, encoding='utf-8') as fd:
324 try:
325 buff = fd.read()
326 except UnicodeDecodeError:
327 # This seems to happen on older Python versions
328 print("Skipping file '%s' due to unicode error" % fn)
329 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)
340 driver_aliases = re.findall('U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
341 buff)
342
343 for alias in driver_aliases: # pragma: no cover
344 if len(alias) != 2:
345 continue
346 self._driver_aliases[alias[1]] = alias[0]
347
348 def scan_drivers(self):
349 """Scan the driver folders to build a list of driver names and aliases
350
351 This procedure will populate self._drivers and self._driver_aliases
352
353 """
354 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
355 if basedir == '':
356 basedir = './'
357 for (dirpath, dirnames, filenames) in os.walk(basedir):
358 for fn in filenames:
359 if not fn.endswith('.c'):
360 continue
361 self.scan_driver(dirpath + '/' + fn)
362
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300363 for fn in self._drivers_additional:
364 if not isinstance(fn, str) or len(fn) == 0:
365 continue
366 if fn[0] == '/':
367 self.scan_driver(fn)
368 else:
369 self.scan_driver(basedir + '/' + fn)
370
Simon Glass2be282c2017-06-18 22:08:59 -0600371 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200372 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600373
Simon Glass2be282c2017-06-18 22:08:59 -0600374 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600375 device tree root node, and progress from there.
376 """
Simon Glass2be282c2017-06-18 22:08:59 -0600377 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600378
Simon Glass1b272732020-10-03 11:31:25 -0600379 def scan_node(self, root, valid_nodes):
Simon Glass2be282c2017-06-18 22:08:59 -0600380 """Scan a node and subnodes to build a tree of node and phandle info
381
Simon Glass72ab7c52017-08-29 14:15:53 -0600382 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600383
384 Args:
385 root: Root node for scan
Simon Glass1b272732020-10-03 11:31:25 -0600386 valid_nodes: List of Node objects to add to
Simon Glass2be282c2017-06-18 22:08:59 -0600387 """
Simon Glass7581c012017-06-18 22:08:58 -0600388 for node in root.subnodes:
389 if 'compatible' in node.props:
390 status = node.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600391 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600392 status.value != 'disabled'):
Simon Glass1b272732020-10-03 11:31:25 -0600393 valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600394
395 # recurse to handle any subnodes
Simon Glass1b272732020-10-03 11:31:25 -0600396 self.scan_node(node, valid_nodes)
Simon Glass7581c012017-06-18 22:08:58 -0600397
Simon Glass2be282c2017-06-18 22:08:59 -0600398 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600399 """Scan the device tree for useful information
400
401 This fills in the following properties:
Simon Glass7581c012017-06-18 22:08:58 -0600402 _valid_nodes: A list of nodes we wish to consider include in the
403 platform data
404 """
Simon Glass1b272732020-10-03 11:31:25 -0600405 valid_nodes = []
406 self.scan_node(self._fdt.GetRoot(), valid_nodes)
407 self._valid_nodes = sorted(valid_nodes,
408 key=lambda x: conv_name_to_c(x.name))
409 for idx, node in enumerate(self._valid_nodes):
410 node.idx = idx
Simon Glass7581c012017-06-18 22:08:58 -0600411
Simon Glassc20ee0e2017-08-29 14:15:50 -0600412 @staticmethod
413 def get_num_cells(node):
414 """Get the number of cells in addresses and sizes for this node
415
416 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700417 node (fdt.None): Node to check
Simon Glassc20ee0e2017-08-29 14:15:50 -0600418
419 Returns:
420 Tuple:
421 Number of address cells for this node
422 Number of size cells for this node
423 """
424 parent = node.parent
425 na, ns = 2, 2
426 if parent:
427 na_prop = parent.props.get('#address-cells')
428 ns_prop = parent.props.get('#size-cells')
429 if na_prop:
430 na = fdt_util.fdt32_to_cpu(na_prop.value)
431 if ns_prop:
432 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
433 return na, ns
434
435 def scan_reg_sizes(self):
436 """Scan for 64-bit 'reg' properties and update the values
437
438 This finds 'reg' properties with 64-bit data and converts the value to
439 an array of 64-values. This allows it to be output in a way that the
440 C code can read.
441 """
442 for node in self._valid_nodes:
443 reg = node.props.get('reg')
444 if not reg:
445 continue
446 na, ns = self.get_num_cells(node)
447 total = na + ns
448
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700449 if reg.type != fdt.Type.INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600450 raise ValueError("Node '%s' reg property is not an int" %
451 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600452 if len(reg.value) % total:
Simon Glass9b330382020-11-08 20:36:21 -0700453 raise ValueError(
454 "Node '%s' reg property has %d cells "
455 'which is not a multiple of na + ns = %d + %d)' %
456 (node.name, len(reg.value), na, ns))
Simon Glassc20ee0e2017-08-29 14:15:50 -0600457 reg.na = na
458 reg.ns = ns
459 if na != 1 or ns != 1:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700460 reg.type = fdt.Type.INT64
Simon Glassc20ee0e2017-08-29 14:15:50 -0600461 i = 0
462 new_value = []
463 val = reg.value
464 if not isinstance(val, list):
465 val = [val]
466 while i < len(val):
467 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
468 i += na
469 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
470 i += ns
471 new_value += [addr, size]
472 reg.value = new_value
473
Simon Glass2be282c2017-06-18 22:08:59 -0600474 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600475 """Scan the device tree building up the C structures we will use.
476
477 Build a dict keyed by C struct name containing a dict of Prop
478 object for each struct field (keyed by property name). Where the
479 same struct appears multiple times, try to use the 'widest'
480 property, i.e. the one with a type which can express all others.
481
482 Once the widest property is determined, all other properties are
483 updated to match that width.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600484
485 Returns:
486 dict containing structures:
487 key (str): Node name, as a C identifier
488 value: dict containing structure fields:
489 key (str): Field name
490 value: Prop object with field information
Simon Glass7581c012017-06-18 22:08:58 -0600491 """
Simon Glass1b272732020-10-03 11:31:25 -0600492 structs = collections.OrderedDict()
Simon Glass7581c012017-06-18 22:08:58 -0600493 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300494 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600495 fields = {}
496
497 # Get a list of all the valid properties in this node.
498 for name, prop in node.props.items():
499 if name not in PROP_IGNORE_LIST and name[0] != '#':
500 fields[name] = copy.deepcopy(prop)
501
502 # If we've seen this node_name before, update the existing struct.
503 if node_name in structs:
504 struct = structs[node_name]
505 for name, prop in fields.items():
506 oldprop = struct.get(name)
507 if oldprop:
508 oldprop.Widen(prop)
509 else:
510 struct[name] = prop
511
512 # Otherwise store this as a new struct.
513 else:
514 structs[node_name] = fields
515
516 upto = 0
517 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])
523 upto += 1
524
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:
563 structs: dict containing structures:
564 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 Glass2be282c2017-06-18 22:08:59 -0600595 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600596 """Output the C code for a node
597
598 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700599 node (fdt.Node): node to output
Simon Glass7581c012017-06-18 22:08:58 -0600600 """
Simon Glass26e408f2020-10-03 09:25:18 -0600601 def _output_list(node, prop):
602 """Output the C code for a devicetree property that holds a list
603
604 Args:
605 node (fdt.Node): Node to output
606 prop (fdt.Prop): Prop to output
607 """
608 self.buf('{')
609 vals = []
610 # For phandles, output a reference to the platform data
611 # of the target node.
612 info = self.get_phandle_argc(prop, node.name)
613 if info:
614 # Process the list as pairs of (phandle, id)
615 pos = 0
616 item = 0
617 for args in info.args:
618 phandle_cell = prop.value[pos]
619 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
620 target_node = self._fdt.phandle_to_node[phandle]
621 name = conv_name_to_c(target_node.name)
622 arg_values = []
623 for i in range(args):
Simon Glass8a38abf2020-10-03 11:31:40 -0600624 arg_values.append(
625 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
Simon Glass26e408f2020-10-03 09:25:18 -0600626 pos += 1 + args
Simon Glass8a38abf2020-10-03 11:31:40 -0600627 vals.append('\t{%d, {%s}}' % (target_node.idx,
628 ', '.join(arg_values)))
Simon Glass26e408f2020-10-03 09:25:18 -0600629 item += 1
630 for val in vals:
631 self.buf('\n\t\t%s,' % val)
632 else:
633 for val in prop.value:
634 vals.append(get_value(prop.type, val))
635
636 # Put 8 values per line to avoid very long lines.
637 for i in range(0, len(vals), 8):
638 if i:
639 self.buf(',\n\t\t')
640 self.buf(', '.join(vals[i:i + 8]))
641 self.buf('}')
642
Walter Lozanodac82282020-07-03 08:07:17 -0300643 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600644 var_name = conv_name_to_c(node.name)
Simon Glass1b272732020-10-03 11:31:25 -0600645 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Walter Lozano51f12632020-06-25 01:10:13 -0300646 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass2be282c2017-06-18 22:08:59 -0600647 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glass1953ce72019-05-17 22:00:32 -0600648 for pname in sorted(node.props):
649 prop = node.props[pname]
Simon Glass7581c012017-06-18 22:08:58 -0600650 if pname in PROP_IGNORE_LIST or pname[0] == '#':
651 continue
Simon Glass2be282c2017-06-18 22:08:59 -0600652 member_name = conv_name_to_c(prop.name)
653 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
Simon Glass7581c012017-06-18 22:08:58 -0600654
655 # Special handling for lists
Simon Glass2be282c2017-06-18 22:08:59 -0600656 if isinstance(prop.value, list):
Simon Glass26e408f2020-10-03 09:25:18 -0600657 _output_list(node, prop)
Simon Glass7581c012017-06-18 22:08:58 -0600658 else:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600659 self.buf(get_value(prop.type, prop.value))
Simon Glass2be282c2017-06-18 22:08:59 -0600660 self.buf(',\n')
661 self.buf('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600662
663 # Add a device declaration
Simon Glass2be282c2017-06-18 22:08:59 -0600664 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
665 self.buf('\t.name\t\t= "%s",\n' % struct_name)
666 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
667 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
Simon Glasse41651f2020-10-03 11:31:35 -0600668 idx = -1
669 if node.parent and node.parent in self._valid_nodes:
670 idx = node.parent.idx
671 self.buf('\t.parent_idx\t= %d,\n' % idx)
Simon Glass2be282c2017-06-18 22:08:59 -0600672 self.buf('};\n')
673 self.buf('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600674
Simon Glass2be282c2017-06-18 22:08:59 -0600675 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600676
Simon Glass2be282c2017-06-18 22:08:59 -0600677 def generate_tables(self):
Simon Glass7581c012017-06-18 22:08:58 -0600678 """Generate device defintions for the platform data
679
680 This writes out C platform data initialisation data and
681 U_BOOT_DEVICE() declarations for each valid node. Where a node has
682 multiple compatible strings, a #define is used to make them equivalent.
683
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100684 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600685 information.
686 """
Simon Glassd5031142017-08-29 14:16:01 -0600687 self.out_header()
Simon Glasscb43ac12020-10-03 11:31:41 -0600688 self.out('/* Allow use of U_BOOT_DEVICE() in this file */\n')
689 self.out('#define DT_PLATDATA_C\n')
690 self.out('\n')
Simon Glass2be282c2017-06-18 22:08:59 -0600691 self.out('#include <common.h>\n')
692 self.out('#include <dm.h>\n')
693 self.out('#include <dt-structs.h>\n')
694 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600695 nodes_to_output = list(self._valid_nodes)
696
697 # Keep outputing nodes until there is none left
698 while nodes_to_output:
699 node = nodes_to_output[0]
700 # Output all the node's dependencies first
701 for req_node in node.phandles:
702 if req_node in nodes_to_output:
Simon Glass2be282c2017-06-18 22:08:59 -0600703 self.output_node(req_node)
Simon Glass7581c012017-06-18 22:08:58 -0600704 nodes_to_output.remove(req_node)
Simon Glass2be282c2017-06-18 22:08:59 -0600705 self.output_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600706 nodes_to_output.remove(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600707
Walter Lozano51f12632020-06-25 01:10:13 -0300708 # Define dm_populate_phandle_data() which will add the linking between
709 # nodes using DM_GET_DEVICE
710 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
711 self.buf('void dm_populate_phandle_data(void) {\n')
Walter Lozano51f12632020-06-25 01:10:13 -0300712 self.buf('}\n')
713
714 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600715
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300716def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
717 drivers_additional=[]):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600718 """Run all the steps of the dtoc tool
719
720 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700721 args (list): List of non-option arguments provided to the problem
722 dtb_file (str): Filename of dtb file to process
723 include_disabled (bool): True to include disabled nodes
724 output (str): Name of output file
725 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
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300731 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional)
Walter Lozanodac82282020-07-03 08:07:17 -0300732 plat.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600733 plat.scan_dtb()
734 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600735 plat.scan_reg_sizes()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600736 plat.setup_output(output)
737 structs = plat.scan_structs()
738 plat.scan_phandles()
739
740 for cmd in args[0].split(','):
741 if cmd == 'struct':
742 plat.generate_structs(structs)
743 elif cmd == 'platdata':
744 plat.generate_tables()
745 else:
746 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
747 cmd)