blob: 8fdf49f8091f5a26d0cb9f0d0b59879163d2d95a [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
57
Simon Glass1f730c82017-06-18 22:08:59 -060058def conv_name_to_c(name):
Simon Glassd570dec2017-06-18 22:08:58 -060059 """Convert a device-tree name to a C identifier
60
Simon Glass2e0bf3f2017-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 Glassd570dec2017-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 Glass1f730c82017-06-18 22:08:59 -060069 new = name.replace('@', '_at_')
70 new = new.replace('-', '_')
71 new = new.replace(',', '_')
72 new = new.replace('.', '_')
Simon Glass1f730c82017-06-18 22:08:59 -060073 return new
Simon Glassd570dec2017-06-18 22:08:58 -060074
Simon Glass1f730c82017-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 Glassd570dec2017-06-18 22:08:58 -060077
Simon Glass1f730c82017-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 Glass7a2add12017-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 Glass534eda92019-05-17 22:00:43 -0600105 return '%#x' % tools.ToByte(value[0])
Simon Glass7a2add12017-06-18 22:09:02 -0600106 elif ftype == fdt.TYPE_STRING:
Simon Glass7f5e2262020-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 Glass7a2add12017-06-18 22:09:02 -0600110 elif ftype == fdt.TYPE_BOOL:
111 return 'true'
Simon Glassfc3ae9c2017-08-29 14:15:48 -0600112 elif ftype == fdt.TYPE_INT64:
113 return '%#x' % value
Simon Glass7a2add12017-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 Glass7a2add12017-06-18 22:09:02 -0600132
Simon Glass1f730c82017-06-18 22:08:59 -0600133class DtbPlatdata(object):
Simon Glassd570dec2017-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 Glass1f730c82017-06-18 22:08:59 -0600141 _fdt: Fdt object, referencing the device tree
Simon Glassd570dec2017-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 Glasseab3f622017-06-18 22:09:01 -0600144 _include_disabled: true to include nodes marked status = "disabled"
Simon Glassd570dec2017-06-18 22:08:58 -0600145 _outfile: The current output file (sys.stdout or a real file)
Walter Lozanoa324e412020-06-25 01:10:08 -0300146 _warning_disabled: true to disable warnings about driver names not found
Simon Glassd570dec2017-06-18 22:08:58 -0600147 _lines: Stashed list of output lines for outputting in the future
Walter Lozano8b239f42020-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 Lozanoe675d962020-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 Lozanodc5b4372020-06-25 01:10:13 -0300156 _links: List of links to be included in dm_populate_phandle_data()
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 = []
168 self._aliases = {}
Walter Lozanoe675d962020-07-03 08:07:17 -0300169 self._drivers = []
170 self._driver_aliases = {}
Walter Lozanodc5b4372020-06-25 01:10:13 -0300171 self._links = []
Walter Lozanod82062b2020-07-28 19:06:23 -0300172 self._drivers_additional = drivers_additional
Walter Lozanoe675d962020-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 Lozanoa324e412020-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 Lozanoe675d962020-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 Glassd570dec2017-06-18 22:08:58 -0600204
Simon Glass1f730c82017-06-18 22:08:59 -0600205 def setup_output(self, fname):
Simon Glassd570dec2017-06-18 22:08:58 -0600206 """Set up the output destination
207
Simon Glass1f730c82017-06-18 22:08:59 -0600208 Once this is done, future calls to self.out() will output to this
Simon Glassd570dec2017-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 Glass1f730c82017-06-18 22:08:59 -0600219 def out(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600220 """Output a string to the output file
221
222 Args:
Simon Glass1f730c82017-06-18 22:08:59 -0600223 line: String to output
Simon Glassd570dec2017-06-18 22:08:58 -0600224 """
Simon Glass1f730c82017-06-18 22:08:59 -0600225 self._outfile.write(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600226
Simon Glass1f730c82017-06-18 22:08:59 -0600227 def buf(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600228 """Buffer up a string to send later
229
230 Args:
Simon Glass1f730c82017-06-18 22:08:59 -0600231 line: String to add to our 'buffer' list
Simon Glassd570dec2017-06-18 22:08:58 -0600232 """
Simon Glass1f730c82017-06-18 22:08:59 -0600233 self._lines.append(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600234
Simon Glass1f730c82017-06-18 22:08:59 -0600235 def get_buf(self):
Simon Glassd570dec2017-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 Glass68d32c72017-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 Glassec3b5e42017-08-29 14:15:55 -0600255 def get_phandle_argc(self, prop, node_name):
256 """Check if a node contains phandles
Simon Glassce5621d2017-08-29 14:15:54 -0600257
Simon Glassec3b5e42017-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.
260
261 Args:
262 prop: Prop object to check
263 Return:
264 Number of argument cells is this is a phandle, else None
265 """
Walter Lozano179f0b62020-06-25 01:10:16 -0300266 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass609e2b12018-07-06 10:27:31 -0600267 if not isinstance(prop.value, list):
268 prop.value = [prop.value]
Simon Glassec3b5e42017-08-29 14:15:55 -0600269 val = prop.value
Simon Glassec3b5e42017-08-29 14:15:55 -0600270 i = 0
Simon Glassce5621d2017-08-29 14:15:54 -0600271
Simon Glassec3b5e42017-08-29 14:15:55 -0600272 max_args = 0
273 args = []
274 while i < len(val):
275 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass609e2b12018-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 Glassec3b5e42017-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 Lozano179f0b62020-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 Glassec3b5e42017-08-29 14:15:55 -0600291 if not cells:
Walter Lozano179f0b62020-06-25 01:10:16 -0300292 raise ValueError("Node '%s' has no cells property" %
293 (target.name))
Simon Glassec3b5e42017-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 Glassce5621d2017-08-29 14:15:54 -0600300
Walter Lozanoe675d962020-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 Lozanod82062b2020-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 Glass1f730c82017-06-18 22:08:59 -0600357 def scan_dtb(self):
Anatolij Gustschinda707d42017-08-18 17:58:51 +0200358 """Scan the device tree to obtain a tree of nodes and properties
Simon Glassd570dec2017-06-18 22:08:58 -0600359
Simon Glass1f730c82017-06-18 22:08:59 -0600360 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glassd570dec2017-06-18 22:08:58 -0600361 device tree root node, and progress from there.
362 """
Simon Glass1f730c82017-06-18 22:08:59 -0600363 self._fdt = fdt.FdtScan(self._dtb_fname)
364
365 def scan_node(self, root):
366 """Scan a node and subnodes to build a tree of node and phandle info
Simon Glassd570dec2017-06-18 22:08:58 -0600367
Simon Glass17ffd622017-08-29 14:15:53 -0600368 This adds each node to self._valid_nodes.
Simon Glass1f730c82017-06-18 22:08:59 -0600369
370 Args:
371 root: Root node for scan
372 """
Simon Glassd570dec2017-06-18 22:08:58 -0600373 for node in root.subnodes:
374 if 'compatible' in node.props:
375 status = node.props.get('status')
Simon Glasseab3f622017-06-18 22:09:01 -0600376 if (not self._include_disabled and not status or
Simon Glass1f730c82017-06-18 22:08:59 -0600377 status.value != 'disabled'):
Simon Glassd570dec2017-06-18 22:08:58 -0600378 self._valid_nodes.append(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600379
380 # recurse to handle any subnodes
Simon Glass1f730c82017-06-18 22:08:59 -0600381 self.scan_node(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600382
Simon Glass1f730c82017-06-18 22:08:59 -0600383 def scan_tree(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600384 """Scan the device tree for useful information
385
386 This fills in the following properties:
Simon Glassd570dec2017-06-18 22:08:58 -0600387 _valid_nodes: A list of nodes we wish to consider include in the
388 platform data
389 """
Simon Glassd570dec2017-06-18 22:08:58 -0600390 self._valid_nodes = []
Simon Glass1f730c82017-06-18 22:08:59 -0600391 return self.scan_node(self._fdt.GetRoot())
Simon Glassd570dec2017-06-18 22:08:58 -0600392
Simon Glass1b1fe412017-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 Glassc38fee042018-07-06 10:27:32 -0600431 raise ValueError("Node '%s' reg property is not an int" %
432 node.name)
Simon Glass1b1fe412017-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 Glass1f730c82017-06-18 22:08:59 -0600454 def scan_structs(self):
Simon Glassd570dec2017-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 Lozanoe675d962020-07-03 08:07:17 -0300467 node_name, _ = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-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 Lozanoe675d962020-07-03 08:07:17 -0300491 node_name, _ = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-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 Lozanoe675d962020-07-03 08:07:17 -0300498 struct_name, aliases = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600499 for alias in aliases:
500 self._aliases[alias] = struct_name
501
502 return structs
503
Simon Glass1f730c82017-06-18 22:08:59 -0600504 def scan_phandles(self):
Simon Glassd570dec2017-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 Glassec3b5e42017-08-29 14:15:55 -0600520 info = self.get_phandle_argc(prop, node.name)
521 if info:
Simon Glassec3b5e42017-08-29 14:15:55 -0600522 # Process the list as pairs of (phandle, id)
Simon Glass3deeb472017-08-29 14:15:59 -0600523 pos = 0
524 for args in info.args:
525 phandle_cell = prop.value[pos]
Simon Glassec3b5e42017-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 Glass3deeb472017-08-29 14:15:59 -0600529 pos += 1 + args
Simon Glassd570dec2017-06-18 22:08:58 -0600530
531
Simon Glass1f730c82017-06-18 22:08:59 -0600532 def generate_structs(self, structs):
Simon Glassd570dec2017-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 Schuchardtc79f03c2020-02-25 21:35:39 +0100537 doc/driver-model/of-plat.rst for more information.
Simon Glassd570dec2017-06-18 22:08:58 -0600538 """
Simon Glass68d32c72017-08-29 14:16:01 -0600539 self.out_header()
Simon Glass1f730c82017-06-18 22:08:59 -0600540 self.out('#include <stdbool.h>\n')
Masahiro Yamada75f82d02018-03-05 01:20:11 +0900541 self.out('#include <linux/libfdt.h>\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600542
543 # Output the struct definition
544 for name in sorted(structs):
Simon Glass1f730c82017-06-18 22:08:59 -0600545 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glassd570dec2017-06-18 22:08:58 -0600546 for pname in sorted(structs[name]):
547 prop = structs[name][pname]
Simon Glassec3b5e42017-08-29 14:15:55 -0600548 info = self.get_phandle_argc(prop, structs[name])
549 if info:
Simon Glassd570dec2017-06-18 22:08:58 -0600550 # For phandles, include a reference to the target
Simon Glasse94414b2017-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 Glass1f730c82017-06-18 22:08:59 -0600553 conv_name_to_c(prop.name),
Simon Glass3deeb472017-08-29 14:15:59 -0600554 len(info.args)))
Simon Glassd570dec2017-06-18 22:08:58 -0600555 else:
556 ptype = TYPE_NAMES[prop.type]
Simon Glass1f730c82017-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 Glassd570dec2017-06-18 22:08:58 -0600563
Simon Glass61b88e52019-05-17 22:00:31 -0600564 for alias, struct_name in self._aliases.items():
Heiko Schocher701c4012019-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 Glassd570dec2017-06-18 22:08:58 -0600568
Simon Glass1f730c82017-06-18 22:08:59 -0600569 def output_node(self, node):
Simon Glassd570dec2017-06-18 22:08:58 -0600570 """Output the C code for a node
571
572 Args:
573 node: node to output
574 """
Walter Lozanoe675d962020-07-03 08:07:17 -0300575 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass1f730c82017-06-18 22:08:59 -0600576 var_name = conv_name_to_c(node.name)
Walter Lozanodc5b4372020-06-25 01:10:13 -0300577 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass1f730c82017-06-18 22:08:59 -0600578 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glassc82de562019-05-17 22:00:32 -0600579 for pname in sorted(node.props):
580 prop = node.props[pname]
Simon Glassd570dec2017-06-18 22:08:58 -0600581 if pname in PROP_IGNORE_LIST or pname[0] == '#':
582 continue
Simon Glass1f730c82017-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 Glassd570dec2017-06-18 22:08:58 -0600585
586 # Special handling for lists
Simon Glass1f730c82017-06-18 22:08:59 -0600587 if isinstance(prop.value, list):
588 self.buf('{')
Simon Glassd570dec2017-06-18 22:08:58 -0600589 vals = []
590 # For phandles, output a reference to the platform data
591 # of the target node.
Simon Glassec3b5e42017-08-29 14:15:55 -0600592 info = self.get_phandle_argc(prop, node.name)
593 if info:
Simon Glassd570dec2017-06-18 22:08:58 -0600594 # Process the list as pairs of (phandle, id)
Simon Glass3deeb472017-08-29 14:15:59 -0600595 pos = 0
Walter Lozanodc5b4372020-06-25 01:10:13 -0300596 item = 0
Simon Glass3deeb472017-08-29 14:15:59 -0600597 for args in info.args:
598 phandle_cell = prop.value[pos]
Simon Glassd570dec2017-06-18 22:08:58 -0600599 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
Simon Glass17ffd622017-08-29 14:15:53 -0600600 target_node = self._fdt.phandle_to_node[phandle]
Simon Glass1f730c82017-06-18 22:08:59 -0600601 name = conv_name_to_c(target_node.name)
Simon Glass3deeb472017-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 Lozanodc5b4372020-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 Glassd0cd0752017-08-29 14:15:57 -0600616 for val in vals:
617 self.buf('\n\t\t%s,' % val)
Simon Glassd570dec2017-06-18 22:08:58 -0600618 else:
619 for val in prop.value:
Simon Glass7a2add12017-06-18 22:09:02 -0600620 vals.append(get_value(prop.type, val))
Simon Glass131e0b02017-08-29 14:15:49 -0600621
Simon Glassd0cd0752017-08-29 14:15:57 -0600622 # Put 8 values per line to avoid very long lines.
Simon Glass61b88e52019-05-17 22:00:31 -0600623 for i in range(0, len(vals), 8):
Simon Glassd0cd0752017-08-29 14:15:57 -0600624 if i:
625 self.buf(',\n\t\t')
626 self.buf(', '.join(vals[i:i + 8]))
Simon Glass1f730c82017-06-18 22:08:59 -0600627 self.buf('}')
Simon Glassd570dec2017-06-18 22:08:58 -0600628 else:
Simon Glass7a2add12017-06-18 22:09:02 -0600629 self.buf(get_value(prop.type, prop.value))
Simon Glass1f730c82017-06-18 22:08:59 -0600630 self.buf(',\n')
631 self.buf('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600632
633 # Add a device declaration
Simon Glass1f730c82017-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 Glassd570dec2017-06-18 22:08:58 -0600640
Simon Glass1f730c82017-06-18 22:08:59 -0600641 self.out(''.join(self.get_buf()))
Simon Glassd570dec2017-06-18 22:08:58 -0600642
Simon Glass1f730c82017-06-18 22:08:59 -0600643 def generate_tables(self):
Simon Glassd570dec2017-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 Schuchardtc79f03c2020-02-25 21:35:39 +0100650 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glassd570dec2017-06-18 22:08:58 -0600651 information.
652 """
Simon Glass68d32c72017-08-29 14:16:01 -0600653 self.out_header()
Simon Glass1f730c82017-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 Glassd570dec2017-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 Glass1f730c82017-06-18 22:08:59 -0600666 self.output_node(req_node)
Simon Glassd570dec2017-06-18 22:08:58 -0600667 nodes_to_output.remove(req_node)
Simon Glass1f730c82017-06-18 22:08:59 -0600668 self.output_node(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600669 nodes_to_output.remove(node)
Simon Glass3fa797a2017-06-18 22:09:03 -0600670
Walter Lozanodc5b4372020-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 Lozano179f0b62020-06-25 01:10:16 -0300676 self.buf('\t%s = DM_GET_DEVICE(%s);\n' %
677 (l['var_node'], l['dev_name']))
Walter Lozanodc5b4372020-06-25 01:10:13 -0300678 self.buf('}\n')
679
680 self.out(''.join(self.get_buf()))
Simon Glass3fa797a2017-06-18 22:09:03 -0600681
Walter Lozanod82062b2020-07-28 19:06:23 -0300682def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
683 drivers_additional=[]):
Simon Glass3fa797a2017-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 Lozanod82062b2020-07-28 19:06:23 -0300695 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional)
Walter Lozanoe675d962020-07-03 08:07:17 -0300696 plat.scan_drivers()
Simon Glass3fa797a2017-06-18 22:09:03 -0600697 plat.scan_dtb()
698 plat.scan_tree()
Simon Glass1b1fe412017-08-29 14:15:50 -0600699 plat.scan_reg_sizes()
Simon Glass3fa797a2017-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)