3 # Copyright (C) 2016 Google, Inc
4 # Written by Simon Glass <sjg@chromium.org>
6 # SPDX-License-Identifier: GPL-2.0+
10 from optparse import OptionError, OptionParser
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'))
22 # When we see these properties we ignore them - i.e. do not create a structure member
31 'u-boot,dm-pre-reloc',
36 # C type declarations for the tyues we support
38 fdt.TYPE_INT: 'fdt32_t',
39 fdt.TYPE_BYTE: 'unsigned char',
40 fdt.TYPE_STRING: 'const char *',
41 fdt.TYPE_BOOL: 'bool',
44 STRUCT_PREFIX = 'dtd_'
47 def Conv_name_to_c(name):
48 """Convert a device-tree name to a C identifier
53 String containing the C version of this name
55 str = name.replace('@', '_at_')
56 str = str.replace('-', '_')
57 str = str.replace(',', '_')
58 str = str.replace('.', '_')
59 str = str.replace('/', '__')
62 def TabTo(num_tabs, str):
63 if len(str) >= num_tabs * 8:
65 return str + '\t' * (num_tabs - len(str) // 8)
68 """Provide a means to convert device tree binary data to platform data
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.
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)
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 = {}
93 def SetupOutput(self, fname):
94 """Set up the output destination
96 Once this is done, future calls to self.Out() will output to this
100 fname: Filename to send output to, or '-' for stdout
103 self._outfile = sys.stdout
105 self._outfile = open(fname, 'w')
108 """Output a string to the output file
111 str: String to output
113 self._outfile.write(str)
116 """Buffer up a string to send later
119 str: String to add to our 'buffer' list
121 self._lines.append(str)
124 """Get the contents of the output buffer, and clear it
127 The output buffer, which is then cleared for future use
133 def GetValue(self, type, value):
134 """Get a value as a C expression
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'
142 type: Data type (fdt_util)
143 value: Data value, as a string of bytes
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:
154 def GetCompatName(self, node):
155 """Get a node's first compatible string as a C identifier
158 node: Node object to check
160 C identifier for the first compatible string
162 compat = node.props['compatible'].value
164 if type(compat) == list:
165 compat, aliases = compat[0], compat[1:]
166 return Conv_name_to_c(compat), [Conv_name_to_c(a) for a in aliases]
169 """Scan the device tree to obtain a tree of notes and properties
171 Once this is done, self.fdt.GetRoot() can be called to obtain the
172 device tree root node, and progress from there.
174 self.fdt = fdt.FdtScan(self._dtb_fname)
176 def ScanNode(self, root):
177 for node in root.subnodes:
178 if 'compatible' in node.props:
179 status = node.props.get('status')
180 if (not self._options.include_disabled and not status or
181 status.value != 'disabled'):
182 self._valid_nodes.append(node)
183 phandle_prop = node.props.get('phandle')
185 phandle = phandle_prop.GetPhandle()
186 self._phandle_node[phandle] = node
188 # recurse to handle any subnodes
192 """Scan the device tree for useful information
194 This fills in the following properties:
195 _phandle_node: A dict of Nodes indexed by phandle (an integer)
196 _valid_nodes: A list of nodes we wish to consider include in the
199 self._phandle_node = {}
200 self._valid_nodes = []
201 return self.ScanNode(self.fdt.GetRoot());
203 for node in self.fdt.GetRoot().subnodes:
204 if 'compatible' in node.props:
205 status = node.props.get('status')
206 if (not self._options.include_disabled and not status or
207 status.value != 'disabled'):
208 node_list.append(node)
209 phandle_prop = node.props.get('phandle')
211 phandle = phandle_prop.GetPhandle()
212 self._phandle_node[phandle] = node
214 self._valid_nodes = node_list
216 def IsPhandle(self, prop):
217 """Check if a node contains phandles
219 We have no reliable way of detecting whether a node uses a phandle
220 or not. As an interim measure, use a list of known property names.
223 prop: Prop object to check
225 True if the object value contains phandles, else False
227 if prop.name in ['clocks']:
231 def ScanStructs(self):
232 """Scan the device tree building up the C structures we will use.
234 Build a dict keyed by C struct name containing a dict of Prop
235 object for each struct field (keyed by property name). Where the
236 same struct appears multiple times, try to use the 'widest'
237 property, i.e. the one with a type which can express all others.
239 Once the widest property is determined, all other properties are
240 updated to match that width.
243 for node in self._valid_nodes:
244 node_name, _ = self.GetCompatName(node)
247 # Get a list of all the valid properties in this node.
248 for name, prop in node.props.items():
249 if name not in PROP_IGNORE_LIST and name[0] != '#':
250 fields[name] = copy.deepcopy(prop)
252 # If we've seen this node_name before, update the existing struct.
253 if node_name in structs:
254 struct = structs[node_name]
255 for name, prop in fields.items():
256 oldprop = struct.get(name)
262 # Otherwise store this as a new struct.
264 structs[node_name] = fields
267 for node in self._valid_nodes:
268 node_name, _ = self.GetCompatName(node)
269 struct = structs[node_name]
270 for name, prop in node.props.items():
271 if name not in PROP_IGNORE_LIST and name[0] != '#':
272 prop.Widen(struct[name])
275 struct_name, aliases = self.GetCompatName(node)
276 for alias in aliases:
277 self._aliases[alias] = struct_name
281 def ScanPhandles(self):
282 """Figure out what phandles each node uses
284 We need to be careful when outputing nodes that use phandles since
285 they must come after the declaration of the phandles in the C file.
286 Otherwise we get a compiler error since the phandle struct is not yet
289 This function adds to each node a list of phandle nodes that the node
290 depends on. This allows us to output things in the right order.
292 for node in self._valid_nodes:
293 node.phandles = set()
294 for pname, prop in node.props.items():
295 if pname in PROP_IGNORE_LIST or pname[0] == '#':
297 if type(prop.value) == list:
298 if self.IsPhandle(prop):
299 # Process the list as pairs of (phandle, id)
300 it = iter(prop.value)
301 for phandle_cell, id_cell in zip(it, it):
302 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
303 id = fdt_util.fdt32_to_cpu(id_cell)
304 target_node = self._phandle_node[phandle]
305 node.phandles.add(target_node)
308 def GenerateStructs(self, structs):
309 """Generate struct defintions for the platform data
311 This writes out the body of a header file consisting of structure
312 definitions for node in self._valid_nodes. See the documentation in
313 README.of-plat for more information.
315 self.Out('#include <stdbool.h>\n')
316 self.Out('#include <libfdt.h>\n')
318 # Output the struct definition
319 for name in sorted(structs):
320 self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
321 for pname in sorted(structs[name]):
322 prop = structs[name][pname]
323 if self.IsPhandle(prop):
324 # For phandles, include a reference to the target
325 self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
326 Conv_name_to_c(prop.name),
327 len(prop.value) / 2))
329 ptype = TYPE_NAMES[prop.type]
330 self.Out('\t%s%s' % (TabTo(2, ptype),
331 Conv_name_to_c(prop.name)))
332 if type(prop.value) == list:
333 self.Out('[%d]' % len(prop.value))
337 for alias, struct_name in self._aliases.iteritems():
338 self.Out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
339 STRUCT_PREFIX, struct_name))
341 def OutputNode(self, node):
342 """Output the C code for a node
347 struct_name, _ = self.GetCompatName(node)
348 var_name = Conv_name_to_c(node.name)
349 self.Buf('static struct %s%s %s%s = {\n' %
350 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
351 for pname, prop in node.props.items():
352 if pname in PROP_IGNORE_LIST or pname[0] == '#':
354 ptype = TYPE_NAMES[prop.type]
355 member_name = Conv_name_to_c(prop.name)
356 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
358 # Special handling for lists
359 if type(prop.value) == list:
362 # For phandles, output a reference to the platform data
363 # of the target node.
364 if self.IsPhandle(prop):
365 # Process the list as pairs of (phandle, id)
366 it = iter(prop.value)
367 for phandle_cell, id_cell in zip(it, it):
368 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
369 id = fdt_util.fdt32_to_cpu(id_cell)
370 target_node = self._phandle_node[phandle]
371 name = Conv_name_to_c(target_node.name)
372 vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
374 for val in prop.value:
375 vals.append(self.GetValue(prop.type, val))
376 self.Buf(', '.join(vals))
379 self.Buf(self.GetValue(prop.type, prop.value))
383 # Add a device declaration
384 self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
385 self.Buf('\t.name\t\t= "%s",\n' % struct_name)
386 self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
387 self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' %
388 (VAL_PREFIX, var_name))
392 self.Out(''.join(self.GetBuf()))
394 def GenerateTables(self):
395 """Generate device defintions for the platform data
397 This writes out C platform data initialisation data and
398 U_BOOT_DEVICE() declarations for each valid node. Where a node has
399 multiple compatible strings, a #define is used to make them equivalent.
401 See the documentation in doc/driver-model/of-plat.txt for more
404 self.Out('#include <common.h>\n')
405 self.Out('#include <dm.h>\n')
406 self.Out('#include <dt-structs.h>\n')
408 nodes_to_output = list(self._valid_nodes)
410 # Keep outputing nodes until there is none left
411 while nodes_to_output:
412 node = nodes_to_output[0]
413 # Output all the node's dependencies first
414 for req_node in node.phandles:
415 if req_node in nodes_to_output:
416 self.OutputNode(req_node)
417 nodes_to_output.remove(req_node)
418 self.OutputNode(node)
419 nodes_to_output.remove(node)
422 if __name__ != "__main__":
425 parser = OptionParser()
426 parser.add_option('-d', '--dtb-file', action='store',
427 help='Specify the .dtb input file')
428 parser.add_option('--include-disabled', action='store_true',
429 help='Include disabled nodes')
430 parser.add_option('-o', '--output', action='store', default='-',
431 help='Select output filename')
432 (options, args) = parser.parse_args()
435 raise ValueError('Please specify a command: struct, platdata')
437 plat = DtbPlatdata(options.dtb_file, options)
440 plat.SetupOutput(options.output)
441 structs = plat.ScanStructs()
444 for cmd in args[0].split(','):
446 plat.GenerateStructs(structs)
447 elif cmd == 'platdata':
448 plat.GenerateTables()
450 raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)