Merge git://git.denx.de/u-boot-dm
[platform/kernel/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 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 }
42
43 STRUCT_PREFIX = 'dtd_'
44 VAL_PREFIX = 'dtv_'
45
46 def conv_name_to_c(name):
47     """Convert a device-tree name to a C identifier
48
49     This uses multiple replace() calls instead of re.sub() since it is faster
50     (400ms for 1m calls versus 1000ms for the 're' version).
51
52     Args:
53         name:   Name to convert
54     Return:
55         String containing the C version of this name
56     """
57     new = name.replace('@', '_at_')
58     new = new.replace('-', '_')
59     new = new.replace(',', '_')
60     new = new.replace('.', '_')
61     return new
62
63 def tab_to(num_tabs, line):
64     """Append tabs to a line of text to reach a tab stop.
65
66     Args:
67         num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
68         line: Line of text to append to
69
70     Returns:
71         line with the correct number of tabs appeneded. If the line already
72         extends past that tab stop then a single space is appended.
73     """
74     if len(line) >= num_tabs * 8:
75         return line + ' '
76     return line + '\t' * (num_tabs - len(line) // 8)
77
78 def get_value(ftype, value):
79     """Get a value as a C expression
80
81     For integers this returns a byte-swapped (little-endian) hex string
82     For bytes this returns a hex string, e.g. 0x12
83     For strings this returns a literal string enclosed in quotes
84     For booleans this return 'true'
85
86     Args:
87         type: Data type (fdt_util)
88         value: Data value, as a string of bytes
89     """
90     if ftype == fdt.TYPE_INT:
91         return '%#x' % fdt_util.fdt32_to_cpu(value)
92     elif ftype == fdt.TYPE_BYTE:
93         return '%#x' % ord(value[0])
94     elif ftype == fdt.TYPE_STRING:
95         return '"%s"' % value
96     elif ftype == fdt.TYPE_BOOL:
97         return 'true'
98
99 def get_compat_name(node):
100     """Get a node's first compatible string as a C identifier
101
102     Args:
103         node: Node object to check
104     Return:
105         Tuple:
106             C identifier for the first compatible string
107             List of C identifiers for all the other compatible strings
108                 (possibly empty)
109     """
110     compat = node.props['compatible'].value
111     aliases = []
112     if isinstance(compat, list):
113         compat, aliases = compat[0], compat[1:]
114     return conv_name_to_c(compat), [conv_name_to_c(a) for a in aliases]
115
116 def is_phandle(prop):
117     """Check if a node contains phandles
118
119     We have no reliable way of detecting whether a node uses a phandle
120     or not. As an interim measure, use a list of known property names.
121
122     Args:
123         prop: Prop object to check
124     Return:
125         True if the object value contains phandles, else False
126     """
127     if prop.name in ['clocks']:
128         return True
129     return False
130
131
132 class DtbPlatdata(object):
133     """Provide a means to convert device tree binary data to platform data
134
135     The output of this process is C structures which can be used in space-
136     constrained encvironments where the ~3KB code overhead of device tree
137     code is not affordable.
138
139     Properties:
140         _fdt: Fdt object, referencing the device tree
141         _dtb_fname: Filename of the input device tree binary file
142         _valid_nodes: A list of Node object with compatible strings
143         _include_disabled: true to include nodes marked status = "disabled"
144         _phandle_nodes: A dict of nodes indexed by phandle number (1, 2...)
145         _outfile: The current output file (sys.stdout or a real file)
146         _lines: Stashed list of output lines for outputting in the future
147         _phandle_nodes: A dict of Nodes indexed by phandle (an integer)
148     """
149     def __init__(self, dtb_fname, include_disabled):
150         self._fdt = None
151         self._dtb_fname = dtb_fname
152         self._valid_nodes = None
153         self._include_disabled = include_disabled
154         self._phandle_nodes = {}
155         self._outfile = None
156         self._lines = []
157         self._aliases = {}
158
159     def setup_output(self, fname):
160         """Set up the output destination
161
162         Once this is done, future calls to self.out() will output to this
163         file.
164
165         Args:
166             fname: Filename to send output to, or '-' for stdout
167         """
168         if fname == '-':
169             self._outfile = sys.stdout
170         else:
171             self._outfile = open(fname, 'w')
172
173     def out(self, line):
174         """Output a string to the output file
175
176         Args:
177             line: String to output
178         """
179         self._outfile.write(line)
180
181     def buf(self, line):
182         """Buffer up a string to send later
183
184         Args:
185             line: String to add to our 'buffer' list
186         """
187         self._lines.append(line)
188
189     def get_buf(self):
190         """Get the contents of the output buffer, and clear it
191
192         Returns:
193             The output buffer, which is then cleared for future use
194         """
195         lines = self._lines
196         self._lines = []
197         return lines
198
199     def scan_dtb(self):
200         """Scan the device tree to obtain a tree of notes and properties
201
202         Once this is done, self._fdt.GetRoot() can be called to obtain the
203         device tree root node, and progress from there.
204         """
205         self._fdt = fdt.FdtScan(self._dtb_fname)
206
207     def scan_node(self, root):
208         """Scan a node and subnodes to build a tree of node and phandle info
209
210         This adds each node to self._valid_nodes and each phandle to
211         self._phandle_nodes.
212
213         Args:
214             root: Root node for scan
215         """
216         for node in root.subnodes:
217             if 'compatible' in node.props:
218                 status = node.props.get('status')
219                 if (not self._include_disabled and not status or
220                         status.value != 'disabled'):
221                     self._valid_nodes.append(node)
222                     phandle_prop = node.props.get('phandle')
223                     if phandle_prop:
224                         phandle = phandle_prop.GetPhandle()
225                         self._phandle_nodes[phandle] = node
226
227             # recurse to handle any subnodes
228             self.scan_node(node)
229
230     def scan_tree(self):
231         """Scan the device tree for useful information
232
233         This fills in the following properties:
234             _phandle_nodes: A dict of Nodes indexed by phandle (an integer)
235             _valid_nodes: A list of nodes we wish to consider include in the
236                 platform data
237         """
238         self._phandle_nodes = {}
239         self._valid_nodes = []
240         return self.scan_node(self._fdt.GetRoot())
241
242     def scan_structs(self):
243         """Scan the device tree building up the C structures we will use.
244
245         Build a dict keyed by C struct name containing a dict of Prop
246         object for each struct field (keyed by property name). Where the
247         same struct appears multiple times, try to use the 'widest'
248         property, i.e. the one with a type which can express all others.
249
250         Once the widest property is determined, all other properties are
251         updated to match that width.
252         """
253         structs = {}
254         for node in self._valid_nodes:
255             node_name, _ = get_compat_name(node)
256             fields = {}
257
258             # Get a list of all the valid properties in this node.
259             for name, prop in node.props.items():
260                 if name not in PROP_IGNORE_LIST and name[0] != '#':
261                     fields[name] = copy.deepcopy(prop)
262
263             # If we've seen this node_name before, update the existing struct.
264             if node_name in structs:
265                 struct = structs[node_name]
266                 for name, prop in fields.items():
267                     oldprop = struct.get(name)
268                     if oldprop:
269                         oldprop.Widen(prop)
270                     else:
271                         struct[name] = prop
272
273             # Otherwise store this as a new struct.
274             else:
275                 structs[node_name] = fields
276
277         upto = 0
278         for node in self._valid_nodes:
279             node_name, _ = get_compat_name(node)
280             struct = structs[node_name]
281             for name, prop in node.props.items():
282                 if name not in PROP_IGNORE_LIST and name[0] != '#':
283                     prop.Widen(struct[name])
284             upto += 1
285
286             struct_name, aliases = get_compat_name(node)
287             for alias in aliases:
288                 self._aliases[alias] = struct_name
289
290         return structs
291
292     def scan_phandles(self):
293         """Figure out what phandles each node uses
294
295         We need to be careful when outputing nodes that use phandles since
296         they must come after the declaration of the phandles in the C file.
297         Otherwise we get a compiler error since the phandle struct is not yet
298         declared.
299
300         This function adds to each node a list of phandle nodes that the node
301         depends on. This allows us to output things in the right order.
302         """
303         for node in self._valid_nodes:
304             node.phandles = set()
305             for pname, prop in node.props.items():
306                 if pname in PROP_IGNORE_LIST or pname[0] == '#':
307                     continue
308                 if isinstance(prop.value, list):
309                     if is_phandle(prop):
310                         # Process the list as pairs of (phandle, id)
311                         value_it = iter(prop.value)
312                         for phandle_cell, _ in zip(value_it, value_it):
313                             phandle = fdt_util.fdt32_to_cpu(phandle_cell)
314                             target_node = self._phandle_nodes[phandle]
315                             node.phandles.add(target_node)
316
317
318     def generate_structs(self, structs):
319         """Generate struct defintions for the platform data
320
321         This writes out the body of a header file consisting of structure
322         definitions for node in self._valid_nodes. See the documentation in
323         README.of-plat for more information.
324         """
325         self.out('#include <stdbool.h>\n')
326         self.out('#include <libfdt.h>\n')
327
328         # Output the struct definition
329         for name in sorted(structs):
330             self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
331             for pname in sorted(structs[name]):
332                 prop = structs[name][pname]
333                 if is_phandle(prop):
334                     # For phandles, include a reference to the target
335                     self.out('\t%s%s[%d]' % (tab_to(2, 'struct phandle_2_cell'),
336                                              conv_name_to_c(prop.name),
337                                              len(prop.value) / 2))
338                 else:
339                     ptype = TYPE_NAMES[prop.type]
340                     self.out('\t%s%s' % (tab_to(2, ptype),
341                                          conv_name_to_c(prop.name)))
342                     if isinstance(prop.value, list):
343                         self.out('[%d]' % len(prop.value))
344                 self.out(';\n')
345             self.out('};\n')
346
347         for alias, struct_name in self._aliases.iteritems():
348             self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
349                                              STRUCT_PREFIX, struct_name))
350
351     def output_node(self, node):
352         """Output the C code for a node
353
354         Args:
355             node: node to output
356         """
357         struct_name, _ = get_compat_name(node)
358         var_name = conv_name_to_c(node.name)
359         self.buf('static struct %s%s %s%s = {\n' %
360                  (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
361         for pname, prop in node.props.items():
362             if pname in PROP_IGNORE_LIST or pname[0] == '#':
363                 continue
364             member_name = conv_name_to_c(prop.name)
365             self.buf('\t%s= ' % tab_to(3, '.' + member_name))
366
367             # Special handling for lists
368             if isinstance(prop.value, list):
369                 self.buf('{')
370                 vals = []
371                 # For phandles, output a reference to the platform data
372                 # of the target node.
373                 if is_phandle(prop):
374                     # Process the list as pairs of (phandle, id)
375                     value_it = iter(prop.value)
376                     for phandle_cell, id_cell in zip(value_it, value_it):
377                         phandle = fdt_util.fdt32_to_cpu(phandle_cell)
378                         id_num = fdt_util.fdt32_to_cpu(id_cell)
379                         target_node = self._phandle_nodes[phandle]
380                         name = conv_name_to_c(target_node.name)
381                         vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id_num))
382                 else:
383                     for val in prop.value:
384                         vals.append(get_value(prop.type, val))
385                 self.buf(', '.join(vals))
386                 self.buf('}')
387             else:
388                 self.buf(get_value(prop.type, prop.value))
389             self.buf(',\n')
390         self.buf('};\n')
391
392         # Add a device declaration
393         self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
394         self.buf('\t.name\t\t= "%s",\n' % struct_name)
395         self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
396         self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
397         self.buf('};\n')
398         self.buf('\n')
399
400         self.out(''.join(self.get_buf()))
401
402     def generate_tables(self):
403         """Generate device defintions for the platform data
404
405         This writes out C platform data initialisation data and
406         U_BOOT_DEVICE() declarations for each valid node. Where a node has
407         multiple compatible strings, a #define is used to make them equivalent.
408
409         See the documentation in doc/driver-model/of-plat.txt for more
410         information.
411         """
412         self.out('#include <common.h>\n')
413         self.out('#include <dm.h>\n')
414         self.out('#include <dt-structs.h>\n')
415         self.out('\n')
416         nodes_to_output = list(self._valid_nodes)
417
418         # Keep outputing nodes until there is none left
419         while nodes_to_output:
420             node = nodes_to_output[0]
421             # Output all the node's dependencies first
422             for req_node in node.phandles:
423                 if req_node in nodes_to_output:
424                     self.output_node(req_node)
425                     nodes_to_output.remove(req_node)
426             self.output_node(node)
427             nodes_to_output.remove(node)
428
429
430 def run_steps(args, dtb_file, include_disabled, output):
431     """Run all the steps of the dtoc tool
432
433     Args:
434         args: List of non-option arguments provided to the problem
435         dtb_file: Filename of dtb file to process
436         include_disabled: True to include disabled nodes
437         output: Name of output file
438     """
439     if not args:
440         raise ValueError('Please specify a command: struct, platdata')
441
442     plat = DtbPlatdata(dtb_file, include_disabled)
443     plat.scan_dtb()
444     plat.scan_tree()
445     plat.setup_output(output)
446     structs = plat.scan_structs()
447     plat.scan_phandles()
448
449     for cmd in args[0].split(','):
450         if cmd == 'struct':
451             plat.generate_structs(structs)
452         elif cmd == 'platdata':
453             plat.generate_tables()
454         else:
455             raise ValueError("Unknown command '%s': (use: struct, platdata)" %
456                              cmd)