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