dtoc: Create a base class for Fdt
[platform/kernel/u-boot.git] / tools / dtoc / fdt_fallback.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 command
10 import fdt
11 from fdt import Fdt, NodeBase, PropBase
12 import fdt_util
13 import sys
14
15 # This deals with a device tree, presenting it as a list of Node and Prop
16 # objects, representing nodes and properties, respectively.
17 #
18 # This implementation uses the fdtget tool to access the device tree, so it
19 # is not very efficient for larger trees. The tool is called once for each
20 # node and property in the tree.
21
22 class Prop(PropBase):
23     """A device tree property
24
25     Properties:
26         name: Property name (as per the device tree)
27         value: Property value as a string of bytes, or a list of strings of
28             bytes
29         type: Value type
30     """
31     def __init__(self, node, name, byte_list_str):
32         PropBase.__init__(self, node, 0, name)
33         if not byte_list_str.strip():
34             self.type = fdt_util.TYPE_BOOL
35             return
36         self.bytes = [chr(int(byte, 16))
37                       for byte in byte_list_str.strip().split(' ')]
38         self.type, self.value = fdt_util.BytesToValue(''.join(self.bytes))
39
40     def GetPhandle(self):
41         """Get a (single) phandle value from a property
42
43         Gets the phandle valuie from a property and returns it as an integer
44         """
45         return fdt_util.fdt32_to_cpu(self.value[:4])
46
47     def Widen(self, newprop):
48         """Figure out which property type is more general
49
50         Given a current property and a new property, this function returns the
51         one that is less specific as to type. The less specific property will
52         be ble to represent the data in the more specific property. This is
53         used for things like:
54
55             node1 {
56                 compatible = "fred";
57                 value = <1>;
58             };
59             node1 {
60                 compatible = "fred";
61                 value = <1 2>;
62             };
63
64         He we want to use an int array for 'value'. The first property
65         suggests that a single int is enough, but the second one shows that
66         it is not. Calling this function with these two propertes would
67         update the current property to be like the second, since it is less
68         specific.
69         """
70         if newprop.type < self.type:
71             self.type = newprop.type
72
73         if type(newprop.value) == list and type(self.value) != list:
74             self.value = newprop.value
75
76         if type(self.value) == list and len(newprop.value) > len(self.value):
77             val = fdt_util.GetEmpty(self.type)
78             while len(self.value) < len(newprop.value):
79                 self.value.append(val)
80
81
82 class Node(NodeBase):
83     """A device tree node
84
85     Properties:
86         name: Device tree node tname
87         path: Full path to node, along with the node name itself
88         _fdt: Device tree object
89         subnodes: A list of subnodes for this node, each a Node object
90         props: A dict of properties for this node, each a Prop object.
91             Keyed by property name
92     """
93     def __init__(self, fdt, offset, name, path):
94         NodeBase.__init__(self, fdt, offset, name, path)
95
96     def Scan(self):
97         """Scan a node's properties and subnodes
98
99         This fills in the props and subnodes properties, recursively
100         searching into subnodes so that the entire tree is built.
101         """
102         for name, byte_list_str in self._fdt.GetProps(self.path).iteritems():
103             prop = Prop(self, name, byte_list_str)
104             self.props[name] = prop
105
106         for name in self._fdt.GetSubNodes(self.path):
107             sep = '' if self.path[-1] == '/' else '/'
108             path = self.path + sep + name
109             node = Node(self._fdt, 0, name, path)
110             self.subnodes.append(node)
111
112             node.Scan()
113
114
115 class FdtFallback(Fdt):
116     """Provides simple access to a flat device tree blob using fdtget/fdtput
117
118     Properties:
119         See superclass
120     """
121
122     def __init__(self, fname):
123         Fdt.__init__(self, fname)
124
125     def Scan(self):
126         """Scan a device tree, building up a tree of Node objects
127
128         This fills in the self._root property
129         """
130         self._root = Node(self, 0, '/', '/')
131         self._root.Scan()
132
133     def GetRoot(self):
134         """Get the root Node of the device tree
135
136         Returns:
137             The root Node object
138         """
139         return self._root
140
141     def GetSubNodes(self, node):
142         """Returns a list of sub-nodes of a given node
143
144         Args:
145             node: Node name to return children from
146
147         Returns:
148             List of children in the node (each a string node name)
149
150         Raises:
151             CmdError: if the node does not exist.
152         """
153         out = command.Output('fdtget', self._fname, '-l', node)
154         return out.strip().splitlines()
155
156     def GetProps(self, node, convert_dashes=False):
157         """Get all properties from a node
158
159         Args:
160             node: full path to node name to look in
161             convert_dashes: True to convert - to _ in node names
162
163         Returns:
164             A dictionary containing all the properties, indexed by node name.
165             The entries are simply strings - no decoding of lists or numbers
166             is done.
167
168         Raises:
169             CmdError: if the node does not exist.
170         """
171         out = command.Output('fdtget', self._fname, node, '-p')
172         props = out.strip().splitlines()
173         props_dict = {}
174         for prop in props:
175             name = prop
176             if convert_dashes:
177                 prop = re.sub('-', '_', prop)
178             props_dict[prop] = self.GetProp(node, name)
179         return props_dict
180
181     def GetProp(self, node, prop, default=None, typespec=None):
182         """Get a property from a device tree.
183
184         This looks up the given node and property, and returns the value as a
185         string,
186
187         If the node or property does not exist, this will return the default
188         value.
189
190         Args:
191             node: Full path to node to look up.
192             prop: Property name to look up.
193             default: Default value to return if nothing is present in the fdt,
194                 or None to raise in this case. This will be converted to a
195                 string.
196             typespec: Type character to use (None for default, 's' for string)
197
198         Returns:
199             string containing the property value.
200
201         Raises:
202             CmdError: if the property does not exist and no default is provided.
203         """
204         args = [self._fname, node, prop, '-t', 'bx']
205         if default is not None:
206           args += ['-d', str(default)]
207         if typespec is not None:
208           args += ['-t%s' % typespec]
209         out = command.Output('fdtget', *args)
210         return out.strip()
211
212     @classmethod
213     def Node(self, fdt, offset, name, path):
214         """Create a new node
215
216         This is used by Fdt.Scan() to create a new node using the correct
217         class.
218
219         Args:
220             fdt: Fdt object
221             offset: Offset of node
222             name: Node name
223             path: Full path to node
224         """
225         node = Node(fdt, offset, name, path)
226         return node