blob: 08e35f148c0d9828b4e3118d230c57abe498676d [file] [log] [blame]
Simon Glassbfad22e2016-07-04 11:58:09 -06001#!/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
9import copy
10from optparse import OptionError, OptionParser
11import os
Simon Glass8a305142016-07-25 18:59:01 -060012import struct
Simon Glassbfad22e2016-07-04 11:58:09 -060013import sys
14
Simon Glassbfad22e2016-07-04 11:58:09 -060015# Bring in the patman libraries
16our_path = os.path.dirname(os.path.realpath(__file__))
17sys.path.append(os.path.join(our_path, '../patman'))
18
Simon Glassb1a5e262016-07-25 18:59:05 -060019import fdt
Simon Glassb71ef892016-07-25 18:59:02 -060020import fdt_util
Simon Glassbfad22e2016-07-04 11:58:09 -060021
Simon Glassbfad22e2016-07-04 11:58:09 -060022# When we see these properties we ignore them - i.e. do not create a structure member
23PROP_IGNORE_LIST = [
24 '#address-cells',
25 '#gpio-cells',
26 '#size-cells',
27 'compatible',
28 'linux,phandle',
29 "status",
30 'phandle',
Simon Glass6522a552016-07-04 11:58:16 -060031 'u-boot,dm-pre-reloc',
Heiko Stübner9a2cdca2017-02-18 19:46:21 +010032 'u-boot,dm-tpl',
33 'u-boot,dm-spl',
Simon Glassbfad22e2016-07-04 11:58:09 -060034]
35
36# C type declarations for the tyues we support
37TYPE_NAMES = {
Simon Glassb1a5e262016-07-25 18:59:05 -060038 fdt.TYPE_INT: 'fdt32_t',
39 fdt.TYPE_BYTE: 'unsigned char',
40 fdt.TYPE_STRING: 'const char *',
41 fdt.TYPE_BOOL: 'bool',
Simon Glassbfad22e2016-07-04 11:58:09 -060042};
43
44STRUCT_PREFIX = 'dtd_'
45VAL_PREFIX = 'dtv_'
46
47def 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(',', '_')
Simon Glassb17ab152017-01-15 21:09:08 -070058 str = str.replace('.', '_')
Simon Glassbfad22e2016-07-04 11:58:09 -060059 str = str.replace('/', '__')
60 return str
61
62def TabTo(num_tabs, str):
63 if len(str) >= num_tabs * 8:
64 return str + ' '
Paul Burton493dd312016-09-27 16:03:58 +010065 return str + '\t' * (num_tabs - len(str) // 8)
Simon Glassbfad22e2016-07-04 11:58:09 -060066
67class 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
92 def SetupOutput(self, fname):
93 """Set up the output destination
94
95 Once this is done, future calls to self.Out() will output to this
96 file.
97
98 Args:
99 fname: Filename to send output to, or '-' for stdout
100 """
101 if fname == '-':
102 self._outfile = sys.stdout
103 else:
104 self._outfile = open(fname, 'w')
105
106 def Out(self, str):
107 """Output a string to the output file
108
109 Args:
110 str: String to output
111 """
112 self._outfile.write(str)
113
114 def Buf(self, str):
115 """Buffer up a string to send later
116
117 Args:
118 str: String to add to our 'buffer' list
119 """
120 self._lines.append(str)
121
122 def GetBuf(self):
123 """Get the contents of the output buffer, and clear it
124
125 Returns:
126 The output buffer, which is then cleared for future use
127 """
128 lines = self._lines
129 self._lines = []
130 return lines
131
132 def GetValue(self, type, value):
133 """Get a value as a C expression
134
135 For integers this returns a byte-swapped (little-endian) hex string
136 For bytes this returns a hex string, e.g. 0x12
137 For strings this returns a literal string enclosed in quotes
138 For booleans this return 'true'
139
140 Args:
141 type: Data type (fdt_util)
142 value: Data value, as a string of bytes
143 """
Simon Glassb1a5e262016-07-25 18:59:05 -0600144 if type == fdt.TYPE_INT:
Simon Glassbfad22e2016-07-04 11:58:09 -0600145 return '%#x' % fdt_util.fdt32_to_cpu(value)
Simon Glassb1a5e262016-07-25 18:59:05 -0600146 elif type == fdt.TYPE_BYTE:
Simon Glassbfad22e2016-07-04 11:58:09 -0600147 return '%#x' % ord(value[0])
Simon Glassb1a5e262016-07-25 18:59:05 -0600148 elif type == fdt.TYPE_STRING:
Simon Glassbfad22e2016-07-04 11:58:09 -0600149 return '"%s"' % value
Simon Glassb1a5e262016-07-25 18:59:05 -0600150 elif type == fdt.TYPE_BOOL:
Simon Glassbfad22e2016-07-04 11:58:09 -0600151 return 'true'
152
153 def GetCompatName(self, node):
154 """Get a node's first compatible string as a C identifier
155
156 Args:
157 node: Node object to check
158 Return:
159 C identifier for the first compatible string
160 """
161 compat = node.props['compatible'].value
162 if type(compat) == list:
163 compat = compat[0]
164 return Conv_name_to_c(compat)
165
166 def ScanDtb(self):
167 """Scan the device tree to obtain a tree of notes and properties
168
169 Once this is done, self.fdt.GetRoot() can be called to obtain the
170 device tree root node, and progress from there.
171 """
Simon Glassa9440932017-05-27 07:38:30 -0600172 self.fdt = fdt.FdtScan(self._dtb_fname)
Simon Glassbfad22e2016-07-04 11:58:09 -0600173
Philipp Tomsichf31ca1e2017-02-22 19:06:04 +0100174 def ScanNode(self, root):
175 for node in root.subnodes:
176 if 'compatible' in node.props:
177 status = node.props.get('status')
178 if (not options.include_disabled and not status or
179 status.value != 'disabled'):
180 self._valid_nodes.append(node)
181 phandle_prop = node.props.get('phandle')
182 if phandle_prop:
183 phandle = phandle_prop.GetPhandle()
184 self._phandle_node[phandle] = node
185
186 # recurse to handle any subnodes
187 self.ScanNode(node);
188
Simon Glassbfad22e2016-07-04 11:58:09 -0600189 def ScanTree(self):
190 """Scan the device tree for useful information
191
192 This fills in the following properties:
193 _phandle_node: A dict of Nodes indexed by phandle (an integer)
194 _valid_nodes: A list of nodes we wish to consider include in the
195 platform data
196 """
Simon Glassbfad22e2016-07-04 11:58:09 -0600197 self._phandle_node = {}
Philipp Tomsichf31ca1e2017-02-22 19:06:04 +0100198 self._valid_nodes = []
199 return self.ScanNode(self.fdt.GetRoot());
200
Simon Glassbfad22e2016-07-04 11:58:09 -0600201 for node in self.fdt.GetRoot().subnodes:
202 if 'compatible' in node.props:
203 status = node.props.get('status')
204 if (not options.include_disabled and not status or
205 status.value != 'disabled'):
206 node_list.append(node)
207 phandle_prop = node.props.get('phandle')
208 if phandle_prop:
209 phandle = phandle_prop.GetPhandle()
210 self._phandle_node[phandle] = node
211
212 self._valid_nodes = node_list
213
214 def IsPhandle(self, prop):
215 """Check if a node contains phandles
216
217 We have no reliable way of detecting whether a node uses a phandle
218 or not. As an interim measure, use a list of known property names.
219
220 Args:
221 prop: Prop object to check
222 Return:
223 True if the object value contains phandles, else False
224 """
225 if prop.name in ['clocks']:
226 return True
227 return False
228
229 def ScanStructs(self):
230 """Scan the device tree building up the C structures we will use.
231
232 Build a dict keyed by C struct name containing a dict of Prop
233 object for each struct field (keyed by property name). Where the
234 same struct appears multiple times, try to use the 'widest'
235 property, i.e. the one with a type which can express all others.
236
237 Once the widest property is determined, all other properties are
238 updated to match that width.
239 """
240 structs = {}
241 for node in self._valid_nodes:
242 node_name = self.GetCompatName(node)
243 fields = {}
244
245 # Get a list of all the valid properties in this node.
Paul Burton0a954482016-09-27 16:03:56 +0100246 for name, prop in node.props.items():
Simon Glassbfad22e2016-07-04 11:58:09 -0600247 if name not in PROP_IGNORE_LIST and name[0] != '#':
248 fields[name] = copy.deepcopy(prop)
249
250 # If we've seen this node_name before, update the existing struct.
251 if node_name in structs:
252 struct = structs[node_name]
Paul Burton0a954482016-09-27 16:03:56 +0100253 for name, prop in fields.items():
Simon Glassbfad22e2016-07-04 11:58:09 -0600254 oldprop = struct.get(name)
255 if oldprop:
256 oldprop.Widen(prop)
257 else:
258 struct[name] = prop
259
260 # Otherwise store this as a new struct.
261 else:
262 structs[node_name] = fields
263
264 upto = 0
265 for node in self._valid_nodes:
266 node_name = self.GetCompatName(node)
267 struct = structs[node_name]
Paul Burton0a954482016-09-27 16:03:56 +0100268 for name, prop in node.props.items():
Simon Glassbfad22e2016-07-04 11:58:09 -0600269 if name not in PROP_IGNORE_LIST and name[0] != '#':
270 prop.Widen(struct[name])
271 upto += 1
272 return structs
273
Simon Glass33ff6792017-04-22 18:42:22 -0600274 def ScanPhandles(self):
275 """Figure out what phandles each node uses
276
277 We need to be careful when outputing nodes that use phandles since
278 they must come after the declaration of the phandles in the C file.
279 Otherwise we get a compiler error since the phandle struct is not yet
280 declared.
281
282 This function adds to each node a list of phandle nodes that the node
283 depends on. This allows us to output things in the right order.
284 """
285 for node in self._valid_nodes:
286 node.phandles = set()
287 for pname, prop in node.props.items():
288 if pname in PROP_IGNORE_LIST or pname[0] == '#':
289 continue
290 if type(prop.value) == list:
291 if self.IsPhandle(prop):
292 # Process the list as pairs of (phandle, id)
293 it = iter(prop.value)
294 for phandle_cell, id_cell in zip(it, it):
295 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
296 id = fdt_util.fdt32_to_cpu(id_cell)
297 target_node = self._phandle_node[phandle]
298 node.phandles.add(target_node)
299
300
Simon Glassbfad22e2016-07-04 11:58:09 -0600301 def GenerateStructs(self, structs):
302 """Generate struct defintions for the platform data
303
304 This writes out the body of a header file consisting of structure
305 definitions for node in self._valid_nodes. See the documentation in
306 README.of-plat for more information.
307 """
308 self.Out('#include <stdbool.h>\n')
309 self.Out('#include <libfdt.h>\n')
310
311 # Output the struct definition
312 for name in sorted(structs):
313 self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
314 for pname in sorted(structs[name]):
315 prop = structs[name][pname]
316 if self.IsPhandle(prop):
317 # For phandles, include a reference to the target
318 self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
319 Conv_name_to_c(prop.name),
320 len(prop.value) / 2))
321 else:
322 ptype = TYPE_NAMES[prop.type]
323 self.Out('\t%s%s' % (TabTo(2, ptype),
324 Conv_name_to_c(prop.name)))
325 if type(prop.value) == list:
326 self.Out('[%d]' % len(prop.value))
327 self.Out(';\n')
328 self.Out('};\n')
329
Simon Glass67d23322017-04-22 18:42:21 -0600330 def OutputNode(self, node):
331 """Output the C code for a node
332
333 Args:
334 node: node to output
335 """
336 struct_name = self.GetCompatName(node)
337 var_name = Conv_name_to_c(node.name)
338 self.Buf('static struct %s%s %s%s = {\n' %
339 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
340 for pname, prop in node.props.items():
341 if pname in PROP_IGNORE_LIST or pname[0] == '#':
342 continue
343 ptype = TYPE_NAMES[prop.type]
344 member_name = Conv_name_to_c(prop.name)
345 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
346
347 # Special handling for lists
348 if type(prop.value) == list:
349 self.Buf('{')
350 vals = []
351 # For phandles, output a reference to the platform data
352 # of the target node.
353 if self.IsPhandle(prop):
354 # Process the list as pairs of (phandle, id)
355 it = iter(prop.value)
356 for phandle_cell, id_cell in zip(it, it):
357 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
358 id = fdt_util.fdt32_to_cpu(id_cell)
359 target_node = self._phandle_node[phandle]
360 name = Conv_name_to_c(target_node.name)
361 vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
362 else:
363 for val in prop.value:
364 vals.append(self.GetValue(prop.type, val))
365 self.Buf(', '.join(vals))
366 self.Buf('}')
367 else:
368 self.Buf(self.GetValue(prop.type, prop.value))
369 self.Buf(',\n')
370 self.Buf('};\n')
371
372 # Add a device declaration
373 self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
374 self.Buf('\t.name\t\t= "%s",\n' % struct_name)
375 self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
376 self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' %
377 (VAL_PREFIX, var_name))
378 self.Buf('};\n')
379 self.Buf('\n')
380
Simon Glass33ff6792017-04-22 18:42:22 -0600381 self.Out(''.join(self.GetBuf()))
382
Simon Glassbfad22e2016-07-04 11:58:09 -0600383 def GenerateTables(self):
384 """Generate device defintions for the platform data
385
386 This writes out C platform data initialisation data and
387 U_BOOT_DEVICE() declarations for each valid node. See the
388 documentation in README.of-plat for more information.
389 """
390 self.Out('#include <common.h>\n')
391 self.Out('#include <dm.h>\n')
392 self.Out('#include <dt-structs.h>\n')
393 self.Out('\n')
Simon Glass33ff6792017-04-22 18:42:22 -0600394 nodes_to_output = list(self._valid_nodes)
Simon Glassbfad22e2016-07-04 11:58:09 -0600395
Simon Glass33ff6792017-04-22 18:42:22 -0600396 # Keep outputing nodes until there is none left
397 while nodes_to_output:
398 node = nodes_to_output[0]
399 # Output all the node's dependencies first
400 for req_node in node.phandles:
401 if req_node in nodes_to_output:
402 self.OutputNode(req_node)
403 nodes_to_output.remove(req_node)
404 self.OutputNode(node)
405 nodes_to_output.remove(node)
Simon Glassbfad22e2016-07-04 11:58:09 -0600406
407
408if __name__ != "__main__":
409 pass
410
411parser = OptionParser()
412parser.add_option('-d', '--dtb-file', action='store',
413 help='Specify the .dtb input file')
414parser.add_option('--include-disabled', action='store_true',
415 help='Include disabled nodes')
416parser.add_option('-o', '--output', action='store', default='-',
417 help='Select output filename')
418(options, args) = parser.parse_args()
419
420if not args:
421 raise ValueError('Please specify a command: struct, platdata')
422
423plat = DtbPlatdata(options.dtb_file, options)
424plat.ScanDtb()
425plat.ScanTree()
426plat.SetupOutput(options.output)
427structs = plat.ScanStructs()
Simon Glass33ff6792017-04-22 18:42:22 -0600428plat.ScanPhandles()
Simon Glassbfad22e2016-07-04 11:58:09 -0600429
430for cmd in args[0].split(','):
431 if cmd == 'struct':
432 plat.GenerateStructs(structs)
433 elif cmd == 'platdata':
434 plat.GenerateTables()
435 else:
436 raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)