]>
Commit | Line | Data |
---|---|---|
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 | ||
11 | This supports converting device tree data to C structures definitions and | |
12 | static data. | |
13 | """ | |
14 | ||
7581c01a | 15 | import copy |
2be282ca | 16 | import sys |
7581c01a SG |
17 | |
18 | import fdt | |
19 | import fdt_util | |
20 | ||
21 | # When we see these properties we ignore them - i.e. do not create a structure member | |
22 | PROP_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 | |
36 | TYPE_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 | |
43 | STRUCT_PREFIX = 'dtd_' | |
44 | VAL_PREFIX = 'dtv_' | |
45 | ||
2be282ca | 46 | def 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 | ||
60 | def 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 |
75 | def 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 | ||
96 | def 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 | ||
113 | def 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 | 129 | class 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) |