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)
176 """Scan the device tree for useful information
178 This fills in the following properties:
179 _phandle_node: A dict of Nodes indexed by phandle (an integer)
180 _valid_nodes: A list of nodes we wish to consider include in the
184 self._phandle_node = {}
185 for node in self.fdt.GetRoot().subnodes:
186 if 'compatible' in node.props:
187 status = node.props.get('status')
188 if (not options.include_disabled and not status or
189 status.value != 'disabled'):
190 node_list.append(node)
191 phandle_prop = node.props.get('phandle')
193 phandle = phandle_prop.GetPhandle()
194 self._phandle_node[phandle] = node
196 self._valid_nodes = node_list
198 def IsPhandle(self, prop):
199 """Check if a node contains phandles
201 We have no reliable way of detecting whether a node uses a phandle
202 or not. As an interim measure, use a list of known property names.
205 prop: Prop object to check
207 True if the object value contains phandles, else False
209 if prop.name in ['clocks']:
213 def ScanStructs(self):
214 """Scan the device tree building up the C structures we will use.
216 Build a dict keyed by C struct name containing a dict of Prop
217 object for each struct field (keyed by property name). Where the
218 same struct appears multiple times, try to use the 'widest'
219 property, i.e. the one with a type which can express all others.
221 Once the widest property is determined, all other properties are
222 updated to match that width.
225 for node in self._valid_nodes:
226 node_name = self.GetCompatName(node)
229 # Get a list of all the valid properties in this node.
230 for name, prop in node.props.items():
231 if name not in PROP_IGNORE_LIST and name[0] != '#':
232 fields[name] = copy.deepcopy(prop)
234 # If we've seen this node_name before, update the existing struct.
235 if node_name in structs:
236 struct = structs[node_name]
237 for name, prop in fields.items():
238 oldprop = struct.get(name)
244 # Otherwise store this as a new struct.
246 structs[node_name] = fields
249 for node in self._valid_nodes:
250 node_name = self.GetCompatName(node)
251 struct = structs[node_name]
252 for name, prop in node.props.items():
253 if name not in PROP_IGNORE_LIST and name[0] != '#':
254 prop.Widen(struct[name])
258 def GenerateStructs(self, structs):
259 """Generate struct defintions for the platform data
261 This writes out the body of a header file consisting of structure
262 definitions for node in self._valid_nodes. See the documentation in
263 README.of-plat for more information.
265 self.Out('#include <stdbool.h>\n')
266 self.Out('#include <libfdt.h>\n')
268 # Output the struct definition
269 for name in sorted(structs):
270 self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
271 for pname in sorted(structs[name]):
272 prop = structs[name][pname]
273 if self.IsPhandle(prop):
274 # For phandles, include a reference to the target
275 self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
276 Conv_name_to_c(prop.name),
277 len(prop.value) / 2))
279 ptype = TYPE_NAMES[prop.type]
280 self.Out('\t%s%s' % (TabTo(2, ptype),
281 Conv_name_to_c(prop.name)))
282 if type(prop.value) == list:
283 self.Out('[%d]' % len(prop.value))
287 def GenerateTables(self):
288 """Generate device defintions for the platform data
290 This writes out C platform data initialisation data and
291 U_BOOT_DEVICE() declarations for each valid node. See the
292 documentation in README.of-plat for more information.
294 self.Out('#include <common.h>\n')
295 self.Out('#include <dm.h>\n')
296 self.Out('#include <dt-structs.h>\n')
299 for node in self._valid_nodes:
300 struct_name = self.GetCompatName(node)
301 var_name = Conv_name_to_c(node.name)
302 self.Buf('static struct %s%s %s%s = {\n' %
303 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
304 for pname, prop in node.props.items():
305 if pname in PROP_IGNORE_LIST or pname[0] == '#':
307 ptype = TYPE_NAMES[prop.type]
308 member_name = Conv_name_to_c(prop.name)
309 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
311 # Special handling for lists
312 if type(prop.value) == list:
315 # For phandles, output a reference to the platform data
316 # of the target node.
317 if self.IsPhandle(prop):
318 # Process the list as pairs of (phandle, id)
319 it = iter(prop.value)
320 for phandle_cell, id_cell in zip(it, it):
321 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
322 id = fdt_util.fdt32_to_cpu(id_cell)
323 target_node = self._phandle_node[phandle]
324 name = Conv_name_to_c(target_node.name)
325 vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
327 for val in prop.value:
328 vals.append(self.GetValue(prop.type, val))
329 self.Buf(', '.join(vals))
332 self.Buf(self.GetValue(prop.type, prop.value))
336 # Add a device declaration
337 self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
338 self.Buf('\t.name\t\t= "%s",\n' % struct_name)
339 self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
340 self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' %
341 (VAL_PREFIX, var_name))
345 # Output phandle target nodes first, since they may be referenced
347 if 'phandle' in node.props:
348 self.Out(''.join(self.GetBuf()))
350 node_txt_list.append(self.GetBuf())
352 # Output all the nodes which are not phandle targets themselves, but
353 # may reference them. This avoids the need for forward declarations.
354 for node_txt in node_txt_list:
355 self.Out(''.join(node_txt))
358 if __name__ != "__main__":
361 parser = OptionParser()
362 parser.add_option('-d', '--dtb-file', action='store',
363 help='Specify the .dtb input file')
364 parser.add_option('--include-disabled', action='store_true',
365 help='Include disabled nodes')
366 parser.add_option('-o', '--output', action='store', default='-',
367 help='Select output filename')
368 (options, args) = parser.parse_args()
371 raise ValueError('Please specify a command: struct, platdata')
373 plat = DtbPlatdata(options.dtb_file, options)
376 plat.SetupOutput(options.output)
377 structs = plat.ScanStructs()
379 for cmd in args[0].split(','):
381 plat.GenerateStructs(structs)
382 elif cmd == 'platdata':
383 plat.GenerateTables()
385 raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)