]> Git Repo - J-u-boot.git/blame - tools/dtoc/dtb_platdata.py
dtoc: Allow specifying the base directory for tests
[J-u-boot.git] / tools / dtoc / dtb_platdata.py
CommitLineData
7581c01a 1#!/usr/bin/python
83d290c5 2# SPDX-License-Identifier: GPL-2.0+
7581c01a
SG
3#
4# Copyright (C) 2017 Google, Inc
5# Written by Simon Glass <[email protected]>
6#
7581c01a 7
2be282ca
SG
8"""Device tree to platform data class
9
10This supports converting device tree data to C structures definitions and
11static data.
9b330382
SG
12
13See doc/driver-model/of-plat.rst for more informaiton
2be282ca
SG
14"""
15
8fed2eb2 16import collections
7581c01a 17import copy
be44f271 18from enum import IntEnum
dac8228d
WL
19import os
20import re
2be282ca 21import sys
7581c01a 22
bf776679
SG
23from dtoc import fdt
24from dtoc import fdt_util
7581c01a 25
9b330382
SG
26# When we see these properties we ignore them - i.e. do not create a structure
27# member
7581c01a
SG
28PROP_IGNORE_LIST = [
29 '#address-cells',
30 '#gpio-cells',
31 '#size-cells',
32 'compatible',
33 'linux,phandle',
34 "status",
35 'phandle',
36 'u-boot,dm-pre-reloc',
37 'u-boot,dm-tpl',
38 'u-boot,dm-spl',
39]
40
5ea9dccf 41# C type declarations for the types we support
7581c01a 42TYPE_NAMES = {
5ea9dccf
SG
43 fdt.Type.INT: 'fdt32_t',
44 fdt.Type.BYTE: 'unsigned char',
45 fdt.Type.STRING: 'const char *',
46 fdt.Type.BOOL: 'bool',
47 fdt.Type.INT64: 'fdt64_t',
2be282ca 48}
7581c01a
SG
49
50STRUCT_PREFIX = 'dtd_'
51VAL_PREFIX = 'dtv_'
52
be44f271
SG
53class Ftype(IntEnum):
54 SOURCE, HEADER = range(2)
55
56
57# This holds information about each type of output file dtoc can create
58# type: Type of file (Ftype)
d1055d68
SG
59# fname: Filename excluding directory, e.g. 'dt-plat.c'
60# hdr_comment: Comment explaining the purpose of the file
61OutputFile = collections.namedtuple('OutputFile',
a7d5f96e 62 ['ftype', 'fname', 'method', 'hdr_comment'])
be44f271 63
8fed2eb2
SG
64# This holds information about a property which includes phandles.
65#
66# max_args: integer: Maximum number or arguments that any phandle uses (int).
67# args: Number of args for each phandle in the property. The total number of
68# phandles is len(args). This is a list of integers.
69PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
70
97136eb5
SG
71# Holds a single phandle link, allowing a C struct value to be assigned to point
72# to a device
73#
74# var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node')
75# dev_name: Name of device to assign to (e.g. 'clock')
76PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name'])
77
8fed2eb2 78
7d637c12
SG
79class Driver:
80 """Information about a driver in U-Boot
81
82 Attributes:
83 name: Name of driver. For U_BOOT_DRIVER(x) this is 'x'
84 """
85 def __init__(self, name):
86 self.name = name
87
88 def __eq__(self, other):
89 return self.name == other.name
90
91 def __repr__(self):
92 return "Driver(name='%s')" % self.name
93
94
2be282ca 95def conv_name_to_c(name):
7581c01a
SG
96 """Convert a device-tree name to a C identifier
97
30107b08
SG
98 This uses multiple replace() calls instead of re.sub() since it is faster
99 (400ms for 1m calls versus 1000ms for the 're' version).
100
7581c01a 101 Args:
9b330382 102 name (str): Name to convert
7581c01a 103 Return:
9b330382 104 str: String containing the C version of this name
7581c01a 105 """
2be282ca
SG
106 new = name.replace('@', '_at_')
107 new = new.replace('-', '_')
108 new = new.replace(',', '_')
109 new = new.replace('.', '_')
2be282ca
SG
110 return new
111
112def tab_to(num_tabs, line):
113 """Append tabs to a line of text to reach a tab stop.
114
115 Args:
9b330382
SG
116 num_tabs (int): Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
117 line (str): Line of text to append to
2be282ca
SG
118
119 Returns:
9b330382 120 str: line with the correct number of tabs appeneded. If the line already
2be282ca
SG
121 extends past that tab stop then a single space is appended.
122 """
123 if len(line) >= num_tabs * 8:
124 return line + ' '
125 return line + '\t' * (num_tabs - len(line) // 8)
126
56e0bbe0
SG
127def get_value(ftype, value):
128 """Get a value as a C expression
129
130 For integers this returns a byte-swapped (little-endian) hex string
131 For bytes this returns a hex string, e.g. 0x12
132 For strings this returns a literal string enclosed in quotes
133 For booleans this return 'true'
134
135 Args:
9b330382
SG
136 ftype (fdt.Type): Data type (fdt_util)
137 value (bytes): Data value, as a string of bytes
138
139 Returns:
140 str: String representation of the value
56e0bbe0 141 """
5ea9dccf 142 if ftype == fdt.Type.INT:
ccc3da77 143 val = '%#x' % fdt_util.fdt32_to_cpu(value)
5ea9dccf 144 elif ftype == fdt.Type.BYTE:
78128d52 145 char = value[0]
ccc3da77 146 val = '%#x' % (ord(char) if isinstance(char, str) else char)
5ea9dccf 147 elif ftype == fdt.Type.STRING:
f02d0eb3
SG
148 # Handle evil ACPI backslashes by adding another backslash before them.
149 # So "\\_SB.GPO0" in the device tree effectively stays like that in C
ccc3da77 150 val = '"%s"' % value.replace('\\', '\\\\')
5ea9dccf 151 elif ftype == fdt.Type.BOOL:
ccc3da77 152 val = 'true'
9b330382 153 else: # ftype == fdt.Type.INT64:
ccc3da77
SG
154 val = '%#x' % value
155 return val
56e0bbe0
SG
156
157def get_compat_name(node):
dcb3ed64 158 """Get the node's list of compatible string as a C identifiers
56e0bbe0
SG
159
160 Args:
9b330382 161 node (fdt.Node): Node object to check
56e0bbe0 162 Return:
ccc3da77 163 list of str: List of C identifiers for all the compatible strings
56e0bbe0
SG
164 """
165 compat = node.props['compatible'].value
dcb3ed64
WL
166 if not isinstance(compat, list):
167 compat = [compat]
168 return [conv_name_to_c(c) for c in compat]
56e0bbe0 169
56e0bbe0 170
ccc3da77 171class DtbPlatdata():
7581c01a
SG
172 """Provide a means to convert device tree binary data to platform data
173
174 The output of this process is C structures which can be used in space-
175 constrained encvironments where the ~3KB code overhead of device tree
176 code is not affordable.
177
178 Properties:
2be282ca 179 _fdt: Fdt object, referencing the device tree
7581c01a 180 _dtb_fname: Filename of the input device tree binary file
1b27273e
SG
181 _valid_nodes: A list of Node object with compatible strings. The list
182 is ordered by conv_name_to_c(node.name)
e36024b0 183 _include_disabled: true to include nodes marked status = "disabled"
7581c01a 184 _outfile: The current output file (sys.stdout or a real file)
361e7335 185 _warning_disabled: true to disable warnings about driver names not found
7581c01a 186 _lines: Stashed list of output lines for outputting in the future
7d637c12
SG
187 _drivers: Dict of valid driver names found in drivers/
188 key: Driver name
189 value: Driver for that driver
dac8228d
WL
190 _driver_aliases: Dict that holds aliases for driver names
191 key: Driver alias declared with
bdf8fd76 192 DM_DRIVER_ALIAS(driver_alias, driver_name)
dac8228d 193 value: Driver name declared with U_BOOT_DRIVER(driver_name)
6c74d1b8 194 _drivers_additional: List of additional drivers to use during scanning
be44f271
SG
195 _dirname: Directory to hold output files, or None for none (all files
196 go to stdout)
a7d5f96e
SG
197 _struct_data (dict): OrderedDict of dtplat structures to output
198 key (str): Node name, as a C identifier
199 value: dict containing structure fields:
200 key (str): Field name
201 value: Prop object with field information
1e0f3f46 202 _basedir (str): Base directory of source tree
7581c01a 203 """
6c74d1b8 204 def __init__(self, dtb_fname, include_disabled, warning_disabled,
78128d52 205 drivers_additional=None):
2be282ca 206 self._fdt = None
7581c01a
SG
207 self._dtb_fname = dtb_fname
208 self._valid_nodes = None
e36024b0 209 self._include_disabled = include_disabled
7581c01a 210 self._outfile = None
361e7335 211 self._warning_disabled = warning_disabled
7581c01a 212 self._lines = []
7d637c12 213 self._drivers = {}
dac8228d 214 self._driver_aliases = {}
78128d52 215 self._drivers_additional = drivers_additional or []
be44f271 216 self._dirnames = [None] * len(Ftype)
a7d5f96e 217 self._struct_data = collections.OrderedDict()
1e0f3f46 218 self._basedir = None
dac8228d
WL
219
220 def get_normalized_compat_name(self, node):
221 """Get a node's normalized compat name
222
dcb3ed64 223 Returns a valid driver name by retrieving node's list of compatible
dac8228d
WL
224 string as a C identifier and performing a check against _drivers
225 and a lookup in driver_aliases printing a warning in case of failure.
226
227 Args:
ccc3da77 228 node (Node): Node object to check
dac8228d
WL
229 Return:
230 Tuple:
231 Driver name associated with the first compatible string
232 List of C identifiers for all the other compatible strings
233 (possibly empty)
234 In case of no match found, the return will be the same as
235 get_compat_name()
236 """
dcb3ed64
WL
237 compat_list_c = get_compat_name(node)
238
239 for compat_c in compat_list_c:
7d637c12 240 if not compat_c in self._drivers.keys():
dcb3ed64
WL
241 compat_c = self._driver_aliases.get(compat_c)
242 if not compat_c:
243 continue
244
245 aliases_c = compat_list_c
246 if compat_c in aliases_c:
247 aliases_c.remove(compat_c)
248 return compat_c, aliases_c
249
250 if not self._warning_disabled:
251 print('WARNING: the driver %s was not found in the driver list'
252 % (compat_list_c[0]))
253
254 return compat_list_c[0], compat_list_c[1:]
7581c01a 255
be44f271
SG
256 def setup_output_dirs(self, output_dirs):
257 """Set up the output directories
258
259 This should be done before setup_output() is called
260
261 Args:
262 output_dirs (tuple of str):
263 Directory to use for C output files.
264 Use None to write files relative current directory
265 Directory to use for H output files.
266 Defaults to the C output dir
267 """
268 def process_dir(ftype, dirname):
269 if dirname:
270 os.makedirs(dirname, exist_ok=True)
271 self._dirnames[ftype] = dirname
272
273 if output_dirs:
274 c_dirname = output_dirs[0]
275 h_dirname = output_dirs[1] if len(output_dirs) > 1 else c_dirname
276 process_dir(Ftype.SOURCE, c_dirname)
277 process_dir(Ftype.HEADER, h_dirname)
278
279 def setup_output(self, ftype, fname):
7581c01a
SG
280 """Set up the output destination
281
2be282ca 282 Once this is done, future calls to self.out() will output to this
be44f271
SG
283 file. The file used is as follows:
284
285 self._dirnames[ftype] is None: output to fname, or stdout if None
286 self._dirnames[ftype] is not None: output to fname in that directory
287
288 Calling this function multiple times will close the old file and open
289 the new one. If they are the same file, nothing happens and output will
290 continue to the same file.
7581c01a
SG
291
292 Args:
be44f271
SG
293 ftype (str): Type of file to create ('c' or 'h')
294 fname (str): Filename to send output to. If there is a directory in
295 self._dirnames for this file type, it will be put in that
296 directory
7581c01a 297 """
be44f271
SG
298 dirname = self._dirnames[ftype]
299 if dirname:
300 pathname = os.path.join(dirname, fname)
301 if self._outfile:
302 self._outfile.close()
303 self._outfile = open(pathname, 'w')
304 elif fname:
305 if not self._outfile:
306 self._outfile = open(fname, 'w')
f62cea0e
SG
307 else:
308 self._outfile = sys.stdout
7581c01a 309
be44f271
SG
310 def finish_output(self):
311 """Finish outputing to a file
312
313 This closes the output file, if one is in use
314 """
315 if self._outfile != sys.stdout:
316 self._outfile.close()
317
2be282ca 318 def out(self, line):
7581c01a
SG
319 """Output a string to the output file
320
321 Args:
9b330382 322 line (str): String to output
7581c01a 323 """
2be282ca 324 self._outfile.write(line)
7581c01a 325
2be282ca 326 def buf(self, line):
7581c01a
SG
327 """Buffer up a string to send later
328
329 Args:
9b330382 330 line (str): String to add to our 'buffer' list
7581c01a 331 """
2be282ca 332 self._lines.append(line)
7581c01a 333
2be282ca 334 def get_buf(self):
7581c01a
SG
335 """Get the contents of the output buffer, and clear it
336
337 Returns:
9b330382 338 list(str): The output buffer, which is then cleared for future use
7581c01a
SG
339 """
340 lines = self._lines
341 self._lines = []
342 return lines
343
d1055d68
SG
344 def out_header(self, outfile):
345 """Output a message indicating that this is an auto-generated file
346
347 Args:
348 outfile: OutputFile describing the file being generated
349 """
d503114c
SG
350 self.out('''/*
351 * DO NOT MODIFY
352 *
d1055d68
SG
353 * %s.
354 * This was generated by dtoc from a .dtb (device tree binary) file.
d503114c
SG
355 */
356
d1055d68 357''' % outfile.hdr_comment)
d503114c 358
8fed2eb2
SG
359 def get_phandle_argc(self, prop, node_name):
360 """Check if a node contains phandles
2925c26b 361
8fed2eb2
SG
362 We have no reliable way of detecting whether a node uses a phandle
363 or not. As an interim measure, use a list of known property names.
2925c26b 364
8fed2eb2 365 Args:
9b330382
SG
366 prop (fdt.Prop): Prop object to check
367 node_name (str): Node name, only used for raising an error
368 Returns:
369 int or None: Number of argument cells is this is a phandle,
370 else None
371 Raises:
372 ValueError: if the phandle cannot be parsed or the required property
373 is not present
8fed2eb2 374 """
ad34017c 375 if prop.name in ['clocks', 'cd-gpios']:
760b7170
SG
376 if not isinstance(prop.value, list):
377 prop.value = [prop.value]
8fed2eb2 378 val = prop.value
8fed2eb2
SG
379 i = 0
380
381 max_args = 0
382 args = []
383 while i < len(val):
384 phandle = fdt_util.fdt32_to_cpu(val[i])
760b7170
SG
385 # If we get to the end of the list, stop. This can happen
386 # since some nodes have more phandles in the list than others,
387 # but we allocate enough space for the largest list. So those
388 # nodes with shorter lists end up with zeroes at the end.
389 if not phandle:
390 break
8fed2eb2
SG
391 target = self._fdt.phandle_to_node.get(phandle)
392 if not target:
393 raise ValueError("Cannot parse '%s' in node '%s'" %
394 (prop.name, node_name))
ad34017c
WL
395 cells = None
396 for prop_name in ['#clock-cells', '#gpio-cells']:
397 cells = target.props.get(prop_name)
398 if cells:
399 break
8fed2eb2 400 if not cells:
ad34017c 401 raise ValueError("Node '%s' has no cells property" %
9b330382 402 (target.name))
8fed2eb2
SG
403 num_args = fdt_util.fdt32_to_cpu(cells.value)
404 max_args = max(max_args, num_args)
405 args.append(num_args)
406 i += 1 + num_args
407 return PhandleInfo(max_args, args)
408 return None
2925c26b 409
78128d52 410 def scan_driver(self, fname):
dac8228d
WL
411 """Scan a driver file to build a list of driver names and aliases
412
413 This procedure will populate self._drivers and self._driver_aliases
414
415 Args
78128d52 416 fname: Driver filename to scan
dac8228d 417 """
78128d52 418 with open(fname, encoding='utf-8') as inf:
dac8228d 419 try:
78128d52 420 buff = inf.read()
dac8228d
WL
421 except UnicodeDecodeError:
422 # This seems to happen on older Python versions
78128d52 423 print("Skipping file '%s' due to unicode error" % fname)
dac8228d
WL
424 return
425
426 # The following re will search for driver names declared as
427 # U_BOOT_DRIVER(driver_name)
ccc3da77 428 drivers = re.findall(r'U_BOOT_DRIVER\((.*)\)', buff)
dac8228d
WL
429
430 for driver in drivers:
7d637c12 431 self._drivers[driver] = Driver(driver)
dac8228d
WL
432
433 # The following re will search for driver aliases declared as
bdf8fd76 434 # DM_DRIVER_ALIAS(alias, driver_name)
78128d52 435 driver_aliases = re.findall(
bdf8fd76 436 r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
78128d52 437 buff)
dac8228d
WL
438
439 for alias in driver_aliases: # pragma: no cover
440 if len(alias) != 2:
441 continue
442 self._driver_aliases[alias[1]] = alias[0]
443
1e0f3f46 444 def scan_drivers(self, basedir=None):
dac8228d
WL
445 """Scan the driver folders to build a list of driver names and aliases
446
447 This procedure will populate self._drivers and self._driver_aliases
448
449 """
1e0f3f46
SG
450 if not basedir:
451 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
452 if basedir == '':
453 basedir = './'
454 self._basedir = basedir
78128d52
SG
455 for (dirpath, _, filenames) in os.walk(basedir):
456 for fname in filenames:
457 if not fname.endswith('.c'):
dac8228d 458 continue
78128d52 459 self.scan_driver(dirpath + '/' + fname)
dac8228d 460
78128d52
SG
461 for fname in self._drivers_additional:
462 if not isinstance(fname, str) or len(fname) == 0:
6c74d1b8 463 continue
78128d52
SG
464 if fname[0] == '/':
465 self.scan_driver(fname)
6c74d1b8 466 else:
78128d52 467 self.scan_driver(basedir + '/' + fname)
6c74d1b8 468
2be282ca 469 def scan_dtb(self):
f1a7ba1d 470 """Scan the device tree to obtain a tree of nodes and properties
7581c01a 471
2be282ca 472 Once this is done, self._fdt.GetRoot() can be called to obtain the
7581c01a
SG
473 device tree root node, and progress from there.
474 """
2be282ca
SG
475 self._fdt = fdt.FdtScan(self._dtb_fname)
476
1b27273e 477 def scan_node(self, root, valid_nodes):
2be282ca
SG
478 """Scan a node and subnodes to build a tree of node and phandle info
479
72ab7c5e 480 This adds each node to self._valid_nodes.
7581c01a 481
2be282ca 482 Args:
ccc3da77
SG
483 root (Node): Root node for scan
484 valid_nodes (list of Node): List of Node objects to add to
2be282ca 485 """
7581c01a
SG
486 for node in root.subnodes:
487 if 'compatible' in node.props:
488 status = node.props.get('status')
e36024b0 489 if (not self._include_disabled and not status or
2be282ca 490 status.value != 'disabled'):
1b27273e 491 valid_nodes.append(node)
7581c01a
SG
492
493 # recurse to handle any subnodes
1b27273e 494 self.scan_node(node, valid_nodes)
7581c01a 495
2be282ca 496 def scan_tree(self):
7581c01a
SG
497 """Scan the device tree for useful information
498
499 This fills in the following properties:
7581c01a
SG
500 _valid_nodes: A list of nodes we wish to consider include in the
501 platform data
502 """
1b27273e
SG
503 valid_nodes = []
504 self.scan_node(self._fdt.GetRoot(), valid_nodes)
505 self._valid_nodes = sorted(valid_nodes,
506 key=lambda x: conv_name_to_c(x.name))
507 for idx, node in enumerate(self._valid_nodes):
508 node.idx = idx
7581c01a 509
c20ee0ed
SG
510 @staticmethod
511 def get_num_cells(node):
512 """Get the number of cells in addresses and sizes for this node
513
514 Args:
9b330382 515 node (fdt.None): Node to check
c20ee0ed
SG
516
517 Returns:
518 Tuple:
519 Number of address cells for this node
520 Number of size cells for this node
521 """
522 parent = node.parent
78128d52 523 num_addr, num_size = 2, 2
c20ee0ed 524 if parent:
78128d52
SG
525 addr_prop = parent.props.get('#address-cells')
526 size_prop = parent.props.get('#size-cells')
527 if addr_prop:
528 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
529 if size_prop:
530 num_size = fdt_util.fdt32_to_cpu(size_prop.value)
531 return num_addr, num_size
c20ee0ed
SG
532
533 def scan_reg_sizes(self):
534 """Scan for 64-bit 'reg' properties and update the values
535
536 This finds 'reg' properties with 64-bit data and converts the value to
537 an array of 64-values. This allows it to be output in a way that the
538 C code can read.
539 """
540 for node in self._valid_nodes:
541 reg = node.props.get('reg')
542 if not reg:
543 continue
78128d52
SG
544 num_addr, num_size = self.get_num_cells(node)
545 total = num_addr + num_size
c20ee0ed 546
5ea9dccf 547 if reg.type != fdt.Type.INT:
dfe5f5b9
SG
548 raise ValueError("Node '%s' reg property is not an int" %
549 node.name)
c20ee0ed 550 if len(reg.value) % total:
9b330382
SG
551 raise ValueError(
552 "Node '%s' reg property has %d cells "
553 'which is not a multiple of na + ns = %d + %d)' %
78128d52
SG
554 (node.name, len(reg.value), num_addr, num_size))
555 reg.num_addr = num_addr
556 reg.num_size = num_size
557 if num_addr != 1 or num_size != 1:
5ea9dccf 558 reg.type = fdt.Type.INT64
c20ee0ed
SG
559 i = 0
560 new_value = []
561 val = reg.value
562 if not isinstance(val, list):
563 val = [val]
564 while i < len(val):
78128d52
SG
565 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
566 i += num_addr
567 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
568 i += num_size
c20ee0ed
SG
569 new_value += [addr, size]
570 reg.value = new_value
571
2be282ca 572 def scan_structs(self):
7581c01a
SG
573 """Scan the device tree building up the C structures we will use.
574
575 Build a dict keyed by C struct name containing a dict of Prop
576 object for each struct field (keyed by property name). Where the
577 same struct appears multiple times, try to use the 'widest'
578 property, i.e. the one with a type which can express all others.
579
580 Once the widest property is determined, all other properties are
581 updated to match that width.
e4fb5faa 582
a7d5f96e 583 The results are written to self._struct_data
7581c01a 584 """
a7d5f96e 585 structs = self._struct_data
7581c01a 586 for node in self._valid_nodes:
dac8228d 587 node_name, _ = self.get_normalized_compat_name(node)
7581c01a
SG
588 fields = {}
589
590 # Get a list of all the valid properties in this node.
591 for name, prop in node.props.items():
592 if name not in PROP_IGNORE_LIST and name[0] != '#':
593 fields[name] = copy.deepcopy(prop)
594
595 # If we've seen this node_name before, update the existing struct.
596 if node_name in structs:
597 struct = structs[node_name]
598 for name, prop in fields.items():
599 oldprop = struct.get(name)
600 if oldprop:
601 oldprop.Widen(prop)
602 else:
603 struct[name] = prop
604
605 # Otherwise store this as a new struct.
606 else:
607 structs[node_name] = fields
608
7581c01a 609 for node in self._valid_nodes:
dac8228d 610 node_name, _ = self.get_normalized_compat_name(node)
7581c01a
SG
611 struct = structs[node_name]
612 for name, prop in node.props.items():
613 if name not in PROP_IGNORE_LIST and name[0] != '#':
614 prop.Widen(struct[name])
7581c01a 615
2be282ca 616 def scan_phandles(self):
7581c01a
SG
617 """Figure out what phandles each node uses
618
619 We need to be careful when outputing nodes that use phandles since
620 they must come after the declaration of the phandles in the C file.
621 Otherwise we get a compiler error since the phandle struct is not yet
622 declared.
623
624 This function adds to each node a list of phandle nodes that the node
625 depends on. This allows us to output things in the right order.
626 """
627 for node in self._valid_nodes:
628 node.phandles = set()
629 for pname, prop in node.props.items():
630 if pname in PROP_IGNORE_LIST or pname[0] == '#':
631 continue
8fed2eb2
SG
632 info = self.get_phandle_argc(prop, node.name)
633 if info:
8fed2eb2 634 # Process the list as pairs of (phandle, id)
634eba4b
SG
635 pos = 0
636 for args in info.args:
637 phandle_cell = prop.value[pos]
8fed2eb2
SG
638 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
639 target_node = self._fdt.phandle_to_node[phandle]
640 node.phandles.add(target_node)
634eba4b 641 pos += 1 + args
7581c01a
SG
642
643
a7d5f96e 644 def generate_structs(self):
7581c01a
SG
645 """Generate struct defintions for the platform data
646
647 This writes out the body of a header file consisting of structure
648 definitions for node in self._valid_nodes. See the documentation in
2799a69e 649 doc/driver-model/of-plat.rst for more information.
7581c01a 650 """
a7d5f96e 651 structs = self._struct_data
2be282ca 652 self.out('#include <stdbool.h>\n')
b08c8c48 653 self.out('#include <linux/libfdt.h>\n')
7581c01a
SG
654
655 # Output the struct definition
656 for name in sorted(structs):
2be282ca 657 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
7581c01a
SG
658 for pname in sorted(structs[name]):
659 prop = structs[name][pname]
8fed2eb2
SG
660 info = self.get_phandle_argc(prop, structs[name])
661 if info:
7581c01a 662 # For phandles, include a reference to the target
0d15463c
SG
663 struct_name = 'struct phandle_%d_arg' % info.max_args
664 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
2be282ca 665 conv_name_to_c(prop.name),
634eba4b 666 len(info.args)))
7581c01a
SG
667 else:
668 ptype = TYPE_NAMES[prop.type]
2be282ca
SG
669 self.out('\t%s%s' % (tab_to(2, ptype),
670 conv_name_to_c(prop.name)))
671 if isinstance(prop.value, list):
672 self.out('[%d]' % len(prop.value))
673 self.out(';\n')
674 self.out('};\n')
7581c01a 675
abf0c802
SG
676 def _output_list(self, node, prop):
677 """Output the C code for a devicetree property that holds a list
678
679 Args:
680 node (fdt.Node): Node to output
681 prop (fdt.Prop): Prop to output
682 """
683 self.buf('{')
684 vals = []
685 # For phandles, output a reference to the platform data
686 # of the target node.
687 info = self.get_phandle_argc(prop, node.name)
688 if info:
689 # Process the list as pairs of (phandle, id)
690 pos = 0
691 for args in info.args:
692 phandle_cell = prop.value[pos]
693 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
694 target_node = self._fdt.phandle_to_node[phandle]
695 arg_values = []
696 for i in range(args):
697 arg_values.append(
698 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
699 pos += 1 + args
700 vals.append('\t{%d, {%s}}' % (target_node.idx,
701 ', '.join(arg_values)))
702 for val in vals:
703 self.buf('\n\t\t%s,' % val)
704 else:
705 for val in prop.value:
706 vals.append(get_value(prop.type, val))
707
708 # Put 8 values per line to avoid very long lines.
709 for i in range(0, len(vals), 8):
710 if i:
711 self.buf(',\n\t\t')
712 self.buf(', '.join(vals[i:i + 8]))
713 self.buf('}')
714
221ddc11
SG
715 def _declare_device(self, var_name, struct_name, node_parent):
716 """Add a device declaration to the output
717
20e442ab 718 This declares a U_BOOT_DRVINFO() for the device being processed
221ddc11
SG
719
720 Args:
721 var_name (str): C name for the node
722 struct_name (str): Name for the dt struct associated with the node
723 node_parent (Node): Parent of the node (or None if none)
724 """
20e442ab 725 self.buf('U_BOOT_DRVINFO(%s) = {\n' % var_name)
221ddc11
SG
726 self.buf('\t.name\t\t= "%s",\n' % struct_name)
727 self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, var_name))
728 self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
729 idx = -1
730 if node_parent and node_parent in self._valid_nodes:
731 idx = node_parent.idx
732 self.buf('\t.parent_idx\t= %d,\n' % idx)
733 self.buf('};\n')
734 self.buf('\n')
735
161dac1d
SG
736 def _output_prop(self, node, prop):
737 """Output a line containing the value of a struct member
738
739 Args:
740 node (Node): Node being output
741 prop (Prop): Prop object to output
742 """
743 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
744 return
745 member_name = conv_name_to_c(prop.name)
746 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
747
748 # Special handling for lists
749 if isinstance(prop.value, list):
750 self._output_list(node, prop)
751 else:
752 self.buf(get_value(prop.type, prop.value))
753 self.buf(',\n')
754
755 def _output_values(self, var_name, struct_name, node):
756 """Output the definition of a device's struct values
757
758 Args:
759 var_name (str): C name for the node
760 struct_name (str): Name for the dt struct associated with the node
761 node (Node): Node being output
762 """
763 self.buf('static struct %s%s %s%s = {\n' %
764 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
765 for pname in sorted(node.props):
766 self._output_prop(node, node.props[pname])
767 self.buf('};\n')
768
2be282ca 769 def output_node(self, node):
7581c01a
SG
770 """Output the C code for a node
771
772 Args:
9b330382 773 node (fdt.Node): node to output
7581c01a 774 """
dac8228d 775 struct_name, _ = self.get_normalized_compat_name(node)
2be282ca 776 var_name = conv_name_to_c(node.name)
1b27273e 777 self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
7581c01a 778
161dac1d 779 self._output_values(var_name, struct_name, node)
221ddc11 780 self._declare_device(var_name, struct_name, node.parent)
7581c01a 781
2be282ca 782 self.out(''.join(self.get_buf()))
7581c01a 783
a7d5f96e 784 def generate_plat(self):
7581c01a
SG
785 """Generate device defintions for the platform data
786
787 This writes out C platform data initialisation data and
20e442ab 788 U_BOOT_DRVINFO() declarations for each valid node. Where a node has
7581c01a
SG
789 multiple compatible strings, a #define is used to make them equivalent.
790
2799a69e 791 See the documentation in doc/driver-model/of-plat.rst for more
7581c01a
SG
792 information.
793 """
20e442ab 794 self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
f31fa99a 795 self.out('#define DT_PLAT_C\n')
cb43ac18 796 self.out('\n')
2be282ca
SG
797 self.out('#include <common.h>\n')
798 self.out('#include <dm.h>\n')
799 self.out('#include <dt-structs.h>\n')
800 self.out('\n')
7581c01a
SG
801 nodes_to_output = list(self._valid_nodes)
802
803 # Keep outputing nodes until there is none left
804 while nodes_to_output:
805 node = nodes_to_output[0]
806 # Output all the node's dependencies first
807 for req_node in node.phandles:
808 if req_node in nodes_to_output:
2be282ca 809 self.output_node(req_node)
7581c01a 810 nodes_to_output.remove(req_node)
2be282ca 811 self.output_node(node)
7581c01a 812 nodes_to_output.remove(node)
fa0ea5b0 813
51f1263d 814 # Define dm_populate_phandle_data() which will add the linking between
8629d30a
SG
815 # nodes using DM_DRVINFO_GET
816 # dtv_dmc_at_xxx.clocks[0].node = DM_DRVINFO_GET(clock_controller_at_xxx)
51f1263d 817 self.buf('void dm_populate_phandle_data(void) {\n')
51f1263d
WL
818 self.buf('}\n')
819
820 self.out(''.join(self.get_buf()))
fa0ea5b0 821
192c111c 822
be44f271
SG
823# Types of output file we understand
824# key: Command used to generate this file
825# value: OutputFile for this command
826OUTPUT_FILES = {
d1055d68
SG
827 'struct':
828 OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
a7d5f96e 829 DtbPlatdata.generate_structs,
d1055d68
SG
830 'Defines the structs used to hold devicetree data'),
831 'platdata':
a7d5f96e 832 OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
d1055d68 833 'Declares the U_BOOT_DRIVER() records and platform data'),
be44f271
SG
834 }
835
836
192c111c 837def run_steps(args, dtb_file, include_disabled, output, output_dirs,
1e0f3f46 838 warning_disabled=False, drivers_additional=None, basedir=None):
fa0ea5b0
SG
839 """Run all the steps of the dtoc tool
840
841 Args:
9b330382
SG
842 args (list): List of non-option arguments provided to the problem
843 dtb_file (str): Filename of dtb file to process
844 include_disabled (bool): True to include disabled nodes
f62cea0e 845 output (str): Name of output file (None for stdout)
192c111c
SG
846 output_dirs (tuple of str):
847 Directory to put C output files
848 Directory to put H output files
78128d52
SG
849 warning_disabled (bool): True to avoid showing warnings about missing
850 drivers
ccc3da77 851 drivers_additional (list): List of additional drivers to use during
78128d52 852 scanning
1e0f3f46
SG
853 basedir (str): Base directory of U-Boot source code. Defaults to the
854 grandparent of this file's directory
9b330382
SG
855 Raises:
856 ValueError: if args has no command, or an unknown command
fa0ea5b0
SG
857 """
858 if not args:
be44f271
SG
859 raise ValueError('Please specify a command: struct, platdata, all')
860 if output and output_dirs and any(output_dirs):
861 raise ValueError('Must specify either output or output_dirs, not both')
fa0ea5b0 862
78128d52
SG
863 plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled,
864 drivers_additional)
1e0f3f46 865 plat.scan_drivers(basedir)
fa0ea5b0
SG
866 plat.scan_dtb()
867 plat.scan_tree()
c20ee0ed 868 plat.scan_reg_sizes()
be44f271 869 plat.setup_output_dirs(output_dirs)
a7d5f96e 870 plat.scan_structs()
fa0ea5b0
SG
871 plat.scan_phandles()
872
10cbd3b7
SG
873 cmds = args[0].split(',')
874 if 'all' in cmds:
875 cmds = sorted(OUTPUT_FILES.keys())
876 for cmd in cmds:
be44f271
SG
877 outfile = OUTPUT_FILES.get(cmd)
878 if not outfile:
879 raise ValueError("Unknown command '%s': (use: %s)" %
10cbd3b7 880 (cmd, ', '.join(sorted(OUTPUT_FILES.keys()))))
be44f271
SG
881 plat.setup_output(outfile.ftype,
882 outfile.fname if output_dirs else output)
d1055d68 883 plat.out_header(outfile)
a7d5f96e 884 outfile.method(plat)
be44f271 885 plat.finish_output()
This page took 0.261229 seconds and 4 git commands to generate.