dm: Use driver_info index instead of pointer
[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 os
17 import re
18 import sys
19
20 from dtoc import fdt
21 from dtoc import fdt_util
22 from patman import tools
23
24 # When we see these properties we ignore them - i.e. do not create a structure member
25 PROP_IGNORE_LIST = [
26     '#address-cells',
27     '#gpio-cells',
28     '#size-cells',
29     'compatible',
30     'linux,phandle',
31     "status",
32     'phandle',
33     'u-boot,dm-pre-reloc',
34     'u-boot,dm-tpl',
35     'u-boot,dm-spl',
36 ]
37
38 # C type declarations for the tyues we support
39 TYPE_NAMES = {
40     fdt.TYPE_INT: 'fdt32_t',
41     fdt.TYPE_BYTE: 'unsigned char',
42     fdt.TYPE_STRING: 'const char *',
43     fdt.TYPE_BOOL: 'bool',
44     fdt.TYPE_INT64: 'fdt64_t',
45 }
46
47 STRUCT_PREFIX = 'dtd_'
48 VAL_PREFIX = 'dtv_'
49
50 # This holds information about a property which includes phandles.
51 #
52 # max_args: integer: Maximum number or arguments that any phandle uses (int).
53 # args: Number of args for each phandle in the property. The total number of
54 #     phandles is len(args). This is a list of integers.
55 PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
56
57 # Holds a single phandle link, allowing a C struct value to be assigned to point
58 # to a device
59 #
60 # var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node')
61 # dev_name: Name of device to assign to (e.g. 'clock')
62 PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name'])
63
64
65 def conv_name_to_c(name):
66     """Convert a device-tree name to a C identifier
67
68     This uses multiple replace() calls instead of re.sub() since it is faster
69     (400ms for 1m calls versus 1000ms for the 're' version).
70
71     Args:
72         name:   Name to convert
73     Return:
74         String containing the C version of this name
75     """
76     new = name.replace('@', '_at_')
77     new = new.replace('-', '_')
78     new = new.replace(',', '_')
79     new = new.replace('.', '_')
80     return new
81
82 def tab_to(num_tabs, line):
83     """Append tabs to a line of text to reach a tab stop.
84
85     Args:
86         num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
87         line: Line of text to append to
88
89     Returns:
90         line with the correct number of tabs appeneded. If the line already
91         extends past that tab stop then a single space is appended.
92     """
93     if len(line) >= num_tabs * 8:
94         return line + ' '
95     return line + '\t' * (num_tabs - len(line) // 8)
96
97 def get_value(ftype, value):
98     """Get a value as a C expression
99
100     For integers this returns a byte-swapped (little-endian) hex string
101     For bytes this returns a hex string, e.g. 0x12
102     For strings this returns a literal string enclosed in quotes
103     For booleans this return 'true'
104
105     Args:
106         type: Data type (fdt_util)
107         value: Data value, as a string of bytes
108     """
109     if ftype == fdt.TYPE_INT:
110         return '%#x' % fdt_util.fdt32_to_cpu(value)
111     elif ftype == fdt.TYPE_BYTE:
112         return '%#x' % tools.ToByte(value[0])
113     elif ftype == fdt.TYPE_STRING:
114         # Handle evil ACPI backslashes by adding another backslash before them.
115         # So "\\_SB.GPO0" in the device tree effectively stays like that in C
116         return '"%s"' % value.replace('\\', '\\\\')
117     elif ftype == fdt.TYPE_BOOL:
118         return 'true'
119     elif ftype == fdt.TYPE_INT64:
120         return '%#x' % value
121
122 def get_compat_name(node):
123     """Get the node's list of compatible string as a C identifiers
124
125     Args:
126         node: Node object to check
127     Return:
128         List of C identifiers for all the compatible strings
129     """
130     compat = node.props['compatible'].value
131     if not isinstance(compat, list):
132         compat = [compat]
133     return [conv_name_to_c(c) for c in compat]
134
135
136 class DtbPlatdata(object):
137     """Provide a means to convert device tree binary data to platform data
138
139     The output of this process is C structures which can be used in space-
140     constrained encvironments where the ~3KB code overhead of device tree
141     code is not affordable.
142
143     Properties:
144         _fdt: Fdt object, referencing the device tree
145         _dtb_fname: Filename of the input device tree binary file
146         _valid_nodes: A list of Node object with compatible strings. The list
147             is ordered by conv_name_to_c(node.name)
148         _include_disabled: true to include nodes marked status = "disabled"
149         _outfile: The current output file (sys.stdout or a real file)
150         _warning_disabled: true to disable warnings about driver names not found
151         _lines: Stashed list of output lines for outputting in the future
152         _drivers: List of valid driver names found in drivers/
153         _driver_aliases: Dict that holds aliases for driver names
154             key: Driver alias declared with
155                 U_BOOT_DRIVER_ALIAS(driver_alias, driver_name)
156             value: Driver name declared with U_BOOT_DRIVER(driver_name)
157         _drivers_additional: List of additional drivers to use during scanning
158     """
159     def __init__(self, dtb_fname, include_disabled, warning_disabled,
160                  drivers_additional=[]):
161         self._fdt = None
162         self._dtb_fname = dtb_fname
163         self._valid_nodes = None
164         self._include_disabled = include_disabled
165         self._outfile = None
166         self._warning_disabled = warning_disabled
167         self._lines = []
168         self._drivers = []
169         self._driver_aliases = {}
170         self._drivers_additional = drivers_additional
171
172     def get_normalized_compat_name(self, node):
173         """Get a node's normalized compat name
174
175         Returns a valid driver name by retrieving node's list of compatible
176         string as a C identifier and performing a check against _drivers
177         and a lookup in driver_aliases printing a warning in case of failure.
178
179         Args:
180             node: Node object to check
181         Return:
182             Tuple:
183                 Driver name associated with the first compatible string
184                 List of C identifiers for all the other compatible strings
185                     (possibly empty)
186                 In case of no match found, the return will be the same as
187                 get_compat_name()
188         """
189         compat_list_c = get_compat_name(node)
190
191         for compat_c in compat_list_c:
192             if not compat_c in self._drivers:
193                 compat_c = self._driver_aliases.get(compat_c)
194                 if not compat_c:
195                     continue
196
197             aliases_c = compat_list_c
198             if compat_c in aliases_c:
199                 aliases_c.remove(compat_c)
200             return compat_c, aliases_c
201
202         if not self._warning_disabled:
203             print('WARNING: the driver %s was not found in the driver list'
204                   % (compat_list_c[0]))
205
206         return compat_list_c[0], compat_list_c[1:]
207
208     def setup_output(self, fname):
209         """Set up the output destination
210
211         Once this is done, future calls to self.out() will output to this
212         file.
213
214         Args:
215             fname: Filename to send output to, or '-' for stdout
216         """
217         if fname == '-':
218             self._outfile = sys.stdout
219         else:
220             self._outfile = open(fname, 'w')
221
222     def out(self, line):
223         """Output a string to the output file
224
225         Args:
226             line: String to output
227         """
228         self._outfile.write(line)
229
230     def buf(self, line):
231         """Buffer up a string to send later
232
233         Args:
234             line: String to add to our 'buffer' list
235         """
236         self._lines.append(line)
237
238     def get_buf(self):
239         """Get the contents of the output buffer, and clear it
240
241         Returns:
242             The output buffer, which is then cleared for future use
243         """
244         lines = self._lines
245         self._lines = []
246         return lines
247
248     def out_header(self):
249         """Output a message indicating that this is an auto-generated file"""
250         self.out('''/*
251  * DO NOT MODIFY
252  *
253  * This file was generated by dtoc from a .dtb (device tree binary) file.
254  */
255
256 ''')
257
258     def get_phandle_argc(self, prop, node_name):
259         """Check if a node contains phandles
260
261         We have no reliable way of detecting whether a node uses a phandle
262         or not. As an interim measure, use a list of known property names.
263
264         Args:
265             prop: Prop object to check
266         Return:
267             Number of argument cells is this is a phandle, else None
268         """
269         if prop.name in ['clocks', 'cd-gpios']:
270             if not isinstance(prop.value, list):
271                 prop.value = [prop.value]
272             val = prop.value
273             i = 0
274
275             max_args = 0
276             args = []
277             while i < len(val):
278                 phandle = fdt_util.fdt32_to_cpu(val[i])
279                 # If we get to the end of the list, stop. This can happen
280                 # since some nodes have more phandles in the list than others,
281                 # but we allocate enough space for the largest list. So those
282                 # nodes with shorter lists end up with zeroes at the end.
283                 if not phandle:
284                     break
285                 target = self._fdt.phandle_to_node.get(phandle)
286                 if not target:
287                     raise ValueError("Cannot parse '%s' in node '%s'" %
288                                      (prop.name, node_name))
289                 cells = None
290                 for prop_name in ['#clock-cells', '#gpio-cells']:
291                     cells = target.props.get(prop_name)
292                     if cells:
293                         break
294                 if not cells:
295                     raise ValueError("Node '%s' has no cells property" %
296                             (target.name))
297                 num_args = fdt_util.fdt32_to_cpu(cells.value)
298                 max_args = max(max_args, num_args)
299                 args.append(num_args)
300                 i += 1 + num_args
301             return PhandleInfo(max_args, args)
302         return None
303
304     def scan_driver(self, fn):
305         """Scan a driver file to build a list of driver names and aliases
306
307         This procedure will populate self._drivers and self._driver_aliases
308
309         Args
310             fn: Driver filename to scan
311         """
312         with open(fn, encoding='utf-8') as fd:
313             try:
314                 buff = fd.read()
315             except UnicodeDecodeError:
316                 # This seems to happen on older Python versions
317                 print("Skipping file '%s' due to unicode error" % fn)
318                 return
319
320             # The following re will search for driver names declared as
321             # U_BOOT_DRIVER(driver_name)
322             drivers = re.findall('U_BOOT_DRIVER\((.*)\)', buff)
323
324             for driver in drivers:
325                 self._drivers.append(driver)
326
327             # The following re will search for driver aliases declared as
328             # U_BOOT_DRIVER_ALIAS(alias, driver_name)
329             driver_aliases = re.findall('U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
330                                         buff)
331
332             for alias in driver_aliases: # pragma: no cover
333                 if len(alias) != 2:
334                     continue
335                 self._driver_aliases[alias[1]] = alias[0]
336
337     def scan_drivers(self):
338         """Scan the driver folders to build a list of driver names and aliases
339
340         This procedure will populate self._drivers and self._driver_aliases
341
342         """
343         basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
344         if basedir == '':
345             basedir = './'
346         for (dirpath, dirnames, filenames) in os.walk(basedir):
347             for fn in filenames:
348                 if not fn.endswith('.c'):
349                     continue
350                 self.scan_driver(dirpath + '/' + fn)
351
352         for fn in self._drivers_additional:
353             if not isinstance(fn, str) or len(fn) == 0:
354                 continue
355             if fn[0] == '/':
356                 self.scan_driver(fn)
357             else:
358                 self.scan_driver(basedir + '/' + fn)
359
360     def scan_dtb(self):
361         """Scan the device tree to obtain a tree of nodes and properties
362
363         Once this is done, self._fdt.GetRoot() can be called to obtain the
364         device tree root node, and progress from there.
365         """
366         self._fdt = fdt.FdtScan(self._dtb_fname)
367
368     def scan_node(self, root, valid_nodes):
369         """Scan a node and subnodes to build a tree of node and phandle info
370
371         This adds each node to self._valid_nodes.
372
373         Args:
374             root: Root node for scan
375             valid_nodes: List of Node objects to add to
376         """
377         for node in root.subnodes:
378             if 'compatible' in node.props:
379                 status = node.props.get('status')
380                 if (not self._include_disabled and not status or
381                         status.value != 'disabled'):
382                     valid_nodes.append(node)
383
384             # recurse to handle any subnodes
385             self.scan_node(node, valid_nodes)
386
387     def scan_tree(self):
388         """Scan the device tree for useful information
389
390         This fills in the following properties:
391             _valid_nodes: A list of nodes we wish to consider include in the
392                 platform data
393         """
394         valid_nodes = []
395         self.scan_node(self._fdt.GetRoot(), valid_nodes)
396         self._valid_nodes = sorted(valid_nodes,
397                                    key=lambda x: conv_name_to_c(x.name))
398         for idx, node in enumerate(self._valid_nodes):
399             node.idx = idx
400
401     @staticmethod
402     def get_num_cells(node):
403         """Get the number of cells in addresses and sizes for this node
404
405         Args:
406             node: Node to check
407
408         Returns:
409             Tuple:
410                 Number of address cells for this node
411                 Number of size cells for this node
412         """
413         parent = node.parent
414         na, ns = 2, 2
415         if parent:
416             na_prop = parent.props.get('#address-cells')
417             ns_prop = parent.props.get('#size-cells')
418             if na_prop:
419                 na = fdt_util.fdt32_to_cpu(na_prop.value)
420             if ns_prop:
421                 ns = fdt_util.fdt32_to_cpu(ns_prop.value)
422         return na, ns
423
424     def scan_reg_sizes(self):
425         """Scan for 64-bit 'reg' properties and update the values
426
427         This finds 'reg' properties with 64-bit data and converts the value to
428         an array of 64-values. This allows it to be output in a way that the
429         C code can read.
430         """
431         for node in self._valid_nodes:
432             reg = node.props.get('reg')
433             if not reg:
434                 continue
435             na, ns = self.get_num_cells(node)
436             total = na + ns
437
438             if reg.type != fdt.TYPE_INT:
439                 raise ValueError("Node '%s' reg property is not an int" %
440                                  node.name)
441             if len(reg.value) % total:
442                 raise ValueError("Node '%s' reg property has %d cells "
443                         'which is not a multiple of na + ns = %d + %d)' %
444                         (node.name, len(reg.value), na, ns))
445             reg.na = na
446             reg.ns = ns
447             if na != 1 or ns != 1:
448                 reg.type = fdt.TYPE_INT64
449                 i = 0
450                 new_value = []
451                 val = reg.value
452                 if not isinstance(val, list):
453                     val = [val]
454                 while i < len(val):
455                     addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.na)
456                     i += na
457                     size = fdt_util.fdt_cells_to_cpu(val[i:], reg.ns)
458                     i += ns
459                     new_value += [addr, size]
460                 reg.value = new_value
461
462     def scan_structs(self):
463         """Scan the device tree building up the C structures we will use.
464
465         Build a dict keyed by C struct name containing a dict of Prop
466         object for each struct field (keyed by property name). Where the
467         same struct appears multiple times, try to use the 'widest'
468         property, i.e. the one with a type which can express all others.
469
470         Once the widest property is determined, all other properties are
471         updated to match that width.
472
473         Returns:
474             dict containing structures:
475                 key (str): Node name, as a C identifier
476                 value: dict containing structure fields:
477                     key (str): Field name
478                     value: Prop object with field information
479         """
480         structs = collections.OrderedDict()
481         for node in self._valid_nodes:
482             node_name, _ = self.get_normalized_compat_name(node)
483             fields = {}
484
485             # Get a list of all the valid properties in this node.
486             for name, prop in node.props.items():
487                 if name not in PROP_IGNORE_LIST and name[0] != '#':
488                     fields[name] = copy.deepcopy(prop)
489
490             # If we've seen this node_name before, update the existing struct.
491             if node_name in structs:
492                 struct = structs[node_name]
493                 for name, prop in fields.items():
494                     oldprop = struct.get(name)
495                     if oldprop:
496                         oldprop.Widen(prop)
497                     else:
498                         struct[name] = prop
499
500             # Otherwise store this as a new struct.
501             else:
502                 structs[node_name] = fields
503
504         upto = 0
505         for node in self._valid_nodes:
506             node_name, _ = self.get_normalized_compat_name(node)
507             struct = structs[node_name]
508             for name, prop in node.props.items():
509                 if name not in PROP_IGNORE_LIST and name[0] != '#':
510                     prop.Widen(struct[name])
511             upto += 1
512
513         return structs
514
515     def scan_phandles(self):
516         """Figure out what phandles each node uses
517
518         We need to be careful when outputing nodes that use phandles since
519         they must come after the declaration of the phandles in the C file.
520         Otherwise we get a compiler error since the phandle struct is not yet
521         declared.
522
523         This function adds to each node a list of phandle nodes that the node
524         depends on. This allows us to output things in the right order.
525         """
526         for node in self._valid_nodes:
527             node.phandles = set()
528             for pname, prop in node.props.items():
529                 if pname in PROP_IGNORE_LIST or pname[0] == '#':
530                     continue
531                 info = self.get_phandle_argc(prop, node.name)
532                 if info:
533                     # Process the list as pairs of (phandle, id)
534                     pos = 0
535                     for args in info.args:
536                         phandle_cell = prop.value[pos]
537                         phandle = fdt_util.fdt32_to_cpu(phandle_cell)
538                         target_node = self._fdt.phandle_to_node[phandle]
539                         node.phandles.add(target_node)
540                         pos += 1 + args
541
542
543     def generate_structs(self, structs):
544         """Generate struct defintions for the platform data
545
546         This writes out the body of a header file consisting of structure
547         definitions for node in self._valid_nodes. See the documentation in
548         doc/driver-model/of-plat.rst for more information.
549
550         Args:
551             structs: dict containing structures:
552                 key (str): Node name, as a C identifier
553                 value: dict containing structure fields:
554                     key (str): Field name
555                     value: Prop object with field information
556
557         """
558         self.out_header()
559         self.out('#include <stdbool.h>\n')
560         self.out('#include <linux/libfdt.h>\n')
561
562         # Output the struct definition
563         for name in sorted(structs):
564             self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
565             for pname in sorted(structs[name]):
566                 prop = structs[name][pname]
567                 info = self.get_phandle_argc(prop, structs[name])
568                 if info:
569                     # For phandles, include a reference to the target
570                     struct_name = 'struct phandle_%d_arg' % info.max_args
571                     self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
572                                              conv_name_to_c(prop.name),
573                                              len(info.args)))
574                 else:
575                     ptype = TYPE_NAMES[prop.type]
576                     self.out('\t%s%s' % (tab_to(2, ptype),
577                                          conv_name_to_c(prop.name)))
578                     if isinstance(prop.value, list):
579                         self.out('[%d]' % len(prop.value))
580                 self.out(';\n')
581             self.out('};\n')
582
583     def output_node(self, node):
584         """Output the C code for a node
585
586         Args:
587             node: node to output
588         """
589         def _output_list(node, prop):
590             """Output the C code for a devicetree property that holds a list
591
592             Args:
593                 node (fdt.Node): Node to output
594                 prop (fdt.Prop): Prop to output
595             """
596             self.buf('{')
597             vals = []
598             # For phandles, output a reference to the platform data
599             # of the target node.
600             info = self.get_phandle_argc(prop, node.name)
601             if info:
602                 # Process the list as pairs of (phandle, id)
603                 pos = 0
604                 item = 0
605                 for args in info.args:
606                     phandle_cell = prop.value[pos]
607                     phandle = fdt_util.fdt32_to_cpu(phandle_cell)
608                     target_node = self._fdt.phandle_to_node[phandle]
609                     name = conv_name_to_c(target_node.name)
610                     arg_values = []
611                     for i in range(args):
612                         arg_values.append(
613                             str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
614                     pos += 1 + args
615                     vals.append('\t{%d, {%s}}' % (target_node.idx,
616                                                   ', '.join(arg_values)))
617                     item += 1
618                 for val in vals:
619                     self.buf('\n\t\t%s,' % val)
620             else:
621                 for val in prop.value:
622                     vals.append(get_value(prop.type, val))
623
624                 # Put 8 values per line to avoid very long lines.
625                 for i in range(0, len(vals), 8):
626                     if i:
627                         self.buf(',\n\t\t')
628                     self.buf(', '.join(vals[i:i + 8]))
629             self.buf('}')
630
631         struct_name, _ = self.get_normalized_compat_name(node)
632         var_name = conv_name_to_c(node.name)
633         self.buf('/* Node %s index %d */\n' % (node.path, node.idx))
634         self.buf('static struct %s%s %s%s = {\n' %
635                  (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
636         for pname in sorted(node.props):
637             prop = node.props[pname]
638             if pname in PROP_IGNORE_LIST or pname[0] == '#':
639                 continue
640             member_name = conv_name_to_c(prop.name)
641             self.buf('\t%s= ' % tab_to(3, '.' + member_name))
642
643             # Special handling for lists
644             if isinstance(prop.value, list):
645                 _output_list(node, prop)
646             else:
647                 self.buf(get_value(prop.type, prop.value))
648             self.buf(',\n')
649         self.buf('};\n')
650
651         # Add a device declaration
652         self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
653         self.buf('\t.name\t\t= "%s",\n' % struct_name)
654         self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
655         self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
656         idx = -1
657         if node.parent and node.parent in self._valid_nodes:
658             idx = node.parent.idx
659         self.buf('\t.parent_idx\t= %d,\n' % idx)
660         self.buf('};\n')
661         self.buf('\n')
662
663         self.out(''.join(self.get_buf()))
664
665     def generate_tables(self):
666         """Generate device defintions for the platform data
667
668         This writes out C platform data initialisation data and
669         U_BOOT_DEVICE() declarations for each valid node. Where a node has
670         multiple compatible strings, a #define is used to make them equivalent.
671
672         See the documentation in doc/driver-model/of-plat.rst for more
673         information.
674         """
675         self.out_header()
676         self.out('#include <common.h>\n')
677         self.out('#include <dm.h>\n')
678         self.out('#include <dt-structs.h>\n')
679         self.out('\n')
680         nodes_to_output = list(self._valid_nodes)
681
682         # Keep outputing nodes until there is none left
683         while nodes_to_output:
684             node = nodes_to_output[0]
685             # Output all the node's dependencies first
686             for req_node in node.phandles:
687                 if req_node in nodes_to_output:
688                     self.output_node(req_node)
689                     nodes_to_output.remove(req_node)
690             self.output_node(node)
691             nodes_to_output.remove(node)
692
693         # Define dm_populate_phandle_data() which will add the linking between
694         # nodes using DM_GET_DEVICE
695         # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx)
696         self.buf('void dm_populate_phandle_data(void) {\n')
697         self.buf('}\n')
698
699         self.out(''.join(self.get_buf()))
700
701 def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False,
702               drivers_additional=[]):
703     """Run all the steps of the dtoc tool
704
705     Args:
706         args: List of non-option arguments provided to the problem
707         dtb_file: Filename of dtb file to process
708         include_disabled: True to include disabled nodes
709         output: Name of output file
710     """
711     if not args:
712         raise ValueError('Please specify a command: struct, platdata')
713
714     plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional)
715     plat.scan_drivers()
716     plat.scan_dtb()
717     plat.scan_tree()
718     plat.scan_reg_sizes()
719     plat.setup_output(output)
720     structs = plat.scan_structs()
721     plat.scan_phandles()
722
723     for cmd in args[0].split(','):
724         if cmd == 'struct':
725             plat.generate_structs(structs)
726         elif cmd == 'platdata':
727             plat.generate_tables()
728         else:
729             raise ValueError("Unknown command '%s': (use: struct, platdata)" %
730                              cmd)