libfdt: move headers to <linux/libfdt.h> and <linux/libfdt_env.h>
[oweals/u-boot.git] / tools / dtoc / dtb_platdata.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2017 Google, Inc
4 # Written by Simon Glass <sjg@chromium.org>
5 #
6 # SPDX-License-Identifier:      GPL-2.0+
7 #
8
9 """Device tree to platform data class
10
11 This supports converting device tree data to C structures definitions and
12 static data.
13 """
14
15 import collections
16 import copy
17 import sys
18
19 import fdt
20 import fdt_util
21
22 # When we see these properties we ignore them - i.e. do not create a structure member
23 PROP_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
37 TYPE_NAMES = {
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',
43 }
44
45 STRUCT_PREFIX = 'dtd_'
46 VAL_PREFIX = 'dtv_'
47
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.
53 PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
54
55
56 def conv_name_to_c(name):
57     """Convert a device-tree name to a C identifier
58
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
62     Args:
63         name:   Name to convert
64     Return:
65         String containing the C version of this name
66     """
67     new = name.replace('@', '_at_')
68     new = new.replace('-', '_')
69     new = new.replace(',', '_')
70     new = new.replace('.', '_')
71     return new
72
73 def 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
88 def 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:
103         return '%#x' % ord(value[0])
104     elif ftype == fdt.TYPE_STRING:
105         return '"%s"' % value
106     elif ftype == fdt.TYPE_BOOL:
107         return 'true'
108     elif ftype == fdt.TYPE_INT64:
109         return '%#x' % value
110
111 def 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
128
129 class DtbPlatdata(object):
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:
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
143     """
144     def __init__(self, dtb_fname, include_disabled):
145         self._fdt = None
146         self._dtb_fname = dtb_fname
147         self._valid_nodes = None
148         self._include_disabled = include_disabled
149         self._outfile = None
150         self._lines = []
151         self._aliases = {}
152
153     def setup_output(self, fname):
154         """Set up the output destination
155
156         Once this is done, future calls to self.out() will output to this
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
167     def out(self, line):
168         """Output a string to the output file
169
170         Args:
171             line: String to output
172         """
173         self._outfile.write(line)
174
175     def buf(self, line):
176         """Buffer up a string to send later
177
178         Args:
179             line: String to add to our 'buffer' list
180         """
181         self._lines.append(line)
182
183     def get_buf(self):
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
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
203     def get_phandle_argc(self, prop, node_name):
204         """Check if a node contains phandles
205
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.
208
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']:
215             val = prop.value
216             if not isinstance(val, list):
217                 val = [val]
218             i = 0
219
220             max_args = 0
221             args = []
222             while i < len(val):
223                 phandle = fdt_util.fdt32_to_cpu(val[i])
224                 target = self._fdt.phandle_to_node.get(phandle)
225                 if not target:
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)
230                 if not cells:
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)
236                 i += 1 + num_args
237             return PhandleInfo(max_args, args)
238         return None
239
240     def scan_dtb(self):
241         """Scan the device tree to obtain a tree of nodes and properties
242
243         Once this is done, self._fdt.GetRoot() can be called to obtain the
244         device tree root node, and progress from there.
245         """
246         self._fdt = fdt.FdtScan(self._dtb_fname)
247
248     def scan_node(self, root):
249         """Scan a node and subnodes to build a tree of node and phandle info
250
251         This adds each node to self._valid_nodes.
252
253         Args:
254             root: Root node for scan
255         """
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)
262
263             # recurse to handle any subnodes
264             self.scan_node(node)
265
266     def scan_tree(self):
267         """Scan the device tree for useful information
268
269         This fills in the following properties:
270             _valid_nodes: A list of nodes we wish to consider include in the
271                 platform data
272         """
273         self._valid_nodes = []
274         return self.scan_node(self._fdt.GetRoot())
275
276     @staticmethod
277     def get_num_cells(node):
278         """Get the number of cells in addresses and sizes for this node
279
280         Args:
281             node: Node to check
282
283         Returns:
284             Tuple:
285                 Number of address cells for this node
286                 Number of size cells for this node
287         """
288         parent = node.parent
289         na, ns = 2, 2
290         if parent:
291             na_prop = parent.props.get('#address-cells')
292             ns_prop = parent.props.get('#size-cells')
293             if na_prop:
294                 na = fdt_util.fdt32_to_cpu(na_prop.value)
295             if ns_prop:
296                 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
297         return na, ns
298
299     def scan_reg_sizes(self):
300         """Scan for 64-bit 'reg' properties and update the values
301
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
304         C code can read.
305         """
306         for node in self._valid_nodes:
307             reg = node.props.get('reg')
308             if not reg:
309                 continue
310             na, ns = self.get_num_cells(node)
311             total = na + ns
312
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))
319             reg.na = na
320             reg.ns = ns
321             if na != 1 or ns != 1:
322                 reg.type = fdt.TYPE_INT64
323                 i = 0
324                 new_value = []
325                 val = reg.value
326                 if not isinstance(val, list):
327                     val = [val]
328                 while i < len(val):
329                     addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
330                     i += na
331                     size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
332                     i += ns
333                     new_value += [addr, size]
334                 reg.value = new_value
335
336     def scan_structs(self):
337         """Scan the device tree building up the C structures we will use.
338
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.
343
344         Once the widest property is determined, all other properties are
345         updated to match that width.
346         """
347         structs = {}
348         for node in self._valid_nodes:
349             node_name, _ = get_compat_name(node)
350             fields = {}
351
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)
356
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)
362                     if oldprop:
363                         oldprop.Widen(prop)
364                     else:
365                         struct[name] = prop
366
367             # Otherwise store this as a new struct.
368             else:
369                 structs[node_name] = fields
370
371         upto = 0
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])
378             upto += 1
379
380             struct_name, aliases = get_compat_name(node)
381             for alias in aliases:
382                 self._aliases[alias] = struct_name
383
384         return structs
385
386     def scan_phandles(self):
387         """Figure out what phandles each node uses
388
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
392         declared.
393
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.
396         """
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] == '#':
401                     continue
402                 info = self.get_phandle_argc(prop, node.name)
403                 if info:
404                     if not isinstance(prop.value, list):
405                         prop.value = [prop.value]
406                     # Process the list as pairs of (phandle, id)
407                     pos = 0
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)
413                         pos += 1 + args
414
415
416     def generate_structs(self, structs):
417         """Generate struct defintions for the platform data
418
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.
422         """
423         self.out_header()
424         self.out('#include <stdbool.h>\n')
425         self.out('#include <linux/libfdt.h>\n')
426
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])
433                 if info:
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),
438                                              len(info.args)))
439                 else:
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))
445                 self.out(';\n')
446             self.out('};\n')
447
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))
451
452     def output_node(self, node):
453         """Output the C code for a node
454
455         Args:
456             node: node to output
457         """
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] == '#':
464                 continue
465             member_name = conv_name_to_c(prop.name)
466             self.buf('\t%s= ' % tab_to(3, '.' + member_name))
467
468             # Special handling for lists
469             if isinstance(prop.value, list):
470                 self.buf('{')
471                 vals = []
472                 # For phandles, output a reference to the platform data
473                 # of the target node.
474                 info = self.get_phandle_argc(prop, node.name)
475                 if info:
476                     # Process the list as pairs of (phandle, id)
477                     pos = 0
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)
483                         arg_values = []
484                         for i in range(args):
485                             arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
486                         pos += 1 + args
487                         vals.append('\t{&%s%s, {%s}}' % (VAL_PREFIX, name,
488                                                      ', '.join(arg_values)))
489                     for val in vals:
490                         self.buf('\n\t\t%s,' % val)
491                 else:
492                     for val in prop.value:
493                         vals.append(get_value(prop.type, val))
494
495                     # Put 8 values per line to avoid very long lines.
496                     for i in xrange(0, len(vals), 8):
497                         if i:
498                             self.buf(',\n\t\t')
499                         self.buf(', '.join(vals[i:i + 8]))
500                 self.buf('}')
501             else:
502                 self.buf(get_value(prop.type, prop.value))
503             self.buf(',\n')
504         self.buf('};\n')
505
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))
511         self.buf('};\n')
512         self.buf('\n')
513
514         self.out(''.join(self.get_buf()))
515
516     def generate_tables(self):
517         """Generate device defintions for the platform data
518
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.
522
523         See the documentation in doc/driver-model/of-plat.txt for more
524         information.
525         """
526         self.out_header()
527         self.out('#include <common.h>\n')
528         self.out('#include <dm.h>\n')
529         self.out('#include <dt-structs.h>\n')
530         self.out('\n')
531         nodes_to_output = list(self._valid_nodes)
532
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)
543
544
545 def run_steps(args, dtb_file, include_disabled, output):
546     """Run all the steps of the dtoc tool
547
548     Args:
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
553     """
554     if not args:
555         raise ValueError('Please specify a command: struct, platdata')
556
557     plat = DtbPlatdata(dtb_file, include_disabled)
558     plat.scan_dtb()
559     plat.scan_tree()
560     plat.scan_reg_sizes()
561     plat.setup_output(output)
562     structs = plat.scan_structs()
563     plat.scan_phandles()
564
565     for cmd in args[0].split(','):
566         if cmd == 'struct':
567             plat.generate_structs(structs)
568         elif cmd == 'platdata':
569             plat.generate_tables()
570         else:
571             raise ValueError("Unknown command '%s': (use: struct, platdata)" %
572                              cmd)