blob: 9b27aecc140ffe64d919181896bd2c95ec29a16a [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)
Walter Lozanod82062b2020-07-28 19:06:23 -0300157 _drivers_additional: List of additional drivers to use during scanning
Simon Glassd570dec2017-06-18 22:08:58 -0600158 """
Walter Lozanod82062b2020-07-28 19:06:23 -0300159 def __init__(self, dtb_fname, include_disabled, warning_disabled,
160 drivers_additional=[]):
Simon Glass1f730c82017-06-18 22:08:59 -0600161 self._fdt = None
Simon Glassd570dec2017-06-18 22:08:58 -0600162 self._dtb_fname = dtb_fname
163 self._valid_nodes = None
Simon Glasseab3f622017-06-18 22:09:01 -0600164 self._include_disabled = include_disabled
Simon Glassd570dec2017-06-18 22:08:58 -0600165 self._outfile = None
Walter Lozanoa324e412020-06-25 01:10:08 -0300166 self._warning_disabled = warning_disabled
Simon Glassd570dec2017-06-18 22:08:58 -0600167 self._lines = []
Walter Lozanoe675d962020-07-03 08:07:17 -0300168 self._drivers = []
169 self._driver_aliases = {}
Walter Lozanod82062b2020-07-28 19:06:23 -0300170 self._drivers_additional = drivers_additional
Walter Lozanoe675d962020-07-03 08:07:17 -0300171
172 def get_normalized_compat_name(self, node):
173 """Get a node's normalized compat name
174
Walter Lozano5fe734c2020-07-23 00:22:03 -0300175 Returns a valid driver name by retrieving node's list of compatible
Walter Lozanoe675d962020-07-03 08:07:17 -0300176 string as a C identifier and performing a check against _drivers
177 and a lookup in driver_aliases printing a warning in case of failure.
178
179 Args:
180 node: Node object to check
181 Return:
182 Tuple:
183 Driver name associated with the first compatible string
184 List of C identifiers for all the other compatible strings
185 (possibly empty)
186 In case of no match found, the return will be the same as
187 get_compat_name()
188 """
Walter Lozano5fe734c2020-07-23 00:22:03 -0300189 compat_list_c = get_compat_name(node)
190
191 for compat_c in compat_list_c:
192 if not compat_c in self._drivers:
193 compat_c = self._driver_aliases.get(compat_c)
194 if not compat_c:
195 continue
196
197 aliases_c = compat_list_c
198 if compat_c in aliases_c:
199 aliases_c.remove(compat_c)
200 return compat_c, aliases_c
201
202 if not self._warning_disabled:
203 print('WARNING: the driver %s was not found in the driver list'
204 % (compat_list_c[0]))
Walter Lozanoe675d962020-07-03 08:07:17 -0300205
Walter Lozano5fe734c2020-07-23 00:22:03 -0300206 return compat_list_c[0], compat_list_c[1:]
Simon Glassd570dec2017-06-18 22:08:58 -0600207
Simon Glass1f730c82017-06-18 22:08:59 -0600208 def setup_output(self, fname):
Simon Glassd570dec2017-06-18 22:08:58 -0600209 """Set up the output destination
210
Simon Glass1f730c82017-06-18 22:08:59 -0600211 Once this is done, future calls to self.out() will output to this
Simon Glassd570dec2017-06-18 22:08:58 -0600212 file.
213
214 Args:
215 fname: Filename to send output to, or '-' for stdout
216 """
217 if fname == '-':
218 self._outfile = sys.stdout
219 else:
220 self._outfile = open(fname, 'w')
221
Simon Glass1f730c82017-06-18 22:08:59 -0600222 def out(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600223 """Output a string to the output file
224
225 Args:
Simon Glass1f730c82017-06-18 22:08:59 -0600226 line: String to output
Simon Glassd570dec2017-06-18 22:08:58 -0600227 """
Simon Glass1f730c82017-06-18 22:08:59 -0600228 self._outfile.write(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600229
Simon Glass1f730c82017-06-18 22:08:59 -0600230 def buf(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600231 """Buffer up a string to send later
232
233 Args:
Simon Glass1f730c82017-06-18 22:08:59 -0600234 line: String to add to our 'buffer' list
Simon Glassd570dec2017-06-18 22:08:58 -0600235 """
Simon Glass1f730c82017-06-18 22:08:59 -0600236 self._lines.append(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600237
Simon Glass1f730c82017-06-18 22:08:59 -0600238 def get_buf(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600239 """Get the contents of the output buffer, and clear it
240
241 Returns:
242 The output buffer, which is then cleared for future use
243 """
244 lines = self._lines
245 self._lines = []
246 return lines
247
Simon Glass68d32c72017-08-29 14:16:01 -0600248 def out_header(self):
249 """Output a message indicating that this is an auto-generated file"""
250 self.out('''/*
251 * DO NOT MODIFY
252 *
253 * This file was generated by dtoc from a .dtb (device tree binary) file.
254 */
255
256''')
257
Simon Glassec3b5e42017-08-29 14:15:55 -0600258 def get_phandle_argc(self, prop, node_name):
259 """Check if a node contains phandles
Simon Glassce5621d2017-08-29 14:15:54 -0600260
Simon Glassec3b5e42017-08-29 14:15:55 -0600261 We have no reliable way of detecting whether a node uses a phandle
262 or not. As an interim measure, use a list of known property names.
263
264 Args:
265 prop: Prop object to check
266 Return:
267 Number of argument cells is this is a phandle, else None
268 """
Walter Lozano179f0b62020-06-25 01:10:16 -0300269 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass609e2b12018-07-06 10:27:31 -0600270 if not isinstance(prop.value, list):
271 prop.value = [prop.value]
Simon Glassec3b5e42017-08-29 14:15:55 -0600272 val = prop.value
Simon Glassec3b5e42017-08-29 14:15:55 -0600273 i = 0
Simon Glassce5621d2017-08-29 14:15:54 -0600274
Simon Glassec3b5e42017-08-29 14:15:55 -0600275 max_args = 0
276 args = []
277 while i < len(val):
278 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass609e2b12018-07-06 10:27:31 -0600279 # If we get to the end of the list, stop. This can happen
280 # since some nodes have more phandles in the list than others,
281 # but we allocate enough space for the largest list. So those
282 # nodes with shorter lists end up with zeroes at the end.
283 if not phandle:
284 break
Simon Glassec3b5e42017-08-29 14:15:55 -0600285 target = self._fdt.phandle_to_node.get(phandle)
286 if not target:
287 raise ValueError("Cannot parse '%s' in node '%s'" %
288 (prop.name, node_name))
Walter Lozano179f0b62020-06-25 01:10:16 -0300289 cells = None
290 for prop_name in ['#clock-cells', '#gpio-cells']:
291 cells = target.props.get(prop_name)
292 if cells:
293 break
Simon Glassec3b5e42017-08-29 14:15:55 -0600294 if not cells:
Walter Lozano179f0b62020-06-25 01:10:16 -0300295 raise ValueError("Node '%s' has no cells property" %
296 (target.name))
Simon Glassec3b5e42017-08-29 14:15:55 -0600297 num_args = fdt_util.fdt32_to_cpu(cells.value)
298 max_args = max(max_args, num_args)
299 args.append(num_args)
300 i += 1 + num_args
301 return PhandleInfo(max_args, args)
302 return None
Simon Glassce5621d2017-08-29 14:15:54 -0600303
Walter Lozanoe675d962020-07-03 08:07:17 -0300304 def scan_driver(self, fn):
305 """Scan a driver file to build a list of driver names and aliases
306
307 This procedure will populate self._drivers and self._driver_aliases
308
309 Args
310 fn: Driver filename to scan
311 """
312 with open(fn, encoding='utf-8') as fd:
313 try:
314 buff = fd.read()
315 except UnicodeDecodeError:
316 # This seems to happen on older Python versions
317 print("Skipping file '%s' due to unicode error" % fn)
318 return
319
320 # The following re will search for driver names declared as
321 # U_BOOT_DRIVER(driver_name)
322 drivers = re.findall('U_BOOT_DRIVER\((.*)\)', buff)
323
324 for driver in drivers:
325 self._drivers.append(driver)
326
327 # The following re will search for driver aliases declared as
328 # U_BOOT_DRIVER_ALIAS(alias, driver_name)
329 driver_aliases = re.findall('U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
330 buff)
331
332 for alias in driver_aliases: # pragma: no cover
333 if len(alias) != 2:
334 continue
335 self._driver_aliases[alias[1]] = alias[0]
336
337 def scan_drivers(self):
338 """Scan the driver folders to build a list of driver names and aliases
339
340 This procedure will populate self._drivers and self._driver_aliases
341
342 """
343 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
344 if basedir == '':
345 basedir = './'
346 for (dirpath, dirnames, filenames) in os.walk(basedir):
347 for fn in filenames:
348 if not fn.endswith('.c'):
349 continue
350 self.scan_driver(dirpath + '/' + fn)
351
Walter Lozanod82062b2020-07-28 19:06:23 -0300352 for fn in self._drivers_additional:
353 if not isinstance(fn, str) or len(fn) == 0:
354 continue
355 if fn[0] == '/':
356 self.scan_driver(fn)
357 else:
358 self.scan_driver(basedir + '/' + fn)
359
Simon Glass1f730c82017-06-18 22:08:59 -0600360 def scan_dtb(self):
Anatolij Gustschinda707d42017-08-18 17:58:51 +0200361 """Scan the device tree to obtain a tree of nodes and properties
Simon Glassd570dec2017-06-18 22:08:58 -0600362
Simon Glass1f730c82017-06-18 22:08:59 -0600363 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glassd570dec2017-06-18 22:08:58 -0600364 device tree root node, and progress from there.
365 """
Simon Glass1f730c82017-06-18 22:08:59 -0600366 self._fdt = fdt.FdtScan(self._dtb_fname)
367
Simon Glass192f8132020-10-03 11:31:25 -0600368 def scan_node(self, root, valid_nodes):
Simon Glass1f730c82017-06-18 22:08:59 -0600369 """Scan a node and subnodes to build a tree of node and phandle info
Simon Glassd570dec2017-06-18 22:08:58 -0600370
Simon Glass17ffd622017-08-29 14:15:53 -0600371 This adds each node to self._valid_nodes.
Simon Glass1f730c82017-06-18 22:08:59 -0600372
373 Args:
374 root: Root node for scan
Simon Glass192f8132020-10-03 11:31:25 -0600375 valid_nodes: List of Node objects to add to
Simon Glass1f730c82017-06-18 22:08:59 -0600376 """
Simon Glassd570dec2017-06-18 22:08:58 -0600377 for node in root.subnodes:
378 if 'compatible' in node.props:
379 status = node.props.get('status')
Simon Glasseab3f622017-06-18 22:09:01 -0600380 if (not self._include_disabled and not status or
Simon Glass1f730c82017-06-18 22:08:59 -0600381 status.value != 'disabled'):
Simon Glass192f8132020-10-03 11:31:25 -0600382 valid_nodes.append(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600383
384 # recurse to handle any subnodes
Simon Glass192f8132020-10-03 11:31:25 -0600385 self.scan_node(node, valid_nodes)
Simon Glassd570dec2017-06-18 22:08:58 -0600386
Simon Glass1f730c82017-06-18 22:08:59 -0600387 def scan_tree(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600388 """Scan the device tree for useful information
389
390 This fills in the following properties:
Simon Glassd570dec2017-06-18 22:08:58 -0600391 _valid_nodes: A list of nodes we wish to consider include in the
392 platform data
393 """
Simon Glass192f8132020-10-03 11:31:25 -0600394 valid_nodes = []
395 self.scan_node(self._fdt.GetRoot(), valid_nodes)
396 self._valid_nodes = sorted(valid_nodes,
397 key=lambda x: conv_name_to_c(x.name))
398 for idx, node in enumerate(self._valid_nodes):
399 node.idx = idx
Simon Glassd570dec2017-06-18 22:08:58 -0600400
Simon Glass1b1fe412017-08-29 14:15:50 -0600401 @staticmethod
402 def get_num_cells(node):
403 """Get the number of cells in addresses and sizes for this node
404
405 Args:
406 node: Node to check
407
408 Returns:
409 Tuple:
410 Number of address cells for this node
411 Number of size cells for this node
412 """
413 parent = node.parent
414 na, ns = 2, 2
415 if parent:
416 na_prop = parent.props.get('#address-cells')
417 ns_prop = parent.props.get('#size-cells')
418 if na_prop:
419 na = fdt_util.fdt32_to_cpu(na_prop.value)
420 if ns_prop:
421 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
422 return na, ns
423
424 def scan_reg_sizes(self):
425 """Scan for 64-bit 'reg' properties and update the values
426
427 This finds 'reg' properties with 64-bit data and converts the value to
428 an array of 64-values. This allows it to be output in a way that the
429 C code can read.
430 """
431 for node in self._valid_nodes:
432 reg = node.props.get('reg')
433 if not reg:
434 continue
435 na, ns = self.get_num_cells(node)
436 total = na + ns
437
438 if reg.type != fdt.TYPE_INT:
Simon Glassc38fee042018-07-06 10:27:32 -0600439 raise ValueError("Node '%s' reg property is not an int" %
440 node.name)
Simon Glass1b1fe412017-08-29 14:15:50 -0600441 if len(reg.value) % total:
442 raise ValueError("Node '%s' reg property has %d cells "
443 'which is not a multiple of na + ns = %d + %d)' %
444 (node.name, len(reg.value), na, ns))
445 reg.na = na
446 reg.ns = ns
447 if na != 1 or ns != 1:
448 reg.type = fdt.TYPE_INT64
449 i = 0
450 new_value = []
451 val = reg.value
452 if not isinstance(val, list):
453 val = [val]
454 while i < len(val):
455 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
456 i += na
457 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
458 i += ns
459 new_value += [addr, size]
460 reg.value = new_value
461
Simon Glass1f730c82017-06-18 22:08:59 -0600462 def scan_structs(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600463 """Scan the device tree building up the C structures we will use.
464
465 Build a dict keyed by C struct name containing a dict of Prop
466 object for each struct field (keyed by property name). Where the
467 same struct appears multiple times, try to use the 'widest'
468 property, i.e. the one with a type which can express all others.
469
470 Once the widest property is determined, all other properties are
471 updated to match that width.
Simon Glass941f8f02020-10-03 11:31:24 -0600472
473 Returns:
474 dict containing structures:
475 key (str): Node name, as a C identifier
476 value: dict containing structure fields:
477 key (str): Field name
478 value: Prop object with field information
Simon Glassd570dec2017-06-18 22:08:58 -0600479 """
Simon Glass192f8132020-10-03 11:31:25 -0600480 structs = collections.OrderedDict()
Simon Glassd570dec2017-06-18 22:08:58 -0600481 for node in self._valid_nodes:
Walter Lozanoe675d962020-07-03 08:07:17 -0300482 node_name, _ = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600483 fields = {}
484
485 # Get a list of all the valid properties in this node.
486 for name, prop in node.props.items():
487 if name not in PROP_IGNORE_LIST and name[0] != '#':
488 fields[name] = copy.deepcopy(prop)
489
490 # If we've seen this node_name before, update the existing struct.
491 if node_name in structs:
492 struct = structs[node_name]
493 for name, prop in fields.items():
494 oldprop = struct.get(name)
495 if oldprop:
496 oldprop.Widen(prop)
497 else:
498 struct[name] = prop
499
500 # Otherwise store this as a new struct.
501 else:
502 structs[node_name] = fields
503
504 upto = 0
505 for node in self._valid_nodes:
Walter Lozanoe675d962020-07-03 08:07:17 -0300506 node_name, _ = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600507 struct = structs[node_name]
508 for name, prop in node.props.items():
509 if name not in PROP_IGNORE_LIST and name[0] != '#':
510 prop.Widen(struct[name])
511 upto += 1
512
Simon Glassd570dec2017-06-18 22:08:58 -0600513 return structs
514
Simon Glass1f730c82017-06-18 22:08:59 -0600515 def scan_phandles(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600516 """Figure out what phandles each node uses
517
518 We need to be careful when outputing nodes that use phandles since
519 they must come after the declaration of the phandles in the C file.
520 Otherwise we get a compiler error since the phandle struct is not yet
521 declared.
522
523 This function adds to each node a list of phandle nodes that the node
524 depends on. This allows us to output things in the right order.
525 """
526 for node in self._valid_nodes:
527 node.phandles = set()
528 for pname, prop in node.props.items():
529 if pname in PROP_IGNORE_LIST or pname[0] == '#':
530 continue
Simon Glassec3b5e42017-08-29 14:15:55 -0600531 info = self.get_phandle_argc(prop, node.name)
532 if info:
Simon Glassec3b5e42017-08-29 14:15:55 -0600533 # Process the list as pairs of (phandle, id)
Simon Glass3deeb472017-08-29 14:15:59 -0600534 pos = 0
535 for args in info.args:
536 phandle_cell = prop.value[pos]
Simon Glassec3b5e42017-08-29 14:15:55 -0600537 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
538 target_node = self._fdt.phandle_to_node[phandle]
539 node.phandles.add(target_node)
Simon Glass3deeb472017-08-29 14:15:59 -0600540 pos += 1 + args
Simon Glassd570dec2017-06-18 22:08:58 -0600541
542
Simon Glass1f730c82017-06-18 22:08:59 -0600543 def generate_structs(self, structs):
Simon Glassd570dec2017-06-18 22:08:58 -0600544 """Generate struct defintions for the platform data
545
546 This writes out the body of a header file consisting of structure
547 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +0100548 doc/driver-model/of-plat.rst for more information.
Simon Glass941f8f02020-10-03 11:31:24 -0600549
550 Args:
551 structs: dict containing structures:
552 key (str): Node name, as a C identifier
553 value: dict containing structure fields:
554 key (str): Field name
555 value: Prop object with field information
556
Simon Glassd570dec2017-06-18 22:08:58 -0600557 """
Simon Glass68d32c72017-08-29 14:16:01 -0600558 self.out_header()
Simon Glass1f730c82017-06-18 22:08:59 -0600559 self.out('#include <stdbool.h>\n')
Masahiro Yamada75f82d02018-03-05 01:20:11 +0900560 self.out('#include <linux/libfdt.h>\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600561
562 # Output the struct definition
563 for name in sorted(structs):
Simon Glass1f730c82017-06-18 22:08:59 -0600564 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glassd570dec2017-06-18 22:08:58 -0600565 for pname in sorted(structs[name]):
566 prop = structs[name][pname]
Simon Glassec3b5e42017-08-29 14:15:55 -0600567 info = self.get_phandle_argc(prop, structs[name])
568 if info:
Simon Glassd570dec2017-06-18 22:08:58 -0600569 # For phandles, include a reference to the target
Simon Glasse94414b2017-08-29 14:15:56 -0600570 struct_name = 'struct phandle_%d_arg' % info.max_args
571 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass1f730c82017-06-18 22:08:59 -0600572 conv_name_to_c(prop.name),
Simon Glass3deeb472017-08-29 14:15:59 -0600573 len(info.args)))
Simon Glassd570dec2017-06-18 22:08:58 -0600574 else:
575 ptype = TYPE_NAMES[prop.type]
Simon Glass1f730c82017-06-18 22:08:59 -0600576 self.out('\t%s%s' % (tab_to(2, ptype),
577 conv_name_to_c(prop.name)))
578 if isinstance(prop.value, list):
579 self.out('[%d]' % len(prop.value))
580 self.out(';\n')
581 self.out('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600582
Simon Glass1f730c82017-06-18 22:08:59 -0600583 def output_node(self, node):
Simon Glassd570dec2017-06-18 22:08:58 -0600584 """Output the C code for a node
585
586 Args:
587 node: node to output
588 """
Simon Glassbc9e2682020-10-03 09:25:18 -0600589 def _output_list(node, prop):
590 """Output the C code for a devicetree property that holds a list
591
592 Args:
593 node (fdt.Node): Node to output
594 prop (fdt.Prop): Prop to output
595 """
596 self.buf('{')
597 vals = []
598 # For phandles, output a reference to the platform data
599 # of the target node.
600 info = self.get_phandle_argc(prop, node.name)
601 if info:
602 # Process the list as pairs of (phandle, id)
603 pos = 0
604 item = 0
605 for args in info.args:
606 phandle_cell = prop.value[pos]
607 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
608 target_node = self._fdt.phandle_to_node[phandle]
609 name = conv_name_to_c(target_node.name)
610 arg_values = []
611 for i in range(args):
Simon Glass5792f4b2020-10-03 11:31:40 -0600612 arg_values.append(
613 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
Simon Glassbc9e2682020-10-03 09:25:18 -0600614 pos += 1 + args
Simon Glass5792f4b2020-10-03 11:31:40 -0600615 vals.append('\t{%d, {%s}}' % (target_node.idx,
616 ', '.join(arg_values)))
Simon Glassbc9e2682020-10-03 09:25:18 -0600617 item += 1
618 for val in vals:
619 self.buf('\n\t\t%s,' % val)
620 else:
621 for val in prop.value:
622 vals.append(get_value(prop.type, val))
623
624 # Put 8 values per line to avoid very long lines.
625 for i in range(0, len(vals), 8):
626 if i:
627 self.buf(',\n\t\t')
628 self.buf(', '.join(vals[i:i + 8]))
629 self.buf('}')
630
Walter Lozanoe675d962020-07-03 08:07:17 -0300631 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass1f730c82017-06-18 22:08:59 -0600632 var_name = conv_name_to_c(node.name)
Simon Glass192f8132020-10-03 11:31:25 -0600633 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Walter Lozanodc5b4372020-06-25 01:10:13 -0300634 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass1f730c82017-06-18 22:08:59 -0600635 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glassc82de562019-05-17 22:00:32 -0600636 for pname in sorted(node.props):
637 prop = node.props[pname]
Simon Glassd570dec2017-06-18 22:08:58 -0600638 if pname in PROP_IGNORE_LIST or pname[0] == '#':
639 continue
Simon Glass1f730c82017-06-18 22:08:59 -0600640 member_name = conv_name_to_c(prop.name)
641 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
Simon Glassd570dec2017-06-18 22:08:58 -0600642
643 # Special handling for lists
Simon Glass1f730c82017-06-18 22:08:59 -0600644 if isinstance(prop.value, list):
Simon Glassbc9e2682020-10-03 09:25:18 -0600645 _output_list(node, prop)
Simon Glassd570dec2017-06-18 22:08:58 -0600646 else:
Simon Glass7a2add12017-06-18 22:09:02 -0600647 self.buf(get_value(prop.type, prop.value))
Simon Glass1f730c82017-06-18 22:08:59 -0600648 self.buf(',\n')
649 self.buf('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600650
651 # Add a device declaration
Simon Glass1f730c82017-06-18 22:08:59 -0600652 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
653 self.buf('\t.name\t\t= "%s",\n' % struct_name)
654 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
655 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
Simon Glass36b15e22020-10-03 11:31:35 -0600656 idx = -1
657 if node.parent and node.parent in self._valid_nodes:
658 idx = node.parent.idx
659 self.buf('\t.parent_idx\t= %d,\n' % idx)
Simon Glass1f730c82017-06-18 22:08:59 -0600660 self.buf('};\n')
661 self.buf('\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600662
Simon Glass1f730c82017-06-18 22:08:59 -0600663 self.out(''.join(self.get_buf()))
Simon Glassd570dec2017-06-18 22:08:58 -0600664
Simon Glass1f730c82017-06-18 22:08:59 -0600665 def generate_tables(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600666 """Generate device defintions for the platform data
667
668 This writes out C platform data initialisation data and
669 U_BOOT_DEVICE() declarations for each valid node. Where a node has
670 multiple compatible strings, a #define is used to make them equivalent.
671
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +0100672 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glassd570dec2017-06-18 22:08:58 -0600673 information.
674 """
Simon Glass68d32c72017-08-29 14:16:01 -0600675 self.out_header()
Simon Glass4c73d7b2020-10-03 11:31:41 -0600676 self.out('/* Allow use of U_BOOT_DEVICE() in this file */\n')
677 self.out('#define DT_PLATDATA_C\n')
678 self.out('\n')
Simon Glass1f730c82017-06-18 22:08:59 -0600679 self.out('#include <common.h>\n')
680 self.out('#include <dm.h>\n')
681 self.out('#include <dt-structs.h>\n')
682 self.out('\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600683 nodes_to_output = list(self._valid_nodes)
684
685 # Keep outputing nodes until there is none left
686 while nodes_to_output:
687 node = nodes_to_output[0]
688 # Output all the node's dependencies first
689 for req_node in node.phandles:
690 if req_node in nodes_to_output:
Simon Glass1f730c82017-06-18 22:08:59 -0600691 self.output_node(req_node)
Simon Glassd570dec2017-06-18 22:08:58 -0600692 nodes_to_output.remove(req_node)
Simon Glass1f730c82017-06-18 22:08:59 -0600693 self.output_node(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600694 nodes_to_output.remove(node)
Simon Glass3fa797a2017-06-18 22:09:03 -0600695
Walter Lozanodc5b4372020-06-25 01:10:13 -0300696 # Define dm_populate_phandle_data() which will add the linking between
697 # nodes using DM_GET_DEVICE
698 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
699 self.buf('void dm_populate_phandle_data(void) {\n')
Walter Lozanodc5b4372020-06-25 01:10:13 -0300700 self.buf('}\n')
701
702 self.out(''.join(self.get_buf()))
Simon Glass3fa797a2017-06-18 22:09:03 -0600703
Walter Lozanod82062b2020-07-28 19:06:23 -0300704def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
705 drivers_additional=[]):
Simon Glass3fa797a2017-06-18 22:09:03 -0600706 """Run all the steps of the dtoc tool
707
708 Args:
709 args: List of non-option arguments provided to the problem
710 dtb_file: Filename of dtb file to process
711 include_disabled: True to include disabled nodes
712 output: Name of output file
713 """
714 if not args:
715 raise ValueError('Please specify a command: struct, platdata')
716
Walter Lozanod82062b2020-07-28 19:06:23 -0300717 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional)
Walter Lozanoe675d962020-07-03 08:07:17 -0300718 plat.scan_drivers()
Simon Glass3fa797a2017-06-18 22:09:03 -0600719 plat.scan_dtb()
720 plat.scan_tree()
Simon Glass1b1fe412017-08-29 14:15:50 -0600721 plat.scan_reg_sizes()
Simon Glass3fa797a2017-06-18 22:09:03 -0600722 plat.setup_output(output)
723 structs = plat.scan_structs()
724 plat.scan_phandles()
725
726 for cmd in args[0].split(','):
727 if cmd == 'struct':
728 plat.generate_structs(structs)
729 elif cmd == 'platdata':
730 plat.generate_tables()
731 else:
732 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
733 cmd)