blob: 07f502717050001381ce284e57a1549c7c6bcce8 [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):
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300116 """Get the node's list of compatible string as a C identifiers
Simon Glass56e0bbe2017-06-18 22:09:02 -0600117
118 Args:
119 node: Node object to check
120 Return:
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300121 List of C identifiers for all the compatible strings
Simon Glass56e0bbe2017-06-18 22:09:02 -0600122 """
123 compat = node.props['compatible'].value
Walter Lozanodcb3ed62020-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 Glass56e0bbe2017-06-18 22:09:02 -0600127
Simon Glass56e0bbe2017-06-18 22:09:02 -0600128
Simon Glass2be282c2017-06-18 22:08:59 -0600129class DtbPlatdata(object):
Simon Glass7581c012017-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 Glass2be282c2017-06-18 22:08:59 -0600137 _fdt: Fdt object, referencing the device tree
Simon Glass7581c012017-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 Glasse36024b2017-06-18 22:09:01 -0600140 _include_disabled: true to include nodes marked status = "disabled"
Simon Glass7581c012017-06-18 22:08:58 -0600141 _outfile: The current output file (sys.stdout or a real file)
Walter Lozano361e7332020-06-25 01:10:08 -0300142 _warning_disabled: true to disable warnings about driver names not found
Simon Glass7581c012017-06-18 22:08:58 -0600143 _lines: Stashed list of output lines for outputting in the future
Walter Lozanoace16e82020-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 Lozanodac82282020-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 Lozano51f12632020-06-25 01:10:13 -0300152 _links: List of links to be included in dm_populate_phandle_data()
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300153 _drivers_additional: List of additional drivers to use during scanning
Simon Glass7581c012017-06-18 22:08:58 -0600154 """
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300155 def __init__(self, dtb_fname, include_disabled, warning_disabled,
156 drivers_additional=[]):
Simon Glass2be282c2017-06-18 22:08:59 -0600157 self._fdt = None
Simon Glass7581c012017-06-18 22:08:58 -0600158 self._dtb_fname = dtb_fname
159 self._valid_nodes = None
Simon Glasse36024b2017-06-18 22:09:01 -0600160 self._include_disabled = include_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600161 self._outfile = None
Walter Lozano361e7332020-06-25 01:10:08 -0300162 self._warning_disabled = warning_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600163 self._lines = []
164 self._aliases = {}
Walter Lozanodac82282020-07-03 08:07:17 -0300165 self._drivers = []
166 self._driver_aliases = {}
Walter Lozano51f12632020-06-25 01:10:13 -0300167 self._links = []
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300168 self._drivers_additional = drivers_additional
Walter Lozanodac82282020-07-03 08:07:17 -0300169
170 def get_normalized_compat_name(self, node):
171 """Get a node's normalized compat name
172
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300173 Returns a valid driver name by retrieving node's list of compatible
Walter Lozanodac82282020-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 Lozanodcb3ed62020-07-23 00:22:03 -0300187 compat_list_c = get_compat_name(node)
Walter Lozanodac82282020-07-03 08:07:17 -0300188
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300189 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]))
203
204 return compat_list_c[0], compat_list_c[1:]
Simon Glass7581c012017-06-18 22:08:58 -0600205
Simon Glass2be282c2017-06-18 22:08:59 -0600206 def setup_output(self, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600207 """Set up the output destination
208
Simon Glass2be282c2017-06-18 22:08:59 -0600209 Once this is done, future calls to self.out() will output to this
Simon Glass7581c012017-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 Glass2be282c2017-06-18 22:08:59 -0600220 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600221 """Output a string to the output file
222
223 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600224 line: String to output
Simon Glass7581c012017-06-18 22:08:58 -0600225 """
Simon Glass2be282c2017-06-18 22:08:59 -0600226 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600227
Simon Glass2be282c2017-06-18 22:08:59 -0600228 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600229 """Buffer up a string to send later
230
231 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600232 line: String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600233 """
Simon Glass2be282c2017-06-18 22:08:59 -0600234 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600235
Simon Glass2be282c2017-06-18 22:08:59 -0600236 def get_buf(self):
Simon Glass7581c012017-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 Glassd5031142017-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 Glass8fed2eb2017-08-29 14:15:55 -0600256 def get_phandle_argc(self, prop, node_name):
257 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600258
Simon Glass8fed2eb2017-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.
Simon Glass2925c262017-08-29 14:15:54 -0600261
Simon Glass8fed2eb2017-08-29 14:15:55 -0600262 Args:
263 prop: Prop object to check
264 Return:
265 Number of argument cells is this is a phandle, else None
266 """
Walter Lozanoad340172020-06-25 01:10:16 -0300267 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass760b7172018-07-06 10:27:31 -0600268 if not isinstance(prop.value, list):
269 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600270 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600271 i = 0
272
273 max_args = 0
274 args = []
275 while i < len(val):
276 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-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 Glass8fed2eb2017-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 Lozanoad340172020-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 Glass8fed2eb2017-08-29 14:15:55 -0600292 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300293 raise ValueError("Node '%s' has no cells property" %
294 (target.name))
Simon Glass8fed2eb2017-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 Glass2925c262017-08-29 14:15:54 -0600301
Walter Lozanodac82282020-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 Lozano6c74d1b2020-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 Glass2be282c2017-06-18 22:08:59 -0600358 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200359 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600360
Simon Glass2be282c2017-06-18 22:08:59 -0600361 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600362 device tree root node, and progress from there.
363 """
Simon Glass2be282c2017-06-18 22:08:59 -0600364 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600365
Simon Glass2be282c2017-06-18 22:08:59 -0600366 def scan_node(self, root):
367 """Scan a node and subnodes to build a tree of node and phandle info
368
Simon Glass72ab7c52017-08-29 14:15:53 -0600369 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600370
371 Args:
372 root: Root node for scan
373 """
Simon Glass7581c012017-06-18 22:08:58 -0600374 for node in root.subnodes:
375 if 'compatible' in node.props:
376 status = node.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600377 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600378 status.value != 'disabled'):
Simon Glass7581c012017-06-18 22:08:58 -0600379 self._valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600380
381 # recurse to handle any subnodes
Simon Glass2be282c2017-06-18 22:08:59 -0600382 self.scan_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600383
Simon Glass2be282c2017-06-18 22:08:59 -0600384 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600385 """Scan the device tree for useful information
386
387 This fills in the following properties:
Simon Glass7581c012017-06-18 22:08:58 -0600388 _valid_nodes: A list of nodes we wish to consider include in the
389 platform data
390 """
Simon Glass7581c012017-06-18 22:08:58 -0600391 self._valid_nodes = []
Simon Glass2be282c2017-06-18 22:08:59 -0600392 return self.scan_node(self._fdt.GetRoot())
Simon Glass7581c012017-06-18 22:08:58 -0600393
Simon Glassc20ee0e2017-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 Glassdfe5f5b2018-07-06 10:27:32 -0600432 raise ValueError("Node '%s' reg property is not an int" %
433 node.name)
Simon Glassc20ee0e2017-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 Glass2be282c2017-06-18 22:08:59 -0600455 def scan_structs(self):
Simon Glass7581c012017-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 Lozanodac82282020-07-03 08:07:17 -0300468 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-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 Lozanodac82282020-07-03 08:07:17 -0300492 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-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 Lozanodac82282020-07-03 08:07:17 -0300499 struct_name, aliases = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600500 for alias in aliases:
501 self._aliases[alias] = struct_name
502
503 return structs
504
Simon Glass2be282c2017-06-18 22:08:59 -0600505 def scan_phandles(self):
Simon Glass7581c012017-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 Glass8fed2eb2017-08-29 14:15:55 -0600521 info = self.get_phandle_argc(prop, node.name)
522 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600523 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600524 pos = 0
525 for args in info.args:
526 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-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 Glass634eba42017-08-29 14:15:59 -0600530 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600531
532
Simon Glass2be282c2017-06-18 22:08:59 -0600533 def generate_structs(self, structs):
Simon Glass7581c012017-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 Schuchardt2799a692020-02-25 21:35:39 +0100538 doc/driver-model/of-plat.rst for more information.
Simon Glass7581c012017-06-18 22:08:58 -0600539 """
Simon Glassd5031142017-08-29 14:16:01 -0600540 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600541 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900542 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600543
544 # Output the struct definition
545 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600546 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600547 for pname in sorted(structs[name]):
548 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600549 info = self.get_phandle_argc(prop, structs[name])
550 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600551 # For phandles, include a reference to the target
Simon Glass0d154632017-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 Glass2be282c2017-06-18 22:08:59 -0600554 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600555 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600556 else:
557 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-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 Glass7581c012017-06-18 22:08:58 -0600564
Simon Glass90a81322019-05-17 22:00:31 -0600565 for alias, struct_name in self._aliases.items():
Heiko Schochere9cde872019-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 Glass7581c012017-06-18 22:08:58 -0600569
Simon Glass2be282c2017-06-18 22:08:59 -0600570 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600571 """Output the C code for a node
572
573 Args:
574 node: node to output
575 """
Walter Lozanodac82282020-07-03 08:07:17 -0300576 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600577 var_name = conv_name_to_c(node.name)
Walter Lozano51f12632020-06-25 01:10:13 -0300578 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass2be282c2017-06-18 22:08:59 -0600579 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glass1953ce72019-05-17 22:00:32 -0600580 for pname in sorted(node.props):
581 prop = node.props[pname]
Simon Glass7581c012017-06-18 22:08:58 -0600582 if pname in PROP_IGNORE_LIST or pname[0] == '#':
583 continue
Simon Glass2be282c2017-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 Glass7581c012017-06-18 22:08:58 -0600586
587 # Special handling for lists
Simon Glass2be282c2017-06-18 22:08:59 -0600588 if isinstance(prop.value, list):
589 self.buf('{')
Simon Glass7581c012017-06-18 22:08:58 -0600590 vals = []
591 # For phandles, output a reference to the platform data
592 # of the target node.
Simon Glass8fed2eb2017-08-29 14:15:55 -0600593 info = self.get_phandle_argc(prop, node.name)
594 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600595 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600596 pos = 0
Walter Lozano51f12632020-06-25 01:10:13 -0300597 item = 0
Simon Glass634eba42017-08-29 14:15:59 -0600598 for args in info.args:
599 phandle_cell = prop.value[pos]
Simon Glass7581c012017-06-18 22:08:58 -0600600 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
Simon Glass72ab7c52017-08-29 14:15:53 -0600601 target_node = self._fdt.phandle_to_node[phandle]
Simon Glass2be282c2017-06-18 22:08:59 -0600602 name = conv_name_to_c(target_node.name)
Simon Glass634eba42017-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 Lozano51f12632020-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 Glass35d50372017-08-29 14:15:57 -0600617 for val in vals:
618 self.buf('\n\t\t%s,' % val)
Simon Glass7581c012017-06-18 22:08:58 -0600619 else:
620 for val in prop.value:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600621 vals.append(get_value(prop.type, val))
Simon Glass21d54ac2017-08-29 14:15:49 -0600622
Simon Glass35d50372017-08-29 14:15:57 -0600623 # Put 8 values per line to avoid very long lines.
Simon Glass90a81322019-05-17 22:00:31 -0600624 for i in range(0, len(vals), 8):
Simon Glass35d50372017-08-29 14:15:57 -0600625 if i:
626 self.buf(',\n\t\t')
627 self.buf(', '.join(vals[i:i + 8]))
Simon Glass2be282c2017-06-18 22:08:59 -0600628 self.buf('}')
Simon Glass7581c012017-06-18 22:08:58 -0600629 else:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600630 self.buf(get_value(prop.type, prop.value))
Simon Glass2be282c2017-06-18 22:08:59 -0600631 self.buf(',\n')
632 self.buf('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600633
634 # Add a device declaration
Simon Glass2be282c2017-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 Glass7581c012017-06-18 22:08:58 -0600641
Simon Glass2be282c2017-06-18 22:08:59 -0600642 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600643
Simon Glass2be282c2017-06-18 22:08:59 -0600644 def generate_tables(self):
Simon Glass7581c012017-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 Schuchardt2799a692020-02-25 21:35:39 +0100651 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600652 information.
653 """
Simon Glassd5031142017-08-29 14:16:01 -0600654 self.out_header()
Simon Glass2be282c2017-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 Glass7581c012017-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 Glass2be282c2017-06-18 22:08:59 -0600667 self.output_node(req_node)
Simon Glass7581c012017-06-18 22:08:58 -0600668 nodes_to_output.remove(req_node)
Simon Glass2be282c2017-06-18 22:08:59 -0600669 self.output_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600670 nodes_to_output.remove(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600671
Walter Lozano51f12632020-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 Lozanoad340172020-06-25 01:10:16 -0300677 self.buf('\t%s = DM_GET_DEVICE(%s);\n' %
678 (l['var_node'], l['dev_name']))
Walter Lozano51f12632020-06-25 01:10:13 -0300679 self.buf('}\n')
680
681 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600682
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300683def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
684 drivers_additional=[]):
Simon Glassfa0ea5b2017-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 Lozano6c74d1b2020-07-28 19:06:23 -0300696 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional)
Walter Lozanodac82282020-07-03 08:07:17 -0300697 plat.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600698 plat.scan_dtb()
699 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600700 plat.scan_reg_sizes()
Simon Glassfa0ea5b2017-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)