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