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 GenerateStructs(self, structs):
276 """Generate struct defintions for the platform data
278 This writes out the body of a header file consisting of structure
279 definitions for node in self._valid_nodes. See the documentation in
280 README.of-plat for more information.
282 self.Out('#include <stdbool.h>\n')
283 self.Out('#include <libfdt.h>\n')
285 # Output the struct definition
286 for name in sorted(structs):
287 self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
288 for pname in sorted(structs[name]):
289 prop = structs[name][pname]
290 if self.IsPhandle(prop):
291 # For phandles, include a reference to the target
292 self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
293 Conv_name_to_c(prop.name),
294 len(prop.value) / 2))
296 ptype = TYPE_NAMES[prop.type]
297 self.Out('\t%s%s' % (TabTo(2, ptype),
298 Conv_name_to_c(prop.name)))
299 if type(prop.value) == list:
300 self.Out('[%d]' % len(prop.value))
304 def GenerateTables(self):
305 """Generate device defintions for the platform data
307 This writes out C platform data initialisation data and
308 U_BOOT_DEVICE() declarations for each valid node. See the
309 documentation in README.of-plat for more information.
311 self.Out('#include <common.h>\n')
312 self.Out('#include <dm.h>\n')
313 self.Out('#include <dt-structs.h>\n')
316 for node in self._valid_nodes:
317 struct_name = self.GetCompatName(node)
318 var_name = Conv_name_to_c(node.name)
319 self.Buf('static struct %s%s %s%s = {\n' %
320 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
321 for pname, prop in node.props.items():
322 if pname in PROP_IGNORE_LIST or pname[0] == '#':
324 ptype = TYPE_NAMES[prop.type]
325 member_name = Conv_name_to_c(prop.name)
326 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
328 # Special handling for lists
329 if type(prop.value) == list:
332 # For phandles, output a reference to the platform data
333 # of the target node.
334 if self.IsPhandle(prop):
335 # Process the list as pairs of (phandle, id)
336 it = iter(prop.value)
337 for phandle_cell, id_cell in zip(it, it):
338 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
339 id = fdt_util.fdt32_to_cpu(id_cell)
340 target_node = self._phandle_node[phandle]
341 name = Conv_name_to_c(target_node.name)
342 vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
344 for val in prop.value:
345 vals.append(self.GetValue(prop.type, val))
346 self.Buf(', '.join(vals))
349 self.Buf(self.GetValue(prop.type, prop.value))
353 # Add a device declaration
354 self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
355 self.Buf('\t.name\t\t= "%s",\n' % struct_name)
356 self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
357 self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' %
358 (VAL_PREFIX, var_name))
362 # Output phandle target nodes first, since they may be referenced
364 if 'phandle' in node.props:
365 self.Out(''.join(self.GetBuf()))
367 node_txt_list.append(self.GetBuf())
369 # Output all the nodes which are not phandle targets themselves, but
370 # may reference them. This avoids the need for forward declarations.
371 for node_txt in node_txt_list:
372 self.Out(''.join(node_txt))
375 if __name__ != "__main__":
378 parser = OptionParser()
379 parser.add_option('-d', '--dtb-file', action='store',
380 help='Specify the .dtb input file')
381 parser.add_option('--include-disabled', action='store_true',
382 help='Include disabled nodes')
383 parser.add_option('-o', '--output', action='store', default='-',
384 help='Select output filename')
385 (options, args) = parser.parse_args()
388 raise ValueError('Please specify a command: struct, platdata')
390 plat = DtbPlatdata(options.dtb_file, options)
393 plat.SetupOutput(options.output)
394 structs = plat.ScanStructs()
396 for cmd in args[0].split(','):
398 plat.GenerateStructs(structs)
399 elif cmd == 'platdata':
400 plat.GenerateTables()
402 raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)