blob: 8832e6ebecbf1b7618846f3a6e0b02aed8fe96a1 [file] [log] [blame]
Simon Glassd570dec2017-06-18 22:08:58 -06001#!/usr/bin/python
Tom Rini10e47792018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Simon Glassd570dec2017-06-18 22:08:58 -06003#
4# Copyright (C) 2017 Google, Inc
5# Written by Simon Glass <sjg@chromium.org>
6#
Simon Glassd570dec2017-06-18 22:08:58 -06007
Simon Glass1f730c82017-06-18 22:08:59 -06008"""Device tree to platform data class
9
10This supports converting device tree data to C structures definitions and
11static data.
12"""
13
Simon Glassec3b5e42017-08-29 14:15:55 -060014import collections
Simon Glassd570dec2017-06-18 22:08:58 -060015import copy
Walter Lozanoe675d962020-07-03 08:07:17 -030016import os
17import re
Simon Glass1f730c82017-06-18 22:08:59 -060018import sys
Simon Glassd570dec2017-06-18 22:08:58 -060019
Simon Glassa997ea52020-04-17 18:09:04 -060020from dtoc import fdt
21from dtoc import fdt_util
22from patman import tools
Simon Glassd570dec2017-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 Glassfc3ae9c2017-08-29 14:15:48 -060044 fdt.TYPE_INT64: 'fdt64_t',
Simon Glass1f730c82017-06-18 22:08:59 -060045}
Simon Glassd570dec2017-06-18 22:08:58 -060046
47STRUCT_PREFIX = 'dtd_'
48VAL_PREFIX = 'dtv_'
49
Simon Glassec3b5e42017-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 Glassc1622112020-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 Glassec3b5e42017-08-29 14:15:55 -060064
Simon Glass1f730c82017-06-18 22:08:59 -060065def conv_name_to_c(name):
Simon Glassd570dec2017-06-18 22:08:58 -060066 """Convert a device-tree name to a C identifier
67
Simon Glass2e0bf3f2017-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 Glassd570dec2017-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 Glass1f730c82017-06-18 22:08:59 -060076 new = name.replace('@', '_at_')
77 new = new.replace('-', '_')
78 new = new.replace(',', '_')
79 new = new.replace('.', '_')
Simon Glass1f730c82017-06-18 22:08:59 -060080 return new
Simon Glassd570dec2017-06-18 22:08:58 -060081
Simon Glass1f730c82017-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 Glassd570dec2017-06-18 22:08:58 -060084
Simon Glass1f730c82017-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 Glass7a2add12017-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 Glass534eda92019-05-17 22:00:43 -0600112 return '%#x' % tools.ToByte(value[0])
Simon Glass7a2add12017-06-18 22:09:02 -0600113 elif ftype == fdt.TYPE_STRING:
Simon Glass7f5e2262020-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 Glass7a2add12017-06-18 22:09:02 -0600117 elif ftype == fdt.TYPE_BOOL:
118 return 'true'
Simon Glassfc3ae9c2017-08-29 14:15:48 -0600119 elif ftype == fdt.TYPE_INT64:
120 return '%#x' % value
Simon Glass7a2add12017-06-18 22:09:02 -0600121
122def get_compat_name(node):
Walter Lozano5fe734c2020-07-23 00:22:03 -0300123 """Get the node's list of compatible string as a C identifiers
Simon Glass7a2add12017-06-18 22:09:02 -0600124
125 Args:
126 node: Node object to check
127 Return:
Walter Lozano5fe734c2020-07-23 00:22:03 -0300128 List of C identifiers for all the compatible strings
Simon Glass7a2add12017-06-18 22:09:02 -0600129 """
130 compat = node.props['compatible'].value
Walter Lozano5fe734c2020-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 Glass7a2add12017-06-18 22:09:02 -0600134
Simon Glass7a2add12017-06-18 22:09:02 -0600135
Simon Glass1f730c82017-06-18 22:08:59 -0600136class DtbPlatdata(object):
Simon Glassd570dec2017-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 Glass1f730c82017-06-18 22:08:59 -0600144 _fdt: Fdt object, referencing the device tree
Simon Glassd570dec2017-06-18 22:08:58 -0600145 _dtb_fname: Filename of the input device tree binary file
Simon Glass192f8132020-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 Glasseab3f622017-06-18 22:09:01 -0600148 _include_disabled: true to include nodes marked status = "disabled"
Simon Glassd570dec2017-06-18 22:08:58 -0600149 _outfile: The current output file (sys.stdout or a real file)
Walter Lozanoa324e412020-06-25 01:10:08 -0300150 _warning_disabled: true to disable warnings about driver names not found
Simon Glassd570dec2017-06-18 22:08:58 -0600151 _lines: Stashed list of output lines for outputting in the future
Walter Lozanoe675d962020-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 Glassc1622112020-10-03 09:25:19 -0600157 _links: List of links to be included in dm_populate_phandle_data(),
158 each a PhandleLink
Walter Lozanod82062b2020-07-28 19:06:23 -0300159 _drivers_additional: List of additional drivers to use during scanning
Simon Glassd570dec2017-06-18 22:08:58 -0600160 """
Walter Lozanod82062b2020-07-28 19:06:23 -0300161 def __init__(self, dtb_fname, include_disabled, warning_disabled,
162 drivers_additional=[]):
Simon Glass1f730c82017-06-18 22:08:59 -0600163 self._fdt = None
Simon Glassd570dec2017-06-18 22:08:58 -0600164 self._dtb_fname = dtb_fname
165 self._valid_nodes = None
Simon Glasseab3f622017-06-18 22:09:01 -0600166 self._include_disabled = include_disabled
Simon Glassd570dec2017-06-18 22:08:58 -0600167 self._outfile = None
Walter Lozanoa324e412020-06-25 01:10:08 -0300168 self._warning_disabled = warning_disabled
Simon Glassd570dec2017-06-18 22:08:58 -0600169 self._lines = []
Walter Lozanoe675d962020-07-03 08:07:17 -0300170 self._drivers = []
171 self._driver_aliases = {}
Walter Lozanodc5b4372020-06-25 01:10:13 -0300172 self._links = []
Walter Lozanod82062b2020-07-28 19:06:23 -0300173 self._drivers_additional = drivers_additional
Walter Lozanoe675d962020-07-03 08:07:17 -0300174
175 def get_normalized_compat_name(self, node):
176 """Get a node's normalized compat name
177
Walter Lozano5fe734c2020-07-23 00:22:03 -0300178 Returns a valid driver name by retrieving node's list of compatible
Walter Lozanoe675d962020-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 Lozano5fe734c2020-07-23 00:22:03 -0300192 compat_list_c = get_compat_name(node)
193
194 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]))
Walter Lozanoe675d962020-07-03 08:07:17 -0300208
Walter Lozano5fe734c2020-07-23 00:22:03 -0300209 return compat_list_c[0], compat_list_c[1:]
Simon Glassd570dec2017-06-18 22:08:58 -0600210
Simon Glass1f730c82017-06-18 22:08:59 -0600211 def setup_output(self, fname):
Simon Glassd570dec2017-06-18 22:08:58 -0600212 """Set up the output destination
213
Simon Glass1f730c82017-06-18 22:08:59 -0600214 Once this is done, future calls to self.out() will output to this
Simon Glassd570dec2017-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 Glass1f730c82017-06-18 22:08:59 -0600225 def out(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600226 """Output a string to the output file
227
228 Args:
Simon Glass1f730c82017-06-18 22:08:59 -0600229 line: String to output
Simon Glassd570dec2017-06-18 22:08:58 -0600230 """
Simon Glass1f730c82017-06-18 22:08:59 -0600231 self._outfile.write(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600232
Simon Glass1f730c82017-06-18 22:08:59 -0600233 def buf(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600234 """Buffer up a string to send later
235
236 Args:
Simon Glass1f730c82017-06-18 22:08:59 -0600237 line: String to add to our 'buffer' list
Simon Glassd570dec2017-06-18 22:08:58 -0600238 """
Simon Glass1f730c82017-06-18 22:08:59 -0600239 self._lines.append(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600240
Simon Glass1f730c82017-06-18 22:08:59 -0600241 def get_buf(self):
Simon Glassd570dec2017-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 Glass68d32c72017-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 Glassec3b5e42017-08-29 14:15:55 -0600261 def get_phandle_argc(self, prop, node_name):
262 """Check if a node contains phandles
Simon Glassce5621d2017-08-29 14:15:54 -0600263
Simon Glassec3b5e42017-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.
266
267 Args:
268 prop: Prop object to check
269 Return:
270 Number of argument cells is this is a phandle, else None
271 """
Walter Lozano179f0b62020-06-25 01:10:16 -0300272 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass609e2b12018-07-06 10:27:31 -0600273 if not isinstance(prop.value, list):
274 prop.value = [prop.value]
Simon Glassec3b5e42017-08-29 14:15:55 -0600275 val = prop.value
Simon Glassec3b5e42017-08-29 14:15:55 -0600276 i = 0
Simon Glassce5621d2017-08-29 14:15:54 -0600277
Simon Glassec3b5e42017-08-29 14:15:55 -0600278 max_args = 0
279 args = []
280 while i < len(val):
281 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass609e2b12018-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 Glassec3b5e42017-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 Lozano179f0b62020-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 Glassec3b5e42017-08-29 14:15:55 -0600297 if not cells:
Walter Lozano179f0b62020-06-25 01:10:16 -0300298 raise ValueError("Node '%s' has no cells property" %
299 (target.name))
Simon Glassec3b5e42017-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 Glassce5621d2017-08-29 14:15:54 -0600306
Walter Lozanoe675d962020-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 Lozanod82062b2020-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 Glass1f730c82017-06-18 22:08:59 -0600363 def scan_dtb(self):
Anatolij Gustschinda707d42017-08-18 17:58:51 +0200364 """Scan the device tree to obtain a tree of nodes and properties
Simon Glassd570dec2017-06-18 22:08:58 -0600365
Simon Glass1f730c82017-06-18 22:08:59 -0600366 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glassd570dec2017-06-18 22:08:58 -0600367 device tree root node, and progress from there.
368 """
Simon Glass1f730c82017-06-18 22:08:59 -0600369 self._fdt = fdt.FdtScan(self._dtb_fname)
370
Simon Glass192f8132020-10-03 11:31:25 -0600371 def scan_node(self, root, valid_nodes):
Simon Glass1f730c82017-06-18 22:08:59 -0600372 """Scan a node and subnodes to build a tree of node and phandle info
Simon Glassd570dec2017-06-18 22:08:58 -0600373
Simon Glass17ffd622017-08-29 14:15:53 -0600374 This adds each node to self._valid_nodes.
Simon Glass1f730c82017-06-18 22:08:59 -0600375
376 Args:
377 root: Root node for scan
Simon Glass192f8132020-10-03 11:31:25 -0600378 valid_nodes: List of Node objects to add to
Simon Glass1f730c82017-06-18 22:08:59 -0600379 """
Simon Glassd570dec2017-06-18 22:08:58 -0600380 for node in root.subnodes:
381 if 'compatible' in node.props:
382 status = node.props.get('status')
Simon Glasseab3f622017-06-18 22:09:01 -0600383 if (not self._include_disabled and not status or
Simon Glass1f730c82017-06-18 22:08:59 -0600384 status.value != 'disabled'):
Simon Glass192f8132020-10-03 11:31:25 -0600385 valid_nodes.append(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600386
387 # recurse to handle any subnodes
Simon Glass192f8132020-10-03 11:31:25 -0600388 self.scan_node(node, valid_nodes)
Simon Glassd570dec2017-06-18 22:08:58 -0600389
Simon Glass1f730c82017-06-18 22:08:59 -0600390 def scan_tree(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600391 """Scan the device tree for useful information
392
393 This fills in the following properties:
Simon Glassd570dec2017-06-18 22:08:58 -0600394 _valid_nodes: A list of nodes we wish to consider include in the
395 platform data
396 """
Simon Glass192f8132020-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 Glassd570dec2017-06-18 22:08:58 -0600403
Simon Glass1b1fe412017-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 Glassc38fee042018-07-06 10:27:32 -0600442 raise ValueError("Node '%s' reg property is not an int" %
443 node.name)
Simon Glass1b1fe412017-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 Glass1f730c82017-06-18 22:08:59 -0600465 def scan_structs(self):
Simon Glassd570dec2017-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 Glass941f8f02020-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 Glassd570dec2017-06-18 22:08:58 -0600482 """
Simon Glass192f8132020-10-03 11:31:25 -0600483 structs = collections.OrderedDict()
Simon Glassd570dec2017-06-18 22:08:58 -0600484 for node in self._valid_nodes:
Walter Lozanoe675d962020-07-03 08:07:17 -0300485 node_name, _ = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-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 Lozanoe675d962020-07-03 08:07:17 -0300509 node_name, _ = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-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 Glassd570dec2017-06-18 22:08:58 -0600516 return structs
517
Simon Glass1f730c82017-06-18 22:08:59 -0600518 def scan_phandles(self):
Simon Glassd570dec2017-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 Glassec3b5e42017-08-29 14:15:55 -0600534 info = self.get_phandle_argc(prop, node.name)
535 if info:
Simon Glassec3b5e42017-08-29 14:15:55 -0600536 # Process the list as pairs of (phandle, id)
Simon Glass3deeb472017-08-29 14:15:59 -0600537 pos = 0
538 for args in info.args:
539 phandle_cell = prop.value[pos]
Simon Glassec3b5e42017-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 Glass3deeb472017-08-29 14:15:59 -0600543 pos += 1 + args
Simon Glassd570dec2017-06-18 22:08:58 -0600544
545
Simon Glass1f730c82017-06-18 22:08:59 -0600546 def generate_structs(self, structs):
Simon Glassd570dec2017-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 Schuchardtc79f03c2020-02-25 21:35:39 +0100551 doc/driver-model/of-plat.rst for more information.
Simon Glass941f8f02020-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 Glassd570dec2017-06-18 22:08:58 -0600560 """
Simon Glass68d32c72017-08-29 14:16:01 -0600561 self.out_header()
Simon Glass1f730c82017-06-18 22:08:59 -0600562 self.out('#include <stdbool.h>\n')
Masahiro Yamada75f82d02018-03-05 01:20:11 +0900563 self.out('#include <linux/libfdt.h>\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600564
565 # Output the struct definition
566 for name in sorted(structs):
Simon Glass1f730c82017-06-18 22:08:59 -0600567 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glassd570dec2017-06-18 22:08:58 -0600568 for pname in sorted(structs[name]):
569 prop = structs[name][pname]
Simon Glassec3b5e42017-08-29 14:15:55 -0600570 info = self.get_phandle_argc(prop, structs[name])
571 if info:
Simon Glassd570dec2017-06-18 22:08:58 -0600572 # For phandles, include a reference to the target
Simon Glasse94414b2017-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 Glass1f730c82017-06-18 22:08:59 -0600575 conv_name_to_c(prop.name),
Simon Glass3deeb472017-08-29 14:15:59 -0600576 len(info.args)))
Simon Glassd570dec2017-06-18 22:08:58 -0600577 else:
578 ptype = TYPE_NAMES[prop.type]
Simon Glass1f730c82017-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 Glassd570dec2017-06-18 22:08:58 -0600585
Simon Glass1f730c82017-06-18 22:08:59 -0600586 def output_node(self, node):
Simon Glassd570dec2017-06-18 22:08:58 -0600587 """Output the C code for a node
588
589 Args:
590 node: node to output
591 """
Simon Glassbc9e2682020-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 Glassc1622112020-10-03 09:25:19 -0600625 self._links.append(PhandleLink(var_node, name))
Simon Glassbc9e2682020-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 Lozanoe675d962020-07-03 08:07:17 -0300640 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass1f730c82017-06-18 22:08:59 -0600641 var_name = conv_name_to_c(node.name)
Simon Glass192f8132020-10-03 11:31:25 -0600642 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Walter Lozanodc5b4372020-06-25 01:10:13 -0300643 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass1f730c82017-06-18 22:08:59 -0600644 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glassc82de562019-05-17 22:00:32 -0600645 for pname in sorted(node.props):
646 prop = node.props[pname]
Simon Glassd570dec2017-06-18 22:08:58 -0600647 if pname in PROP_IGNORE_LIST or pname[0] == '#':
648 continue
Simon Glass1f730c82017-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 Glassd570dec2017-06-18 22:08:58 -0600651
652 # Special handling for lists
Simon Glass1f730c82017-06-18 22:08:59 -0600653 if isinstance(prop.value, list):
Simon Glassbc9e2682020-10-03 09:25:18 -0600654 _output_list(node, prop)
Simon Glassd570dec2017-06-18 22:08:58 -0600655 else:
Simon Glass7a2add12017-06-18 22:09:02 -0600656 self.buf(get_value(prop.type, prop.value))
Simon Glass1f730c82017-06-18 22:08:59 -0600657 self.buf(',\n')
658 self.buf('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600659
660 # Add a device declaration
Simon Glass1f730c82017-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))
Simon Glass36b15e22020-10-03 11:31:35 -0600665 idx = -1
666 if node.parent and node.parent in self._valid_nodes:
667 idx = node.parent.idx
668 self.buf('\t.parent_idx\t= %d,\n' % idx)
Simon Glass1f730c82017-06-18 22:08:59 -0600669 self.buf('};\n')
670 self.buf('\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600671
Simon Glass1f730c82017-06-18 22:08:59 -0600672 self.out(''.join(self.get_buf()))
Simon Glassd570dec2017-06-18 22:08:58 -0600673
Simon Glass1f730c82017-06-18 22:08:59 -0600674 def generate_tables(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600675 """Generate device defintions for the platform data
676
677 This writes out C platform data initialisation data and
678 U_BOOT_DEVICE() declarations for each valid node. Where a node has
679 multiple compatible strings, a #define is used to make them equivalent.
680
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +0100681 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glassd570dec2017-06-18 22:08:58 -0600682 information.
683 """
Simon Glass68d32c72017-08-29 14:16:01 -0600684 self.out_header()
Simon Glass1f730c82017-06-18 22:08:59 -0600685 self.out('#include <common.h>\n')
686 self.out('#include <dm.h>\n')
687 self.out('#include <dt-structs.h>\n')
688 self.out('\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600689 nodes_to_output = list(self._valid_nodes)
690
691 # Keep outputing nodes until there is none left
692 while nodes_to_output:
693 node = nodes_to_output[0]
694 # Output all the node's dependencies first
695 for req_node in node.phandles:
696 if req_node in nodes_to_output:
Simon Glass1f730c82017-06-18 22:08:59 -0600697 self.output_node(req_node)
Simon Glassd570dec2017-06-18 22:08:58 -0600698 nodes_to_output.remove(req_node)
Simon Glass1f730c82017-06-18 22:08:59 -0600699 self.output_node(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600700 nodes_to_output.remove(node)
Simon Glass3fa797a2017-06-18 22:09:03 -0600701
Walter Lozanodc5b4372020-06-25 01:10:13 -0300702 # Define dm_populate_phandle_data() which will add the linking between
703 # nodes using DM_GET_DEVICE
704 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
705 self.buf('void dm_populate_phandle_data(void) {\n')
Simon Glassc1622112020-10-03 09:25:19 -0600706 for link in self._links:
Walter Lozano179f0b62020-06-25 01:10:16 -0300707 self.buf('\t%s = DM_GET_DEVICE(%s);\n' %
Simon Glassc1622112020-10-03 09:25:19 -0600708 (link.var_node, link.dev_name))
Walter Lozanodc5b4372020-06-25 01:10:13 -0300709 self.buf('}\n')
710
711 self.out(''.join(self.get_buf()))
Simon Glass3fa797a2017-06-18 22:09:03 -0600712
Walter Lozanod82062b2020-07-28 19:06:23 -0300713def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
714 drivers_additional=[]):
Simon Glass3fa797a2017-06-18 22:09:03 -0600715 """Run all the steps of the dtoc tool
716
717 Args:
718 args: List of non-option arguments provided to the problem
719 dtb_file: Filename of dtb file to process
720 include_disabled: True to include disabled nodes
721 output: Name of output file
722 """
723 if not args:
724 raise ValueError('Please specify a command: struct, platdata')
725
Walter Lozanod82062b2020-07-28 19:06:23 -0300726 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional)
Walter Lozanoe675d962020-07-03 08:07:17 -0300727 plat.scan_drivers()
Simon Glass3fa797a2017-06-18 22:09:03 -0600728 plat.scan_dtb()
729 plat.scan_tree()
Simon Glass1b1fe412017-08-29 14:15:50 -0600730 plat.scan_reg_sizes()
Simon Glass3fa797a2017-06-18 22:09:03 -0600731 plat.setup_output(output)
732 structs = plat.scan_structs()
733 plat.scan_phandles()
734
735 for cmd in args[0].split(','):
736 if cmd == 'struct':
737 plat.generate_structs(structs)
738 elif cmd == 'platdata':
739 plat.generate_tables()
740 else:
741 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
742 cmd)