diff mbox

[U-Boot,v1] dtoc: make ScanTree recurse into subnodes

Message ID 1487352265-65491-1-git-send-email-philipp.tomsich@theobroma-systems.com
State Superseded
Delegated to: Simon Glass
Headers show

Commit Message

Philipp Tomsich Feb. 17, 2017, 5:24 p.m. UTC
Previously, dtoc could only process the top-level nodes which led to
device nodes in hierarchical trees to be ignored. E.g. the mmc0 node
in the following example would be ignored, as only the soc node was
processed:

  / {
	soc {
		mmc0 {
			/* ... */
		};
	};
  };

This introduces a recursive helper method ScanNode, which is used by
ScanTree to recursively parse the entire tree hierarchy.

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 tools/dtoc/dtoc.py | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

Comments

Simon Glass Feb. 22, 2017, 3:59 a.m. UTC | #1
Hi Philipp,

On 17 February 2017 at 10:24, Philipp Tomsich
<philipp.tomsich@theobroma-systems.com> wrote:
> Previously, dtoc could only process the top-level nodes which led to
> device nodes in hierarchical trees to be ignored. E.g. the mmc0 node
> in the following example would be ignored, as only the soc node was
> processed:
>
>   / {
>         soc {
>                 mmc0 {
>                         /* ... */
>                 };
>         };
>   };
>
> This introduces a recursive helper method ScanNode, which is used by
> ScanTree to recursively parse the entire tree hierarchy.
>
> Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
> ---
>  tools/dtoc/dtoc.py | 19 ++++++++++++++++++-
>  1 file changed, 18 insertions(+), 1 deletion(-)

Thanks for the patch. It seems to be in a funny format, in that only a
few lines are changed but it has the whole file as context. Can you
perhaps try using 'patman' to generate the patch?

Regards,
Simon
diff mbox

Patch

diff --git a/tools/dtoc/dtoc.py b/tools/dtoc/dtoc.py
index 6df7b0d..fe8b0c2 100755
--- a/tools/dtoc/dtoc.py
+++ b/tools/dtoc/dtoc.py
@@ -66,290 +66,307 @@  def TabTo(num_tabs, str):
 class DtbPlatdata:
     """Provide a means to convert device tree binary data to platform data
 
     The output of this process is C structures which can be used in space-
     constrained encvironments where the ~3KB code overhead of device tree
     code is not affordable.
 
     Properties:
         fdt: Fdt object, referencing the device tree
         _dtb_fname: Filename of the input device tree binary file
         _valid_nodes: A list of Node object with compatible strings
         _options: Command-line options
         _phandle_node: A dict of nodes indexed by phandle number (1, 2...)
         _outfile: The current output file (sys.stdout or a real file)
         _lines: Stashed list of output lines for outputting in the future
         _phandle_node: A dict of Nodes indexed by phandle (an integer)
     """
     def __init__(self, dtb_fname, options):
         self._dtb_fname = dtb_fname
         self._valid_nodes = None
         self._options = options
         self._phandle_node = {}
         self._outfile = None
         self._lines = []
 
     def SetupOutput(self, fname):
         """Set up the output destination
 
         Once this is done, future calls to self.Out() will output to this
         file.
 
         Args:
             fname: Filename to send output to, or '-' for stdout
         """
         if fname == '-':
             self._outfile = sys.stdout
         else:
             self._outfile = open(fname, 'w')
 
     def Out(self, str):
         """Output a string to the output file
 
         Args:
             str: String to output
         """
         self._outfile.write(str)
 
     def Buf(self, str):
         """Buffer up a string to send later
 
         Args:
             str: String to add to our 'buffer' list
         """
         self._lines.append(str)
 
     def GetBuf(self):
         """Get the contents of the output buffer, and clear it
 
         Returns:
             The output buffer, which is then cleared for future use
         """
         lines = self._lines
         self._lines = []
         return lines
 
     def GetValue(self, type, value):
         """Get a value as a C expression
 
         For integers this returns a byte-swapped (little-endian) hex string
         For bytes this returns a hex string, e.g. 0x12
         For strings this returns a literal string enclosed in quotes
         For booleans this return 'true'
 
         Args:
             type: Data type (fdt_util)
             value: Data value, as a string of bytes
         """
         if type == fdt.TYPE_INT:
             return '%#x' % fdt_util.fdt32_to_cpu(value)
         elif type == fdt.TYPE_BYTE:
             return '%#x' % ord(value[0])
         elif type == fdt.TYPE_STRING:
             return '"%s"' % value
         elif type == fdt.TYPE_BOOL:
             return 'true'
 
     def GetCompatName(self, node):
         """Get a node's first compatible string as a C identifier
 
         Args:
             node: Node object to check
         Return:
             C identifier for the first compatible string
         """
         compat = node.props['compatible'].value
         if type(compat) == list:
             compat = compat[0]
         return Conv_name_to_c(compat)
 
     def ScanDtb(self):
         """Scan the device tree to obtain a tree of notes and properties
 
         Once this is done, self.fdt.GetRoot() can be called to obtain the
         device tree root node, and progress from there.
         """
         self.fdt = fdt_select.FdtScan(self._dtb_fname)
 
+    def ScanNode(self, root):
+        for node in root.subnodes:
+            if 'compatible' in node.props:
+                status = node.props.get('status')
+                if (not options.include_disabled and not status or
+                    status.value != 'disabled'):
+                    self._valid_nodes.append(node)
+                    phandle_prop = node.props.get('phandle')
+                    if phandle_prop:
+                        phandle = phandle_prop.GetPhandle()
+                        self._phandle_node[phandle] = node
+
+            # recurse to handle any subnodes
+            self.ScanNode(node);
+
     def ScanTree(self):
         """Scan the device tree for useful information
 
         This fills in the following properties:
             _phandle_node: A dict of Nodes indexed by phandle (an integer)
             _valid_nodes: A list of nodes we wish to consider include in the
                 platform data
         """
-        node_list = []
         self._phandle_node = {}
+        self._valid_nodes = []
+        return self.ScanNode(self.fdt.GetRoot());
+
         for node in self.fdt.GetRoot().subnodes:
             if 'compatible' in node.props:
                 status = node.props.get('status')
                 if (not options.include_disabled and not status or
                     status.value != 'disabled'):
                     node_list.append(node)
                     phandle_prop = node.props.get('phandle')
                     if phandle_prop:
                         phandle = phandle_prop.GetPhandle()
                         self._phandle_node[phandle] = node
 
         self._valid_nodes = node_list
 
     def IsPhandle(self, prop):
         """Check if a node contains phandles
 
         We have no reliable way of detecting whether a node uses a phandle
         or not. As an interim measure, use a list of known property names.
 
         Args:
             prop: Prop object to check
         Return:
             True if the object value contains phandles, else False
         """
         if prop.name in ['clocks']:
             return True
         return False
 
     def ScanStructs(self):
         """Scan the device tree building up the C structures we will use.
 
         Build a dict keyed by C struct name containing a dict of Prop
         object for each struct field (keyed by property name). Where the
         same struct appears multiple times, try to use the 'widest'
         property, i.e. the one with a type which can express all others.
 
         Once the widest property is determined, all other properties are
         updated to match that width.
         """
         structs = {}
         for node in self._valid_nodes:
             node_name = self.GetCompatName(node)
             fields = {}
 
             # Get a list of all the valid properties in this node.
             for name, prop in node.props.items():
                 if name not in PROP_IGNORE_LIST and name[0] != '#':
                     fields[name] = copy.deepcopy(prop)
 
             # If we've seen this node_name before, update the existing struct.
             if node_name in structs:
                 struct = structs[node_name]
                 for name, prop in fields.items():
                     oldprop = struct.get(name)
                     if oldprop:
                         oldprop.Widen(prop)
                     else:
                         struct[name] = prop
 
             # Otherwise store this as a new struct.
             else:
                 structs[node_name] = fields
 
         upto = 0
         for node in self._valid_nodes:
             node_name = self.GetCompatName(node)
             struct = structs[node_name]
             for name, prop in node.props.items():
                 if name not in PROP_IGNORE_LIST and name[0] != '#':
                     prop.Widen(struct[name])
             upto += 1
         return structs
 
     def GenerateStructs(self, structs):
         """Generate struct defintions for the platform data
 
         This writes out the body of a header file consisting of structure
         definitions for node in self._valid_nodes. See the documentation in
         README.of-plat for more information.
         """
         self.Out('#include <stdbool.h>\n')
         self.Out('#include <libfdt.h>\n')
 
         # Output the struct definition
         for name in sorted(structs):
             self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name));
             for pname in sorted(structs[name]):
                 prop = structs[name][pname]
                 if self.IsPhandle(prop):
                     # For phandles, include a reference to the target
                     self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'),
                                              Conv_name_to_c(prop.name),
                                              len(prop.value) / 2))
                 else:
                     ptype = TYPE_NAMES[prop.type]
                     self.Out('\t%s%s' % (TabTo(2, ptype),
                                          Conv_name_to_c(prop.name)))
                     if type(prop.value) == list:
                         self.Out('[%d]' % len(prop.value))
                 self.Out(';\n')
             self.Out('};\n')
 
     def GenerateTables(self):
         """Generate device defintions for the platform data
 
         This writes out C platform data initialisation data and
         U_BOOT_DEVICE() declarations for each valid node. See the
         documentation in README.of-plat for more information.
         """
         self.Out('#include <common.h>\n')
         self.Out('#include <dm.h>\n')
         self.Out('#include <dt-structs.h>\n')
         self.Out('\n')
         node_txt_list = []
         for node in self._valid_nodes:
             struct_name = self.GetCompatName(node)
             var_name = Conv_name_to_c(node.name)
             self.Buf('static struct %s%s %s%s = {\n' %
                 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name))
             for pname, prop in node.props.items():
                 if pname in PROP_IGNORE_LIST or pname[0] == '#':
                     continue
                 ptype = TYPE_NAMES[prop.type]
                 member_name = Conv_name_to_c(prop.name)
                 self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
 
                 # Special handling for lists
                 if type(prop.value) == list:
                     self.Buf('{')
                     vals = []
                     # For phandles, output a reference to the platform data
                     # of the target node.
                     if self.IsPhandle(prop):
                         # Process the list as pairs of (phandle, id)
                         it = iter(prop.value)
                         for phandle_cell, id_cell in zip(it, it):
                             phandle = fdt_util.fdt32_to_cpu(phandle_cell)
                             id = fdt_util.fdt32_to_cpu(id_cell)
                             target_node = self._phandle_node[phandle]
                             name = Conv_name_to_c(target_node.name)
                             vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id))
                     else:
                         for val in prop.value:
                             vals.append(self.GetValue(prop.type, val))
                     self.Buf(', '.join(vals))
                     self.Buf('}')
                 else:
                     self.Buf(self.GetValue(prop.type, prop.value))
                 self.Buf(',\n')
             self.Buf('};\n')
 
             # Add a device declaration
             self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name)
             self.Buf('\t.name\t\t= "%s",\n' % struct_name)
             self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name))
             self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' %
                      (VAL_PREFIX, var_name))
             self.Buf('};\n')
             self.Buf('\n')
 
             # Output phandle target nodes first, since they may be referenced
             # by others
             if 'phandle' in node.props:
                 self.Out(''.join(self.GetBuf()))
             else:
                 node_txt_list.append(self.GetBuf())
 
         # Output all the nodes which are not phandle targets themselves, but
         # may reference them. This avoids the need for forward declarations.
         for node_txt in node_txt_list:
             self.Out(''.join(node_txt))