]> Git Repo - J-u-boot.git/blame - tools/dtoc/dtb_platdata.py
dtoc: Move static functions out of the class
[J-u-boot.git] / tools / dtoc / dtb_platdata.py
CommitLineData
7581c01a
SG
1#!/usr/bin/python
2#
3# Copyright (C) 2017 Google, Inc
4# Written by Simon Glass <[email protected]>
5#
6# SPDX-License-Identifier: GPL-2.0+
7#
8
2be282ca
SG
9"""Device tree to platform data class
10
11This supports converting device tree data to C structures definitions and
12static data.
13"""
14
7581c01a 15import copy
2be282ca 16import sys
7581c01a
SG
17
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',
2be282ca 41}
7581c01a
SG
42
43STRUCT_PREFIX = 'dtd_'
44VAL_PREFIX = 'dtv_'
45
2be282ca 46def conv_name_to_c(name):
7581c01a
SG
47 """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 """
2be282ca
SG
54 new = name.replace('@', '_at_')
55 new = new.replace('-', '_')
56 new = new.replace(',', '_')
57 new = new.replace('.', '_')
2be282ca
SG
58 return new
59
60def tab_to(num_tabs, line):
61 """Append tabs to a line of text to reach a tab stop.
62
63 Args:
64 num_tabs: Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
65 line: Line of text to append to
66
67 Returns:
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.
70 """
71 if len(line) >= num_tabs * 8:
72 return line + ' '
73 return line + '\t' * (num_tabs - len(line) // 8)
74
56e0bbe0
SG
75def get_value(ftype, value):
76 """Get a value as a C expression
77
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'
82
83 Args:
84 type: Data type (fdt_util)
85 value: Data value, as a string of bytes
86 """
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:
92 return '"%s"' % value
93 elif ftype == fdt.TYPE_BOOL:
94 return 'true'
95
96def get_compat_name(node):
97 """Get a node's first compatible string as a C identifier
98
99 Args:
100 node: Node object to check
101 Return:
102 Tuple:
103 C identifier for the first compatible string
104 List of C identifiers for all the other compatible strings
105 (possibly empty)
106 """
107 compat = node.props['compatible'].value
108 aliases = []
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]
112
113def is_phandle(prop):
114 """Check if a node contains phandles
115
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.
118
119 Args:
120 prop: Prop object to check
121 Return:
122 True if the object value contains phandles, else False
123 """
124 if prop.name in ['clocks']:
125 return True
126 return False
127
128
2be282ca 129class DtbPlatdata(object):
7581c01a
SG
130 """Provide a means to convert device tree binary data to platform data
131
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.
135
136 Properties:
2be282ca 137 _fdt: Fdt object, referencing the device tree
7581c01a
SG
138 _dtb_fname: Filename of the input device tree binary file
139 _valid_nodes: A list of Node object with compatible strings
e36024b0 140 _include_disabled: true to include nodes marked status = "disabled"
2be282ca 141 _phandle_nodes: A dict of nodes indexed by phandle number (1, 2...)
7581c01a
SG
142 _outfile: The current output file (sys.stdout or a real file)
143 _lines: Stashed list of output lines for outputting in the future
2be282ca 144 _phandle_nodes: A dict of Nodes indexed by phandle (an integer)
7581c01a 145 """
e36024b0 146 def __init__(self, dtb_fname, include_disabled):
2be282ca 147 self._fdt = None
7581c01a
SG
148 self._dtb_fname = dtb_fname
149 self._valid_nodes = None
e36024b0 150 self._include_disabled = include_disabled
2be282ca 151 self._phandle_nodes = {}
7581c01a
SG
152 self._outfile = None
153 self._lines = []
154 self._aliases = {}
155
2be282ca 156 def setup_output(self, fname):
7581c01a
SG
157 """Set up the output destination
158
2be282ca 159 Once this is done, future calls to self.out() will output to this
7581c01a
SG
160 file.
161
162 Args:
163 fname: Filename to send output to, or '-' for stdout
164 """
165 if fname == '-':
166 self._outfile = sys.stdout
167 else:
168 self._outfile = open(fname, 'w')
169
2be282ca 170 def out(self, line):
7581c01a
SG
171 """Output a string to the output file
172
173 Args:
2be282ca 174 line: String to output
7581c01a 175 """
2be282ca 176 self._outfile.write(line)
7581c01a 177
2be282ca 178 def buf(self, line):
7581c01a
SG
179 """Buffer up a string to send later
180
181 Args:
2be282ca 182 line: String to add to our 'buffer' list
7581c01a 183 """
2be282ca 184 self._lines.append(line)
7581c01a 185
2be282ca 186 def get_buf(self):
7581c01a
SG
187 """Get the contents of the output buffer, and clear it
188
189 Returns:
190 The output buffer, which is then cleared for future use
191 """
192 lines = self._lines
193 self._lines = []
194 return lines
195
2be282ca 196 def scan_dtb(self):
7581c01a
SG
197 """Scan the device tree to obtain a tree of notes and properties
198
2be282ca 199 Once this is done, self._fdt.GetRoot() can be called to obtain the
7581c01a
SG
200 device tree root node, and progress from there.
201 """
2be282ca
SG
202 self._fdt = fdt.FdtScan(self._dtb_fname)
203
204 def scan_node(self, root):
205 """Scan a node and subnodes to build a tree of node and phandle info
206
207 This adds each node to self._valid_nodes and each phandle to
208 self._phandle_nodes.
7581c01a 209
2be282ca
SG
210 Args:
211 root: Root node for scan
212 """
7581c01a
SG
213 for node in root.subnodes:
214 if 'compatible' in node.props:
215 status = node.props.get('status')
e36024b0 216 if (not self._include_disabled and not status or
2be282ca 217 status.value != 'disabled'):
7581c01a
SG
218 self._valid_nodes.append(node)
219 phandle_prop = node.props.get('phandle')
220 if phandle_prop:
221 phandle = phandle_prop.GetPhandle()
2be282ca 222 self._phandle_nodes[phandle] = node
7581c01a
SG
223
224 # recurse to handle any subnodes
2be282ca 225 self.scan_node(node)
7581c01a 226
2be282ca 227 def scan_tree(self):
7581c01a
SG
228 """Scan the device tree for useful information
229
230 This fills in the following properties:
2be282ca 231 _phandle_nodes: A dict of Nodes indexed by phandle (an integer)
7581c01a
SG
232 _valid_nodes: A list of nodes we wish to consider include in the
233 platform data
234 """
2be282ca 235 self._phandle_nodes = {}
7581c01a 236 self._valid_nodes = []
2be282ca 237 return self.scan_node(self._fdt.GetRoot())
7581c01a 238
2be282ca 239 def scan_structs(self):
7581c01a
SG
240 """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:
56e0bbe0 252 node_name, _ = get_compat_name(node)
7581c01a
SG
253 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:
56e0bbe0 276 node_name, _ = get_compat_name(node)
7581c01a
SG
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])
281 upto += 1
282
56e0bbe0 283 struct_name, aliases = get_compat_name(node)
7581c01a
SG
284 for alias in aliases:
285 self._aliases[alias] = struct_name
286
287 return structs
288
2be282ca 289 def scan_phandles(self):
7581c01a
SG
290 """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
2be282ca 305 if isinstance(prop.value, list):
56e0bbe0 306 if is_phandle(prop):
7581c01a 307 # Process the list as pairs of (phandle, id)
2be282ca
SG
308 value_it = iter(prop.value)
309 for phandle_cell, _ in zip(value_it, value_it):
7581c01a 310 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
2be282ca 311 target_node = self._phandle_nodes[phandle]
7581c01a
SG
312 node.phandles.add(target_node)
313
314
2be282ca 315 def generate_structs(self, structs):
7581c01a
SG
316 """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 """
2be282ca
SG
322 self.out('#include <stdbool.h>\n')
323 self.out('#include <libfdt.h>\n')
7581c01a
SG
324
325 # Output the struct definition
326 for name in sorted(structs):
2be282ca 327 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
7581c01a
SG
328 for pname in sorted(structs[name]):
329 prop = structs[name][pname]
56e0bbe0 330 if is_phandle(prop):
7581c01a 331 # For phandles, include a reference to the target
2be282ca
SG
332 self.out('\t%s%s[%d]' % (tab_to(2, 'struct phandle_2_cell'),
333 conv_name_to_c(prop.name),
7581c01a
SG
334 len(prop.value) / 2))
335 else:
336 ptype = TYPE_NAMES[prop.type]
2be282ca
SG
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))
341 self.out(';\n')
342 self.out('};\n')
7581c01a
SG
343
344 for alias, struct_name in self._aliases.iteritems():
2be282ca 345 self.out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
7581c01a
SG
346 STRUCT_PREFIX, struct_name))
347
2be282ca 348 def output_node(self, node):
7581c01a
SG
349 """Output the C code for a node
350
351 Args:
352 node: node to output
353 """
56e0bbe0 354 struct_name, _ = get_compat_name(node)
2be282ca
SG
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))
7581c01a
SG
358 for pname, prop in node.props.items():
359 if pname in PROP_IGNORE_LIST or pname[0] == '#':
360 continue
2be282ca
SG
361 member_name = conv_name_to_c(prop.name)
362 self.buf('\t%s= ' % tab_to(3, '.' + member_name))
7581c01a
SG
363
364 # Special handling for lists
2be282ca
SG
365 if isinstance(prop.value, list):
366 self.buf('{')
7581c01a
SG
367 vals = []
368 # For phandles, output a reference to the platform data
369 # of the target node.
56e0bbe0 370 if is_phandle(prop):
7581c01a 371 # Process the list as pairs of (phandle, id)
2be282ca
SG
372 value_it = iter(prop.value)
373 for phandle_cell, id_cell in zip(value_it, value_it):
7581c01a 374 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
2be282ca
SG
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))
7581c01a
SG
379 else:
380 for val in prop.value:
56e0bbe0 381 vals.append(get_value(prop.type, val))
2be282ca
SG
382 self.buf(', '.join(vals))
383 self.buf('}')
7581c01a 384 else:
56e0bbe0 385 self.buf(get_value(prop.type, prop.value))
2be282ca
SG
386 self.buf(',\n')
387 self.buf('};\n')
7581c01a
SG
388
389 # Add a device declaration
2be282ca
SG
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))
394 self.buf('};\n')
395 self.buf('\n')
7581c01a 396
2be282ca 397 self.out(''.join(self.get_buf()))
7581c01a 398
2be282ca 399 def generate_tables(self):
7581c01a
SG
400 """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 """
2be282ca
SG
409 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')
7581c01a
SG
413 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:
2be282ca 421 self.output_node(req_node)
7581c01a 422 nodes_to_output.remove(req_node)
2be282ca 423 self.output_node(node)
7581c01a 424 nodes_to_output.remove(node)
This page took 0.075275 seconds and 4 git commands to generate.