blob: 579a6749c40c4dbd1f4a29bda809edaf0010591f [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):
Walter Lozano5fe734c2020-07-23 00:22:03 -0300116 """Get the node's list of compatible string as a C identifiers
Simon Glass7a2add12017-06-18 22:09:02 -0600117
118 Args:
119 node: Node object to check
120 Return:
Walter Lozano5fe734c2020-07-23 00:22:03 -0300121 List of C identifiers for all the compatible strings
Simon Glass7a2add12017-06-18 22:09:02 -0600122 """
123 compat = node.props['compatible'].value
Walter Lozano5fe734c2020-07-23 00:22:03 -0300124 if not isinstance(compat, list):
125 compat = [compat]
126 return [conv_name_to_c(c) for c in compat]
Simon Glass7a2add12017-06-18 22:09:02 -0600127
Simon Glass7a2add12017-06-18 22:09:02 -0600128
Simon Glass1f730c82017-06-18 22:08:59 -0600129class DtbPlatdata(object):
Simon Glassd570dec2017-06-18 22:08:58 -0600130 """Provide a means to convert device tree binary data to platform data
131
132 The output of this process is C structures which can be used in space-
133 constrained encvironments where the ~3KB code overhead of device tree
134 code is not affordable.
135
136 Properties:
Simon Glass1f730c82017-06-18 22:08:59 -0600137 _fdt: Fdt object, referencing the device tree
Simon Glassd570dec2017-06-18 22:08:58 -0600138 _dtb_fname: Filename of the input device tree binary file
139 _valid_nodes: A list of Node object with compatible strings
Simon Glasseab3f622017-06-18 22:09:01 -0600140 _include_disabled: true to include nodes marked status = "disabled"
Simon Glassd570dec2017-06-18 22:08:58 -0600141 _outfile: The current output file (sys.stdout or a real file)
Walter Lozanoa324e412020-06-25 01:10:08 -0300142 _warning_disabled: true to disable warnings about driver names not found
Simon Glassd570dec2017-06-18 22:08:58 -0600143 _lines: Stashed list of output lines for outputting in the future
Walter Lozanoe675d962020-07-03 08:07:17 -0300144 _drivers: List of valid driver names found in drivers/
145 _driver_aliases: Dict that holds aliases for driver names
146 key: Driver alias declared with
147 U_BOOT_DRIVER_ALIAS(driver_alias, driver_name)
148 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Walter Lozanodc5b4372020-06-25 01:10:13 -0300149 _links: List of links to be included in dm_populate_phandle_data()
Walter Lozanod82062b2020-07-28 19:06:23 -0300150 _drivers_additional: List of additional drivers to use during scanning
Simon Glassd570dec2017-06-18 22:08:58 -0600151 """
Walter Lozanod82062b2020-07-28 19:06:23 -0300152 def __init__(self, dtb_fname, include_disabled, warning_disabled,
153 drivers_additional=[]):
Simon Glass1f730c82017-06-18 22:08:59 -0600154 self._fdt = None
Simon Glassd570dec2017-06-18 22:08:58 -0600155 self._dtb_fname = dtb_fname
156 self._valid_nodes = None
Simon Glasseab3f622017-06-18 22:09:01 -0600157 self._include_disabled = include_disabled
Simon Glassd570dec2017-06-18 22:08:58 -0600158 self._outfile = None
Walter Lozanoa324e412020-06-25 01:10:08 -0300159 self._warning_disabled = warning_disabled
Simon Glassd570dec2017-06-18 22:08:58 -0600160 self._lines = []
Walter Lozanoe675d962020-07-03 08:07:17 -0300161 self._drivers = []
162 self._driver_aliases = {}
Walter Lozanodc5b4372020-06-25 01:10:13 -0300163 self._links = []
Walter Lozanod82062b2020-07-28 19:06:23 -0300164 self._drivers_additional = drivers_additional
Walter Lozanoe675d962020-07-03 08:07:17 -0300165
166 def get_normalized_compat_name(self, node):
167 """Get a node's normalized compat name
168
Walter Lozano5fe734c2020-07-23 00:22:03 -0300169 Returns a valid driver name by retrieving node's list of compatible
Walter Lozanoe675d962020-07-03 08:07:17 -0300170 string as a C identifier and performing a check against _drivers
171 and a lookup in driver_aliases printing a warning in case of failure.
172
173 Args:
174 node: Node object to check
175 Return:
176 Tuple:
177 Driver name associated with the first compatible string
178 List of C identifiers for all the other compatible strings
179 (possibly empty)
180 In case of no match found, the return will be the same as
181 get_compat_name()
182 """
Walter Lozano5fe734c2020-07-23 00:22:03 -0300183 compat_list_c = get_compat_name(node)
184
185 for compat_c in compat_list_c:
186 if not compat_c in self._drivers:
187 compat_c = self._driver_aliases.get(compat_c)
188 if not compat_c:
189 continue
190
191 aliases_c = compat_list_c
192 if compat_c in aliases_c:
193 aliases_c.remove(compat_c)
194 return compat_c, aliases_c
195
196 if not self._warning_disabled:
197 print('WARNING: the driver %s was not found in the driver list'
198 % (compat_list_c[0]))
Walter Lozanoe675d962020-07-03 08:07:17 -0300199
Walter Lozano5fe734c2020-07-23 00:22:03 -0300200 return compat_list_c[0], compat_list_c[1:]
Simon Glassd570dec2017-06-18 22:08:58 -0600201
Simon Glass1f730c82017-06-18 22:08:59 -0600202 def setup_output(self, fname):
Simon Glassd570dec2017-06-18 22:08:58 -0600203 """Set up the output destination
204
Simon Glass1f730c82017-06-18 22:08:59 -0600205 Once this is done, future calls to self.out() will output to this
Simon Glassd570dec2017-06-18 22:08:58 -0600206 file.
207
208 Args:
209 fname: Filename to send output to, or '-' for stdout
210 """
211 if fname == '-':
212 self._outfile = sys.stdout
213 else:
214 self._outfile = open(fname, 'w')
215
Simon Glass1f730c82017-06-18 22:08:59 -0600216 def out(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600217 """Output a string to the output file
218
219 Args:
Simon Glass1f730c82017-06-18 22:08:59 -0600220 line: String to output
Simon Glassd570dec2017-06-18 22:08:58 -0600221 """
Simon Glass1f730c82017-06-18 22:08:59 -0600222 self._outfile.write(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600223
Simon Glass1f730c82017-06-18 22:08:59 -0600224 def buf(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600225 """Buffer up a string to send later
226
227 Args:
Simon Glass1f730c82017-06-18 22:08:59 -0600228 line: String to add to our 'buffer' list
Simon Glassd570dec2017-06-18 22:08:58 -0600229 """
Simon Glass1f730c82017-06-18 22:08:59 -0600230 self._lines.append(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600231
Simon Glass1f730c82017-06-18 22:08:59 -0600232 def get_buf(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600233 """Get the contents of the output buffer, and clear it
234
235 Returns:
236 The output buffer, which is then cleared for future use
237 """
238 lines = self._lines
239 self._lines = []
240 return lines
241
Simon Glass68d32c72017-08-29 14:16:01 -0600242 def out_header(self):
243 """Output a message indicating that this is an auto-generated file"""
244 self.out('''/*
245 * DO NOT MODIFY
246 *
247 * This file was generated by dtoc from a .dtb (device tree binary) file.
248 */
249
250''')
251
Simon Glassec3b5e42017-08-29 14:15:55 -0600252 def get_phandle_argc(self, prop, node_name):
253 """Check if a node contains phandles
Simon Glassce5621d2017-08-29 14:15:54 -0600254
Simon Glassec3b5e42017-08-29 14:15:55 -0600255 We have no reliable way of detecting whether a node uses a phandle
256 or not. As an interim measure, use a list of known property names.
257
258 Args:
259 prop: Prop object to check
260 Return:
261 Number of argument cells is this is a phandle, else None
262 """
Walter Lozano179f0b62020-06-25 01:10:16 -0300263 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass609e2b12018-07-06 10:27:31 -0600264 if not isinstance(prop.value, list):
265 prop.value = [prop.value]
Simon Glassec3b5e42017-08-29 14:15:55 -0600266 val = prop.value
Simon Glassec3b5e42017-08-29 14:15:55 -0600267 i = 0
Simon Glassce5621d2017-08-29 14:15:54 -0600268
Simon Glassec3b5e42017-08-29 14:15:55 -0600269 max_args = 0
270 args = []
271 while i < len(val):
272 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass609e2b12018-07-06 10:27:31 -0600273 # If we get to the end of the list, stop. This can happen
274 # since some nodes have more phandles in the list than others,
275 # but we allocate enough space for the largest list. So those
276 # nodes with shorter lists end up with zeroes at the end.
277 if not phandle:
278 break
Simon Glassec3b5e42017-08-29 14:15:55 -0600279 target = self._fdt.phandle_to_node.get(phandle)
280 if not target:
281 raise ValueError("Cannot parse '%s' in node '%s'" %
282 (prop.name, node_name))
Walter Lozano179f0b62020-06-25 01:10:16 -0300283 cells = None
284 for prop_name in ['#clock-cells', '#gpio-cells']:
285 cells = target.props.get(prop_name)
286 if cells:
287 break
Simon Glassec3b5e42017-08-29 14:15:55 -0600288 if not cells:
Walter Lozano179f0b62020-06-25 01:10:16 -0300289 raise ValueError("Node '%s' has no cells property" %
290 (target.name))
Simon Glassec3b5e42017-08-29 14:15:55 -0600291 num_args = fdt_util.fdt32_to_cpu(cells.value)
292 max_args = max(max_args, num_args)
293 args.append(num_args)
294 i += 1 + num_args
295 return PhandleInfo(max_args, args)
296 return None
Simon Glassce5621d2017-08-29 14:15:54 -0600297
Walter Lozanoe675d962020-07-03 08:07:17 -0300298 def scan_driver(self, fn):
299 """Scan a driver file to build a list of driver names and aliases
300
301 This procedure will populate self._drivers and self._driver_aliases
302
303 Args
304 fn: Driver filename to scan
305 """
306 with open(fn, encoding='utf-8') as fd:
307 try:
308 buff = fd.read()
309 except UnicodeDecodeError:
310 # This seems to happen on older Python versions
311 print("Skipping file '%s' due to unicode error" % fn)
312 return
313
314 # The following re will search for driver names declared as
315 # U_BOOT_DRIVER(driver_name)
316 drivers = re.findall('U_BOOT_DRIVER\((.*)\)', buff)
317
318 for driver in drivers:
319 self._drivers.append(driver)
320
321 # The following re will search for driver aliases declared as
322 # U_BOOT_DRIVER_ALIAS(alias, driver_name)
323 driver_aliases = re.findall('U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
324 buff)
325
326 for alias in driver_aliases: # pragma: no cover
327 if len(alias) != 2:
328 continue
329 self._driver_aliases[alias[1]] = alias[0]
330
331 def scan_drivers(self):
332 """Scan the driver folders to build a list of driver names and aliases
333
334 This procedure will populate self._drivers and self._driver_aliases
335
336 """
337 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
338 if basedir == '':
339 basedir = './'
340 for (dirpath, dirnames, filenames) in os.walk(basedir):
341 for fn in filenames:
342 if not fn.endswith('.c'):
343 continue
344 self.scan_driver(dirpath + '/' + fn)
345
Walter Lozanod82062b2020-07-28 19:06:23 -0300346 for fn in self._drivers_additional:
347 if not isinstance(fn, str) or len(fn) == 0:
348 continue
349 if fn[0] == '/':
350 self.scan_driver(fn)
351 else:
352 self.scan_driver(basedir + '/' + fn)
353
Simon Glass1f730c82017-06-18 22:08:59 -0600354 def scan_dtb(self):
Anatolij Gustschinda707d42017-08-18 17:58:51 +0200355 """Scan the device tree to obtain a tree of nodes and properties
Simon Glassd570dec2017-06-18 22:08:58 -0600356
Simon Glass1f730c82017-06-18 22:08:59 -0600357 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glassd570dec2017-06-18 22:08:58 -0600358 device tree root node, and progress from there.
359 """
Simon Glass1f730c82017-06-18 22:08:59 -0600360 self._fdt = fdt.FdtScan(self._dtb_fname)
361
362 def scan_node(self, root):
363 """Scan a node and subnodes to build a tree of node and phandle info
Simon Glassd570dec2017-06-18 22:08:58 -0600364
Simon Glass17ffd622017-08-29 14:15:53 -0600365 This adds each node to self._valid_nodes.
Simon Glass1f730c82017-06-18 22:08:59 -0600366
367 Args:
368 root: Root node for scan
369 """
Simon Glassd570dec2017-06-18 22:08:58 -0600370 for node in root.subnodes:
371 if 'compatible' in node.props:
372 status = node.props.get('status')
Simon Glasseab3f622017-06-18 22:09:01 -0600373 if (not self._include_disabled and not status or
Simon Glass1f730c82017-06-18 22:08:59 -0600374 status.value != 'disabled'):
Simon Glassd570dec2017-06-18 22:08:58 -0600375 self._valid_nodes.append(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600376
377 # recurse to handle any subnodes
Simon Glass1f730c82017-06-18 22:08:59 -0600378 self.scan_node(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600379
Simon Glass1f730c82017-06-18 22:08:59 -0600380 def scan_tree(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600381 """Scan the device tree for useful information
382
383 This fills in the following properties:
Simon Glassd570dec2017-06-18 22:08:58 -0600384 _valid_nodes: A list of nodes we wish to consider include in the
385 platform data
386 """
Simon Glassd570dec2017-06-18 22:08:58 -0600387 self._valid_nodes = []
Simon Glass1f730c82017-06-18 22:08:59 -0600388 return self.scan_node(self._fdt.GetRoot())
Simon Glassd570dec2017-06-18 22:08:58 -0600389
Simon Glass1b1fe412017-08-29 14:15:50 -0600390 @staticmethod
391 def get_num_cells(node):
392 """Get the number of cells in addresses and sizes for this node
393
394 Args:
395 node: Node to check
396
397 Returns:
398 Tuple:
399 Number of address cells for this node
400 Number of size cells for this node
401 """
402 parent = node.parent
403 na, ns = 2, 2
404 if parent:
405 na_prop = parent.props.get('#address-cells')
406 ns_prop = parent.props.get('#size-cells')
407 if na_prop:
408 na = fdt_util.fdt32_to_cpu(na_prop.value)
409 if ns_prop:
410 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
411 return na, ns
412
413 def scan_reg_sizes(self):
414 """Scan for 64-bit 'reg' properties and update the values
415
416 This finds 'reg' properties with 64-bit data and converts the value to
417 an array of 64-values. This allows it to be output in a way that the
418 C code can read.
419 """
420 for node in self._valid_nodes:
421 reg = node.props.get('reg')
422 if not reg:
423 continue
424 na, ns = self.get_num_cells(node)
425 total = na + ns
426
427 if reg.type != fdt.TYPE_INT:
Simon Glassc38fee042018-07-06 10:27:32 -0600428 raise ValueError("Node '%s' reg property is not an int" %
429 node.name)
Simon Glass1b1fe412017-08-29 14:15:50 -0600430 if len(reg.value) % total:
431 raise ValueError("Node '%s' reg property has %d cells "
432 'which is not a multiple of na + ns = %d + %d)' %
433 (node.name, len(reg.value), na, ns))
434 reg.na = na
435 reg.ns = ns
436 if na != 1 or ns != 1:
437 reg.type = fdt.TYPE_INT64
438 i = 0
439 new_value = []
440 val = reg.value
441 if not isinstance(val, list):
442 val = [val]
443 while i < len(val):
444 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
445 i += na
446 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
447 i += ns
448 new_value += [addr, size]
449 reg.value = new_value
450
Simon Glass1f730c82017-06-18 22:08:59 -0600451 def scan_structs(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600452 """Scan the device tree building up the C structures we will use.
453
454 Build a dict keyed by C struct name containing a dict of Prop
455 object for each struct field (keyed by property name). Where the
456 same struct appears multiple times, try to use the 'widest'
457 property, i.e. the one with a type which can express all others.
458
459 Once the widest property is determined, all other properties are
460 updated to match that width.
461 """
462 structs = {}
463 for node in self._valid_nodes:
Walter Lozanoe675d962020-07-03 08:07:17 -0300464 node_name, _ = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600465 fields = {}
466
467 # Get a list of all the valid properties in this node.
468 for name, prop in node.props.items():
469 if name not in PROP_IGNORE_LIST and name[0] != '#':
470 fields[name] = copy.deepcopy(prop)
471
472 # If we've seen this node_name before, update the existing struct.
473 if node_name in structs:
474 struct = structs[node_name]
475 for name, prop in fields.items():
476 oldprop = struct.get(name)
477 if oldprop:
478 oldprop.Widen(prop)
479 else:
480 struct[name] = prop
481
482 # Otherwise store this as a new struct.
483 else:
484 structs[node_name] = fields
485
486 upto = 0
487 for node in self._valid_nodes:
Walter Lozanoe675d962020-07-03 08:07:17 -0300488 node_name, _ = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600489 struct = structs[node_name]
490 for name, prop in node.props.items():
491 if name not in PROP_IGNORE_LIST and name[0] != '#':
492 prop.Widen(struct[name])
493 upto += 1
494
Simon Glassd570dec2017-06-18 22:08:58 -0600495 return structs
496
Simon Glass1f730c82017-06-18 22:08:59 -0600497 def scan_phandles(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600498 """Figure out what phandles each node uses
499
500 We need to be careful when outputing nodes that use phandles since
501 they must come after the declaration of the phandles in the C file.
502 Otherwise we get a compiler error since the phandle struct is not yet
503 declared.
504
505 This function adds to each node a list of phandle nodes that the node
506 depends on. This allows us to output things in the right order.
507 """
508 for node in self._valid_nodes:
509 node.phandles = set()
510 for pname, prop in node.props.items():
511 if pname in PROP_IGNORE_LIST or pname[0] == '#':
512 continue
Simon Glassec3b5e42017-08-29 14:15:55 -0600513 info = self.get_phandle_argc(prop, node.name)
514 if info:
Simon Glassec3b5e42017-08-29 14:15:55 -0600515 # Process the list as pairs of (phandle, id)
Simon Glass3deeb472017-08-29 14:15:59 -0600516 pos = 0
517 for args in info.args:
518 phandle_cell = prop.value[pos]
Simon Glassec3b5e42017-08-29 14:15:55 -0600519 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
520 target_node = self._fdt.phandle_to_node[phandle]
521 node.phandles.add(target_node)
Simon Glass3deeb472017-08-29 14:15:59 -0600522 pos += 1 + args
Simon Glassd570dec2017-06-18 22:08:58 -0600523
524
Simon Glass1f730c82017-06-18 22:08:59 -0600525 def generate_structs(self, structs):
Simon Glassd570dec2017-06-18 22:08:58 -0600526 """Generate struct defintions for the platform data
527
528 This writes out the body of a header file consisting of structure
529 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +0100530 doc/driver-model/of-plat.rst for more information.
Simon Glassd570dec2017-06-18 22:08:58 -0600531 """
Simon Glass68d32c72017-08-29 14:16:01 -0600532 self.out_header()
Simon Glass1f730c82017-06-18 22:08:59 -0600533 self.out('#include <stdbool.h>\n')
Masahiro Yamada75f82d02018-03-05 01:20:11 +0900534 self.out('#include <linux/libfdt.h>\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600535
536 # Output the struct definition
537 for name in sorted(structs):
Simon Glass1f730c82017-06-18 22:08:59 -0600538 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glassd570dec2017-06-18 22:08:58 -0600539 for pname in sorted(structs[name]):
540 prop = structs[name][pname]
Simon Glassec3b5e42017-08-29 14:15:55 -0600541 info = self.get_phandle_argc(prop, structs[name])
542 if info:
Simon Glassd570dec2017-06-18 22:08:58 -0600543 # For phandles, include a reference to the target
Simon Glasse94414b2017-08-29 14:15:56 -0600544 struct_name = 'struct phandle_%d_arg' % info.max_args
545 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass1f730c82017-06-18 22:08:59 -0600546 conv_name_to_c(prop.name),
Simon Glass3deeb472017-08-29 14:15:59 -0600547 len(info.args)))
Simon Glassd570dec2017-06-18 22:08:58 -0600548 else:
549 ptype = TYPE_NAMES[prop.type]
Simon Glass1f730c82017-06-18 22:08:59 -0600550 self.out('\t%s%s' % (tab_to(2, ptype),
551 conv_name_to_c(prop.name)))
552 if isinstance(prop.value, list):
553 self.out('[%d]' % len(prop.value))
554 self.out(';\n')
555 self.out('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600556
Simon Glass1f730c82017-06-18 22:08:59 -0600557 def output_node(self, node):
Simon Glassd570dec2017-06-18 22:08:58 -0600558 """Output the C code for a node
559
560 Args:
561 node: node to output
562 """
Walter Lozanoe675d962020-07-03 08:07:17 -0300563 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass1f730c82017-06-18 22:08:59 -0600564 var_name = conv_name_to_c(node.name)
Walter Lozanodc5b4372020-06-25 01:10:13 -0300565 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass1f730c82017-06-18 22:08:59 -0600566 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glassc82de562019-05-17 22:00:32 -0600567 for pname in sorted(node.props):
568 prop = node.props[pname]
Simon Glassd570dec2017-06-18 22:08:58 -0600569 if pname in PROP_IGNORE_LIST or pname[0] == '#':
570 continue
Simon Glass1f730c82017-06-18 22:08:59 -0600571 member_name = conv_name_to_c(prop.name)
572 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
Simon Glassd570dec2017-06-18 22:08:58 -0600573
574 # Special handling for lists
Simon Glass1f730c82017-06-18 22:08:59 -0600575 if isinstance(prop.value, list):
576 self.buf('{')
Simon Glassd570dec2017-06-18 22:08:58 -0600577 vals = []
578 # For phandles, output a reference to the platform data
579 # of the target node.
Simon Glassec3b5e42017-08-29 14:15:55 -0600580 info = self.get_phandle_argc(prop, node.name)
581 if info:
Simon Glassd570dec2017-06-18 22:08:58 -0600582 # Process the list as pairs of (phandle, id)
Simon Glass3deeb472017-08-29 14:15:59 -0600583 pos = 0
Walter Lozanodc5b4372020-06-25 01:10:13 -0300584 item = 0
Simon Glass3deeb472017-08-29 14:15:59 -0600585 for args in info.args:
586 phandle_cell = prop.value[pos]
Simon Glassd570dec2017-06-18 22:08:58 -0600587 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
Simon Glass17ffd622017-08-29 14:15:53 -0600588 target_node = self._fdt.phandle_to_node[phandle]
Simon Glass1f730c82017-06-18 22:08:59 -0600589 name = conv_name_to_c(target_node.name)
Simon Glass3deeb472017-08-29 14:15:59 -0600590 arg_values = []
591 for i in range(args):
592 arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
593 pos += 1 + args
Walter Lozanodc5b4372020-06-25 01:10:13 -0300594 # node member is filled with NULL as the real value
595 # will be update at run-time during dm_init_and_scan()
596 # by dm_populate_phandle_data()
597 vals.append('\t{NULL, {%s}}' % (', '.join(arg_values)))
598 var_node = '%s%s.%s[%d].node' % \
599 (VAL_PREFIX, var_name, member_name, item)
600 # Save the the link information to be use to define
601 # dm_populate_phandle_data()
602 self._links.append({'var_node': var_node, 'dev_name': name})
603 item += 1
Simon Glassd0cd0752017-08-29 14:15:57 -0600604 for val in vals:
605 self.buf('\n\t\t%s,' % val)
Simon Glassd570dec2017-06-18 22:08:58 -0600606 else:
607 for val in prop.value:
Simon Glass7a2add12017-06-18 22:09:02 -0600608 vals.append(get_value(prop.type, val))
Simon Glass131e0b02017-08-29 14:15:49 -0600609
Simon Glassd0cd0752017-08-29 14:15:57 -0600610 # Put 8 values per line to avoid very long lines.
Simon Glass61b88e52019-05-17 22:00:31 -0600611 for i in range(0, len(vals), 8):
Simon Glassd0cd0752017-08-29 14:15:57 -0600612 if i:
613 self.buf(',\n\t\t')
614 self.buf(', '.join(vals[i:i + 8]))
Simon Glass1f730c82017-06-18 22:08:59 -0600615 self.buf('}')
Simon Glassd570dec2017-06-18 22:08:58 -0600616 else:
Simon Glass7a2add12017-06-18 22:09:02 -0600617 self.buf(get_value(prop.type, prop.value))
Simon Glass1f730c82017-06-18 22:08:59 -0600618 self.buf(',\n')
619 self.buf('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600620
621 # Add a device declaration
Simon Glass1f730c82017-06-18 22:08:59 -0600622 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
623 self.buf('\t.name\t\t= "%s",\n' % struct_name)
624 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
625 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
626 self.buf('};\n')
627 self.buf('\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600628
Simon Glass1f730c82017-06-18 22:08:59 -0600629 self.out(''.join(self.get_buf()))
Simon Glassd570dec2017-06-18 22:08:58 -0600630
Simon Glass1f730c82017-06-18 22:08:59 -0600631 def generate_tables(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600632 """Generate device defintions for the platform data
633
634 This writes out C platform data initialisation data and
635 U_BOOT_DEVICE() declarations for each valid node. Where a node has
636 multiple compatible strings, a #define is used to make them equivalent.
637
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +0100638 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glassd570dec2017-06-18 22:08:58 -0600639 information.
640 """
Simon Glass68d32c72017-08-29 14:16:01 -0600641 self.out_header()
Simon Glass1f730c82017-06-18 22:08:59 -0600642 self.out('#include <common.h>\n')
643 self.out('#include <dm.h>\n')
644 self.out('#include <dt-structs.h>\n')
645 self.out('\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600646 nodes_to_output = list(self._valid_nodes)
647
648 # Keep outputing nodes until there is none left
649 while nodes_to_output:
650 node = nodes_to_output[0]
651 # Output all the node's dependencies first
652 for req_node in node.phandles:
653 if req_node in nodes_to_output:
Simon Glass1f730c82017-06-18 22:08:59 -0600654 self.output_node(req_node)
Simon Glassd570dec2017-06-18 22:08:58 -0600655 nodes_to_output.remove(req_node)
Simon Glass1f730c82017-06-18 22:08:59 -0600656 self.output_node(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600657 nodes_to_output.remove(node)
Simon Glass3fa797a2017-06-18 22:09:03 -0600658
Walter Lozanodc5b4372020-06-25 01:10:13 -0300659 # Define dm_populate_phandle_data() which will add the linking between
660 # nodes using DM_GET_DEVICE
661 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
662 self.buf('void dm_populate_phandle_data(void) {\n')
663 for l in self._links:
Walter Lozano179f0b62020-06-25 01:10:16 -0300664 self.buf('\t%s = DM_GET_DEVICE(%s);\n' %
665 (l['var_node'], l['dev_name']))
Walter Lozanodc5b4372020-06-25 01:10:13 -0300666 self.buf('}\n')
667
668 self.out(''.join(self.get_buf()))
Simon Glass3fa797a2017-06-18 22:09:03 -0600669
Walter Lozanod82062b2020-07-28 19:06:23 -0300670def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
671 drivers_additional=[]):
Simon Glass3fa797a2017-06-18 22:09:03 -0600672 """Run all the steps of the dtoc tool
673
674 Args:
675 args: List of non-option arguments provided to the problem
676 dtb_file: Filename of dtb file to process
677 include_disabled: True to include disabled nodes
678 output: Name of output file
679 """
680 if not args:
681 raise ValueError('Please specify a command: struct, platdata')
682
Walter Lozanod82062b2020-07-28 19:06:23 -0300683 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional)
Walter Lozanoe675d962020-07-03 08:07:17 -0300684 plat.scan_drivers()
Simon Glass3fa797a2017-06-18 22:09:03 -0600685 plat.scan_dtb()
686 plat.scan_tree()
Simon Glass1b1fe412017-08-29 14:15:50 -0600687 plat.scan_reg_sizes()
Simon Glass3fa797a2017-06-18 22:09:03 -0600688 plat.setup_output(output)
689 structs = plat.scan_structs()
690 plat.scan_phandles()
691
692 for cmd in args[0].split(','):
693 if cmd == 'struct':
694 plat.generate_structs(structs)
695 elif cmd == 'platdata':
696 plat.generate_tables()
697 else:
698 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
699 cmd)