| #!/usr/bin/python |
| # SPDX-License-Identifier: GPL-2.0+ |
| # |
| # Copyright (C) 2016 Google, Inc |
| # Written by Simon Glass <sjg@chromium.org> |
| # |
| |
| # Utility functions for reading from a device tree. Once the upstream pylibfdt |
| # implementation advances far enough, we should be able to drop these. |
| |
| import os |
| import struct |
| import sys |
| import tempfile |
| |
| from u_boot_pylib import command |
| from u_boot_pylib import tools |
| |
| def fdt32_to_cpu(val): |
| """Convert a device tree cell to an integer |
| |
| Args: |
| Value to convert (4-character string representing the cell value) |
| |
| Return: |
| A native-endian integer value |
| """ |
| return struct.unpack('>I', val)[0] |
| |
| def fdt64_to_cpu(val): |
| """Convert a device tree cell to an integer |
| |
| Args: |
| val (list): Value to convert (list of 2 4-character strings representing |
| the cell value) |
| |
| Return: |
| int: A native-endian integer value |
| """ |
| return fdt32_to_cpu(val[0]) << 32 | fdt32_to_cpu(val[1]) |
| |
| def fdt_cells_to_cpu(val, cells): |
| """Convert one or two cells to a long integer |
| |
| Args: |
| Value to convert (array of one or more 4-character strings) |
| |
| Return: |
| A native-endian integer value |
| """ |
| if not cells: |
| return 0 |
| out = int(fdt32_to_cpu(val[0])) |
| if cells == 2: |
| out = out << 32 | fdt32_to_cpu(val[1]) |
| return out |
| |
| def EnsureCompiled(fname, tmpdir=None, capture_stderr=False): |
| """Compile an fdt .dts source file into a .dtb binary blob if needed. |
| |
| Args: |
| fname: Filename (if .dts it will be compiled). It not it will be |
| left alone |
| tmpdir: Temporary directory for output files, or None to use the |
| tools-module output directory |
| |
| Returns: |
| Filename of resulting .dtb file |
| """ |
| _, ext = os.path.splitext(fname) |
| if ext != '.dts': |
| return fname |
| |
| if tmpdir: |
| dts_input = os.path.join(tmpdir, 'source.dts') |
| dtb_output = os.path.join(tmpdir, 'source.dtb') |
| else: |
| dts_input = tools.get_output_filename('source.dts') |
| dtb_output = tools.get_output_filename('source.dtb') |
| |
| search_paths = [os.path.join(os.getcwd(), 'include')] |
| root, _ = os.path.splitext(fname) |
| cc, args = tools.get_target_compile_tool('cc') |
| args += ['-E', '-P', '-x', 'assembler-with-cpp', '-D__ASSEMBLY__'] |
| args += ['-Ulinux'] |
| for path in search_paths: |
| args.extend(['-I', path]) |
| args += ['-o', dts_input, fname] |
| command.run(cc, *args) |
| |
| # If we don't have a directory, put it in the tools tempdir |
| search_list = [] |
| for path in search_paths: |
| search_list.extend(['-i', path]) |
| dtc, args = tools.get_target_compile_tool('dtc') |
| args += ['-I', 'dts', '-o', dtb_output, '-O', 'dtb', |
| '-W', 'no-unit_address_vs_reg'] |
| args.extend(search_list) |
| args.append(dts_input) |
| command.run(dtc, *args, capture_stderr=capture_stderr) |
| return dtb_output |
| |
| def GetInt(node, propname, default=None): |
| """Get an integer from a property |
| |
| Args: |
| node: Node object to read from |
| propname: property name to read |
| default: Default value to use if the node/property do not exist |
| |
| Returns: |
| Integer value read, or default if none |
| """ |
| prop = node.props.get(propname) |
| if not prop: |
| return default |
| if isinstance(prop.value, list): |
| raise ValueError("Node '%s' property '%s' has list value: expecting " |
| "a single integer" % (node.name, propname)) |
| value = fdt32_to_cpu(prop.value) |
| return value |
| |
| def GetInt64(node, propname, default=None): |
| """Get a 64-bit integer from a property |
| |
| Args: |
| node (Node): Node object to read from |
| propname (str): property name to read |
| default (int): Default value to use if the node/property do not exist |
| |
| Returns: |
| int: value read, or default if none |
| |
| Raises: |
| ValueError: Property is not of the correct size |
| """ |
| prop = node.props.get(propname) |
| if not prop: |
| return default |
| if not isinstance(prop.value, list) or len(prop.value) != 2: |
| raise ValueError("Node '%s' property '%s' should be a list with 2 items for 64-bit values" % |
| (node.name, propname)) |
| value = fdt64_to_cpu(prop.value) |
| return value |
| |
| def GetString(node, propname, default=None): |
| """Get a string from a property |
| |
| Args: |
| node: Node object to read from |
| propname: property name to read |
| default: Default value to use if the node/property do not exist |
| |
| Returns: |
| String value read, or default if none |
| """ |
| prop = node.props.get(propname) |
| if not prop: |
| return default |
| value = prop.value |
| if not prop.bytes: |
| return '' |
| if isinstance(value, list): |
| raise ValueError("Node '%s' property '%s' has list value: expecting " |
| "a single string" % (node.name, propname)) |
| return value |
| |
| def GetStringList(node, propname, default=None): |
| """Get a string list from a property |
| |
| Args: |
| node (Node): Node object to read from |
| propname (str): property name to read |
| default (list of str): Default value to use if the node/property do not |
| exist, or None |
| |
| Returns: |
| String value read, or default if none |
| """ |
| prop = node.props.get(propname) |
| if not prop: |
| return default |
| value = prop.value |
| if not prop.bytes: |
| return [] |
| if not isinstance(value, list): |
| strval = GetString(node, propname) |
| return [strval] |
| return value |
| |
| def GetArgs(node, propname): |
| prop = node.props.get(propname) |
| if not prop: |
| raise ValueError(f"Node '{node.path}': Expected property '{propname}'") |
| if prop.bytes: |
| value = GetStringList(node, propname) |
| else: |
| value = [] |
| if not value: |
| args = [] |
| elif len(value) == 1: |
| args = value[0].split() |
| else: |
| args = value |
| return args |
| |
| def GetBool(node, propname, default=False): |
| """Get an boolean from a property |
| |
| Args: |
| node: Node object to read from |
| propname: property name to read |
| default: Default value to use if the node/property do not exist |
| |
| Returns: |
| Boolean value read, or default if none (if you set this to True the |
| function will always return True) |
| """ |
| if propname in node.props: |
| return True |
| return default |
| |
| def GetByte(node, propname, default=None): |
| """Get an byte from a property |
| |
| Args: |
| node: Node object to read from |
| propname: property name to read |
| default: Default value to use if the node/property do not exist |
| |
| Returns: |
| Byte value read, or default if none |
| """ |
| prop = node.props.get(propname) |
| if not prop: |
| return default |
| value = prop.value |
| if isinstance(value, list): |
| raise ValueError("Node '%s' property '%s' has list value: expecting " |
| "a single byte" % (node.name, propname)) |
| if len(value) != 1: |
| raise ValueError("Node '%s' property '%s' has length %d, expecting %d" % |
| (node.name, propname, len(value), 1)) |
| return ord(value[0]) |
| |
| def GetBytes(node, propname, size, default=None): |
| """Get a set of bytes from a property |
| |
| Args: |
| node (Node): Node object to read from |
| propname (str): property name to read |
| size (int): Number of bytes to expect |
| default (bytes): Default value or None |
| |
| Returns: |
| bytes: Bytes value read, or default if none |
| """ |
| prop = node.props.get(propname) |
| if not prop: |
| return default |
| if len(prop.bytes) != size: |
| raise ValueError("Node '%s' property '%s' has length %d, expecting %d" % |
| (node.name, propname, len(prop.bytes), size)) |
| return prop.bytes |
| |
| def GetPhandleList(node, propname): |
| """Get a list of phandles from a property |
| |
| Args: |
| node: Node object to read from |
| propname: property name to read |
| |
| Returns: |
| List of phandles read, each an integer |
| """ |
| prop = node.props.get(propname) |
| if not prop: |
| return None |
| value = prop.value |
| if not isinstance(value, list): |
| value = [value] |
| return [fdt32_to_cpu(v) for v in value] |
| |
| def GetPhandleNameOffset(node, propname): |
| """Get a <&phandle>, "string", <offset> value from a property |
| |
| Args: |
| node: Node object to read from |
| propname: property name to read |
| |
| Returns: |
| tuple: |
| Node object |
| str |
| int |
| or None if the property does not exist |
| """ |
| prop = node.props.get(propname) |
| if not prop: |
| return None |
| value = prop.bytes |
| phandle = fdt32_to_cpu(value[:4]) |
| node = node.GetFdt().LookupPhandle(phandle) |
| name = '' |
| for byte in value[4:]: |
| if not byte: |
| break |
| name += chr(byte) |
| val = fdt32_to_cpu(value[4 + len(name) + 1:]) |
| return node, name, val |
| |
| def GetDatatype(node, propname, datatype): |
| """Get a value of a given type from a property |
| |
| Args: |
| node: Node object to read from |
| propname: property name to read |
| datatype: Type to read (str or int) |
| |
| Returns: |
| value read, or None if none |
| |
| Raises: |
| ValueError if datatype is not str or int |
| """ |
| if datatype == str: |
| return GetString(node, propname) |
| elif datatype == int: |
| return GetInt(node, propname) |
| raise ValueError("fdt_util internal error: Unknown data type '%s'" % |
| datatype) |