blob: 1662d7ddb17fd88647a7be7657642fe67d6c2d9d [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:
107 return '"%s"' % value
108 elif ftype == fdt.TYPE_BOOL:
109 return 'true'
Simon Glassfbdfd222017-08-29 14:15:48 -0600110 elif ftype == fdt.TYPE_INT64:
111 return '%#x' % value
Simon Glass56e0bbe2017-06-18 22:09:02 -0600112
113def get_compat_name(node):
114 """Get a node's first compatible string as a C identifier
115
116 Args:
117 node: Node object to check
118 Return:
119 Tuple:
120 C identifier for the first compatible string
121 List of C identifiers for all the other compatible strings
122 (possibly empty)
123 """
124 compat = node.props['compatible'].value
125 aliases = []
126 if isinstance(compat, list):
127 compat, aliases = compat[0], compat[1:]
128 return conv_name_to_c(compat), [conv_name_to_c(a) for a in aliases]
129
Simon Glass56e0bbe2017-06-18 22:09:02 -0600130
Simon Glass2be282c2017-06-18 22:08:59 -0600131class DtbPlatdata(object):
Simon Glass7581c012017-06-18 22:08:58 -0600132 """Provide a means to convert device tree binary data to platform data
133
134 The output of this process is C structures which can be used in space-
135 constrained encvironments where the ~3KB code overhead of device tree
136 code is not affordable.
137
138 Properties:
Simon Glass2be282c2017-06-18 22:08:59 -0600139 _fdt: Fdt object, referencing the device tree
Simon Glass7581c012017-06-18 22:08:58 -0600140 _dtb_fname: Filename of the input device tree binary file
141 _valid_nodes: A list of Node object with compatible strings
Simon Glasse36024b2017-06-18 22:09:01 -0600142 _include_disabled: true to include nodes marked status = "disabled"
Simon Glass7581c012017-06-18 22:08:58 -0600143 _outfile: The current output file (sys.stdout or a real file)
144 _lines: Stashed list of output lines for outputting in the future
Walter Lozanoace16e82020-06-25 01:10:05 -0300145 _aliases: Dict that hold aliases for compatible strings
146 key: First compatible string declared in a node
147 value: List of additional compatible strings declared in a node
Walter Lozanodac82282020-07-03 08:07:17 -0300148 _drivers: List of valid driver names found in drivers/
149 _driver_aliases: Dict that holds aliases for driver names
150 key: Driver alias declared with
151 U_BOOT_DRIVER_ALIAS(driver_alias, driver_name)
152 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Simon Glass7581c012017-06-18 22:08:58 -0600153 """
Simon Glasse36024b2017-06-18 22:09:01 -0600154 def __init__(self, dtb_fname, include_disabled):
Simon Glass2be282c2017-06-18 22:08:59 -0600155 self._fdt = None
Simon Glass7581c012017-06-18 22:08:58 -0600156 self._dtb_fname = dtb_fname
157 self._valid_nodes = None
Simon Glasse36024b2017-06-18 22:09:01 -0600158 self._include_disabled = include_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600159 self._outfile = None
160 self._lines = []
161 self._aliases = {}
Walter Lozanodac82282020-07-03 08:07:17 -0300162 self._drivers = []
163 self._driver_aliases = {}
164
165 def get_normalized_compat_name(self, node):
166 """Get a node's normalized compat name
167
168 Returns a valid driver name by retrieving node's first compatible
169 string as a C identifier and performing a check against _drivers
170 and a lookup in driver_aliases printing a warning in case of failure.
171
172 Args:
173 node: Node object to check
174 Return:
175 Tuple:
176 Driver name associated with the first compatible string
177 List of C identifiers for all the other compatible strings
178 (possibly empty)
179 In case of no match found, the return will be the same as
180 get_compat_name()
181 """
182 compat_c, aliases_c = get_compat_name(node)
183 if compat_c not in self._drivers:
184 compat_c_old = compat_c
185 compat_c = self._driver_aliases.get(compat_c)
186 if not compat_c:
187 print('WARNING: the driver %s was not found in the driver list'
188 % (compat_c_old))
189 compat_c = compat_c_old
190 else:
191 aliases_c = [compat_c_old] + aliases_c
192
193 return compat_c, aliases_c
Simon Glass7581c012017-06-18 22:08:58 -0600194
Simon Glass2be282c2017-06-18 22:08:59 -0600195 def setup_output(self, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600196 """Set up the output destination
197
Simon Glass2be282c2017-06-18 22:08:59 -0600198 Once this is done, future calls to self.out() will output to this
Simon Glass7581c012017-06-18 22:08:58 -0600199 file.
200
201 Args:
202 fname: Filename to send output to, or '-' for stdout
203 """
204 if fname == '-':
205 self._outfile = sys.stdout
206 else:
207 self._outfile = open(fname, 'w')
208
Simon Glass2be282c2017-06-18 22:08:59 -0600209 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600210 """Output a string to the output file
211
212 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600213 line: String to output
Simon Glass7581c012017-06-18 22:08:58 -0600214 """
Simon Glass2be282c2017-06-18 22:08:59 -0600215 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600216
Simon Glass2be282c2017-06-18 22:08:59 -0600217 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600218 """Buffer up a string to send later
219
220 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600221 line: String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600222 """
Simon Glass2be282c2017-06-18 22:08:59 -0600223 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600224
Simon Glass2be282c2017-06-18 22:08:59 -0600225 def get_buf(self):
Simon Glass7581c012017-06-18 22:08:58 -0600226 """Get the contents of the output buffer, and clear it
227
228 Returns:
229 The output buffer, which is then cleared for future use
230 """
231 lines = self._lines
232 self._lines = []
233 return lines
234
Simon Glassd5031142017-08-29 14:16:01 -0600235 def out_header(self):
236 """Output a message indicating that this is an auto-generated file"""
237 self.out('''/*
238 * DO NOT MODIFY
239 *
240 * This file was generated by dtoc from a .dtb (device tree binary) file.
241 */
242
243''')
244
Simon Glass8fed2eb2017-08-29 14:15:55 -0600245 def get_phandle_argc(self, prop, node_name):
246 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600247
Simon Glass8fed2eb2017-08-29 14:15:55 -0600248 We have no reliable way of detecting whether a node uses a phandle
249 or not. As an interim measure, use a list of known property names.
Simon Glass2925c262017-08-29 14:15:54 -0600250
Simon Glass8fed2eb2017-08-29 14:15:55 -0600251 Args:
252 prop: Prop object to check
253 Return:
254 Number of argument cells is this is a phandle, else None
255 """
256 if prop.name in ['clocks']:
Simon Glass760b7172018-07-06 10:27:31 -0600257 if not isinstance(prop.value, list):
258 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600259 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600260 i = 0
261
262 max_args = 0
263 args = []
264 while i < len(val):
265 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-07-06 10:27:31 -0600266 # If we get to the end of the list, stop. This can happen
267 # since some nodes have more phandles in the list than others,
268 # but we allocate enough space for the largest list. So those
269 # nodes with shorter lists end up with zeroes at the end.
270 if not phandle:
271 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600272 target = self._fdt.phandle_to_node.get(phandle)
273 if not target:
274 raise ValueError("Cannot parse '%s' in node '%s'" %
275 (prop.name, node_name))
276 prop_name = '#clock-cells'
277 cells = target.props.get(prop_name)
278 if not cells:
279 raise ValueError("Node '%s' has no '%s' property" %
280 (target.name, prop_name))
281 num_args = fdt_util.fdt32_to_cpu(cells.value)
282 max_args = max(max_args, num_args)
283 args.append(num_args)
284 i += 1 + num_args
285 return PhandleInfo(max_args, args)
286 return None
Simon Glass2925c262017-08-29 14:15:54 -0600287
Walter Lozanodac82282020-07-03 08:07:17 -0300288 def scan_driver(self, fn):
289 """Scan a driver file to build a list of driver names and aliases
290
291 This procedure will populate self._drivers and self._driver_aliases
292
293 Args
294 fn: Driver filename to scan
295 """
296 with open(fn, encoding='utf-8') as fd:
297 try:
298 buff = fd.read()
299 except UnicodeDecodeError:
300 # This seems to happen on older Python versions
301 print("Skipping file '%s' due to unicode error" % fn)
302 return
303
304 # The following re will search for driver names declared as
305 # U_BOOT_DRIVER(driver_name)
306 drivers = re.findall('U_BOOT_DRIVER\((.*)\)', buff)
307
308 for driver in drivers:
309 self._drivers.append(driver)
310
311 # The following re will search for driver aliases declared as
312 # U_BOOT_DRIVER_ALIAS(alias, driver_name)
313 driver_aliases = re.findall('U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
314 buff)
315
316 for alias in driver_aliases: # pragma: no cover
317 if len(alias) != 2:
318 continue
319 self._driver_aliases[alias[1]] = alias[0]
320
321 def scan_drivers(self):
322 """Scan the driver folders to build a list of driver names and aliases
323
324 This procedure will populate self._drivers and self._driver_aliases
325
326 """
327 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
328 if basedir == '':
329 basedir = './'
330 for (dirpath, dirnames, filenames) in os.walk(basedir):
331 for fn in filenames:
332 if not fn.endswith('.c'):
333 continue
334 self.scan_driver(dirpath + '/' + fn)
335
Simon Glass2be282c2017-06-18 22:08:59 -0600336 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200337 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600338
Simon Glass2be282c2017-06-18 22:08:59 -0600339 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600340 device tree root node, and progress from there.
341 """
Simon Glass2be282c2017-06-18 22:08:59 -0600342 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600343
Simon Glass2be282c2017-06-18 22:08:59 -0600344 def scan_node(self, root):
345 """Scan a node and subnodes to build a tree of node and phandle info
346
Simon Glass72ab7c52017-08-29 14:15:53 -0600347 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600348
349 Args:
350 root: Root node for scan
351 """
Simon Glass7581c012017-06-18 22:08:58 -0600352 for node in root.subnodes:
353 if 'compatible' in node.props:
354 status = node.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600355 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600356 status.value != 'disabled'):
Simon Glass7581c012017-06-18 22:08:58 -0600357 self._valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600358
359 # recurse to handle any subnodes
Simon Glass2be282c2017-06-18 22:08:59 -0600360 self.scan_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600361
Simon Glass2be282c2017-06-18 22:08:59 -0600362 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600363 """Scan the device tree for useful information
364
365 This fills in the following properties:
Simon Glass7581c012017-06-18 22:08:58 -0600366 _valid_nodes: A list of nodes we wish to consider include in the
367 platform data
368 """
Simon Glass7581c012017-06-18 22:08:58 -0600369 self._valid_nodes = []
Simon Glass2be282c2017-06-18 22:08:59 -0600370 return self.scan_node(self._fdt.GetRoot())
Simon Glass7581c012017-06-18 22:08:58 -0600371
Simon Glassc20ee0e2017-08-29 14:15:50 -0600372 @staticmethod
373 def get_num_cells(node):
374 """Get the number of cells in addresses and sizes for this node
375
376 Args:
377 node: Node to check
378
379 Returns:
380 Tuple:
381 Number of address cells for this node
382 Number of size cells for this node
383 """
384 parent = node.parent
385 na, ns = 2, 2
386 if parent:
387 na_prop = parent.props.get('#address-cells')
388 ns_prop = parent.props.get('#size-cells')
389 if na_prop:
390 na = fdt_util.fdt32_to_cpu(na_prop.value)
391 if ns_prop:
392 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
393 return na, ns
394
395 def scan_reg_sizes(self):
396 """Scan for 64-bit 'reg' properties and update the values
397
398 This finds 'reg' properties with 64-bit data and converts the value to
399 an array of 64-values. This allows it to be output in a way that the
400 C code can read.
401 """
402 for node in self._valid_nodes:
403 reg = node.props.get('reg')
404 if not reg:
405 continue
406 na, ns = self.get_num_cells(node)
407 total = na + ns
408
409 if reg.type != fdt.TYPE_INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600410 raise ValueError("Node '%s' reg property is not an int" %
411 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600412 if len(reg.value) % total:
413 raise ValueError("Node '%s' reg property has %d cells "
414 'which is not a multiple of na + ns = %d + %d)' %
415 (node.name, len(reg.value), na, ns))
416 reg.na = na
417 reg.ns = ns
418 if na != 1 or ns != 1:
419 reg.type = fdt.TYPE_INT64
420 i = 0
421 new_value = []
422 val = reg.value
423 if not isinstance(val, list):
424 val = [val]
425 while i < len(val):
426 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
427 i += na
428 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
429 i += ns
430 new_value += [addr, size]
431 reg.value = new_value
432
Simon Glass2be282c2017-06-18 22:08:59 -0600433 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600434 """Scan the device tree building up the C structures we will use.
435
436 Build a dict keyed by C struct name containing a dict of Prop
437 object for each struct field (keyed by property name). Where the
438 same struct appears multiple times, try to use the 'widest'
439 property, i.e. the one with a type which can express all others.
440
441 Once the widest property is determined, all other properties are
442 updated to match that width.
443 """
444 structs = {}
445 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300446 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600447 fields = {}
448
449 # Get a list of all the valid properties in this node.
450 for name, prop in node.props.items():
451 if name not in PROP_IGNORE_LIST and name[0] != '#':
452 fields[name] = copy.deepcopy(prop)
453
454 # If we've seen this node_name before, update the existing struct.
455 if node_name in structs:
456 struct = structs[node_name]
457 for name, prop in fields.items():
458 oldprop = struct.get(name)
459 if oldprop:
460 oldprop.Widen(prop)
461 else:
462 struct[name] = prop
463
464 # Otherwise store this as a new struct.
465 else:
466 structs[node_name] = fields
467
468 upto = 0
469 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300470 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600471 struct = structs[node_name]
472 for name, prop in node.props.items():
473 if name not in PROP_IGNORE_LIST and name[0] != '#':
474 prop.Widen(struct[name])
475 upto += 1
476
Walter Lozanodac82282020-07-03 08:07:17 -0300477 struct_name, aliases = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600478 for alias in aliases:
479 self._aliases[alias] = struct_name
480
481 return structs
482
Simon Glass2be282c2017-06-18 22:08:59 -0600483 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600484 """Figure out what phandles each node uses
485
486 We need to be careful when outputing nodes that use phandles since
487 they must come after the declaration of the phandles in the C file.
488 Otherwise we get a compiler error since the phandle struct is not yet
489 declared.
490
491 This function adds to each node a list of phandle nodes that the node
492 depends on. This allows us to output things in the right order.
493 """
494 for node in self._valid_nodes:
495 node.phandles = set()
496 for pname, prop in node.props.items():
497 if pname in PROP_IGNORE_LIST or pname[0] == '#':
498 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600499 info = self.get_phandle_argc(prop, node.name)
500 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600501 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600502 pos = 0
503 for args in info.args:
504 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600505 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
506 target_node = self._fdt.phandle_to_node[phandle]
507 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600508 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600509
510
Simon Glass2be282c2017-06-18 22:08:59 -0600511 def generate_structs(self, structs):
Simon Glass7581c012017-06-18 22:08:58 -0600512 """Generate struct defintions for the platform data
513
514 This writes out the body of a header file consisting of structure
515 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100516 doc/driver-model/of-plat.rst for more information.
Simon Glass7581c012017-06-18 22:08:58 -0600517 """
Simon Glassd5031142017-08-29 14:16:01 -0600518 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600519 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900520 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600521
522 # Output the struct definition
523 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600524 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600525 for pname in sorted(structs[name]):
526 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600527 info = self.get_phandle_argc(prop, structs[name])
528 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600529 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600530 struct_name = 'struct phandle_%d_arg' % info.max_args
531 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600532 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600533 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600534 else:
535 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600536 self.out('\t%s%s' % (tab_to(2, ptype),
537 conv_name_to_c(prop.name)))
538 if isinstance(prop.value, list):
539 self.out('[%d]' % len(prop.value))
540 self.out(';\n')
541 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600542
Simon Glass90a81322019-05-17 22:00:31 -0600543 for alias, struct_name in self._aliases.items():
Heiko Schochere9cde872019-04-16 13:31:58 +0200544 if alias not in sorted(structs):
545 self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
546 STRUCT_PREFIX, struct_name))
Simon Glass7581c012017-06-18 22:08:58 -0600547
Simon Glass2be282c2017-06-18 22:08:59 -0600548 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600549 """Output the C code for a node
550
551 Args:
552 node: node to output
553 """
Walter Lozanodac82282020-07-03 08:07:17 -0300554 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600555 var_name = conv_name_to_c(node.name)
Simon Goldschmidt7d05d3a2019-01-07 20:29:26 +0100556 self.buf('static const struct %s%s %s%s = {\n' %
Simon Glass2be282c2017-06-18 22:08:59 -0600557 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glass1953ce72019-05-17 22:00:32 -0600558 for pname in sorted(node.props):
559 prop = node.props[pname]
Simon Glass7581c012017-06-18 22:08:58 -0600560 if pname in PROP_IGNORE_LIST or pname[0] == '#':
561 continue
Simon Glass2be282c2017-06-18 22:08:59 -0600562 member_name = conv_name_to_c(prop.name)
563 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
Simon Glass7581c012017-06-18 22:08:58 -0600564
565 # Special handling for lists
Simon Glass2be282c2017-06-18 22:08:59 -0600566 if isinstance(prop.value, list):
567 self.buf('{')
Simon Glass7581c012017-06-18 22:08:58 -0600568 vals = []
569 # For phandles, output a reference to the platform data
570 # of the target node.
Simon Glass8fed2eb2017-08-29 14:15:55 -0600571 info = self.get_phandle_argc(prop, node.name)
572 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600573 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600574 pos = 0
575 for args in info.args:
576 phandle_cell = prop.value[pos]
Simon Glass7581c012017-06-18 22:08:58 -0600577 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
Simon Glass72ab7c52017-08-29 14:15:53 -0600578 target_node = self._fdt.phandle_to_node[phandle]
Simon Glass2be282c2017-06-18 22:08:59 -0600579 name = conv_name_to_c(target_node.name)
Simon Glass634eba42017-08-29 14:15:59 -0600580 arg_values = []
581 for i in range(args):
582 arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
583 pos += 1 + args
584 vals.append('\t{&%s%s, {%s}}' % (VAL_PREFIX, name,
585 ', '.join(arg_values)))
Simon Glass35d50372017-08-29 14:15:57 -0600586 for val in vals:
587 self.buf('\n\t\t%s,' % val)
Simon Glass7581c012017-06-18 22:08:58 -0600588 else:
589 for val in prop.value:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600590 vals.append(get_value(prop.type, val))
Simon Glass21d54ac2017-08-29 14:15:49 -0600591
Simon Glass35d50372017-08-29 14:15:57 -0600592 # Put 8 values per line to avoid very long lines.
Simon Glass90a81322019-05-17 22:00:31 -0600593 for i in range(0, len(vals), 8):
Simon Glass35d50372017-08-29 14:15:57 -0600594 if i:
595 self.buf(',\n\t\t')
596 self.buf(', '.join(vals[i:i + 8]))
Simon Glass2be282c2017-06-18 22:08:59 -0600597 self.buf('}')
Simon Glass7581c012017-06-18 22:08:58 -0600598 else:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600599 self.buf(get_value(prop.type, prop.value))
Simon Glass2be282c2017-06-18 22:08:59 -0600600 self.buf(',\n')
601 self.buf('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600602
603 # Add a device declaration
Simon Glass2be282c2017-06-18 22:08:59 -0600604 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
605 self.buf('\t.name\t\t= "%s",\n' % struct_name)
606 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
607 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
608 self.buf('};\n')
609 self.buf('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600610
Simon Glass2be282c2017-06-18 22:08:59 -0600611 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600612
Simon Glass2be282c2017-06-18 22:08:59 -0600613 def generate_tables(self):
Simon Glass7581c012017-06-18 22:08:58 -0600614 """Generate device defintions for the platform data
615
616 This writes out C platform data initialisation data and
617 U_BOOT_DEVICE() declarations for each valid node. Where a node has
618 multiple compatible strings, a #define is used to make them equivalent.
619
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100620 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600621 information.
622 """
Simon Glassd5031142017-08-29 14:16:01 -0600623 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600624 self.out('#include <common.h>\n')
625 self.out('#include <dm.h>\n')
626 self.out('#include <dt-structs.h>\n')
627 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600628 nodes_to_output = list(self._valid_nodes)
629
630 # Keep outputing nodes until there is none left
631 while nodes_to_output:
632 node = nodes_to_output[0]
633 # Output all the node's dependencies first
634 for req_node in node.phandles:
635 if req_node in nodes_to_output:
Simon Glass2be282c2017-06-18 22:08:59 -0600636 self.output_node(req_node)
Simon Glass7581c012017-06-18 22:08:58 -0600637 nodes_to_output.remove(req_node)
Simon Glass2be282c2017-06-18 22:08:59 -0600638 self.output_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600639 nodes_to_output.remove(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600640
641
642def run_steps(args, dtb_file, include_disabled, output):
643 """Run all the steps of the dtoc tool
644
645 Args:
646 args: List of non-option arguments provided to the problem
647 dtb_file: Filename of dtb file to process
648 include_disabled: True to include disabled nodes
649 output: Name of output file
650 """
651 if not args:
652 raise ValueError('Please specify a command: struct, platdata')
653
654 plat = DtbPlatdata(dtb_file, include_disabled)
Walter Lozanodac82282020-07-03 08:07:17 -0300655 plat.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600656 plat.scan_dtb()
657 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600658 plat.scan_reg_sizes()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600659 plat.setup_output(output)
660 structs = plat.scan_structs()
661 plat.scan_phandles()
662
663 for cmd in args[0].split(','):
664 if cmd == 'struct':
665 plat.generate_structs(structs)
666 elif cmd == 'platdata':
667 plat.generate_tables()
668 else:
669 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
670 cmd)