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'))
23 # When we see these properties we ignore them - i.e. do not create a structure member
32 'u-boot,dm-pre-reloc',
37 # C type declarations for the tyues we support
39 fdt.TYPE_INT: 'fdt32_t',
40 fdt.TYPE_BYTE: 'unsigned char',
41 fdt.TYPE_STRING: 'const char *',
42 fdt.TYPE_BOOL: 'bool',
45 STRUCT_PREFIX = 'dtd_'
48 def Conv_name_to_c(name):
49 """Convert a device-tree name to a C identifier
54 String containing the C version of this name
56 str = name.replace('@', '_at_')
57 str = str.replace('-', '_')
58 str = str.replace(',', '_')
59 str = str.replace('.', '_')
60 str = str.replace('/', '__')
63 def TabTo(num_tabs, str):
64 if len(str) >= num_tabs * 8:
66 return str + '\t' * (num_tabs - len(str) // 8)
69 """Provide a means to convert device tree binary data to platform data
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.
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)
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 = {}
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
163 if type(compat) == list:
165 return Conv_name_to_c(compat)
168 """Scan the device tree to obtain a tree of notes and properties
170 Once this is done, self.fdt.GetRoot() can be called to obtain the
171 device tree root node, and progress from there.
173 self.fdt = fdt_select.FdtScan(self._dtb_fname)
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')
184 phandle = phandle_prop.GetPhandle()
185 self._phandle_node[phandle] = node
187 # recurse to handle any subnodes
191 """Scan the device tree for useful information
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
198 self._phandle_node = {}
199 self._valid_nodes = []
200 return self.ScanNode(self.fdt.GetRoot());
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')
210 phandle = phandle_prop.GetPhandle()
211 self._phandle_node[phandle] = node
213 self._valid_nodes = node_list
215 def IsPhandle(self, prop):
216 """Check if a node contains phandles
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.
222 prop: Prop object to check
224 True if the object value contains phandles, else False
226 if prop.name in ['clocks']:
230 def ScanStructs(self):
231 """Scan the device tree building up the C structures we will use.
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.
238 Once the widest property is determined, all other properties are
239 updated to match that width.
242 for node in self._valid_nodes:
243 node_name = self.GetCompatName(node)
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)
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)
261 # Otherwise store this as a new struct.
263 structs[node_name] = fields
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])
275 def ScanPhandles(self):
276 """Figure out what phandles each node uses
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
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.
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] == '#':
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)
302 def GenerateStructs(self, structs):
303 """Generate struct defintions for the platform data
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.
309 self.Out('#include <stdbool.h>\n')
310 self.Out('#include <libfdt.h>\n')
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))
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))
331 def OutputNode(self, node):
332 """Output the C code for a node
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] == '#':
344 ptype = TYPE_NAMES[prop.type]
345 member_name = Conv_name_to_c(prop.name)
346 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
348 # Special handling for lists
349 if type(prop.value) == list:
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))
364 for val in prop.value:
365 vals.append(self.GetValue(prop.type, val))
366 self.Buf(', '.join(vals))
369 self.Buf(self.GetValue(prop.type, prop.value))
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))
382 self.Out(''.join(self.GetBuf()))
384 def GenerateTables(self):
385 """Generate device defintions for the platform data
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.
391 self.Out('#include <common.h>\n')
392 self.Out('#include <dm.h>\n')
393 self.Out('#include <dt-structs.h>\n')
395 nodes_to_output = list(self._valid_nodes)
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)
409 if __name__ != "__main__":
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()
422 raise ValueError('Please specify a command: struct, platdata')
424 plat = DtbPlatdata(options.dtb_file, options)
427 plat.SetupOutput(options.output)
428 structs = plat.ScanStructs()
431 for cmd in args[0].split(','):
433 plat.GenerateStructs(structs)
434 elif cmd == 'platdata':
435 plat.GenerateTables()
437 raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)