]> git.sur5r.net Git - u-boot/blob - tools/dtoc/dtoc.py
dtoc: Use self._options instead of the global options
[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_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         self._aliases = {}
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         aliases = []
164         if type(compat) == list:
165             compat, aliases = compat[0], compat[1:]
166         return Conv_name_to_c(compat), [Conv_name_to_c(a) for a in aliases]
167
168     def ScanDtb(self):
169         """Scan the device tree to obtain a tree of notes and properties
170
171         Once this is done, self.fdt.GetRoot() can be called to obtain the
172         device tree root node, and progress from there.
173         """
174         self.fdt = fdt.FdtScan(self._dtb_fname)
175
176     def ScanNode(self, root):
177         for node in root.subnodes:
178             if 'compatible' in node.props:
179                 status = node.props.get('status')
180                 if (not self._options.include_disabled and not status or
181                     status.value != 'disabled'):
182                     self._valid_nodes.append(node)
183                     phandle_prop = node.props.get('phandle')
184                     if phandle_prop:
185                         phandle = phandle_prop.GetPhandle()
186                         self._phandle_node[phandle] = node
187
188             # recurse to handle any subnodes
189             self.ScanNode(node);
190
191     def ScanTree(self):
192         """Scan the device tree for useful information
193
194         This fills in the following properties:
195             _phandle_node: A dict of Nodes indexed by phandle (an integer)
196             _valid_nodes: A list of nodes we wish to consider include in the
197                 platform data
198         """
199         self._phandle_node = {}
200         self._valid_nodes = []
201         return self.ScanNode(self.fdt.GetRoot());
202
203         for node in self.fdt.GetRoot().subnodes:
204             if 'compatible' in node.props:
205                 status = node.props.get('status')
206                 if (not self._options.include_disabled and not status or
207                     status.value != 'disabled'):
208                     node_list.append(node)
209                     phandle_prop = node.props.get('phandle')
210                     if phandle_prop:
211                         phandle = phandle_prop.GetPhandle()
212                         self._phandle_node[phandle] = node
213
214         self._valid_nodes = node_list
215
216     def IsPhandle(self, prop):
217         """Check if a node contains phandles
218
219         We have no reliable way of detecting whether a node uses a phandle
220         or not. As an interim measure, use a list of known property names.
221
222         Args:
223             prop: Prop object to check
224         Return:
225             True if the object value contains phandles, else False
226         """
227         if prop.name in ['clocks']:
228             return True
229         return False
230
231     def ScanStructs(self):
232         """Scan the device tree building up the C structures we will use.
233
234         Build a dict keyed by C struct name containing a dict of Prop
235         object for each struct field (keyed by property name). Where the
236         same struct appears multiple times, try to use the 'widest'
237         property, i.e. the one with a type which can express all others.
238
239         Once the widest property is determined, all other properties are
240         updated to match that width.
241         """
242         structs = {}
243         for node in self._valid_nodes:
244             node_name, _ = self.GetCompatName(node)
245             fields = {}
246
247             # Get a list of all the valid properties in this node.
248             for name, prop in node.props.items():
249                 if name not in PROP_IGNORE_LIST and name[0] != '#':
250                     fields[name] = copy.deepcopy(prop)
251
252             # If we've seen this node_name before, update the existing struct.
253             if node_name in structs:
254                 struct = structs[node_name]
255                 for name, prop in fields.items():
256                     oldprop = struct.get(name)
257                     if oldprop:
258                         oldprop.Widen(prop)
259                     else:
260                         struct[name] = prop
261
262             # Otherwise store this as a new struct.
263             else:
264                 structs[node_name] = fields
265
266         upto = 0
267         for node in self._valid_nodes:
268             node_name, _ = self.GetCompatName(node)
269             struct = structs[node_name]
270             for name, prop in node.props.items():
271                 if name not in PROP_IGNORE_LIST and name[0] != '#':
272                     prop.Widen(struct[name])
273             upto += 1
274
275             struct_name, aliases = self.GetCompatName(node)
276             for alias in aliases:
277                 self._aliases[alias] = struct_name
278
279         return structs
280
281     def ScanPhandles(self):
282         """Figure out what phandles each node uses
283
284         We need to be careful when outputing nodes that use phandles since
285         they must come after the declaration of the phandles in the C file.
286         Otherwise we get a compiler error since the phandle struct is not yet
287         declared.
288
289         This function adds to each node a list of phandle nodes that the node
290         depends on. This allows us to output things in the right order.
291         """
292         for node in self._valid_nodes:
293             node.phandles = set()
294             for pname, prop in node.props.items():
295                 if pname in PROP_IGNORE_LIST or pname[0] == '#':
296                     continue
297                 if type(prop.value) == list:
298                     if self.IsPhandle(prop):
299                         # Process the list as pairs of (phandle, id)
300                         it = iter(prop.value)
301                         for phandle_cell, id_cell in zip(it, it):
302                             phandle = fdt_util.fdt32_to_cpu(phandle_cell)
303                             id = fdt_util.fdt32_to_cpu(id_cell)
304                             target_node = self._phandle_node[phandle]
305                             node.phandles.add(target_node)
306
307
308     def GenerateStructs(self, structs):
309         """Generate struct defintions for the platform data
310
311         This writes out the body of a header file consisting of structure
312         definitions for node in self._valid_nodes. See the documentation in
313         README.of-plat for more information.
314         """
315         self.Out('#include <stdbool.h>\n')
316         self.Out('#include <libfdt.h>\n')
317
318         # Output the struct definition
319         for name in sorted(structs):
320             self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
321             for pname in sorted(structs[name]):
322                 prop = structs[name][pname]
323                 if self.IsPhandle(prop):
324                     # For phandles, include a reference to the target
325                     self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
326                                              Conv_name_to_c(prop.name),
327                                              len(prop.value) / 2))
328                 else:
329                     ptype = TYPE_NAMES[prop.type]
330                     self.Out('\t%s%s' % (TabTo(2, ptype),
331                                          Conv_name_to_c(prop.name)))
332                     if type(prop.value) == list:
333                         self.Out('[%d]' % len(prop.value))
334                 self.Out(';\n')
335             self.Out('};\n')
336
337         for alias, struct_name in self._aliases.iteritems():
338             self.Out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
339                                              STRUCT_PREFIX, struct_name))
340
341     def OutputNode(self, node):
342         """Output the C code for a node
343
344         Args:
345             node: node to output
346         """
347         struct_name, _ = self.GetCompatName(node)
348         var_name = Conv_name_to_c(node.name)
349         self.Buf('static struct %s%s %s%s = {\n' %
350             (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
351         for pname, prop in node.props.items():
352             if pname in PROP_IGNORE_LIST or pname[0] == '#':
353                 continue
354             ptype = TYPE_NAMES[prop.type]
355             member_name = Conv_name_to_c(prop.name)
356             self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
357
358             # Special handling for lists
359             if type(prop.value) == list:
360                 self.Buf('{')
361                 vals = []
362                 # For phandles, output a reference to the platform data
363                 # of the target node.
364                 if self.IsPhandle(prop):
365                     # Process the list as pairs of (phandle, id)
366                     it = iter(prop.value)
367                     for phandle_cell, id_cell in zip(it, it):
368                         phandle = fdt_util.fdt32_to_cpu(phandle_cell)
369                         id = fdt_util.fdt32_to_cpu(id_cell)
370                         target_node = self._phandle_node[phandle]
371                         name = Conv_name_to_c(target_node.name)
372                         vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
373                 else:
374                     for val in prop.value:
375                         vals.append(self.GetValue(prop.type, val))
376                 self.Buf(', '.join(vals))
377                 self.Buf('}')
378             else:
379                 self.Buf(self.GetValue(prop.type, prop.value))
380             self.Buf(',\n')
381         self.Buf('};\n')
382
383         # Add a device declaration
384         self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
385         self.Buf('\t.name\t\t= "%s",\n' % struct_name)
386         self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
387         self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' %
388                     (VAL_PREFIX, var_name))
389         self.Buf('};\n')
390         self.Buf('\n')
391
392         self.Out(''.join(self.GetBuf()))
393
394     def GenerateTables(self):
395         """Generate device defintions for the platform data
396
397         This writes out C platform data initialisation data and
398         U_BOOT_DEVICE() declarations for each valid node. Where a node has
399         multiple compatible strings, a #define is used to make them equivalent.
400
401         See the documentation in doc/driver-model/of-plat.txt for more
402         information.
403         """
404         self.Out('#include <common.h>\n')
405         self.Out('#include <dm.h>\n')
406         self.Out('#include <dt-structs.h>\n')
407         self.Out('\n')
408         nodes_to_output = list(self._valid_nodes)
409
410         # Keep outputing nodes until there is none left
411         while nodes_to_output:
412             node = nodes_to_output[0]
413             # Output all the node's dependencies first
414             for req_node in node.phandles:
415                 if req_node in nodes_to_output:
416                     self.OutputNode(req_node)
417                     nodes_to_output.remove(req_node)
418             self.OutputNode(node)
419             nodes_to_output.remove(node)
420
421
422 if __name__ != "__main__":
423     pass
424
425 parser = OptionParser()
426 parser.add_option('-d', '--dtb-file', action='store',
427                   help='Specify the .dtb input file')
428 parser.add_option('--include-disabled', action='store_true',
429                   help='Include disabled nodes')
430 parser.add_option('-o', '--output', action='store', default='-',
431                   help='Select output filename')
432 (options, args) = parser.parse_args()
433
434 if not args:
435     raise ValueError('Please specify a command: struct, platdata')
436
437 plat = DtbPlatdata(options.dtb_file, options)
438 plat.ScanDtb()
439 plat.ScanTree()
440 plat.SetupOutput(options.output)
441 structs = plat.ScanStructs()
442 plat.ScanPhandles()
443
444 for cmd in args[0].split(','):
445     if cmd == 'struct':
446         plat.GenerateStructs(structs)
447     elif cmd == 'platdata':
448         plat.GenerateTables()
449     else:
450         raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)