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
16 # Bring in the patman libraries
17 our_path = os.path.dirname(os.path.realpath(__file__))
18 sys.path.append(os.path.join(our_path, '../patman'))
20 # Bring in either the normal fdt library (which relies on libfdt) or the
21 # fallback one (which uses fdtget and is slower). Both provide the same
22 # interfface for this file to use.
29 from fdt_fallback import Fdt
30 import fdt_fallback as fdt
34 # When we see these properties we ignore them - i.e. do not create a structure member
45 # C type declarations for the tyues we support
47 fdt_util.TYPE_INT: 'fdt32_t',
48 fdt_util.TYPE_BYTE: 'unsigned char',
49 fdt_util.TYPE_STRING: 'const char *',
50 fdt_util.TYPE_BOOL: 'bool',
53 STRUCT_PREFIX = 'dtd_'
56 def Conv_name_to_c(name):
57 """Convert a device-tree name to a C identifier
62 String containing the C version of this name
64 str = name.replace('@', '_at_')
65 str = str.replace('-', '_')
66 str = str.replace(',', '_')
67 str = str.replace('/', '__')
70 def TabTo(num_tabs, str):
71 if len(str) >= num_tabs * 8:
73 return str + '\t' * (num_tabs - len(str) / 8)
76 """Provide a means to convert device tree binary data to platform data
78 The output of this process is C structures which can be used in space-
79 constrained encvironments where the ~3KB code overhead of device tree
80 code is not affordable.
83 fdt: Fdt object, referencing the device tree
84 _dtb_fname: Filename of the input device tree binary file
85 _valid_nodes: A list of Node object with compatible strings
86 _options: Command-line options
87 _phandle_node: A dict of nodes indexed by phandle number (1, 2...)
88 _outfile: The current output file (sys.stdout or a real file)
89 _lines: Stashed list of output lines for outputting in the future
90 _phandle_node: A dict of Nodes indexed by phandle (an integer)
92 def __init__(self, dtb_fname, options):
93 self._dtb_fname = dtb_fname
94 self._valid_nodes = None
95 self._options = options
96 self._phandle_node = {}
100 def SetupOutput(self, fname):
101 """Set up the output destination
103 Once this is done, future calls to self.Out() will output to this
107 fname: Filename to send output to, or '-' for stdout
110 self._outfile = sys.stdout
112 self._outfile = open(fname, 'w')
115 """Output a string to the output file
118 str: String to output
120 self._outfile.write(str)
123 """Buffer up a string to send later
126 str: String to add to our 'buffer' list
128 self._lines.append(str)
131 """Get the contents of the output buffer, and clear it
134 The output buffer, which is then cleared for future use
140 def GetValue(self, type, value):
141 """Get a value as a C expression
143 For integers this returns a byte-swapped (little-endian) hex string
144 For bytes this returns a hex string, e.g. 0x12
145 For strings this returns a literal string enclosed in quotes
146 For booleans this return 'true'
149 type: Data type (fdt_util)
150 value: Data value, as a string of bytes
152 if type == fdt_util.TYPE_INT:
153 return '%#x' % fdt_util.fdt32_to_cpu(value)
154 elif type == fdt_util.TYPE_BYTE:
155 return '%#x' % ord(value[0])
156 elif type == fdt_util.TYPE_STRING:
157 return '"%s"' % value
158 elif type == fdt_util.TYPE_BOOL:
161 def GetCompatName(self, node):
162 """Get a node's first compatible string as a C identifier
165 node: Node object to check
167 C identifier for the first compatible string
169 compat = node.props['compatible'].value
170 if type(compat) == list:
172 return Conv_name_to_c(compat)
175 """Scan the device tree to obtain a tree of notes and properties
177 Once this is done, self.fdt.GetRoot() can be called to obtain the
178 device tree root node, and progress from there.
180 self.fdt = Fdt(self._dtb_fname)
184 """Scan the device tree for useful information
186 This fills in the following properties:
187 _phandle_node: A dict of Nodes indexed by phandle (an integer)
188 _valid_nodes: A list of nodes we wish to consider include in the
192 self._phandle_node = {}
193 for node in self.fdt.GetRoot().subnodes:
194 if 'compatible' in node.props:
195 status = node.props.get('status')
196 if (not options.include_disabled and not status or
197 status.value != 'disabled'):
198 node_list.append(node)
199 phandle_prop = node.props.get('phandle')
201 phandle = phandle_prop.GetPhandle()
202 self._phandle_node[phandle] = node
204 self._valid_nodes = node_list
206 def IsPhandle(self, prop):
207 """Check if a node contains phandles
209 We have no reliable way of detecting whether a node uses a phandle
210 or not. As an interim measure, use a list of known property names.
213 prop: Prop object to check
215 True if the object value contains phandles, else False
217 if prop.name in ['clocks']:
221 def ScanStructs(self):
222 """Scan the device tree building up the C structures we will use.
224 Build a dict keyed by C struct name containing a dict of Prop
225 object for each struct field (keyed by property name). Where the
226 same struct appears multiple times, try to use the 'widest'
227 property, i.e. the one with a type which can express all others.
229 Once the widest property is determined, all other properties are
230 updated to match that width.
233 for node in self._valid_nodes:
234 node_name = self.GetCompatName(node)
237 # Get a list of all the valid properties in this node.
238 for name, prop in node.props.iteritems():
239 if name not in PROP_IGNORE_LIST and name[0] != '#':
240 fields[name] = copy.deepcopy(prop)
242 # If we've seen this node_name before, update the existing struct.
243 if node_name in structs:
244 struct = structs[node_name]
245 for name, prop in fields.iteritems():
246 oldprop = struct.get(name)
252 # Otherwise store this as a new struct.
254 structs[node_name] = fields
257 for node in self._valid_nodes:
258 node_name = self.GetCompatName(node)
259 struct = structs[node_name]
260 for name, prop in node.props.iteritems():
261 if name not in PROP_IGNORE_LIST and name[0] != '#':
262 prop.Widen(struct[name])
266 def GenerateStructs(self, structs):
267 """Generate struct defintions for the platform data
269 This writes out the body of a header file consisting of structure
270 definitions for node in self._valid_nodes. See the documentation in
271 README.of-plat for more information.
273 self.Out('#include <stdbool.h>\n')
274 self.Out('#include <libfdt.h>\n')
276 # Output the struct definition
277 for name in sorted(structs):
278 self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
279 for pname in sorted(structs[name]):
280 prop = structs[name][pname]
281 if self.IsPhandle(prop):
282 # For phandles, include a reference to the target
283 self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
284 Conv_name_to_c(prop.name),
285 len(prop.value) / 2))
287 ptype = TYPE_NAMES[prop.type]
288 self.Out('\t%s%s' % (TabTo(2, ptype),
289 Conv_name_to_c(prop.name)))
290 if type(prop.value) == list:
291 self.Out('[%d]' % len(prop.value))
295 def GenerateTables(self):
296 """Generate device defintions for the platform data
298 This writes out C platform data initialisation data and
299 U_BOOT_DEVICE() declarations for each valid node. See the
300 documentation in README.of-plat for more information.
302 self.Out('#include <common.h>\n')
303 self.Out('#include <dm.h>\n')
304 self.Out('#include <dt-structs.h>\n')
307 for node in self._valid_nodes:
308 struct_name = self.GetCompatName(node)
309 var_name = Conv_name_to_c(node.name)
310 self.Buf('static struct %s%s %s%s = {\n' %
311 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
312 for pname, prop in node.props.iteritems():
313 if pname in PROP_IGNORE_LIST or pname[0] == '#':
315 ptype = TYPE_NAMES[prop.type]
316 member_name = Conv_name_to_c(prop.name)
317 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
319 # Special handling for lists
320 if type(prop.value) == list:
323 # For phandles, output a reference to the platform data
324 # of the target node.
325 if self.IsPhandle(prop):
326 # Process the list as pairs of (phandle, id)
327 it = iter(prop.value)
328 for phandle_cell, id_cell in zip(it, it):
329 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
330 id = fdt_util.fdt32_to_cpu(id_cell)
331 target_node = self._phandle_node[phandle]
332 name = Conv_name_to_c(target_node.name)
333 vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
335 for val in prop.value:
336 vals.append(self.GetValue(prop.type, val))
337 self.Buf(', '.join(vals))
340 self.Buf(self.GetValue(prop.type, prop.value))
344 # Add a device declaration
345 self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
346 self.Buf('\t.name\t\t= "%s",\n' % struct_name)
347 self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
351 # Output phandle target nodes first, since they may be referenced
353 if 'phandle' in node.props:
354 self.Out(''.join(self.GetBuf()))
356 node_txt_list.append(self.GetBuf())
358 # Output all the nodes which are not phandle targets themselves, but
359 # may reference them. This avoids the need for forward declarations.
360 for node_txt in node_txt_list:
361 self.Out(''.join(node_txt))
364 if __name__ != "__main__":
367 parser = OptionParser()
368 parser.add_option('-d', '--dtb-file', action='store',
369 help='Specify the .dtb input file')
370 parser.add_option('--include-disabled', action='store_true',
371 help='Include disabled nodes')
372 parser.add_option('-o', '--output', action='store', default='-',
373 help='Select output filename')
374 (options, args) = parser.parse_args()
377 raise ValueError('Please specify a command: struct, platdata')
379 plat = DtbPlatdata(options.dtb_file, options)
382 plat.SetupOutput(options.output)
383 structs = plat.ScanStructs()
385 for cmd in args[0].split(','):
387 plat.GenerateStructs(structs)
388 elif cmd == 'platdata':
389 plat.GenerateTables()
391 raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)