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'))
22 # When we see these properties we ignore them - i.e. do not create a structure member
31 'u-boot,dm-pre-reloc',
34 # C type declarations for the tyues we support
36 fdt_util.TYPE_INT: 'fdt32_t',
37 fdt_util.TYPE_BYTE: 'unsigned char',
38 fdt_util.TYPE_STRING: 'const char *',
39 fdt_util.TYPE_BOOL: 'bool',
42 STRUCT_PREFIX = 'dtd_'
45 def Conv_name_to_c(name):
46 """Convert a device-tree name to a C identifier
51 String containing the C version of this name
53 str = name.replace('@', '_at_')
54 str = str.replace('-', '_')
55 str = str.replace(',', '_')
56 str = str.replace('/', '__')
59 def TabTo(num_tabs, str):
60 if len(str) >= num_tabs * 8:
62 return str + '\t' * (num_tabs - len(str) / 8)
65 """Provide a means to convert device tree binary data to platform data
67 The output of this process is C structures which can be used in space-
68 constrained encvironments where the ~3KB code overhead of device tree
69 code is not affordable.
72 fdt: Fdt object, referencing the device tree
73 _dtb_fname: Filename of the input device tree binary file
74 _valid_nodes: A list of Node object with compatible strings
75 _options: Command-line options
76 _phandle_node: A dict of nodes indexed by phandle number (1, 2...)
77 _outfile: The current output file (sys.stdout or a real file)
78 _lines: Stashed list of output lines for outputting in the future
79 _phandle_node: A dict of Nodes indexed by phandle (an integer)
81 def __init__(self, dtb_fname, options):
82 self._dtb_fname = dtb_fname
83 self._valid_nodes = None
84 self._options = options
85 self._phandle_node = {}
89 def SetupOutput(self, fname):
90 """Set up the output destination
92 Once this is done, future calls to self.Out() will output to this
96 fname: Filename to send output to, or '-' for stdout
99 self._outfile = sys.stdout
101 self._outfile = open(fname, 'w')
104 """Output a string to the output file
107 str: String to output
109 self._outfile.write(str)
112 """Buffer up a string to send later
115 str: String to add to our 'buffer' list
117 self._lines.append(str)
120 """Get the contents of the output buffer, and clear it
123 The output buffer, which is then cleared for future use
129 def GetValue(self, type, value):
130 """Get a value as a C expression
132 For integers this returns a byte-swapped (little-endian) hex string
133 For bytes this returns a hex string, e.g. 0x12
134 For strings this returns a literal string enclosed in quotes
135 For booleans this return 'true'
138 type: Data type (fdt_util)
139 value: Data value, as a string of bytes
141 if type == fdt_util.TYPE_INT:
142 return '%#x' % fdt_util.fdt32_to_cpu(value)
143 elif type == fdt_util.TYPE_BYTE:
144 return '%#x' % ord(value[0])
145 elif type == fdt_util.TYPE_STRING:
146 return '"%s"' % value
147 elif type == fdt_util.TYPE_BOOL:
150 def GetCompatName(self, node):
151 """Get a node's first compatible string as a C identifier
154 node: Node object to check
156 C identifier for the first compatible string
158 compat = node.props['compatible'].value
159 if type(compat) == list:
161 return Conv_name_to_c(compat)
164 """Scan the device tree to obtain a tree of notes and properties
166 Once this is done, self.fdt.GetRoot() can be called to obtain the
167 device tree root node, and progress from there.
169 self.fdt = fdt_select.FdtScan(self._dtb_fname)
172 """Scan the device tree for useful information
174 This fills in the following properties:
175 _phandle_node: A dict of Nodes indexed by phandle (an integer)
176 _valid_nodes: A list of nodes we wish to consider include in the
180 self._phandle_node = {}
181 for node in self.fdt.GetRoot().subnodes:
182 if 'compatible' in node.props:
183 status = node.props.get('status')
184 if (not options.include_disabled and not status or
185 status.value != 'disabled'):
186 node_list.append(node)
187 phandle_prop = node.props.get('phandle')
189 phandle = phandle_prop.GetPhandle()
190 self._phandle_node[phandle] = node
192 self._valid_nodes = node_list
194 def IsPhandle(self, prop):
195 """Check if a node contains phandles
197 We have no reliable way of detecting whether a node uses a phandle
198 or not. As an interim measure, use a list of known property names.
201 prop: Prop object to check
203 True if the object value contains phandles, else False
205 if prop.name in ['clocks']:
209 def ScanStructs(self):
210 """Scan the device tree building up the C structures we will use.
212 Build a dict keyed by C struct name containing a dict of Prop
213 object for each struct field (keyed by property name). Where the
214 same struct appears multiple times, try to use the 'widest'
215 property, i.e. the one with a type which can express all others.
217 Once the widest property is determined, all other properties are
218 updated to match that width.
221 for node in self._valid_nodes:
222 node_name = self.GetCompatName(node)
225 # Get a list of all the valid properties in this node.
226 for name, prop in node.props.iteritems():
227 if name not in PROP_IGNORE_LIST and name[0] != '#':
228 fields[name] = copy.deepcopy(prop)
230 # If we've seen this node_name before, update the existing struct.
231 if node_name in structs:
232 struct = structs[node_name]
233 for name, prop in fields.iteritems():
234 oldprop = struct.get(name)
240 # Otherwise store this as a new struct.
242 structs[node_name] = fields
245 for node in self._valid_nodes:
246 node_name = self.GetCompatName(node)
247 struct = structs[node_name]
248 for name, prop in node.props.iteritems():
249 if name not in PROP_IGNORE_LIST and name[0] != '#':
250 prop.Widen(struct[name])
254 def GenerateStructs(self, structs):
255 """Generate struct defintions for the platform data
257 This writes out the body of a header file consisting of structure
258 definitions for node in self._valid_nodes. See the documentation in
259 README.of-plat for more information.
261 self.Out('#include <stdbool.h>\n')
262 self.Out('#include <libfdt.h>\n')
264 # Output the struct definition
265 for name in sorted(structs):
266 self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
267 for pname in sorted(structs[name]):
268 prop = structs[name][pname]
269 if self.IsPhandle(prop):
270 # For phandles, include a reference to the target
271 self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
272 Conv_name_to_c(prop.name),
273 len(prop.value) / 2))
275 ptype = TYPE_NAMES[prop.type]
276 self.Out('\t%s%s' % (TabTo(2, ptype),
277 Conv_name_to_c(prop.name)))
278 if type(prop.value) == list:
279 self.Out('[%d]' % len(prop.value))
283 def GenerateTables(self):
284 """Generate device defintions for the platform data
286 This writes out C platform data initialisation data and
287 U_BOOT_DEVICE() declarations for each valid node. See the
288 documentation in README.of-plat for more information.
290 self.Out('#include <common.h>\n')
291 self.Out('#include <dm.h>\n')
292 self.Out('#include <dt-structs.h>\n')
295 for node in self._valid_nodes:
296 struct_name = self.GetCompatName(node)
297 var_name = Conv_name_to_c(node.name)
298 self.Buf('static struct %s%s %s%s = {\n' %
299 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
300 for pname, prop in node.props.iteritems():
301 if pname in PROP_IGNORE_LIST or pname[0] == '#':
303 ptype = TYPE_NAMES[prop.type]
304 member_name = Conv_name_to_c(prop.name)
305 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
307 # Special handling for lists
308 if type(prop.value) == list:
311 # For phandles, output a reference to the platform data
312 # of the target node.
313 if self.IsPhandle(prop):
314 # Process the list as pairs of (phandle, id)
315 it = iter(prop.value)
316 for phandle_cell, id_cell in zip(it, it):
317 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
318 id = fdt_util.fdt32_to_cpu(id_cell)
319 target_node = self._phandle_node[phandle]
320 name = Conv_name_to_c(target_node.name)
321 vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
323 for val in prop.value:
324 vals.append(self.GetValue(prop.type, val))
325 self.Buf(', '.join(vals))
328 self.Buf(self.GetValue(prop.type, prop.value))
332 # Add a device declaration
333 self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
334 self.Buf('\t.name\t\t= "%s",\n' % struct_name)
335 self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
336 self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' %
337 (VAL_PREFIX, var_name))
341 # Output phandle target nodes first, since they may be referenced
343 if 'phandle' in node.props:
344 self.Out(''.join(self.GetBuf()))
346 node_txt_list.append(self.GetBuf())
348 # Output all the nodes which are not phandle targets themselves, but
349 # may reference them. This avoids the need for forward declarations.
350 for node_txt in node_txt_list:
351 self.Out(''.join(node_txt))
354 if __name__ != "__main__":
357 parser = OptionParser()
358 parser.add_option('-d', '--dtb-file', action='store',
359 help='Specify the .dtb input file')
360 parser.add_option('--include-disabled', action='store_true',
361 help='Include disabled nodes')
362 parser.add_option('-o', '--output', action='store', default='-',
363 help='Select output filename')
364 (options, args) = parser.parse_args()
367 raise ValueError('Please specify a command: struct, platdata')
369 plat = DtbPlatdata(options.dtb_file, options)
372 plat.SetupOutput(options.output)
373 structs = plat.ScanStructs()
375 for cmd in args[0].split(','):
377 plat.GenerateStructs(structs)
378 elif cmd == 'platdata':
379 plat.GenerateTables()
381 raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)