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
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 def conv_name_to_c(name):
48 """Convert a device-tree name to a C identifier
50 This uses multiple replace() calls instead of re.sub() since it is faster
51 (400ms for 1m calls versus 1000ms for the 're' version).
56 String containing the C version of this name
58 new = name.replace('@', '_at_')
59 new = new.replace('-', '_')
60 new = new.replace(',', '_')
61 new = new.replace('.', '_')
64 def tab_to(num_tabs, line):
65 """Append tabs to a line of text to reach a tab stop.
68 num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
69 line: Line of text to append to
72 line with the correct number of tabs appeneded. If the line already
73 extends past that tab stop then a single space is appended.
75 if len(line) >= num_tabs * 8:
77 return line + '\t' * (num_tabs - len(line) // 8)
79 def get_value(ftype, value):
80 """Get a value as a C expression
82 For integers this returns a byte-swapped (little-endian) hex string
83 For bytes this returns a hex string, e.g. 0x12
84 For strings this returns a literal string enclosed in quotes
85 For booleans this return 'true'
88 type: Data type (fdt_util)
89 value: Data value, as a string of bytes
91 if ftype == fdt.TYPE_INT:
92 return '%#x' % fdt_util.fdt32_to_cpu(value)
93 elif ftype == fdt.TYPE_BYTE:
94 return '%#x' % ord(value[0])
95 elif ftype == fdt.TYPE_STRING:
97 elif ftype == fdt.TYPE_BOOL:
99 elif ftype == fdt.TYPE_INT64:
102 def get_compat_name(node):
103 """Get a node's first compatible string as a C identifier
106 node: Node object to check
109 C identifier for the first compatible string
110 List of C identifiers for all the other compatible strings
113 compat = node.props['compatible'].value
115 if isinstance(compat, list):
116 compat, aliases = compat[0], compat[1:]
117 return conv_name_to_c(compat), [conv_name_to_c(a) for a in aliases]
119 def is_phandle(prop):
120 """Check if a node contains phandles
122 We have no reliable way of detecting whether a node uses a phandle
123 or not. As an interim measure, use a list of known property names.
126 prop: Prop object to check
128 True if the object value contains phandles, else False
130 if prop.name in ['clocks']:
135 class DtbPlatdata(object):
136 """Provide a means to convert device tree binary data to platform data
138 The output of this process is C structures which can be used in space-
139 constrained encvironments where the ~3KB code overhead of device tree
140 code is not affordable.
143 _fdt: Fdt object, referencing the device tree
144 _dtb_fname: Filename of the input device tree binary file
145 _valid_nodes: A list of Node object with compatible strings
146 _include_disabled: true to include nodes marked status = "disabled"
147 _phandle_nodes: A dict of nodes indexed by phandle number (1, 2...)
148 _outfile: The current output file (sys.stdout or a real file)
149 _lines: Stashed list of output lines for outputting in the future
150 _phandle_nodes: A dict of Nodes indexed by phandle (an integer)
152 def __init__(self, dtb_fname, include_disabled):
154 self._dtb_fname = dtb_fname
155 self._valid_nodes = None
156 self._include_disabled = include_disabled
157 self._phandle_nodes = {}
162 def setup_output(self, fname):
163 """Set up the output destination
165 Once this is done, future calls to self.out() will output to this
169 fname: Filename to send output to, or '-' for stdout
172 self._outfile = sys.stdout
174 self._outfile = open(fname, 'w')
177 """Output a string to the output file
180 line: String to output
182 self._outfile.write(line)
185 """Buffer up a string to send later
188 line: String to add to our 'buffer' list
190 self._lines.append(line)
193 """Get the contents of the output buffer, and clear it
196 The output buffer, which is then cleared for future use
203 """Scan the device tree to obtain a tree of nodes and properties
205 Once this is done, self._fdt.GetRoot() can be called to obtain the
206 device tree root node, and progress from there.
208 self._fdt = fdt.FdtScan(self._dtb_fname)
210 def scan_node(self, root):
211 """Scan a node and subnodes to build a tree of node and phandle info
213 This adds each node to self._valid_nodes and each phandle to
217 root: Root node for scan
219 for node in root.subnodes:
220 if 'compatible' in node.props:
221 status = node.props.get('status')
222 if (not self._include_disabled and not status or
223 status.value != 'disabled'):
224 self._valid_nodes.append(node)
225 phandle_prop = node.props.get('phandle')
227 phandle = phandle_prop.GetPhandle()
228 self._phandle_nodes[phandle] = node
230 # recurse to handle any subnodes
234 """Scan the device tree for useful information
236 This fills in the following properties:
237 _phandle_nodes: A dict of Nodes indexed by phandle (an integer)
238 _valid_nodes: A list of nodes we wish to consider include in the
241 self._phandle_nodes = {}
242 self._valid_nodes = []
243 return self.scan_node(self._fdt.GetRoot())
246 def get_num_cells(node):
247 """Get the number of cells in addresses and sizes for this node
254 Number of address cells for this node
255 Number of size cells for this node
260 na_prop = parent.props.get('#address-cells')
261 ns_prop = parent.props.get('#size-cells')
263 na = fdt_util.fdt32_to_cpu(na_prop.value)
265 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
268 def scan_reg_sizes(self):
269 """Scan for 64-bit 'reg' properties and update the values
271 This finds 'reg' properties with 64-bit data and converts the value to
272 an array of 64-values. This allows it to be output in a way that the
275 for node in self._valid_nodes:
276 reg = node.props.get('reg')
279 na, ns = self.get_num_cells(node)
282 if reg.type != fdt.TYPE_INT:
283 raise ValueError("Node '%s' reg property is not an int")
284 if len(reg.value) % total:
285 raise ValueError("Node '%s' reg property has %d cells "
286 'which is not a multiple of na + ns = %d + %d)' %
287 (node.name, len(reg.value), na, ns))
290 if na != 1 or ns != 1:
291 reg.type = fdt.TYPE_INT64
295 if not isinstance(val, list):
298 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
300 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
302 new_value += [addr, size]
303 reg.value = new_value
305 def scan_structs(self):
306 """Scan the device tree building up the C structures we will use.
308 Build a dict keyed by C struct name containing a dict of Prop
309 object for each struct field (keyed by property name). Where the
310 same struct appears multiple times, try to use the 'widest'
311 property, i.e. the one with a type which can express all others.
313 Once the widest property is determined, all other properties are
314 updated to match that width.
317 for node in self._valid_nodes:
318 node_name, _ = get_compat_name(node)
321 # Get a list of all the valid properties in this node.
322 for name, prop in node.props.items():
323 if name not in PROP_IGNORE_LIST and name[0] != '#':
324 fields[name] = copy.deepcopy(prop)
326 # If we've seen this node_name before, update the existing struct.
327 if node_name in structs:
328 struct = structs[node_name]
329 for name, prop in fields.items():
330 oldprop = struct.get(name)
336 # Otherwise store this as a new struct.
338 structs[node_name] = fields
341 for node in self._valid_nodes:
342 node_name, _ = get_compat_name(node)
343 struct = structs[node_name]
344 for name, prop in node.props.items():
345 if name not in PROP_IGNORE_LIST and name[0] != '#':
346 prop.Widen(struct[name])
349 struct_name, aliases = get_compat_name(node)
350 for alias in aliases:
351 self._aliases[alias] = struct_name
355 def scan_phandles(self):
356 """Figure out what phandles each node uses
358 We need to be careful when outputing nodes that use phandles since
359 they must come after the declaration of the phandles in the C file.
360 Otherwise we get a compiler error since the phandle struct is not yet
363 This function adds to each node a list of phandle nodes that the node
364 depends on. This allows us to output things in the right order.
366 for node in self._valid_nodes:
367 node.phandles = set()
368 for pname, prop in node.props.items():
369 if pname in PROP_IGNORE_LIST or pname[0] == '#':
371 if isinstance(prop.value, list):
373 # Process the list as pairs of (phandle, id)
374 value_it = iter(prop.value)
375 for phandle_cell, _ in zip(value_it, value_it):
376 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
377 target_node = self._phandle_nodes[phandle]
378 node.phandles.add(target_node)
381 def generate_structs(self, structs):
382 """Generate struct defintions for the platform data
384 This writes out the body of a header file consisting of structure
385 definitions for node in self._valid_nodes. See the documentation in
386 README.of-plat for more information.
388 self.out('#include <stdbool.h>\n')
389 self.out('#include <libfdt.h>\n')
391 # Output the struct definition
392 for name in sorted(structs):
393 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
394 for pname in sorted(structs[name]):
395 prop = structs[name][pname]
397 # For phandles, include a reference to the target
398 self.out('\t%s%s[%d]' % (tab_to(2, 'struct phandle_2_cell'),
399 conv_name_to_c(prop.name),
400 len(prop.value) / 2))
402 ptype = TYPE_NAMES[prop.type]
403 self.out('\t%s%s' % (tab_to(2, ptype),
404 conv_name_to_c(prop.name)))
405 if isinstance(prop.value, list):
406 self.out('[%d]' % len(prop.value))
410 for alias, struct_name in self._aliases.iteritems():
411 self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
412 STRUCT_PREFIX, struct_name))
414 def output_node(self, node):
415 """Output the C code for a node
420 struct_name, _ = get_compat_name(node)
421 var_name = conv_name_to_c(node.name)
422 self.buf('static struct %s%s %s%s = {\n' %
423 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
424 for pname, prop in node.props.items():
425 if pname in PROP_IGNORE_LIST or pname[0] == '#':
427 member_name = conv_name_to_c(prop.name)
428 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
430 # Special handling for lists
431 if isinstance(prop.value, list):
434 # For phandles, output a reference to the platform data
435 # of the target node.
437 # Process the list as pairs of (phandle, id)
438 value_it = iter(prop.value)
439 for phandle_cell, id_cell in zip(value_it, value_it):
440 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
441 id_num = fdt_util.fdt32_to_cpu(id_cell)
442 target_node = self._phandle_nodes[phandle]
443 name = conv_name_to_c(target_node.name)
444 vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id_num))
446 for val in prop.value:
447 vals.append(get_value(prop.type, val))
449 # Put 8 values per line to avoid very long lines.
450 for i in xrange(0, len(vals), 8):
453 self.buf(', '.join(vals[i:i + 8]))
456 self.buf(get_value(prop.type, prop.value))
460 # Add a device declaration
461 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
462 self.buf('\t.name\t\t= "%s",\n' % struct_name)
463 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
464 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
468 self.out(''.join(self.get_buf()))
470 def generate_tables(self):
471 """Generate device defintions for the platform data
473 This writes out C platform data initialisation data and
474 U_BOOT_DEVICE() declarations for each valid node. Where a node has
475 multiple compatible strings, a #define is used to make them equivalent.
477 See the documentation in doc/driver-model/of-plat.txt for more
480 self.out('#include <common.h>\n')
481 self.out('#include <dm.h>\n')
482 self.out('#include <dt-structs.h>\n')
484 nodes_to_output = list(self._valid_nodes)
486 # Keep outputing nodes until there is none left
487 while nodes_to_output:
488 node = nodes_to_output[0]
489 # Output all the node's dependencies first
490 for req_node in node.phandles:
491 if req_node in nodes_to_output:
492 self.output_node(req_node)
493 nodes_to_output.remove(req_node)
494 self.output_node(node)
495 nodes_to_output.remove(node)
498 def run_steps(args, dtb_file, include_disabled, output):
499 """Run all the steps of the dtoc tool
502 args: List of non-option arguments provided to the problem
503 dtb_file: Filename of dtb file to process
504 include_disabled: True to include disabled nodes
505 output: Name of output file
508 raise ValueError('Please specify a command: struct, platdata')
510 plat = DtbPlatdata(dtb_file, include_disabled)
513 plat.scan_reg_sizes()
514 plat.setup_output(output)
515 structs = plat.scan_structs()
518 for cmd in args[0].split(','):
520 plat.generate_structs(structs)
521 elif cmd == 'platdata':
522 plat.generate_tables()
524 raise ValueError("Unknown command '%s': (use: struct, platdata)" %