blob: 2e0b9c04e209e2b9b5297886c66152445b34b676 [file] [log] [blame]
Simon Glass69f2ed72016-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 Glass58593112016-07-25 18:59:01 -060012import struct
Simon Glass69f2ed72016-07-04 11:58:09 -060013import sys
14
Simon Glass69f2ed72016-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 Glassbc1dea32016-07-25 18:59:05 -060019import fdt
Simon Glassba482582016-07-25 18:59:02 -060020import fdt_select
21import fdt_util
Simon Glass69f2ed72016-07-04 11:58:09 -060022
Simon Glass69f2ed72016-07-04 11:58:09 -060023# When we see these properties we ignore them - i.e. do not create a structure member
24PROP_IGNORE_LIST = [
25 '#address-cells',
26 '#gpio-cells',
27 '#size-cells',
28 'compatible',
29 'linux,phandle',
30 "status",
31 'phandle',
Simon Glassefefe122016-07-04 11:58:16 -060032 'u-boot,dm-pre-reloc',
Heiko Stübner27326c72017-02-18 19:46:21 +010033 'u-boot,dm-tpl',
34 'u-boot,dm-spl',
Simon Glass69f2ed72016-07-04 11:58:09 -060035]
36
37# C type declarations for the tyues we support
38TYPE_NAMES = {
Simon Glassbc1dea32016-07-25 18:59:05 -060039 fdt.TYPE_INT: 'fdt32_t',
40 fdt.TYPE_BYTE: 'unsigned char',
41 fdt.TYPE_STRING: 'const char *',
42 fdt.TYPE_BOOL: 'bool',
Simon Glass69f2ed72016-07-04 11:58:09 -060043};
44
45STRUCT_PREFIX = 'dtd_'
46VAL_PREFIX = 'dtv_'
47
48def 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(',', '_')
Simon Glass6b6024a2017-01-15 21:09:08 -070059 str = str.replace('.', '_')
Simon Glass69f2ed72016-07-04 11:58:09 -060060 str = str.replace('/', '__')
61 return str
62
63def TabTo(num_tabs, str):
64 if len(str) >= num_tabs * 8:
65 return str + ' '
Paul Burton34c38892016-09-27 16:03:58 +010066 return str + '\t' * (num_tabs - len(str) // 8)
Simon Glass69f2ed72016-07-04 11:58:09 -060067
68class 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 """
Simon Glassbc1dea32016-07-25 18:59:05 -0600145 if type == fdt.TYPE_INT:
Simon Glass69f2ed72016-07-04 11:58:09 -0600146 return '%#x' % fdt_util.fdt32_to_cpu(value)
Simon Glassbc1dea32016-07-25 18:59:05 -0600147 elif type == fdt.TYPE_BYTE:
Simon Glass69f2ed72016-07-04 11:58:09 -0600148 return '%#x' % ord(value[0])
Simon Glassbc1dea32016-07-25 18:59:05 -0600149 elif type == fdt.TYPE_STRING:
Simon Glass69f2ed72016-07-04 11:58:09 -0600150 return '"%s"' % value
Simon Glassbc1dea32016-07-25 18:59:05 -0600151 elif type == fdt.TYPE_BOOL:
Simon Glass69f2ed72016-07-04 11:58:09 -0600152 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 """
Simon Glassba482582016-07-25 18:59:02 -0600173 self.fdt = fdt_select.FdtScan(self._dtb_fname)
Simon Glass69f2ed72016-07-04 11:58:09 -0600174
Philipp Tomsich55bc0802017-02-22 19:06:04 +0100175 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
Simon Glass69f2ed72016-07-04 11:58:09 -0600190 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 """
Simon Glass69f2ed72016-07-04 11:58:09 -0600198 self._phandle_node = {}
Philipp Tomsich55bc0802017-02-22 19:06:04 +0100199 self._valid_nodes = []
200 return self.ScanNode(self.fdt.GetRoot());
201
Simon Glass69f2ed72016-07-04 11:58:09 -0600202 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.
Paul Burton4ae65492016-09-27 16:03:56 +0100247 for name, prop in node.props.items():
Simon Glass69f2ed72016-07-04 11:58:09 -0600248 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]
Paul Burton4ae65492016-09-27 16:03:56 +0100254 for name, prop in fields.items():
Simon Glass69f2ed72016-07-04 11:58:09 -0600255 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]
Paul Burton4ae65492016-09-27 16:03:56 +0100269 for name, prop in node.props.items():
Simon Glass69f2ed72016-07-04 11:58:09 -0600270 if name not in PROP_IGNORE_LIST and name[0] != '#':
271 prop.Widen(struct[name])
272 upto += 1
273 return structs
274
Simon Glass439c9472017-04-22 18:42:22 -0600275 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
Simon Glass69f2ed72016-07-04 11:58:09 -0600302 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
Simon Glass49eec8c2017-04-22 18:42:21 -0600331 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
Simon Glass439c9472017-04-22 18:42:22 -0600382 self.Out(''.join(self.GetBuf()))
383
Simon Glass69f2ed72016-07-04 11:58:09 -0600384 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')
Simon Glass439c9472017-04-22 18:42:22 -0600395 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)
Simon Glass49eec8c2017-04-22 18:42:21 -0600405 self.OutputNode(node)
Simon Glass439c9472017-04-22 18:42:22 -0600406 nodes_to_output.remove(node)
Simon Glass69f2ed72016-07-04 11:58:09 -0600407
408
409if __name__ != "__main__":
410 pass
411
412parser = OptionParser()
413parser.add_option('-d', '--dtb-file', action='store',
414 help='Specify the .dtb input file')
415parser.add_option('--include-disabled', action='store_true',
416 help='Include disabled nodes')
417parser.add_option('-o', '--output', action='store', default='-',
418 help='Select output filename')
419(options, args) = parser.parse_args()
420
421if not args:
422 raise ValueError('Please specify a command: struct, platdata')
423
424plat = DtbPlatdata(options.dtb_file, options)
425plat.ScanDtb()
426plat.ScanTree()
427plat.SetupOutput(options.output)
428structs = plat.ScanStructs()
Simon Glass439c9472017-04-22 18:42:22 -0600429plat.ScanPhandles()
Simon Glass69f2ed72016-07-04 11:58:09 -0600430
431for 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)