]> Git Repo - J-u-boot.git/blame - tools/dtoc/dtb_platdata.py
patman: Tidy up sys.path changes
[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.
12"""
13
8fed2eb2 14import collections
7581c01a 15import copy
2be282ca 16import sys
7581c01a 17
bf776679
SG
18from dtoc import fdt
19from dtoc import fdt_util
20from patman import tools
7581c01a
SG
21
22# When we see these properties we ignore them - i.e. do not create a structure member
23PROP_IGNORE_LIST = [
24 '#address-cells',
25 '#gpio-cells',
26 '#size-cells',
27 'compatible',
28 'linux,phandle',
29 "status",
30 'phandle',
31 'u-boot,dm-pre-reloc',
32 'u-boot,dm-tpl',
33 'u-boot,dm-spl',
34]
35
36# C type declarations for the tyues we support
37TYPE_NAMES = {
38 fdt.TYPE_INT: 'fdt32_t',
39 fdt.TYPE_BYTE: 'unsigned char',
40 fdt.TYPE_STRING: 'const char *',
41 fdt.TYPE_BOOL: 'bool',
fbdfd228 42 fdt.TYPE_INT64: 'fdt64_t',
2be282ca 43}
7581c01a
SG
44
45STRUCT_PREFIX = 'dtd_'
46VAL_PREFIX = 'dtv_'
47
8fed2eb2
SG
48# This holds information about a property which includes phandles.
49#
50# max_args: integer: Maximum number or arguments that any phandle uses (int).
51# args: Number of args for each phandle in the property. The total number of
52# phandles is len(args). This is a list of integers.
53PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
54
55
2be282ca 56def conv_name_to_c(name):
7581c01a
SG
57 """Convert a device-tree name to a C identifier
58
30107b08
SG
59 This uses multiple replace() calls instead of re.sub() since it is faster
60 (400ms for 1m calls versus 1000ms for the 're' version).
61
7581c01a
SG
62 Args:
63 name: Name to convert
64 Return:
65 String containing the C version of this name
66 """
2be282ca
SG
67 new = name.replace('@', '_at_')
68 new = new.replace('-', '_')
69 new = new.replace(',', '_')
70 new = new.replace('.', '_')
2be282ca
SG
71 return new
72
73def tab_to(num_tabs, line):
74 """Append tabs to a line of text to reach a tab stop.
75
76 Args:
77 num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
78 line: Line of text to append to
79
80 Returns:
81 line with the correct number of tabs appeneded. If the line already
82 extends past that tab stop then a single space is appended.
83 """
84 if len(line) >= num_tabs * 8:
85 return line + ' '
86 return line + '\t' * (num_tabs - len(line) // 8)
87
56e0bbe0
SG
88def get_value(ftype, value):
89 """Get a value as a C expression
90
91 For integers this returns a byte-swapped (little-endian) hex string
92 For bytes this returns a hex string, e.g. 0x12
93 For strings this returns a literal string enclosed in quotes
94 For booleans this return 'true'
95
96 Args:
97 type: Data type (fdt_util)
98 value: Data value, as a string of bytes
99 """
100 if ftype == fdt.TYPE_INT:
101 return '%#x' % fdt_util.fdt32_to_cpu(value)
102 elif ftype == fdt.TYPE_BYTE:
9b044f7e 103 return '%#x' % tools.ToByte(value[0])
56e0bbe0
SG
104 elif ftype == fdt.TYPE_STRING:
105 return '"%s"' % value
106 elif ftype == fdt.TYPE_BOOL:
107 return 'true'
fbdfd228
SG
108 elif ftype == fdt.TYPE_INT64:
109 return '%#x' % value
56e0bbe0
SG
110
111def get_compat_name(node):
112 """Get a node's first compatible string as a C identifier
113
114 Args:
115 node: Node object to check
116 Return:
117 Tuple:
118 C identifier for the first compatible string
119 List of C identifiers for all the other compatible strings
120 (possibly empty)
121 """
122 compat = node.props['compatible'].value
123 aliases = []
124 if isinstance(compat, list):
125 compat, aliases = compat[0], compat[1:]
126 return conv_name_to_c(compat), [conv_name_to_c(a) for a in aliases]
127
56e0bbe0 128
2be282ca 129class DtbPlatdata(object):
7581c01a
SG
130 """Provide a means to convert device tree binary data to platform data
131
132 The output of this process is C structures which can be used in space-
133 constrained encvironments where the ~3KB code overhead of device tree
134 code is not affordable.
135
136 Properties:
2be282ca 137 _fdt: Fdt object, referencing the device tree
7581c01a
SG
138 _dtb_fname: Filename of the input device tree binary file
139 _valid_nodes: A list of Node object with compatible strings
e36024b0 140 _include_disabled: true to include nodes marked status = "disabled"
7581c01a
SG
141 _outfile: The current output file (sys.stdout or a real file)
142 _lines: Stashed list of output lines for outputting in the future
7581c01a 143 """
e36024b0 144 def __init__(self, dtb_fname, include_disabled):
2be282ca 145 self._fdt = None
7581c01a
SG
146 self._dtb_fname = dtb_fname
147 self._valid_nodes = None
e36024b0 148 self._include_disabled = include_disabled
7581c01a
SG
149 self._outfile = None
150 self._lines = []
151 self._aliases = {}
152
2be282ca 153 def setup_output(self, fname):
7581c01a
SG
154 """Set up the output destination
155
2be282ca 156 Once this is done, future calls to self.out() will output to this
7581c01a
SG
157 file.
158
159 Args:
160 fname: Filename to send output to, or '-' for stdout
161 """
162 if fname == '-':
163 self._outfile = sys.stdout
164 else:
165 self._outfile = open(fname, 'w')
166
2be282ca 167 def out(self, line):
7581c01a
SG
168 """Output a string to the output file
169
170 Args:
2be282ca 171 line: String to output
7581c01a 172 """
2be282ca 173 self._outfile.write(line)
7581c01a 174
2be282ca 175 def buf(self, line):
7581c01a
SG
176 """Buffer up a string to send later
177
178 Args:
2be282ca 179 line: String to add to our 'buffer' list
7581c01a 180 """
2be282ca 181 self._lines.append(line)
7581c01a 182
2be282ca 183 def get_buf(self):
7581c01a
SG
184 """Get the contents of the output buffer, and clear it
185
186 Returns:
187 The output buffer, which is then cleared for future use
188 """
189 lines = self._lines
190 self._lines = []
191 return lines
192
d503114c
SG
193 def out_header(self):
194 """Output a message indicating that this is an auto-generated file"""
195 self.out('''/*
196 * DO NOT MODIFY
197 *
198 * This file was generated by dtoc from a .dtb (device tree binary) file.
199 */
200
201''')
202
8fed2eb2
SG
203 def get_phandle_argc(self, prop, node_name):
204 """Check if a node contains phandles
2925c26b 205
8fed2eb2
SG
206 We have no reliable way of detecting whether a node uses a phandle
207 or not. As an interim measure, use a list of known property names.
2925c26b 208
8fed2eb2
SG
209 Args:
210 prop: Prop object to check
211 Return:
212 Number of argument cells is this is a phandle, else None
213 """
214 if prop.name in ['clocks']:
760b7170
SG
215 if not isinstance(prop.value, list):
216 prop.value = [prop.value]
8fed2eb2 217 val = prop.value
8fed2eb2
SG
218 i = 0
219
220 max_args = 0
221 args = []
222 while i < len(val):
223 phandle = fdt_util.fdt32_to_cpu(val[i])
760b7170
SG
224 # If we get to the end of the list, stop. This can happen
225 # since some nodes have more phandles in the list than others,
226 # but we allocate enough space for the largest list. So those
227 # nodes with shorter lists end up with zeroes at the end.
228 if not phandle:
229 break
8fed2eb2
SG
230 target = self._fdt.phandle_to_node.get(phandle)
231 if not target:
232 raise ValueError("Cannot parse '%s' in node '%s'" %
233 (prop.name, node_name))
234 prop_name = '#clock-cells'
235 cells = target.props.get(prop_name)
236 if not cells:
237 raise ValueError("Node '%s' has no '%s' property" %
238 (target.name, prop_name))
239 num_args = fdt_util.fdt32_to_cpu(cells.value)
240 max_args = max(max_args, num_args)
241 args.append(num_args)
242 i += 1 + num_args
243 return PhandleInfo(max_args, args)
244 return None
2925c26b 245
2be282ca 246 def scan_dtb(self):
f1a7ba1d 247 """Scan the device tree to obtain a tree of nodes and properties
7581c01a 248
2be282ca 249 Once this is done, self._fdt.GetRoot() can be called to obtain the
7581c01a
SG
250 device tree root node, and progress from there.
251 """
2be282ca
SG
252 self._fdt = fdt.FdtScan(self._dtb_fname)
253
254 def scan_node(self, root):
255 """Scan a node and subnodes to build a tree of node and phandle info
256
72ab7c5e 257 This adds each node to self._valid_nodes.
7581c01a 258
2be282ca
SG
259 Args:
260 root: Root node for scan
261 """
7581c01a
SG
262 for node in root.subnodes:
263 if 'compatible' in node.props:
264 status = node.props.get('status')
e36024b0 265 if (not self._include_disabled and not status or
2be282ca 266 status.value != 'disabled'):
7581c01a 267 self._valid_nodes.append(node)
7581c01a
SG
268
269 # recurse to handle any subnodes
2be282ca 270 self.scan_node(node)
7581c01a 271
2be282ca 272 def scan_tree(self):
7581c01a
SG
273 """Scan the device tree for useful information
274
275 This fills in the following properties:
7581c01a
SG
276 _valid_nodes: A list of nodes we wish to consider include in the
277 platform data
278 """
7581c01a 279 self._valid_nodes = []
2be282ca 280 return self.scan_node(self._fdt.GetRoot())
7581c01a 281
c20ee0ed
SG
282 @staticmethod
283 def get_num_cells(node):
284 """Get the number of cells in addresses and sizes for this node
285
286 Args:
287 node: Node to check
288
289 Returns:
290 Tuple:
291 Number of address cells for this node
292 Number of size cells for this node
293 """
294 parent = node.parent
295 na, ns = 2, 2
296 if parent:
297 na_prop = parent.props.get('#address-cells')
298 ns_prop = parent.props.get('#size-cells')
299 if na_prop:
300 na = fdt_util.fdt32_to_cpu(na_prop.value)
301 if ns_prop:
302 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
303 return na, ns
304
305 def scan_reg_sizes(self):
306 """Scan for 64-bit 'reg' properties and update the values
307
308 This finds 'reg' properties with 64-bit data and converts the value to
309 an array of 64-values. This allows it to be output in a way that the
310 C code can read.
311 """
312 for node in self._valid_nodes:
313 reg = node.props.get('reg')
314 if not reg:
315 continue
316 na, ns = self.get_num_cells(node)
317 total = na + ns
318
319 if reg.type != fdt.TYPE_INT:
dfe5f5b9
SG
320 raise ValueError("Node '%s' reg property is not an int" %
321 node.name)
c20ee0ed
SG
322 if len(reg.value) % total:
323 raise ValueError("Node '%s' reg property has %d cells "
324 'which is not a multiple of na + ns = %d + %d)' %
325 (node.name, len(reg.value), na, ns))
326 reg.na = na
327 reg.ns = ns
328 if na != 1 or ns != 1:
329 reg.type = fdt.TYPE_INT64
330 i = 0
331 new_value = []
332 val = reg.value
333 if not isinstance(val, list):
334 val = [val]
335 while i < len(val):
336 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
337 i += na
338 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
339 i += ns
340 new_value += [addr, size]
341 reg.value = new_value
342
2be282ca 343 def scan_structs(self):
7581c01a
SG
344 """Scan the device tree building up the C structures we will use.
345
346 Build a dict keyed by C struct name containing a dict of Prop
347 object for each struct field (keyed by property name). Where the
348 same struct appears multiple times, try to use the 'widest'
349 property, i.e. the one with a type which can express all others.
350
351 Once the widest property is determined, all other properties are
352 updated to match that width.
353 """
354 structs = {}
355 for node in self._valid_nodes:
56e0bbe0 356 node_name, _ = get_compat_name(node)
7581c01a
SG
357 fields = {}
358
359 # Get a list of all the valid properties in this node.
360 for name, prop in node.props.items():
361 if name not in PROP_IGNORE_LIST and name[0] != '#':
362 fields[name] = copy.deepcopy(prop)
363
364 # If we've seen this node_name before, update the existing struct.
365 if node_name in structs:
366 struct = structs[node_name]
367 for name, prop in fields.items():
368 oldprop = struct.get(name)
369 if oldprop:
370 oldprop.Widen(prop)
371 else:
372 struct[name] = prop
373
374 # Otherwise store this as a new struct.
375 else:
376 structs[node_name] = fields
377
378 upto = 0
379 for node in self._valid_nodes:
56e0bbe0 380 node_name, _ = get_compat_name(node)
7581c01a
SG
381 struct = structs[node_name]
382 for name, prop in node.props.items():
383 if name not in PROP_IGNORE_LIST and name[0] != '#':
384 prop.Widen(struct[name])
385 upto += 1
386
56e0bbe0 387 struct_name, aliases = get_compat_name(node)
7581c01a
SG
388 for alias in aliases:
389 self._aliases[alias] = struct_name
390
391 return structs
392
2be282ca 393 def scan_phandles(self):
7581c01a
SG
394 """Figure out what phandles each node uses
395
396 We need to be careful when outputing nodes that use phandles since
397 they must come after the declaration of the phandles in the C file.
398 Otherwise we get a compiler error since the phandle struct is not yet
399 declared.
400
401 This function adds to each node a list of phandle nodes that the node
402 depends on. This allows us to output things in the right order.
403 """
404 for node in self._valid_nodes:
405 node.phandles = set()
406 for pname, prop in node.props.items():
407 if pname in PROP_IGNORE_LIST or pname[0] == '#':
408 continue
8fed2eb2
SG
409 info = self.get_phandle_argc(prop, node.name)
410 if info:
8fed2eb2 411 # Process the list as pairs of (phandle, id)
634eba4b
SG
412 pos = 0
413 for args in info.args:
414 phandle_cell = prop.value[pos]
8fed2eb2
SG
415 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
416 target_node = self._fdt.phandle_to_node[phandle]
417 node.phandles.add(target_node)
634eba4b 418 pos += 1 + args
7581c01a
SG
419
420
2be282ca 421 def generate_structs(self, structs):
7581c01a
SG
422 """Generate struct defintions for the platform data
423
424 This writes out the body of a header file consisting of structure
425 definitions for node in self._valid_nodes. See the documentation in
2799a69e 426 doc/driver-model/of-plat.rst for more information.
7581c01a 427 """
d503114c 428 self.out_header()
2be282ca 429 self.out('#include <stdbool.h>\n')
b08c8c48 430 self.out('#include <linux/libfdt.h>\n')
7581c01a
SG
431
432 # Output the struct definition
433 for name in sorted(structs):
2be282ca 434 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
7581c01a
SG
435 for pname in sorted(structs[name]):
436 prop = structs[name][pname]
8fed2eb2
SG
437 info = self.get_phandle_argc(prop, structs[name])
438 if info:
7581c01a 439 # For phandles, include a reference to the target
0d15463c
SG
440 struct_name = 'struct phandle_%d_arg' % info.max_args
441 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
2be282ca 442 conv_name_to_c(prop.name),
634eba4b 443 len(info.args)))
7581c01a
SG
444 else:
445 ptype = TYPE_NAMES[prop.type]
2be282ca
SG
446 self.out('\t%s%s' % (tab_to(2, ptype),
447 conv_name_to_c(prop.name)))
448 if isinstance(prop.value, list):
449 self.out('[%d]' % len(prop.value))
450 self.out(';\n')
451 self.out('};\n')
7581c01a 452
90a8132f 453 for alias, struct_name in self._aliases.items():
e9cde87e
HS
454 if alias not in sorted(structs):
455 self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
456 STRUCT_PREFIX, struct_name))
7581c01a 457
2be282ca 458 def output_node(self, node):
7581c01a
SG
459 """Output the C code for a node
460
461 Args:
462 node: node to output
463 """
56e0bbe0 464 struct_name, _ = get_compat_name(node)
2be282ca 465 var_name = conv_name_to_c(node.name)
7d05d3a8 466 self.buf('static const struct %s%s %s%s = {\n' %
2be282ca 467 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
1953ce75
SG
468 for pname in sorted(node.props):
469 prop = node.props[pname]
7581c01a
SG
470 if pname in PROP_IGNORE_LIST or pname[0] == '#':
471 continue
2be282ca
SG
472 member_name = conv_name_to_c(prop.name)
473 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
7581c01a
SG
474
475 # Special handling for lists
2be282ca
SG
476 if isinstance(prop.value, list):
477 self.buf('{')
7581c01a
SG
478 vals = []
479 # For phandles, output a reference to the platform data
480 # of the target node.
8fed2eb2
SG
481 info = self.get_phandle_argc(prop, node.name)
482 if info:
7581c01a 483 # Process the list as pairs of (phandle, id)
634eba4b
SG
484 pos = 0
485 for args in info.args:
486 phandle_cell = prop.value[pos]
7581c01a 487 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
72ab7c5e 488 target_node = self._fdt.phandle_to_node[phandle]
2be282ca 489 name = conv_name_to_c(target_node.name)
634eba4b
SG
490 arg_values = []
491 for i in range(args):
492 arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
493 pos += 1 + args
494 vals.append('\t{&%s%s, {%s}}' % (VAL_PREFIX, name,
495 ', '.join(arg_values)))
35d50370
SG
496 for val in vals:
497 self.buf('\n\t\t%s,' % val)
7581c01a
SG
498 else:
499 for val in prop.value:
56e0bbe0 500 vals.append(get_value(prop.type, val))
21d54ac3 501
35d50370 502 # Put 8 values per line to avoid very long lines.
90a8132f 503 for i in range(0, len(vals), 8):
35d50370
SG
504 if i:
505 self.buf(',\n\t\t')
506 self.buf(', '.join(vals[i:i + 8]))
2be282ca 507 self.buf('}')
7581c01a 508 else:
56e0bbe0 509 self.buf(get_value(prop.type, prop.value))
2be282ca
SG
510 self.buf(',\n')
511 self.buf('};\n')
7581c01a
SG
512
513 # Add a device declaration
2be282ca
SG
514 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
515 self.buf('\t.name\t\t= "%s",\n' % struct_name)
516 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
517 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
518 self.buf('};\n')
519 self.buf('\n')
7581c01a 520
2be282ca 521 self.out(''.join(self.get_buf()))
7581c01a 522
2be282ca 523 def generate_tables(self):
7581c01a
SG
524 """Generate device defintions for the platform data
525
526 This writes out C platform data initialisation data and
527 U_BOOT_DEVICE() declarations for each valid node. Where a node has
528 multiple compatible strings, a #define is used to make them equivalent.
529
2799a69e 530 See the documentation in doc/driver-model/of-plat.rst for more
7581c01a
SG
531 information.
532 """
d503114c 533 self.out_header()
2be282ca
SG
534 self.out('#include <common.h>\n')
535 self.out('#include <dm.h>\n')
536 self.out('#include <dt-structs.h>\n')
537 self.out('\n')
7581c01a
SG
538 nodes_to_output = list(self._valid_nodes)
539
540 # Keep outputing nodes until there is none left
541 while nodes_to_output:
542 node = nodes_to_output[0]
543 # Output all the node's dependencies first
544 for req_node in node.phandles:
545 if req_node in nodes_to_output:
2be282ca 546 self.output_node(req_node)
7581c01a 547 nodes_to_output.remove(req_node)
2be282ca 548 self.output_node(node)
7581c01a 549 nodes_to_output.remove(node)
fa0ea5b0
SG
550
551
552def run_steps(args, dtb_file, include_disabled, output):
553 """Run all the steps of the dtoc tool
554
555 Args:
556 args: List of non-option arguments provided to the problem
557 dtb_file: Filename of dtb file to process
558 include_disabled: True to include disabled nodes
559 output: Name of output file
560 """
561 if not args:
562 raise ValueError('Please specify a command: struct, platdata')
563
564 plat = DtbPlatdata(dtb_file, include_disabled)
565 plat.scan_dtb()
566 plat.scan_tree()
c20ee0ed 567 plat.scan_reg_sizes()
fa0ea5b0
SG
568 plat.setup_output(output)
569 structs = plat.scan_structs()
570 plat.scan_phandles()
571
572 for cmd in args[0].split(','):
573 if cmd == 'struct':
574 plat.generate_structs(structs)
575 elif cmd == 'platdata':
576 plat.generate_tables()
577 else:
578 raise ValueError("Unknown command '%s': (use: struct, platdata)" %
579 cmd)
This page took 0.234147 seconds and 4 git commands to generate.