]> git.ipfire.org Git - people/ms/u-boot.git/blob - tools/dtoc/dtb_platdata.py
dtoc: Split out the main class into its own file
[people/ms/u-boot.git] / tools / dtoc / dtb_platdata.py
1 #!/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
9 import copy
10
11 import fdt
12 import fdt_util
13
14 # When we see these properties we ignore them - i.e. do not create a structure member
15 PROP_IGNORE_LIST = [
16 '#address-cells',
17 '#gpio-cells',
18 '#size-cells',
19 'compatible',
20 'linux,phandle',
21 "status",
22 'phandle',
23 'u-boot,dm-pre-reloc',
24 'u-boot,dm-tpl',
25 'u-boot,dm-spl',
26 ]
27
28 # C type declarations for the tyues we support
29 TYPE_NAMES = {
30 fdt.TYPE_INT: 'fdt32_t',
31 fdt.TYPE_BYTE: 'unsigned char',
32 fdt.TYPE_STRING: 'const char *',
33 fdt.TYPE_BOOL: 'bool',
34 };
35
36 STRUCT_PREFIX = 'dtd_'
37 VAL_PREFIX = 'dtv_'
38
39 def Conv_name_to_c(name):
40 """Convert a device-tree name to a C identifier
41
42 Args:
43 name: Name to convert
44 Return:
45 String containing the C version of this name
46 """
47 str = name.replace('@', '_at_')
48 str = str.replace('-', '_')
49 str = str.replace(',', '_')
50 str = str.replace('.', '_')
51 str = str.replace('/', '__')
52 return str
53
54 def TabTo(num_tabs, str):
55 if len(str) >= num_tabs * 8:
56 return str + ' '
57 return str + '\t' * (num_tabs - len(str) // 8)
58
59 class DtbPlatdata:
60 """Provide a means to convert device tree binary data to platform data
61
62 The output of this process is C structures which can be used in space-
63 constrained encvironments where the ~3KB code overhead of device tree
64 code is not affordable.
65
66 Properties:
67 fdt: Fdt object, referencing the device tree
68 _dtb_fname: Filename of the input device tree binary file
69 _valid_nodes: A list of Node object with compatible strings
70 _options: Command-line options
71 _phandle_node: A dict of nodes indexed by phandle number (1, 2...)
72 _outfile: The current output file (sys.stdout or a real file)
73 _lines: Stashed list of output lines for outputting in the future
74 _phandle_node: A dict of Nodes indexed by phandle (an integer)
75 """
76 def __init__(self, dtb_fname, options):
77 self._dtb_fname = dtb_fname
78 self._valid_nodes = None
79 self._options = options
80 self._phandle_node = {}
81 self._outfile = None
82 self._lines = []
83 self._aliases = {}
84
85 def SetupOutput(self, fname):
86 """Set up the output destination
87
88 Once this is done, future calls to self.Out() will output to this
89 file.
90
91 Args:
92 fname: Filename to send output to, or '-' for stdout
93 """
94 if fname == '-':
95 self._outfile = sys.stdout
96 else:
97 self._outfile = open(fname, 'w')
98
99 def Out(self, str):
100 """Output a string to the output file
101
102 Args:
103 str: String to output
104 """
105 self._outfile.write(str)
106
107 def Buf(self, str):
108 """Buffer up a string to send later
109
110 Args:
111 str: String to add to our 'buffer' list
112 """
113 self._lines.append(str)
114
115 def GetBuf(self):
116 """Get the contents of the output buffer, and clear it
117
118 Returns:
119 The output buffer, which is then cleared for future use
120 """
121 lines = self._lines
122 self._lines = []
123 return lines
124
125 def GetValue(self, type, value):
126 """Get a value as a C expression
127
128 For integers this returns a byte-swapped (little-endian) hex string
129 For bytes this returns a hex string, e.g. 0x12
130 For strings this returns a literal string enclosed in quotes
131 For booleans this return 'true'
132
133 Args:
134 type: Data type (fdt_util)
135 value: Data value, as a string of bytes
136 """
137 if type == fdt.TYPE_INT:
138 return '%#x' % fdt_util.fdt32_to_cpu(value)
139 elif type == fdt.TYPE_BYTE:
140 return '%#x' % ord(value[0])
141 elif type == fdt.TYPE_STRING:
142 return '"%s"' % value
143 elif type == fdt.TYPE_BOOL:
144 return 'true'
145
146 def GetCompatName(self, node):
147 """Get a node's first compatible string as a C identifier
148
149 Args:
150 node: Node object to check
151 Return:
152 C identifier for the first compatible string
153 """
154 compat = node.props['compatible'].value
155 aliases = []
156 if type(compat) == list:
157 compat, aliases = compat[0], compat[1:]
158 return Conv_name_to_c(compat), [Conv_name_to_c(a) for a in aliases]
159
160 def ScanDtb(self):
161 """Scan the device tree to obtain a tree of notes and properties
162
163 Once this is done, self.fdt.GetRoot() can be called to obtain the
164 device tree root node, and progress from there.
165 """
166 self.fdt = fdt.FdtScan(self._dtb_fname)
167
168 def ScanNode(self, root):
169 for node in root.subnodes:
170 if 'compatible' in node.props:
171 status = node.props.get('status')
172 if (not self._options.include_disabled and not status or
173 status.value != 'disabled'):
174 self._valid_nodes.append(node)
175 phandle_prop = node.props.get('phandle')
176 if phandle_prop:
177 phandle = phandle_prop.GetPhandle()
178 self._phandle_node[phandle] = node
179
180 # recurse to handle any subnodes
181 self.ScanNode(node);
182
183 def ScanTree(self):
184 """Scan the device tree for useful information
185
186 This fills in the following properties:
187 _phandle_node: A dict of Nodes indexed by phandle (an integer)
188 _valid_nodes: A list of nodes we wish to consider include in the
189 platform data
190 """
191 self._phandle_node = {}
192 self._valid_nodes = []
193 return self.ScanNode(self.fdt.GetRoot());
194
195 for node in self.fdt.GetRoot().subnodes:
196 if 'compatible' in node.props:
197 status = node.props.get('status')
198 if (not self._options.include_disabled and not status or
199 status.value != 'disabled'):
200 node_list.append(node)
201 phandle_prop = node.props.get('phandle')
202 if phandle_prop:
203 phandle = phandle_prop.GetPhandle()
204 self._phandle_node[phandle] = node
205
206 self._valid_nodes = node_list
207
208 def IsPhandle(self, prop):
209 """Check if a node contains phandles
210
211 We have no reliable way of detecting whether a node uses a phandle
212 or not. As an interim measure, use a list of known property names.
213
214 Args:
215 prop: Prop object to check
216 Return:
217 True if the object value contains phandles, else False
218 """
219 if prop.name in ['clocks']:
220 return True
221 return False
222
223 def ScanStructs(self):
224 """Scan the device tree building up the C structures we will use.
225
226 Build a dict keyed by C struct name containing a dict of Prop
227 object for each struct field (keyed by property name). Where the
228 same struct appears multiple times, try to use the 'widest'
229 property, i.e. the one with a type which can express all others.
230
231 Once the widest property is determined, all other properties are
232 updated to match that width.
233 """
234 structs = {}
235 for node in self._valid_nodes:
236 node_name, _ = self.GetCompatName(node)
237 fields = {}
238
239 # Get a list of all the valid properties in this node.
240 for name, prop in node.props.items():
241 if name not in PROP_IGNORE_LIST and name[0] != '#':
242 fields[name] = copy.deepcopy(prop)
243
244 # If we've seen this node_name before, update the existing struct.
245 if node_name in structs:
246 struct = structs[node_name]
247 for name, prop in fields.items():
248 oldprop = struct.get(name)
249 if oldprop:
250 oldprop.Widen(prop)
251 else:
252 struct[name] = prop
253
254 # Otherwise store this as a new struct.
255 else:
256 structs[node_name] = fields
257
258 upto = 0
259 for node in self._valid_nodes:
260 node_name, _ = self.GetCompatName(node)
261 struct = structs[node_name]
262 for name, prop in node.props.items():
263 if name not in PROP_IGNORE_LIST and name[0] != '#':
264 prop.Widen(struct[name])
265 upto += 1
266
267 struct_name, aliases = self.GetCompatName(node)
268 for alias in aliases:
269 self._aliases[alias] = struct_name
270
271 return structs
272
273 def ScanPhandles(self):
274 """Figure out what phandles each node uses
275
276 We need to be careful when outputing nodes that use phandles since
277 they must come after the declaration of the phandles in the C file.
278 Otherwise we get a compiler error since the phandle struct is not yet
279 declared.
280
281 This function adds to each node a list of phandle nodes that the node
282 depends on. This allows us to output things in the right order.
283 """
284 for node in self._valid_nodes:
285 node.phandles = set()
286 for pname, prop in node.props.items():
287 if pname in PROP_IGNORE_LIST or pname[0] == '#':
288 continue
289 if type(prop.value) == list:
290 if self.IsPhandle(prop):
291 # Process the list as pairs of (phandle, id)
292 it = iter(prop.value)
293 for phandle_cell, id_cell in zip(it, it):
294 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
295 id = fdt_util.fdt32_to_cpu(id_cell)
296 target_node = self._phandle_node[phandle]
297 node.phandles.add(target_node)
298
299
300 def GenerateStructs(self, structs):
301 """Generate struct defintions for the platform data
302
303 This writes out the body of a header file consisting of structure
304 definitions for node in self._valid_nodes. See the documentation in
305 README.of-plat for more information.
306 """
307 self.Out('#include <stdbool.h>\n')
308 self.Out('#include <libfdt.h>\n')
309
310 # Output the struct definition
311 for name in sorted(structs):
312 self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
313 for pname in sorted(structs[name]):
314 prop = structs[name][pname]
315 if self.IsPhandle(prop):
316 # For phandles, include a reference to the target
317 self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
318 Conv_name_to_c(prop.name),
319 len(prop.value) / 2))
320 else:
321 ptype = TYPE_NAMES[prop.type]
322 self.Out('\t%s%s' % (TabTo(2, ptype),
323 Conv_name_to_c(prop.name)))
324 if type(prop.value) == list:
325 self.Out('[%d]' % len(prop.value))
326 self.Out(';\n')
327 self.Out('};\n')
328
329 for alias, struct_name in self._aliases.iteritems():
330 self.Out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias,
331 STRUCT_PREFIX, struct_name))
332
333 def OutputNode(self, node):
334 """Output the C code for a node
335
336 Args:
337 node: node to output
338 """
339 struct_name, _ = self.GetCompatName(node)
340 var_name = Conv_name_to_c(node.name)
341 self.Buf('static struct %s%s %s%s = {\n' %
342 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
343 for pname, prop in node.props.items():
344 if pname in PROP_IGNORE_LIST or pname[0] == '#':
345 continue
346 ptype = TYPE_NAMES[prop.type]
347 member_name = Conv_name_to_c(prop.name)
348 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
349
350 # Special handling for lists
351 if type(prop.value) == list:
352 self.Buf('{')
353 vals = []
354 # For phandles, output a reference to the platform data
355 # of the target node.
356 if self.IsPhandle(prop):
357 # Process the list as pairs of (phandle, id)
358 it = iter(prop.value)
359 for phandle_cell, id_cell in zip(it, it):
360 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
361 id = fdt_util.fdt32_to_cpu(id_cell)
362 target_node = self._phandle_node[phandle]
363 name = Conv_name_to_c(target_node.name)
364 vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
365 else:
366 for val in prop.value:
367 vals.append(self.GetValue(prop.type, val))
368 self.Buf(', '.join(vals))
369 self.Buf('}')
370 else:
371 self.Buf(self.GetValue(prop.type, prop.value))
372 self.Buf(',\n')
373 self.Buf('};\n')
374
375 # Add a device declaration
376 self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
377 self.Buf('\t.name\t\t= "%s",\n' % struct_name)
378 self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
379 self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' %
380 (VAL_PREFIX, var_name))
381 self.Buf('};\n')
382 self.Buf('\n')
383
384 self.Out(''.join(self.GetBuf()))
385
386 def GenerateTables(self):
387 """Generate device defintions for the platform data
388
389 This writes out C platform data initialisation data and
390 U_BOOT_DEVICE() declarations for each valid node. Where a node has
391 multiple compatible strings, a #define is used to make them equivalent.
392
393 See the documentation in doc/driver-model/of-plat.txt for more
394 information.
395 """
396 self.Out('#include <common.h>\n')
397 self.Out('#include <dm.h>\n')
398 self.Out('#include <dt-structs.h>\n')
399 self.Out('\n')
400 nodes_to_output = list(self._valid_nodes)
401
402 # Keep outputing nodes until there is none left
403 while nodes_to_output:
404 node = nodes_to_output[0]
405 # Output all the node's dependencies first
406 for req_node in node.phandles:
407 if req_node in nodes_to_output:
408 self.OutputNode(req_node)
409 nodes_to_output.remove(req_node)
410 self.OutputNode(node)
411 nodes_to_output.remove(node)