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