blob: 31a9b3877ea1f119c119cebb097a7e24f9f3d5a6 [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.
12"""
13
Simon Glass8fed2eb2017-08-29 14:15:55 -060014import collections
Simon Glass7581c012017-06-18 22:08:58 -060015import copy
Walter Lozanodac82282020-07-03 08:07:17 -030016import os
17import re
Simon Glass2be282c2017-06-18 22:08:59 -060018import sys
Simon Glass7581c012017-06-18 22:08:58 -060019
Simon Glassbf776672020-04-17 18:09:04 -060020from dtoc import fdt
21from dtoc import fdt_util
22from patman import tools
Simon Glass7581c012017-06-18 22:08:58 -060023
24# When we see these properties we ignore them - i.e. do not create a structure member
25PROP_IGNORE_LIST = [
26 '#address-cells',
27 '#gpio-cells',
28 '#size-cells',
29 'compatible',
30 'linux,phandle',
31 "status",
32 'phandle',
33 'u-boot,dm-pre-reloc',
34 'u-boot,dm-tpl',
35 'u-boot,dm-spl',
36]
37
38# C type declarations for the tyues we support
39TYPE_NAMES = {
40 fdt.TYPE_INT: 'fdt32_t',
41 fdt.TYPE_BYTE: 'unsigned char',
42 fdt.TYPE_STRING: 'const char *',
43 fdt.TYPE_BOOL: 'bool',
Simon Glassfbdfd222017-08-29 14:15:48 -060044 fdt.TYPE_INT64: 'fdt64_t',
Simon Glass2be282c2017-06-18 22:08:59 -060045}
Simon Glass7581c012017-06-18 22:08:58 -060046
47STRUCT_PREFIX = 'dtd_'
48VAL_PREFIX = 'dtv_'
49
Simon Glass8fed2eb2017-08-29 14:15:55 -060050# This holds information about a property which includes phandles.
51#
52# max_args: integer: Maximum number or arguments that any phandle uses (int).
53# args: Number of args for each phandle in the property. The total number of
54# phandles is len(args). This is a list of integers.
55PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
56
Simon Glass97136eb2020-10-03 09:25:19 -060057# Holds a single phandle link, allowing a C struct value to be assigned to point
58# to a device
59#
60# var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node')
61# dev_name: Name of device to assign to (e.g. 'clock')
62PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name'])
63
Simon Glass8fed2eb2017-08-29 14:15:55 -060064
Simon Glass2be282c2017-06-18 22:08:59 -060065def conv_name_to_c(name):
Simon Glass7581c012017-06-18 22:08:58 -060066 """Convert a device-tree name to a C identifier
67
Simon Glass30107b02017-06-18 22:09:04 -060068 This uses multiple replace() calls instead of re.sub() since it is faster
69 (400ms for 1m calls versus 1000ms for the 're' version).
70
Simon Glass7581c012017-06-18 22:08:58 -060071 Args:
72 name: Name to convert
73 Return:
74 String containing the C version of this name
75 """
Simon Glass2be282c2017-06-18 22:08:59 -060076 new = name.replace('@', '_at_')
77 new = new.replace('-', '_')
78 new = new.replace(',', '_')
79 new = new.replace('.', '_')
Simon Glass2be282c2017-06-18 22:08:59 -060080 return new
Simon Glass7581c012017-06-18 22:08:58 -060081
Simon Glass2be282c2017-06-18 22:08:59 -060082def tab_to(num_tabs, line):
83 """Append tabs to a line of text to reach a tab stop.
Simon Glass7581c012017-06-18 22:08:58 -060084
Simon Glass2be282c2017-06-18 22:08:59 -060085 Args:
86 num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
87 line: Line of text to append to
88
89 Returns:
90 line with the correct number of tabs appeneded. If the line already
91 extends past that tab stop then a single space is appended.
92 """
93 if len(line) >= num_tabs * 8:
94 return line + ' '
95 return line + '\t' * (num_tabs - len(line) // 8)
96
Simon Glass56e0bbe2017-06-18 22:09:02 -060097def get_value(ftype, value):
98 """Get a value as a C expression
99
100 For integers this returns a byte-swapped (little-endian) hex string
101 For bytes this returns a hex string, e.g. 0x12
102 For strings this returns a literal string enclosed in quotes
103 For booleans this return 'true'
104
105 Args:
106 type: Data type (fdt_util)
107 value: Data value, as a string of bytes
108 """
109 if ftype == fdt.TYPE_INT:
110 return '%#x' % fdt_util.fdt32_to_cpu(value)
111 elif ftype == fdt.TYPE_BYTE:
Simon Glass9b044f72019-05-17 22:00:43 -0600112 return '%#x' % tools.ToByte(value[0])
Simon Glass56e0bbe2017-06-18 22:09:02 -0600113 elif ftype == fdt.TYPE_STRING:
Simon Glassf02d0eb2020-07-07 21:32:06 -0600114 # Handle evil ACPI backslashes by adding another backslash before them.
115 # So "\\_SB.GPO0" in the device tree effectively stays like that in C
116 return '"%s"' % value.replace('\\', '\\\\')
Simon Glass56e0bbe2017-06-18 22:09:02 -0600117 elif ftype == fdt.TYPE_BOOL:
118 return 'true'
Simon Glassfbdfd222017-08-29 14:15:48 -0600119 elif ftype == fdt.TYPE_INT64:
120 return '%#x' % value
Simon Glass56e0bbe2017-06-18 22:09:02 -0600121
122def get_compat_name(node):
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300123 """Get the node's list of compatible string as a C identifiers
Simon Glass56e0bbe2017-06-18 22:09:02 -0600124
125 Args:
126 node: Node object to check
127 Return:
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300128 List of C identifiers for all the compatible strings
Simon Glass56e0bbe2017-06-18 22:09:02 -0600129 """
130 compat = node.props['compatible'].value
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300131 if not isinstance(compat, list):
132 compat = [compat]
133 return [conv_name_to_c(c) for c in compat]
Simon Glass56e0bbe2017-06-18 22:09:02 -0600134
Simon Glass56e0bbe2017-06-18 22:09:02 -0600135
Simon Glass2be282c2017-06-18 22:08:59 -0600136class DtbPlatdata(object):
Simon Glass7581c012017-06-18 22:08:58 -0600137 """Provide a means to convert device tree binary data to platform data
138
139 The output of this process is C structures which can be used in space-
140 constrained encvironments where the ~3KB code overhead of device tree
141 code is not affordable.
142
143 Properties:
Simon Glass2be282c2017-06-18 22:08:59 -0600144 _fdt: Fdt object, referencing the device tree
Simon Glass7581c012017-06-18 22:08:58 -0600145 _dtb_fname: Filename of the input device tree binary file
Simon Glass1b272732020-10-03 11:31:25 -0600146 _valid_nodes: A list of Node object with compatible strings. The list
147 is ordered by conv_name_to_c(node.name)
Simon Glasse36024b2017-06-18 22:09:01 -0600148 _include_disabled: true to include nodes marked status = "disabled"
Simon Glass7581c012017-06-18 22:08:58 -0600149 _outfile: The current output file (sys.stdout or a real file)
Walter Lozano361e7332020-06-25 01:10:08 -0300150 _warning_disabled: true to disable warnings about driver names not found
Simon Glass7581c012017-06-18 22:08:58 -0600151 _lines: Stashed list of output lines for outputting in the future
Walter Lozanodac82282020-07-03 08:07:17 -0300152 _drivers: List of valid driver names found in drivers/
153 _driver_aliases: Dict that holds aliases for driver names
154 key: Driver alias declared with
155 U_BOOT_DRIVER_ALIAS(driver_alias, driver_name)
156 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Simon Glass97136eb2020-10-03 09:25:19 -0600157 _links: List of links to be included in dm_populate_phandle_data(),
158 each a PhandleLink
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300159 _drivers_additional: List of additional drivers to use during scanning
Simon Glass7581c012017-06-18 22:08:58 -0600160 """
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300161 def __init__(self, dtb_fname, include_disabled, warning_disabled,
162 drivers_additional=[]):
Simon Glass2be282c2017-06-18 22:08:59 -0600163 self._fdt = None
Simon Glass7581c012017-06-18 22:08:58 -0600164 self._dtb_fname = dtb_fname
165 self._valid_nodes = None
Simon Glasse36024b2017-06-18 22:09:01 -0600166 self._include_disabled = include_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600167 self._outfile = None
Walter Lozano361e7332020-06-25 01:10:08 -0300168 self._warning_disabled = warning_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600169 self._lines = []
Walter Lozanodac82282020-07-03 08:07:17 -0300170 self._drivers = []
171 self._driver_aliases = {}
Walter Lozano51f12632020-06-25 01:10:13 -0300172 self._links = []
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300173 self._drivers_additional = drivers_additional
Walter Lozanodac82282020-07-03 08:07:17 -0300174
175 def get_normalized_compat_name(self, node):
176 """Get a node's normalized compat name
177
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300178 Returns a valid driver name by retrieving node's list of compatible
Walter Lozanodac82282020-07-03 08:07:17 -0300179 string as a C identifier and performing a check against _drivers
180 and a lookup in driver_aliases printing a warning in case of failure.
181
182 Args:
183 node: Node object to check
184 Return:
185 Tuple:
186 Driver name associated with the first compatible string
187 List of C identifiers for all the other compatible strings
188 (possibly empty)
189 In case of no match found, the return will be the same as
190 get_compat_name()
191 """
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300192 compat_list_c = get_compat_name(node)
Walter Lozanodac82282020-07-03 08:07:17 -0300193
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300194 for compat_c in compat_list_c:
195 if not compat_c in self._drivers:
196 compat_c = self._driver_aliases.get(compat_c)
197 if not compat_c:
198 continue
199
200 aliases_c = compat_list_c
201 if compat_c in aliases_c:
202 aliases_c.remove(compat_c)
203 return compat_c, aliases_c
204
205 if not self._warning_disabled:
206 print('WARNING: the driver %s was not found in the driver list'
207 % (compat_list_c[0]))
208
209 return compat_list_c[0], compat_list_c[1:]
Simon Glass7581c012017-06-18 22:08:58 -0600210
Simon Glass2be282c2017-06-18 22:08:59 -0600211 def setup_output(self, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600212 """Set up the output destination
213
Simon Glass2be282c2017-06-18 22:08:59 -0600214 Once this is done, future calls to self.out() will output to this
Simon Glass7581c012017-06-18 22:08:58 -0600215 file.
216
217 Args:
218 fname: Filename to send output to, or '-' for stdout
219 """
220 if fname == '-':
221 self._outfile = sys.stdout
222 else:
223 self._outfile = open(fname, 'w')
224
Simon Glass2be282c2017-06-18 22:08:59 -0600225 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600226 """Output a string to the output file
227
228 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600229 line: String to output
Simon Glass7581c012017-06-18 22:08:58 -0600230 """
Simon Glass2be282c2017-06-18 22:08:59 -0600231 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600232
Simon Glass2be282c2017-06-18 22:08:59 -0600233 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600234 """Buffer up a string to send later
235
236 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600237 line: String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600238 """
Simon Glass2be282c2017-06-18 22:08:59 -0600239 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600240
Simon Glass2be282c2017-06-18 22:08:59 -0600241 def get_buf(self):
Simon Glass7581c012017-06-18 22:08:58 -0600242 """Get the contents of the output buffer, and clear it
243
244 Returns:
245 The output buffer, which is then cleared for future use
246 """
247 lines = self._lines
248 self._lines = []
249 return lines
250
Simon Glassd5031142017-08-29 14:16:01 -0600251 def out_header(self):
252 """Output a message indicating that this is an auto-generated file"""
253 self.out('''/*
254 * DO NOT MODIFY
255 *
256 * This file was generated by dtoc from a .dtb (device tree binary) file.
257 */
258
259''')
260
Simon Glass8fed2eb2017-08-29 14:15:55 -0600261 def get_phandle_argc(self, prop, node_name):
262 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600263
Simon Glass8fed2eb2017-08-29 14:15:55 -0600264 We have no reliable way of detecting whether a node uses a phandle
265 or not. As an interim measure, use a list of known property names.
Simon Glass2925c262017-08-29 14:15:54 -0600266
Simon Glass8fed2eb2017-08-29 14:15:55 -0600267 Args:
268 prop: Prop object to check
269 Return:
270 Number of argument cells is this is a phandle, else None
271 """
Walter Lozanoad340172020-06-25 01:10:16 -0300272 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass760b7172018-07-06 10:27:31 -0600273 if not isinstance(prop.value, list):
274 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600275 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600276 i = 0
277
278 max_args = 0
279 args = []
280 while i < len(val):
281 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-07-06 10:27:31 -0600282 # If we get to the end of the list, stop. This can happen
283 # since some nodes have more phandles in the list than others,
284 # but we allocate enough space for the largest list. So those
285 # nodes with shorter lists end up with zeroes at the end.
286 if not phandle:
287 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600288 target = self._fdt.phandle_to_node.get(phandle)
289 if not target:
290 raise ValueError("Cannot parse '%s' in node '%s'" %
291 (prop.name, node_name))
Walter Lozanoad340172020-06-25 01:10:16 -0300292 cells = None
293 for prop_name in ['#clock-cells', '#gpio-cells']:
294 cells = target.props.get(prop_name)
295 if cells:
296 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600297 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300298 raise ValueError("Node '%s' has no cells property" %
299 (target.name))
Simon Glass8fed2eb2017-08-29 14:15:55 -0600300 num_args = fdt_util.fdt32_to_cpu(cells.value)
301 max_args = max(max_args, num_args)
302 args.append(num_args)
303 i += 1 + num_args
304 return PhandleInfo(max_args, args)
305 return None
Simon Glass2925c262017-08-29 14:15:54 -0600306
Walter Lozanodac82282020-07-03 08:07:17 -0300307 def scan_driver(self, fn):
308 """Scan a driver file to build a list of driver names and aliases
309
310 This procedure will populate self._drivers and self._driver_aliases
311
312 Args
313 fn: Driver filename to scan
314 """
315 with open(fn, encoding='utf-8') as fd:
316 try:
317 buff = fd.read()
318 except UnicodeDecodeError:
319 # This seems to happen on older Python versions
320 print("Skipping file '%s' due to unicode error" % fn)
321 return
322
323 # The following re will search for driver names declared as
324 # U_BOOT_DRIVER(driver_name)
325 drivers = re.findall('U_BOOT_DRIVER\((.*)\)', buff)
326
327 for driver in drivers:
328 self._drivers.append(driver)
329
330 # The following re will search for driver aliases declared as
331 # U_BOOT_DRIVER_ALIAS(alias, driver_name)
332 driver_aliases = re.findall('U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
333 buff)
334
335 for alias in driver_aliases: # pragma: no cover
336 if len(alias) != 2:
337 continue
338 self._driver_aliases[alias[1]] = alias[0]
339
340 def scan_drivers(self):
341 """Scan the driver folders to build a list of driver names and aliases
342
343 This procedure will populate self._drivers and self._driver_aliases
344
345 """
346 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
347 if basedir == '':
348 basedir = './'
349 for (dirpath, dirnames, filenames) in os.walk(basedir):
350 for fn in filenames:
351 if not fn.endswith('.c'):
352 continue
353 self.scan_driver(dirpath + '/' + fn)
354
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300355 for fn in self._drivers_additional:
356 if not isinstance(fn, str) or len(fn) == 0:
357 continue
358 if fn[0] == '/':
359 self.scan_driver(fn)
360 else:
361 self.scan_driver(basedir + '/' + fn)
362
Simon Glass2be282c2017-06-18 22:08:59 -0600363 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200364 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600365
Simon Glass2be282c2017-06-18 22:08:59 -0600366 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600367 device tree root node, and progress from there.
368 """
Simon Glass2be282c2017-06-18 22:08:59 -0600369 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600370
Simon Glass1b272732020-10-03 11:31:25 -0600371 def scan_node(self, root, valid_nodes):
Simon Glass2be282c2017-06-18 22:08:59 -0600372 """Scan a node and subnodes to build a tree of node and phandle info
373
Simon Glass72ab7c52017-08-29 14:15:53 -0600374 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600375
376 Args:
377 root: Root node for scan
Simon Glass1b272732020-10-03 11:31:25 -0600378 valid_nodes: List of Node objects to add to
Simon Glass2be282c2017-06-18 22:08:59 -0600379 """
Simon Glass7581c012017-06-18 22:08:58 -0600380 for node in root.subnodes:
381 if 'compatible' in node.props:
382 status = node.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600383 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600384 status.value != 'disabled'):
Simon Glass1b272732020-10-03 11:31:25 -0600385 valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600386
387 # recurse to handle any subnodes
Simon Glass1b272732020-10-03 11:31:25 -0600388 self.scan_node(node, valid_nodes)
Simon Glass7581c012017-06-18 22:08:58 -0600389
Simon Glass2be282c2017-06-18 22:08:59 -0600390 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600391 """Scan the device tree for useful information
392
393 This fills in the following properties:
Simon Glass7581c012017-06-18 22:08:58 -0600394 _valid_nodes: A list of nodes we wish to consider include in the
395 platform data
396 """
Simon Glass1b272732020-10-03 11:31:25 -0600397 valid_nodes = []
398 self.scan_node(self._fdt.GetRoot(), valid_nodes)
399 self._valid_nodes = sorted(valid_nodes,
400 key=lambda x: conv_name_to_c(x.name))
401 for idx, node in enumerate(self._valid_nodes):
402 node.idx = idx
Simon Glass7581c012017-06-18 22:08:58 -0600403
Simon Glassc20ee0e2017-08-29 14:15:50 -0600404 @staticmethod
405 def get_num_cells(node):
406 """Get the number of cells in addresses and sizes for this node
407
408 Args:
409 node: Node to check
410
411 Returns:
412 Tuple:
413 Number of address cells for this node
414 Number of size cells for this node
415 """
416 parent = node.parent
417 na, ns = 2, 2
418 if parent:
419 na_prop = parent.props.get('#address-cells')
420 ns_prop = parent.props.get('#size-cells')
421 if na_prop:
422 na = fdt_util.fdt32_to_cpu(na_prop.value)
423 if ns_prop:
424 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
425 return na, ns
426
427 def scan_reg_sizes(self):
428 """Scan for 64-bit 'reg' properties and update the values
429
430 This finds 'reg' properties with 64-bit data and converts the value to
431 an array of 64-values. This allows it to be output in a way that the
432 C code can read.
433 """
434 for node in self._valid_nodes:
435 reg = node.props.get('reg')
436 if not reg:
437 continue
438 na, ns = self.get_num_cells(node)
439 total = na + ns
440
441 if reg.type != fdt.TYPE_INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600442 raise ValueError("Node '%s' reg property is not an int" %
443 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600444 if len(reg.value) % total:
445 raise ValueError("Node '%s' reg property has %d cells "
446 'which is not a multiple of na + ns = %d + %d)' %
447 (node.name, len(reg.value), na, ns))
448 reg.na = na
449 reg.ns = ns
450 if na != 1 or ns != 1:
451 reg.type = fdt.TYPE_INT64
452 i = 0
453 new_value = []
454 val = reg.value
455 if not isinstance(val, list):
456 val = [val]
457 while i < len(val):
458 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
459 i += na
460 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
461 i += ns
462 new_value += [addr, size]
463 reg.value = new_value
464
Simon Glass2be282c2017-06-18 22:08:59 -0600465 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600466 """Scan the device tree building up the C structures we will use.
467
468 Build a dict keyed by C struct name containing a dict of Prop
469 object for each struct field (keyed by property name). Where the
470 same struct appears multiple times, try to use the 'widest'
471 property, i.e. the one with a type which can express all others.
472
473 Once the widest property is determined, all other properties are
474 updated to match that width.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600475
476 Returns:
477 dict containing structures:
478 key (str): Node name, as a C identifier
479 value: dict containing structure fields:
480 key (str): Field name
481 value: Prop object with field information
Simon Glass7581c012017-06-18 22:08:58 -0600482 """
Simon Glass1b272732020-10-03 11:31:25 -0600483 structs = collections.OrderedDict()
Simon Glass7581c012017-06-18 22:08:58 -0600484 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300485 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600486 fields = {}
487
488 # Get a list of all the valid properties in this node.
489 for name, prop in node.props.items():
490 if name not in PROP_IGNORE_LIST and name[0] != '#':
491 fields[name] = copy.deepcopy(prop)
492
493 # If we've seen this node_name before, update the existing struct.
494 if node_name in structs:
495 struct = structs[node_name]
496 for name, prop in fields.items():
497 oldprop = struct.get(name)
498 if oldprop:
499 oldprop.Widen(prop)
500 else:
501 struct[name] = prop
502
503 # Otherwise store this as a new struct.
504 else:
505 structs[node_name] = fields
506
507 upto = 0
508 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300509 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600510 struct = structs[node_name]
511 for name, prop in node.props.items():
512 if name not in PROP_IGNORE_LIST and name[0] != '#':
513 prop.Widen(struct[name])
514 upto += 1
515
Simon Glass7581c012017-06-18 22:08:58 -0600516 return structs
517
Simon Glass2be282c2017-06-18 22:08:59 -0600518 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600519 """Figure out what phandles each node uses
520
521 We need to be careful when outputing nodes that use phandles since
522 they must come after the declaration of the phandles in the C file.
523 Otherwise we get a compiler error since the phandle struct is not yet
524 declared.
525
526 This function adds to each node a list of phandle nodes that the node
527 depends on. This allows us to output things in the right order.
528 """
529 for node in self._valid_nodes:
530 node.phandles = set()
531 for pname, prop in node.props.items():
532 if pname in PROP_IGNORE_LIST or pname[0] == '#':
533 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600534 info = self.get_phandle_argc(prop, node.name)
535 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600536 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600537 pos = 0
538 for args in info.args:
539 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600540 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
541 target_node = self._fdt.phandle_to_node[phandle]
542 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600543 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600544
545
Simon Glass2be282c2017-06-18 22:08:59 -0600546 def generate_structs(self, structs):
Simon Glass7581c012017-06-18 22:08:58 -0600547 """Generate struct defintions for the platform data
548
549 This writes out the body of a header file consisting of structure
550 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100551 doc/driver-model/of-plat.rst for more information.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600552
553 Args:
554 structs: dict containing structures:
555 key (str): Node name, as a C identifier
556 value: dict containing structure fields:
557 key (str): Field name
558 value: Prop object with field information
559
Simon Glass7581c012017-06-18 22:08:58 -0600560 """
Simon Glassd5031142017-08-29 14:16:01 -0600561 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600562 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900563 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600564
565 # Output the struct definition
566 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600567 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600568 for pname in sorted(structs[name]):
569 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600570 info = self.get_phandle_argc(prop, structs[name])
571 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600572 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600573 struct_name = 'struct phandle_%d_arg' % info.max_args
574 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600575 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600576 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600577 else:
578 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600579 self.out('\t%s%s' % (tab_to(2, ptype),
580 conv_name_to_c(prop.name)))
581 if isinstance(prop.value, list):
582 self.out('[%d]' % len(prop.value))
583 self.out(';\n')
584 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600585
Simon Glass2be282c2017-06-18 22:08:59 -0600586 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600587 """Output the C code for a node
588
589 Args:
590 node: node to output
591 """
Simon Glass26e408f2020-10-03 09:25:18 -0600592 def _output_list(node, prop):
593 """Output the C code for a devicetree property that holds a list
594
595 Args:
596 node (fdt.Node): Node to output
597 prop (fdt.Prop): Prop to output
598 """
599 self.buf('{')
600 vals = []
601 # For phandles, output a reference to the platform data
602 # of the target node.
603 info = self.get_phandle_argc(prop, node.name)
604 if info:
605 # Process the list as pairs of (phandle, id)
606 pos = 0
607 item = 0
608 for args in info.args:
609 phandle_cell = prop.value[pos]
610 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
611 target_node = self._fdt.phandle_to_node[phandle]
612 name = conv_name_to_c(target_node.name)
613 arg_values = []
614 for i in range(args):
615 arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
616 pos += 1 + args
617 # node member is filled with NULL as the real value
618 # will be update at run-time during dm_init_and_scan()
619 # by dm_populate_phandle_data()
620 vals.append('\t{NULL, {%s}}' % (', '.join(arg_values)))
621 var_node = '%s%s.%s[%d].node' % \
622 (VAL_PREFIX, var_name, member_name, item)
623 # Save the the link information to be use to define
624 # dm_populate_phandle_data()
Simon Glass97136eb2020-10-03 09:25:19 -0600625 self._links.append(PhandleLink(var_node, name))
Simon Glass26e408f2020-10-03 09:25:18 -0600626 item += 1
627 for val in vals:
628 self.buf('\n\t\t%s,' % val)
629 else:
630 for val in prop.value:
631 vals.append(get_value(prop.type, val))
632
633 # Put 8 values per line to avoid very long lines.
634 for i in range(0, len(vals), 8):
635 if i:
636 self.buf(',\n\t\t')
637 self.buf(', '.join(vals[i:i + 8]))
638 self.buf('}')
639
Walter Lozanodac82282020-07-03 08:07:17 -0300640 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600641 var_name = conv_name_to_c(node.name)
Simon Glass1b272732020-10-03 11:31:25 -0600642 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Walter Lozano51f12632020-06-25 01:10:13 -0300643 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass2be282c2017-06-18 22:08:59 -0600644 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glass1953ce72019-05-17 22:00:32 -0600645 for pname in sorted(node.props):
646 prop = node.props[pname]
Simon Glass7581c012017-06-18 22:08:58 -0600647 if pname in PROP_IGNORE_LIST or pname[0] == '#':
648 continue
Simon Glass2be282c2017-06-18 22:08:59 -0600649 member_name = conv_name_to_c(prop.name)
650 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
Simon Glass7581c012017-06-18 22:08:58 -0600651
652 # Special handling for lists
Simon Glass2be282c2017-06-18 22:08:59 -0600653 if isinstance(prop.value, list):
Simon Glass26e408f2020-10-03 09:25:18 -0600654 _output_list(node, prop)
Simon Glass7581c012017-06-18 22:08:58 -0600655 else:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600656 self.buf(get_value(prop.type, prop.value))
Simon Glass2be282c2017-06-18 22:08:59 -0600657 self.buf(',\n')
658 self.buf('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600659
660 # Add a device declaration
Simon Glass2be282c2017-06-18 22:08:59 -0600661 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
662 self.buf('\t.name\t\t= "%s",\n' % struct_name)
663 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
664 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
665 self.buf('};\n')
666 self.buf('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600667
Simon Glass2be282c2017-06-18 22:08:59 -0600668 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600669
Simon Glass2be282c2017-06-18 22:08:59 -0600670 def generate_tables(self):
Simon Glass7581c012017-06-18 22:08:58 -0600671 """Generate device defintions for the platform data
672
673 This writes out C platform data initialisation data and
674 U_BOOT_DEVICE() declarations for each valid node. Where a node has
675 multiple compatible strings, a #define is used to make them equivalent.
676
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100677 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600678 information.
679 """
Simon Glassd5031142017-08-29 14:16:01 -0600680 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600681 self.out('#include <common.h>\n')
682 self.out('#include <dm.h>\n')
683 self.out('#include <dt-structs.h>\n')
684 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600685 nodes_to_output = list(self._valid_nodes)
686
687 # Keep outputing nodes until there is none left
688 while nodes_to_output:
689 node = nodes_to_output[0]
690 # Output all the node's dependencies first
691 for req_node in node.phandles:
692 if req_node in nodes_to_output:
Simon Glass2be282c2017-06-18 22:08:59 -0600693 self.output_node(req_node)
Simon Glass7581c012017-06-18 22:08:58 -0600694 nodes_to_output.remove(req_node)
Simon Glass2be282c2017-06-18 22:08:59 -0600695 self.output_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600696 nodes_to_output.remove(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600697
Walter Lozano51f12632020-06-25 01:10:13 -0300698 # Define dm_populate_phandle_data() which will add the linking between
699 # nodes using DM_GET_DEVICE
700 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
701 self.buf('void dm_populate_phandle_data(void) {\n')
Simon Glass97136eb2020-10-03 09:25:19 -0600702 for link in self._links:
Walter Lozanoad340172020-06-25 01:10:16 -0300703 self.buf('\t%s = DM_GET_DEVICE(%s);\n' %
Simon Glass97136eb2020-10-03 09:25:19 -0600704 (link.var_node, link.dev_name))
Walter Lozano51f12632020-06-25 01:10:13 -0300705 self.buf('}\n')
706
707 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600708
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300709def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
710 drivers_additional=[]):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600711 """Run all the steps of the dtoc tool
712
713 Args:
714 args: List of non-option arguments provided to the problem
715 dtb_file: Filename of dtb file to process
716 include_disabled: True to include disabled nodes
717 output: Name of output file
718 """
719 if not args:
720 raise ValueError('Please specify a command: struct, platdata')
721
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300722 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional)
Walter Lozanodac82282020-07-03 08:07:17 -0300723 plat.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600724 plat.scan_dtb()
725 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600726 plat.scan_reg_sizes()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600727 plat.setup_output(output)
728 structs = plat.scan_structs()
729 plat.scan_phandles()
730
731 for cmd in args[0].split(','):
732 if cmd == 'struct':
733 plat.generate_structs(structs)
734 elif cmd == 'platdata':
735 plat.generate_tables()
736 else:
737 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
738 cmd)