blob: 07f502717050001381ce284e57a1549c7c6bcce8 [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 Lozano8b239f42020-06-25 01:10:05 -0300144 _aliases: Dict that hold aliases for compatible strings
145 key: First compatible string declared in a node
146 value: List of additional compatible strings declared in a node
Walter Lozanoe675d962020-07-03 08:07:17 -0300147 _drivers: List of valid driver names found in drivers/
148 _driver_aliases: Dict that holds aliases for driver names
149 key: Driver alias declared with
150 U_BOOT_DRIVER_ALIAS(driver_alias, driver_name)
151 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Walter Lozanodc5b4372020-06-25 01:10:13 -0300152 _links: List of links to be included in dm_populate_phandle_data()
Walter Lozanod82062b2020-07-28 19:06:23 -0300153 _drivers_additional: List of additional drivers to use during scanning
Simon Glassd570dec2017-06-18 22:08:58 -0600154 """
Walter Lozanod82062b2020-07-28 19:06:23 -0300155 def __init__(self, dtb_fname, include_disabled, warning_disabled,
156 drivers_additional=[]):
Simon Glass1f730c82017-06-18 22:08:59 -0600157 self._fdt = None
Simon Glassd570dec2017-06-18 22:08:58 -0600158 self._dtb_fname = dtb_fname
159 self._valid_nodes = None
Simon Glasseab3f622017-06-18 22:09:01 -0600160 self._include_disabled = include_disabled
Simon Glassd570dec2017-06-18 22:08:58 -0600161 self._outfile = None
Walter Lozanoa324e412020-06-25 01:10:08 -0300162 self._warning_disabled = warning_disabled
Simon Glassd570dec2017-06-18 22:08:58 -0600163 self._lines = []
164 self._aliases = {}
Walter Lozanoe675d962020-07-03 08:07:17 -0300165 self._drivers = []
166 self._driver_aliases = {}
Walter Lozanodc5b4372020-06-25 01:10:13 -0300167 self._links = []
Walter Lozanod82062b2020-07-28 19:06:23 -0300168 self._drivers_additional = drivers_additional
Walter Lozanoe675d962020-07-03 08:07:17 -0300169
170 def get_normalized_compat_name(self, node):
171 """Get a node's normalized compat name
172
Walter Lozano5fe734c2020-07-23 00:22:03 -0300173 Returns a valid driver name by retrieving node's list of compatible
Walter Lozanoe675d962020-07-03 08:07:17 -0300174 string as a C identifier and performing a check against _drivers
175 and a lookup in driver_aliases printing a warning in case of failure.
176
177 Args:
178 node: Node object to check
179 Return:
180 Tuple:
181 Driver name associated with the first compatible string
182 List of C identifiers for all the other compatible strings
183 (possibly empty)
184 In case of no match found, the return will be the same as
185 get_compat_name()
186 """
Walter Lozano5fe734c2020-07-23 00:22:03 -0300187 compat_list_c = get_compat_name(node)
188
189 for compat_c in compat_list_c:
190 if not compat_c in self._drivers:
191 compat_c = self._driver_aliases.get(compat_c)
192 if not compat_c:
193 continue
194
195 aliases_c = compat_list_c
196 if compat_c in aliases_c:
197 aliases_c.remove(compat_c)
198 return compat_c, aliases_c
199
200 if not self._warning_disabled:
201 print('WARNING: the driver %s was not found in the driver list'
202 % (compat_list_c[0]))
Walter Lozanoe675d962020-07-03 08:07:17 -0300203
Walter Lozano5fe734c2020-07-23 00:22:03 -0300204 return compat_list_c[0], compat_list_c[1:]
Simon Glassd570dec2017-06-18 22:08:58 -0600205
Simon Glass1f730c82017-06-18 22:08:59 -0600206 def setup_output(self, fname):
Simon Glassd570dec2017-06-18 22:08:58 -0600207 """Set up the output destination
208
Simon Glass1f730c82017-06-18 22:08:59 -0600209 Once this is done, future calls to self.out() will output to this
Simon Glassd570dec2017-06-18 22:08:58 -0600210 file.
211
212 Args:
213 fname: Filename to send output to, or '-' for stdout
214 """
215 if fname == '-':
216 self._outfile = sys.stdout
217 else:
218 self._outfile = open(fname, 'w')
219
Simon Glass1f730c82017-06-18 22:08:59 -0600220 def out(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600221 """Output a string to the output file
222
223 Args:
Simon Glass1f730c82017-06-18 22:08:59 -0600224 line: String to output
Simon Glassd570dec2017-06-18 22:08:58 -0600225 """
Simon Glass1f730c82017-06-18 22:08:59 -0600226 self._outfile.write(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600227
Simon Glass1f730c82017-06-18 22:08:59 -0600228 def buf(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600229 """Buffer up a string to send later
230
231 Args:
Simon Glass1f730c82017-06-18 22:08:59 -0600232 line: String to add to our 'buffer' list
Simon Glassd570dec2017-06-18 22:08:58 -0600233 """
Simon Glass1f730c82017-06-18 22:08:59 -0600234 self._lines.append(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600235
Simon Glass1f730c82017-06-18 22:08:59 -0600236 def get_buf(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600237 """Get the contents of the output buffer, and clear it
238
239 Returns:
240 The output buffer, which is then cleared for future use
241 """
242 lines = self._lines
243 self._lines = []
244 return lines
245
Simon Glass68d32c72017-08-29 14:16:01 -0600246 def out_header(self):
247 """Output a message indicating that this is an auto-generated file"""
248 self.out('''/*
249 * DO NOT MODIFY
250 *
251 * This file was generated by dtoc from a .dtb (device tree binary) file.
252 */
253
254''')
255
Simon Glassec3b5e42017-08-29 14:15:55 -0600256 def get_phandle_argc(self, prop, node_name):
257 """Check if a node contains phandles
Simon Glassce5621d2017-08-29 14:15:54 -0600258
Simon Glassec3b5e42017-08-29 14:15:55 -0600259 We have no reliable way of detecting whether a node uses a phandle
260 or not. As an interim measure, use a list of known property names.
261
262 Args:
263 prop: Prop object to check
264 Return:
265 Number of argument cells is this is a phandle, else None
266 """
Walter Lozano179f0b62020-06-25 01:10:16 -0300267 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass609e2b12018-07-06 10:27:31 -0600268 if not isinstance(prop.value, list):
269 prop.value = [prop.value]
Simon Glassec3b5e42017-08-29 14:15:55 -0600270 val = prop.value
Simon Glassec3b5e42017-08-29 14:15:55 -0600271 i = 0
Simon Glassce5621d2017-08-29 14:15:54 -0600272
Simon Glassec3b5e42017-08-29 14:15:55 -0600273 max_args = 0
274 args = []
275 while i < len(val):
276 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass609e2b12018-07-06 10:27:31 -0600277 # If we get to the end of the list, stop. This can happen
278 # since some nodes have more phandles in the list than others,
279 # but we allocate enough space for the largest list. So those
280 # nodes with shorter lists end up with zeroes at the end.
281 if not phandle:
282 break
Simon Glassec3b5e42017-08-29 14:15:55 -0600283 target = self._fdt.phandle_to_node.get(phandle)
284 if not target:
285 raise ValueError("Cannot parse '%s' in node '%s'" %
286 (prop.name, node_name))
Walter Lozano179f0b62020-06-25 01:10:16 -0300287 cells = None
288 for prop_name in ['#clock-cells', '#gpio-cells']:
289 cells = target.props.get(prop_name)
290 if cells:
291 break
Simon Glassec3b5e42017-08-29 14:15:55 -0600292 if not cells:
Walter Lozano179f0b62020-06-25 01:10:16 -0300293 raise ValueError("Node '%s' has no cells property" %
294 (target.name))
Simon Glassec3b5e42017-08-29 14:15:55 -0600295 num_args = fdt_util.fdt32_to_cpu(cells.value)
296 max_args = max(max_args, num_args)
297 args.append(num_args)
298 i += 1 + num_args
299 return PhandleInfo(max_args, args)
300 return None
Simon Glassce5621d2017-08-29 14:15:54 -0600301
Walter Lozanoe675d962020-07-03 08:07:17 -0300302 def scan_driver(self, fn):
303 """Scan a driver file to build a list of driver names and aliases
304
305 This procedure will populate self._drivers and self._driver_aliases
306
307 Args
308 fn: Driver filename to scan
309 """
310 with open(fn, encoding='utf-8') as fd:
311 try:
312 buff = fd.read()
313 except UnicodeDecodeError:
314 # This seems to happen on older Python versions
315 print("Skipping file '%s' due to unicode error" % fn)
316 return
317
318 # The following re will search for driver names declared as
319 # U_BOOT_DRIVER(driver_name)
320 drivers = re.findall('U_BOOT_DRIVER\((.*)\)', buff)
321
322 for driver in drivers:
323 self._drivers.append(driver)
324
325 # The following re will search for driver aliases declared as
326 # U_BOOT_DRIVER_ALIAS(alias, driver_name)
327 driver_aliases = re.findall('U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
328 buff)
329
330 for alias in driver_aliases: # pragma: no cover
331 if len(alias) != 2:
332 continue
333 self._driver_aliases[alias[1]] = alias[0]
334
335 def scan_drivers(self):
336 """Scan the driver folders to build a list of driver names and aliases
337
338 This procedure will populate self._drivers and self._driver_aliases
339
340 """
341 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
342 if basedir == '':
343 basedir = './'
344 for (dirpath, dirnames, filenames) in os.walk(basedir):
345 for fn in filenames:
346 if not fn.endswith('.c'):
347 continue
348 self.scan_driver(dirpath + '/' + fn)
349
Walter Lozanod82062b2020-07-28 19:06:23 -0300350 for fn in self._drivers_additional:
351 if not isinstance(fn, str) or len(fn) == 0:
352 continue
353 if fn[0] == '/':
354 self.scan_driver(fn)
355 else:
356 self.scan_driver(basedir + '/' + fn)
357
Simon Glass1f730c82017-06-18 22:08:59 -0600358 def scan_dtb(self):
Anatolij Gustschinda707d42017-08-18 17:58:51 +0200359 """Scan the device tree to obtain a tree of nodes and properties
Simon Glassd570dec2017-06-18 22:08:58 -0600360
Simon Glass1f730c82017-06-18 22:08:59 -0600361 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glassd570dec2017-06-18 22:08:58 -0600362 device tree root node, and progress from there.
363 """
Simon Glass1f730c82017-06-18 22:08:59 -0600364 self._fdt = fdt.FdtScan(self._dtb_fname)
365
366 def scan_node(self, root):
367 """Scan a node and subnodes to build a tree of node and phandle info
Simon Glassd570dec2017-06-18 22:08:58 -0600368
Simon Glass17ffd622017-08-29 14:15:53 -0600369 This adds each node to self._valid_nodes.
Simon Glass1f730c82017-06-18 22:08:59 -0600370
371 Args:
372 root: Root node for scan
373 """
Simon Glassd570dec2017-06-18 22:08:58 -0600374 for node in root.subnodes:
375 if 'compatible' in node.props:
376 status = node.props.get('status')
Simon Glasseab3f622017-06-18 22:09:01 -0600377 if (not self._include_disabled and not status or
Simon Glass1f730c82017-06-18 22:08:59 -0600378 status.value != 'disabled'):
Simon Glassd570dec2017-06-18 22:08:58 -0600379 self._valid_nodes.append(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600380
381 # recurse to handle any subnodes
Simon Glass1f730c82017-06-18 22:08:59 -0600382 self.scan_node(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600383
Simon Glass1f730c82017-06-18 22:08:59 -0600384 def scan_tree(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600385 """Scan the device tree for useful information
386
387 This fills in the following properties:
Simon Glassd570dec2017-06-18 22:08:58 -0600388 _valid_nodes: A list of nodes we wish to consider include in the
389 platform data
390 """
Simon Glassd570dec2017-06-18 22:08:58 -0600391 self._valid_nodes = []
Simon Glass1f730c82017-06-18 22:08:59 -0600392 return self.scan_node(self._fdt.GetRoot())
Simon Glassd570dec2017-06-18 22:08:58 -0600393
Simon Glass1b1fe412017-08-29 14:15:50 -0600394 @staticmethod
395 def get_num_cells(node):
396 """Get the number of cells in addresses and sizes for this node
397
398 Args:
399 node: Node to check
400
401 Returns:
402 Tuple:
403 Number of address cells for this node
404 Number of size cells for this node
405 """
406 parent = node.parent
407 na, ns = 2, 2
408 if parent:
409 na_prop = parent.props.get('#address-cells')
410 ns_prop = parent.props.get('#size-cells')
411 if na_prop:
412 na = fdt_util.fdt32_to_cpu(na_prop.value)
413 if ns_prop:
414 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
415 return na, ns
416
417 def scan_reg_sizes(self):
418 """Scan for 64-bit 'reg' properties and update the values
419
420 This finds 'reg' properties with 64-bit data and converts the value to
421 an array of 64-values. This allows it to be output in a way that the
422 C code can read.
423 """
424 for node in self._valid_nodes:
425 reg = node.props.get('reg')
426 if not reg:
427 continue
428 na, ns = self.get_num_cells(node)
429 total = na + ns
430
431 if reg.type != fdt.TYPE_INT:
Simon Glassc38fee042018-07-06 10:27:32 -0600432 raise ValueError("Node '%s' reg property is not an int" %
433 node.name)
Simon Glass1b1fe412017-08-29 14:15:50 -0600434 if len(reg.value) % total:
435 raise ValueError("Node '%s' reg property has %d cells "
436 'which is not a multiple of na + ns = %d + %d)' %
437 (node.name, len(reg.value), na, ns))
438 reg.na = na
439 reg.ns = ns
440 if na != 1 or ns != 1:
441 reg.type = fdt.TYPE_INT64
442 i = 0
443 new_value = []
444 val = reg.value
445 if not isinstance(val, list):
446 val = [val]
447 while i < len(val):
448 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
449 i += na
450 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
451 i += ns
452 new_value += [addr, size]
453 reg.value = new_value
454
Simon Glass1f730c82017-06-18 22:08:59 -0600455 def scan_structs(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600456 """Scan the device tree building up the C structures we will use.
457
458 Build a dict keyed by C struct name containing a dict of Prop
459 object for each struct field (keyed by property name). Where the
460 same struct appears multiple times, try to use the 'widest'
461 property, i.e. the one with a type which can express all others.
462
463 Once the widest property is determined, all other properties are
464 updated to match that width.
465 """
466 structs = {}
467 for node in self._valid_nodes:
Walter Lozanoe675d962020-07-03 08:07:17 -0300468 node_name, _ = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600469 fields = {}
470
471 # Get a list of all the valid properties in this node.
472 for name, prop in node.props.items():
473 if name not in PROP_IGNORE_LIST and name[0] != '#':
474 fields[name] = copy.deepcopy(prop)
475
476 # If we've seen this node_name before, update the existing struct.
477 if node_name in structs:
478 struct = structs[node_name]
479 for name, prop in fields.items():
480 oldprop = struct.get(name)
481 if oldprop:
482 oldprop.Widen(prop)
483 else:
484 struct[name] = prop
485
486 # Otherwise store this as a new struct.
487 else:
488 structs[node_name] = fields
489
490 upto = 0
491 for node in self._valid_nodes:
Walter Lozanoe675d962020-07-03 08:07:17 -0300492 node_name, _ = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600493 struct = structs[node_name]
494 for name, prop in node.props.items():
495 if name not in PROP_IGNORE_LIST and name[0] != '#':
496 prop.Widen(struct[name])
497 upto += 1
498
Walter Lozanoe675d962020-07-03 08:07:17 -0300499 struct_name, aliases = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600500 for alias in aliases:
501 self._aliases[alias] = struct_name
502
503 return structs
504
Simon Glass1f730c82017-06-18 22:08:59 -0600505 def scan_phandles(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600506 """Figure out what phandles each node uses
507
508 We need to be careful when outputing nodes that use phandles since
509 they must come after the declaration of the phandles in the C file.
510 Otherwise we get a compiler error since the phandle struct is not yet
511 declared.
512
513 This function adds to each node a list of phandle nodes that the node
514 depends on. This allows us to output things in the right order.
515 """
516 for node in self._valid_nodes:
517 node.phandles = set()
518 for pname, prop in node.props.items():
519 if pname in PROP_IGNORE_LIST or pname[0] == '#':
520 continue
Simon Glassec3b5e42017-08-29 14:15:55 -0600521 info = self.get_phandle_argc(prop, node.name)
522 if info:
Simon Glassec3b5e42017-08-29 14:15:55 -0600523 # Process the list as pairs of (phandle, id)
Simon Glass3deeb472017-08-29 14:15:59 -0600524 pos = 0
525 for args in info.args:
526 phandle_cell = prop.value[pos]
Simon Glassec3b5e42017-08-29 14:15:55 -0600527 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
528 target_node = self._fdt.phandle_to_node[phandle]
529 node.phandles.add(target_node)
Simon Glass3deeb472017-08-29 14:15:59 -0600530 pos += 1 + args
Simon Glassd570dec2017-06-18 22:08:58 -0600531
532
Simon Glass1f730c82017-06-18 22:08:59 -0600533 def generate_structs(self, structs):
Simon Glassd570dec2017-06-18 22:08:58 -0600534 """Generate struct defintions for the platform data
535
536 This writes out the body of a header file consisting of structure
537 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +0100538 doc/driver-model/of-plat.rst for more information.
Simon Glassd570dec2017-06-18 22:08:58 -0600539 """
Simon Glass68d32c72017-08-29 14:16:01 -0600540 self.out_header()
Simon Glass1f730c82017-06-18 22:08:59 -0600541 self.out('#include <stdbool.h>\n')
Masahiro Yamada75f82d02018-03-05 01:20:11 +0900542 self.out('#include <linux/libfdt.h>\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600543
544 # Output the struct definition
545 for name in sorted(structs):
Simon Glass1f730c82017-06-18 22:08:59 -0600546 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glassd570dec2017-06-18 22:08:58 -0600547 for pname in sorted(structs[name]):
548 prop = structs[name][pname]
Simon Glassec3b5e42017-08-29 14:15:55 -0600549 info = self.get_phandle_argc(prop, structs[name])
550 if info:
Simon Glassd570dec2017-06-18 22:08:58 -0600551 # For phandles, include a reference to the target
Simon Glasse94414b2017-08-29 14:15:56 -0600552 struct_name = 'struct phandle_%d_arg' % info.max_args
553 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass1f730c82017-06-18 22:08:59 -0600554 conv_name_to_c(prop.name),
Simon Glass3deeb472017-08-29 14:15:59 -0600555 len(info.args)))
Simon Glassd570dec2017-06-18 22:08:58 -0600556 else:
557 ptype = TYPE_NAMES[prop.type]
Simon Glass1f730c82017-06-18 22:08:59 -0600558 self.out('\t%s%s' % (tab_to(2, ptype),
559 conv_name_to_c(prop.name)))
560 if isinstance(prop.value, list):
561 self.out('[%d]' % len(prop.value))
562 self.out(';\n')
563 self.out('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600564
Simon Glass61b88e52019-05-17 22:00:31 -0600565 for alias, struct_name in self._aliases.items():
Heiko Schocher701c4012019-04-16 13:31:58 +0200566 if alias not in sorted(structs):
567 self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
568 STRUCT_PREFIX, struct_name))
Simon Glassd570dec2017-06-18 22:08:58 -0600569
Simon Glass1f730c82017-06-18 22:08:59 -0600570 def output_node(self, node):
Simon Glassd570dec2017-06-18 22:08:58 -0600571 """Output the C code for a node
572
573 Args:
574 node: node to output
575 """
Walter Lozanoe675d962020-07-03 08:07:17 -0300576 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass1f730c82017-06-18 22:08:59 -0600577 var_name = conv_name_to_c(node.name)
Walter Lozanodc5b4372020-06-25 01:10:13 -0300578 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass1f730c82017-06-18 22:08:59 -0600579 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glassc82de562019-05-17 22:00:32 -0600580 for pname in sorted(node.props):
581 prop = node.props[pname]
Simon Glassd570dec2017-06-18 22:08:58 -0600582 if pname in PROP_IGNORE_LIST or pname[0] == '#':
583 continue
Simon Glass1f730c82017-06-18 22:08:59 -0600584 member_name = conv_name_to_c(prop.name)
585 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
Simon Glassd570dec2017-06-18 22:08:58 -0600586
587 # Special handling for lists
Simon Glass1f730c82017-06-18 22:08:59 -0600588 if isinstance(prop.value, list):
589 self.buf('{')
Simon Glassd570dec2017-06-18 22:08:58 -0600590 vals = []
591 # For phandles, output a reference to the platform data
592 # of the target node.
Simon Glassec3b5e42017-08-29 14:15:55 -0600593 info = self.get_phandle_argc(prop, node.name)
594 if info:
Simon Glassd570dec2017-06-18 22:08:58 -0600595 # Process the list as pairs of (phandle, id)
Simon Glass3deeb472017-08-29 14:15:59 -0600596 pos = 0
Walter Lozanodc5b4372020-06-25 01:10:13 -0300597 item = 0
Simon Glass3deeb472017-08-29 14:15:59 -0600598 for args in info.args:
599 phandle_cell = prop.value[pos]
Simon Glassd570dec2017-06-18 22:08:58 -0600600 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
Simon Glass17ffd622017-08-29 14:15:53 -0600601 target_node = self._fdt.phandle_to_node[phandle]
Simon Glass1f730c82017-06-18 22:08:59 -0600602 name = conv_name_to_c(target_node.name)
Simon Glass3deeb472017-08-29 14:15:59 -0600603 arg_values = []
604 for i in range(args):
605 arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
606 pos += 1 + args
Walter Lozanodc5b4372020-06-25 01:10:13 -0300607 # node member is filled with NULL as the real value
608 # will be update at run-time during dm_init_and_scan()
609 # by dm_populate_phandle_data()
610 vals.append('\t{NULL, {%s}}' % (', '.join(arg_values)))
611 var_node = '%s%s.%s[%d].node' % \
612 (VAL_PREFIX, var_name, member_name, item)
613 # Save the the link information to be use to define
614 # dm_populate_phandle_data()
615 self._links.append({'var_node': var_node, 'dev_name': name})
616 item += 1
Simon Glassd0cd0752017-08-29 14:15:57 -0600617 for val in vals:
618 self.buf('\n\t\t%s,' % val)
Simon Glassd570dec2017-06-18 22:08:58 -0600619 else:
620 for val in prop.value:
Simon Glass7a2add12017-06-18 22:09:02 -0600621 vals.append(get_value(prop.type, val))
Simon Glass131e0b02017-08-29 14:15:49 -0600622
Simon Glassd0cd0752017-08-29 14:15:57 -0600623 # Put 8 values per line to avoid very long lines.
Simon Glass61b88e52019-05-17 22:00:31 -0600624 for i in range(0, len(vals), 8):
Simon Glassd0cd0752017-08-29 14:15:57 -0600625 if i:
626 self.buf(',\n\t\t')
627 self.buf(', '.join(vals[i:i + 8]))
Simon Glass1f730c82017-06-18 22:08:59 -0600628 self.buf('}')
Simon Glassd570dec2017-06-18 22:08:58 -0600629 else:
Simon Glass7a2add12017-06-18 22:09:02 -0600630 self.buf(get_value(prop.type, prop.value))
Simon Glass1f730c82017-06-18 22:08:59 -0600631 self.buf(',\n')
632 self.buf('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600633
634 # Add a device declaration
Simon Glass1f730c82017-06-18 22:08:59 -0600635 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
636 self.buf('\t.name\t\t= "%s",\n' % struct_name)
637 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
638 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
639 self.buf('};\n')
640 self.buf('\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600641
Simon Glass1f730c82017-06-18 22:08:59 -0600642 self.out(''.join(self.get_buf()))
Simon Glassd570dec2017-06-18 22:08:58 -0600643
Simon Glass1f730c82017-06-18 22:08:59 -0600644 def generate_tables(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600645 """Generate device defintions for the platform data
646
647 This writes out C platform data initialisation data and
648 U_BOOT_DEVICE() declarations for each valid node. Where a node has
649 multiple compatible strings, a #define is used to make them equivalent.
650
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +0100651 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glassd570dec2017-06-18 22:08:58 -0600652 information.
653 """
Simon Glass68d32c72017-08-29 14:16:01 -0600654 self.out_header()
Simon Glass1f730c82017-06-18 22:08:59 -0600655 self.out('#include <common.h>\n')
656 self.out('#include <dm.h>\n')
657 self.out('#include <dt-structs.h>\n')
658 self.out('\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600659 nodes_to_output = list(self._valid_nodes)
660
661 # Keep outputing nodes until there is none left
662 while nodes_to_output:
663 node = nodes_to_output[0]
664 # Output all the node's dependencies first
665 for req_node in node.phandles:
666 if req_node in nodes_to_output:
Simon Glass1f730c82017-06-18 22:08:59 -0600667 self.output_node(req_node)
Simon Glassd570dec2017-06-18 22:08:58 -0600668 nodes_to_output.remove(req_node)
Simon Glass1f730c82017-06-18 22:08:59 -0600669 self.output_node(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600670 nodes_to_output.remove(node)
Simon Glass3fa797a2017-06-18 22:09:03 -0600671
Walter Lozanodc5b4372020-06-25 01:10:13 -0300672 # Define dm_populate_phandle_data() which will add the linking between
673 # nodes using DM_GET_DEVICE
674 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
675 self.buf('void dm_populate_phandle_data(void) {\n')
676 for l in self._links:
Walter Lozano179f0b62020-06-25 01:10:16 -0300677 self.buf('\t%s = DM_GET_DEVICE(%s);\n' %
678 (l['var_node'], l['dev_name']))
Walter Lozanodc5b4372020-06-25 01:10:13 -0300679 self.buf('}\n')
680
681 self.out(''.join(self.get_buf()))
Simon Glass3fa797a2017-06-18 22:09:03 -0600682
Walter Lozanod82062b2020-07-28 19:06:23 -0300683def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
684 drivers_additional=[]):
Simon Glass3fa797a2017-06-18 22:09:03 -0600685 """Run all the steps of the dtoc tool
686
687 Args:
688 args: List of non-option arguments provided to the problem
689 dtb_file: Filename of dtb file to process
690 include_disabled: True to include disabled nodes
691 output: Name of output file
692 """
693 if not args:
694 raise ValueError('Please specify a command: struct, platdata')
695
Walter Lozanod82062b2020-07-28 19:06:23 -0300696 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional)
Walter Lozanoe675d962020-07-03 08:07:17 -0300697 plat.scan_drivers()
Simon Glass3fa797a2017-06-18 22:09:03 -0600698 plat.scan_dtb()
699 plat.scan_tree()
Simon Glass1b1fe412017-08-29 14:15:50 -0600700 plat.scan_reg_sizes()
Simon Glass3fa797a2017-06-18 22:09:03 -0600701 plat.setup_output(output)
702 structs = plat.scan_structs()
703 plat.scan_phandles()
704
705 for cmd in args[0].split(','):
706 if cmd == 'struct':
707 plat.generate_structs(structs)
708 elif cmd == 'platdata':
709 plat.generate_tables()
710 else:
711 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
712 cmd)