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 = {}
92 def SetupOutput(self, fname):
93 """Set up the output destination
95 Once this is done, future calls to self.Out() will output to this
99 fname: Filename to send output to, or '-' for stdout
102 self._outfile = sys.stdout
104 self._outfile = open(fname, 'w')
107 """Output a string to the output file
110 str: String to output
112 self._outfile.write(str)
115 """Buffer up a string to send later
118 str: String to add to our 'buffer' list
120 self._lines.append(str)
123 """Get the contents of the output buffer, and clear it
126 The output buffer, which is then cleared for future use
132 def GetValue(self, type, value):
133 """Get a value as a C expression
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'
141 type: Data type (fdt_util)
142 value: Data value, as a string of bytes
144 if type == fdt.TYPE_INT:
145 return '%#x' % fdt_util.fdt32_to_cpu(value)
146 elif type == fdt.TYPE_BYTE:
147 return '%#x' % ord(value[0])
148 elif type == fdt.TYPE_STRING:
149 return '"%s"' % value
150 elif type == fdt.TYPE_BOOL:
153 def GetCompatName(self, node):
154 """Get a node's first compatible string as a C identifier
157 node: Node object to check
159 C identifier for the first compatible string
161 compat = node.props['compatible'].value
162 if type(compat) == list:
164 return Conv_name_to_c(compat)
167 """Scan the device tree to obtain a tree of notes and properties
169 Once this is done, self.fdt.GetRoot() can be called to obtain the
170 device tree root node, and progress from there.
172 self.fdt = fdt.FdtScan(self._dtb_fname)
174 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')
183 phandle = phandle_prop.GetPhandle()
184 self._phandle_node[phandle] = node
186 # recurse to handle any subnodes
190 """Scan the device tree for useful information
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
197 self._phandle_node = {}
198 self._valid_nodes = []
199 return self.ScanNode(self.fdt.GetRoot());
201 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')
209 phandle = phandle_prop.GetPhandle()
210 self._phandle_node[phandle] = node
212 self._valid_nodes = node_list
214 def IsPhandle(self, prop):
215 """Check if a node contains phandles
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.
221 prop: Prop object to check
223 True if the object value contains phandles, else False
225 if prop.name in ['clocks']:
229 def ScanStructs(self):
230 """Scan the device tree building up the C structures we will use.
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.
237 Once the widest property is determined, all other properties are
238 updated to match that width.
241 for node in self._valid_nodes:
242 node_name = self.GetCompatName(node)
245 # Get a list of all the valid properties in this node.
246 for name, prop in node.props.items():
247 if name not in PROP_IGNORE_LIST and name[0] != '#':
248 fields[name] = copy.deepcopy(prop)
250 # If we've seen this node_name before, update the existing struct.
251 if node_name in structs:
252 struct = structs[node_name]
253 for name, prop in fields.items():
254 oldprop = struct.get(name)
260 # Otherwise store this as a new struct.
262 structs[node_name] = fields
265 for node in self._valid_nodes:
266 node_name = self.GetCompatName(node)
267 struct = structs[node_name]
268 for name, prop in node.props.items():
269 if name not in PROP_IGNORE_LIST and name[0] != '#':
270 prop.Widen(struct[name])
274 def ScanPhandles(self):
275 """Figure out what phandles each node uses
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
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.
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] == '#':
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)
301 def GenerateStructs(self, structs):
302 """Generate struct defintions for the platform data
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.
308 self.Out('#include <stdbool.h>\n')
309 self.Out('#include <libfdt.h>\n')
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))
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))
330 def OutputNode(self, node):
331 """Output the C code for a node
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] == '#':
343 ptype = TYPE_NAMES[prop.type]
344 member_name = Conv_name_to_c(prop.name)
345 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
347 # Special handling for lists
348 if type(prop.value) == list:
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))
363 for val in prop.value:
364 vals.append(self.GetValue(prop.type, val))
365 self.Buf(', '.join(vals))
368 self.Buf(self.GetValue(prop.type, prop.value))
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))
381 self.Out(''.join(self.GetBuf()))
383 def GenerateTables(self):
384 """Generate device defintions for the platform data
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.
390 self.Out('#include <common.h>\n')
391 self.Out('#include <dm.h>\n')
392 self.Out('#include <dt-structs.h>\n')
394 nodes_to_output = list(self._valid_nodes)
396 # 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)
408 if __name__ != "__main__":
411 parser = OptionParser()
412 parser.add_option('-d', '--dtb-file', action='store',
413 help='Specify the .dtb input file')
414 parser.add_option('--include-disabled', action='store_true',
415 help='Include disabled nodes')
416 parser.add_option('-o', '--output', action='store', default='-',
417 help='Select output filename')
418 (options, args) = parser.parse_args()
421 raise ValueError('Please specify a command: struct, platdata')
423 plat = DtbPlatdata(options.dtb_file, options)
426 plat.SetupOutput(options.output)
427 structs = plat.ScanStructs()
430 for cmd in args[0].split(','):
432 plat.GenerateStructs(structs)
433 elif cmd == 'platdata':
434 plat.GenerateTables()
436 raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)