odroid: remove CONFIG_DM_I2C_COMPAT config
[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_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 ScanPhandles(self):
276         """Figure out what phandles each node uses
277
278         We need to be careful when outputing nodes that use phandles since
279         they must come after the declaration of the phandles in the C file.
280         Otherwise we get a compiler error since the phandle struct is not yet
281         declared.
282
283         This function adds to each node a list of phandle nodes that the node
284         depends on. This allows us to output things in the right order.
285         """
286         for node in self._valid_nodes:
287             node.phandles = set()
288             for pname, prop in node.props.items():
289                 if pname in PROP_IGNORE_LIST or pname[0] == '#':
290                     continue
291                 if type(prop.value) == list:
292                     if self.IsPhandle(prop):
293                         # Process the list as pairs of (phandle, id)
294                         it = iter(prop.value)
295                         for phandle_cell, id_cell in zip(it, it):
296                             phandle = fdt_util.fdt32_to_cpu(phandle_cell)
297                             id = fdt_util.fdt32_to_cpu(id_cell)
298                             target_node = self._phandle_node[phandle]
299                             node.phandles.add(target_node)
300
301
302     def GenerateStructs(self, structs):
303         """Generate struct defintions for the platform data
304
305         This writes out the body of a header file consisting of structure
306         definitions for node in self._valid_nodes. See the documentation in
307         README.of-plat for more information.
308         """
309         self.Out('#include <stdbool.h>\n')
310         self.Out('#include <libfdt.h>\n')
311
312         # Output the struct definition
313         for name in sorted(structs):
314             self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
315             for pname in sorted(structs[name]):
316                 prop = structs[name][pname]
317                 if self.IsPhandle(prop):
318                     # For phandles, include a reference to the target
319                     self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
320                                              Conv_name_to_c(prop.name),
321                                              len(prop.value) / 2))
322                 else:
323                     ptype = TYPE_NAMES[prop.type]
324                     self.Out('\t%s%s' % (TabTo(2, ptype),
325                                          Conv_name_to_c(prop.name)))
326                     if type(prop.value) == list:
327                         self.Out('[%d]' % len(prop.value))
328                 self.Out(';\n')
329             self.Out('};\n')
330
331     def OutputNode(self, node):
332         """Output the C code for a node
333
334         Args:
335             node: node to output
336         """
337         struct_name = self.GetCompatName(node)
338         var_name = Conv_name_to_c(node.name)
339         self.Buf('static struct %s%s %s%s = {\n' %
340             (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
341         for pname, prop in node.props.items():
342             if pname in PROP_IGNORE_LIST or pname[0] == '#':
343                 continue
344             ptype = TYPE_NAMES[prop.type]
345             member_name = Conv_name_to_c(prop.name)
346             self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
347
348             # Special handling for lists
349             if type(prop.value) == list:
350                 self.Buf('{')
351                 vals = []
352                 # For phandles, output a reference to the platform data
353                 # of the target node.
354                 if self.IsPhandle(prop):
355                     # Process the list as pairs of (phandle, id)
356                     it = iter(prop.value)
357                     for phandle_cell, id_cell in zip(it, it):
358                         phandle = fdt_util.fdt32_to_cpu(phandle_cell)
359                         id = fdt_util.fdt32_to_cpu(id_cell)
360                         target_node = self._phandle_node[phandle]
361                         name = Conv_name_to_c(target_node.name)
362                         vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
363                 else:
364                     for val in prop.value:
365                         vals.append(self.GetValue(prop.type, val))
366                 self.Buf(', '.join(vals))
367                 self.Buf('}')
368             else:
369                 self.Buf(self.GetValue(prop.type, prop.value))
370             self.Buf(',\n')
371         self.Buf('};\n')
372
373         # Add a device declaration
374         self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
375         self.Buf('\t.name\t\t= "%s",\n' % struct_name)
376         self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
377         self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' %
378                     (VAL_PREFIX, var_name))
379         self.Buf('};\n')
380         self.Buf('\n')
381
382         self.Out(''.join(self.GetBuf()))
383
384     def GenerateTables(self):
385         """Generate device defintions for the platform data
386
387         This writes out C platform data initialisation data and
388         U_BOOT_DEVICE() declarations for each valid node. See the
389         documentation in README.of-plat for more information.
390         """
391         self.Out('#include <common.h>\n')
392         self.Out('#include <dm.h>\n')
393         self.Out('#include <dt-structs.h>\n')
394         self.Out('\n')
395         nodes_to_output = list(self._valid_nodes)
396
397         # Keep outputing nodes until there is none left
398         while nodes_to_output:
399             node = nodes_to_output[0]
400             # Output all the node's dependencies first
401             for req_node in node.phandles:
402                 if req_node in nodes_to_output:
403                     self.OutputNode(req_node)
404                     nodes_to_output.remove(req_node)
405             self.OutputNode(node)
406             nodes_to_output.remove(node)
407
408
409 if __name__ != "__main__":
410     pass
411
412 parser = OptionParser()
413 parser.add_option('-d', '--dtb-file', action='store',
414                   help='Specify the .dtb input file')
415 parser.add_option('--include-disabled', action='store_true',
416                   help='Include disabled nodes')
417 parser.add_option('-o', '--output', action='store', default='-',
418                   help='Select output filename')
419 (options, args) = parser.parse_args()
420
421 if not args:
422     raise ValueError('Please specify a command: struct, platdata')
423
424 plat = DtbPlatdata(options.dtb_file, options)
425 plat.ScanDtb()
426 plat.ScanTree()
427 plat.SetupOutput(options.output)
428 structs = plat.ScanStructs()
429 plat.ScanPhandles()
430
431 for cmd in args[0].split(','):
432     if cmd == 'struct':
433         plat.GenerateStructs(structs)
434     elif cmd == 'platdata':
435         plat.GenerateTables()
436     else:
437         raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)