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('.', '_')
58 str = str.replace('/', '__')
61 def TabTo(num_tabs, str):
62 if len(str) >= num_tabs * 8:
64 return str + '\t' * (num_tabs - len(str) // 8)
67 """Provide a means to convert device tree binary data to platform data
69 The output of this process is C structures which can be used in space-
70 constrained encvironments where the ~3KB code overhead of device tree
71 code is not affordable.
74 fdt: Fdt object, referencing the device tree
75 _dtb_fname: Filename of the input device tree binary file
76 _valid_nodes: A list of Node object with compatible strings
77 _options: Command-line options
78 _phandle_node: A dict of nodes indexed by phandle number (1, 2...)
79 _outfile: The current output file (sys.stdout or a real file)
80 _lines: Stashed list of output lines for outputting in the future
81 _phandle_node: A dict of Nodes indexed by phandle (an integer)
83 def __init__(self, dtb_fname, options):
84 self._dtb_fname = dtb_fname
85 self._valid_nodes = None
86 self._options = options
87 self._phandle_node = {}
91 def SetupOutput(self, fname):
92 """Set up the output destination
94 Once this is done, future calls to self.Out() will output to this
98 fname: Filename to send output to, or '-' for stdout
101 self._outfile = sys.stdout
103 self._outfile = open(fname, 'w')
106 """Output a string to the output file
109 str: String to output
111 self._outfile.write(str)
114 """Buffer up a string to send later
117 str: String to add to our 'buffer' list
119 self._lines.append(str)
122 """Get the contents of the output buffer, and clear it
125 The output buffer, which is then cleared for future use
131 def GetValue(self, type, value):
132 """Get a value as a C expression
134 For integers this returns a byte-swapped (little-endian) hex string
135 For bytes this returns a hex string, e.g. 0x12
136 For strings this returns a literal string enclosed in quotes
137 For booleans this return 'true'
140 type: Data type (fdt_util)
141 value: Data value, as a string of bytes
143 if type == fdt.TYPE_INT:
144 return '%#x' % fdt_util.fdt32_to_cpu(value)
145 elif type == fdt.TYPE_BYTE:
146 return '%#x' % ord(value[0])
147 elif type == fdt.TYPE_STRING:
148 return '"%s"' % value
149 elif type == fdt.TYPE_BOOL:
152 def GetCompatName(self, node):
153 """Get a node's first compatible string as a C identifier
156 node: Node object to check
158 C identifier for the first compatible string
160 compat = node.props['compatible'].value
161 if type(compat) == list:
163 return Conv_name_to_c(compat)
166 """Scan the device tree to obtain a tree of notes and properties
168 Once this is done, self.fdt.GetRoot() can be called to obtain the
169 device tree root node, and progress from there.
171 self.fdt = fdt_select.FdtScan(self._dtb_fname)
174 """Scan the device tree for useful information
176 This fills in the following properties:
177 _phandle_node: A dict of Nodes indexed by phandle (an integer)
178 _valid_nodes: A list of nodes we wish to consider include in the
182 self._phandle_node = {}
183 for node in self.fdt.GetRoot().subnodes:
184 if 'compatible' in node.props:
185 status = node.props.get('status')
186 if (not options.include_disabled and not status or
187 status.value != 'disabled'):
188 node_list.append(node)
189 phandle_prop = node.props.get('phandle')
191 phandle = phandle_prop.GetPhandle()
192 self._phandle_node[phandle] = node
194 self._valid_nodes = node_list
196 def IsPhandle(self, prop):
197 """Check if a node contains phandles
199 We have no reliable way of detecting whether a node uses a phandle
200 or not. As an interim measure, use a list of known property names.
203 prop: Prop object to check
205 True if the object value contains phandles, else False
207 if prop.name in ['clocks']:
211 def ScanStructs(self):
212 """Scan the device tree building up the C structures we will use.
214 Build a dict keyed by C struct name containing a dict of Prop
215 object for each struct field (keyed by property name). Where the
216 same struct appears multiple times, try to use the 'widest'
217 property, i.e. the one with a type which can express all others.
219 Once the widest property is determined, all other properties are
220 updated to match that width.
223 for node in self._valid_nodes:
224 node_name = self.GetCompatName(node)
227 # Get a list of all the valid properties in this node.
228 for name, prop in node.props.items():
229 if name not in PROP_IGNORE_LIST and name[0] != '#':
230 fields[name] = copy.deepcopy(prop)
232 # If we've seen this node_name before, update the existing struct.
233 if node_name in structs:
234 struct = structs[node_name]
235 for name, prop in fields.items():
236 oldprop = struct.get(name)
242 # Otherwise store this as a new struct.
244 structs[node_name] = fields
247 for node in self._valid_nodes:
248 node_name = self.GetCompatName(node)
249 struct = structs[node_name]
250 for name, prop in node.props.items():
251 if name not in PROP_IGNORE_LIST and name[0] != '#':
252 prop.Widen(struct[name])
256 def GenerateStructs(self, structs):
257 """Generate struct defintions for the platform data
259 This writes out the body of a header file consisting of structure
260 definitions for node in self._valid_nodes. See the documentation in
261 README.of-plat for more information.
263 self.Out('#include <stdbool.h>\n')
264 self.Out('#include <libfdt.h>\n')
266 # Output the struct definition
267 for name in sorted(structs):
268 self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
269 for pname in sorted(structs[name]):
270 prop = structs[name][pname]
271 if self.IsPhandle(prop):
272 # For phandles, include a reference to the target
273 self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
274 Conv_name_to_c(prop.name),
275 len(prop.value) / 2))
277 ptype = TYPE_NAMES[prop.type]
278 self.Out('\t%s%s' % (TabTo(2, ptype),
279 Conv_name_to_c(prop.name)))
280 if type(prop.value) == list:
281 self.Out('[%d]' % len(prop.value))
285 def GenerateTables(self):
286 """Generate device defintions for the platform data
288 This writes out C platform data initialisation data and
289 U_BOOT_DEVICE() declarations for each valid node. See the
290 documentation in README.of-plat for more information.
292 self.Out('#include <common.h>\n')
293 self.Out('#include <dm.h>\n')
294 self.Out('#include <dt-structs.h>\n')
297 for node in self._valid_nodes:
298 struct_name = self.GetCompatName(node)
299 var_name = Conv_name_to_c(node.name)
300 self.Buf('static struct %s%s %s%s = {\n' %
301 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
302 for pname, prop in node.props.items():
303 if pname in PROP_IGNORE_LIST or pname[0] == '#':
305 ptype = TYPE_NAMES[prop.type]
306 member_name = Conv_name_to_c(prop.name)
307 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
309 # Special handling for lists
310 if type(prop.value) == list:
313 # For phandles, output a reference to the platform data
314 # of the target node.
315 if self.IsPhandle(prop):
316 # Process the list as pairs of (phandle, id)
317 it = iter(prop.value)
318 for phandle_cell, id_cell in zip(it, it):
319 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
320 id = fdt_util.fdt32_to_cpu(id_cell)
321 target_node = self._phandle_node[phandle]
322 name = Conv_name_to_c(target_node.name)
323 vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
325 for val in prop.value:
326 vals.append(self.GetValue(prop.type, val))
327 self.Buf(', '.join(vals))
330 self.Buf(self.GetValue(prop.type, prop.value))
334 # Add a device declaration
335 self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
336 self.Buf('\t.name\t\t= "%s",\n' % struct_name)
337 self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
338 self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' %
339 (VAL_PREFIX, var_name))
343 # Output phandle target nodes first, since they may be referenced
345 if 'phandle' in node.props:
346 self.Out(''.join(self.GetBuf()))
348 node_txt_list.append(self.GetBuf())
350 # Output all the nodes which are not phandle targets themselves, but
351 # may reference them. This avoids the need for forward declarations.
352 for node_txt in node_txt_list:
353 self.Out(''.join(node_txt))
356 if __name__ != "__main__":
359 parser = OptionParser()
360 parser.add_option('-d', '--dtb-file', action='store',
361 help='Specify the .dtb input file')
362 parser.add_option('--include-disabled', action='store_true',
363 help='Include disabled nodes')
364 parser.add_option('-o', '--output', action='store', default='-',
365 help='Select output filename')
366 (options, args) = parser.parse_args()
369 raise ValueError('Please specify a command: struct, platdata')
371 plat = DtbPlatdata(options.dtb_file, options)
374 plat.SetupOutput(options.output)
375 structs = plat.ScanStructs()
377 for cmd in args[0].split(','):
379 plat.GenerateStructs(structs)
380 elif cmd == 'platdata':
381 plat.GenerateTables()
383 raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)