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 u_boot_pylib import command 17from u_boot_pylib 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 fdt64_to_cpu(val): 31 """Convert a device tree cell to an integer 32 33 Args: 34 val (list): Value to convert (list of 2 4-character strings representing 35 the cell value) 36 37 Return: 38 int: A native-endian integer value 39 """ 40 return fdt32_to_cpu(val[0]) << 32 | fdt32_to_cpu(val[1]) 41 42def fdt_cells_to_cpu(val, cells): 43 """Convert one or two cells to a long integer 44 45 Args: 46 Value to convert (array of one or more 4-character strings) 47 48 Return: 49 A native-endian integer value 50 """ 51 if not cells: 52 return 0 53 out = int(fdt32_to_cpu(val[0])) 54 if cells == 2: 55 out = out << 32 | fdt32_to_cpu(val[1]) 56 return out 57 58def EnsureCompiled(fname, tmpdir=None, capture_stderr=False, indir=None): 59 """Compile an fdt .dts source file into a .dtb binary blob if needed. 60 61 Args: 62 fname: Filename (if .dts it will be compiled). It not it will be 63 left alone 64 tmpdir: Temporary directory for output files, or None to use the 65 tools-module output directory 66 indir: List of directories where input files can be found 67 68 Returns: 69 Filename of resulting .dtb file 70 """ 71 _, ext = os.path.splitext(fname) 72 if ext != '.dts': 73 return fname 74 75 if tmpdir: 76 dts_input = os.path.join(tmpdir, 'source.dts') 77 dtb_output = os.path.join(tmpdir, 'source.dtb') 78 else: 79 dts_input = tools.get_output_filename('source.dts') 80 dtb_output = tools.get_output_filename('source.dtb') 81 82 search_paths = [os.path.join(os.getcwd(), 'include')] 83 if indir is not None: 84 search_paths += indir 85 root, _ = os.path.splitext(fname) 86 cc, args = tools.get_target_compile_tool('cc') 87 args += ['-E', '-P', '-x', 'assembler-with-cpp', '-D__ASSEMBLY__'] 88 args += ['-Ulinux'] 89 for path in search_paths: 90 args.extend(['-I', path]) 91 args += ['-o', dts_input, fname] 92 command.run(cc, *args) 93 94 # If we don't have a directory, put it in the tools tempdir 95 search_list = [] 96 for path in search_paths: 97 search_list.extend(['-i', path]) 98 dtc, args = tools.get_target_compile_tool('dtc') 99 args += ['-I', 'dts', '-o', dtb_output, '-O', 'dtb', 100 '-W', 'no-unit_address_vs_reg'] 101 args.extend(search_list) 102 args.append(dts_input) 103 command.run(dtc, *args, capture_stderr=capture_stderr) 104 return dtb_output 105 106def GetInt(node, propname, default=None): 107 """Get an integer from a property 108 109 Args: 110 node: Node object to read from 111 propname: property name to read 112 default: Default value to use if the node/property do not exist 113 114 Returns: 115 Integer value read, or default if none 116 """ 117 prop = node.props.get(propname) 118 if not prop: 119 return default 120 if isinstance(prop.value, list): 121 raise ValueError("Node '%s' property '%s' has list value: expecting " 122 "a single integer" % (node.name, propname)) 123 value = fdt32_to_cpu(prop.value) 124 return value 125 126def GetInt64(node, propname, default=None): 127 """Get a 64-bit integer from a property 128 129 Args: 130 node (Node): Node object to read from 131 propname (str): property name to read 132 default (int): Default value to use if the node/property do not exist 133 134 Returns: 135 int: value read, or default if none 136 137 Raises: 138 ValueError: Property is not of the correct size 139 """ 140 prop = node.props.get(propname) 141 if not prop: 142 return default 143 if not isinstance(prop.value, list) or len(prop.value) != 2: 144 raise ValueError("Node '%s' property '%s' should be a list with 2 items for 64-bit values" % 145 (node.name, propname)) 146 value = fdt64_to_cpu(prop.value) 147 return value 148 149def GetString(node, propname, default=None): 150 """Get a string from a property 151 152 Args: 153 node: Node object to read from 154 propname: property name to read 155 default: Default value to use if the node/property do not exist 156 157 Returns: 158 String value read, or default if none 159 """ 160 prop = node.props.get(propname) 161 if not prop: 162 return default 163 value = prop.value 164 if not prop.bytes: 165 return '' 166 if isinstance(value, list): 167 raise ValueError("Node '%s' property '%s' has list value: expecting " 168 "a single string" % (node.name, propname)) 169 return value 170 171def GetStringList(node, propname, default=None): 172 """Get a string list from a property 173 174 Args: 175 node (Node): Node object to read from 176 propname (str): property name to read 177 default (list of str): Default value to use if the node/property do not 178 exist, or None 179 180 Returns: 181 String value read, or default if none 182 """ 183 prop = node.props.get(propname) 184 if not prop: 185 return default 186 value = prop.value 187 if not prop.bytes: 188 return [] 189 if not isinstance(value, list): 190 strval = GetString(node, propname) 191 return [strval] 192 return value 193 194def GetArgs(node, propname): 195 prop = node.props.get(propname) 196 if not prop: 197 raise ValueError(f"Node '{node.path}': Expected property '{propname}'") 198 if prop.bytes: 199 value = GetStringList(node, propname) 200 else: 201 value = [] 202 if not value: 203 args = [] 204 elif len(value) == 1: 205 args = value[0].split() 206 else: 207 args = value 208 return args 209 210def GetBool(node, propname, default=False): 211 """Get an boolean from a property 212 213 Args: 214 node: Node object to read from 215 propname: property name to read 216 default: Default value to use if the node/property do not exist 217 218 Returns: 219 Boolean value read, or default if none (if you set this to True the 220 function will always return True) 221 """ 222 if propname in node.props: 223 return True 224 return default 225 226def GetByte(node, propname, default=None): 227 """Get an byte from a property 228 229 Args: 230 node: Node object to read from 231 propname: property name to read 232 default: Default value to use if the node/property do not exist 233 234 Returns: 235 Byte value read, or default if none 236 """ 237 prop = node.props.get(propname) 238 if not prop: 239 return default 240 value = prop.value 241 if isinstance(value, list): 242 raise ValueError("Node '%s' property '%s' has list value: expecting " 243 "a single byte" % (node.name, propname)) 244 if len(value) != 1: 245 raise ValueError("Node '%s' property '%s' has length %d, expecting %d" % 246 (node.name, propname, len(value), 1)) 247 return ord(value[0]) 248 249def GetBytes(node, propname, size, default=None): 250 """Get a set of bytes from a property 251 252 Args: 253 node (Node): Node object to read from 254 propname (str): property name to read 255 size (int): Number of bytes to expect 256 default (bytes): Default value or None 257 258 Returns: 259 bytes: Bytes value read, or default if none 260 """ 261 prop = node.props.get(propname) 262 if not prop: 263 return default 264 if len(prop.bytes) != size: 265 raise ValueError("Node '%s' property '%s' has length %d, expecting %d" % 266 (node.name, propname, len(prop.bytes), size)) 267 return prop.bytes 268 269def GetPhandleList(node, propname): 270 """Get a list of phandles from a property 271 272 Args: 273 node: Node object to read from 274 propname: property name to read 275 276 Returns: 277 List of phandles read, each an integer 278 """ 279 prop = node.props.get(propname) 280 if not prop: 281 return None 282 value = prop.value 283 if not isinstance(value, list): 284 value = [value] 285 return [fdt32_to_cpu(v) for v in value] 286 287def GetPhandleNameOffset(node, propname): 288 """Get a <&phandle>, "string", <offset> value from a property 289 290 Args: 291 node: Node object to read from 292 propname: property name to read 293 294 Returns: 295 tuple: 296 Node object 297 str 298 int 299 or None if the property does not exist 300 """ 301 prop = node.props.get(propname) 302 if not prop: 303 return None 304 value = prop.bytes 305 phandle = fdt32_to_cpu(value[:4]) 306 node = node.GetFdt().LookupPhandle(phandle) 307 name = '' 308 for byte in value[4:]: 309 if not byte: 310 break 311 name += chr(byte) 312 val = fdt32_to_cpu(value[4 + len(name) + 1:]) 313 return node, name, val 314 315def GetDatatype(node, propname, datatype): 316 """Get a value of a given type from a property 317 318 Args: 319 node: Node object to read from 320 propname: property name to read 321 datatype: Type to read (str or int) 322 323 Returns: 324 value read, or None if none 325 326 Raises: 327 ValueError if datatype is not str or int 328 """ 329 if datatype == str: 330 return GetString(node, propname) 331 elif datatype == int: 332 return GetInt(node, propname) 333 raise ValueError("fdt_util internal error: Unknown data type '%s'" % 334 datatype) 335