blob: 11050b66f71057821f0a2076fcbba0a4ad6f734e [file] [log] [blame]
Simon Glassbfad22e2016-07-04 11:58:09 -06001#!/usr/bin/python
2#
3# Copyright (C) 2016 Google, Inc
4# Written by Simon Glass <sjg@chromium.org>
5#
6# SPDX-License-Identifier: GPL-2.0+
7#
8
9import copy
10from optparse import OptionError, OptionParser
11import os
Simon Glass8a305142016-07-25 18:59:01 -060012import struct
Simon Glassbfad22e2016-07-04 11:58:09 -060013import sys
14
Simon Glassbfad22e2016-07-04 11:58:09 -060015# Bring in the patman libraries
16our_path = os.path.dirname(os.path.realpath(__file__))
17sys.path.append(os.path.join(our_path, '../patman'))
18
Simon Glassb1a5e262016-07-25 18:59:05 -060019import fdt
Simon Glassb71ef892016-07-25 18:59:02 -060020import fdt_select
21import fdt_util
Simon Glassbfad22e2016-07-04 11:58:09 -060022
Simon Glassbfad22e2016-07-04 11:58:09 -060023# When we see these properties we ignore them - i.e. do not create a structure member
24PROP_IGNORE_LIST = [
25 '#address-cells',
26 '#gpio-cells',
27 '#size-cells',
28 'compatible',
29 'linux,phandle',
30 "status",
31 'phandle',
Simon Glass6522a552016-07-04 11:58:16 -060032 'u-boot,dm-pre-reloc',
Simon Glassbfad22e2016-07-04 11:58:09 -060033]
34
35# C type declarations for the tyues we support
36TYPE_NAMES = {
Simon Glassb1a5e262016-07-25 18:59:05 -060037 fdt.TYPE_INT: 'fdt32_t',
38 fdt.TYPE_BYTE: 'unsigned char',
39 fdt.TYPE_STRING: 'const char *',
40 fdt.TYPE_BOOL: 'bool',
Simon Glassbfad22e2016-07-04 11:58:09 -060041};
42
43STRUCT_PREFIX = 'dtd_'
44VAL_PREFIX = 'dtv_'
45
46def Conv_name_to_c(name):
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 """
54 str = name.replace('@', '_at_')
55 str = str.replace('-', '_')
56 str = str.replace(',', '_')
57 str = str.replace('/', '__')
58 return str
59
60def TabTo(num_tabs, str):
61 if len(str) >= num_tabs * 8:
62 return str + ' '
Paul Burton493dd312016-09-27 16:03:58 +010063 return str + '\t' * (num_tabs - len(str) // 8)
Simon Glassbfad22e2016-07-04 11:58:09 -060064
65class DtbPlatdata:
66 """Provide a means to convert device tree binary data to platform data
67
68 The output of this process is C structures which can be used in space-
69 constrained encvironments where the ~3KB code overhead of device tree
70 code is not affordable.
71
72 Properties:
73 fdt: Fdt object, referencing the device tree
74 _dtb_fname: Filename of the input device tree binary file
75 _valid_nodes: A list of Node object with compatible strings
76 _options: Command-line options
77 _phandle_node: A dict of nodes indexed by phandle number (1, 2...)
78 _outfile: The current output file (sys.stdout or a real file)
79 _lines: Stashed list of output lines for outputting in the future
80 _phandle_node: A dict of Nodes indexed by phandle (an integer)
81 """
82 def __init__(self, dtb_fname, options):
83 self._dtb_fname = dtb_fname
84 self._valid_nodes = None
85 self._options = options
86 self._phandle_node = {}
87 self._outfile = None
88 self._lines = []
89
90 def SetupOutput(self, fname):
91 """Set up the output destination
92
93 Once this is done, future calls to self.Out() will output to this
94 file.
95
96 Args:
97 fname: Filename to send output to, or '-' for stdout
98 """
99 if fname == '-':
100 self._outfile = sys.stdout
101 else:
102 self._outfile = open(fname, 'w')
103
104 def Out(self, str):
105 """Output a string to the output file
106
107 Args:
108 str: String to output
109 """
110 self._outfile.write(str)
111
112 def Buf(self, str):
113 """Buffer up a string to send later
114
115 Args:
116 str: String to add to our 'buffer' list
117 """
118 self._lines.append(str)
119
120 def GetBuf(self):
121 """Get the contents of the output buffer, and clear it
122
123 Returns:
124 The output buffer, which is then cleared for future use
125 """
126 lines = self._lines
127 self._lines = []
128 return lines
129
130 def GetValue(self, type, value):
131 """Get a value as a C expression
132
133 For integers this returns a byte-swapped (little-endian) hex string
134 For bytes this returns a hex string, e.g. 0x12
135 For strings this returns a literal string enclosed in quotes
136 For booleans this return 'true'
137
138 Args:
139 type: Data type (fdt_util)
140 value: Data value, as a string of bytes
141 """
Simon Glassb1a5e262016-07-25 18:59:05 -0600142 if type == fdt.TYPE_INT:
Simon Glassbfad22e2016-07-04 11:58:09 -0600143 return '%#x' % fdt_util.fdt32_to_cpu(value)
Simon Glassb1a5e262016-07-25 18:59:05 -0600144 elif type == fdt.TYPE_BYTE:
Simon Glassbfad22e2016-07-04 11:58:09 -0600145 return '%#x' % ord(value[0])
Simon Glassb1a5e262016-07-25 18:59:05 -0600146 elif type == fdt.TYPE_STRING:
Simon Glassbfad22e2016-07-04 11:58:09 -0600147 return '"%s"' % value
Simon Glassb1a5e262016-07-25 18:59:05 -0600148 elif type == fdt.TYPE_BOOL:
Simon Glassbfad22e2016-07-04 11:58:09 -0600149 return 'true'
150
151 def GetCompatName(self, node):
152 """Get a node's first compatible string as a C identifier
153
154 Args:
155 node: Node object to check
156 Return:
157 C identifier for the first compatible string
158 """
159 compat = node.props['compatible'].value
160 if type(compat) == list:
161 compat = compat[0]
162 return Conv_name_to_c(compat)
163
164 def ScanDtb(self):
165 """Scan the device tree to obtain a tree of notes and properties
166
167 Once this is done, self.fdt.GetRoot() can be called to obtain the
168 device tree root node, and progress from there.
169 """
Simon Glassb71ef892016-07-25 18:59:02 -0600170 self.fdt = fdt_select.FdtScan(self._dtb_fname)
Simon Glassbfad22e2016-07-04 11:58:09 -0600171
172 def ScanTree(self):
173 """Scan the device tree for useful information
174
175 This fills in the following properties:
176 _phandle_node: A dict of Nodes indexed by phandle (an integer)
177 _valid_nodes: A list of nodes we wish to consider include in the
178 platform data
179 """
180 node_list = []
181 self._phandle_node = {}
182 for node in self.fdt.GetRoot().subnodes:
183 if 'compatible' in node.props:
184 status = node.props.get('status')
185 if (not options.include_disabled and not status or
186 status.value != 'disabled'):
187 node_list.append(node)
188 phandle_prop = node.props.get('phandle')
189 if phandle_prop:
190 phandle = phandle_prop.GetPhandle()
191 self._phandle_node[phandle] = node
192
193 self._valid_nodes = node_list
194
195 def IsPhandle(self, prop):
196 """Check if a node contains phandles
197
198 We have no reliable way of detecting whether a node uses a phandle
199 or not. As an interim measure, use a list of known property names.
200
201 Args:
202 prop: Prop object to check
203 Return:
204 True if the object value contains phandles, else False
205 """
206 if prop.name in ['clocks']:
207 return True
208 return False
209
210 def ScanStructs(self):
211 """Scan the device tree building up the C structures we will use.
212
213 Build a dict keyed by C struct name containing a dict of Prop
214 object for each struct field (keyed by property name). Where the
215 same struct appears multiple times, try to use the 'widest'
216 property, i.e. the one with a type which can express all others.
217
218 Once the widest property is determined, all other properties are
219 updated to match that width.
220 """
221 structs = {}
222 for node in self._valid_nodes:
223 node_name = self.GetCompatName(node)
224 fields = {}
225
226 # Get a list of all the valid properties in this node.
Paul Burton0a954482016-09-27 16:03:56 +0100227 for name, prop in node.props.items():
Simon Glassbfad22e2016-07-04 11:58:09 -0600228 if name not in PROP_IGNORE_LIST and name[0] != '#':
229 fields[name] = copy.deepcopy(prop)
230
231 # If we've seen this node_name before, update the existing struct.
232 if node_name in structs:
233 struct = structs[node_name]
Paul Burton0a954482016-09-27 16:03:56 +0100234 for name, prop in fields.items():
Simon Glassbfad22e2016-07-04 11:58:09 -0600235 oldprop = struct.get(name)
236 if oldprop:
237 oldprop.Widen(prop)
238 else:
239 struct[name] = prop
240
241 # Otherwise store this as a new struct.
242 else:
243 structs[node_name] = fields
244
245 upto = 0
246 for node in self._valid_nodes:
247 node_name = self.GetCompatName(node)
248 struct = structs[node_name]
Paul Burton0a954482016-09-27 16:03:56 +0100249 for name, prop in node.props.items():
Simon Glassbfad22e2016-07-04 11:58:09 -0600250 if name not in PROP_IGNORE_LIST and name[0] != '#':
251 prop.Widen(struct[name])
252 upto += 1
253 return structs
254
255 def GenerateStructs(self, structs):
256 """Generate struct defintions for the platform data
257
258 This writes out the body of a header file consisting of structure
259 definitions for node in self._valid_nodes. See the documentation in
260 README.of-plat for more information.
261 """
262 self.Out('#include <stdbool.h>\n')
263 self.Out('#include <libfdt.h>\n')
264
265 # Output the struct definition
266 for name in sorted(structs):
267 self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
268 for pname in sorted(structs[name]):
269 prop = structs[name][pname]
270 if self.IsPhandle(prop):
271 # For phandles, include a reference to the target
272 self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
273 Conv_name_to_c(prop.name),
274 len(prop.value) / 2))
275 else:
276 ptype = TYPE_NAMES[prop.type]
277 self.Out('\t%s%s' % (TabTo(2, ptype),
278 Conv_name_to_c(prop.name)))
279 if type(prop.value) == list:
280 self.Out('[%d]' % len(prop.value))
281 self.Out(';\n')
282 self.Out('};\n')
283
284 def GenerateTables(self):
285 """Generate device defintions for the platform data
286
287 This writes out C platform data initialisation data and
288 U_BOOT_DEVICE() declarations for each valid node. See the
289 documentation in README.of-plat for more information.
290 """
291 self.Out('#include <common.h>\n')
292 self.Out('#include <dm.h>\n')
293 self.Out('#include <dt-structs.h>\n')
294 self.Out('\n')
295 node_txt_list = []
296 for node in self._valid_nodes:
297 struct_name = self.GetCompatName(node)
298 var_name = Conv_name_to_c(node.name)
299 self.Buf('static struct %s%s %s%s = {\n' %
300 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
Paul Burton0a954482016-09-27 16:03:56 +0100301 for pname, prop in node.props.items():
Simon Glassbfad22e2016-07-04 11:58:09 -0600302 if pname in PROP_IGNORE_LIST or pname[0] == '#':
303 continue
304 ptype = TYPE_NAMES[prop.type]
305 member_name = Conv_name_to_c(prop.name)
306 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
307
308 # Special handling for lists
309 if type(prop.value) == list:
310 self.Buf('{')
311 vals = []
312 # For phandles, output a reference to the platform data
313 # of the target node.
314 if self.IsPhandle(prop):
315 # Process the list as pairs of (phandle, id)
316 it = iter(prop.value)
317 for phandle_cell, id_cell in zip(it, it):
318 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
319 id = fdt_util.fdt32_to_cpu(id_cell)
320 target_node = self._phandle_node[phandle]
321 name = Conv_name_to_c(target_node.name)
322 vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
323 else:
324 for val in prop.value:
325 vals.append(self.GetValue(prop.type, val))
326 self.Buf(', '.join(vals))
327 self.Buf('}')
328 else:
329 self.Buf(self.GetValue(prop.type, prop.value))
330 self.Buf(',\n')
331 self.Buf('};\n')
332
333 # Add a device declaration
334 self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
335 self.Buf('\t.name\t\t= "%s",\n' % struct_name)
336 self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
Simon Glassafbf9b82016-07-04 11:58:18 -0600337 self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' %
338 (VAL_PREFIX, var_name))
Simon Glassbfad22e2016-07-04 11:58:09 -0600339 self.Buf('};\n')
340 self.Buf('\n')
341
342 # Output phandle target nodes first, since they may be referenced
343 # by others
344 if 'phandle' in node.props:
345 self.Out(''.join(self.GetBuf()))
346 else:
347 node_txt_list.append(self.GetBuf())
348
349 # Output all the nodes which are not phandle targets themselves, but
350 # may reference them. This avoids the need for forward declarations.
351 for node_txt in node_txt_list:
352 self.Out(''.join(node_txt))
353
354
355if __name__ != "__main__":
356 pass
357
358parser = OptionParser()
359parser.add_option('-d', '--dtb-file', action='store',
360 help='Specify the .dtb input file')
361parser.add_option('--include-disabled', action='store_true',
362 help='Include disabled nodes')
363parser.add_option('-o', '--output', action='store', default='-',
364 help='Select output filename')
365(options, args) = parser.parse_args()
366
367if not args:
368 raise ValueError('Please specify a command: struct, platdata')
369
370plat = DtbPlatdata(options.dtb_file, options)
371plat.ScanDtb()
372plat.ScanTree()
373plat.SetupOutput(options.output)
374structs = plat.ScanStructs()
375
376for cmd in args[0].split(','):
377 if cmd == 'struct':
378 plat.GenerateStructs(structs)
379 elif cmd == 'platdata':
380 plat.GenerateTables()
381 else:
382 raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd)