xref: /openbmc/u-boot/tools/dtoc/fdt.py (revision 699c4e59)
1#!/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 fdt_util
10import libfdt
11import sys
12
13# This deals with a device tree, presenting it as a list of Node and Prop
14# objects, representing nodes and properties, respectively.
15#
16# This implementation uses a libfdt Python library to access the device tree,
17# so it is fairly efficient.
18
19class Prop:
20    """A device tree property
21
22    Properties:
23        name: Property name (as per the device tree)
24        value: Property value as a string of bytes, or a list of strings of
25            bytes
26        type: Value type
27    """
28    def __init__(self, name, bytes):
29        self.name = name
30        self.value = None
31        if not bytes:
32            self.type = fdt_util.TYPE_BOOL
33            self.value = True
34            return
35        self.type, self.value = fdt_util.BytesToValue(bytes)
36
37    def GetPhandle(self):
38        """Get a (single) phandle value from a property
39
40        Gets the phandle valuie from a property and returns it as an integer
41        """
42        return fdt_util.fdt32_to_cpu(self.value[:4])
43
44    def Widen(self, newprop):
45        """Figure out which property type is more general
46
47        Given a current property and a new property, this function returns the
48        one that is less specific as to type. The less specific property will
49        be ble to represent the data in the more specific property. This is
50        used for things like:
51
52            node1 {
53                compatible = "fred";
54                value = <1>;
55            };
56            node1 {
57                compatible = "fred";
58                value = <1 2>;
59            };
60
61        He we want to use an int array for 'value'. The first property
62        suggests that a single int is enough, but the second one shows that
63        it is not. Calling this function with these two propertes would
64        update the current property to be like the second, since it is less
65        specific.
66        """
67        if newprop.type < self.type:
68            self.type = newprop.type
69
70        if type(newprop.value) == list and type(self.value) != list:
71            self.value = [self.value]
72
73        if type(self.value) == list and len(newprop.value) > len(self.value):
74            val = fdt_util.GetEmpty(self.type)
75            while len(self.value) < len(newprop.value):
76                self.value.append(val)
77
78
79class Node:
80    """A device tree node
81
82    Properties:
83        offset: Integer offset in the device tree
84        name: Device tree node tname
85        path: Full path to node, along with the node name itself
86        _fdt: Device tree object
87        subnodes: A list of subnodes for this node, each a Node object
88        props: A dict of properties for this node, each a Prop object.
89            Keyed by property name
90    """
91    def __init__(self, fdt, offset, name, path):
92        self.offset = offset
93        self.name = name
94        self.path = path
95        self._fdt = fdt
96        self.subnodes = []
97        self.props = {}
98
99    def Scan(self):
100        """Scan a node's properties and subnodes
101
102        This fills in the props and subnodes properties, recursively
103        searching into subnodes so that the entire tree is built.
104        """
105        self.props = self._fdt.GetProps(self.path)
106
107        offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.offset)
108        while offset >= 0:
109            sep = '' if self.path[-1] == '/' else '/'
110            name = libfdt.Name(self._fdt.GetFdt(), offset)
111            path = self.path + sep + name
112            node = Node(self._fdt, offset, name, path)
113            self.subnodes.append(node)
114
115            node.Scan()
116            offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
117
118
119class Fdt:
120    """Provides simple access to a flat device tree blob.
121
122    Properties:
123      fname: Filename of fdt
124      _root: Root of device tree (a Node object)
125    """
126
127    def __init__(self, fname):
128        self.fname = fname
129        with open(fname) as fd:
130            self._fdt = fd.read()
131
132    def GetFdt(self):
133        """Get the contents of the FDT
134
135        Returns:
136            The FDT contents as a string of bytes
137        """
138        return self._fdt
139
140    def Scan(self):
141        """Scan a device tree, building up a tree of Node objects
142
143        This fills in the self._root property
144        """
145        self._root = Node(self, 0, '/', '/')
146        self._root.Scan()
147
148    def GetRoot(self):
149        """Get the root Node of the device tree
150
151        Returns:
152            The root Node object
153        """
154        return self._root
155
156    def GetProps(self, node):
157        """Get all properties from a node.
158
159        Args:
160            node: Full path to node name to look in.
161
162        Returns:
163            A dictionary containing all the properties, indexed by node name.
164            The entries are Prop objects.
165
166        Raises:
167            ValueError: if the node does not exist.
168        """
169        offset = libfdt.fdt_path_offset(self._fdt, node)
170        if offset < 0:
171            libfdt.Raise(offset)
172        props_dict = {}
173        poffset = libfdt.fdt_first_property_offset(self._fdt, offset)
174        while poffset >= 0:
175            dprop, plen = libfdt.fdt_get_property_by_offset(self._fdt, poffset)
176            prop = Prop(libfdt.String(self._fdt, dprop.nameoff), libfdt.Data(dprop))
177            props_dict[prop.name] = prop
178
179            poffset = libfdt.fdt_next_property_offset(self._fdt, poffset)
180        return props_dict
181