dtoc: Fix properties with a single zero-arg phandle
[oweals/u-boot.git] / 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             if not isinstance(prop.value, list):
215                 prop.value = [prop.value]
216             val = prop.value
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                 # 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.
227                 if not phandle:
228                     break
229                 target = self._fdt.phandle_to_node.get(phandle)
230                 if not target:
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)
235                 if not cells:
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)
241                 i += 1 + num_args
242             return PhandleInfo(max_args, args)
243         return None
244
245     def scan_dtb(self):
246         """Scan the device tree to obtain a tree of nodes and properties
247
248         Once this is done, self._fdt.GetRoot() can be called to obtain the
249         device tree root node, and progress from there.
250         """
251         self._fdt = fdt.FdtScan(self._dtb_fname)
252
253     def scan_node(self, root):
254         """Scan a node and subnodes to build a tree of node and phandle info
255
256         This adds each node to self._valid_nodes.
257
258         Args:
259             root: Root node for scan
260         """
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)
267
268             # recurse to handle any subnodes
269             self.scan_node(node)
270
271     def scan_tree(self):
272         """Scan the device tree for useful information
273
274         This fills in the following properties:
275             _valid_nodes: A list of nodes we wish to consider include in the
276                 platform data
277         """
278         self._valid_nodes = []
279         return self.scan_node(self._fdt.GetRoot())
280
281     @staticmethod
282     def get_num_cells(node):
283         """Get the number of cells in addresses and sizes for this node
284
285         Args:
286             node: Node to check
287
288         Returns:
289             Tuple:
290                 Number of address cells for this node
291                 Number of size cells for this node
292         """
293         parent = node.parent
294         na, ns = 2, 2
295         if parent:
296             na_prop = parent.props.get('#address-cells')
297             ns_prop = parent.props.get('#size-cells')
298             if na_prop:
299                 na = fdt_util.fdt32_to_cpu(na_prop.value)
300             if ns_prop:
301                 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
302         return na, ns
303
304     def scan_reg_sizes(self):
305         """Scan for 64-bit 'reg' properties and update the values
306
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
309         C code can read.
310         """
311         for node in self._valid_nodes:
312             reg = node.props.get('reg')
313             if not reg:
314                 continue
315             na, ns = self.get_num_cells(node)
316             total = na + ns
317
318             if reg.type != fdt.TYPE_INT:
319                 raise ValueError("Node '%s' reg property is not an int")
320             if len(reg.value) % total:
321                 raise ValueError("Node '%s' reg property has %d cells "
322                         'which is not a multiple of na + ns = %d + %d)' %
323                         (node.name, len(reg.value), na, ns))
324             reg.na = na
325             reg.ns = ns
326             if na != 1 or ns != 1:
327                 reg.type = fdt.TYPE_INT64
328                 i = 0
329                 new_value = []
330                 val = reg.value
331                 if not isinstance(val, list):
332                     val = [val]
333                 while i < len(val):
334                     addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
335                     i += na
336                     size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
337                     i += ns
338                     new_value += [addr, size]
339                 reg.value = new_value
340
341     def scan_structs(self):
342         """Scan the device tree building up the C structures we will use.
343
344         Build a dict keyed by C struct name containing a dict of Prop
345         object for each struct field (keyed by property name). Where the
346         same struct appears multiple times, try to use the 'widest'
347         property, i.e. the one with a type which can express all others.
348
349         Once the widest property is determined, all other properties are
350         updated to match that width.
351         """
352         structs = {}
353         for node in self._valid_nodes:
354             node_name, _ = get_compat_name(node)
355             fields = {}
356
357             # Get a list of all the valid properties in this node.
358             for name, prop in node.props.items():
359                 if name not in PROP_IGNORE_LIST and name[0] != '#':
360                     fields[name] = copy.deepcopy(prop)
361
362             # If we've seen this node_name before, update the existing struct.
363             if node_name in structs:
364                 struct = structs[node_name]
365                 for name, prop in fields.items():
366                     oldprop = struct.get(name)
367                     if oldprop:
368                         oldprop.Widen(prop)
369                     else:
370                         struct[name] = prop
371
372             # Otherwise store this as a new struct.
373             else:
374                 structs[node_name] = fields
375
376         upto = 0
377         for node in self._valid_nodes:
378             node_name, _ = get_compat_name(node)
379             struct = structs[node_name]
380             for name, prop in node.props.items():
381                 if name not in PROP_IGNORE_LIST and name[0] != '#':
382                     prop.Widen(struct[name])
383             upto += 1
384
385             struct_name, aliases = get_compat_name(node)
386             for alias in aliases:
387                 self._aliases[alias] = struct_name
388
389         return structs
390
391     def scan_phandles(self):
392         """Figure out what phandles each node uses
393
394         We need to be careful when outputing nodes that use phandles since
395         they must come after the declaration of the phandles in the C file.
396         Otherwise we get a compiler error since the phandle struct is not yet
397         declared.
398
399         This function adds to each node a list of phandle nodes that the node
400         depends on. This allows us to output things in the right order.
401         """
402         for node in self._valid_nodes:
403             node.phandles = set()
404             for pname, prop in node.props.items():
405                 if pname in PROP_IGNORE_LIST or pname[0] == '#':
406                     continue
407                 info = self.get_phandle_argc(prop, node.name)
408                 if info:
409                     # Process the list as pairs of (phandle, id)
410                     pos = 0
411                     for args in info.args:
412                         phandle_cell = prop.value[pos]
413                         phandle = fdt_util.fdt32_to_cpu(phandle_cell)
414                         target_node = self._fdt.phandle_to_node[phandle]
415                         node.phandles.add(target_node)
416                         pos += 1 + args
417
418
419     def generate_structs(self, structs):
420         """Generate struct defintions for the platform data
421
422         This writes out the body of a header file consisting of structure
423         definitions for node in self._valid_nodes. See the documentation in
424         README.of-plat for more information.
425         """
426         self.out_header()
427         self.out('#include <stdbool.h>\n')
428         self.out('#include <linux/libfdt.h>\n')
429
430         # Output the struct definition
431         for name in sorted(structs):
432             self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
433             for pname in sorted(structs[name]):
434                 prop = structs[name][pname]
435                 info = self.get_phandle_argc(prop, structs[name])
436                 if info:
437                     # For phandles, include a reference to the target
438                     struct_name = 'struct phandle_%d_arg' % info.max_args
439                     self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
440                                              conv_name_to_c(prop.name),
441                                              len(info.args)))
442                 else:
443                     ptype = TYPE_NAMES[prop.type]
444                     self.out('\t%s%s' % (tab_to(2, ptype),
445                                          conv_name_to_c(prop.name)))
446                     if isinstance(prop.value, list):
447                         self.out('[%d]' % len(prop.value))
448                 self.out(';\n')
449             self.out('};\n')
450
451         for alias, struct_name in self._aliases.iteritems():
452             self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
453                                              STRUCT_PREFIX, struct_name))
454
455     def output_node(self, node):
456         """Output the C code for a node
457
458         Args:
459             node: node to output
460         """
461         struct_name, _ = get_compat_name(node)
462         var_name = conv_name_to_c(node.name)
463         self.buf('static struct %s%s %s%s = {\n' %
464                  (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
465         for pname, prop in node.props.items():
466             if pname in PROP_IGNORE_LIST or pname[0] == '#':
467                 continue
468             member_name = conv_name_to_c(prop.name)
469             self.buf('\t%s= ' % tab_to(3, '.' + member_name))
470
471             # Special handling for lists
472             if isinstance(prop.value, list):
473                 self.buf('{')
474                 vals = []
475                 # For phandles, output a reference to the platform data
476                 # of the target node.
477                 info = self.get_phandle_argc(prop, node.name)
478                 if info:
479                     # Process the list as pairs of (phandle, id)
480                     pos = 0
481                     for args in info.args:
482                         phandle_cell = prop.value[pos]
483                         phandle = fdt_util.fdt32_to_cpu(phandle_cell)
484                         target_node = self._fdt.phandle_to_node[phandle]
485                         name = conv_name_to_c(target_node.name)
486                         arg_values = []
487                         for i in range(args):
488                             arg_values.append(str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
489                         pos += 1 + args
490                         vals.append('\t{&%s%s, {%s}}' % (VAL_PREFIX, name,
491                                                      ', '.join(arg_values)))
492                     for val in vals:
493                         self.buf('\n\t\t%s,' % val)
494                 else:
495                     for val in prop.value:
496                         vals.append(get_value(prop.type, val))
497
498                     # Put 8 values per line to avoid very long lines.
499                     for i in xrange(0, len(vals), 8):
500                         if i:
501                             self.buf(',\n\t\t')
502                         self.buf(', '.join(vals[i:i + 8]))
503                 self.buf('}')
504             else:
505                 self.buf(get_value(prop.type, prop.value))
506             self.buf(',\n')
507         self.buf('};\n')
508
509         # Add a device declaration
510         self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
511         self.buf('\t.name\t\t= "%s",\n' % struct_name)
512         self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
513         self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
514         self.buf('};\n')
515         self.buf('\n')
516
517         self.out(''.join(self.get_buf()))
518
519     def generate_tables(self):
520         """Generate device defintions for the platform data
521
522         This writes out C platform data initialisation data and
523         U_BOOT_DEVICE() declarations for each valid node. Where a node has
524         multiple compatible strings, a #define is used to make them equivalent.
525
526         See the documentation in doc/driver-model/of-plat.txt for more
527         information.
528         """
529         self.out_header()
530         self.out('#include <common.h>\n')
531         self.out('#include <dm.h>\n')
532         self.out('#include <dt-structs.h>\n')
533         self.out('\n')
534         nodes_to_output = list(self._valid_nodes)
535
536         # Keep outputing nodes until there is none left
537         while nodes_to_output:
538             node = nodes_to_output[0]
539             # Output all the node's dependencies first
540             for req_node in node.phandles:
541                 if req_node in nodes_to_output:
542                     self.output_node(req_node)
543                     nodes_to_output.remove(req_node)
544             self.output_node(node)
545             nodes_to_output.remove(node)
546
547
548 def run_steps(args, dtb_file, include_disabled, output):
549     """Run all the steps of the dtoc tool
550
551     Args:
552         args: List of non-option arguments provided to the problem
553         dtb_file: Filename of dtb file to process
554         include_disabled: True to include disabled nodes
555         output: Name of output file
556     """
557     if not args:
558         raise ValueError('Please specify a command: struct, platdata')
559
560     plat = DtbPlatdata(dtb_file, include_disabled)
561     plat.scan_dtb()
562     plat.scan_tree()
563     plat.scan_reg_sizes()
564     plat.setup_output(output)
565     structs = plat.scan_structs()
566     plat.scan_phandles()
567
568     for cmd in args[0].split(','):
569         if cmd == 'struct':
570             plat.generate_structs(structs)
571         elif cmd == 'platdata':
572             plat.generate_tables()
573         else:
574             raise ValueError("Unknown command '%s': (use: struct, platdata)" %
575                              cmd)