blob: c148c49625ecf4ab036aa68622e21ee503d6d7a5 [file] [log] [blame]
Simon Glassd570dec2017-06-18 22:08:58 -06001#!/usr/bin/python
Tom Rini10e47792018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Simon Glassd570dec2017-06-18 22:08:58 -06003#
4# Copyright (C) 2017 Google, Inc
5# Written by Simon Glass <sjg@chromium.org>
6#
Simon Glassd570dec2017-06-18 22:08:58 -06007
Simon Glass1f730c82017-06-18 22:08:59 -06008"""Device tree to platform data class
9
10This supports converting device tree data to C structures definitions and
11static data.
12"""
13
Simon Glassec3b5e42017-08-29 14:15:55 -060014import collections
Simon Glassd570dec2017-06-18 22:08:58 -060015import copy
Walter Lozanoe675d962020-07-03 08:07:17 -030016import os
17import re
Simon Glass1f730c82017-06-18 22:08:59 -060018import sys
Simon Glassd570dec2017-06-18 22:08:58 -060019
Simon Glassa997ea52020-04-17 18:09:04 -060020from dtoc import fdt
21from dtoc import fdt_util
22from patman import tools
Simon Glassd570dec2017-06-18 22:08:58 -060023
24# When we see these properties we ignore them - i.e. do not create a structure member
25PROP_IGNORE_LIST = [
26 '#address-cells',
27 '#gpio-cells',
28 '#size-cells',
29 'compatible',
30 'linux,phandle',
31 "status",
32 'phandle',
33 'u-boot,dm-pre-reloc',
34 'u-boot,dm-tpl',
35 'u-boot,dm-spl',
36]
37
38# C type declarations for the tyues we support
39TYPE_NAMES = {
40 fdt.TYPE_INT: 'fdt32_t',
41 fdt.TYPE_BYTE: 'unsigned char',
42 fdt.TYPE_STRING: 'const char *',
43 fdt.TYPE_BOOL: 'bool',
Simon Glassfc3ae9c2017-08-29 14:15:48 -060044 fdt.TYPE_INT64: 'fdt64_t',
Simon Glass1f730c82017-06-18 22:08:59 -060045}
Simon Glassd570dec2017-06-18 22:08:58 -060046
47STRUCT_PREFIX = 'dtd_'
48VAL_PREFIX = 'dtv_'
49
Simon Glassec3b5e42017-08-29 14:15:55 -060050# This holds information about a property which includes phandles.
51#
52# max_args: integer: Maximum number or arguments that any phandle uses (int).
53# args: Number of args for each phandle in the property. The total number of
54# phandles is len(args). This is a list of integers.
55PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
56
57
Simon Glass1f730c82017-06-18 22:08:59 -060058def conv_name_to_c(name):
Simon Glassd570dec2017-06-18 22:08:58 -060059 """Convert a device-tree name to a C identifier
60
Simon Glass2e0bf3f2017-06-18 22:09:04 -060061 This uses multiple replace() calls instead of re.sub() since it is faster
62 (400ms for 1m calls versus 1000ms for the 're' version).
63
Simon Glassd570dec2017-06-18 22:08:58 -060064 Args:
65 name: Name to convert
66 Return:
67 String containing the C version of this name
68 """
Simon Glass1f730c82017-06-18 22:08:59 -060069 new = name.replace('@', '_at_')
70 new = new.replace('-', '_')
71 new = new.replace(',', '_')
72 new = new.replace('.', '_')
Simon Glass1f730c82017-06-18 22:08:59 -060073 return new
Simon Glassd570dec2017-06-18 22:08:58 -060074
Simon Glass1f730c82017-06-18 22:08:59 -060075def tab_to(num_tabs, line):
76 """Append tabs to a line of text to reach a tab stop.
Simon Glassd570dec2017-06-18 22:08:58 -060077
Simon Glass1f730c82017-06-18 22:08:59 -060078 Args:
79 num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
80 line: Line of text to append to
81
82 Returns:
83 line with the correct number of tabs appeneded. If the line already
84 extends past that tab stop then a single space is appended.
85 """
86 if len(line) >= num_tabs * 8:
87 return line + ' '
88 return line + '\t' * (num_tabs - len(line) // 8)
89
Simon Glass7a2add12017-06-18 22:09:02 -060090def get_value(ftype, value):
91 """Get a value as a C expression
92
93 For integers this returns a byte-swapped (little-endian) hex string
94 For bytes this returns a hex string, e.g. 0x12
95 For strings this returns a literal string enclosed in quotes
96 For booleans this return 'true'
97
98 Args:
99 type: Data type (fdt_util)
100 value: Data value, as a string of bytes
101 """
102 if ftype == fdt.TYPE_INT:
103 return '%#x' % fdt_util.fdt32_to_cpu(value)
104 elif ftype == fdt.TYPE_BYTE:
Simon Glass534eda92019-05-17 22:00:43 -0600105 return '%#x' % tools.ToByte(value[0])
Simon Glass7a2add12017-06-18 22:09:02 -0600106 elif ftype == fdt.TYPE_STRING:
107 return '"%s"' % value
108 elif ftype == fdt.TYPE_BOOL:
109 return 'true'
Simon Glassfc3ae9c2017-08-29 14:15:48 -0600110 elif ftype == fdt.TYPE_INT64:
111 return '%#x' % value
Simon Glass7a2add12017-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 Glass7a2add12017-06-18 22:09:02 -0600130
Simon Glass1f730c82017-06-18 22:08:59 -0600131class DtbPlatdata(object):
Simon Glassd570dec2017-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 Glass1f730c82017-06-18 22:08:59 -0600139 _fdt: Fdt object, referencing the device tree
Simon Glassd570dec2017-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 Glasseab3f622017-06-18 22:09:01 -0600142 _include_disabled: true to include nodes marked status = "disabled"
Simon Glassd570dec2017-06-18 22:08:58 -0600143 _outfile: The current output file (sys.stdout or a real file)
Walter Lozanoa324e412020-06-25 01:10:08 -0300144 _warning_disabled: true to disable warnings about driver names not found
Simon Glassd570dec2017-06-18 22:08:58 -0600145 _lines: Stashed list of output lines for outputting in the future
Walter Lozano8b239f42020-06-25 01:10:05 -0300146 _aliases: Dict that hold aliases for compatible strings
147 key: First compatible string declared in a node
148 value: List of additional compatible strings declared in a node
Walter Lozanoe675d962020-07-03 08:07:17 -0300149 _drivers: List of valid driver names found in drivers/
150 _driver_aliases: Dict that holds aliases for driver names
151 key: Driver alias declared with
152 U_BOOT_DRIVER_ALIAS(driver_alias, driver_name)
153 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Walter Lozanodc5b4372020-06-25 01:10:13 -0300154 _links: List of links to be included in dm_populate_phandle_data()
Simon Glassd570dec2017-06-18 22:08:58 -0600155 """
Walter Lozanoa324e412020-06-25 01:10:08 -0300156 def __init__(self, dtb_fname, include_disabled, warning_disabled):
Simon Glass1f730c82017-06-18 22:08:59 -0600157 self._fdt = None
Simon Glassd570dec2017-06-18 22:08:58 -0600158 self._dtb_fname = dtb_fname
159 self._valid_nodes = None
Simon Glasseab3f622017-06-18 22:09:01 -0600160 self._include_disabled = include_disabled
Simon Glassd570dec2017-06-18 22:08:58 -0600161 self._outfile = None
Walter Lozanoa324e412020-06-25 01:10:08 -0300162 self._warning_disabled = warning_disabled
Simon Glassd570dec2017-06-18 22:08:58 -0600163 self._lines = []
164 self._aliases = {}
Walter Lozanoe675d962020-07-03 08:07:17 -0300165 self._drivers = []
166 self._driver_aliases = {}
Walter Lozanodc5b4372020-06-25 01:10:13 -0300167 self._links = []
Walter Lozanoe675d962020-07-03 08:07:17 -0300168
169 def get_normalized_compat_name(self, node):
170 """Get a node's normalized compat name
171
172 Returns a valid driver name by retrieving node's first compatible
173 string as a C identifier and performing a check against _drivers
174 and a lookup in driver_aliases printing a warning in case of failure.
175
176 Args:
177 node: Node object to check
178 Return:
179 Tuple:
180 Driver name associated with the first compatible string
181 List of C identifiers for all the other compatible strings
182 (possibly empty)
183 In case of no match found, the return will be the same as
184 get_compat_name()
185 """
186 compat_c, aliases_c = get_compat_name(node)
187 if compat_c not in self._drivers:
188 compat_c_old = compat_c
189 compat_c = self._driver_aliases.get(compat_c)
190 if not compat_c:
Walter Lozanoa324e412020-06-25 01:10:08 -0300191 if not self._warning_disabled:
192 print('WARNING: the driver %s was not found in the driver list'
193 % (compat_c_old))
Walter Lozanoe675d962020-07-03 08:07:17 -0300194 compat_c = compat_c_old
195 else:
196 aliases_c = [compat_c_old] + aliases_c
197
198 return compat_c, aliases_c
Simon Glassd570dec2017-06-18 22:08:58 -0600199
Simon Glass1f730c82017-06-18 22:08:59 -0600200 def setup_output(self, fname):
Simon Glassd570dec2017-06-18 22:08:58 -0600201 """Set up the output destination
202
Simon Glass1f730c82017-06-18 22:08:59 -0600203 Once this is done, future calls to self.out() will output to this
Simon Glassd570dec2017-06-18 22:08:58 -0600204 file.
205
206 Args:
207 fname: Filename to send output to, or '-' for stdout
208 """
209 if fname == '-':
210 self._outfile = sys.stdout
211 else:
212 self._outfile = open(fname, 'w')
213
Simon Glass1f730c82017-06-18 22:08:59 -0600214 def out(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600215 """Output a string to the output file
216
217 Args:
Simon Glass1f730c82017-06-18 22:08:59 -0600218 line: String to output
Simon Glassd570dec2017-06-18 22:08:58 -0600219 """
Simon Glass1f730c82017-06-18 22:08:59 -0600220 self._outfile.write(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600221
Simon Glass1f730c82017-06-18 22:08:59 -0600222 def buf(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600223 """Buffer up a string to send later
224
225 Args:
Simon Glass1f730c82017-06-18 22:08:59 -0600226 line: String to add to our 'buffer' list
Simon Glassd570dec2017-06-18 22:08:58 -0600227 """
Simon Glass1f730c82017-06-18 22:08:59 -0600228 self._lines.append(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600229
Simon Glass1f730c82017-06-18 22:08:59 -0600230 def get_buf(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600231 """Get the contents of the output buffer, and clear it
232
233 Returns:
234 The output buffer, which is then cleared for future use
235 """
236 lines = self._lines
237 self._lines = []
238 return lines
239
Simon Glass68d32c72017-08-29 14:16:01 -0600240 def out_header(self):
241 """Output a message indicating that this is an auto-generated file"""
242 self.out('''/*
243 * DO NOT MODIFY
244 *
245 * This file was generated by dtoc from a .dtb (device tree binary) file.
246 */
247
248''')
249
Simon Glassec3b5e42017-08-29 14:15:55 -0600250 def get_phandle_argc(self, prop, node_name):
251 """Check if a node contains phandles
Simon Glassce5621d2017-08-29 14:15:54 -0600252
Simon Glassec3b5e42017-08-29 14:15:55 -0600253 We have no reliable way of detecting whether a node uses a phandle
254 or not. As an interim measure, use a list of known property names.
255
256 Args:
257 prop: Prop object to check
258 Return:
259 Number of argument cells is this is a phandle, else None
260 """
Walter Lozano179f0b62020-06-25 01:10:16 -0300261 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass609e2b12018-07-06 10:27:31 -0600262 if not isinstance(prop.value, list):
263 prop.value = [prop.value]
Simon Glassec3b5e42017-08-29 14:15:55 -0600264 val = prop.value
Simon Glassec3b5e42017-08-29 14:15:55 -0600265 i = 0
Simon Glassce5621d2017-08-29 14:15:54 -0600266
Simon Glassec3b5e42017-08-29 14:15:55 -0600267 max_args = 0
268 args = []
269 while i < len(val):
270 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass609e2b12018-07-06 10:27:31 -0600271 # If we get to the end of the list, stop. This can happen
272 # since some nodes have more phandles in the list than others,
273 # but we allocate enough space for the largest list. So those
274 # nodes with shorter lists end up with zeroes at the end.
275 if not phandle:
276 break
Simon Glassec3b5e42017-08-29 14:15:55 -0600277 target = self._fdt.phandle_to_node.get(phandle)
278 if not target:
279 raise ValueError("Cannot parse '%s' in node '%s'" %
280 (prop.name, node_name))
Walter Lozano179f0b62020-06-25 01:10:16 -0300281 cells = None
282 for prop_name in ['#clock-cells', '#gpio-cells']:
283 cells = target.props.get(prop_name)
284 if cells:
285 break
Simon Glassec3b5e42017-08-29 14:15:55 -0600286 if not cells:
Walter Lozano179f0b62020-06-25 01:10:16 -0300287 raise ValueError("Node '%s' has no cells property" %
288 (target.name))
Simon Glassec3b5e42017-08-29 14:15:55 -0600289 num_args = fdt_util.fdt32_to_cpu(cells.value)
290 max_args = max(max_args, num_args)
291 args.append(num_args)
292 i += 1 + num_args
293 return PhandleInfo(max_args, args)
294 return None
Simon Glassce5621d2017-08-29 14:15:54 -0600295
Walter Lozanoe675d962020-07-03 08:07:17 -0300296 def scan_driver(self, fn):
297 """Scan a driver file to build a list of driver names and aliases
298
299 This procedure will populate self._drivers and self._driver_aliases
300
301 Args
302 fn: Driver filename to scan
303 """
304 with open(fn, encoding='utf-8') as fd:
305 try:
306 buff = fd.read()
307 except UnicodeDecodeError:
308 # This seems to happen on older Python versions
309 print("Skipping file '%s' due to unicode error" % fn)
310 return
311
312 # The following re will search for driver names declared as
313 # U_BOOT_DRIVER(driver_name)
314 drivers = re.findall('U_BOOT_DRIVER\((.*)\)', buff)
315
316 for driver in drivers:
317 self._drivers.append(driver)
318
319 # The following re will search for driver aliases declared as
320 # U_BOOT_DRIVER_ALIAS(alias, driver_name)
321 driver_aliases = re.findall('U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
322 buff)
323
324 for alias in driver_aliases: # pragma: no cover
325 if len(alias) != 2:
326 continue
327 self._driver_aliases[alias[1]] = alias[0]
328
329 def scan_drivers(self):
330 """Scan the driver folders to build a list of driver names and aliases
331
332 This procedure will populate self._drivers and self._driver_aliases
333
334 """
335 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
336 if basedir == '':
337 basedir = './'
338 for (dirpath, dirnames, filenames) in os.walk(basedir):
339 for fn in filenames:
340 if not fn.endswith('.c'):
341 continue
342 self.scan_driver(dirpath + '/' + fn)
343
Simon Glass1f730c82017-06-18 22:08:59 -0600344 def scan_dtb(self):
Anatolij Gustschinda707d42017-08-18 17:58:51 +0200345 """Scan the device tree to obtain a tree of nodes and properties
Simon Glassd570dec2017-06-18 22:08:58 -0600346
Simon Glass1f730c82017-06-18 22:08:59 -0600347 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glassd570dec2017-06-18 22:08:58 -0600348 device tree root node, and progress from there.
349 """
Simon Glass1f730c82017-06-18 22:08:59 -0600350 self._fdt = fdt.FdtScan(self._dtb_fname)
351
352 def scan_node(self, root):
353 """Scan a node and subnodes to build a tree of node and phandle info
Simon Glassd570dec2017-06-18 22:08:58 -0600354
Simon Glass17ffd622017-08-29 14:15:53 -0600355 This adds each node to self._valid_nodes.
Simon Glass1f730c82017-06-18 22:08:59 -0600356
357 Args:
358 root: Root node for scan
359 """
Simon Glassd570dec2017-06-18 22:08:58 -0600360 for node in root.subnodes:
361 if 'compatible' in node.props:
362 status = node.props.get('status')
Simon Glasseab3f622017-06-18 22:09:01 -0600363 if (not self._include_disabled and not status or
Simon Glass1f730c82017-06-18 22:08:59 -0600364 status.value != 'disabled'):
Simon Glassd570dec2017-06-18 22:08:58 -0600365 self._valid_nodes.append(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600366
367 # recurse to handle any subnodes
Simon Glass1f730c82017-06-18 22:08:59 -0600368 self.scan_node(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600369
Simon Glass1f730c82017-06-18 22:08:59 -0600370 def scan_tree(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600371 """Scan the device tree for useful information
372
373 This fills in the following properties:
Simon Glassd570dec2017-06-18 22:08:58 -0600374 _valid_nodes: A list of nodes we wish to consider include in the
375 platform data
376 """
Simon Glassd570dec2017-06-18 22:08:58 -0600377 self._valid_nodes = []
Simon Glass1f730c82017-06-18 22:08:59 -0600378 return self.scan_node(self._fdt.GetRoot())
Simon Glassd570dec2017-06-18 22:08:58 -0600379
Simon Glass1b1fe412017-08-29 14:15:50 -0600380 @staticmethod
381 def get_num_cells(node):
382 """Get the number of cells in addresses and sizes for this node
383
384 Args:
385 node: Node to check
386
387 Returns:
388 Tuple:
389 Number of address cells for this node
390 Number of size cells for this node
391 """
392 parent = node.parent
393 na, ns = 2, 2
394 if parent:
395 na_prop = parent.props.get('#address-cells')
396 ns_prop = parent.props.get('#size-cells')
397 if na_prop:
398 na = fdt_util.fdt32_to_cpu(na_prop.value)
399 if ns_prop:
400 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
401 return na, ns
402
403 def scan_reg_sizes(self):
404 """Scan for 64-bit 'reg' properties and update the values
405
406 This finds 'reg' properties with 64-bit data and converts the value to
407 an array of 64-values. This allows it to be output in a way that the
408 C code can read.
409 """
410 for node in self._valid_nodes:
411 reg = node.props.get('reg')
412 if not reg:
413 continue
414 na, ns = self.get_num_cells(node)
415 total = na + ns
416
417 if reg.type != fdt.TYPE_INT:
Simon Glassc38fee042018-07-06 10:27:32 -0600418 raise ValueError("Node '%s' reg property is not an int" %
419 node.name)
Simon Glass1b1fe412017-08-29 14:15:50 -0600420 if len(reg.value) % total:
421 raise ValueError("Node '%s' reg property has %d cells "
422 'which is not a multiple of na + ns = %d + %d)' %
423 (node.name, len(reg.value), na, ns))
424 reg.na = na
425 reg.ns = ns
426 if na != 1 or ns != 1:
427 reg.type = fdt.TYPE_INT64
428 i = 0
429 new_value = []
430 val = reg.value
431 if not isinstance(val, list):
432 val = [val]
433 while i < len(val):
434 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
435 i += na
436 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
437 i += ns
438 new_value += [addr, size]
439 reg.value = new_value
440
Simon Glass1f730c82017-06-18 22:08:59 -0600441 def scan_structs(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600442 """Scan the device tree building up the C structures we will use.
443
444 Build a dict keyed by C struct name containing a dict of Prop
445 object for each struct field (keyed by property name). Where the
446 same struct appears multiple times, try to use the 'widest'
447 property, i.e. the one with a type which can express all others.
448
449 Once the widest property is determined, all other properties are
450 updated to match that width.
451 """
452 structs = {}
453 for node in self._valid_nodes:
Walter Lozanoe675d962020-07-03 08:07:17 -0300454 node_name, _ = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600455 fields = {}
456
457 # Get a list of all the valid properties in this node.
458 for name, prop in node.props.items():
459 if name not in PROP_IGNORE_LIST and name[0] != '#':
460 fields[name] = copy.deepcopy(prop)
461
462 # If we've seen this node_name before, update the existing struct.
463 if node_name in structs:
464 struct = structs[node_name]
465 for name, prop in fields.items():
466 oldprop = struct.get(name)
467 if oldprop:
468 oldprop.Widen(prop)
469 else:
470 struct[name] = prop
471
472 # Otherwise store this as a new struct.
473 else:
474 structs[node_name] = fields
475
476 upto = 0
477 for node in self._valid_nodes:
Walter Lozanoe675d962020-07-03 08:07:17 -0300478 node_name, _ = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600479 struct = structs[node_name]
480 for name, prop in node.props.items():
481 if name not in PROP_IGNORE_LIST and name[0] != '#':
482 prop.Widen(struct[name])
483 upto += 1
484
Walter Lozanoe675d962020-07-03 08:07:17 -0300485 struct_name, aliases = self.get_normalized_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600486 for alias in aliases:
487 self._aliases[alias] = struct_name
488
489 return structs
490
Simon Glass1f730c82017-06-18 22:08:59 -0600491 def scan_phandles(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600492 """Figure out what phandles each node uses
493
494 We need to be careful when outputing nodes that use phandles since
495 they must come after the declaration of the phandles in the C file.
496 Otherwise we get a compiler error since the phandle struct is not yet
497 declared.
498
499 This function adds to each node a list of phandle nodes that the node
500 depends on. This allows us to output things in the right order.
501 """
502 for node in self._valid_nodes:
503 node.phandles = set()
504 for pname, prop in node.props.items():
505 if pname in PROP_IGNORE_LIST or pname[0] == '#':
506 continue
Simon Glassec3b5e42017-08-29 14:15:55 -0600507 info = self.get_phandle_argc(prop, node.name)
508 if info:
Simon Glassec3b5e42017-08-29 14:15:55 -0600509 # Process the list as pairs of (phandle, id)
Simon Glass3deeb472017-08-29 14:15:59 -0600510 pos = 0
511 for args in info.args:
512 phandle_cell = prop.value[pos]
Simon Glassec3b5e42017-08-29 14:15:55 -0600513 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
514 target_node = self._fdt.phandle_to_node[phandle]
515 node.phandles.add(target_node)
Simon Glass3deeb472017-08-29 14:15:59 -0600516 pos += 1 + args
Simon Glassd570dec2017-06-18 22:08:58 -0600517
518
Simon Glass1f730c82017-06-18 22:08:59 -0600519 def generate_structs(self, structs):
Simon Glassd570dec2017-06-18 22:08:58 -0600520 """Generate struct defintions for the platform data
521
522 This writes out the body of a header file consisting of structure
523 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +0100524 doc/driver-model/of-plat.rst for more information.
Simon Glassd570dec2017-06-18 22:08:58 -0600525 """
Simon Glass68d32c72017-08-29 14:16:01 -0600526 self.out_header()
Simon Glass1f730c82017-06-18 22:08:59 -0600527 self.out('#include <stdbool.h>\n')
Masahiro Yamada75f82d02018-03-05 01:20:11 +0900528 self.out('#include <linux/libfdt.h>\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600529
530 # Output the struct definition
531 for name in sorted(structs):
Simon Glass1f730c82017-06-18 22:08:59 -0600532 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glassd570dec2017-06-18 22:08:58 -0600533 for pname in sorted(structs[name]):
534 prop = structs[name][pname]
Simon Glassec3b5e42017-08-29 14:15:55 -0600535 info = self.get_phandle_argc(prop, structs[name])
536 if info:
Simon Glassd570dec2017-06-18 22:08:58 -0600537 # For phandles, include a reference to the target
Simon Glasse94414b2017-08-29 14:15:56 -0600538 struct_name = 'struct phandle_%d_arg' % info.max_args
539 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass1f730c82017-06-18 22:08:59 -0600540 conv_name_to_c(prop.name),
Simon Glass3deeb472017-08-29 14:15:59 -0600541 len(info.args)))
Simon Glassd570dec2017-06-18 22:08:58 -0600542 else:
543 ptype = TYPE_NAMES[prop.type]
Simon Glass1f730c82017-06-18 22:08:59 -0600544 self.out('\t%s%s' % (tab_to(2, ptype),
545 conv_name_to_c(prop.name)))
546 if isinstance(prop.value, list):
547 self.out('[%d]' % len(prop.value))
548 self.out(';\n')
549 self.out('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600550
Simon Glass61b88e52019-05-17 22:00:31 -0600551 for alias, struct_name in self._aliases.items():
Heiko Schocher701c4012019-04-16 13:31:58 +0200552 if alias not in sorted(structs):
553 self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
554 STRUCT_PREFIX, struct_name))
Simon Glassd570dec2017-06-18 22:08:58 -0600555
Simon Glass1f730c82017-06-18 22:08:59 -0600556 def output_node(self, node):
Simon Glassd570dec2017-06-18 22:08:58 -0600557 """Output the C code for a node
558
559 Args:
560 node: node to output
561 """
Walter Lozanoe675d962020-07-03 08:07:17 -0300562 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass1f730c82017-06-18 22:08:59 -0600563 var_name = conv_name_to_c(node.name)
Walter Lozanodc5b4372020-06-25 01:10:13 -0300564 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass1f730c82017-06-18 22:08:59 -0600565 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glassc82de562019-05-17 22:00:32 -0600566 for pname in sorted(node.props):
567 prop = node.props[pname]
Simon Glassd570dec2017-06-18 22:08:58 -0600568 if pname in PROP_IGNORE_LIST or pname[0] == '#':
569 continue
Simon Glass1f730c82017-06-18 22:08:59 -0600570 member_name = conv_name_to_c(prop.name)
571 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
Simon Glassd570dec2017-06-18 22:08:58 -0600572
573 # Special handling for lists
Simon Glass1f730c82017-06-18 22:08:59 -0600574 if isinstance(prop.value, list):
575 self.buf('{')
Simon Glassd570dec2017-06-18 22:08:58 -0600576 vals = []
577 # For phandles, output a reference to the platform data
578 # of the target node.
Simon Glassec3b5e42017-08-29 14:15:55 -0600579 info = self.get_phandle_argc(prop, node.name)
580 if info:
Simon Glassd570dec2017-06-18 22:08:58 -0600581 # Process the list as pairs of (phandle, id)
Simon Glass3deeb472017-08-29 14:15:59 -0600582 pos = 0
Walter Lozanodc5b4372020-06-25 01:10:13 -0300583 item = 0
Simon Glass3deeb472017-08-29 14:15:59 -0600584 for args in info.args:
585 phandle_cell = prop.value[pos]
Simon Glassd570dec2017-06-18 22:08:58 -0600586 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
Simon Glass17ffd622017-08-29 14:15:53 -0600587 target_node = self._fdt.phandle_to_node[phandle]
Simon Glass1f730c82017-06-18 22:08:59 -0600588 name = conv_name_to_c(target_node.name)
Simon Glass3deeb472017-08-29 14:15:59 -0600589 arg_values = []
590 for i in range(args):
591 arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
592 pos += 1 + args
Walter Lozanodc5b4372020-06-25 01:10:13 -0300593 # node member is filled with NULL as the real value
594 # will be update at run-time during dm_init_and_scan()
595 # by dm_populate_phandle_data()
596 vals.append('\t{NULL, {%s}}' % (', '.join(arg_values)))
597 var_node = '%s%s.%s[%d].node' % \
598 (VAL_PREFIX, var_name, member_name, item)
599 # Save the the link information to be use to define
600 # dm_populate_phandle_data()
601 self._links.append({'var_node': var_node, 'dev_name': name})
602 item += 1
Simon Glassd0cd0752017-08-29 14:15:57 -0600603 for val in vals:
604 self.buf('\n\t\t%s,' % val)
Simon Glassd570dec2017-06-18 22:08:58 -0600605 else:
606 for val in prop.value:
Simon Glass7a2add12017-06-18 22:09:02 -0600607 vals.append(get_value(prop.type, val))
Simon Glass131e0b02017-08-29 14:15:49 -0600608
Simon Glassd0cd0752017-08-29 14:15:57 -0600609 # Put 8 values per line to avoid very long lines.
Simon Glass61b88e52019-05-17 22:00:31 -0600610 for i in range(0, len(vals), 8):
Simon Glassd0cd0752017-08-29 14:15:57 -0600611 if i:
612 self.buf(',\n\t\t')
613 self.buf(', '.join(vals[i:i + 8]))
Simon Glass1f730c82017-06-18 22:08:59 -0600614 self.buf('}')
Simon Glassd570dec2017-06-18 22:08:58 -0600615 else:
Simon Glass7a2add12017-06-18 22:09:02 -0600616 self.buf(get_value(prop.type, prop.value))
Simon Glass1f730c82017-06-18 22:08:59 -0600617 self.buf(',\n')
618 self.buf('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600619
620 # Add a device declaration
Simon Glass1f730c82017-06-18 22:08:59 -0600621 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
622 self.buf('\t.name\t\t= "%s",\n' % struct_name)
623 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
624 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
625 self.buf('};\n')
626 self.buf('\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600627
Simon Glass1f730c82017-06-18 22:08:59 -0600628 self.out(''.join(self.get_buf()))
Simon Glassd570dec2017-06-18 22:08:58 -0600629
Simon Glass1f730c82017-06-18 22:08:59 -0600630 def generate_tables(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600631 """Generate device defintions for the platform data
632
633 This writes out C platform data initialisation data and
634 U_BOOT_DEVICE() declarations for each valid node. Where a node has
635 multiple compatible strings, a #define is used to make them equivalent.
636
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +0100637 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glassd570dec2017-06-18 22:08:58 -0600638 information.
639 """
Simon Glass68d32c72017-08-29 14:16:01 -0600640 self.out_header()
Simon Glass1f730c82017-06-18 22:08:59 -0600641 self.out('#include <common.h>\n')
642 self.out('#include <dm.h>\n')
643 self.out('#include <dt-structs.h>\n')
644 self.out('\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600645 nodes_to_output = list(self._valid_nodes)
646
647 # Keep outputing nodes until there is none left
648 while nodes_to_output:
649 node = nodes_to_output[0]
650 # Output all the node's dependencies first
651 for req_node in node.phandles:
652 if req_node in nodes_to_output:
Simon Glass1f730c82017-06-18 22:08:59 -0600653 self.output_node(req_node)
Simon Glassd570dec2017-06-18 22:08:58 -0600654 nodes_to_output.remove(req_node)
Simon Glass1f730c82017-06-18 22:08:59 -0600655 self.output_node(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600656 nodes_to_output.remove(node)
Simon Glass3fa797a2017-06-18 22:09:03 -0600657
Walter Lozanodc5b4372020-06-25 01:10:13 -0300658 # Define dm_populate_phandle_data() which will add the linking between
659 # nodes using DM_GET_DEVICE
660 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
661 self.buf('void dm_populate_phandle_data(void) {\n')
662 for l in self._links:
Walter Lozano179f0b62020-06-25 01:10:16 -0300663 self.buf('\t%s = DM_GET_DEVICE(%s);\n' %
664 (l['var_node'], l['dev_name']))
Walter Lozanodc5b4372020-06-25 01:10:13 -0300665 self.buf('}\n')
666
667 self.out(''.join(self.get_buf()))
Simon Glass3fa797a2017-06-18 22:09:03 -0600668
Walter Lozanoa324e412020-06-25 01:10:08 -0300669def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False):
Simon Glass3fa797a2017-06-18 22:09:03 -0600670 """Run all the steps of the dtoc tool
671
672 Args:
673 args: List of non-option arguments provided to the problem
674 dtb_file: Filename of dtb file to process
675 include_disabled: True to include disabled nodes
676 output: Name of output file
677 """
678 if not args:
679 raise ValueError('Please specify a command: struct, platdata')
680
Walter Lozanoa324e412020-06-25 01:10:08 -0300681 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled)
Walter Lozanoe675d962020-07-03 08:07:17 -0300682 plat.scan_drivers()
Simon Glass3fa797a2017-06-18 22:09:03 -0600683 plat.scan_dtb()
684 plat.scan_tree()
Simon Glass1b1fe412017-08-29 14:15:50 -0600685 plat.scan_reg_sizes()
Simon Glass3fa797a2017-06-18 22:09:03 -0600686 plat.setup_output(output)
687 structs = plat.scan_structs()
688 plat.scan_phandles()
689
690 for cmd in args[0].split(','):
691 if cmd == 'struct':
692 plat.generate_structs(structs)
693 elif cmd == 'platdata':
694 plat.generate_tables()
695 else:
696 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
697 cmd)