2 # SPDX-License-Identifier: GPL-2.0+
4 # Copyright (C) 2017 Google, Inc
5 # Written by Simon Glass <sjg@chromium.org>
8 """Device tree to platform data class
10 This supports converting device tree data to C structures definitions and
21 # When we see these properties we ignore them - i.e. do not create a structure member
30 'u-boot,dm-pre-reloc',
35 # C type declarations for the tyues we support
37 fdt.TYPE_INT: 'fdt32_t',
38 fdt.TYPE_BYTE: 'unsigned char',
39 fdt.TYPE_STRING: 'const char *',
40 fdt.TYPE_BOOL: 'bool',
41 fdt.TYPE_INT64: 'fdt64_t',
44 STRUCT_PREFIX = 'dtd_'
47 # This holds information about a property which includes phandles.
49 # max_args: integer: Maximum number or arguments that any phandle uses (int).
50 # args: Number of args for each phandle in the property. The total number of
51 # phandles is len(args). This is a list of integers.
52 PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
55 def conv_name_to_c(name):
56 """Convert a device-tree name to a C identifier
58 This uses multiple replace() calls instead of re.sub() since it is faster
59 (400ms for 1m calls versus 1000ms for the 're' version).
64 String containing the C version of this name
66 new = name.replace('@', '_at_')
67 new = new.replace('-', '_')
68 new = new.replace(',', '_')
69 new = new.replace('.', '_')
72 def tab_to(num_tabs, line):
73 """Append tabs to a line of text to reach a tab stop.
76 num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
77 line: Line of text to append to
80 line with the correct number of tabs appeneded. If the line already
81 extends past that tab stop then a single space is appended.
83 if len(line) >= num_tabs * 8:
85 return line + '\t' * (num_tabs - len(line) // 8)
87 def get_value(ftype, value):
88 """Get a value as a C expression
90 For integers this returns a byte-swapped (little-endian) hex string
91 For bytes this returns a hex string, e.g. 0x12
92 For strings this returns a literal string enclosed in quotes
93 For booleans this return 'true'
96 type: Data type (fdt_util)
97 value: Data value, as a string of bytes
99 if ftype == fdt.TYPE_INT:
100 return '%#x' % fdt_util.fdt32_to_cpu(value)
101 elif ftype == fdt.TYPE_BYTE:
102 return '%#x' % ord(value[0])
103 elif ftype == fdt.TYPE_STRING:
104 return '"%s"' % value
105 elif ftype == fdt.TYPE_BOOL:
107 elif ftype == fdt.TYPE_INT64:
110 def get_compat_name(node):
111 """Get a node's first compatible string as a C identifier
114 node: Node object to check
117 C identifier for the first compatible string
118 List of C identifiers for all the other compatible strings
121 compat = node.props['compatible'].value
123 if isinstance(compat, list):
124 compat, aliases = compat[0], compat[1:]
125 return conv_name_to_c(compat), [conv_name_to_c(a) for a in aliases]
128 class DtbPlatdata(object):
129 """Provide a means to convert device tree binary data to platform data
131 The output of this process is C structures which can be used in space-
132 constrained encvironments where the ~3KB code overhead of device tree
133 code is not affordable.
136 _fdt: Fdt object, referencing the device tree
137 _dtb_fname: Filename of the input device tree binary file
138 _valid_nodes: A list of Node object with compatible strings
139 _include_disabled: true to include nodes marked status = "disabled"
140 _outfile: The current output file (sys.stdout or a real file)
141 _lines: Stashed list of output lines for outputting in the future
143 def __init__(self, dtb_fname, include_disabled):
145 self._dtb_fname = dtb_fname
146 self._valid_nodes = None
147 self._include_disabled = include_disabled
152 def setup_output(self, fname):
153 """Set up the output destination
155 Once this is done, future calls to self.out() will output to this
159 fname: Filename to send output to, or '-' for stdout
162 self._outfile = sys.stdout
164 self._outfile = open(fname, 'w')
167 """Output a string to the output file
170 line: String to output
172 self._outfile.write(line)
175 """Buffer up a string to send later
178 line: String to add to our 'buffer' list
180 self._lines.append(line)
183 """Get the contents of the output buffer, and clear it
186 The output buffer, which is then cleared for future use
192 def out_header(self):
193 """Output a message indicating that this is an auto-generated file"""
197 * This file was generated by dtoc from a .dtb (device tree binary) file.
202 def get_phandle_argc(self, prop, node_name):
203 """Check if a node contains phandles
205 We have no reliable way of detecting whether a node uses a phandle
206 or not. As an interim measure, use a list of known property names.
209 prop: Prop object to check
211 Number of argument cells is this is a phandle, else None
213 if prop.name in ['clocks']:
214 if not isinstance(prop.value, list):
215 prop.value = [prop.value]
222 phandle = fdt_util.fdt32_to_cpu(val[i])
223 # If we get to the end of the list, stop. This can happen
224 # since some nodes have more phandles in the list than others,
225 # but we allocate enough space for the largest list. So those
226 # nodes with shorter lists end up with zeroes at the end.
229 target = self._fdt.phandle_to_node.get(phandle)
231 raise ValueError("Cannot parse '%s' in node '%s'" %
232 (prop.name, node_name))
233 prop_name = '#clock-cells'
234 cells = target.props.get(prop_name)
236 raise ValueError("Node '%s' has no '%s' property" %
237 (target.name, prop_name))
238 num_args = fdt_util.fdt32_to_cpu(cells.value)
239 max_args = max(max_args, num_args)
240 args.append(num_args)
242 return PhandleInfo(max_args, args)
246 """Scan the device tree to obtain a tree of nodes and properties
248 Once this is done, self._fdt.GetRoot() can be called to obtain the
249 device tree root node, and progress from there.
251 self._fdt = fdt.FdtScan(self._dtb_fname)
253 def scan_node(self, root):
254 """Scan a node and subnodes to build a tree of node and phandle info
256 This adds each node to self._valid_nodes.
259 root: Root node for scan
261 for node in root.subnodes:
262 if 'compatible' in node.props:
263 status = node.props.get('status')
264 if (not self._include_disabled and not status or
265 status.value != 'disabled'):
266 self._valid_nodes.append(node)
268 # recurse to handle any subnodes
272 """Scan the device tree for useful information
274 This fills in the following properties:
275 _valid_nodes: A list of nodes we wish to consider include in the
278 self._valid_nodes = []
279 return self.scan_node(self._fdt.GetRoot())
282 def get_num_cells(node):
283 """Get the number of cells in addresses and sizes for this node
290 Number of address cells for this node
291 Number of size cells for this node
296 na_prop = parent.props.get('#address-cells')
297 ns_prop = parent.props.get('#size-cells')
299 na = fdt_util.fdt32_to_cpu(na_prop.value)
301 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
304 def scan_reg_sizes(self):
305 """Scan for 64-bit 'reg' properties and update the values
307 This finds 'reg' properties with 64-bit data and converts the value to
308 an array of 64-values. This allows it to be output in a way that the
311 for node in self._valid_nodes:
312 reg = node.props.get('reg')
315 na, ns = self.get_num_cells(node)
318 if reg.type != fdt.TYPE_INT:
319 raise ValueError("Node '%s' reg property is not an int" %
321 if len(reg.value) % total:
322 raise ValueError("Node '%s' reg property has %d cells "
323 'which is not a multiple of na + ns = %d + %d)' %
324 (node.name, len(reg.value), na, ns))
327 if na != 1 or ns != 1:
328 reg.type = fdt.TYPE_INT64
332 if not isinstance(val, list):
335 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
337 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
339 new_value += [addr, size]
340 reg.value = new_value
342 def scan_structs(self):
343 """Scan the device tree building up the C structures we will use.
345 Build a dict keyed by C struct name containing a dict of Prop
346 object for each struct field (keyed by property name). Where the
347 same struct appears multiple times, try to use the 'widest'
348 property, i.e. the one with a type which can express all others.
350 Once the widest property is determined, all other properties are
351 updated to match that width.
354 for node in self._valid_nodes:
355 node_name, _ = get_compat_name(node)
358 # Get a list of all the valid properties in this node.
359 for name, prop in node.props.items():
360 if name not in PROP_IGNORE_LIST and name[0] != '#':
361 fields[name] = copy.deepcopy(prop)
363 # If we've seen this node_name before, update the existing struct.
364 if node_name in structs:
365 struct = structs[node_name]
366 for name, prop in fields.items():
367 oldprop = struct.get(name)
373 # Otherwise store this as a new struct.
375 structs[node_name] = fields
378 for node in self._valid_nodes:
379 node_name, _ = get_compat_name(node)
380 struct = structs[node_name]
381 for name, prop in node.props.items():
382 if name not in PROP_IGNORE_LIST and name[0] != '#':
383 prop.Widen(struct[name])
386 struct_name, aliases = get_compat_name(node)
387 for alias in aliases:
388 self._aliases[alias] = struct_name
392 def scan_phandles(self):
393 """Figure out what phandles each node uses
395 We need to be careful when outputing nodes that use phandles since
396 they must come after the declaration of the phandles in the C file.
397 Otherwise we get a compiler error since the phandle struct is not yet
400 This function adds to each node a list of phandle nodes that the node
401 depends on. This allows us to output things in the right order.
403 for node in self._valid_nodes:
404 node.phandles = set()
405 for pname, prop in node.props.items():
406 if pname in PROP_IGNORE_LIST or pname[0] == '#':
408 info = self.get_phandle_argc(prop, node.name)
410 # Process the list as pairs of (phandle, id)
412 for args in info.args:
413 phandle_cell = prop.value[pos]
414 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
415 target_node = self._fdt.phandle_to_node[phandle]
416 node.phandles.add(target_node)
420 def generate_structs(self, structs):
421 """Generate struct defintions for the platform data
423 This writes out the body of a header file consisting of structure
424 definitions for node in self._valid_nodes. See the documentation in
425 README.of-plat for more information.
428 self.out('#include <stdbool.h>\n')
429 self.out('#include <linux/libfdt.h>\n')
431 # Output the struct definition
432 for name in sorted(structs):
433 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
434 for pname in sorted(structs[name]):
435 prop = structs[name][pname]
436 info = self.get_phandle_argc(prop, structs[name])
438 # For phandles, include a reference to the target
439 struct_name = 'struct phandle_%d_arg' % info.max_args
440 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
441 conv_name_to_c(prop.name),
444 ptype = TYPE_NAMES[prop.type]
445 self.out('\t%s%s' % (tab_to(2, ptype),
446 conv_name_to_c(prop.name)))
447 if isinstance(prop.value, list):
448 self.out('[%d]' % len(prop.value))
452 for alias, struct_name in self._aliases.items():
453 if alias not in sorted(structs):
454 self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
455 STRUCT_PREFIX, struct_name))
457 def output_node(self, node):
458 """Output the C code for a node
463 struct_name, _ = get_compat_name(node)
464 var_name = conv_name_to_c(node.name)
465 self.buf('static const struct %s%s %s%s = {\n' %
466 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
467 for pname, prop in node.props.items():
468 if pname in PROP_IGNORE_LIST or pname[0] == '#':
470 member_name = conv_name_to_c(prop.name)
471 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
473 # Special handling for lists
474 if isinstance(prop.value, list):
477 # For phandles, output a reference to the platform data
478 # of the target node.
479 info = self.get_phandle_argc(prop, node.name)
481 # Process the list as pairs of (phandle, id)
483 for args in info.args:
484 phandle_cell = prop.value[pos]
485 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
486 target_node = self._fdt.phandle_to_node[phandle]
487 name = conv_name_to_c(target_node.name)
489 for i in range(args):
490 arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
492 vals.append('\t{&%s%s, {%s}}' % (VAL_PREFIX, name,
493 ', '.join(arg_values)))
495 self.buf('\n\t\t%s,' % val)
497 for val in prop.value:
498 vals.append(get_value(prop.type, val))
500 # Put 8 values per line to avoid very long lines.
501 for i in range(0, len(vals), 8):
504 self.buf(', '.join(vals[i:i + 8]))
507 self.buf(get_value(prop.type, prop.value))
511 # Add a device declaration
512 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
513 self.buf('\t.name\t\t= "%s",\n' % struct_name)
514 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
515 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
519 self.out(''.join(self.get_buf()))
521 def generate_tables(self):
522 """Generate device defintions for the platform data
524 This writes out C platform data initialisation data and
525 U_BOOT_DEVICE() declarations for each valid node. Where a node has
526 multiple compatible strings, a #define is used to make them equivalent.
528 See the documentation in doc/driver-model/of-plat.txt for more
532 self.out('#include <common.h>\n')
533 self.out('#include <dm.h>\n')
534 self.out('#include <dt-structs.h>\n')
536 nodes_to_output = list(self._valid_nodes)
538 # Keep outputing nodes until there is none left
539 while nodes_to_output:
540 node = nodes_to_output[0]
541 # Output all the node's dependencies first
542 for req_node in node.phandles:
543 if req_node in nodes_to_output:
544 self.output_node(req_node)
545 nodes_to_output.remove(req_node)
546 self.output_node(node)
547 nodes_to_output.remove(node)
550 def run_steps(args, dtb_file, include_disabled, output):
551 """Run all the steps of the dtoc tool
554 args: List of non-option arguments provided to the problem
555 dtb_file: Filename of dtb file to process
556 include_disabled: True to include disabled nodes
557 output: Name of output file
560 raise ValueError('Please specify a command: struct, platdata')
562 plat = DtbPlatdata(dtb_file, include_disabled)
565 plat.scan_reg_sizes()
566 plat.setup_output(output)
567 structs = plat.scan_structs()
570 for cmd in args[0].split(','):
572 plat.generate_structs(structs)
573 elif cmd == 'platdata':
574 plat.generate_tables()
576 raise ValueError("Unknown command '%s': (use: struct, platdata)" %