blob: 8ba8f1636961e19377fe5e6469d3c8ac10d44c81 [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()
Simon Glass7581c012017-06-18 22:08:58 -0600157 """
Walter Lozano361e7332020-06-25 01:10:08 -0300158 def __init__(self, dtb_fname, include_disabled, warning_disabled):
Simon Glass2be282c2017-06-18 22:08:59 -0600159 self._fdt = None
Simon Glass7581c012017-06-18 22:08:58 -0600160 self._dtb_fname = dtb_fname
161 self._valid_nodes = None
Simon Glasse36024b2017-06-18 22:09:01 -0600162 self._include_disabled = include_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600163 self._outfile = None
Walter Lozano361e7332020-06-25 01:10:08 -0300164 self._warning_disabled = warning_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600165 self._lines = []
166 self._aliases = {}
Walter Lozanodac82282020-07-03 08:07:17 -0300167 self._drivers = []
168 self._driver_aliases = {}
Walter Lozano51f12632020-06-25 01:10:13 -0300169 self._links = []
Walter Lozanodac82282020-07-03 08:07:17 -0300170
171 def get_normalized_compat_name(self, node):
172 """Get a node's normalized compat name
173
174 Returns a valid driver name by retrieving node's first compatible
175 string as a C identifier and performing a check against _drivers
176 and a lookup in driver_aliases printing a warning in case of failure.
177
178 Args:
179 node: Node object to check
180 Return:
181 Tuple:
182 Driver name associated with the first compatible string
183 List of C identifiers for all the other compatible strings
184 (possibly empty)
185 In case of no match found, the return will be the same as
186 get_compat_name()
187 """
188 compat_c, aliases_c = get_compat_name(node)
189 if compat_c not in self._drivers:
190 compat_c_old = compat_c
191 compat_c = self._driver_aliases.get(compat_c)
192 if not compat_c:
Walter Lozano361e7332020-06-25 01:10:08 -0300193 if not self._warning_disabled:
194 print('WARNING: the driver %s was not found in the driver list'
195 % (compat_c_old))
Walter Lozanodac82282020-07-03 08:07:17 -0300196 compat_c = compat_c_old
197 else:
198 aliases_c = [compat_c_old] + aliases_c
199
200 return compat_c, aliases_c
Simon Glass7581c012017-06-18 22:08:58 -0600201
Simon Glass2be282c2017-06-18 22:08:59 -0600202 def setup_output(self, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600203 """Set up the output destination
204
Simon Glass2be282c2017-06-18 22:08:59 -0600205 Once this is done, future calls to self.out() will output to this
Simon Glass7581c012017-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 Glass2be282c2017-06-18 22:08:59 -0600216 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600217 """Output a string to the output file
218
219 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600220 line: String to output
Simon Glass7581c012017-06-18 22:08:58 -0600221 """
Simon Glass2be282c2017-06-18 22:08:59 -0600222 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600223
Simon Glass2be282c2017-06-18 22:08:59 -0600224 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600225 """Buffer up a string to send later
226
227 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600228 line: String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600229 """
Simon Glass2be282c2017-06-18 22:08:59 -0600230 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600231
Simon Glass2be282c2017-06-18 22:08:59 -0600232 def get_buf(self):
Simon Glass7581c012017-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 Glassd5031142017-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 Glass8fed2eb2017-08-29 14:15:55 -0600252 def get_phandle_argc(self, prop, node_name):
253 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600254
Simon Glass8fed2eb2017-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.
Simon Glass2925c262017-08-29 14:15:54 -0600257
Simon Glass8fed2eb2017-08-29 14:15:55 -0600258 Args:
259 prop: Prop object to check
260 Return:
261 Number of argument cells is this is a phandle, else None
262 """
Walter Lozanoad340172020-06-25 01:10:16 -0300263 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass760b7172018-07-06 10:27:31 -0600264 if not isinstance(prop.value, list):
265 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600266 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600267 i = 0
268
269 max_args = 0
270 args = []
271 while i < len(val):
272 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-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 Glass8fed2eb2017-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 Lozanoad340172020-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 Glass8fed2eb2017-08-29 14:15:55 -0600288 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300289 raise ValueError("Node '%s' has no cells property" %
290 (target.name))
Simon Glass8fed2eb2017-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 Glass2925c262017-08-29 14:15:54 -0600297
Walter Lozanodac82282020-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
Simon Glass2be282c2017-06-18 22:08:59 -0600346 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200347 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600348
Simon Glass2be282c2017-06-18 22:08:59 -0600349 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600350 device tree root node, and progress from there.
351 """
Simon Glass2be282c2017-06-18 22:08:59 -0600352 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600353
Simon Glass2be282c2017-06-18 22:08:59 -0600354 def scan_node(self, root):
355 """Scan a node and subnodes to build a tree of node and phandle info
356
Simon Glass72ab7c52017-08-29 14:15:53 -0600357 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600358
359 Args:
360 root: Root node for scan
361 """
Simon Glass7581c012017-06-18 22:08:58 -0600362 for node in root.subnodes:
363 if 'compatible' in node.props:
364 status = node.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600365 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600366 status.value != 'disabled'):
Simon Glass7581c012017-06-18 22:08:58 -0600367 self._valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600368
369 # recurse to handle any subnodes
Simon Glass2be282c2017-06-18 22:08:59 -0600370 self.scan_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600371
Simon Glass2be282c2017-06-18 22:08:59 -0600372 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600373 """Scan the device tree for useful information
374
375 This fills in the following properties:
Simon Glass7581c012017-06-18 22:08:58 -0600376 _valid_nodes: A list of nodes we wish to consider include in the
377 platform data
378 """
Simon Glass7581c012017-06-18 22:08:58 -0600379 self._valid_nodes = []
Simon Glass2be282c2017-06-18 22:08:59 -0600380 return self.scan_node(self._fdt.GetRoot())
Simon Glass7581c012017-06-18 22:08:58 -0600381
Simon Glassc20ee0e2017-08-29 14:15:50 -0600382 @staticmethod
383 def get_num_cells(node):
384 """Get the number of cells in addresses and sizes for this node
385
386 Args:
387 node: Node to check
388
389 Returns:
390 Tuple:
391 Number of address cells for this node
392 Number of size cells for this node
393 """
394 parent = node.parent
395 na, ns = 2, 2
396 if parent:
397 na_prop = parent.props.get('#address-cells')
398 ns_prop = parent.props.get('#size-cells')
399 if na_prop:
400 na = fdt_util.fdt32_to_cpu(na_prop.value)
401 if ns_prop:
402 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
403 return na, ns
404
405 def scan_reg_sizes(self):
406 """Scan for 64-bit 'reg' properties and update the values
407
408 This finds 'reg' properties with 64-bit data and converts the value to
409 an array of 64-values. This allows it to be output in a way that the
410 C code can read.
411 """
412 for node in self._valid_nodes:
413 reg = node.props.get('reg')
414 if not reg:
415 continue
416 na, ns = self.get_num_cells(node)
417 total = na + ns
418
419 if reg.type != fdt.TYPE_INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600420 raise ValueError("Node '%s' reg property is not an int" %
421 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600422 if len(reg.value) % total:
423 raise ValueError("Node '%s' reg property has %d cells "
424 'which is not a multiple of na + ns = %d + %d)' %
425 (node.name, len(reg.value), na, ns))
426 reg.na = na
427 reg.ns = ns
428 if na != 1 or ns != 1:
429 reg.type = fdt.TYPE_INT64
430 i = 0
431 new_value = []
432 val = reg.value
433 if not isinstance(val, list):
434 val = [val]
435 while i < len(val):
436 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
437 i += na
438 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
439 i += ns
440 new_value += [addr, size]
441 reg.value = new_value
442
Simon Glass2be282c2017-06-18 22:08:59 -0600443 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600444 """Scan the device tree building up the C structures we will use.
445
446 Build a dict keyed by C struct name containing a dict of Prop
447 object for each struct field (keyed by property name). Where the
448 same struct appears multiple times, try to use the 'widest'
449 property, i.e. the one with a type which can express all others.
450
451 Once the widest property is determined, all other properties are
452 updated to match that width.
453 """
454 structs = {}
455 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300456 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600457 fields = {}
458
459 # Get a list of all the valid properties in this node.
460 for name, prop in node.props.items():
461 if name not in PROP_IGNORE_LIST and name[0] != '#':
462 fields[name] = copy.deepcopy(prop)
463
464 # If we've seen this node_name before, update the existing struct.
465 if node_name in structs:
466 struct = structs[node_name]
467 for name, prop in fields.items():
468 oldprop = struct.get(name)
469 if oldprop:
470 oldprop.Widen(prop)
471 else:
472 struct[name] = prop
473
474 # Otherwise store this as a new struct.
475 else:
476 structs[node_name] = fields
477
478 upto = 0
479 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300480 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600481 struct = structs[node_name]
482 for name, prop in node.props.items():
483 if name not in PROP_IGNORE_LIST and name[0] != '#':
484 prop.Widen(struct[name])
485 upto += 1
486
Walter Lozanodac82282020-07-03 08:07:17 -0300487 struct_name, aliases = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600488 for alias in aliases:
489 self._aliases[alias] = struct_name
490
491 return structs
492
Simon Glass2be282c2017-06-18 22:08:59 -0600493 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600494 """Figure out what phandles each node uses
495
496 We need to be careful when outputing nodes that use phandles since
497 they must come after the declaration of the phandles in the C file.
498 Otherwise we get a compiler error since the phandle struct is not yet
499 declared.
500
501 This function adds to each node a list of phandle nodes that the node
502 depends on. This allows us to output things in the right order.
503 """
504 for node in self._valid_nodes:
505 node.phandles = set()
506 for pname, prop in node.props.items():
507 if pname in PROP_IGNORE_LIST or pname[0] == '#':
508 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600509 info = self.get_phandle_argc(prop, node.name)
510 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600511 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600512 pos = 0
513 for args in info.args:
514 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600515 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
516 target_node = self._fdt.phandle_to_node[phandle]
517 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600518 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600519
520
Simon Glass2be282c2017-06-18 22:08:59 -0600521 def generate_structs(self, structs):
Simon Glass7581c012017-06-18 22:08:58 -0600522 """Generate struct defintions for the platform data
523
524 This writes out the body of a header file consisting of structure
525 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100526 doc/driver-model/of-plat.rst for more information.
Simon Glass7581c012017-06-18 22:08:58 -0600527 """
Simon Glassd5031142017-08-29 14:16:01 -0600528 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600529 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900530 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600531
532 # Output the struct definition
533 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600534 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600535 for pname in sorted(structs[name]):
536 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600537 info = self.get_phandle_argc(prop, structs[name])
538 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600539 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600540 struct_name = 'struct phandle_%d_arg' % info.max_args
541 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600542 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600543 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600544 else:
545 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600546 self.out('\t%s%s' % (tab_to(2, ptype),
547 conv_name_to_c(prop.name)))
548 if isinstance(prop.value, list):
549 self.out('[%d]' % len(prop.value))
550 self.out(';\n')
551 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600552
Simon Glass90a81322019-05-17 22:00:31 -0600553 for alias, struct_name in self._aliases.items():
Heiko Schochere9cde872019-04-16 13:31:58 +0200554 if alias not in sorted(structs):
555 self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
556 STRUCT_PREFIX, struct_name))
Simon Glass7581c012017-06-18 22:08:58 -0600557
Simon Glass2be282c2017-06-18 22:08:59 -0600558 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600559 """Output the C code for a node
560
561 Args:
562 node: node to output
563 """
Walter Lozanodac82282020-07-03 08:07:17 -0300564 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600565 var_name = conv_name_to_c(node.name)
Walter Lozano51f12632020-06-25 01:10:13 -0300566 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass2be282c2017-06-18 22:08:59 -0600567 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glass1953ce72019-05-17 22:00:32 -0600568 for pname in sorted(node.props):
569 prop = node.props[pname]
Simon Glass7581c012017-06-18 22:08:58 -0600570 if pname in PROP_IGNORE_LIST or pname[0] == '#':
571 continue
Simon Glass2be282c2017-06-18 22:08:59 -0600572 member_name = conv_name_to_c(prop.name)
573 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
Simon Glass7581c012017-06-18 22:08:58 -0600574
575 # Special handling for lists
Simon Glass2be282c2017-06-18 22:08:59 -0600576 if isinstance(prop.value, list):
577 self.buf('{')
Simon Glass7581c012017-06-18 22:08:58 -0600578 vals = []
579 # For phandles, output a reference to the platform data
580 # of the target node.
Simon Glass8fed2eb2017-08-29 14:15:55 -0600581 info = self.get_phandle_argc(prop, node.name)
582 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600583 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600584 pos = 0
Walter Lozano51f12632020-06-25 01:10:13 -0300585 item = 0
Simon Glass634eba42017-08-29 14:15:59 -0600586 for args in info.args:
587 phandle_cell = prop.value[pos]
Simon Glass7581c012017-06-18 22:08:58 -0600588 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
Simon Glass72ab7c52017-08-29 14:15:53 -0600589 target_node = self._fdt.phandle_to_node[phandle]
Simon Glass2be282c2017-06-18 22:08:59 -0600590 name = conv_name_to_c(target_node.name)
Simon Glass634eba42017-08-29 14:15:59 -0600591 arg_values = []
592 for i in range(args):
593 arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
594 pos += 1 + args
Walter Lozano51f12632020-06-25 01:10:13 -0300595 # node member is filled with NULL as the real value
596 # will be update at run-time during dm_init_and_scan()
597 # by dm_populate_phandle_data()
598 vals.append('\t{NULL, {%s}}' % (', '.join(arg_values)))
599 var_node = '%s%s.%s[%d].node' % \
600 (VAL_PREFIX, var_name, member_name, item)
601 # Save the the link information to be use to define
602 # dm_populate_phandle_data()
603 self._links.append({'var_node': var_node, 'dev_name': name})
604 item += 1
Simon Glass35d50372017-08-29 14:15:57 -0600605 for val in vals:
606 self.buf('\n\t\t%s,' % val)
Simon Glass7581c012017-06-18 22:08:58 -0600607 else:
608 for val in prop.value:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600609 vals.append(get_value(prop.type, val))
Simon Glass21d54ac2017-08-29 14:15:49 -0600610
Simon Glass35d50372017-08-29 14:15:57 -0600611 # Put 8 values per line to avoid very long lines.
Simon Glass90a81322019-05-17 22:00:31 -0600612 for i in range(0, len(vals), 8):
Simon Glass35d50372017-08-29 14:15:57 -0600613 if i:
614 self.buf(',\n\t\t')
615 self.buf(', '.join(vals[i:i + 8]))
Simon Glass2be282c2017-06-18 22:08:59 -0600616 self.buf('}')
Simon Glass7581c012017-06-18 22:08:58 -0600617 else:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600618 self.buf(get_value(prop.type, prop.value))
Simon Glass2be282c2017-06-18 22:08:59 -0600619 self.buf(',\n')
620 self.buf('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600621
622 # Add a device declaration
Simon Glass2be282c2017-06-18 22:08:59 -0600623 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
624 self.buf('\t.name\t\t= "%s",\n' % struct_name)
625 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
626 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
627 self.buf('};\n')
628 self.buf('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600629
Simon Glass2be282c2017-06-18 22:08:59 -0600630 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600631
Simon Glass2be282c2017-06-18 22:08:59 -0600632 def generate_tables(self):
Simon Glass7581c012017-06-18 22:08:58 -0600633 """Generate device defintions for the platform data
634
635 This writes out C platform data initialisation data and
636 U_BOOT_DEVICE() declarations for each valid node. Where a node has
637 multiple compatible strings, a #define is used to make them equivalent.
638
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100639 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600640 information.
641 """
Simon Glassd5031142017-08-29 14:16:01 -0600642 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600643 self.out('#include <common.h>\n')
644 self.out('#include <dm.h>\n')
645 self.out('#include <dt-structs.h>\n')
646 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600647 nodes_to_output = list(self._valid_nodes)
648
649 # Keep outputing nodes until there is none left
650 while nodes_to_output:
651 node = nodes_to_output[0]
652 # Output all the node's dependencies first
653 for req_node in node.phandles:
654 if req_node in nodes_to_output:
Simon Glass2be282c2017-06-18 22:08:59 -0600655 self.output_node(req_node)
Simon Glass7581c012017-06-18 22:08:58 -0600656 nodes_to_output.remove(req_node)
Simon Glass2be282c2017-06-18 22:08:59 -0600657 self.output_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600658 nodes_to_output.remove(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600659
Walter Lozano51f12632020-06-25 01:10:13 -0300660 # Define dm_populate_phandle_data() which will add the linking between
661 # nodes using DM_GET_DEVICE
662 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
663 self.buf('void dm_populate_phandle_data(void) {\n')
664 for l in self._links:
Walter Lozanoad340172020-06-25 01:10:16 -0300665 self.buf('\t%s = DM_GET_DEVICE(%s);\n' %
666 (l['var_node'], l['dev_name']))
Walter Lozano51f12632020-06-25 01:10:13 -0300667 self.buf('}\n')
668
669 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600670
Walter Lozano361e7332020-06-25 01:10:08 -0300671def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False):
Simon Glassfa0ea5b2017-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 Lozano361e7332020-06-25 01:10:08 -0300683 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled)
Walter Lozanodac82282020-07-03 08:07:17 -0300684 plat.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600685 plat.scan_dtb()
686 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600687 plat.scan_reg_sizes()
Simon Glassfa0ea5b2017-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)