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',
35 # C type declarations for the tyues we support
37 fdt.TYPE_INT: 'fdt32_t',
38 fdt.TYPE_BYTE: 'unsigned char',
39 fdt.TYPE_STRING: 'const char *',
40 fdt.TYPE_BOOL: 'bool',
43 STRUCT_PREFIX = 'dtd_'
46 def Conv_name_to_c(name):
47 """Convert a device-tree name to a C identifier
52 String containing the C version of this name
54 str = name.replace('@', '_at_')
55 str = str.replace('-', '_')
56 str = str.replace(',', '_')
57 str = str.replace('/', '__')
60 def TabTo(num_tabs, str):
61 if len(str) >= num_tabs * 8:
63 return str + '\t' * (num_tabs - len(str) / 8)
66 """Provide a means to convert device tree binary data to platform data
68 The output of this process is C structures which can be used in space-
69 constrained encvironments where the ~3KB code overhead of device tree
70 code is not affordable.
73 fdt: Fdt object, referencing the device tree
74 _dtb_fname: Filename of the input device tree binary file
75 _valid_nodes: A list of Node object with compatible strings
76 _options: Command-line options
77 _phandle_node: A dict of nodes indexed by phandle number (1, 2...)
78 _outfile: The current output file (sys.stdout or a real file)
79 _lines: Stashed list of output lines for outputting in the future
80 _phandle_node: A dict of Nodes indexed by phandle (an integer)
82 def __init__(self, dtb_fname, options):
83 self._dtb_fname = dtb_fname
84 self._valid_nodes = None
85 self._options = options
86 self._phandle_node = {}
90 def SetupOutput(self, fname):
91 """Set up the output destination
93 Once this is done, future calls to self.Out() will output to this
97 fname: Filename to send output to, or '-' for stdout
100 self._outfile = sys.stdout
102 self._outfile = open(fname, 'w')
105 """Output a string to the output file
108 str: String to output
110 self._outfile.write(str)
113 """Buffer up a string to send later
116 str: String to add to our 'buffer' list
118 self._lines.append(str)
121 """Get the contents of the output buffer, and clear it
124 The output buffer, which is then cleared for future use
130 def GetValue(self, type, value):
131 """Get a value as a C expression
133 For integers this returns a byte-swapped (little-endian) hex string
134 For bytes this returns a hex string, e.g. 0x12
135 For strings this returns a literal string enclosed in quotes
136 For booleans this return 'true'
139 type: Data type (fdt_util)
140 value: Data value, as a string of bytes
142 if type == fdt.TYPE_INT:
143 return '%#x' % fdt_util.fdt32_to_cpu(value)
144 elif type == fdt.TYPE_BYTE:
145 return '%#x' % ord(value[0])
146 elif type == fdt.TYPE_STRING:
147 return '"%s"' % value
148 elif type == fdt.TYPE_BOOL:
151 def GetCompatName(self, node):
152 """Get a node's first compatible string as a C identifier
155 node: Node object to check
157 C identifier for the first compatible string
159 compat = node.props['compatible'].value
160 if type(compat) == list:
162 return Conv_name_to_c(compat)
165 """Scan the device tree to obtain a tree of notes and properties
167 Once this is done, self.fdt.GetRoot() can be called to obtain the
168 device tree root node, and progress from there.
170 self.fdt = fdt_select.FdtScan(self._dtb_fname)
173 """Scan the device tree for useful information
175 This fills in the following properties:
176 _phandle_node: A dict of Nodes indexed by phandle (an integer)
177 _valid_nodes: A list of nodes we wish to consider include in the
181 self._phandle_node = {}
182 for node in self.fdt.GetRoot().subnodes:
183 if 'compatible' in node.props:
184 status = node.props.get('status')
185 if (not options.include_disabled and not status or
186 status.value != 'disabled'):
187 node_list.append(node)
188 phandle_prop = node.props.get('phandle')
190 phandle = phandle_prop.GetPhandle()
191 self._phandle_node[phandle] = node
193 self._valid_nodes = node_list
195 def IsPhandle(self, prop):
196 """Check if a node contains phandles
198 We have no reliable way of detecting whether a node uses a phandle
199 or not. As an interim measure, use a list of known property names.
202 prop: Prop object to check
204 True if the object value contains phandles, else False
206 if prop.name in ['clocks']:
210 def ScanStructs(self):
211 """Scan the device tree building up the C structures we will use.
213 Build a dict keyed by C struct name containing a dict of Prop
214 object for each struct field (keyed by property name). Where the
215 same struct appears multiple times, try to use the 'widest'
216 property, i.e. the one with a type which can express all others.
218 Once the widest property is determined, all other properties are
219 updated to match that width.
222 for node in self._valid_nodes:
223 node_name = self.GetCompatName(node)
226 # Get a list of all the valid properties in this node.
227 for name, prop in node.props.iteritems():
228 if name not in PROP_IGNORE_LIST and name[0] != '#':
229 fields[name] = copy.deepcopy(prop)
231 # If we've seen this node_name before, update the existing struct.
232 if node_name in structs:
233 struct = structs[node_name]
234 for name, prop in fields.iteritems():
235 oldprop = struct.get(name)
241 # Otherwise store this as a new struct.
243 structs[node_name] = fields
246 for node in self._valid_nodes:
247 node_name = self.GetCompatName(node)
248 struct = structs[node_name]
249 for name, prop in node.props.iteritems():
250 if name not in PROP_IGNORE_LIST and name[0] != '#':
251 prop.Widen(struct[name])
255 def GenerateStructs(self, structs):
256 """Generate struct defintions for the platform data
258 This writes out the body of a header file consisting of structure
259 definitions for node in self._valid_nodes. See the documentation in
260 README.of-plat for more information.
262 self.Out('#include <stdbool.h>\n')
263 self.Out('#include <libfdt.h>\n')
265 # Output the struct definition
266 for name in sorted(structs):
267 self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
268 for pname in sorted(structs[name]):
269 prop = structs[name][pname]
270 if self.IsPhandle(prop):
271 # For phandles, include a reference to the target
272 self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
273 Conv_name_to_c(prop.name),
274 len(prop.value) / 2))
276 ptype = TYPE_NAMES[prop.type]
277 self.Out('\t%s%s' % (TabTo(2, ptype),
278 Conv_name_to_c(prop.name)))
279 if type(prop.value) == list:
280 self.Out('[%d]' % len(prop.value))
284 def GenerateTables(self):
285 """Generate device defintions for the platform data
287 This writes out C platform data initialisation data and
288 U_BOOT_DEVICE() declarations for each valid node. See the
289 documentation in README.of-plat for more information.
291 self.Out('#include <common.h>\n')
292 self.Out('#include <dm.h>\n')
293 self.Out('#include <dt-structs.h>\n')
296 for node in self._valid_nodes:
297 struct_name = self.GetCompatName(node)
298 var_name = Conv_name_to_c(node.name)
299 self.Buf('static struct %s%s %s%s = {\n' %
300 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
301 for pname, prop in node.props.iteritems():
302 if pname in PROP_IGNORE_LIST or pname[0] == '#':
304 ptype = TYPE_NAMES[prop.type]
305 member_name = Conv_name_to_c(prop.name)
306 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
308 # Special handling for lists
309 if type(prop.value) == list:
312 # For phandles, output a reference to the platform data
313 # of the target node.
314 if self.IsPhandle(prop):
315 # Process the list as pairs of (phandle, id)
316 it = iter(prop.value)
317 for phandle_cell, id_cell in zip(it, it):
318 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
319 id = fdt_util.fdt32_to_cpu(id_cell)
320 target_node = self._phandle_node[phandle]
321 name = Conv_name_to_c(target_node.name)
322 vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
324 for val in prop.value:
325 vals.append(self.GetValue(prop.type, val))
326 self.Buf(', '.join(vals))
329 self.Buf(self.GetValue(prop.type, prop.value))
333 # Add a device declaration
334 self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
335 self.Buf('\t.name\t\t= "%s",\n' % struct_name)
336 self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
337 self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' %
338 (VAL_PREFIX, var_name))
342 # Output phandle target nodes first, since they may be referenced
344 if 'phandle' in node.props:
345 self.Out(''.join(self.GetBuf()))
347 node_txt_list.append(self.GetBuf())
349 # Output all the nodes which are not phandle targets themselves, but
350 # may reference them. This avoids the need for forward declarations.
351 for node_txt in node_txt_list:
352 self.Out(''.join(node_txt))
355 if __name__ != "__main__":
358 parser = OptionParser()
359 parser.add_option('-d', '--dtb-file', action='store',
360 help='Specify the .dtb input file')
361 parser.add_option('--include-disabled', action='store_true',
362 help='Include disabled nodes')
363 parser.add_option('-o', '--output', action='store', default='-',
364 help='Select output filename')
365 (options, args) = parser.parse_args()
368 raise ValueError('Please specify a command: struct, platdata')
370 plat = DtbPlatdata(options.dtb_file, options)
373 plat.SetupOutput(options.output)
374 structs = plat.ScanStructs()
376 for cmd in args[0].split(','):
378 plat.GenerateStructs(structs)
379 elif cmd == 'platdata':
380 plat.GenerateTables()
382 raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)