blob: 8fdf49f8091f5a26d0cb9f0d0b59879163d2d95a [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
57
Simon Glass2be282c2017-06-18 22:08:59 -060058def conv_name_to_c(name):
Simon Glass7581c012017-06-18 22:08:58 -060059 """Convert a device-tree name to a C identifier
60
Simon Glass30107b02017-06-18 22:09:04 -060061 This uses multiple replace() calls instead of re.sub() since it is faster
62 (400ms for 1m calls versus 1000ms for the 're' version).
63
Simon Glass7581c012017-06-18 22:08:58 -060064 Args:
65 name: Name to convert
66 Return:
67 String containing the C version of this name
68 """
Simon Glass2be282c2017-06-18 22:08:59 -060069 new = name.replace('@', '_at_')
70 new = new.replace('-', '_')
71 new = new.replace(',', '_')
72 new = new.replace('.', '_')
Simon Glass2be282c2017-06-18 22:08:59 -060073 return new
Simon Glass7581c012017-06-18 22:08:58 -060074
Simon Glass2be282c2017-06-18 22:08:59 -060075def tab_to(num_tabs, line):
76 """Append tabs to a line of text to reach a tab stop.
Simon Glass7581c012017-06-18 22:08:58 -060077
Simon Glass2be282c2017-06-18 22:08:59 -060078 Args:
79 num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
80 line: Line of text to append to
81
82 Returns:
83 line with the correct number of tabs appeneded. If the line already
84 extends past that tab stop then a single space is appended.
85 """
86 if len(line) >= num_tabs * 8:
87 return line + ' '
88 return line + '\t' * (num_tabs - len(line) // 8)
89
Simon Glass56e0bbe2017-06-18 22:09:02 -060090def get_value(ftype, value):
91 """Get a value as a C expression
92
93 For integers this returns a byte-swapped (little-endian) hex string
94 For bytes this returns a hex string, e.g. 0x12
95 For strings this returns a literal string enclosed in quotes
96 For booleans this return 'true'
97
98 Args:
99 type: Data type (fdt_util)
100 value: Data value, as a string of bytes
101 """
102 if ftype == fdt.TYPE_INT:
103 return '%#x' % fdt_util.fdt32_to_cpu(value)
104 elif ftype == fdt.TYPE_BYTE:
Simon Glass9b044f72019-05-17 22:00:43 -0600105 return '%#x' % tools.ToByte(value[0])
Simon Glass56e0bbe2017-06-18 22:09:02 -0600106 elif ftype == fdt.TYPE_STRING:
Simon Glassf02d0eb2020-07-07 21:32:06 -0600107 # Handle evil ACPI backslashes by adding another backslash before them.
108 # So "\\_SB.GPO0" in the device tree effectively stays like that in C
109 return '"%s"' % value.replace('\\', '\\\\')
Simon Glass56e0bbe2017-06-18 22:09:02 -0600110 elif ftype == fdt.TYPE_BOOL:
111 return 'true'
Simon Glassfbdfd222017-08-29 14:15:48 -0600112 elif ftype == fdt.TYPE_INT64:
113 return '%#x' % value
Simon Glass56e0bbe2017-06-18 22:09:02 -0600114
115def get_compat_name(node):
116 """Get a node's first compatible string as a C identifier
117
118 Args:
119 node: Node object to check
120 Return:
121 Tuple:
122 C identifier for the first compatible string
123 List of C identifiers for all the other compatible strings
124 (possibly empty)
125 """
126 compat = node.props['compatible'].value
127 aliases = []
128 if isinstance(compat, list):
129 compat, aliases = compat[0], compat[1:]
130 return conv_name_to_c(compat), [conv_name_to_c(a) for a in aliases]
131
Simon Glass56e0bbe2017-06-18 22:09:02 -0600132
Simon Glass2be282c2017-06-18 22:08:59 -0600133class DtbPlatdata(object):
Simon Glass7581c012017-06-18 22:08:58 -0600134 """Provide a means to convert device tree binary data to platform data
135
136 The output of this process is C structures which can be used in space-
137 constrained encvironments where the ~3KB code overhead of device tree
138 code is not affordable.
139
140 Properties:
Simon Glass2be282c2017-06-18 22:08:59 -0600141 _fdt: Fdt object, referencing the device tree
Simon Glass7581c012017-06-18 22:08:58 -0600142 _dtb_fname: Filename of the input device tree binary file
143 _valid_nodes: A list of Node object with compatible strings
Simon Glasse36024b2017-06-18 22:09:01 -0600144 _include_disabled: true to include nodes marked status = "disabled"
Simon Glass7581c012017-06-18 22:08:58 -0600145 _outfile: The current output file (sys.stdout or a real file)
Walter Lozano361e7332020-06-25 01:10:08 -0300146 _warning_disabled: true to disable warnings about driver names not found
Simon Glass7581c012017-06-18 22:08:58 -0600147 _lines: Stashed list of output lines for outputting in the future
Walter Lozanoace16e82020-06-25 01:10:05 -0300148 _aliases: Dict that hold aliases for compatible strings
149 key: First compatible string declared in a node
150 value: List of additional compatible strings declared in a node
Walter Lozanodac82282020-07-03 08:07:17 -0300151 _drivers: List of valid driver names found in drivers/
152 _driver_aliases: Dict that holds aliases for driver names
153 key: Driver alias declared with
154 U_BOOT_DRIVER_ALIAS(driver_alias, driver_name)
155 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Walter Lozano51f12632020-06-25 01:10:13 -0300156 _links: List of links to be included in dm_populate_phandle_data()
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 = []
168 self._aliases = {}
Walter Lozanodac82282020-07-03 08:07:17 -0300169 self._drivers = []
170 self._driver_aliases = {}
Walter Lozano51f12632020-06-25 01:10:13 -0300171 self._links = []
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300172 self._drivers_additional = drivers_additional
Walter Lozanodac82282020-07-03 08:07:17 -0300173
174 def get_normalized_compat_name(self, node):
175 """Get a node's normalized compat name
176
177 Returns a valid driver name by retrieving node's first compatible
178 string as a C identifier and performing a check against _drivers
179 and a lookup in driver_aliases printing a warning in case of failure.
180
181 Args:
182 node: Node object to check
183 Return:
184 Tuple:
185 Driver name associated with the first compatible string
186 List of C identifiers for all the other compatible strings
187 (possibly empty)
188 In case of no match found, the return will be the same as
189 get_compat_name()
190 """
191 compat_c, aliases_c = get_compat_name(node)
192 if compat_c not in self._drivers:
193 compat_c_old = compat_c
194 compat_c = self._driver_aliases.get(compat_c)
195 if not compat_c:
Walter Lozano361e7332020-06-25 01:10:08 -0300196 if not self._warning_disabled:
197 print('WARNING: the driver %s was not found in the driver list'
198 % (compat_c_old))
Walter Lozanodac82282020-07-03 08:07:17 -0300199 compat_c = compat_c_old
200 else:
201 aliases_c = [compat_c_old] + aliases_c
202
203 return compat_c, aliases_c
Simon Glass7581c012017-06-18 22:08:58 -0600204
Simon Glass2be282c2017-06-18 22:08:59 -0600205 def setup_output(self, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600206 """Set up the output destination
207
Simon Glass2be282c2017-06-18 22:08:59 -0600208 Once this is done, future calls to self.out() will output to this
Simon Glass7581c012017-06-18 22:08:58 -0600209 file.
210
211 Args:
212 fname: Filename to send output to, or '-' for stdout
213 """
214 if fname == '-':
215 self._outfile = sys.stdout
216 else:
217 self._outfile = open(fname, 'w')
218
Simon Glass2be282c2017-06-18 22:08:59 -0600219 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600220 """Output a string to the output file
221
222 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600223 line: String to output
Simon Glass7581c012017-06-18 22:08:58 -0600224 """
Simon Glass2be282c2017-06-18 22:08:59 -0600225 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600226
Simon Glass2be282c2017-06-18 22:08:59 -0600227 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600228 """Buffer up a string to send later
229
230 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600231 line: String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600232 """
Simon Glass2be282c2017-06-18 22:08:59 -0600233 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600234
Simon Glass2be282c2017-06-18 22:08:59 -0600235 def get_buf(self):
Simon Glass7581c012017-06-18 22:08:58 -0600236 """Get the contents of the output buffer, and clear it
237
238 Returns:
239 The output buffer, which is then cleared for future use
240 """
241 lines = self._lines
242 self._lines = []
243 return lines
244
Simon Glassd5031142017-08-29 14:16:01 -0600245 def out_header(self):
246 """Output a message indicating that this is an auto-generated file"""
247 self.out('''/*
248 * DO NOT MODIFY
249 *
250 * This file was generated by dtoc from a .dtb (device tree binary) file.
251 */
252
253''')
254
Simon Glass8fed2eb2017-08-29 14:15:55 -0600255 def get_phandle_argc(self, prop, node_name):
256 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600257
Simon Glass8fed2eb2017-08-29 14:15:55 -0600258 We have no reliable way of detecting whether a node uses a phandle
259 or not. As an interim measure, use a list of known property names.
Simon Glass2925c262017-08-29 14:15:54 -0600260
Simon Glass8fed2eb2017-08-29 14:15:55 -0600261 Args:
262 prop: Prop object to check
263 Return:
264 Number of argument cells is this is a phandle, else None
265 """
Walter Lozanoad340172020-06-25 01:10:16 -0300266 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass760b7172018-07-06 10:27:31 -0600267 if not isinstance(prop.value, list):
268 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600269 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600270 i = 0
271
272 max_args = 0
273 args = []
274 while i < len(val):
275 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-07-06 10:27:31 -0600276 # If we get to the end of the list, stop. This can happen
277 # since some nodes have more phandles in the list than others,
278 # but we allocate enough space for the largest list. So those
279 # nodes with shorter lists end up with zeroes at the end.
280 if not phandle:
281 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600282 target = self._fdt.phandle_to_node.get(phandle)
283 if not target:
284 raise ValueError("Cannot parse '%s' in node '%s'" %
285 (prop.name, node_name))
Walter Lozanoad340172020-06-25 01:10:16 -0300286 cells = None
287 for prop_name in ['#clock-cells', '#gpio-cells']:
288 cells = target.props.get(prop_name)
289 if cells:
290 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600291 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300292 raise ValueError("Node '%s' has no cells property" %
293 (target.name))
Simon Glass8fed2eb2017-08-29 14:15:55 -0600294 num_args = fdt_util.fdt32_to_cpu(cells.value)
295 max_args = max(max_args, num_args)
296 args.append(num_args)
297 i += 1 + num_args
298 return PhandleInfo(max_args, args)
299 return None
Simon Glass2925c262017-08-29 14:15:54 -0600300
Walter Lozanodac82282020-07-03 08:07:17 -0300301 def scan_driver(self, fn):
302 """Scan a driver file to build a list of driver names and aliases
303
304 This procedure will populate self._drivers and self._driver_aliases
305
306 Args
307 fn: Driver filename to scan
308 """
309 with open(fn, encoding='utf-8') as fd:
310 try:
311 buff = fd.read()
312 except UnicodeDecodeError:
313 # This seems to happen on older Python versions
314 print("Skipping file '%s' due to unicode error" % fn)
315 return
316
317 # The following re will search for driver names declared as
318 # U_BOOT_DRIVER(driver_name)
319 drivers = re.findall('U_BOOT_DRIVER\((.*)\)', buff)
320
321 for driver in drivers:
322 self._drivers.append(driver)
323
324 # The following re will search for driver aliases declared as
325 # U_BOOT_DRIVER_ALIAS(alias, driver_name)
326 driver_aliases = re.findall('U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
327 buff)
328
329 for alias in driver_aliases: # pragma: no cover
330 if len(alias) != 2:
331 continue
332 self._driver_aliases[alias[1]] = alias[0]
333
334 def scan_drivers(self):
335 """Scan the driver folders to build a list of driver names and aliases
336
337 This procedure will populate self._drivers and self._driver_aliases
338
339 """
340 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
341 if basedir == '':
342 basedir = './'
343 for (dirpath, dirnames, filenames) in os.walk(basedir):
344 for fn in filenames:
345 if not fn.endswith('.c'):
346 continue
347 self.scan_driver(dirpath + '/' + fn)
348
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300349 for fn in self._drivers_additional:
350 if not isinstance(fn, str) or len(fn) == 0:
351 continue
352 if fn[0] == '/':
353 self.scan_driver(fn)
354 else:
355 self.scan_driver(basedir + '/' + fn)
356
Simon Glass2be282c2017-06-18 22:08:59 -0600357 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200358 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600359
Simon Glass2be282c2017-06-18 22:08:59 -0600360 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600361 device tree root node, and progress from there.
362 """
Simon Glass2be282c2017-06-18 22:08:59 -0600363 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600364
Simon Glass2be282c2017-06-18 22:08:59 -0600365 def scan_node(self, root):
366 """Scan a node and subnodes to build a tree of node and phandle info
367
Simon Glass72ab7c52017-08-29 14:15:53 -0600368 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600369
370 Args:
371 root: Root node for scan
372 """
Simon Glass7581c012017-06-18 22:08:58 -0600373 for node in root.subnodes:
374 if 'compatible' in node.props:
375 status = node.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600376 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600377 status.value != 'disabled'):
Simon Glass7581c012017-06-18 22:08:58 -0600378 self._valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600379
380 # recurse to handle any subnodes
Simon Glass2be282c2017-06-18 22:08:59 -0600381 self.scan_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600382
Simon Glass2be282c2017-06-18 22:08:59 -0600383 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600384 """Scan the device tree for useful information
385
386 This fills in the following properties:
Simon Glass7581c012017-06-18 22:08:58 -0600387 _valid_nodes: A list of nodes we wish to consider include in the
388 platform data
389 """
Simon Glass7581c012017-06-18 22:08:58 -0600390 self._valid_nodes = []
Simon Glass2be282c2017-06-18 22:08:59 -0600391 return self.scan_node(self._fdt.GetRoot())
Simon Glass7581c012017-06-18 22:08:58 -0600392
Simon Glassc20ee0e2017-08-29 14:15:50 -0600393 @staticmethod
394 def get_num_cells(node):
395 """Get the number of cells in addresses and sizes for this node
396
397 Args:
398 node: Node to check
399
400 Returns:
401 Tuple:
402 Number of address cells for this node
403 Number of size cells for this node
404 """
405 parent = node.parent
406 na, ns = 2, 2
407 if parent:
408 na_prop = parent.props.get('#address-cells')
409 ns_prop = parent.props.get('#size-cells')
410 if na_prop:
411 na = fdt_util.fdt32_to_cpu(na_prop.value)
412 if ns_prop:
413 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
414 return na, ns
415
416 def scan_reg_sizes(self):
417 """Scan for 64-bit 'reg' properties and update the values
418
419 This finds 'reg' properties with 64-bit data and converts the value to
420 an array of 64-values. This allows it to be output in a way that the
421 C code can read.
422 """
423 for node in self._valid_nodes:
424 reg = node.props.get('reg')
425 if not reg:
426 continue
427 na, ns = self.get_num_cells(node)
428 total = na + ns
429
430 if reg.type != fdt.TYPE_INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600431 raise ValueError("Node '%s' reg property is not an int" %
432 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600433 if len(reg.value) % total:
434 raise ValueError("Node '%s' reg property has %d cells "
435 'which is not a multiple of na + ns = %d + %d)' %
436 (node.name, len(reg.value), na, ns))
437 reg.na = na
438 reg.ns = ns
439 if na != 1 or ns != 1:
440 reg.type = fdt.TYPE_INT64
441 i = 0
442 new_value = []
443 val = reg.value
444 if not isinstance(val, list):
445 val = [val]
446 while i < len(val):
447 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
448 i += na
449 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
450 i += ns
451 new_value += [addr, size]
452 reg.value = new_value
453
Simon Glass2be282c2017-06-18 22:08:59 -0600454 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600455 """Scan the device tree building up the C structures we will use.
456
457 Build a dict keyed by C struct name containing a dict of Prop
458 object for each struct field (keyed by property name). Where the
459 same struct appears multiple times, try to use the 'widest'
460 property, i.e. the one with a type which can express all others.
461
462 Once the widest property is determined, all other properties are
463 updated to match that width.
464 """
465 structs = {}
466 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300467 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600468 fields = {}
469
470 # Get a list of all the valid properties in this node.
471 for name, prop in node.props.items():
472 if name not in PROP_IGNORE_LIST and name[0] != '#':
473 fields[name] = copy.deepcopy(prop)
474
475 # If we've seen this node_name before, update the existing struct.
476 if node_name in structs:
477 struct = structs[node_name]
478 for name, prop in fields.items():
479 oldprop = struct.get(name)
480 if oldprop:
481 oldprop.Widen(prop)
482 else:
483 struct[name] = prop
484
485 # Otherwise store this as a new struct.
486 else:
487 structs[node_name] = fields
488
489 upto = 0
490 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300491 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600492 struct = structs[node_name]
493 for name, prop in node.props.items():
494 if name not in PROP_IGNORE_LIST and name[0] != '#':
495 prop.Widen(struct[name])
496 upto += 1
497
Walter Lozanodac82282020-07-03 08:07:17 -0300498 struct_name, aliases = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600499 for alias in aliases:
500 self._aliases[alias] = struct_name
501
502 return structs
503
Simon Glass2be282c2017-06-18 22:08:59 -0600504 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600505 """Figure out what phandles each node uses
506
507 We need to be careful when outputing nodes that use phandles since
508 they must come after the declaration of the phandles in the C file.
509 Otherwise we get a compiler error since the phandle struct is not yet
510 declared.
511
512 This function adds to each node a list of phandle nodes that the node
513 depends on. This allows us to output things in the right order.
514 """
515 for node in self._valid_nodes:
516 node.phandles = set()
517 for pname, prop in node.props.items():
518 if pname in PROP_IGNORE_LIST or pname[0] == '#':
519 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600520 info = self.get_phandle_argc(prop, node.name)
521 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600522 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600523 pos = 0
524 for args in info.args:
525 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600526 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
527 target_node = self._fdt.phandle_to_node[phandle]
528 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600529 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600530
531
Simon Glass2be282c2017-06-18 22:08:59 -0600532 def generate_structs(self, structs):
Simon Glass7581c012017-06-18 22:08:58 -0600533 """Generate struct defintions for the platform data
534
535 This writes out the body of a header file consisting of structure
536 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100537 doc/driver-model/of-plat.rst for more information.
Simon Glass7581c012017-06-18 22:08:58 -0600538 """
Simon Glassd5031142017-08-29 14:16:01 -0600539 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600540 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900541 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600542
543 # Output the struct definition
544 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600545 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600546 for pname in sorted(structs[name]):
547 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600548 info = self.get_phandle_argc(prop, structs[name])
549 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600550 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600551 struct_name = 'struct phandle_%d_arg' % info.max_args
552 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600553 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600554 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600555 else:
556 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600557 self.out('\t%s%s' % (tab_to(2, ptype),
558 conv_name_to_c(prop.name)))
559 if isinstance(prop.value, list):
560 self.out('[%d]' % len(prop.value))
561 self.out(';\n')
562 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600563
Simon Glass90a81322019-05-17 22:00:31 -0600564 for alias, struct_name in self._aliases.items():
Heiko Schochere9cde872019-04-16 13:31:58 +0200565 if alias not in sorted(structs):
566 self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
567 STRUCT_PREFIX, struct_name))
Simon Glass7581c012017-06-18 22:08:58 -0600568
Simon Glass2be282c2017-06-18 22:08:59 -0600569 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600570 """Output the C code for a node
571
572 Args:
573 node: node to output
574 """
Walter Lozanodac82282020-07-03 08:07:17 -0300575 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600576 var_name = conv_name_to_c(node.name)
Walter Lozano51f12632020-06-25 01:10:13 -0300577 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass2be282c2017-06-18 22:08:59 -0600578 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glass1953ce72019-05-17 22:00:32 -0600579 for pname in sorted(node.props):
580 prop = node.props[pname]
Simon Glass7581c012017-06-18 22:08:58 -0600581 if pname in PROP_IGNORE_LIST or pname[0] == '#':
582 continue
Simon Glass2be282c2017-06-18 22:08:59 -0600583 member_name = conv_name_to_c(prop.name)
584 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
Simon Glass7581c012017-06-18 22:08:58 -0600585
586 # Special handling for lists
Simon Glass2be282c2017-06-18 22:08:59 -0600587 if isinstance(prop.value, list):
588 self.buf('{')
Simon Glass7581c012017-06-18 22:08:58 -0600589 vals = []
590 # For phandles, output a reference to the platform data
591 # of the target node.
Simon Glass8fed2eb2017-08-29 14:15:55 -0600592 info = self.get_phandle_argc(prop, node.name)
593 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600594 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600595 pos = 0
Walter Lozano51f12632020-06-25 01:10:13 -0300596 item = 0
Simon Glass634eba42017-08-29 14:15:59 -0600597 for args in info.args:
598 phandle_cell = prop.value[pos]
Simon Glass7581c012017-06-18 22:08:58 -0600599 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
Simon Glass72ab7c52017-08-29 14:15:53 -0600600 target_node = self._fdt.phandle_to_node[phandle]
Simon Glass2be282c2017-06-18 22:08:59 -0600601 name = conv_name_to_c(target_node.name)
Simon Glass634eba42017-08-29 14:15:59 -0600602 arg_values = []
603 for i in range(args):
604 arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
605 pos += 1 + args
Walter Lozano51f12632020-06-25 01:10:13 -0300606 # node member is filled with NULL as the real value
607 # will be update at run-time during dm_init_and_scan()
608 # by dm_populate_phandle_data()
609 vals.append('\t{NULL, {%s}}' % (', '.join(arg_values)))
610 var_node = '%s%s.%s[%d].node' % \
611 (VAL_PREFIX, var_name, member_name, item)
612 # Save the the link information to be use to define
613 # dm_populate_phandle_data()
614 self._links.append({'var_node': var_node, 'dev_name': name})
615 item += 1
Simon Glass35d50372017-08-29 14:15:57 -0600616 for val in vals:
617 self.buf('\n\t\t%s,' % val)
Simon Glass7581c012017-06-18 22:08:58 -0600618 else:
619 for val in prop.value:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600620 vals.append(get_value(prop.type, val))
Simon Glass21d54ac2017-08-29 14:15:49 -0600621
Simon Glass35d50372017-08-29 14:15:57 -0600622 # Put 8 values per line to avoid very long lines.
Simon Glass90a81322019-05-17 22:00:31 -0600623 for i in range(0, len(vals), 8):
Simon Glass35d50372017-08-29 14:15:57 -0600624 if i:
625 self.buf(',\n\t\t')
626 self.buf(', '.join(vals[i:i + 8]))
Simon Glass2be282c2017-06-18 22:08:59 -0600627 self.buf('}')
Simon Glass7581c012017-06-18 22:08:58 -0600628 else:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600629 self.buf(get_value(prop.type, prop.value))
Simon Glass2be282c2017-06-18 22:08:59 -0600630 self.buf(',\n')
631 self.buf('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600632
633 # Add a device declaration
Simon Glass2be282c2017-06-18 22:08:59 -0600634 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
635 self.buf('\t.name\t\t= "%s",\n' % struct_name)
636 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
637 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
638 self.buf('};\n')
639 self.buf('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600640
Simon Glass2be282c2017-06-18 22:08:59 -0600641 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600642
Simon Glass2be282c2017-06-18 22:08:59 -0600643 def generate_tables(self):
Simon Glass7581c012017-06-18 22:08:58 -0600644 """Generate device defintions for the platform data
645
646 This writes out C platform data initialisation data and
647 U_BOOT_DEVICE() declarations for each valid node. Where a node has
648 multiple compatible strings, a #define is used to make them equivalent.
649
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100650 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600651 information.
652 """
Simon Glassd5031142017-08-29 14:16:01 -0600653 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600654 self.out('#include <common.h>\n')
655 self.out('#include <dm.h>\n')
656 self.out('#include <dt-structs.h>\n')
657 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600658 nodes_to_output = list(self._valid_nodes)
659
660 # Keep outputing nodes until there is none left
661 while nodes_to_output:
662 node = nodes_to_output[0]
663 # Output all the node's dependencies first
664 for req_node in node.phandles:
665 if req_node in nodes_to_output:
Simon Glass2be282c2017-06-18 22:08:59 -0600666 self.output_node(req_node)
Simon Glass7581c012017-06-18 22:08:58 -0600667 nodes_to_output.remove(req_node)
Simon Glass2be282c2017-06-18 22:08:59 -0600668 self.output_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600669 nodes_to_output.remove(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600670
Walter Lozano51f12632020-06-25 01:10:13 -0300671 # Define dm_populate_phandle_data() which will add the linking between
672 # nodes using DM_GET_DEVICE
673 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
674 self.buf('void dm_populate_phandle_data(void) {\n')
675 for l in self._links:
Walter Lozanoad340172020-06-25 01:10:16 -0300676 self.buf('\t%s = DM_GET_DEVICE(%s);\n' %
677 (l['var_node'], l['dev_name']))
Walter Lozano51f12632020-06-25 01:10:13 -0300678 self.buf('}\n')
679
680 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600681
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300682def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
683 drivers_additional=[]):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600684 """Run all the steps of the dtoc tool
685
686 Args:
687 args: List of non-option arguments provided to the problem
688 dtb_file: Filename of dtb file to process
689 include_disabled: True to include disabled nodes
690 output: Name of output file
691 """
692 if not args:
693 raise ValueError('Please specify a command: struct, platdata')
694
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300695 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional)
Walter Lozanodac82282020-07-03 08:07:17 -0300696 plat.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600697 plat.scan_dtb()
698 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600699 plat.scan_reg_sizes()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600700 plat.setup_output(output)
701 structs = plat.scan_structs()
702 plat.scan_phandles()
703
704 for cmd in args[0].split(','):
705 if cmd == 'struct':
706 plat.generate_structs(structs)
707 elif cmd == 'platdata':
708 plat.generate_tables()
709 else:
710 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
711 cmd)