Merge branch 'master' of git://git.denx.de/u-boot-video
[platform/kernel/u-boot.git] / tools / dtoc / dtoc.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2016 Google, Inc
4 # Written by Simon Glass <sjg@chromium.org>
5 #
6 # SPDX-License-Identifier:      GPL-2.0+
7 #
8
9 import copy
10 from optparse import OptionError, OptionParser
11 import os
12 import sys
13
14 import fdt_util
15
16 # Bring in the patman libraries
17 our_path = os.path.dirname(os.path.realpath(__file__))
18 sys.path.append(os.path.join(our_path, '../patman'))
19
20 # Bring in either the normal fdt library (which relies on libfdt) or the
21 # fallback one (which uses fdtget and is slower). Both provide the same
22 # interfface for this file to use.
23 try:
24     from fdt import Fdt
25     import fdt
26     have_libfdt = True
27 except ImportError:
28     have_libfdt = False
29     from fdt_fallback import Fdt
30     import fdt_fallback as fdt
31
32 import struct
33
34 # When we see these properties we ignore them - i.e. do not create a structure member
35 PROP_IGNORE_LIST = [
36     '#address-cells',
37     '#gpio-cells',
38     '#size-cells',
39     'compatible',
40     'linux,phandle',
41     "status",
42     'phandle',
43     'u-boot,dm-pre-reloc',
44 ]
45
46 # C type declarations for the tyues we support
47 TYPE_NAMES = {
48     fdt_util.TYPE_INT: 'fdt32_t',
49     fdt_util.TYPE_BYTE: 'unsigned char',
50     fdt_util.TYPE_STRING: 'const char *',
51     fdt_util.TYPE_BOOL: 'bool',
52 };
53
54 STRUCT_PREFIX = 'dtd_'
55 VAL_PREFIX = 'dtv_'
56
57 def Conv_name_to_c(name):
58     """Convert a device-tree name to a C identifier
59
60     Args:
61         name:   Name to convert
62     Return:
63         String containing the C version of this name
64     """
65     str = name.replace('@', '_at_')
66     str = str.replace('-', '_')
67     str = str.replace(',', '_')
68     str = str.replace('/', '__')
69     return str
70
71 def TabTo(num_tabs, str):
72     if len(str) >= num_tabs * 8:
73         return str + ' '
74     return str + '\t' * (num_tabs - len(str) / 8)
75
76 class DtbPlatdata:
77     """Provide a means to convert device tree binary data to platform data
78
79     The output of this process is C structures which can be used in space-
80     constrained encvironments where the ~3KB code overhead of device tree
81     code is not affordable.
82
83     Properties:
84         fdt: Fdt object, referencing the device tree
85         _dtb_fname: Filename of the input device tree binary file
86         _valid_nodes: A list of Node object with compatible strings
87         _options: Command-line options
88         _phandle_node: A dict of nodes indexed by phandle number (1, 2...)
89         _outfile: The current output file (sys.stdout or a real file)
90         _lines: Stashed list of output lines for outputting in the future
91         _phandle_node: A dict of Nodes indexed by phandle (an integer)
92     """
93     def __init__(self, dtb_fname, options):
94         self._dtb_fname = dtb_fname
95         self._valid_nodes = None
96         self._options = options
97         self._phandle_node = {}
98         self._outfile = None
99         self._lines = []
100
101     def SetupOutput(self, fname):
102         """Set up the output destination
103
104         Once this is done, future calls to self.Out() will output to this
105         file.
106
107         Args:
108             fname: Filename to send output to, or '-' for stdout
109         """
110         if fname == '-':
111             self._outfile = sys.stdout
112         else:
113             self._outfile = open(fname, 'w')
114
115     def Out(self, str):
116         """Output a string to the output file
117
118         Args:
119             str: String to output
120         """
121         self._outfile.write(str)
122
123     def Buf(self, str):
124         """Buffer up a string to send later
125
126         Args:
127             str: String to add to our 'buffer' list
128         """
129         self._lines.append(str)
130
131     def GetBuf(self):
132         """Get the contents of the output buffer, and clear it
133
134         Returns:
135             The output buffer, which is then cleared for future use
136         """
137         lines = self._lines
138         self._lines = []
139         return lines
140
141     def GetValue(self, type, value):
142         """Get a value as a C expression
143
144         For integers this returns a byte-swapped (little-endian) hex string
145         For bytes this returns a hex string, e.g. 0x12
146         For strings this returns a literal string enclosed in quotes
147         For booleans this return 'true'
148
149         Args:
150             type: Data type (fdt_util)
151             value: Data value, as a string of bytes
152         """
153         if type == fdt_util.TYPE_INT:
154             return '%#x' % fdt_util.fdt32_to_cpu(value)
155         elif type == fdt_util.TYPE_BYTE:
156             return '%#x' % ord(value[0])
157         elif type == fdt_util.TYPE_STRING:
158             return '"%s"' % value
159         elif type == fdt_util.TYPE_BOOL:
160             return 'true'
161
162     def GetCompatName(self, node):
163         """Get a node's first compatible string as a C identifier
164
165         Args:
166             node: Node object to check
167         Return:
168             C identifier for the first compatible string
169         """
170         compat = node.props['compatible'].value
171         if type(compat) == list:
172             compat = compat[0]
173         return Conv_name_to_c(compat)
174
175     def ScanDtb(self):
176         """Scan the device tree to obtain a tree of notes and properties
177
178         Once this is done, self.fdt.GetRoot() can be called to obtain the
179         device tree root node, and progress from there.
180         """
181         self.fdt = Fdt(self._dtb_fname)
182         self.fdt.Scan()
183
184     def ScanTree(self):
185         """Scan the device tree for useful information
186
187         This fills in the following properties:
188             _phandle_node: A dict of Nodes indexed by phandle (an integer)
189             _valid_nodes: A list of nodes we wish to consider include in the
190                 platform data
191         """
192         node_list = []
193         self._phandle_node = {}
194         for node in self.fdt.GetRoot().subnodes:
195             if 'compatible' in node.props:
196                 status = node.props.get('status')
197                 if (not options.include_disabled and not status or
198                     status.value != 'disabled'):
199                     node_list.append(node)
200                     phandle_prop = node.props.get('phandle')
201                     if phandle_prop:
202                         phandle = phandle_prop.GetPhandle()
203                         self._phandle_node[phandle] = node
204
205         self._valid_nodes = node_list
206
207     def IsPhandle(self, prop):
208         """Check if a node contains phandles
209
210         We have no reliable way of detecting whether a node uses a phandle
211         or not. As an interim measure, use a list of known property names.
212
213         Args:
214             prop: Prop object to check
215         Return:
216             True if the object value contains phandles, else False
217         """
218         if prop.name in ['clocks']:
219             return True
220         return False
221
222     def ScanStructs(self):
223         """Scan the device tree building up the C structures we will use.
224
225         Build a dict keyed by C struct name containing a dict of Prop
226         object for each struct field (keyed by property name). Where the
227         same struct appears multiple times, try to use the 'widest'
228         property, i.e. the one with a type which can express all others.
229
230         Once the widest property is determined, all other properties are
231         updated to match that width.
232         """
233         structs = {}
234         for node in self._valid_nodes:
235             node_name = self.GetCompatName(node)
236             fields = {}
237
238             # Get a list of all the valid properties in this node.
239             for name, prop in node.props.iteritems():
240                 if name not in PROP_IGNORE_LIST and name[0] != '#':
241                     fields[name] = copy.deepcopy(prop)
242
243             # If we've seen this node_name before, update the existing struct.
244             if node_name in structs:
245                 struct = structs[node_name]
246                 for name, prop in fields.iteritems():
247                     oldprop = struct.get(name)
248                     if oldprop:
249                         oldprop.Widen(prop)
250                     else:
251                         struct[name] = prop
252
253             # Otherwise store this as a new struct.
254             else:
255                 structs[node_name] = fields
256
257         upto = 0
258         for node in self._valid_nodes:
259             node_name = self.GetCompatName(node)
260             struct = structs[node_name]
261             for name, prop in node.props.iteritems():
262                 if name not in PROP_IGNORE_LIST and name[0] != '#':
263                     prop.Widen(struct[name])
264             upto += 1
265         return structs
266
267     def GenerateStructs(self, structs):
268         """Generate struct defintions for the platform data
269
270         This writes out the body of a header file consisting of structure
271         definitions for node in self._valid_nodes. See the documentation in
272         README.of-plat for more information.
273         """
274         self.Out('#include <stdbool.h>\n')
275         self.Out('#include <libfdt.h>\n')
276
277         # Output the struct definition
278         for name in sorted(structs):
279             self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
280             for pname in sorted(structs[name]):
281                 prop = structs[name][pname]
282                 if self.IsPhandle(prop):
283                     # For phandles, include a reference to the target
284                     self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
285                                              Conv_name_to_c(prop.name),
286                                              len(prop.value) / 2))
287                 else:
288                     ptype = TYPE_NAMES[prop.type]
289                     self.Out('\t%s%s' % (TabTo(2, ptype),
290                                          Conv_name_to_c(prop.name)))
291                     if type(prop.value) == list:
292                         self.Out('[%d]' % len(prop.value))
293                 self.Out(';\n')
294             self.Out('};\n')
295
296     def GenerateTables(self):
297         """Generate device defintions for the platform data
298
299         This writes out C platform data initialisation data and
300         U_BOOT_DEVICE() declarations for each valid node. See the
301         documentation in README.of-plat for more information.
302         """
303         self.Out('#include <common.h>\n')
304         self.Out('#include <dm.h>\n')
305         self.Out('#include <dt-structs.h>\n')
306         self.Out('\n')
307         node_txt_list = []
308         for node in self._valid_nodes:
309             struct_name = self.GetCompatName(node)
310             var_name = Conv_name_to_c(node.name)
311             self.Buf('static struct %s%s %s%s = {\n' %
312                 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
313             for pname, prop in node.props.iteritems():
314                 if pname in PROP_IGNORE_LIST or pname[0] == '#':
315                     continue
316                 ptype = TYPE_NAMES[prop.type]
317                 member_name = Conv_name_to_c(prop.name)
318                 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
319
320                 # Special handling for lists
321                 if type(prop.value) == list:
322                     self.Buf('{')
323                     vals = []
324                     # For phandles, output a reference to the platform data
325                     # of the target node.
326                     if self.IsPhandle(prop):
327                         # Process the list as pairs of (phandle, id)
328                         it = iter(prop.value)
329                         for phandle_cell, id_cell in zip(it, it):
330                             phandle = fdt_util.fdt32_to_cpu(phandle_cell)
331                             id = fdt_util.fdt32_to_cpu(id_cell)
332                             target_node = self._phandle_node[phandle]
333                             name = Conv_name_to_c(target_node.name)
334                             vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
335                     else:
336                         for val in prop.value:
337                             vals.append(self.GetValue(prop.type, val))
338                     self.Buf(', '.join(vals))
339                     self.Buf('}')
340                 else:
341                     self.Buf(self.GetValue(prop.type, prop.value))
342                 self.Buf(',\n')
343             self.Buf('};\n')
344
345             # Add a device declaration
346             self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
347             self.Buf('\t.name\t\t= "%s",\n' % struct_name)
348             self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
349             self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' %
350                      (VAL_PREFIX, var_name))
351             self.Buf('};\n')
352             self.Buf('\n')
353
354             # Output phandle target nodes first, since they may be referenced
355             # by others
356             if 'phandle' in node.props:
357                 self.Out(''.join(self.GetBuf()))
358             else:
359                 node_txt_list.append(self.GetBuf())
360
361         # Output all the nodes which are not phandle targets themselves, but
362         # may reference them. This avoids the need for forward declarations.
363         for node_txt in node_txt_list:
364             self.Out(''.join(node_txt))
365
366
367 if __name__ != "__main__":
368     pass
369
370 parser = OptionParser()
371 parser.add_option('-d', '--dtb-file', action='store',
372                   help='Specify the .dtb input file')
373 parser.add_option('--include-disabled', action='store_true',
374                   help='Include disabled nodes')
375 parser.add_option('-o', '--output', action='store', default='-',
376                   help='Select output filename')
377 (options, args) = parser.parse_args()
378
379 if not args:
380     raise ValueError('Please specify a command: struct, platdata')
381
382 plat = DtbPlatdata(options.dtb_file, options)
383 plat.ScanDtb()
384 plat.ScanTree()
385 plat.SetupOutput(options.output)
386 structs = plat.ScanStructs()
387
388 for cmd in args[0].split(','):
389     if cmd == 'struct':
390         plat.GenerateStructs(structs)
391     elif cmd == 'platdata':
392         plat.GenerateTables()
393     else:
394         raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)