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