blob: c4d3265f055e4b5241287e42c5671439168c22b5 [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 """
261 if prop.name in ['clocks']:
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))
281 prop_name = '#clock-cells'
282 cells = target.props.get(prop_name)
283 if not cells:
284 raise ValueError("Node '%s' has no '%s' property" %
285 (target.name, prop_name))
286 num_args = fdt_util.fdt32_to_cpu(cells.value)
287 max_args = max(max_args, num_args)
288 args.append(num_args)
289 i += 1 + num_args
290 return PhandleInfo(max_args, args)
291 return None
Simon Glass2925c262017-08-29 14:15:54 -0600292
Walter Lozanodac82282020-07-03 08:07:17 -0300293 def scan_driver(self, fn):
294 """Scan a driver file to build a list of driver names and aliases
295
296 This procedure will populate self._drivers and self._driver_aliases
297
298 Args
299 fn: Driver filename to scan
300 """
301 with open(fn, encoding='utf-8') as fd:
302 try:
303 buff = fd.read()
304 except UnicodeDecodeError:
305 # This seems to happen on older Python versions
306 print("Skipping file '%s' due to unicode error" % fn)
307 return
308
309 # The following re will search for driver names declared as
310 # U_BOOT_DRIVER(driver_name)
311 drivers = re.findall('U_BOOT_DRIVER\((.*)\)', buff)
312
313 for driver in drivers:
314 self._drivers.append(driver)
315
316 # The following re will search for driver aliases declared as
317 # U_BOOT_DRIVER_ALIAS(alias, driver_name)
318 driver_aliases = re.findall('U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
319 buff)
320
321 for alias in driver_aliases: # pragma: no cover
322 if len(alias) != 2:
323 continue
324 self._driver_aliases[alias[1]] = alias[0]
325
326 def scan_drivers(self):
327 """Scan the driver folders to build a list of driver names and aliases
328
329 This procedure will populate self._drivers and self._driver_aliases
330
331 """
332 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
333 if basedir == '':
334 basedir = './'
335 for (dirpath, dirnames, filenames) in os.walk(basedir):
336 for fn in filenames:
337 if not fn.endswith('.c'):
338 continue
339 self.scan_driver(dirpath + '/' + fn)
340
Simon Glass2be282c2017-06-18 22:08:59 -0600341 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200342 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600343
Simon Glass2be282c2017-06-18 22:08:59 -0600344 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600345 device tree root node, and progress from there.
346 """
Simon Glass2be282c2017-06-18 22:08:59 -0600347 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600348
Simon Glass2be282c2017-06-18 22:08:59 -0600349 def scan_node(self, root):
350 """Scan a node and subnodes to build a tree of node and phandle info
351
Simon Glass72ab7c52017-08-29 14:15:53 -0600352 This adds each node to self._valid_nodes.
Simon Glass2be282c2017-06-18 22:08:59 -0600353
354 Args:
355 root: Root node for scan
356 """
Simon Glass7581c012017-06-18 22:08:58 -0600357 for node in root.subnodes:
358 if 'compatible' in node.props:
359 status = node.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600360 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600361 status.value != 'disabled'):
Simon Glass7581c012017-06-18 22:08:58 -0600362 self._valid_nodes.append(node)
Simon Glass7581c012017-06-18 22:08:58 -0600363
364 # recurse to handle any subnodes
Simon Glass2be282c2017-06-18 22:08:59 -0600365 self.scan_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600366
Simon Glass2be282c2017-06-18 22:08:59 -0600367 def scan_tree(self):
Simon Glass7581c012017-06-18 22:08:58 -0600368 """Scan the device tree for useful information
369
370 This fills in the following properties:
Simon Glass7581c012017-06-18 22:08:58 -0600371 _valid_nodes: A list of nodes we wish to consider include in the
372 platform data
373 """
Simon Glass7581c012017-06-18 22:08:58 -0600374 self._valid_nodes = []
Simon Glass2be282c2017-06-18 22:08:59 -0600375 return self.scan_node(self._fdt.GetRoot())
Simon Glass7581c012017-06-18 22:08:58 -0600376
Simon Glassc20ee0e2017-08-29 14:15:50 -0600377 @staticmethod
378 def get_num_cells(node):
379 """Get the number of cells in addresses and sizes for this node
380
381 Args:
382 node: Node to check
383
384 Returns:
385 Tuple:
386 Number of address cells for this node
387 Number of size cells for this node
388 """
389 parent = node.parent
390 na, ns = 2, 2
391 if parent:
392 na_prop = parent.props.get('#address-cells')
393 ns_prop = parent.props.get('#size-cells')
394 if na_prop:
395 na = fdt_util.fdt32_to_cpu(na_prop.value)
396 if ns_prop:
397 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
398 return na, ns
399
400 def scan_reg_sizes(self):
401 """Scan for 64-bit 'reg' properties and update the values
402
403 This finds 'reg' properties with 64-bit data and converts the value to
404 an array of 64-values. This allows it to be output in a way that the
405 C code can read.
406 """
407 for node in self._valid_nodes:
408 reg = node.props.get('reg')
409 if not reg:
410 continue
411 na, ns = self.get_num_cells(node)
412 total = na + ns
413
414 if reg.type != fdt.TYPE_INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600415 raise ValueError("Node '%s' reg property is not an int" %
416 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600417 if len(reg.value) % total:
418 raise ValueError("Node '%s' reg property has %d cells "
419 'which is not a multiple of na + ns = %d + %d)' %
420 (node.name, len(reg.value), na, ns))
421 reg.na = na
422 reg.ns = ns
423 if na != 1 or ns != 1:
424 reg.type = fdt.TYPE_INT64
425 i = 0
426 new_value = []
427 val = reg.value
428 if not isinstance(val, list):
429 val = [val]
430 while i < len(val):
431 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
432 i += na
433 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
434 i += ns
435 new_value += [addr, size]
436 reg.value = new_value
437
Simon Glass2be282c2017-06-18 22:08:59 -0600438 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600439 """Scan the device tree building up the C structures we will use.
440
441 Build a dict keyed by C struct name containing a dict of Prop
442 object for each struct field (keyed by property name). Where the
443 same struct appears multiple times, try to use the 'widest'
444 property, i.e. the one with a type which can express all others.
445
446 Once the widest property is determined, all other properties are
447 updated to match that width.
448 """
449 structs = {}
450 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300451 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600452 fields = {}
453
454 # Get a list of all the valid properties in this node.
455 for name, prop in node.props.items():
456 if name not in PROP_IGNORE_LIST and name[0] != '#':
457 fields[name] = copy.deepcopy(prop)
458
459 # If we've seen this node_name before, update the existing struct.
460 if node_name in structs:
461 struct = structs[node_name]
462 for name, prop in fields.items():
463 oldprop = struct.get(name)
464 if oldprop:
465 oldprop.Widen(prop)
466 else:
467 struct[name] = prop
468
469 # Otherwise store this as a new struct.
470 else:
471 structs[node_name] = fields
472
473 upto = 0
474 for node in self._valid_nodes:
Walter Lozanodac82282020-07-03 08:07:17 -0300475 node_name, _ = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600476 struct = structs[node_name]
477 for name, prop in node.props.items():
478 if name not in PROP_IGNORE_LIST and name[0] != '#':
479 prop.Widen(struct[name])
480 upto += 1
481
Walter Lozanodac82282020-07-03 08:07:17 -0300482 struct_name, aliases = self.get_normalized_compat_name(node)
Simon Glass7581c012017-06-18 22:08:58 -0600483 for alias in aliases:
484 self._aliases[alias] = struct_name
485
486 return structs
487
Simon Glass2be282c2017-06-18 22:08:59 -0600488 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600489 """Figure out what phandles each node uses
490
491 We need to be careful when outputing nodes that use phandles since
492 they must come after the declaration of the phandles in the C file.
493 Otherwise we get a compiler error since the phandle struct is not yet
494 declared.
495
496 This function adds to each node a list of phandle nodes that the node
497 depends on. This allows us to output things in the right order.
498 """
499 for node in self._valid_nodes:
500 node.phandles = set()
501 for pname, prop in node.props.items():
502 if pname in PROP_IGNORE_LIST or pname[0] == '#':
503 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600504 info = self.get_phandle_argc(prop, node.name)
505 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600506 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600507 pos = 0
508 for args in info.args:
509 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600510 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
511 target_node = self._fdt.phandle_to_node[phandle]
512 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600513 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600514
515
Simon Glass2be282c2017-06-18 22:08:59 -0600516 def generate_structs(self, structs):
Simon Glass7581c012017-06-18 22:08:58 -0600517 """Generate struct defintions for the platform data
518
519 This writes out the body of a header file consisting of structure
520 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100521 doc/driver-model/of-plat.rst for more information.
Simon Glass7581c012017-06-18 22:08:58 -0600522 """
Simon Glassd5031142017-08-29 14:16:01 -0600523 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600524 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900525 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600526
527 # Output the struct definition
528 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600529 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600530 for pname in sorted(structs[name]):
531 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600532 info = self.get_phandle_argc(prop, structs[name])
533 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600534 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600535 struct_name = 'struct phandle_%d_arg' % info.max_args
536 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600537 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600538 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600539 else:
540 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600541 self.out('\t%s%s' % (tab_to(2, ptype),
542 conv_name_to_c(prop.name)))
543 if isinstance(prop.value, list):
544 self.out('[%d]' % len(prop.value))
545 self.out(';\n')
546 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600547
Simon Glass90a81322019-05-17 22:00:31 -0600548 for alias, struct_name in self._aliases.items():
Heiko Schochere9cde872019-04-16 13:31:58 +0200549 if alias not in sorted(structs):
550 self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
551 STRUCT_PREFIX, struct_name))
Simon Glass7581c012017-06-18 22:08:58 -0600552
Simon Glass2be282c2017-06-18 22:08:59 -0600553 def output_node(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600554 """Output the C code for a node
555
556 Args:
557 node: node to output
558 """
Walter Lozanodac82282020-07-03 08:07:17 -0300559 struct_name, _ = self.get_normalized_compat_name(node)
Simon Glass2be282c2017-06-18 22:08:59 -0600560 var_name = conv_name_to_c(node.name)
Walter Lozano51f12632020-06-25 01:10:13 -0300561 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass2be282c2017-06-18 22:08:59 -0600562 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Simon Glass1953ce72019-05-17 22:00:32 -0600563 for pname in sorted(node.props):
564 prop = node.props[pname]
Simon Glass7581c012017-06-18 22:08:58 -0600565 if pname in PROP_IGNORE_LIST or pname[0] == '#':
566 continue
Simon Glass2be282c2017-06-18 22:08:59 -0600567 member_name = conv_name_to_c(prop.name)
568 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
Simon Glass7581c012017-06-18 22:08:58 -0600569
570 # Special handling for lists
Simon Glass2be282c2017-06-18 22:08:59 -0600571 if isinstance(prop.value, list):
572 self.buf('{')
Simon Glass7581c012017-06-18 22:08:58 -0600573 vals = []
574 # For phandles, output a reference to the platform data
575 # of the target node.
Simon Glass8fed2eb2017-08-29 14:15:55 -0600576 info = self.get_phandle_argc(prop, node.name)
577 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600578 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600579 pos = 0
Walter Lozano51f12632020-06-25 01:10:13 -0300580 item = 0
Simon Glass634eba42017-08-29 14:15:59 -0600581 for args in info.args:
582 phandle_cell = prop.value[pos]
Simon Glass7581c012017-06-18 22:08:58 -0600583 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
Simon Glass72ab7c52017-08-29 14:15:53 -0600584 target_node = self._fdt.phandle_to_node[phandle]
Simon Glass2be282c2017-06-18 22:08:59 -0600585 name = conv_name_to_c(target_node.name)
Simon Glass634eba42017-08-29 14:15:59 -0600586 arg_values = []
587 for i in range(args):
588 arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
589 pos += 1 + args
Walter Lozano51f12632020-06-25 01:10:13 -0300590 # node member is filled with NULL as the real value
591 # will be update at run-time during dm_init_and_scan()
592 # by dm_populate_phandle_data()
593 vals.append('\t{NULL, {%s}}' % (', '.join(arg_values)))
594 var_node = '%s%s.%s[%d].node' % \
595 (VAL_PREFIX, var_name, member_name, item)
596 # Save the the link information to be use to define
597 # dm_populate_phandle_data()
598 self._links.append({'var_node': var_node, 'dev_name': name})
599 item += 1
Simon Glass35d50372017-08-29 14:15:57 -0600600 for val in vals:
601 self.buf('\n\t\t%s,' % val)
Simon Glass7581c012017-06-18 22:08:58 -0600602 else:
603 for val in prop.value:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600604 vals.append(get_value(prop.type, val))
Simon Glass21d54ac2017-08-29 14:15:49 -0600605
Simon Glass35d50372017-08-29 14:15:57 -0600606 # Put 8 values per line to avoid very long lines.
Simon Glass90a81322019-05-17 22:00:31 -0600607 for i in range(0, len(vals), 8):
Simon Glass35d50372017-08-29 14:15:57 -0600608 if i:
609 self.buf(',\n\t\t')
610 self.buf(', '.join(vals[i:i + 8]))
Simon Glass2be282c2017-06-18 22:08:59 -0600611 self.buf('}')
Simon Glass7581c012017-06-18 22:08:58 -0600612 else:
Simon Glass56e0bbe2017-06-18 22:09:02 -0600613 self.buf(get_value(prop.type, prop.value))
Simon Glass2be282c2017-06-18 22:08:59 -0600614 self.buf(',\n')
615 self.buf('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600616
617 # Add a device declaration
Simon Glass2be282c2017-06-18 22:08:59 -0600618 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
619 self.buf('\t.name\t\t= "%s",\n' % struct_name)
620 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
621 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
622 self.buf('};\n')
623 self.buf('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600624
Simon Glass2be282c2017-06-18 22:08:59 -0600625 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -0600626
Simon Glass2be282c2017-06-18 22:08:59 -0600627 def generate_tables(self):
Simon Glass7581c012017-06-18 22:08:58 -0600628 """Generate device defintions for the platform data
629
630 This writes out C platform data initialisation data and
631 U_BOOT_DEVICE() declarations for each valid node. Where a node has
632 multiple compatible strings, a #define is used to make them equivalent.
633
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100634 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -0600635 information.
636 """
Simon Glassd5031142017-08-29 14:16:01 -0600637 self.out_header()
Simon Glass2be282c2017-06-18 22:08:59 -0600638 self.out('#include <common.h>\n')
639 self.out('#include <dm.h>\n')
640 self.out('#include <dt-structs.h>\n')
641 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -0600642 nodes_to_output = list(self._valid_nodes)
643
644 # Keep outputing nodes until there is none left
645 while nodes_to_output:
646 node = nodes_to_output[0]
647 # Output all the node's dependencies first
648 for req_node in node.phandles:
649 if req_node in nodes_to_output:
Simon Glass2be282c2017-06-18 22:08:59 -0600650 self.output_node(req_node)
Simon Glass7581c012017-06-18 22:08:58 -0600651 nodes_to_output.remove(req_node)
Simon Glass2be282c2017-06-18 22:08:59 -0600652 self.output_node(node)
Simon Glass7581c012017-06-18 22:08:58 -0600653 nodes_to_output.remove(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600654
Walter Lozano51f12632020-06-25 01:10:13 -0300655 # Define dm_populate_phandle_data() which will add the linking between
656 # nodes using DM_GET_DEVICE
657 # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
658 self.buf('void dm_populate_phandle_data(void) {\n')
659 for l in self._links:
660 self.buf('\t%s = DM_GET_DEVICE(%s);\n' % (l['var_node'], l['dev_name']))
661 self.buf('}\n')
662
663 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600664
Walter Lozano361e7332020-06-25 01:10:08 -0300665def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False):
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600666 """Run all the steps of the dtoc tool
667
668 Args:
669 args: List of non-option arguments provided to the problem
670 dtb_file: Filename of dtb file to process
671 include_disabled: True to include disabled nodes
672 output: Name of output file
673 """
674 if not args:
675 raise ValueError('Please specify a command: struct, platdata')
676
Walter Lozano361e7332020-06-25 01:10:08 -0300677 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled)
Walter Lozanodac82282020-07-03 08:07:17 -0300678 plat.scan_drivers()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600679 plat.scan_dtb()
680 plat.scan_tree()
Simon Glassc20ee0e2017-08-29 14:15:50 -0600681 plat.scan_reg_sizes()
Simon Glassfa0ea5b2017-06-18 22:09:03 -0600682 plat.setup_output(output)
683 structs = plat.scan_structs()
684 plat.scan_phandles()
685
686 for cmd in args[0].split(','):
687 if cmd == 'struct':
688 plat.generate_structs(structs)
689 elif cmd == 'platdata':
690 plat.generate_tables()
691 else:
692 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
693 cmd)