1#!/usr/bin/python
2# SPDX-License-Identifier: GPL-2.0+
3#
4# Copyright (C) 2016 Google, Inc
5# Written by Simon Glass <sjg@chromium.org>
6#
7
8# Utility functions for reading from a device tree. Once the upstream pylibfdt
9# implementation advances far enough, we should be able to drop these.
10
11import os
12import struct
13import sys
14import tempfile
15
16from patman import command
17from patman import tools
18
19def fdt32_to_cpu(val):
20    """Convert a device tree cell to an integer
21
22    Args:
23        Value to convert (4-character string representing the cell value)
24
25    Return:
26        A native-endian integer value
27    """
28    return struct.unpack('>I', val)[0]
29
30def fdt_cells_to_cpu(val, cells):
31    """Convert one or two cells to a long integer
32
33    Args:
34        Value to convert (array of one or more 4-character strings)
35
36    Return:
37        A native-endian integer value
38    """
39    if not cells:
40        return 0
41    out = int(fdt32_to_cpu(val[0]))
42    if cells == 2:
43        out = out << 32 | fdt32_to_cpu(val[1])
44    return out
45
46def EnsureCompiled(fname, tmpdir=None, capture_stderr=False):
47    """Compile an fdt .dts source file into a .dtb binary blob if needed.
48
49    Args:
50        fname: Filename (if .dts it will be compiled). It not it will be
51            left alone
52        tmpdir: Temporary directory for output files, or None to use the
53            tools-module output directory
54
55    Returns:
56        Filename of resulting .dtb file
57    """
58    _, ext = os.path.splitext(fname)
59    if ext != '.dts':
60        return fname
61
62    if tmpdir:
63        dts_input = os.path.join(tmpdir, 'source.dts')
64        dtb_output = os.path.join(tmpdir, 'source.dtb')
65    else:
66        dts_input = tools.GetOutputFilename('source.dts')
67        dtb_output = tools.GetOutputFilename('source.dtb')
68
69    search_paths = [os.path.join(os.getcwd(), 'include')]
70    root, _ = os.path.splitext(fname)
71    cc, args = tools.GetTargetCompileTool('cc')
72    args += ['-E', '-P', '-x', 'assembler-with-cpp', '-D__ASSEMBLY__']
73    args += ['-Ulinux']
74    for path in search_paths:
75        args.extend(['-I', path])
76    args += ['-o', dts_input, fname]
77    command.Run(cc, *args)
78
79    # If we don't have a directory, put it in the tools tempdir
80    search_list = []
81    for path in search_paths:
82        search_list.extend(['-i', path])
83    dtc, args = tools.GetTargetCompileTool('dtc')
84    args += ['-I', 'dts', '-o', dtb_output, '-O', 'dtb',
85            '-W', 'no-unit_address_vs_reg']
86    args.extend(search_list)
87    args.append(dts_input)
88    command.Run(dtc, *args, capture_stderr=capture_stderr)
89    return dtb_output
90
91def GetInt(node, propname, default=None):
92    """Get an integer from a property
93
94    Args:
95        node: Node object to read from
96        propname: property name to read
97        default: Default value to use if the node/property do not exist
98
99    Returns:
100        Integer value read, or default if none
101    """
102    prop = node.props.get(propname)
103    if not prop:
104        return default
105    if isinstance(prop.value, list):
106        raise ValueError("Node '%s' property '%s' has list value: expecting "
107                         "a single integer" % (node.name, propname))
108    value = fdt32_to_cpu(prop.value)
109    return value
110
111def GetString(node, propname, default=None):
112    """Get a string from a property
113
114    Args:
115        node: Node object to read from
116        propname: property name to read
117        default: Default value to use if the node/property do not exist
118
119    Returns:
120        String value read, or default if none
121    """
122    prop = node.props.get(propname)
123    if not prop:
124        return default
125    value = prop.value
126    if isinstance(value, list):
127        raise ValueError("Node '%s' property '%s' has list value: expecting "
128                         "a single string" % (node.name, propname))
129    return value
130
131def GetBool(node, propname, default=False):
132    """Get an boolean from a property
133
134    Args:
135        node: Node object to read from
136        propname: property name to read
137        default: Default value to use if the node/property do not exist
138
139    Returns:
140        Boolean value read, or default if none (if you set this to True the
141            function will always return True)
142    """
143    if propname in node.props:
144        return True
145    return default
146
147def GetByte(node, propname, default=None):
148    """Get an byte from a property
149
150    Args:
151        node: Node object to read from
152        propname: property name to read
153        default: Default value to use if the node/property do not exist
154
155    Returns:
156        Byte value read, or default if none
157    """
158    prop = node.props.get(propname)
159    if not prop:
160        return default
161    value = prop.value
162    if isinstance(value, list):
163        raise ValueError("Node '%s' property '%s' has list value: expecting "
164                         "a single byte" % (node.name, propname))
165    if len(value) != 1:
166        raise ValueError("Node '%s' property '%s' has length %d, expecting %d" %
167                         (node.name, propname, len(value), 1))
168    return ord(value[0])
169
170def GetPhandleList(node, propname):
171    """Get a list of phandles from a property
172
173    Args:
174        node: Node object to read from
175        propname: property name to read
176
177    Returns:
178        List of phandles read, each an integer
179    """
180    prop = node.props.get(propname)
181    if not prop:
182        return None
183    value = prop.value
184    if not isinstance(value, list):
185        value = [value]
186    return [fdt32_to_cpu(v) for v in value]
187
188def GetDatatype(node, propname, datatype):
189    """Get a value of a given type from a property
190
191    Args:
192        node: Node object to read from
193        propname: property name to read
194        datatype: Type to read (str or int)
195
196    Returns:
197        value read, or None if none
198
199    Raises:
200        ValueError if datatype is not str or int
201    """
202    if datatype == str:
203        return GetString(node, propname)
204    elif datatype == int:
205        return GetInt(node, propname)
206    raise ValueError("fdt_util internal error: Unknown data type '%s'" %
207                     datatype)
208