3 # Copyright (C) 2017 Google, Inc
4 # Written by Simon Glass <sjg@chromium.org>
6 # SPDX-License-Identifier: GPL-2.0+
9 """Device tree to platform data class
11 This supports converting device tree data to C structures definitions and
22 # When we see these properties we ignore them - i.e. do not create a structure member
31 'u-boot,dm-pre-reloc',
36 # C type declarations for the tyues we support
38 fdt.TYPE_INT: 'fdt32_t',
39 fdt.TYPE_BYTE: 'unsigned char',
40 fdt.TYPE_STRING: 'const char *',
41 fdt.TYPE_BOOL: 'bool',
42 fdt.TYPE_INT64: 'fdt64_t',
45 STRUCT_PREFIX = 'dtd_'
48 # This holds information about a property which includes phandles.
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.
53 PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
56 def conv_name_to_c(name):
57 """Convert a device-tree name to a C identifier
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).
65 String containing the C version of this name
67 new = name.replace('@', '_at_')
68 new = new.replace('-', '_')
69 new = new.replace(',', '_')
70 new = new.replace('.', '_')
73 def tab_to(num_tabs, line):
74 """Append tabs to a line of text to reach a tab stop.
77 num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
78 line: Line of text to append to
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.
84 if len(line) >= num_tabs * 8:
86 return line + '\t' * (num_tabs - len(line) // 8)
88 def get_value(ftype, value):
89 """Get a value as a C expression
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'
97 type: Data type (fdt_util)
98 value: Data value, as a string of bytes
100 if ftype == fdt.TYPE_INT:
101 return '%#x' % fdt_util.fdt32_to_cpu(value)
102 elif ftype == fdt.TYPE_BYTE:
103 return '%#x' % ord(value[0])
104 elif ftype == fdt.TYPE_STRING:
105 return '"%s"' % value
106 elif ftype == fdt.TYPE_BOOL:
108 elif ftype == fdt.TYPE_INT64:
111 def get_compat_name(node):
112 """Get a node's first compatible string as a C identifier
115 node: Node object to check
118 C identifier for the first compatible string
119 List of C identifiers for all the other compatible strings
122 compat = node.props['compatible'].value
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]
129 class DtbPlatdata(object):
130 """Provide a means to convert device tree binary data to platform data
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.
137 _fdt: Fdt object, referencing the device tree
138 _dtb_fname: Filename of the input device tree binary file
139 _valid_nodes: A list of Node object with compatible strings
140 _include_disabled: true to include nodes marked status = "disabled"
141 _outfile: The current output file (sys.stdout or a real file)
142 _lines: Stashed list of output lines for outputting in the future
144 def __init__(self, dtb_fname, include_disabled):
146 self._dtb_fname = dtb_fname
147 self._valid_nodes = None
148 self._include_disabled = include_disabled
153 def setup_output(self, fname):
154 """Set up the output destination
156 Once this is done, future calls to self.out() will output to this
160 fname: Filename to send output to, or '-' for stdout
163 self._outfile = sys.stdout
165 self._outfile = open(fname, 'w')
168 """Output a string to the output file
171 line: String to output
173 self._outfile.write(line)
176 """Buffer up a string to send later
179 line: String to add to our 'buffer' list
181 self._lines.append(line)
184 """Get the contents of the output buffer, and clear it
187 The output buffer, which is then cleared for future use
193 def out_header(self):
194 """Output a message indicating that this is an auto-generated file"""
198 * This file was generated by dtoc from a .dtb (device tree binary) file.
203 def get_phandle_argc(self, prop, node_name):
204 """Check if a node contains phandles
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.
210 prop: Prop object to check
212 Number of argument cells is this is a phandle, else None
214 if prop.name in ['clocks']:
216 if not isinstance(val, list):
223 phandle = fdt_util.fdt32_to_cpu(val[i])
224 target = self._fdt.phandle_to_node.get(phandle)
226 raise ValueError("Cannot parse '%s' in node '%s'" %
227 (prop.name, node_name))
228 prop_name = '#clock-cells'
229 cells = target.props.get(prop_name)
231 raise ValueError("Node '%s' has no '%s' property" %
232 (target.name, prop_name))
233 num_args = fdt_util.fdt32_to_cpu(cells.value)
234 max_args = max(max_args, num_args)
235 args.append(num_args)
237 return PhandleInfo(max_args, args)
241 """Scan the device tree to obtain a tree of nodes and properties
243 Once this is done, self._fdt.GetRoot() can be called to obtain the
244 device tree root node, and progress from there.
246 self._fdt = fdt.FdtScan(self._dtb_fname)
248 def scan_node(self, root):
249 """Scan a node and subnodes to build a tree of node and phandle info
251 This adds each node to self._valid_nodes.
254 root: Root node for scan
256 for node in root.subnodes:
257 if 'compatible' in node.props:
258 status = node.props.get('status')
259 if (not self._include_disabled and not status or
260 status.value != 'disabled'):
261 self._valid_nodes.append(node)
263 # recurse to handle any subnodes
267 """Scan the device tree for useful information
269 This fills in the following properties:
270 _valid_nodes: A list of nodes we wish to consider include in the
273 self._valid_nodes = []
274 return self.scan_node(self._fdt.GetRoot())
277 def get_num_cells(node):
278 """Get the number of cells in addresses and sizes for this node
285 Number of address cells for this node
286 Number of size cells for this node
291 na_prop = parent.props.get('#address-cells')
292 ns_prop = parent.props.get('#size-cells')
294 na = fdt_util.fdt32_to_cpu(na_prop.value)
296 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
299 def scan_reg_sizes(self):
300 """Scan for 64-bit 'reg' properties and update the values
302 This finds 'reg' properties with 64-bit data and converts the value to
303 an array of 64-values. This allows it to be output in a way that the
306 for node in self._valid_nodes:
307 reg = node.props.get('reg')
310 na, ns = self.get_num_cells(node)
313 if reg.type != fdt.TYPE_INT:
314 raise ValueError("Node '%s' reg property is not an int")
315 if len(reg.value) % total:
316 raise ValueError("Node '%s' reg property has %d cells "
317 'which is not a multiple of na + ns = %d + %d)' %
318 (node.name, len(reg.value), na, ns))
321 if na != 1 or ns != 1:
322 reg.type = fdt.TYPE_INT64
326 if not isinstance(val, list):
329 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
331 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
333 new_value += [addr, size]
334 reg.value = new_value
336 def scan_structs(self):
337 """Scan the device tree building up the C structures we will use.
339 Build a dict keyed by C struct name containing a dict of Prop
340 object for each struct field (keyed by property name). Where the
341 same struct appears multiple times, try to use the 'widest'
342 property, i.e. the one with a type which can express all others.
344 Once the widest property is determined, all other properties are
345 updated to match that width.
348 for node in self._valid_nodes:
349 node_name, _ = get_compat_name(node)
352 # Get a list of all the valid properties in this node.
353 for name, prop in node.props.items():
354 if name not in PROP_IGNORE_LIST and name[0] != '#':
355 fields[name] = copy.deepcopy(prop)
357 # If we've seen this node_name before, update the existing struct.
358 if node_name in structs:
359 struct = structs[node_name]
360 for name, prop in fields.items():
361 oldprop = struct.get(name)
367 # Otherwise store this as a new struct.
369 structs[node_name] = fields
372 for node in self._valid_nodes:
373 node_name, _ = get_compat_name(node)
374 struct = structs[node_name]
375 for name, prop in node.props.items():
376 if name not in PROP_IGNORE_LIST and name[0] != '#':
377 prop.Widen(struct[name])
380 struct_name, aliases = get_compat_name(node)
381 for alias in aliases:
382 self._aliases[alias] = struct_name
386 def scan_phandles(self):
387 """Figure out what phandles each node uses
389 We need to be careful when outputing nodes that use phandles since
390 they must come after the declaration of the phandles in the C file.
391 Otherwise we get a compiler error since the phandle struct is not yet
394 This function adds to each node a list of phandle nodes that the node
395 depends on. This allows us to output things in the right order.
397 for node in self._valid_nodes:
398 node.phandles = set()
399 for pname, prop in node.props.items():
400 if pname in PROP_IGNORE_LIST or pname[0] == '#':
402 info = self.get_phandle_argc(prop, node.name)
404 if not isinstance(prop.value, list):
405 prop.value = [prop.value]
406 # Process the list as pairs of (phandle, id)
408 for args in info.args:
409 phandle_cell = prop.value[pos]
410 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
411 target_node = self._fdt.phandle_to_node[phandle]
412 node.phandles.add(target_node)
416 def generate_structs(self, structs):
417 """Generate struct defintions for the platform data
419 This writes out the body of a header file consisting of structure
420 definitions for node in self._valid_nodes. See the documentation in
421 README.of-plat for more information.
424 self.out('#include <stdbool.h>\n')
425 self.out('#include <linux/libfdt.h>\n')
427 # Output the struct definition
428 for name in sorted(structs):
429 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
430 for pname in sorted(structs[name]):
431 prop = structs[name][pname]
432 info = self.get_phandle_argc(prop, structs[name])
434 # For phandles, include a reference to the target
435 struct_name = 'struct phandle_%d_arg' % info.max_args
436 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
437 conv_name_to_c(prop.name),
440 ptype = TYPE_NAMES[prop.type]
441 self.out('\t%s%s' % (tab_to(2, ptype),
442 conv_name_to_c(prop.name)))
443 if isinstance(prop.value, list):
444 self.out('[%d]' % len(prop.value))
448 for alias, struct_name in self._aliases.iteritems():
449 self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
450 STRUCT_PREFIX, struct_name))
452 def output_node(self, node):
453 """Output the C code for a node
458 struct_name, _ = get_compat_name(node)
459 var_name = conv_name_to_c(node.name)
460 self.buf('static struct %s%s %s%s = {\n' %
461 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
462 for pname, prop in node.props.items():
463 if pname in PROP_IGNORE_LIST or pname[0] == '#':
465 member_name = conv_name_to_c(prop.name)
466 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
468 # Special handling for lists
469 if isinstance(prop.value, list):
472 # For phandles, output a reference to the platform data
473 # of the target node.
474 info = self.get_phandle_argc(prop, node.name)
476 # Process the list as pairs of (phandle, id)
478 for args in info.args:
479 phandle_cell = prop.value[pos]
480 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
481 target_node = self._fdt.phandle_to_node[phandle]
482 name = conv_name_to_c(target_node.name)
484 for i in range(args):
485 arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
487 vals.append('\t{&%s%s, {%s}}' % (VAL_PREFIX, name,
488 ', '.join(arg_values)))
490 self.buf('\n\t\t%s,' % val)
492 for val in prop.value:
493 vals.append(get_value(prop.type, val))
495 # Put 8 values per line to avoid very long lines.
496 for i in xrange(0, len(vals), 8):
499 self.buf(', '.join(vals[i:i + 8]))
502 self.buf(get_value(prop.type, prop.value))
506 # Add a device declaration
507 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
508 self.buf('\t.name\t\t= "%s",\n' % struct_name)
509 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
510 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
514 self.out(''.join(self.get_buf()))
516 def generate_tables(self):
517 """Generate device defintions for the platform data
519 This writes out C platform data initialisation data and
520 U_BOOT_DEVICE() declarations for each valid node. Where a node has
521 multiple compatible strings, a #define is used to make them equivalent.
523 See the documentation in doc/driver-model/of-plat.txt for more
527 self.out('#include <common.h>\n')
528 self.out('#include <dm.h>\n')
529 self.out('#include <dt-structs.h>\n')
531 nodes_to_output = list(self._valid_nodes)
533 # Keep outputing nodes until there is none left
534 while nodes_to_output:
535 node = nodes_to_output[0]
536 # Output all the node's dependencies first
537 for req_node in node.phandles:
538 if req_node in nodes_to_output:
539 self.output_node(req_node)
540 nodes_to_output.remove(req_node)
541 self.output_node(node)
542 nodes_to_output.remove(node)
545 def run_steps(args, dtb_file, include_disabled, output):
546 """Run all the steps of the dtoc tool
549 args: List of non-option arguments provided to the problem
550 dtb_file: Filename of dtb file to process
551 include_disabled: True to include disabled nodes
552 output: Name of output file
555 raise ValueError('Please specify a command: struct, platdata')
557 plat = DtbPlatdata(dtb_file, include_disabled)
560 plat.scan_reg_sizes()
561 plat.setup_output(output)
562 structs = plat.scan_structs()
565 for cmd in args[0].split(','):
567 plat.generate_structs(structs)
568 elif cmd == 'platdata':
569 plat.generate_tables()
571 raise ValueError("Unknown command '%s': (use: struct, platdata)" %