xref: /openbmc/u-boot/tools/dtoc/fdt.py (revision 8b7d51249eca113c4965a7c417f33d7eb569434b)
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 fdt_util
10 import libfdt
11 import 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 
19 class 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 
79 class 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 
119 class 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