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