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
43 'u-boot,dm-pre-reloc',
46 # C type declarations for the tyues we support
48 fdt_util.TYPE_INT: 'fdt32_t',
49 fdt_util.TYPE_BYTE: 'unsigned char',
50 fdt_util.TYPE_STRING: 'const char *',
51 fdt_util.TYPE_BOOL: 'bool',
54 STRUCT_PREFIX = 'dtd_'
57 def Conv_name_to_c(name):
58 """Convert a device-tree name to a C identifier
63 String containing the C version of this name
65 str = name.replace('@', '_at_')
66 str = str.replace('-', '_')
67 str = str.replace(',', '_')
68 str = str.replace('/', '__')
71 def TabTo(num_tabs, str):
72 if len(str) >= num_tabs * 8:
74 return str + '\t' * (num_tabs - len(str) / 8)
77 """Provide a means to convert device tree binary data to platform data
79 The output of this process is C structures which can be used in space-
80 constrained encvironments where the ~3KB code overhead of device tree
81 code is not affordable.
84 fdt: Fdt object, referencing the device tree
85 _dtb_fname: Filename of the input device tree binary file
86 _valid_nodes: A list of Node object with compatible strings
87 _options: Command-line options
88 _phandle_node: A dict of nodes indexed by phandle number (1, 2...)
89 _outfile: The current output file (sys.stdout or a real file)
90 _lines: Stashed list of output lines for outputting in the future
91 _phandle_node: A dict of Nodes indexed by phandle (an integer)
93 def __init__(self, dtb_fname, options):
94 self._dtb_fname = dtb_fname
95 self._valid_nodes = None
96 self._options = options
97 self._phandle_node = {}
101 def SetupOutput(self, fname):
102 """Set up the output destination
104 Once this is done, future calls to self.Out() will output to this
108 fname: Filename to send output to, or '-' for stdout
111 self._outfile = sys.stdout
113 self._outfile = open(fname, 'w')
116 """Output a string to the output file
119 str: String to output
121 self._outfile.write(str)
124 """Buffer up a string to send later
127 str: String to add to our 'buffer' list
129 self._lines.append(str)
132 """Get the contents of the output buffer, and clear it
135 The output buffer, which is then cleared for future use
141 def GetValue(self, type, value):
142 """Get a value as a C expression
144 For integers this returns a byte-swapped (little-endian) hex string
145 For bytes this returns a hex string, e.g. 0x12
146 For strings this returns a literal string enclosed in quotes
147 For booleans this return 'true'
150 type: Data type (fdt_util)
151 value: Data value, as a string of bytes
153 if type == fdt_util.TYPE_INT:
154 return '%#x' % fdt_util.fdt32_to_cpu(value)
155 elif type == fdt_util.TYPE_BYTE:
156 return '%#x' % ord(value[0])
157 elif type == fdt_util.TYPE_STRING:
158 return '"%s"' % value
159 elif type == fdt_util.TYPE_BOOL:
162 def GetCompatName(self, node):
163 """Get a node's first compatible string as a C identifier
166 node: Node object to check
168 C identifier for the first compatible string
170 compat = node.props['compatible'].value
171 if type(compat) == list:
173 return Conv_name_to_c(compat)
176 """Scan the device tree to obtain a tree of notes and properties
178 Once this is done, self.fdt.GetRoot() can be called to obtain the
179 device tree root node, and progress from there.
181 self.fdt = Fdt(self._dtb_fname)
185 """Scan the device tree for useful information
187 This fills in the following properties:
188 _phandle_node: A dict of Nodes indexed by phandle (an integer)
189 _valid_nodes: A list of nodes we wish to consider include in the
193 self._phandle_node = {}
194 for node in self.fdt.GetRoot().subnodes:
195 if 'compatible' in node.props:
196 status = node.props.get('status')
197 if (not options.include_disabled and not status or
198 status.value != 'disabled'):
199 node_list.append(node)
200 phandle_prop = node.props.get('phandle')
202 phandle = phandle_prop.GetPhandle()
203 self._phandle_node[phandle] = node
205 self._valid_nodes = node_list
207 def IsPhandle(self, prop):
208 """Check if a node contains phandles
210 We have no reliable way of detecting whether a node uses a phandle
211 or not. As an interim measure, use a list of known property names.
214 prop: Prop object to check
216 True if the object value contains phandles, else False
218 if prop.name in ['clocks']:
222 def ScanStructs(self):
223 """Scan the device tree building up the C structures we will use.
225 Build a dict keyed by C struct name containing a dict of Prop
226 object for each struct field (keyed by property name). Where the
227 same struct appears multiple times, try to use the 'widest'
228 property, i.e. the one with a type which can express all others.
230 Once the widest property is determined, all other properties are
231 updated to match that width.
234 for node in self._valid_nodes:
235 node_name = self.GetCompatName(node)
238 # Get a list of all the valid properties in this node.
239 for name, prop in node.props.iteritems():
240 if name not in PROP_IGNORE_LIST and name[0] != '#':
241 fields[name] = copy.deepcopy(prop)
243 # If we've seen this node_name before, update the existing struct.
244 if node_name in structs:
245 struct = structs[node_name]
246 for name, prop in fields.iteritems():
247 oldprop = struct.get(name)
253 # Otherwise store this as a new struct.
255 structs[node_name] = fields
258 for node in self._valid_nodes:
259 node_name = self.GetCompatName(node)
260 struct = structs[node_name]
261 for name, prop in node.props.iteritems():
262 if name not in PROP_IGNORE_LIST and name[0] != '#':
263 prop.Widen(struct[name])
267 def GenerateStructs(self, structs):
268 """Generate struct defintions for the platform data
270 This writes out the body of a header file consisting of structure
271 definitions for node in self._valid_nodes. See the documentation in
272 README.of-plat for more information.
274 self.Out('#include <stdbool.h>\n')
275 self.Out('#include <libfdt.h>\n')
277 # Output the struct definition
278 for name in sorted(structs):
279 self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
280 for pname in sorted(structs[name]):
281 prop = structs[name][pname]
282 if self.IsPhandle(prop):
283 # For phandles, include a reference to the target
284 self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
285 Conv_name_to_c(prop.name),
286 len(prop.value) / 2))
288 ptype = TYPE_NAMES[prop.type]
289 self.Out('\t%s%s' % (TabTo(2, ptype),
290 Conv_name_to_c(prop.name)))
291 if type(prop.value) == list:
292 self.Out('[%d]' % len(prop.value))
296 def GenerateTables(self):
297 """Generate device defintions for the platform data
299 This writes out C platform data initialisation data and
300 U_BOOT_DEVICE() declarations for each valid node. See the
301 documentation in README.of-plat for more information.
303 self.Out('#include <common.h>\n')
304 self.Out('#include <dm.h>\n')
305 self.Out('#include <dt-structs.h>\n')
308 for node in self._valid_nodes:
309 struct_name = self.GetCompatName(node)
310 var_name = Conv_name_to_c(node.name)
311 self.Buf('static struct %s%s %s%s = {\n' %
312 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
313 for pname, prop in node.props.iteritems():
314 if pname in PROP_IGNORE_LIST or pname[0] == '#':
316 ptype = TYPE_NAMES[prop.type]
317 member_name = Conv_name_to_c(prop.name)
318 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
320 # Special handling for lists
321 if type(prop.value) == list:
324 # For phandles, output a reference to the platform data
325 # of the target node.
326 if self.IsPhandle(prop):
327 # Process the list as pairs of (phandle, id)
328 it = iter(prop.value)
329 for phandle_cell, id_cell in zip(it, it):
330 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
331 id = fdt_util.fdt32_to_cpu(id_cell)
332 target_node = self._phandle_node[phandle]
333 name = Conv_name_to_c(target_node.name)
334 vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
336 for val in prop.value:
337 vals.append(self.GetValue(prop.type, val))
338 self.Buf(', '.join(vals))
341 self.Buf(self.GetValue(prop.type, prop.value))
345 # Add a device declaration
346 self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
347 self.Buf('\t.name\t\t= "%s",\n' % struct_name)
348 self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
349 self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' %
350 (VAL_PREFIX, var_name))
354 # Output phandle target nodes first, since they may be referenced
356 if 'phandle' in node.props:
357 self.Out(''.join(self.GetBuf()))
359 node_txt_list.append(self.GetBuf())
361 # Output all the nodes which are not phandle targets themselves, but
362 # may reference them. This avoids the need for forward declarations.
363 for node_txt in node_txt_list:
364 self.Out(''.join(node_txt))
367 if __name__ != "__main__":
370 parser = OptionParser()
371 parser.add_option('-d', '--dtb-file', action='store',
372 help='Specify the .dtb input file')
373 parser.add_option('--include-disabled', action='store_true',
374 help='Include disabled nodes')
375 parser.add_option('-o', '--output', action='store', default='-',
376 help='Select output filename')
377 (options, args) = parser.parse_args()
380 raise ValueError('Please specify a command: struct, platdata')
382 plat = DtbPlatdata(options.dtb_file, options)
385 plat.SetupOutput(options.output)
386 structs = plat.ScanStructs()
388 for cmd in args[0].split(','):
390 plat.GenerateStructs(structs)
391 elif cmd == 'platdata':
392 plat.GenerateTables()
394 raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)