From patchwork Fri Feb 17 17:24:25 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philipp Tomsich X-Patchwork-Id: 729243 X-Patchwork-Delegate: sjg@chromium.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 3vQ0fd06zhz9s7w for ; Sat, 18 Feb 2017 04:40:25 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id C3C69B3878; Fri, 17 Feb 2017 18:40:21 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id v_HO6LyAnp9b; Fri, 17 Feb 2017 18:40:21 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 756E7B3895; Fri, 17 Feb 2017 18:40:19 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id DAF2EA7B3C for ; Fri, 17 Feb 2017 18:40:14 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id DxonloRqAvbU for ; Fri, 17 Feb 2017 18:40:14 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail.theobroma-systems.com (vegas.theobroma-systems.com [144.76.126.164]) by theia.denx.de (Postfix) with ESMTPS id 0AC424A99B for ; Fri, 17 Feb 2017 18:40:11 +0100 (CET) Received: from [86.59.122.178] (port=54308 helo=android.lan) by mail.theobroma-systems.com with esmtpsa (TLS1.2:DHE_RSA_AES_256_CBC_SHA256:256) (Exim 4.80) (envelope-from ) id 1cemGl-0008Lr-Cy; Fri, 17 Feb 2017 18:24:31 +0100 From: Philipp Tomsich To: u-boot@lists.denx.de, Simon Glass Date: Fri, 17 Feb 2017 18:24:25 +0100 Message-Id: <1487352265-65491-1-git-send-email-philipp.tomsich@theobroma-systems.com> X-Mailer: git-send-email 1.9.1 Cc: Andre Przywara , Philipp Tomsich , Klaus Goger Subject: [U-Boot] [PATCH v1] dtoc: make ScanTree recurse into subnodes X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" 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 --- tools/dtoc/dtoc.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) 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 \n') self.Out('#include \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 \n') self.Out('#include \n') self.Out('#include \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))