blob: ee98010423c06d2358018cdc430a3128884ca7b8 [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
Simon Glass5ea9dcc2020-11-08 20:36:17 -070038# C type declarations for the types we support
Simon Glass7581c012017-06-18 22:08:58 -060039TYPE_NAMES = {
Simon Glass5ea9dcc2020-11-08 20:36:17 -070040 fdt.Type.INT: 'fdt32_t',
41 fdt.Type.BYTE: 'unsigned char',
42 fdt.Type.STRING: 'const char *',
43 fdt.Type.BOOL: 'bool',
44 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 """
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700109 if ftype == fdt.Type.INT:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600110 return '%#x' % fdt_util.fdt32_to_cpu(value)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700111 elif ftype == fdt.Type.BYTE:
Simon Glassfc0056e2020-11-08 20:36:18 -0700112 ch = value[0]
113 return '%#x' % ord(ch) if type(ch) == str else ch
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700114 elif ftype == fdt.Type.STRING:
Simon Glassf02d0eb2020-07-07 21:32:06 -0600115 # Handle evil ACPI backslashes by adding another backslash before them.
116 # So "\\_SB.GPO0" in the device tree effectively stays like that in C
117 return '"%s"' % value.replace('\\', '\\\\')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700118 elif ftype == fdt.Type.BOOL:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600119 return 'true'
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700120 elif ftype == fdt.Type.INT64:
Simon Glassfbdfd222017-08-29 14:15:48 -0600121 return '%#x' % value
Simon Glass56e0bbe2017-06-18 22:09:02 -0600122
123def get_compat_name(node):
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300124 """Get the node's list of compatible string as a C identifiers
Simon Glass56e0bbe2017-06-18 22:09:02 -0600125
126 Args:
127 node: Node object to check
128 Return:
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300129 List of C identifiers for all the compatible strings
Simon Glass56e0bbe2017-06-18 22:09:02 -0600130 """
131 compat = node.props['compatible'].value
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300132 if not isinstance(compat, list):
133 compat = [compat]
134 return [conv_name_to_c(c) for c in compat]
Simon Glass56e0bbe2017-06-18 22:09:02 -0600135
Simon Glass56e0bbe2017-06-18 22:09:02 -0600136
Simon Glass2be282c2017-06-18 22:08:59 -0600137class DtbPlatdata(object):
Simon Glass7581c012017-06-18 22:08:58 -0600138 """Provide a means to convert device tree binary data to platform data
139
140 The output of this process is C structures which can be used in space-
141 constrained encvironments where the ~3KB code overhead of device tree
142 code is not affordable.
143
144 Properties:
Simon Glass2be282c2017-06-18 22:08:59 -0600145 _fdt: Fdt object, referencing the device tree
Simon Glass7581c012017-06-18 22:08:58 -0600146 _dtb_fname: Filename of the input device tree binary file
Simon Glass1b272732020-10-03 11:31:25 -0600147 _valid_nodes: A list of Node object with compatible strings. The list
148 is ordered by conv_name_to_c(node.name)
Simon Glasse36024b2017-06-18 22:09:01 -0600149 _include_disabled: true to include nodes marked status = "disabled"
Simon Glass7581c012017-06-18 22:08:58 -0600150 _outfile: The current output file (sys.stdout or a real file)
Walter Lozano361e7332020-06-25 01:10:08 -0300151 _warning_disabled: true to disable warnings about driver names not found
Simon Glass7581c012017-06-18 22:08:58 -0600152 _lines: Stashed list of output lines for outputting in the future
Walter Lozanodac82282020-07-03 08:07:17 -0300153 _drivers: List of valid driver names found in drivers/
154 _driver_aliases: Dict that holds aliases for driver names
155 key: Driver alias declared with
156 U_BOOT_DRIVER_ALIAS(driver_alias, driver_name)
157 value: Driver name declared with U_BOOT_DRIVER(driver_name)
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 Lozano6c74d1b2020-07-28 19:06:23 -0300171 self._drivers_additional = drivers_additional
Walter Lozanodac82282020-07-03 08:07:17 -0300172
173 def get_normalized_compat_name(self, node):
174 """Get a node's normalized compat name
175
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300176 Returns a valid driver name by retrieving node's list of compatible
Walter Lozanodac82282020-07-03 08:07:17 -0300177 string as a C identifier and performing a check against _drivers
178 and a lookup in driver_aliases printing a warning in case of failure.
179
180 Args:
181 node: Node object to check
182 Return:
183 Tuple:
184 Driver name associated with the first compatible string
185 List of C identifiers for all the other compatible strings
186 (possibly empty)
187 In case of no match found, the return will be the same as
188 get_compat_name()
189 """
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300190 compat_list_c = get_compat_name(node)
Walter Lozanodac82282020-07-03 08:07:17 -0300191
Walter Lozanodcb3ed62020-07-23 00:22:03 -0300192 for compat_c in compat_list_c:
193 if not compat_c in self._drivers:
194 compat_c = self._driver_aliases.get(compat_c)
195 if not compat_c:
196 continue
197
198 aliases_c = compat_list_c
199 if compat_c in aliases_c:
200 aliases_c.remove(compat_c)
201 return compat_c, aliases_c
202
203 if not self._warning_disabled:
204 print('WARNING: the driver %s was not found in the driver list'
205 % (compat_list_c[0]))
206
207 return compat_list_c[0], compat_list_c[1:]
Simon Glass7581c012017-06-18 22:08:58 -0600208
Simon Glass2be282c2017-06-18 22:08:59 -0600209 def setup_output(self, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600210 """Set up the output destination
211
Simon Glass2be282c2017-06-18 22:08:59 -0600212 Once this is done, future calls to self.out() will output to this
Simon Glass7581c012017-06-18 22:08:58 -0600213 file.
214
215 Args:
216 fname: Filename to send output to, or '-' for stdout
217 """
218 if fname == '-':
219 self._outfile = sys.stdout
220 else:
221 self._outfile = open(fname, 'w')
222
Simon Glass2be282c2017-06-18 22:08:59 -0600223 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600224 """Output a string to the output file
225
226 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600227 line: String to output
Simon Glass7581c012017-06-18 22:08:58 -0600228 """
Simon Glass2be282c2017-06-18 22:08:59 -0600229 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600230
Simon Glass2be282c2017-06-18 22:08:59 -0600231 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600232 """Buffer up a string to send later
233
234 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600235 line: String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600236 """
Simon Glass2be282c2017-06-18 22:08:59 -0600237 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600238
Simon Glass2be282c2017-06-18 22:08:59 -0600239 def get_buf(self):
Simon Glass7581c012017-06-18 22:08:58 -0600240 """Get the contents of the output buffer, and clear it
241
242 Returns:
243 The output buffer, which is then cleared for future use
244 """
245 lines = self._lines
246 self._lines = []
247 return lines
248
Simon Glassd5031142017-08-29 14:16:01 -0600249 def out_header(self):
250 """Output a message indicating that this is an auto-generated file"""
251 self.out('''/*
252 * DO NOT MODIFY
253 *
254 * This file was generated by dtoc from a .dtb (device tree binary) file.
255 */
256
257''')
258
Simon Glass8fed2eb2017-08-29 14:15:55 -0600259 def get_phandle_argc(self, prop, node_name):
260 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600261
Simon Glass8fed2eb2017-08-29 14:15:55 -0600262 We have no reliable way of detecting whether a node uses a phandle
263 or not. As an interim measure, use a list of known property names.
Simon Glass2925c262017-08-29 14:15:54 -0600264
Simon Glass8fed2eb2017-08-29 14:15:55 -0600265 Args:
266 prop: Prop object to check
267 Return:
268 Number of argument cells is this is a phandle, else None
269 """
Walter Lozanoad340172020-06-25 01:10:16 -0300270 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass760b7172018-07-06 10:27:31 -0600271 if not isinstance(prop.value, list):
272 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600273 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600274 i = 0
275
276 max_args = 0
277 args = []
278 while i < len(val):
279 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-07-06 10:27:31 -0600280 # If we get to the end of the list, stop. This can happen
281 # since some nodes have more phandles in the list than others,
282 # but we allocate enough space for the largest list. So those
283 # nodes with shorter lists end up with zeroes at the end.
284 if not phandle:
285 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600286 target = self._fdt.phandle_to_node.get(phandle)
287 if not target:
288 raise ValueError("Cannot parse '%s' in node '%s'" %
289 (prop.name, node_name))
Walter Lozanoad340172020-06-25 01:10:16 -0300290 cells = None
291 for prop_name in ['#clock-cells', '#gpio-cells']:
292 cells = target.props.get(prop_name)
293 if cells:
294 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600295 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300296 raise ValueError("Node '%s' has no cells property" %
297 (target.name))
Simon Glass8fed2eb2017-08-29 14:15:55 -0600298 num_args = fdt_util.fdt32_to_cpu(cells.value)
299 max_args = max(max_args, num_args)
300 args.append(num_args)
301 i += 1 + num_args
302 return PhandleInfo(max_args, args)
303 return None
Simon Glass2925c262017-08-29 14:15:54 -0600304
Walter Lozanodac82282020-07-03 08:07:17 -0300305 def scan_driver(self, fn):
306 """Scan a driver file to build a list of driver names and aliases
307
308 This procedure will populate self._drivers and self._driver_aliases
309
310 Args
311 fn: Driver filename to scan
312 """
313 with open(fn, encoding='utf-8') as fd:
314 try:
315 buff = fd.read()
316 except UnicodeDecodeError:
317 # This seems to happen on older Python versions
318 print("Skipping file '%s' due to unicode error" % fn)
319 return
320
321 # The following re will search for driver names declared as
322 # U_BOOT_DRIVER(driver_name)
323 drivers = re.findall('U_BOOT_DRIVER\((.*)\)', buff)
324
325 for driver in drivers:
326 self._drivers.append(driver)
327
328 # The following re will search for driver aliases declared as
329 # U_BOOT_DRIVER_ALIAS(alias, driver_name)
330 driver_aliases = re.findall('U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
331 buff)
332
333 for alias in driver_aliases: # pragma: no cover
334 if len(alias) != 2:
335 continue
336 self._driver_aliases[alias[1]] = alias[0]
337
338 def scan_drivers(self):
339 """Scan the driver folders to build a list of driver names and aliases
340
341 This procedure will populate self._drivers and self._driver_aliases
342
343 """
344 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
345 if basedir == '':
346 basedir = './'
347 for (dirpath, dirnames, filenames) in os.walk(basedir):
348 for fn in filenames:
349 if not fn.endswith('.c'):
350 continue
351 self.scan_driver(dirpath + '/' + fn)
352
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300353 for fn in self._drivers_additional:
354 if not isinstance(fn, str) or len(fn) == 0:
355 continue
356 if fn[0] == '/':
357 self.scan_driver(fn)
358 else:
359 self.scan_driver(basedir + '/' + fn)
360
Simon Glass2be282c2017-06-18 22:08:59 -0600361 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200362 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600363
Simon Glass2be282c2017-06-18 22:08:59 -0600364 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600365 device tree root node, and progress from there.
366 """
Simon Glass2be282c2017-06-18 22:08:59 -0600367 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600368
Simon Glass1b272732020-10-03 11:31:25 -0600369 def scan_node(self, root, valid_nodes):
Simon Glass2be282c2017-06-18 22:08:59 -0600370 """Scan a node and subnodes to build a tree of node and phandle info
371
Simon Glass72ab7c52017-08-29 14:15:53 -0600372 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600373
374 Args:
375 root: Root node for scan
Simon Glass1b272732020-10-03 11:31:25 -0600376 valid_nodes: List of Node objects to add to
Simon Glass2be282c2017-06-18 22:08:59 -0600377 """
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 Glass1b272732020-10-03 11:31:25 -0600383 valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600384
385 # recurse to handle any subnodes
Simon Glass1b272732020-10-03 11:31:25 -0600386 self.scan_node(node, valid_nodes)
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 Glass1b272732020-10-03 11:31:25 -0600395 valid_nodes = []
396 self.scan_node(self._fdt.GetRoot(), valid_nodes)
397 self._valid_nodes = sorted(valid_nodes,
398 key=lambda x: conv_name_to_c(x.name))
399 for idx, node in enumerate(self._valid_nodes):
400 node.idx = idx
Simon Glass7581c012017-06-18 22:08:58 -0600401
Simon Glassc20ee0e2017-08-29 14:15:50 -0600402 @staticmethod
403 def get_num_cells(node):
404 """Get the number of cells in addresses and sizes for this node
405
406 Args:
407 node: Node to check
408
409 Returns:
410 Tuple:
411 Number of address cells for this node
412 Number of size cells for this node
413 """
414 parent = node.parent
415 na, ns = 2, 2
416 if parent:
417 na_prop = parent.props.get('#address-cells')
418 ns_prop = parent.props.get('#size-cells')
419 if na_prop:
420 na = fdt_util.fdt32_to_cpu(na_prop.value)
421 if ns_prop:
422 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
423 return na, ns
424
425 def scan_reg_sizes(self):
426 """Scan for 64-bit 'reg' properties and update the values
427
428 This finds 'reg' properties with 64-bit data and converts the value to
429 an array of 64-values. This allows it to be output in a way that the
430 C code can read.
431 """
432 for node in self._valid_nodes:
433 reg = node.props.get('reg')
434 if not reg:
435 continue
436 na, ns = self.get_num_cells(node)
437 total = na + ns
438
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700439 if reg.type != fdt.Type.INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600440 raise ValueError("Node '%s' reg property is not an int" %
441 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600442 if len(reg.value) % total:
443 raise ValueError("Node '%s' reg property has %d cells "
444 'which is not a multiple of na + ns = %d + %d)' %
445 (node.name, len(reg.value), na, ns))
446 reg.na = na
447 reg.ns = ns
448 if na != 1 or ns != 1:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700449 reg.type = fdt.Type.INT64
Simon Glassc20ee0e2017-08-29 14:15:50 -0600450 i = 0
451 new_value = []
452 val = reg.value
453 if not isinstance(val, list):
454 val = [val]
455 while i < len(val):
456 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
457 i += na
458 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
459 i += ns
460 new_value += [addr, size]
461 reg.value = new_value
462
Simon Glass2be282c2017-06-18 22:08:59 -0600463 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600464 """Scan the device tree building up the C structures we will use.
465
466 Build a dict keyed by C struct name containing a dict of Prop
467 object for each struct field (keyed by property name). Where the
468 same struct appears multiple times, try to use the 'widest'
469 property, i.e. the one with a type which can express all others.
470
471 Once the widest property is determined, all other properties are
472 updated to match that width.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600473
474 Returns:
475 dict containing structures:
476 key (str): Node name, as a C identifier
477 value: dict containing structure fields:
478 key (str): Field name
479 value: Prop object with field information
Simon Glass7581c012017-06-18 22:08:58 -0600480 """
Simon Glass1b272732020-10-03 11:31:25 -0600481 structs = collections.OrderedDict()
Simon Glass7581c012017-06-18 22:08:58 -0600482 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300483 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600484 fields = {}
485
486 # Get a list of all the valid properties in this node.
487 for name, prop in node.props.items():
488 if name not in PROP_IGNORE_LIST and name[0] != '#':
489 fields[name] = copy.deepcopy(prop)
490
491 # If we've seen this node_name before, update the existing struct.
492 if node_name in structs:
493 struct = structs[node_name]
494 for name, prop in fields.items():
495 oldprop = struct.get(name)
496 if oldprop:
497 oldprop.Widen(prop)
498 else:
499 struct[name] = prop
500
501 # Otherwise store this as a new struct.
502 else:
503 structs[node_name] = fields
504
505 upto = 0
506 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300507 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600508 struct = structs[node_name]
509 for name, prop in node.props.items():
510 if name not in PROP_IGNORE_LIST and name[0] != '#':
511 prop.Widen(struct[name])
512 upto += 1
513
Simon Glass7581c012017-06-18 22:08:58 -0600514 return structs
515
Simon Glass2be282c2017-06-18 22:08:59 -0600516 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600517 """Figure out what phandles each node uses
518
519 We need to be careful when outputing nodes that use phandles since
520 they must come after the declaration of the phandles in the C file.
521 Otherwise we get a compiler error since the phandle struct is not yet
522 declared.
523
524 This function adds to each node a list of phandle nodes that the node
525 depends on. This allows us to output things in the right order.
526 """
527 for node in self._valid_nodes:
528 node.phandles = set()
529 for pname, prop in node.props.items():
530 if pname in PROP_IGNORE_LIST or pname[0] == '#':
531 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600532 info = self.get_phandle_argc(prop, node.name)
533 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600534 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600535 pos = 0
536 for args in info.args:
537 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600538 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
539 target_node = self._fdt.phandle_to_node[phandle]
540 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600541 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600542
543
Simon Glass2be282c2017-06-18 22:08:59 -0600544 def generate_structs(self, structs):
Simon Glass7581c012017-06-18 22:08:58 -0600545 """Generate struct defintions for the platform data
546
547 This writes out the body of a header file consisting of structure
548 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100549 doc/driver-model/of-plat.rst for more information.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600550
551 Args:
552 structs: dict containing structures:
553 key (str): Node name, as a C identifier
554 value: dict containing structure fields:
555 key (str): Field name
556 value: Prop object with field information
557
Simon Glass7581c012017-06-18 22:08:58 -0600558 """
Simon Glassd5031142017-08-29 14:16:01 -0600559 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600560 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900561 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600562
563 # Output the struct definition
564 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600565 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600566 for pname in sorted(structs[name]):
567 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600568 info = self.get_phandle_argc(prop, structs[name])
569 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600570 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600571 struct_name = 'struct phandle_%d_arg' % info.max_args
572 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600573 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600574 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600575 else:
576 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600577 self.out('\t%s%s' % (tab_to(2, ptype),
578 conv_name_to_c(prop.name)))
579 if isinstance(prop.value, list):
580 self.out('[%d]' % len(prop.value))
581 self.out(';\n')
582 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600583
Simon Glass2be282c2017-06-18 22:08:59 -0600584 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600585 """Output the C code for a node
586
587 Args:
588 node: node to output
589 """
Simon Glass26e408f2020-10-03 09:25:18 -0600590 def _output_list(node, prop):
591 """Output the C code for a devicetree property that holds a list
592
593 Args:
594 node (fdt.Node): Node to output
595 prop (fdt.Prop): Prop to output
596 """
597 self.buf('{')
598 vals = []
599 # For phandles, output a reference to the platform data
600 # of the target node.
601 info = self.get_phandle_argc(prop, node.name)
602 if info:
603 # Process the list as pairs of (phandle, id)
604 pos = 0
605 item = 0
606 for args in info.args:
607 phandle_cell = prop.value[pos]
608 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
609 target_node = self._fdt.phandle_to_node[phandle]
610 name = conv_name_to_c(target_node.name)
611 arg_values = []
612 for i in range(args):
Simon Glass8a38abf2020-10-03 11:31:40 -0600613 arg_values.append(
614 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
Simon Glass26e408f2020-10-03 09:25:18 -0600615 pos += 1 + args
Simon Glass8a38abf2020-10-03 11:31:40 -0600616 vals.append('\t{%d, {%s}}' % (target_node.idx,
617 ', '.join(arg_values)))
Simon Glass26e408f2020-10-03 09:25:18 -0600618 item += 1
619 for val in vals:
620 self.buf('\n\t\t%s,' % val)
621 else:
622 for val in prop.value:
623 vals.append(get_value(prop.type, val))
624
625 # Put 8 values per line to avoid very long lines.
626 for i in range(0, len(vals), 8):
627 if i:
628 self.buf(',\n\t\t')
629 self.buf(', '.join(vals[i:i + 8]))
630 self.buf('}')
631
Walter Lozanodac82282020-07-03 08:07:17 -0300632 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600633 var_name = conv_name_to_c(node.name)
Simon Glass1b272732020-10-03 11:31:25 -0600634 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
Walter Lozano51f12632020-06-25 01:10:13 -0300635 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass2be282c2017-06-18 22:08:59 -0600636 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glass1953ce72019-05-17 22:00:32 -0600637 for pname in sorted(node.props):
638 prop = node.props[pname]
Simon Glass7581c012017-06-18 22:08:58 -0600639 if pname in PROP_IGNORE_LIST or pname[0] == '#':
640 continue
Simon Glass2be282c2017-06-18 22:08:59 -0600641 member_name = conv_name_to_c(prop.name)
642 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
Simon Glass7581c012017-06-18 22:08:58 -0600643
644 # Special handling for lists
Simon Glass2be282c2017-06-18 22:08:59 -0600645 if isinstance(prop.value, list):
Simon Glass26e408f2020-10-03 09:25:18 -0600646 _output_list(node, prop)
Simon Glass7581c012017-06-18 22:08:58 -0600647 else:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600648 self.buf(get_value(prop.type, prop.value))
Simon Glass2be282c2017-06-18 22:08:59 -0600649 self.buf(',\n')
650 self.buf('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600651
652 # Add a device declaration
Simon Glass2be282c2017-06-18 22:08:59 -0600653 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
654 self.buf('\t.name\t\t= "%s",\n' % struct_name)
655 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
656 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
Simon Glasse41651f2020-10-03 11:31:35 -0600657 idx = -1
658 if node.parent and node.parent in self._valid_nodes:
659 idx = node.parent.idx
660 self.buf('\t.parent_idx\t= %d,\n' % idx)
Simon Glass2be282c2017-06-18 22:08:59 -0600661 self.buf('};\n')
662 self.buf('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600663
Simon Glass2be282c2017-06-18 22:08:59 -0600664 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600665
Simon Glass2be282c2017-06-18 22:08:59 -0600666 def generate_tables(self):
Simon Glass7581c012017-06-18 22:08:58 -0600667 """Generate device defintions for the platform data
668
669 This writes out C platform data initialisation data and
670 U_BOOT_DEVICE() declarations for each valid node. Where a node has
671 multiple compatible strings, a #define is used to make them equivalent.
672
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100673 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600674 information.
675 """
Simon Glassd5031142017-08-29 14:16:01 -0600676 self.out_header()
Simon Glasscb43ac12020-10-03 11:31:41 -0600677 self.out('/* Allow use of U_BOOT_DEVICE() in this file */\n')
678 self.out('#define DT_PLATDATA_C\n')
679 self.out('\n')
Simon Glass2be282c2017-06-18 22:08:59 -0600680 self.out('#include <common.h>\n')
681 self.out('#include <dm.h>\n')
682 self.out('#include <dt-structs.h>\n')
683 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600684 nodes_to_output = list(self._valid_nodes)
685
686 # Keep outputing nodes until there is none left
687 while nodes_to_output:
688 node = nodes_to_output[0]
689 # Output all the node's dependencies first
690 for req_node in node.phandles:
691 if req_node in nodes_to_output:
Simon Glass2be282c2017-06-18 22:08:59 -0600692 self.output_node(req_node)
Simon Glass7581c012017-06-18 22:08:58 -0600693 nodes_to_output.remove(req_node)
Simon Glass2be282c2017-06-18 22:08:59 -0600694 self.output_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600695 nodes_to_output.remove(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600696
Walter Lozano51f12632020-06-25 01:10:13 -0300697 # Define dm_populate_phandle_data() which will add the linking between
698 # nodes using DM_GET_DEVICE
699 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
700 self.buf('void dm_populate_phandle_data(void) {\n')
Walter Lozano51f12632020-06-25 01:10:13 -0300701 self.buf('}\n')
702
703 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600704
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300705def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
706 drivers_additional=[]):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600707 """Run all the steps of the dtoc tool
708
709 Args:
710 args: List of non-option arguments provided to the problem
711 dtb_file: Filename of dtb file to process
712 include_disabled: True to include disabled nodes
713 output: Name of output file
714 """
715 if not args:
716 raise ValueError('Please specify a command: struct, platdata')
717
Walter Lozano6c74d1b2020-07-28 19:06:23 -0300718 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional)
Walter Lozanodac82282020-07-03 08:07:17 -0300719 plat.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600720 plat.scan_dtb()
721 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600722 plat.scan_reg_sizes()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600723 plat.setup_output(output)
724 structs = plat.scan_structs()
725 plat.scan_phandles()
726
727 for cmd in args[0].split(','):
728 if cmd == 'struct':
729 plat.generate_structs(structs)
730 elif cmd == 'platdata':
731 plat.generate_tables()
732 else:
733 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
734 cmd)