xref: /rk3399_rockchip-uboot/tools/dtoc/dtb_platdata.py (revision 7581c01a159ce04101798a39c04b5fa37ac718d2)
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
9import copy
10
11import fdt
12import fdt_util
13
14# When we see these properties we ignore them - i.e. do not create a structure member
15PROP_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
29TYPE_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
36STRUCT_PREFIX = 'dtd_'
37VAL_PREFIX = 'dtv_'
38
39def 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
54def TabTo(num_tabs, str):
55    if len(str) >= num_tabs * 8:
56        return str + ' '
57    return str + '\t' * (num_tabs - len(str) // 8)
58
59class 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)
412