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