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