blob: c148c49625ecf4ab036aa68622e21ee503d6d7a5 [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)
Walter Lozano361e7332020-06-25 01:10:08 -0300144 _warning_disabled: true to disable warnings about driver names not found
Simon Glass7581c012017-06-18 22:08:58 -0600145 _lines: Stashed list of output lines for outputting in the future
Walter Lozanoace16e82020-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 Lozanodac82282020-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 Lozano51f12632020-06-25 01:10:13 -0300154 _links: List of links to be included in dm_populate_phandle_data()
Simon Glass7581c012017-06-18 22:08:58 -0600155 """
Walter Lozano361e7332020-06-25 01:10:08 -0300156 def __init__(self, dtb_fname, include_disabled, warning_disabled):
Simon Glass2be282c2017-06-18 22:08:59 -0600157 self._fdt = None
Simon Glass7581c012017-06-18 22:08:58 -0600158 self._dtb_fname = dtb_fname
159 self._valid_nodes = None
Simon Glasse36024b2017-06-18 22:09:01 -0600160 self._include_disabled = include_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600161 self._outfile = None
Walter Lozano361e7332020-06-25 01:10:08 -0300162 self._warning_disabled = warning_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600163 self._lines = []
164 self._aliases = {}
Walter Lozanodac82282020-07-03 08:07:17 -0300165 self._drivers = []
166 self._driver_aliases = {}
Walter Lozano51f12632020-06-25 01:10:13 -0300167 self._links = []
Walter Lozanodac82282020-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 Lozano361e7332020-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 Lozanodac82282020-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 Glass7581c012017-06-18 22:08:58 -0600199
Simon Glass2be282c2017-06-18 22:08:59 -0600200 def setup_output(self, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600201 """Set up the output destination
202
Simon Glass2be282c2017-06-18 22:08:59 -0600203 Once this is done, future calls to self.out() will output to this
Simon Glass7581c012017-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 Glass2be282c2017-06-18 22:08:59 -0600214 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600215 """Output a string to the output file
216
217 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600218 line: String to output
Simon Glass7581c012017-06-18 22:08:58 -0600219 """
Simon Glass2be282c2017-06-18 22:08:59 -0600220 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600221
Simon Glass2be282c2017-06-18 22:08:59 -0600222 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600223 """Buffer up a string to send later
224
225 Args:
Simon Glass2be282c2017-06-18 22:08:59 -0600226 line: String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600227 """
Simon Glass2be282c2017-06-18 22:08:59 -0600228 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600229
Simon Glass2be282c2017-06-18 22:08:59 -0600230 def get_buf(self):
Simon Glass7581c012017-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 Glassd5031142017-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 Glass8fed2eb2017-08-29 14:15:55 -0600250 def get_phandle_argc(self, prop, node_name):
251 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600252
Simon Glass8fed2eb2017-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.
Simon Glass2925c262017-08-29 14:15:54 -0600255
Simon Glass8fed2eb2017-08-29 14:15:55 -0600256 Args:
257 prop: Prop object to check
258 Return:
259 Number of argument cells is this is a phandle, else None
260 """
Walter Lozanoad340172020-06-25 01:10:16 -0300261 if prop.name in ['clocks', 'cd-gpios']:
Simon Glass760b7172018-07-06 10:27:31 -0600262 if not isinstance(prop.value, list):
263 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600264 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600265 i = 0
266
267 max_args = 0
268 args = []
269 while i < len(val):
270 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-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 Glass8fed2eb2017-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 Lozanoad340172020-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 Glass8fed2eb2017-08-29 14:15:55 -0600286 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300287 raise ValueError("Node '%s' has no cells property" %
288 (target.name))
Simon Glass8fed2eb2017-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 Glass2925c262017-08-29 14:15:54 -0600295
Walter Lozanodac82282020-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 Glass2be282c2017-06-18 22:08:59 -0600344 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200345 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600346
Simon Glass2be282c2017-06-18 22:08:59 -0600347 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600348 device tree root node, and progress from there.
349 """
Simon Glass2be282c2017-06-18 22:08:59 -0600350 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600351
Simon Glass2be282c2017-06-18 22:08:59 -0600352 def scan_node(self, root):
353 """Scan a node and subnodes to build a tree of node and phandle info
354
Simon Glass72ab7c52017-08-29 14:15:53 -0600355 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600356
357 Args:
358 root: Root node for scan
359 """
Simon Glass7581c012017-06-18 22:08:58 -0600360 for node in root.subnodes:
361 if 'compatible' in node.props:
362 status = node.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600363 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600364 status.value != 'disabled'):
Simon Glass7581c012017-06-18 22:08:58 -0600365 self._valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600366
367 # recurse to handle any subnodes
Simon Glass2be282c2017-06-18 22:08:59 -0600368 self.scan_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600369
Simon Glass2be282c2017-06-18 22:08:59 -0600370 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600371 """Scan the device tree for useful information
372
373 This fills in the following properties:
Simon Glass7581c012017-06-18 22:08:58 -0600374 _valid_nodes: A list of nodes we wish to consider include in the
375 platform data
376 """
Simon Glass7581c012017-06-18 22:08:58 -0600377 self._valid_nodes = []
Simon Glass2be282c2017-06-18 22:08:59 -0600378 return self.scan_node(self._fdt.GetRoot())
Simon Glass7581c012017-06-18 22:08:58 -0600379
Simon Glassc20ee0e2017-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 Glassdfe5f5b2018-07-06 10:27:32 -0600418 raise ValueError("Node '%s' reg property is not an int" %
419 node.name)
Simon Glassc20ee0e2017-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 Glass2be282c2017-06-18 22:08:59 -0600441 def scan_structs(self):
Simon Glass7581c012017-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 Lozanodac82282020-07-03 08:07:17 -0300454 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-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 Lozanodac82282020-07-03 08:07:17 -0300478 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-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 Lozanodac82282020-07-03 08:07:17 -0300485 struct_name, aliases = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600486 for alias in aliases:
487 self._aliases[alias] = struct_name
488
489 return structs
490
Simon Glass2be282c2017-06-18 22:08:59 -0600491 def scan_phandles(self):
Simon Glass7581c012017-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 Glass8fed2eb2017-08-29 14:15:55 -0600507 info = self.get_phandle_argc(prop, node.name)
508 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600509 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600510 pos = 0
511 for args in info.args:
512 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-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 Glass634eba42017-08-29 14:15:59 -0600516 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600517
518
Simon Glass2be282c2017-06-18 22:08:59 -0600519 def generate_structs(self, structs):
Simon Glass7581c012017-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 Schuchardt2799a692020-02-25 21:35:39 +0100524 doc/driver-model/of-plat.rst for more information.
Simon Glass7581c012017-06-18 22:08:58 -0600525 """
Simon Glassd5031142017-08-29 14:16:01 -0600526 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600527 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900528 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600529
530 # Output the struct definition
531 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600532 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600533 for pname in sorted(structs[name]):
534 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600535 info = self.get_phandle_argc(prop, structs[name])
536 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600537 # For phandles, include a reference to the target
Simon Glass0d154632017-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 Glass2be282c2017-06-18 22:08:59 -0600540 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600541 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600542 else:
543 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-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 Glass7581c012017-06-18 22:08:58 -0600550
Simon Glass90a81322019-05-17 22:00:31 -0600551 for alias, struct_name in self._aliases.items():
Heiko Schochere9cde872019-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 Glass7581c012017-06-18 22:08:58 -0600555
Simon Glass2be282c2017-06-18 22:08:59 -0600556 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600557 """Output the C code for a node
558
559 Args:
560 node: node to output
561 """
Walter Lozanodac82282020-07-03 08:07:17 -0300562 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600563 var_name = conv_name_to_c(node.name)
Walter Lozano51f12632020-06-25 01:10:13 -0300564 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass2be282c2017-06-18 22:08:59 -0600565 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glass1953ce72019-05-17 22:00:32 -0600566 for pname in sorted(node.props):
567 prop = node.props[pname]
Simon Glass7581c012017-06-18 22:08:58 -0600568 if pname in PROP_IGNORE_LIST or pname[0] == '#':
569 continue
Simon Glass2be282c2017-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 Glass7581c012017-06-18 22:08:58 -0600572
573 # Special handling for lists
Simon Glass2be282c2017-06-18 22:08:59 -0600574 if isinstance(prop.value, list):
575 self.buf('{')
Simon Glass7581c012017-06-18 22:08:58 -0600576 vals = []
577 # For phandles, output a reference to the platform data
578 # of the target node.
Simon Glass8fed2eb2017-08-29 14:15:55 -0600579 info = self.get_phandle_argc(prop, node.name)
580 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600581 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600582 pos = 0
Walter Lozano51f12632020-06-25 01:10:13 -0300583 item = 0
Simon Glass634eba42017-08-29 14:15:59 -0600584 for args in info.args:
585 phandle_cell = prop.value[pos]
Simon Glass7581c012017-06-18 22:08:58 -0600586 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
Simon Glass72ab7c52017-08-29 14:15:53 -0600587 target_node = self._fdt.phandle_to_node[phandle]
Simon Glass2be282c2017-06-18 22:08:59 -0600588 name = conv_name_to_c(target_node.name)
Simon Glass634eba42017-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 Lozano51f12632020-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 Glass35d50372017-08-29 14:15:57 -0600603 for val in vals:
604 self.buf('\n\t\t%s,' % val)
Simon Glass7581c012017-06-18 22:08:58 -0600605 else:
606 for val in prop.value:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600607 vals.append(get_value(prop.type, val))
Simon Glass21d54ac2017-08-29 14:15:49 -0600608
Simon Glass35d50372017-08-29 14:15:57 -0600609 # Put 8 values per line to avoid very long lines.
Simon Glass90a81322019-05-17 22:00:31 -0600610 for i in range(0, len(vals), 8):
Simon Glass35d50372017-08-29 14:15:57 -0600611 if i:
612 self.buf(',\n\t\t')
613 self.buf(', '.join(vals[i:i + 8]))
Simon Glass2be282c2017-06-18 22:08:59 -0600614 self.buf('}')
Simon Glass7581c012017-06-18 22:08:58 -0600615 else:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600616 self.buf(get_value(prop.type, prop.value))
Simon Glass2be282c2017-06-18 22:08:59 -0600617 self.buf(',\n')
618 self.buf('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600619
620 # Add a device declaration
Simon Glass2be282c2017-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 Glass7581c012017-06-18 22:08:58 -0600627
Simon Glass2be282c2017-06-18 22:08:59 -0600628 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600629
Simon Glass2be282c2017-06-18 22:08:59 -0600630 def generate_tables(self):
Simon Glass7581c012017-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 Schuchardt2799a692020-02-25 21:35:39 +0100637 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600638 information.
639 """
Simon Glassd5031142017-08-29 14:16:01 -0600640 self.out_header()
Simon Glass2be282c2017-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 Glass7581c012017-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 Glass2be282c2017-06-18 22:08:59 -0600653 self.output_node(req_node)
Simon Glass7581c012017-06-18 22:08:58 -0600654 nodes_to_output.remove(req_node)
Simon Glass2be282c2017-06-18 22:08:59 -0600655 self.output_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600656 nodes_to_output.remove(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600657
Walter Lozano51f12632020-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 Lozanoad340172020-06-25 01:10:16 -0300663 self.buf('\t%s = DM_GET_DEVICE(%s);\n' %
664 (l['var_node'], l['dev_name']))
Walter Lozano51f12632020-06-25 01:10:13 -0300665 self.buf('}\n')
666
667 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600668
Walter Lozano361e7332020-06-25 01:10:08 -0300669def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False):
Simon Glassfa0ea5b2017-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 Lozano361e7332020-06-25 01:10:08 -0300681 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled)
Walter Lozanodac82282020-07-03 08:07:17 -0300682 plat.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600683 plat.scan_dtb()
684 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600685 plat.scan_reg_sizes()
Simon Glassfa0ea5b2017-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)