dtoc: Don't handle properties with / in them
[platform/kernel/u-boot.git] / tools / dtoc / dtb_platdata.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2017 Google, Inc
4 # Written by Simon Glass <sjg@chromium.org>
5 #
6 # SPDX-License-Identifier:      GPL-2.0+
7 #
8
9 """Device tree to platform data class
10
11 This supports converting device tree data to C structures definitions and
12 static data.
13 """
14
15 import copy
16 import sys
17
18 import fdt
19 import fdt_util
20
21 # When we see these properties we ignore them - i.e. do not create a structure member
22 PROP_IGNORE_LIST = [
23     '#address-cells',
24     '#gpio-cells',
25     '#size-cells',
26     'compatible',
27     'linux,phandle',
28     "status",
29     'phandle',
30     'u-boot,dm-pre-reloc',
31     'u-boot,dm-tpl',
32     'u-boot,dm-spl',
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     new = name.replace('@', '_at_')
55     new = new.replace('-', '_')
56     new = new.replace(',', '_')
57     new = new.replace('.', '_')
58     return new
59
60 def tab_to(num_tabs, line):
61     """Append tabs to a line of text to reach a tab stop.
62
63     Args:
64         num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
65         line: Line of text to append to
66
67     Returns:
68         line with the correct number of tabs appeneded. If the line already
69         extends past that tab stop then a single space is appended.
70     """
71     if len(line) >= num_tabs * 8:
72         return line + ' '
73     return line + '\t' * (num_tabs - len(line) // 8)
74
75 class DtbPlatdata(object):
76     """Provide a means to convert device tree binary data to platform data
77
78     The output of this process is C structures which can be used in space-
79     constrained encvironments where the ~3KB code overhead of device tree
80     code is not affordable.
81
82     Properties:
83         _fdt: Fdt object, referencing the device tree
84         _dtb_fname: Filename of the input device tree binary file
85         _valid_nodes: A list of Node object with compatible strings
86         _options: Command-line options
87         _phandle_nodes: A dict of nodes indexed by phandle number (1, 2...)
88         _outfile: The current output file (sys.stdout or a real file)
89         _lines: Stashed list of output lines for outputting in the future
90         _phandle_nodes: A dict of Nodes indexed by phandle (an integer)
91     """
92     def __init__(self, dtb_fname, options):
93         self._fdt = None
94         self._dtb_fname = dtb_fname
95         self._valid_nodes = None
96         self._options = options
97         self._phandle_nodes = {}
98         self._outfile = None
99         self._lines = []
100         self._aliases = {}
101
102     def setup_output(self, fname):
103         """Set up the output destination
104
105         Once this is done, future calls to self.out() will output to this
106         file.
107
108         Args:
109             fname: Filename to send output to, or '-' for stdout
110         """
111         if fname == '-':
112             self._outfile = sys.stdout
113         else:
114             self._outfile = open(fname, 'w')
115
116     def out(self, line):
117         """Output a string to the output file
118
119         Args:
120             line: String to output
121         """
122         self._outfile.write(line)
123
124     def buf(self, line):
125         """Buffer up a string to send later
126
127         Args:
128             line: String to add to our 'buffer' list
129         """
130         self._lines.append(line)
131
132     def get_buf(self):
133         """Get the contents of the output buffer, and clear it
134
135         Returns:
136             The output buffer, which is then cleared for future use
137         """
138         lines = self._lines
139         self._lines = []
140         return lines
141
142     @staticmethod
143     def get_value(ftype, value):
144         """Get a value as a C expression
145
146         For integers this returns a byte-swapped (little-endian) hex string
147         For bytes this returns a hex string, e.g. 0x12
148         For strings this returns a literal string enclosed in quotes
149         For booleans this return 'true'
150
151         Args:
152             type: Data type (fdt_util)
153             value: Data value, as a string of bytes
154         """
155         if ftype == fdt.TYPE_INT:
156             return '%#x' % fdt_util.fdt32_to_cpu(value)
157         elif ftype == fdt.TYPE_BYTE:
158             return '%#x' % ord(value[0])
159         elif ftype == fdt.TYPE_STRING:
160             return '"%s"' % value
161         elif ftype == fdt.TYPE_BOOL:
162             return 'true'
163
164     @staticmethod
165     def get_compat_name(node):
166         """Get a node's first compatible string as a C identifier
167
168         Args:
169             node: Node object to check
170         Return:
171             C identifier for the first compatible string
172         """
173         compat = node.props['compatible'].value
174         aliases = []
175         if isinstance(compat, list):
176             compat, aliases = compat[0], compat[1:]
177         return conv_name_to_c(compat), [conv_name_to_c(a) for a in aliases]
178
179     def scan_dtb(self):
180         """Scan the device tree to obtain a tree of notes and properties
181
182         Once this is done, self._fdt.GetRoot() can be called to obtain the
183         device tree root node, and progress from there.
184         """
185         self._fdt = fdt.FdtScan(self._dtb_fname)
186
187     def scan_node(self, root):
188         """Scan a node and subnodes to build a tree of node and phandle info
189
190         This adds each node to self._valid_nodes and each phandle to
191         self._phandle_nodes.
192
193         Args:
194             root: Root node for scan
195         """
196         for node in root.subnodes:
197             if 'compatible' in node.props:
198                 status = node.props.get('status')
199                 if (not self._options.include_disabled and not status or
200                         status.value != 'disabled'):
201                     self._valid_nodes.append(node)
202                     phandle_prop = node.props.get('phandle')
203                     if phandle_prop:
204                         phandle = phandle_prop.GetPhandle()
205                         self._phandle_nodes[phandle] = node
206
207             # recurse to handle any subnodes
208             self.scan_node(node)
209
210     def scan_tree(self):
211         """Scan the device tree for useful information
212
213         This fills in the following properties:
214             _phandle_nodes: A dict of Nodes indexed by phandle (an integer)
215             _valid_nodes: A list of nodes we wish to consider include in the
216                 platform data
217         """
218         self._phandle_nodes = {}
219         self._valid_nodes = []
220         return self.scan_node(self._fdt.GetRoot())
221
222     @staticmethod
223     def is_phandle(prop):
224         """Check if a node contains phandles
225
226         We have no reliable way of detecting whether a node uses a phandle
227         or not. As an interim measure, use a list of known property names.
228
229         Args:
230             prop: Prop object to check
231         Return:
232             True if the object value contains phandles, else False
233         """
234         if prop.name in ['clocks']:
235             return True
236         return False
237
238     def scan_structs(self):
239         """Scan the device tree building up the C structures we will use.
240
241         Build a dict keyed by C struct name containing a dict of Prop
242         object for each struct field (keyed by property name). Where the
243         same struct appears multiple times, try to use the 'widest'
244         property, i.e. the one with a type which can express all others.
245
246         Once the widest property is determined, all other properties are
247         updated to match that width.
248         """
249         structs = {}
250         for node in self._valid_nodes:
251             node_name, _ = self.get_compat_name(node)
252             fields = {}
253
254             # Get a list of all the valid properties in this node.
255             for name, prop in node.props.items():
256                 if name not in PROP_IGNORE_LIST and name[0] != '#':
257                     fields[name] = copy.deepcopy(prop)
258
259             # If we've seen this node_name before, update the existing struct.
260             if node_name in structs:
261                 struct = structs[node_name]
262                 for name, prop in fields.items():
263                     oldprop = struct.get(name)
264                     if oldprop:
265                         oldprop.Widen(prop)
266                     else:
267                         struct[name] = prop
268
269             # Otherwise store this as a new struct.
270             else:
271                 structs[node_name] = fields
272
273         upto = 0
274         for node in self._valid_nodes:
275             node_name, _ = self.get_compat_name(node)
276             struct = structs[node_name]
277             for name, prop in node.props.items():
278                 if name not in PROP_IGNORE_LIST and name[0] != '#':
279                     prop.Widen(struct[name])
280             upto += 1
281
282             struct_name, aliases = self.get_compat_name(node)
283             for alias in aliases:
284                 self._aliases[alias] = struct_name
285
286         return structs
287
288     def scan_phandles(self):
289         """Figure out what phandles each node uses
290
291         We need to be careful when outputing nodes that use phandles since
292         they must come after the declaration of the phandles in the C file.
293         Otherwise we get a compiler error since the phandle struct is not yet
294         declared.
295
296         This function adds to each node a list of phandle nodes that the node
297         depends on. This allows us to output things in the right order.
298         """
299         for node in self._valid_nodes:
300             node.phandles = set()
301             for pname, prop in node.props.items():
302                 if pname in PROP_IGNORE_LIST or pname[0] == '#':
303                     continue
304                 if isinstance(prop.value, list):
305                     if self.is_phandle(prop):
306                         # Process the list as pairs of (phandle, id)
307                         value_it = iter(prop.value)
308                         for phandle_cell, _ in zip(value_it, value_it):
309                             phandle = fdt_util.fdt32_to_cpu(phandle_cell)
310                             target_node = self._phandle_nodes[phandle]
311                             node.phandles.add(target_node)
312
313
314     def generate_structs(self, structs):
315         """Generate struct defintions for the platform data
316
317         This writes out the body of a header file consisting of structure
318         definitions for node in self._valid_nodes. See the documentation in
319         README.of-plat for more information.
320         """
321         self.out('#include <stdbool.h>\n')
322         self.out('#include <libfdt.h>\n')
323
324         # Output the struct definition
325         for name in sorted(structs):
326             self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
327             for pname in sorted(structs[name]):
328                 prop = structs[name][pname]
329                 if self.is_phandle(prop):
330                     # For phandles, include a reference to the target
331                     self.out('\t%s%s[%d]' % (tab_to(2, 'struct phandle_2_cell'),
332                                              conv_name_to_c(prop.name),
333                                              len(prop.value) / 2))
334                 else:
335                     ptype = TYPE_NAMES[prop.type]
336                     self.out('\t%s%s' % (tab_to(2, ptype),
337                                          conv_name_to_c(prop.name)))
338                     if isinstance(prop.value, list):
339                         self.out('[%d]' % len(prop.value))
340                 self.out(';\n')
341             self.out('};\n')
342
343         for alias, struct_name in self._aliases.iteritems():
344             self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
345                                              STRUCT_PREFIX, struct_name))
346
347     def output_node(self, node):
348         """Output the C code for a node
349
350         Args:
351             node: node to output
352         """
353         struct_name, _ = self.get_compat_name(node)
354         var_name = conv_name_to_c(node.name)
355         self.buf('static struct %s%s %s%s = {\n' %
356                  (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
357         for pname, prop in node.props.items():
358             if pname in PROP_IGNORE_LIST or pname[0] == '#':
359                 continue
360             member_name = conv_name_to_c(prop.name)
361             self.buf('\t%s= ' % tab_to(3, '.' + member_name))
362
363             # Special handling for lists
364             if isinstance(prop.value, list):
365                 self.buf('{')
366                 vals = []
367                 # For phandles, output a reference to the platform data
368                 # of the target node.
369                 if self.is_phandle(prop):
370                     # Process the list as pairs of (phandle, id)
371                     value_it = iter(prop.value)
372                     for phandle_cell, id_cell in zip(value_it, value_it):
373                         phandle = fdt_util.fdt32_to_cpu(phandle_cell)
374                         id_num = fdt_util.fdt32_to_cpu(id_cell)
375                         target_node = self._phandle_nodes[phandle]
376                         name = conv_name_to_c(target_node.name)
377                         vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id_num))
378                 else:
379                     for val in prop.value:
380                         vals.append(self.get_value(prop.type, val))
381                 self.buf(', '.join(vals))
382                 self.buf('}')
383             else:
384                 self.buf(self.get_value(prop.type, prop.value))
385             self.buf(',\n')
386         self.buf('};\n')
387
388         # Add a device declaration
389         self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
390         self.buf('\t.name\t\t= "%s",\n' % struct_name)
391         self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
392         self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
393         self.buf('};\n')
394         self.buf('\n')
395
396         self.out(''.join(self.get_buf()))
397
398     def generate_tables(self):
399         """Generate device defintions for the platform data
400
401         This writes out C platform data initialisation data and
402         U_BOOT_DEVICE() declarations for each valid node. Where a node has
403         multiple compatible strings, a #define is used to make them equivalent.
404
405         See the documentation in doc/driver-model/of-plat.txt for more
406         information.
407         """
408         self.out('#include <common.h>\n')
409         self.out('#include <dm.h>\n')
410         self.out('#include <dt-structs.h>\n')
411         self.out('\n')
412         nodes_to_output = list(self._valid_nodes)
413
414         # Keep outputing nodes until there is none left
415         while nodes_to_output:
416             node = nodes_to_output[0]
417             # Output all the node's dependencies first
418             for req_node in node.phandles:
419                 if req_node in nodes_to_output:
420                     self.output_node(req_node)
421                     nodes_to_output.remove(req_node)
422             self.output_node(node)
423             nodes_to_output.remove(node)