3 # Copyright (C) 2017 Google, Inc
4 # Written by Simon Glass <sjg@chromium.org>
6 # SPDX-License-Identifier: GPL-2.0+
9 """Device tree to platform data class
11 This supports converting device tree data to C structures definitions and
21 # When we see these properties we ignore them - i.e. do not create a structure member
30 '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 new = name.replace('@', '_at_')
55 new = new.replace('-', '_')
56 new = new.replace(',', '_')
57 new = new.replace('.', '_')
60 def tab_to(num_tabs, line):
61 """Append tabs to a line of text to reach a tab stop.
64 num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
65 line: Line of text to append to
68 line with the correct number of tabs appeneded. If the line already
69 extends past that tab stop then a single space is appended.
71 if len(line) >= num_tabs * 8:
73 return line + '\t' * (num_tabs - len(line) // 8)
75 def get_value(ftype, value):
76 """Get a value as a C expression
78 For integers this returns a byte-swapped (little-endian) hex string
79 For bytes this returns a hex string, e.g. 0x12
80 For strings this returns a literal string enclosed in quotes
81 For booleans this return 'true'
84 type: Data type (fdt_util)
85 value: Data value, as a string of bytes
87 if ftype == fdt.TYPE_INT:
88 return '%#x' % fdt_util.fdt32_to_cpu(value)
89 elif ftype == fdt.TYPE_BYTE:
90 return '%#x' % ord(value[0])
91 elif ftype == fdt.TYPE_STRING:
93 elif ftype == fdt.TYPE_BOOL:
96 def get_compat_name(node):
97 """Get a node's first compatible string as a C identifier
100 node: Node object to check
103 C identifier for the first compatible string
104 List of C identifiers for all the other compatible strings
107 compat = node.props['compatible'].value
109 if isinstance(compat, list):
110 compat, aliases = compat[0], compat[1:]
111 return conv_name_to_c(compat), [conv_name_to_c(a) for a in aliases]
113 def is_phandle(prop):
114 """Check if a node contains phandles
116 We have no reliable way of detecting whether a node uses a phandle
117 or not. As an interim measure, use a list of known property names.
120 prop: Prop object to check
122 True if the object value contains phandles, else False
124 if prop.name in ['clocks']:
129 class DtbPlatdata(object):
130 """Provide a means to convert device tree binary data to platform data
132 The output of this process is C structures which can be used in space-
133 constrained encvironments where the ~3KB code overhead of device tree
134 code is not affordable.
137 _fdt: Fdt object, referencing the device tree
138 _dtb_fname: Filename of the input device tree binary file
139 _valid_nodes: A list of Node object with compatible strings
140 _include_disabled: true to include nodes marked status = "disabled"
141 _phandle_nodes: A dict of nodes indexed by phandle number (1, 2...)
142 _outfile: The current output file (sys.stdout or a real file)
143 _lines: Stashed list of output lines for outputting in the future
144 _phandle_nodes: A dict of Nodes indexed by phandle (an integer)
146 def __init__(self, dtb_fname, include_disabled):
148 self._dtb_fname = dtb_fname
149 self._valid_nodes = None
150 self._include_disabled = include_disabled
151 self._phandle_nodes = {}
156 def setup_output(self, fname):
157 """Set up the output destination
159 Once this is done, future calls to self.out() will output to this
163 fname: Filename to send output to, or '-' for stdout
166 self._outfile = sys.stdout
168 self._outfile = open(fname, 'w')
171 """Output a string to the output file
174 line: String to output
176 self._outfile.write(line)
179 """Buffer up a string to send later
182 line: String to add to our 'buffer' list
184 self._lines.append(line)
187 """Get the contents of the output buffer, and clear it
190 The output buffer, which is then cleared for future use
197 """Scan the device tree to obtain a tree of notes and properties
199 Once this is done, self._fdt.GetRoot() can be called to obtain the
200 device tree root node, and progress from there.
202 self._fdt = fdt.FdtScan(self._dtb_fname)
204 def scan_node(self, root):
205 """Scan a node and subnodes to build a tree of node and phandle info
207 This adds each node to self._valid_nodes and each phandle to
211 root: Root node for scan
213 for node in root.subnodes:
214 if 'compatible' in node.props:
215 status = node.props.get('status')
216 if (not self._include_disabled and not status or
217 status.value != 'disabled'):
218 self._valid_nodes.append(node)
219 phandle_prop = node.props.get('phandle')
221 phandle = phandle_prop.GetPhandle()
222 self._phandle_nodes[phandle] = node
224 # recurse to handle any subnodes
228 """Scan the device tree for useful information
230 This fills in the following properties:
231 _phandle_nodes: A dict of Nodes indexed by phandle (an integer)
232 _valid_nodes: A list of nodes we wish to consider include in the
235 self._phandle_nodes = {}
236 self._valid_nodes = []
237 return self.scan_node(self._fdt.GetRoot())
239 def scan_structs(self):
240 """Scan the device tree building up the C structures we will use.
242 Build a dict keyed by C struct name containing a dict of Prop
243 object for each struct field (keyed by property name). Where the
244 same struct appears multiple times, try to use the 'widest'
245 property, i.e. the one with a type which can express all others.
247 Once the widest property is determined, all other properties are
248 updated to match that width.
251 for node in self._valid_nodes:
252 node_name, _ = get_compat_name(node)
255 # Get a list of all the valid properties in this node.
256 for name, prop in node.props.items():
257 if name not in PROP_IGNORE_LIST and name[0] != '#':
258 fields[name] = copy.deepcopy(prop)
260 # If we've seen this node_name before, update the existing struct.
261 if node_name in structs:
262 struct = structs[node_name]
263 for name, prop in fields.items():
264 oldprop = struct.get(name)
270 # Otherwise store this as a new struct.
272 structs[node_name] = fields
275 for node in self._valid_nodes:
276 node_name, _ = get_compat_name(node)
277 struct = structs[node_name]
278 for name, prop in node.props.items():
279 if name not in PROP_IGNORE_LIST and name[0] != '#':
280 prop.Widen(struct[name])
283 struct_name, aliases = get_compat_name(node)
284 for alias in aliases:
285 self._aliases[alias] = struct_name
289 def scan_phandles(self):
290 """Figure out what phandles each node uses
292 We need to be careful when outputing nodes that use phandles since
293 they must come after the declaration of the phandles in the C file.
294 Otherwise we get a compiler error since the phandle struct is not yet
297 This function adds to each node a list of phandle nodes that the node
298 depends on. This allows us to output things in the right order.
300 for node in self._valid_nodes:
301 node.phandles = set()
302 for pname, prop in node.props.items():
303 if pname in PROP_IGNORE_LIST or pname[0] == '#':
305 if isinstance(prop.value, list):
307 # Process the list as pairs of (phandle, id)
308 value_it = iter(prop.value)
309 for phandle_cell, _ in zip(value_it, value_it):
310 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
311 target_node = self._phandle_nodes[phandle]
312 node.phandles.add(target_node)
315 def generate_structs(self, structs):
316 """Generate struct defintions for the platform data
318 This writes out the body of a header file consisting of structure
319 definitions for node in self._valid_nodes. See the documentation in
320 README.of-plat for more information.
322 self.out('#include <stdbool.h>\n')
323 self.out('#include <libfdt.h>\n')
325 # Output the struct definition
326 for name in sorted(structs):
327 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
328 for pname in sorted(structs[name]):
329 prop = structs[name][pname]
331 # For phandles, include a reference to the target
332 self.out('\t%s%s[%d]' % (tab_to(2, 'struct phandle_2_cell'),
333 conv_name_to_c(prop.name),
334 len(prop.value) / 2))
336 ptype = TYPE_NAMES[prop.type]
337 self.out('\t%s%s' % (tab_to(2, ptype),
338 conv_name_to_c(prop.name)))
339 if isinstance(prop.value, list):
340 self.out('[%d]' % len(prop.value))
344 for alias, struct_name in self._aliases.iteritems():
345 self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
346 STRUCT_PREFIX, struct_name))
348 def output_node(self, node):
349 """Output the C code for a node
354 struct_name, _ = get_compat_name(node)
355 var_name = conv_name_to_c(node.name)
356 self.buf('static struct %s%s %s%s = {\n' %
357 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
358 for pname, prop in node.props.items():
359 if pname in PROP_IGNORE_LIST or pname[0] == '#':
361 member_name = conv_name_to_c(prop.name)
362 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
364 # Special handling for lists
365 if isinstance(prop.value, list):
368 # For phandles, output a reference to the platform data
369 # of the target node.
371 # Process the list as pairs of (phandle, id)
372 value_it = iter(prop.value)
373 for phandle_cell, id_cell in zip(value_it, value_it):
374 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
375 id_num = fdt_util.fdt32_to_cpu(id_cell)
376 target_node = self._phandle_nodes[phandle]
377 name = conv_name_to_c(target_node.name)
378 vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id_num))
380 for val in prop.value:
381 vals.append(get_value(prop.type, val))
382 self.buf(', '.join(vals))
385 self.buf(get_value(prop.type, prop.value))
389 # Add a device declaration
390 self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
391 self.buf('\t.name\t\t= "%s",\n' % struct_name)
392 self.buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
393 self.buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name))
397 self.out(''.join(self.get_buf()))
399 def generate_tables(self):
400 """Generate device defintions for the platform data
402 This writes out C platform data initialisation data and
403 U_BOOT_DEVICE() declarations for each valid node. Where a node has
404 multiple compatible strings, a #define is used to make them equivalent.
406 See the documentation in doc/driver-model/of-plat.txt for more
409 self.out('#include <common.h>\n')
410 self.out('#include <dm.h>\n')
411 self.out('#include <dt-structs.h>\n')
413 nodes_to_output = list(self._valid_nodes)
415 # Keep outputing nodes until there is none left
416 while nodes_to_output:
417 node = nodes_to_output[0]
418 # Output all the node's dependencies first
419 for req_node in node.phandles:
420 if req_node in nodes_to_output:
421 self.output_node(req_node)
422 nodes_to_output.remove(req_node)
423 self.output_node(node)
424 nodes_to_output.remove(node)