blob: 72725bb6fa1080cac5981a41cf608e6b1063c62f [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
Simon Glass97136eb2020-10-03 09:25:19 -060057# Holds a single phandle link, allowing a C struct value to be assigned to point
58# to a device
59#
60# var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node')
61# dev_name: Name of device to assign to (e.g. 'clock')
62PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name'])
63
Simon Glass8fed2eb2017-08-29 14:15:55 -060064
Simon Glass2be282c2017-06-18 22:08:59 -060065def conv_name_to_c(name):
Simon Glass7581c012017-06-18 22:08:58 -060066 """Convert a device-tree name to a C identifier
67
Simon Glass30107b02017-06-18 22:09:04 -060068 This uses multiple replace() calls instead of re.sub() since it is faster
69 (400ms for 1m calls versus 1000ms for the 're' version).
70
Simon Glass7581c012017-06-18 22:08:58 -060071 Args:
72 name: Name to convert
73 Return:
74 String containing the C version of this name
75 """
Simon Glass2be282c2017-06-18 22:08:59 -060076 new = name.replace('@', '_at_')
77 new = new.replace('-', '_')
78 new = new.replace(',', '_')
79 new = new.replace('.', '_')
Simon Glass2be282c2017-06-18 22:08:59 -060080 return new
Simon Glass7581c012017-06-18 22:08:58 -060081
Simon Glass2be282c2017-06-18 22:08:59 -060082def tab_to(num_tabs, line):
83 """Append tabs to a line of text to reach a tab stop.
Simon Glass7581c012017-06-18 22:08:58 -060084
Simon Glass2be282c2017-06-18 22:08:59 -060085 Args:
86 num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
87 line: Line of text to append to
88
89 Returns:
90 line with the correct number of tabs appeneded. If the line already
91 extends past that tab stop then a single space is appended.
92 """
93 if len(line) >= num_tabs * 8:
94 return line + ' '
95 return line + '\t' * (num_tabs - len(line) // 8)
96
Simon Glass56e0bbe2017-06-18 22:09:02 -060097def get_value(ftype, value):
98 """Get a value as a C expression
99
100 For integers this returns a byte-swapped (little-endian) hex string
101 For bytes this returns a hex string, e.g. 0x12
102 For strings this returns a literal string enclosed in quotes
103 For booleans this return 'true'
104
105 Args:
106 type: Data type (fdt_util)
107 value: Data value, as a string of bytes
108 """
109 if ftype == fdt.TYPE_INT:
110 return '%#x' % fdt_util.fdt32_to_cpu(value)
111 elif ftype == fdt.TYPE_BYTE:
Simon Glass9b044f72019-05-17 22:00:43 -0600112 return '%#x' % tools.ToByte(value[0])
Simon Glass56e0bbe2017-06-18 22:09:02 -0600113 elif ftype == fdt.TYPE_STRING:
Simon Glassf02d0eb2020-07-07 21:32:06 -0600114 # Handle evil ACPI backslashes by adding another backslash before them.
115 # So "\\_SB.GPO0" in the device tree effectively stays like that in C
116 return '"%s"' % value.replace('\\', '\\\\')
Simon Glass56e0bbe2017-06-18 22:09:02 -0600117 elif ftype == fdt.TYPE_BOOL:
118 return 'true'
Simon Glassfbdfd222017-08-29 14:15:48 -0600119 elif ftype == fdt.TYPE_INT64:
120 return '%#x' % value
Simon Glass56e0bbe2017-06-18 22:09:02 -0600121
122def get_compat_name(node):
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300123 """Get the node's list of compatible string as a C identifiers
Simon Glass56e0bbe2017-06-18 22:09:02 -0600124
125 Args:
126 node: Node object to check
127 Return:
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300128 List of C identifiers for all the compatible strings
Simon Glass56e0bbe2017-06-18 22:09:02 -0600129 """
130 compat = node.props['compatible'].value
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300131 if not isinstance(compat, list):
132 compat = [compat]
133 return [conv_name_to_c(c) for c in compat]
Simon Glass56e0bbe2017-06-18 22:09:02 -0600134
Simon Glass56e0bbe2017-06-18 22:09:02 -0600135
Simon Glass2be282c2017-06-18 22:08:59 -0600136class DtbPlatdata(object):
Simon Glass7581c012017-06-18 22:08:58 -0600137 """Provide a means to convert device tree binary data to platform data
138
139 The output of this process is C structures which can be used in space-
140 constrained encvironments where the ~3KB code overhead of device tree
141 code is not affordable.
142
143 Properties:
Simon Glass2be282c2017-06-18 22:08:59 -0600144 _fdt: Fdt object, referencing the device tree
Simon Glass7581c012017-06-18 22:08:58 -0600145 _dtb_fname: Filename of the input device tree binary file
146 _valid_nodes: A list of Node object with compatible strings
Simon Glasse36024b2017-06-18 22:09:01 -0600147 _include_disabled: true to include nodes marked status = "disabled"
Simon Glass7581c012017-06-18 22:08:58 -0600148 _outfile: The current output file (sys.stdout or a real file)
Walter Lozano361e7332020-06-25 01:10:08 -0300149 _warning_disabled: true to disable warnings about driver names not found
Simon Glass7581c012017-06-18 22:08:58 -0600150 _lines: Stashed list of output lines for outputting in the future
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)
Simon Glass97136eb2020-10-03 09:25:19 -0600156 _links: List of links to be included in dm_populate_phandle_data(),
157 each a PhandleLink
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300158 _drivers_additional: List of additional drivers to use during scanning
Simon Glass7581c012017-06-18 22:08:58 -0600159 """
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300160 def __init__(self, dtb_fname, include_disabled, warning_disabled,
161 drivers_additional=[]):
Simon Glass2be282c2017-06-18 22:08:59 -0600162 self._fdt = None
Simon Glass7581c012017-06-18 22:08:58 -0600163 self._dtb_fname = dtb_fname
164 self._valid_nodes = None
Simon Glasse36024b2017-06-18 22:09:01 -0600165 self._include_disabled = include_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600166 self._outfile = None
Walter Lozano361e7332020-06-25 01:10:08 -0300167 self._warning_disabled = warning_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600168 self._lines = []
Walter Lozanodac82282020-07-03 08:07:17 -0300169 self._drivers = []
170 self._driver_aliases = {}
Walter Lozano51f12632020-06-25 01:10:13 -0300171 self._links = []
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300172 self._drivers_additional = drivers_additional
Walter Lozanodac82282020-07-03 08:07:17 -0300173
174 def get_normalized_compat_name(self, node):
175 """Get a node's normalized compat name
176
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300177 Returns a valid driver name by retrieving node's list of compatible
Walter Lozanodac82282020-07-03 08:07:17 -0300178 string as a C identifier and performing a check against _drivers
179 and a lookup in driver_aliases printing a warning in case of failure.
180
181 Args:
182 node: Node object to check
183 Return:
184 Tuple:
185 Driver name associated with the first compatible string
186 List of C identifiers for all the other compatible strings
187 (possibly empty)
188 In case of no match found, the return will be the same as
189 get_compat_name()
190 """
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300191 compat_list_c = get_compat_name(node)
Walter Lozanodac82282020-07-03 08:07:17 -0300192
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300193 for compat_c in compat_list_c:
194 if not compat_c in self._drivers:
195 compat_c = self._driver_aliases.get(compat_c)
196 if not compat_c:
197 continue
198
199 aliases_c = compat_list_c
200 if compat_c in aliases_c:
201 aliases_c.remove(compat_c)
202 return compat_c, aliases_c
203
204 if not self._warning_disabled:
205 print('WARNING: the driver %s was not found in the driver list'
206 % (compat_list_c[0]))
207
208 return compat_list_c[0], compat_list_c[1:]
Simon Glass7581c012017-06-18 22:08:58 -0600209
Simon Glass2be282c2017-06-18 22:08:59 -0600210 def setup_output(self, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600211 """Set up the output destination
212
Simon Glass2be282c2017-06-18 22:08:59 -0600213 Once this is done, future calls to self.out() will output to this
Simon Glass7581c012017-06-18 22:08:58 -0600214 file.
215
216 Args:
217 fname: Filename to send output to, or '-' for stdout
218 """
219 if fname == '-':
220 self._outfile = sys.stdout
221 else:
222 self._outfile = open(fname, 'w')
223
Simon Glass2be282c2017-06-18 22:08:59 -0600224 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600225 """Output a string to the output file
226
227 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600228 line: String to output
Simon Glass7581c012017-06-18 22:08:58 -0600229 """
Simon Glass2be282c2017-06-18 22:08:59 -0600230 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600231
Simon Glass2be282c2017-06-18 22:08:59 -0600232 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600233 """Buffer up a string to send later
234
235 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600236 line: String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600237 """
Simon Glass2be282c2017-06-18 22:08:59 -0600238 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600239
Simon Glass2be282c2017-06-18 22:08:59 -0600240 def get_buf(self):
Simon Glass7581c012017-06-18 22:08:58 -0600241 """Get the contents of the output buffer, and clear it
242
243 Returns:
244 The output buffer, which is then cleared for future use
245 """
246 lines = self._lines
247 self._lines = []
248 return lines
249
Simon Glassd5031142017-08-29 14:16:01 -0600250 def out_header(self):
251 """Output a message indicating that this is an auto-generated file"""
252 self.out('''/*
253 * DO NOT MODIFY
254 *
255 * This file was generated by dtoc from a .dtb (device tree binary) file.
256 */
257
258''')
259
Simon Glass8fed2eb2017-08-29 14:15:55 -0600260 def get_phandle_argc(self, prop, node_name):
261 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600262
Simon Glass8fed2eb2017-08-29 14:15:55 -0600263 We have no reliable way of detecting whether a node uses a phandle
264 or not. As an interim measure, use a list of known property names.
Simon Glass2925c262017-08-29 14:15:54 -0600265
Simon Glass8fed2eb2017-08-29 14:15:55 -0600266 Args:
267 prop: Prop object to check
268 Return:
269 Number of argument cells is this is a phandle, else None
270 """
Walter Lozanoad340172020-06-25 01:10:16 -0300271 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass760b7172018-07-06 10:27:31 -0600272 if not isinstance(prop.value, list):
273 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600274 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600275 i = 0
276
277 max_args = 0
278 args = []
279 while i < len(val):
280 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-07-06 10:27:31 -0600281 # If we get to the end of the list, stop. This can happen
282 # since some nodes have more phandles in the list than others,
283 # but we allocate enough space for the largest list. So those
284 # nodes with shorter lists end up with zeroes at the end.
285 if not phandle:
286 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600287 target = self._fdt.phandle_to_node.get(phandle)
288 if not target:
289 raise ValueError("Cannot parse '%s' in node '%s'" %
290 (prop.name, node_name))
Walter Lozanoad340172020-06-25 01:10:16 -0300291 cells = None
292 for prop_name in ['#clock-cells', '#gpio-cells']:
293 cells = target.props.get(prop_name)
294 if cells:
295 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600296 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300297 raise ValueError("Node '%s' has no cells property" %
298 (target.name))
Simon Glass8fed2eb2017-08-29 14:15:55 -0600299 num_args = fdt_util.fdt32_to_cpu(cells.value)
300 max_args = max(max_args, num_args)
301 args.append(num_args)
302 i += 1 + num_args
303 return PhandleInfo(max_args, args)
304 return None
Simon Glass2925c262017-08-29 14:15:54 -0600305
Walter Lozanodac82282020-07-03 08:07:17 -0300306 def scan_driver(self, fn):
307 """Scan a driver file to build a list of driver names and aliases
308
309 This procedure will populate self._drivers and self._driver_aliases
310
311 Args
312 fn: Driver filename to scan
313 """
314 with open(fn, encoding='utf-8') as fd:
315 try:
316 buff = fd.read()
317 except UnicodeDecodeError:
318 # This seems to happen on older Python versions
319 print("Skipping file '%s' due to unicode error" % fn)
320 return
321
322 # The following re will search for driver names declared as
323 # U_BOOT_DRIVER(driver_name)
324 drivers = re.findall('U_BOOT_DRIVER\((.*)\)', buff)
325
326 for driver in drivers:
327 self._drivers.append(driver)
328
329 # The following re will search for driver aliases declared as
330 # U_BOOT_DRIVER_ALIAS(alias, driver_name)
331 driver_aliases = re.findall('U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
332 buff)
333
334 for alias in driver_aliases: # pragma: no cover
335 if len(alias) != 2:
336 continue
337 self._driver_aliases[alias[1]] = alias[0]
338
339 def scan_drivers(self):
340 """Scan the driver folders to build a list of driver names and aliases
341
342 This procedure will populate self._drivers and self._driver_aliases
343
344 """
345 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
346 if basedir == '':
347 basedir = './'
348 for (dirpath, dirnames, filenames) in os.walk(basedir):
349 for fn in filenames:
350 if not fn.endswith('.c'):
351 continue
352 self.scan_driver(dirpath + '/' + fn)
353
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300354 for fn in self._drivers_additional:
355 if not isinstance(fn, str) or len(fn) == 0:
356 continue
357 if fn[0] == '/':
358 self.scan_driver(fn)
359 else:
360 self.scan_driver(basedir + '/' + fn)
361
Simon Glass2be282c2017-06-18 22:08:59 -0600362 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200363 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600364
Simon Glass2be282c2017-06-18 22:08:59 -0600365 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600366 device tree root node, and progress from there.
367 """
Simon Glass2be282c2017-06-18 22:08:59 -0600368 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600369
Simon Glass2be282c2017-06-18 22:08:59 -0600370 def scan_node(self, root):
371 """Scan a node and subnodes to build a tree of node and phandle info
372
Simon Glass72ab7c52017-08-29 14:15:53 -0600373 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600374
375 Args:
376 root: Root node for scan
377 """
Simon Glass7581c012017-06-18 22:08:58 -0600378 for node in root.subnodes:
379 if 'compatible' in node.props:
380 status = node.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600381 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600382 status.value != 'disabled'):
Simon Glass7581c012017-06-18 22:08:58 -0600383 self._valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600384
385 # recurse to handle any subnodes
Simon Glass2be282c2017-06-18 22:08:59 -0600386 self.scan_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600387
Simon Glass2be282c2017-06-18 22:08:59 -0600388 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600389 """Scan the device tree for useful information
390
391 This fills in the following properties:
Simon Glass7581c012017-06-18 22:08:58 -0600392 _valid_nodes: A list of nodes we wish to consider include in the
393 platform data
394 """
Simon Glass7581c012017-06-18 22:08:58 -0600395 self._valid_nodes = []
Simon Glass2be282c2017-06-18 22:08:59 -0600396 return self.scan_node(self._fdt.GetRoot())
Simon Glass7581c012017-06-18 22:08:58 -0600397
Simon Glassc20ee0e2017-08-29 14:15:50 -0600398 @staticmethod
399 def get_num_cells(node):
400 """Get the number of cells in addresses and sizes for this node
401
402 Args:
403 node: Node to check
404
405 Returns:
406 Tuple:
407 Number of address cells for this node
408 Number of size cells for this node
409 """
410 parent = node.parent
411 na, ns = 2, 2
412 if parent:
413 na_prop = parent.props.get('#address-cells')
414 ns_prop = parent.props.get('#size-cells')
415 if na_prop:
416 na = fdt_util.fdt32_to_cpu(na_prop.value)
417 if ns_prop:
418 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
419 return na, ns
420
421 def scan_reg_sizes(self):
422 """Scan for 64-bit 'reg' properties and update the values
423
424 This finds 'reg' properties with 64-bit data and converts the value to
425 an array of 64-values. This allows it to be output in a way that the
426 C code can read.
427 """
428 for node in self._valid_nodes:
429 reg = node.props.get('reg')
430 if not reg:
431 continue
432 na, ns = self.get_num_cells(node)
433 total = na + ns
434
435 if reg.type != fdt.TYPE_INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600436 raise ValueError("Node '%s' reg property is not an int" %
437 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600438 if len(reg.value) % total:
439 raise ValueError("Node '%s' reg property has %d cells "
440 'which is not a multiple of na + ns = %d + %d)' %
441 (node.name, len(reg.value), na, ns))
442 reg.na = na
443 reg.ns = ns
444 if na != 1 or ns != 1:
445 reg.type = fdt.TYPE_INT64
446 i = 0
447 new_value = []
448 val = reg.value
449 if not isinstance(val, list):
450 val = [val]
451 while i < len(val):
452 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
453 i += na
454 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
455 i += ns
456 new_value += [addr, size]
457 reg.value = new_value
458
Simon Glass2be282c2017-06-18 22:08:59 -0600459 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600460 """Scan the device tree building up the C structures we will use.
461
462 Build a dict keyed by C struct name containing a dict of Prop
463 object for each struct field (keyed by property name). Where the
464 same struct appears multiple times, try to use the 'widest'
465 property, i.e. the one with a type which can express all others.
466
467 Once the widest property is determined, all other properties are
468 updated to match that width.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600469
470 Returns:
471 dict containing structures:
472 key (str): Node name, as a C identifier
473 value: dict containing structure fields:
474 key (str): Field name
475 value: Prop object with field information
Simon Glass7581c012017-06-18 22:08:58 -0600476 """
477 structs = {}
478 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300479 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600480 fields = {}
481
482 # Get a list of all the valid properties in this node.
483 for name, prop in node.props.items():
484 if name not in PROP_IGNORE_LIST and name[0] != '#':
485 fields[name] = copy.deepcopy(prop)
486
487 # If we've seen this node_name before, update the existing struct.
488 if node_name in structs:
489 struct = structs[node_name]
490 for name, prop in fields.items():
491 oldprop = struct.get(name)
492 if oldprop:
493 oldprop.Widen(prop)
494 else:
495 struct[name] = prop
496
497 # Otherwise store this as a new struct.
498 else:
499 structs[node_name] = fields
500
501 upto = 0
502 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300503 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600504 struct = structs[node_name]
505 for name, prop in node.props.items():
506 if name not in PROP_IGNORE_LIST and name[0] != '#':
507 prop.Widen(struct[name])
508 upto += 1
509
Simon Glass7581c012017-06-18 22:08:58 -0600510 return structs
511
Simon Glass2be282c2017-06-18 22:08:59 -0600512 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600513 """Figure out what phandles each node uses
514
515 We need to be careful when outputing nodes that use phandles since
516 they must come after the declaration of the phandles in the C file.
517 Otherwise we get a compiler error since the phandle struct is not yet
518 declared.
519
520 This function adds to each node a list of phandle nodes that the node
521 depends on. This allows us to output things in the right order.
522 """
523 for node in self._valid_nodes:
524 node.phandles = set()
525 for pname, prop in node.props.items():
526 if pname in PROP_IGNORE_LIST or pname[0] == '#':
527 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600528 info = self.get_phandle_argc(prop, node.name)
529 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600530 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600531 pos = 0
532 for args in info.args:
533 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600534 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
535 target_node = self._fdt.phandle_to_node[phandle]
536 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600537 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600538
539
Simon Glass2be282c2017-06-18 22:08:59 -0600540 def generate_structs(self, structs):
Simon Glass7581c012017-06-18 22:08:58 -0600541 """Generate struct defintions for the platform data
542
543 This writes out the body of a header file consisting of structure
544 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100545 doc/driver-model/of-plat.rst for more information.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600546
547 Args:
548 structs: dict containing structures:
549 key (str): Node name, as a C identifier
550 value: dict containing structure fields:
551 key (str): Field name
552 value: Prop object with field information
553
Simon Glass7581c012017-06-18 22:08:58 -0600554 """
Simon Glassd5031142017-08-29 14:16:01 -0600555 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600556 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900557 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600558
559 # Output the struct definition
560 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600561 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600562 for pname in sorted(structs[name]):
563 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600564 info = self.get_phandle_argc(prop, structs[name])
565 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600566 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600567 struct_name = 'struct phandle_%d_arg' % info.max_args
568 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600569 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600570 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600571 else:
572 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600573 self.out('\t%s%s' % (tab_to(2, ptype),
574 conv_name_to_c(prop.name)))
575 if isinstance(prop.value, list):
576 self.out('[%d]' % len(prop.value))
577 self.out(';\n')
578 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600579
Simon Glass2be282c2017-06-18 22:08:59 -0600580 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600581 """Output the C code for a node
582
583 Args:
584 node: node to output
585 """
Simon Glass26e408f2020-10-03 09:25:18 -0600586 def _output_list(node, prop):
587 """Output the C code for a devicetree property that holds a list
588
589 Args:
590 node (fdt.Node): Node to output
591 prop (fdt.Prop): Prop to output
592 """
593 self.buf('{')
594 vals = []
595 # For phandles, output a reference to the platform data
596 # of the target node.
597 info = self.get_phandle_argc(prop, node.name)
598 if info:
599 # Process the list as pairs of (phandle, id)
600 pos = 0
601 item = 0
602 for args in info.args:
603 phandle_cell = prop.value[pos]
604 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
605 target_node = self._fdt.phandle_to_node[phandle]
606 name = conv_name_to_c(target_node.name)
607 arg_values = []
608 for i in range(args):
609 arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
610 pos += 1 + args
611 # node member is filled with NULL as the real value
612 # will be update at run-time during dm_init_and_scan()
613 # by dm_populate_phandle_data()
614 vals.append('\t{NULL, {%s}}' % (', '.join(arg_values)))
615 var_node = '%s%s.%s[%d].node' % \
616 (VAL_PREFIX, var_name, member_name, item)
617 # Save the the link information to be use to define
618 # dm_populate_phandle_data()
Simon Glass97136eb2020-10-03 09:25:19 -0600619 self._links.append(PhandleLink(var_node, name))
Simon Glass26e408f2020-10-03 09:25:18 -0600620 item += 1
621 for val in vals:
622 self.buf('\n\t\t%s,' % val)
623 else:
624 for val in prop.value:
625 vals.append(get_value(prop.type, val))
626
627 # Put 8 values per line to avoid very long lines.
628 for i in range(0, len(vals), 8):
629 if i:
630 self.buf(',\n\t\t')
631 self.buf(', '.join(vals[i:i + 8]))
632 self.buf('}')
633
Walter Lozanodac82282020-07-03 08:07:17 -0300634 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600635 var_name = conv_name_to_c(node.name)
Walter Lozano51f12632020-06-25 01:10:13 -0300636 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass2be282c2017-06-18 22:08:59 -0600637 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glass1953ce72019-05-17 22:00:32 -0600638 for pname in sorted(node.props):
639 prop = node.props[pname]
Simon Glass7581c012017-06-18 22:08:58 -0600640 if pname in PROP_IGNORE_LIST or pname[0] == '#':
641 continue
Simon Glass2be282c2017-06-18 22:08:59 -0600642 member_name = conv_name_to_c(prop.name)
643 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
Simon Glass7581c012017-06-18 22:08:58 -0600644
645 # Special handling for lists
Simon Glass2be282c2017-06-18 22:08:59 -0600646 if isinstance(prop.value, list):
Simon Glass26e408f2020-10-03 09:25:18 -0600647 _output_list(node, prop)
Simon Glass7581c012017-06-18 22:08:58 -0600648 else:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600649 self.buf(get_value(prop.type, prop.value))
Simon Glass2be282c2017-06-18 22:08:59 -0600650 self.buf(',\n')
651 self.buf('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600652
653 # Add a device declaration
Simon Glass2be282c2017-06-18 22:08:59 -0600654 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
655 self.buf('\t.name\t\t= "%s",\n' % struct_name)
656 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
657 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
658 self.buf('};\n')
659 self.buf('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600660
Simon Glass2be282c2017-06-18 22:08:59 -0600661 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600662
Simon Glass2be282c2017-06-18 22:08:59 -0600663 def generate_tables(self):
Simon Glass7581c012017-06-18 22:08:58 -0600664 """Generate device defintions for the platform data
665
666 This writes out C platform data initialisation data and
667 U_BOOT_DEVICE() declarations for each valid node. Where a node has
668 multiple compatible strings, a #define is used to make them equivalent.
669
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100670 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600671 information.
672 """
Simon Glassd5031142017-08-29 14:16:01 -0600673 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600674 self.out('#include <common.h>\n')
675 self.out('#include <dm.h>\n')
676 self.out('#include <dt-structs.h>\n')
677 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600678 nodes_to_output = list(self._valid_nodes)
679
680 # Keep outputing nodes until there is none left
681 while nodes_to_output:
682 node = nodes_to_output[0]
683 # Output all the node's dependencies first
684 for req_node in node.phandles:
685 if req_node in nodes_to_output:
Simon Glass2be282c2017-06-18 22:08:59 -0600686 self.output_node(req_node)
Simon Glass7581c012017-06-18 22:08:58 -0600687 nodes_to_output.remove(req_node)
Simon Glass2be282c2017-06-18 22:08:59 -0600688 self.output_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600689 nodes_to_output.remove(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600690
Walter Lozano51f12632020-06-25 01:10:13 -0300691 # Define dm_populate_phandle_data() which will add the linking between
692 # nodes using DM_GET_DEVICE
693 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
694 self.buf('void dm_populate_phandle_data(void) {\n')
Simon Glass97136eb2020-10-03 09:25:19 -0600695 for link in self._links:
Walter Lozanoad340172020-06-25 01:10:16 -0300696 self.buf('\t%s = DM_GET_DEVICE(%s);\n' %
Simon Glass97136eb2020-10-03 09:25:19 -0600697 (link.var_node, link.dev_name))
Walter Lozano51f12632020-06-25 01:10:13 -0300698 self.buf('}\n')
699
700 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600701
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300702def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
703 drivers_additional=[]):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600704 """Run all the steps of the dtoc tool
705
706 Args:
707 args: List of non-option arguments provided to the problem
708 dtb_file: Filename of dtb file to process
709 include_disabled: True to include disabled nodes
710 output: Name of output file
711 """
712 if not args:
713 raise ValueError('Please specify a command: struct, platdata')
714
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300715 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional)
Walter Lozanodac82282020-07-03 08:07:17 -0300716 plat.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600717 plat.scan_dtb()
718 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600719 plat.scan_reg_sizes()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600720 plat.setup_output(output)
721 structs = plat.scan_structs()
722 plat.scan_phandles()
723
724 for cmd in args[0].split(','):
725 if cmd == 'struct':
726 plat.generate_structs(structs)
727 elif cmd == 'platdata':
728 plat.generate_tables()
729 else:
730 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
731 cmd)