blob: 9b27aecc140ffe64d919181896bd2c95ec29a16a [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)
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300157 _drivers_additional: List of additional drivers to use during scanning
Simon Glass7581c012017-06-18 22:08:58 -0600158 """
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300159 def __init__(self, dtb_fname, include_disabled, warning_disabled,
160 drivers_additional=[]):
Simon Glass2be282c2017-06-18 22:08:59 -0600161 self._fdt = None
Simon Glass7581c012017-06-18 22:08:58 -0600162 self._dtb_fname = dtb_fname
163 self._valid_nodes = None
Simon Glasse36024b2017-06-18 22:09:01 -0600164 self._include_disabled = include_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600165 self._outfile = None
Walter Lozano361e7332020-06-25 01:10:08 -0300166 self._warning_disabled = warning_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600167 self._lines = []
Walter Lozanodac82282020-07-03 08:07:17 -0300168 self._drivers = []
169 self._driver_aliases = {}
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300170 self._drivers_additional = drivers_additional
Walter Lozanodac82282020-07-03 08:07:17 -0300171
172 def get_normalized_compat_name(self, node):
173 """Get a node's normalized compat name
174
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300175 Returns a valid driver name by retrieving node's list of compatible
Walter Lozanodac82282020-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 Lozanodcb3ed62020-07-23 00:22:03 -0300189 compat_list_c = get_compat_name(node)
Walter Lozanodac82282020-07-03 08:07:17 -0300190
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300191 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]))
205
206 return compat_list_c[0], compat_list_c[1:]
Simon Glass7581c012017-06-18 22:08:58 -0600207
Simon Glass2be282c2017-06-18 22:08:59 -0600208 def setup_output(self, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600209 """Set up the output destination
210
Simon Glass2be282c2017-06-18 22:08:59 -0600211 Once this is done, future calls to self.out() will output to this
Simon Glass7581c012017-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 Glass2be282c2017-06-18 22:08:59 -0600222 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600223 """Output a string to the output file
224
225 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600226 line: String to output
Simon Glass7581c012017-06-18 22:08:58 -0600227 """
Simon Glass2be282c2017-06-18 22:08:59 -0600228 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600229
Simon Glass2be282c2017-06-18 22:08:59 -0600230 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600231 """Buffer up a string to send later
232
233 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600234 line: String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600235 """
Simon Glass2be282c2017-06-18 22:08:59 -0600236 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600237
Simon Glass2be282c2017-06-18 22:08:59 -0600238 def get_buf(self):
Simon Glass7581c012017-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 Glassd5031142017-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 Glass8fed2eb2017-08-29 14:15:55 -0600258 def get_phandle_argc(self, prop, node_name):
259 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600260
Simon Glass8fed2eb2017-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.
Simon Glass2925c262017-08-29 14:15:54 -0600263
Simon Glass8fed2eb2017-08-29 14:15:55 -0600264 Args:
265 prop: Prop object to check
266 Return:
267 Number of argument cells is this is a phandle, else None
268 """
Walter Lozanoad340172020-06-25 01:10:16 -0300269 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass760b7172018-07-06 10:27:31 -0600270 if not isinstance(prop.value, list):
271 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600272 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600273 i = 0
274
275 max_args = 0
276 args = []
277 while i < len(val):
278 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-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 Glass8fed2eb2017-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 Lozanoad340172020-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 Glass8fed2eb2017-08-29 14:15:55 -0600294 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300295 raise ValueError("Node '%s' has no cells property" %
296 (target.name))
Simon Glass8fed2eb2017-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 Glass2925c262017-08-29 14:15:54 -0600303
Walter Lozanodac82282020-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 Lozano6c74d1b2020-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 Glass2be282c2017-06-18 22:08:59 -0600360 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200361 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600362
Simon Glass2be282c2017-06-18 22:08:59 -0600363 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600364 device tree root node, and progress from there.
365 """
Simon Glass2be282c2017-06-18 22:08:59 -0600366 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600367
Simon Glass1b272732020-10-03 11:31:25 -0600368 def scan_node(self, root, valid_nodes):
Simon Glass2be282c2017-06-18 22:08:59 -0600369 """Scan a node and subnodes to build a tree of node and phandle info
370
Simon Glass72ab7c52017-08-29 14:15:53 -0600371 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600372
373 Args:
374 root: Root node for scan
Simon Glass1b272732020-10-03 11:31:25 -0600375 valid_nodes: List of Node objects to add to
Simon Glass2be282c2017-06-18 22:08:59 -0600376 """
Simon Glass7581c012017-06-18 22:08:58 -0600377 for node in root.subnodes:
378 if 'compatible' in node.props:
379 status = node.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600380 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600381 status.value != 'disabled'):
Simon Glass1b272732020-10-03 11:31:25 -0600382 valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600383
384 # recurse to handle any subnodes
Simon Glass1b272732020-10-03 11:31:25 -0600385 self.scan_node(node, valid_nodes)
Simon Glass7581c012017-06-18 22:08:58 -0600386
Simon Glass2be282c2017-06-18 22:08:59 -0600387 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600388 """Scan the device tree for useful information
389
390 This fills in the following properties:
Simon Glass7581c012017-06-18 22:08:58 -0600391 _valid_nodes: A list of nodes we wish to consider include in the
392 platform data
393 """
Simon Glass1b272732020-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 Glass7581c012017-06-18 22:08:58 -0600400
Simon Glassc20ee0e2017-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 Glassdfe5f5b2018-07-06 10:27:32 -0600439 raise ValueError("Node '%s' reg property is not an int" %
440 node.name)
Simon Glassc20ee0e2017-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 Glass2be282c2017-06-18 22:08:59 -0600462 def scan_structs(self):
Simon Glass7581c012017-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 Glasse4fb5fa2020-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 Glass7581c012017-06-18 22:08:58 -0600479 """
Simon Glass1b272732020-10-03 11:31:25 -0600480 structs = collections.OrderedDict()
Simon Glass7581c012017-06-18 22:08:58 -0600481 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300482 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-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 Lozanodac82282020-07-03 08:07:17 -0300506 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-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 Glass7581c012017-06-18 22:08:58 -0600513 return structs
514
Simon Glass2be282c2017-06-18 22:08:59 -0600515 def scan_phandles(self):
Simon Glass7581c012017-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 Glass8fed2eb2017-08-29 14:15:55 -0600531 info = self.get_phandle_argc(prop, node.name)
532 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600533 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600534 pos = 0
535 for args in info.args:
536 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-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 Glass634eba42017-08-29 14:15:59 -0600540 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600541
542
Simon Glass2be282c2017-06-18 22:08:59 -0600543 def generate_structs(self, structs):
Simon Glass7581c012017-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 Schuchardt2799a692020-02-25 21:35:39 +0100548 doc/driver-model/of-plat.rst for more information.
Simon Glasse4fb5fa2020-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 Glass7581c012017-06-18 22:08:58 -0600557 """
Simon Glassd5031142017-08-29 14:16:01 -0600558 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600559 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900560 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600561
562 # Output the struct definition
563 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600564 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600565 for pname in sorted(structs[name]):
566 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600567 info = self.get_phandle_argc(prop, structs[name])
568 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600569 # For phandles, include a reference to the target
Simon Glass0d154632017-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 Glass2be282c2017-06-18 22:08:59 -0600572 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600573 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600574 else:
575 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-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 Glass7581c012017-06-18 22:08:58 -0600582
Simon Glass2be282c2017-06-18 22:08:59 -0600583 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600584 """Output the C code for a node
585
586 Args:
587 node: node to output
588 """
Simon Glass26e408f2020-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 Glass8a38abf2020-10-03 11:31:40 -0600612 arg_values.append(
613 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
Simon Glass26e408f2020-10-03 09:25:18 -0600614 pos += 1 + args
Simon Glass8a38abf2020-10-03 11:31:40 -0600615 vals.append('\t{%d, {%s}}' % (target_node.idx,
616 ', '.join(arg_values)))
Simon Glass26e408f2020-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 Lozanodac82282020-07-03 08:07:17 -0300631 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600632 var_name = conv_name_to_c(node.name)
Simon Glass1b272732020-10-03 11:31:25 -0600633 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Walter Lozano51f12632020-06-25 01:10:13 -0300634 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass2be282c2017-06-18 22:08:59 -0600635 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glass1953ce72019-05-17 22:00:32 -0600636 for pname in sorted(node.props):
637 prop = node.props[pname]
Simon Glass7581c012017-06-18 22:08:58 -0600638 if pname in PROP_IGNORE_LIST or pname[0] == '#':
639 continue
Simon Glass2be282c2017-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 Glass7581c012017-06-18 22:08:58 -0600642
643 # Special handling for lists
Simon Glass2be282c2017-06-18 22:08:59 -0600644 if isinstance(prop.value, list):
Simon Glass26e408f2020-10-03 09:25:18 -0600645 _output_list(node, prop)
Simon Glass7581c012017-06-18 22:08:58 -0600646 else:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600647 self.buf(get_value(prop.type, prop.value))
Simon Glass2be282c2017-06-18 22:08:59 -0600648 self.buf(',\n')
649 self.buf('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600650
651 # Add a device declaration
Simon Glass2be282c2017-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 Glasse41651f2020-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 Glass2be282c2017-06-18 22:08:59 -0600660 self.buf('};\n')
661 self.buf('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600662
Simon Glass2be282c2017-06-18 22:08:59 -0600663 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600664
Simon Glass2be282c2017-06-18 22:08:59 -0600665 def generate_tables(self):
Simon Glass7581c012017-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 Schuchardt2799a692020-02-25 21:35:39 +0100672 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600673 information.
674 """
Simon Glassd5031142017-08-29 14:16:01 -0600675 self.out_header()
Simon Glasscb43ac12020-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 Glass2be282c2017-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 Glass7581c012017-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 Glass2be282c2017-06-18 22:08:59 -0600691 self.output_node(req_node)
Simon Glass7581c012017-06-18 22:08:58 -0600692 nodes_to_output.remove(req_node)
Simon Glass2be282c2017-06-18 22:08:59 -0600693 self.output_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600694 nodes_to_output.remove(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600695
Walter Lozano51f12632020-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 Lozano51f12632020-06-25 01:10:13 -0300700 self.buf('}\n')
701
702 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600703
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300704def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
705 drivers_additional=[]):
Simon Glassfa0ea5b2017-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 Lozano6c74d1b2020-07-28 19:06:23 -0300717 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional)
Walter Lozanodac82282020-07-03 08:07:17 -0300718 plat.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600719 plat.scan_dtb()
720 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600721 plat.scan_reg_sizes()
Simon Glassfa0ea5b2017-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)