blob: 75adf48deafaffad308c90d67933858dfac81344 [file] [log] [blame]
Simon Glassd570dec2017-06-18 22:08:58 -06001#!/usr/bin/python
2#
3# Copyright (C) 2017 Google, Inc
4# Written by Simon Glass <sjg@chromium.org>
5#
6# SPDX-License-Identifier: GPL-2.0+
7#
8
Simon Glass1f730c82017-06-18 22:08:59 -06009"""Device tree to platform data class
10
11This supports converting device tree data to C structures definitions and
12static data.
13"""
14
Simon Glassd570dec2017-06-18 22:08:58 -060015import copy
Simon Glass1f730c82017-06-18 22:08:59 -060016import sys
Simon Glassd570dec2017-06-18 22:08:58 -060017
18import fdt
19import fdt_util
20
21# When we see these properties we ignore them - i.e. do not create a structure member
22PROP_IGNORE_LIST = [
23 '#address-cells',
24 '#gpio-cells',
25 '#size-cells',
26 'compatible',
27 'linux,phandle',
28 "status",
29 'phandle',
30 'u-boot,dm-pre-reloc',
31 'u-boot,dm-tpl',
32 'u-boot,dm-spl',
33]
34
35# C type declarations for the tyues we support
36TYPE_NAMES = {
37 fdt.TYPE_INT: 'fdt32_t',
38 fdt.TYPE_BYTE: 'unsigned char',
39 fdt.TYPE_STRING: 'const char *',
40 fdt.TYPE_BOOL: 'bool',
Simon Glass1f730c82017-06-18 22:08:59 -060041}
Simon Glassd570dec2017-06-18 22:08:58 -060042
43STRUCT_PREFIX = 'dtd_'
44VAL_PREFIX = 'dtv_'
45
Simon Glass1f730c82017-06-18 22:08:59 -060046def conv_name_to_c(name):
Simon Glassd570dec2017-06-18 22:08:58 -060047 """Convert a device-tree name to a C identifier
48
49 Args:
50 name: Name to convert
51 Return:
52 String containing the C version of this name
53 """
Simon Glass1f730c82017-06-18 22:08:59 -060054 new = name.replace('@', '_at_')
55 new = new.replace('-', '_')
56 new = new.replace(',', '_')
57 new = new.replace('.', '_')
58 new = new.replace('/', '__')
59 return new
Simon Glassd570dec2017-06-18 22:08:58 -060060
Simon Glass1f730c82017-06-18 22:08:59 -060061def tab_to(num_tabs, line):
62 """Append tabs to a line of text to reach a tab stop.
Simon Glassd570dec2017-06-18 22:08:58 -060063
Simon Glass1f730c82017-06-18 22:08:59 -060064 Args:
65 num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
66 line: Line of text to append to
67
68 Returns:
69 line with the correct number of tabs appeneded. If the line already
70 extends past that tab stop then a single space is appended.
71 """
72 if len(line) >= num_tabs * 8:
73 return line + ' '
74 return line + '\t' * (num_tabs - len(line) // 8)
75
76class DtbPlatdata(object):
Simon Glassd570dec2017-06-18 22:08:58 -060077 """Provide a means to convert device tree binary data to platform data
78
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.
82
83 Properties:
Simon Glass1f730c82017-06-18 22:08:59 -060084 _fdt: Fdt object, referencing the device tree
Simon Glassd570dec2017-06-18 22:08:58 -060085 _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
Simon Glass1f730c82017-06-18 22:08:59 -060088 _phandle_nodes: A dict of nodes indexed by phandle number (1, 2...)
Simon Glassd570dec2017-06-18 22:08:58 -060089 _outfile: The current output file (sys.stdout or a real file)
90 _lines: Stashed list of output lines for outputting in the future
Simon Glass1f730c82017-06-18 22:08:59 -060091 _phandle_nodes: A dict of Nodes indexed by phandle (an integer)
Simon Glassd570dec2017-06-18 22:08:58 -060092 """
93 def __init__(self, dtb_fname, options):
Simon Glass1f730c82017-06-18 22:08:59 -060094 self._fdt = None
Simon Glassd570dec2017-06-18 22:08:58 -060095 self._dtb_fname = dtb_fname
96 self._valid_nodes = None
97 self._options = options
Simon Glass1f730c82017-06-18 22:08:59 -060098 self._phandle_nodes = {}
Simon Glassd570dec2017-06-18 22:08:58 -060099 self._outfile = None
100 self._lines = []
101 self._aliases = {}
102
Simon Glass1f730c82017-06-18 22:08:59 -0600103 def setup_output(self, fname):
Simon Glassd570dec2017-06-18 22:08:58 -0600104 """Set up the output destination
105
Simon Glass1f730c82017-06-18 22:08:59 -0600106 Once this is done, future calls to self.out() will output to this
Simon Glassd570dec2017-06-18 22:08:58 -0600107 file.
108
109 Args:
110 fname: Filename to send output to, or '-' for stdout
111 """
112 if fname == '-':
113 self._outfile = sys.stdout
114 else:
115 self._outfile = open(fname, 'w')
116
Simon Glass1f730c82017-06-18 22:08:59 -0600117 def out(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600118 """Output a string to the output file
119
120 Args:
Simon Glass1f730c82017-06-18 22:08:59 -0600121 line: String to output
Simon Glassd570dec2017-06-18 22:08:58 -0600122 """
Simon Glass1f730c82017-06-18 22:08:59 -0600123 self._outfile.write(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600124
Simon Glass1f730c82017-06-18 22:08:59 -0600125 def buf(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600126 """Buffer up a string to send later
127
128 Args:
Simon Glass1f730c82017-06-18 22:08:59 -0600129 line: String to add to our 'buffer' list
Simon Glassd570dec2017-06-18 22:08:58 -0600130 """
Simon Glass1f730c82017-06-18 22:08:59 -0600131 self._lines.append(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600132
Simon Glass1f730c82017-06-18 22:08:59 -0600133 def get_buf(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600134 """Get the contents of the output buffer, and clear it
135
136 Returns:
137 The output buffer, which is then cleared for future use
138 """
139 lines = self._lines
140 self._lines = []
141 return lines
142
Simon Glass1f730c82017-06-18 22:08:59 -0600143 @staticmethod
144 def get_value(ftype, value):
Simon Glassd570dec2017-06-18 22:08:58 -0600145 """Get a value as a C expression
146
147 For integers this returns a byte-swapped (little-endian) hex string
148 For bytes this returns a hex string, e.g. 0x12
149 For strings this returns a literal string enclosed in quotes
150 For booleans this return 'true'
151
152 Args:
153 type: Data type (fdt_util)
154 value: Data value, as a string of bytes
155 """
Simon Glass1f730c82017-06-18 22:08:59 -0600156 if ftype == fdt.TYPE_INT:
Simon Glassd570dec2017-06-18 22:08:58 -0600157 return '%#x' % fdt_util.fdt32_to_cpu(value)
Simon Glass1f730c82017-06-18 22:08:59 -0600158 elif ftype == fdt.TYPE_BYTE:
Simon Glassd570dec2017-06-18 22:08:58 -0600159 return '%#x' % ord(value[0])
Simon Glass1f730c82017-06-18 22:08:59 -0600160 elif ftype == fdt.TYPE_STRING:
Simon Glassd570dec2017-06-18 22:08:58 -0600161 return '"%s"' % value
Simon Glass1f730c82017-06-18 22:08:59 -0600162 elif ftype == fdt.TYPE_BOOL:
Simon Glassd570dec2017-06-18 22:08:58 -0600163 return 'true'
164
Simon Glass1f730c82017-06-18 22:08:59 -0600165 @staticmethod
166 def get_compat_name(node):
Simon Glassd570dec2017-06-18 22:08:58 -0600167 """Get a node's first compatible string as a C identifier
168
169 Args:
170 node: Node object to check
171 Return:
172 C identifier for the first compatible string
173 """
174 compat = node.props['compatible'].value
175 aliases = []
Simon Glass1f730c82017-06-18 22:08:59 -0600176 if isinstance(compat, list):
Simon Glassd570dec2017-06-18 22:08:58 -0600177 compat, aliases = compat[0], compat[1:]
Simon Glass1f730c82017-06-18 22:08:59 -0600178 return conv_name_to_c(compat), [conv_name_to_c(a) for a in aliases]
Simon Glassd570dec2017-06-18 22:08:58 -0600179
Simon Glass1f730c82017-06-18 22:08:59 -0600180 def scan_dtb(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600181 """Scan the device tree to obtain a tree of notes and properties
182
Simon Glass1f730c82017-06-18 22:08:59 -0600183 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glassd570dec2017-06-18 22:08:58 -0600184 device tree root node, and progress from there.
185 """
Simon Glass1f730c82017-06-18 22:08:59 -0600186 self._fdt = fdt.FdtScan(self._dtb_fname)
187
188 def scan_node(self, root):
189 """Scan a node and subnodes to build a tree of node and phandle info
Simon Glassd570dec2017-06-18 22:08:58 -0600190
Simon Glass1f730c82017-06-18 22:08:59 -0600191 This adds each node to self._valid_nodes and each phandle to
192 self._phandle_nodes.
193
194 Args:
195 root: Root node for scan
196 """
Simon Glassd570dec2017-06-18 22:08:58 -0600197 for node in root.subnodes:
198 if 'compatible' in node.props:
199 status = node.props.get('status')
200 if (not self._options.include_disabled and not status or
Simon Glass1f730c82017-06-18 22:08:59 -0600201 status.value != 'disabled'):
Simon Glassd570dec2017-06-18 22:08:58 -0600202 self._valid_nodes.append(node)
203 phandle_prop = node.props.get('phandle')
204 if phandle_prop:
205 phandle = phandle_prop.GetPhandle()
Simon Glass1f730c82017-06-18 22:08:59 -0600206 self._phandle_nodes[phandle] = node
Simon Glassd570dec2017-06-18 22:08:58 -0600207
208 # recurse to handle any subnodes
Simon Glass1f730c82017-06-18 22:08:59 -0600209 self.scan_node(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600210
Simon Glass1f730c82017-06-18 22:08:59 -0600211 def scan_tree(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600212 """Scan the device tree for useful information
213
214 This fills in the following properties:
Simon Glass1f730c82017-06-18 22:08:59 -0600215 _phandle_nodes: A dict of Nodes indexed by phandle (an integer)
Simon Glassd570dec2017-06-18 22:08:58 -0600216 _valid_nodes: A list of nodes we wish to consider include in the
217 platform data
218 """
Simon Glass1f730c82017-06-18 22:08:59 -0600219 self._phandle_nodes = {}
Simon Glassd570dec2017-06-18 22:08:58 -0600220 self._valid_nodes = []
Simon Glass1f730c82017-06-18 22:08:59 -0600221 return self.scan_node(self._fdt.GetRoot())
Simon Glassd570dec2017-06-18 22:08:58 -0600222
Simon Glass1f730c82017-06-18 22:08:59 -0600223 @staticmethod
224 def is_phandle(prop):
Simon Glassd570dec2017-06-18 22:08:58 -0600225 """Check if a node contains phandles
226
227 We have no reliable way of detecting whether a node uses a phandle
228 or not. As an interim measure, use a list of known property names.
229
230 Args:
231 prop: Prop object to check
232 Return:
233 True if the object value contains phandles, else False
234 """
235 if prop.name in ['clocks']:
236 return True
237 return False
238
Simon Glass1f730c82017-06-18 22:08:59 -0600239 def scan_structs(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600240 """Scan the device tree building up the C structures we will use.
241
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.
246
247 Once the widest property is determined, all other properties are
248 updated to match that width.
249 """
250 structs = {}
251 for node in self._valid_nodes:
Simon Glass1f730c82017-06-18 22:08:59 -0600252 node_name, _ = self.get_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600253 fields = {}
254
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)
259
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)
265 if oldprop:
266 oldprop.Widen(prop)
267 else:
268 struct[name] = prop
269
270 # Otherwise store this as a new struct.
271 else:
272 structs[node_name] = fields
273
274 upto = 0
275 for node in self._valid_nodes:
Simon Glass1f730c82017-06-18 22:08:59 -0600276 node_name, _ = self.get_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600277 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])
281 upto += 1
282
Simon Glass1f730c82017-06-18 22:08:59 -0600283 struct_name, aliases = self.get_compat_name(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600284 for alias in aliases:
285 self._aliases[alias] = struct_name
286
287 return structs
288
Simon Glass1f730c82017-06-18 22:08:59 -0600289 def scan_phandles(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600290 """Figure out what phandles each node uses
291
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
295 declared.
296
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.
299 """
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] == '#':
304 continue
Simon Glass1f730c82017-06-18 22:08:59 -0600305 if isinstance(prop.value, list):
306 if self.is_phandle(prop):
Simon Glassd570dec2017-06-18 22:08:58 -0600307 # Process the list as pairs of (phandle, id)
Simon Glass1f730c82017-06-18 22:08:59 -0600308 value_it = iter(prop.value)
309 for phandle_cell, _ in zip(value_it, value_it):
Simon Glassd570dec2017-06-18 22:08:58 -0600310 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
Simon Glass1f730c82017-06-18 22:08:59 -0600311 target_node = self._phandle_nodes[phandle]
Simon Glassd570dec2017-06-18 22:08:58 -0600312 node.phandles.add(target_node)
313
314
Simon Glass1f730c82017-06-18 22:08:59 -0600315 def generate_structs(self, structs):
Simon Glassd570dec2017-06-18 22:08:58 -0600316 """Generate struct defintions for the platform data
317
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.
321 """
Simon Glass1f730c82017-06-18 22:08:59 -0600322 self.out('#include <stdbool.h>\n')
323 self.out('#include <libfdt.h>\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600324
325 # Output the struct definition
326 for name in sorted(structs):
Simon Glass1f730c82017-06-18 22:08:59 -0600327 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glassd570dec2017-06-18 22:08:58 -0600328 for pname in sorted(structs[name]):
329 prop = structs[name][pname]
Simon Glass1f730c82017-06-18 22:08:59 -0600330 if self.is_phandle(prop):
Simon Glassd570dec2017-06-18 22:08:58 -0600331 # For phandles, include a reference to the target
Simon Glass1f730c82017-06-18 22:08:59 -0600332 self.out('\t%s%s[%d]' % (tab_to(2, 'struct phandle_2_cell'),
333 conv_name_to_c(prop.name),
Simon Glassd570dec2017-06-18 22:08:58 -0600334 len(prop.value) / 2))
335 else:
336 ptype = TYPE_NAMES[prop.type]
Simon Glass1f730c82017-06-18 22:08:59 -0600337 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))
341 self.out(';\n')
342 self.out('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600343
344 for alias, struct_name in self._aliases.iteritems():
Simon Glass1f730c82017-06-18 22:08:59 -0600345 self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
Simon Glassd570dec2017-06-18 22:08:58 -0600346 STRUCT_PREFIX, struct_name))
347
Simon Glass1f730c82017-06-18 22:08:59 -0600348 def output_node(self, node):
Simon Glassd570dec2017-06-18 22:08:58 -0600349 """Output the C code for a node
350
351 Args:
352 node: node to output
353 """
Simon Glass1f730c82017-06-18 22:08:59 -0600354 struct_name, _ = self.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))
Simon Glassd570dec2017-06-18 22:08:58 -0600358 for pname, prop in node.props.items():
359 if pname in PROP_IGNORE_LIST or pname[0] == '#':
360 continue
Simon Glass1f730c82017-06-18 22:08:59 -0600361 member_name = conv_name_to_c(prop.name)
362 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
Simon Glassd570dec2017-06-18 22:08:58 -0600363
364 # Special handling for lists
Simon Glass1f730c82017-06-18 22:08:59 -0600365 if isinstance(prop.value, list):
366 self.buf('{')
Simon Glassd570dec2017-06-18 22:08:58 -0600367 vals = []
368 # For phandles, output a reference to the platform data
369 # of the target node.
Simon Glass1f730c82017-06-18 22:08:59 -0600370 if self.is_phandle(prop):
Simon Glassd570dec2017-06-18 22:08:58 -0600371 # Process the list as pairs of (phandle, id)
Simon Glass1f730c82017-06-18 22:08:59 -0600372 value_it = iter(prop.value)
373 for phandle_cell, id_cell in zip(value_it, value_it):
Simon Glassd570dec2017-06-18 22:08:58 -0600374 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
Simon Glass1f730c82017-06-18 22:08:59 -0600375 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))
Simon Glassd570dec2017-06-18 22:08:58 -0600379 else:
380 for val in prop.value:
Simon Glass1f730c82017-06-18 22:08:59 -0600381 vals.append(self.get_value(prop.type, val))
382 self.buf(', '.join(vals))
383 self.buf('}')
Simon Glassd570dec2017-06-18 22:08:58 -0600384 else:
Simon Glass1f730c82017-06-18 22:08:59 -0600385 self.buf(self.get_value(prop.type, prop.value))
386 self.buf(',\n')
387 self.buf('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600388
389 # Add a device declaration
Simon Glass1f730c82017-06-18 22:08:59 -0600390 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))
394 self.buf('};\n')
395 self.buf('\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600396
Simon Glass1f730c82017-06-18 22:08:59 -0600397 self.out(''.join(self.get_buf()))
Simon Glassd570dec2017-06-18 22:08:58 -0600398
Simon Glass1f730c82017-06-18 22:08:59 -0600399 def generate_tables(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600400 """Generate device defintions for the platform data
401
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.
405
406 See the documentation in doc/driver-model/of-plat.txt for more
407 information.
408 """
Simon Glass1f730c82017-06-18 22:08:59 -0600409 self.out('#include <common.h>\n')
410 self.out('#include <dm.h>\n')
411 self.out('#include <dt-structs.h>\n')
412 self.out('\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600413 nodes_to_output = list(self._valid_nodes)
414
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:
Simon Glass1f730c82017-06-18 22:08:59 -0600421 self.output_node(req_node)
Simon Glassd570dec2017-06-18 22:08:58 -0600422 nodes_to_output.remove(req_node)
Simon Glass1f730c82017-06-18 22:08:59 -0600423 self.output_node(node)
Simon Glassd570dec2017-06-18 22:08:58 -0600424 nodes_to_output.remove(node)