169f2ed77SSimon Glass#!/usr/bin/python 269f2ed77SSimon Glass# 369f2ed77SSimon Glass# Copyright (C) 2016 Google, Inc 469f2ed77SSimon Glass# Written by Simon Glass <sjg@chromium.org> 569f2ed77SSimon Glass# 669f2ed77SSimon Glass# SPDX-License-Identifier: GPL-2.0+ 769f2ed77SSimon Glass# 869f2ed77SSimon Glass 969f2ed77SSimon Glassimport copy 1069f2ed77SSimon Glassfrom optparse import OptionError, OptionParser 1169f2ed77SSimon Glassimport os 1258593115SSimon Glassimport struct 1369f2ed77SSimon Glassimport sys 1469f2ed77SSimon Glass 1569f2ed77SSimon Glass# Bring in the patman libraries 1669f2ed77SSimon Glassour_path = os.path.dirname(os.path.realpath(__file__)) 1769f2ed77SSimon Glasssys.path.append(os.path.join(our_path, '../patman')) 1869f2ed77SSimon Glass 19*bc1dea36SSimon Glassimport fdt 20ba482585SSimon Glassimport fdt_select 21ba482585SSimon Glassimport fdt_util 2269f2ed77SSimon Glass 2369f2ed77SSimon Glass# When we see these properties we ignore them - i.e. do not create a structure member 2469f2ed77SSimon GlassPROP_IGNORE_LIST = [ 2569f2ed77SSimon Glass '#address-cells', 2669f2ed77SSimon Glass '#gpio-cells', 2769f2ed77SSimon Glass '#size-cells', 2869f2ed77SSimon Glass 'compatible', 2969f2ed77SSimon Glass 'linux,phandle', 3069f2ed77SSimon Glass "status", 3169f2ed77SSimon Glass 'phandle', 32efefe122SSimon Glass 'u-boot,dm-pre-reloc', 3369f2ed77SSimon Glass] 3469f2ed77SSimon Glass 3569f2ed77SSimon Glass# C type declarations for the tyues we support 3669f2ed77SSimon GlassTYPE_NAMES = { 37*bc1dea36SSimon Glass fdt.TYPE_INT: 'fdt32_t', 38*bc1dea36SSimon Glass fdt.TYPE_BYTE: 'unsigned char', 39*bc1dea36SSimon Glass fdt.TYPE_STRING: 'const char *', 40*bc1dea36SSimon Glass fdt.TYPE_BOOL: 'bool', 4169f2ed77SSimon Glass}; 4269f2ed77SSimon Glass 4369f2ed77SSimon GlassSTRUCT_PREFIX = 'dtd_' 4469f2ed77SSimon GlassVAL_PREFIX = 'dtv_' 4569f2ed77SSimon Glass 4669f2ed77SSimon Glassdef Conv_name_to_c(name): 4769f2ed77SSimon Glass """Convert a device-tree name to a C identifier 4869f2ed77SSimon Glass 4969f2ed77SSimon Glass Args: 5069f2ed77SSimon Glass name: Name to convert 5169f2ed77SSimon Glass Return: 5269f2ed77SSimon Glass String containing the C version of this name 5369f2ed77SSimon Glass """ 5469f2ed77SSimon Glass str = name.replace('@', '_at_') 5569f2ed77SSimon Glass str = str.replace('-', '_') 5669f2ed77SSimon Glass str = str.replace(',', '_') 5769f2ed77SSimon Glass str = str.replace('/', '__') 5869f2ed77SSimon Glass return str 5969f2ed77SSimon Glass 6069f2ed77SSimon Glassdef TabTo(num_tabs, str): 6169f2ed77SSimon Glass if len(str) >= num_tabs * 8: 6269f2ed77SSimon Glass return str + ' ' 6369f2ed77SSimon Glass return str + '\t' * (num_tabs - len(str) / 8) 6469f2ed77SSimon Glass 6569f2ed77SSimon Glassclass DtbPlatdata: 6669f2ed77SSimon Glass """Provide a means to convert device tree binary data to platform data 6769f2ed77SSimon Glass 6869f2ed77SSimon Glass The output of this process is C structures which can be used in space- 6969f2ed77SSimon Glass constrained encvironments where the ~3KB code overhead of device tree 7069f2ed77SSimon Glass code is not affordable. 7169f2ed77SSimon Glass 7269f2ed77SSimon Glass Properties: 7369f2ed77SSimon Glass fdt: Fdt object, referencing the device tree 7469f2ed77SSimon Glass _dtb_fname: Filename of the input device tree binary file 7569f2ed77SSimon Glass _valid_nodes: A list of Node object with compatible strings 7669f2ed77SSimon Glass _options: Command-line options 7769f2ed77SSimon Glass _phandle_node: A dict of nodes indexed by phandle number (1, 2...) 7869f2ed77SSimon Glass _outfile: The current output file (sys.stdout or a real file) 7969f2ed77SSimon Glass _lines: Stashed list of output lines for outputting in the future 8069f2ed77SSimon Glass _phandle_node: A dict of Nodes indexed by phandle (an integer) 8169f2ed77SSimon Glass """ 8269f2ed77SSimon Glass def __init__(self, dtb_fname, options): 8369f2ed77SSimon Glass self._dtb_fname = dtb_fname 8469f2ed77SSimon Glass self._valid_nodes = None 8569f2ed77SSimon Glass self._options = options 8669f2ed77SSimon Glass self._phandle_node = {} 8769f2ed77SSimon Glass self._outfile = None 8869f2ed77SSimon Glass self._lines = [] 8969f2ed77SSimon Glass 9069f2ed77SSimon Glass def SetupOutput(self, fname): 9169f2ed77SSimon Glass """Set up the output destination 9269f2ed77SSimon Glass 9369f2ed77SSimon Glass Once this is done, future calls to self.Out() will output to this 9469f2ed77SSimon Glass file. 9569f2ed77SSimon Glass 9669f2ed77SSimon Glass Args: 9769f2ed77SSimon Glass fname: Filename to send output to, or '-' for stdout 9869f2ed77SSimon Glass """ 9969f2ed77SSimon Glass if fname == '-': 10069f2ed77SSimon Glass self._outfile = sys.stdout 10169f2ed77SSimon Glass else: 10269f2ed77SSimon Glass self._outfile = open(fname, 'w') 10369f2ed77SSimon Glass 10469f2ed77SSimon Glass def Out(self, str): 10569f2ed77SSimon Glass """Output a string to the output file 10669f2ed77SSimon Glass 10769f2ed77SSimon Glass Args: 10869f2ed77SSimon Glass str: String to output 10969f2ed77SSimon Glass """ 11069f2ed77SSimon Glass self._outfile.write(str) 11169f2ed77SSimon Glass 11269f2ed77SSimon Glass def Buf(self, str): 11369f2ed77SSimon Glass """Buffer up a string to send later 11469f2ed77SSimon Glass 11569f2ed77SSimon Glass Args: 11669f2ed77SSimon Glass str: String to add to our 'buffer' list 11769f2ed77SSimon Glass """ 11869f2ed77SSimon Glass self._lines.append(str) 11969f2ed77SSimon Glass 12069f2ed77SSimon Glass def GetBuf(self): 12169f2ed77SSimon Glass """Get the contents of the output buffer, and clear it 12269f2ed77SSimon Glass 12369f2ed77SSimon Glass Returns: 12469f2ed77SSimon Glass The output buffer, which is then cleared for future use 12569f2ed77SSimon Glass """ 12669f2ed77SSimon Glass lines = self._lines 12769f2ed77SSimon Glass self._lines = [] 12869f2ed77SSimon Glass return lines 12969f2ed77SSimon Glass 13069f2ed77SSimon Glass def GetValue(self, type, value): 13169f2ed77SSimon Glass """Get a value as a C expression 13269f2ed77SSimon Glass 13369f2ed77SSimon Glass For integers this returns a byte-swapped (little-endian) hex string 13469f2ed77SSimon Glass For bytes this returns a hex string, e.g. 0x12 13569f2ed77SSimon Glass For strings this returns a literal string enclosed in quotes 13669f2ed77SSimon Glass For booleans this return 'true' 13769f2ed77SSimon Glass 13869f2ed77SSimon Glass Args: 13969f2ed77SSimon Glass type: Data type (fdt_util) 14069f2ed77SSimon Glass value: Data value, as a string of bytes 14169f2ed77SSimon Glass """ 142*bc1dea36SSimon Glass if type == fdt.TYPE_INT: 14369f2ed77SSimon Glass return '%#x' % fdt_util.fdt32_to_cpu(value) 144*bc1dea36SSimon Glass elif type == fdt.TYPE_BYTE: 14569f2ed77SSimon Glass return '%#x' % ord(value[0]) 146*bc1dea36SSimon Glass elif type == fdt.TYPE_STRING: 14769f2ed77SSimon Glass return '"%s"' % value 148*bc1dea36SSimon Glass elif type == fdt.TYPE_BOOL: 14969f2ed77SSimon Glass return 'true' 15069f2ed77SSimon Glass 15169f2ed77SSimon Glass def GetCompatName(self, node): 15269f2ed77SSimon Glass """Get a node's first compatible string as a C identifier 15369f2ed77SSimon Glass 15469f2ed77SSimon Glass Args: 15569f2ed77SSimon Glass node: Node object to check 15669f2ed77SSimon Glass Return: 15769f2ed77SSimon Glass C identifier for the first compatible string 15869f2ed77SSimon Glass """ 15969f2ed77SSimon Glass compat = node.props['compatible'].value 16069f2ed77SSimon Glass if type(compat) == list: 16169f2ed77SSimon Glass compat = compat[0] 16269f2ed77SSimon Glass return Conv_name_to_c(compat) 16369f2ed77SSimon Glass 16469f2ed77SSimon Glass def ScanDtb(self): 16569f2ed77SSimon Glass """Scan the device tree to obtain a tree of notes and properties 16669f2ed77SSimon Glass 16769f2ed77SSimon Glass Once this is done, self.fdt.GetRoot() can be called to obtain the 16869f2ed77SSimon Glass device tree root node, and progress from there. 16969f2ed77SSimon Glass """ 170ba482585SSimon Glass self.fdt = fdt_select.FdtScan(self._dtb_fname) 17169f2ed77SSimon Glass 17269f2ed77SSimon Glass def ScanTree(self): 17369f2ed77SSimon Glass """Scan the device tree for useful information 17469f2ed77SSimon Glass 17569f2ed77SSimon Glass This fills in the following properties: 17669f2ed77SSimon Glass _phandle_node: A dict of Nodes indexed by phandle (an integer) 17769f2ed77SSimon Glass _valid_nodes: A list of nodes we wish to consider include in the 17869f2ed77SSimon Glass platform data 17969f2ed77SSimon Glass """ 18069f2ed77SSimon Glass node_list = [] 18169f2ed77SSimon Glass self._phandle_node = {} 18269f2ed77SSimon Glass for node in self.fdt.GetRoot().subnodes: 18369f2ed77SSimon Glass if 'compatible' in node.props: 18469f2ed77SSimon Glass status = node.props.get('status') 18569f2ed77SSimon Glass if (not options.include_disabled and not status or 18669f2ed77SSimon Glass status.value != 'disabled'): 18769f2ed77SSimon Glass node_list.append(node) 18869f2ed77SSimon Glass phandle_prop = node.props.get('phandle') 18969f2ed77SSimon Glass if phandle_prop: 19069f2ed77SSimon Glass phandle = phandle_prop.GetPhandle() 19169f2ed77SSimon Glass self._phandle_node[phandle] = node 19269f2ed77SSimon Glass 19369f2ed77SSimon Glass self._valid_nodes = node_list 19469f2ed77SSimon Glass 19569f2ed77SSimon Glass def IsPhandle(self, prop): 19669f2ed77SSimon Glass """Check if a node contains phandles 19769f2ed77SSimon Glass 19869f2ed77SSimon Glass We have no reliable way of detecting whether a node uses a phandle 19969f2ed77SSimon Glass or not. As an interim measure, use a list of known property names. 20069f2ed77SSimon Glass 20169f2ed77SSimon Glass Args: 20269f2ed77SSimon Glass prop: Prop object to check 20369f2ed77SSimon Glass Return: 20469f2ed77SSimon Glass True if the object value contains phandles, else False 20569f2ed77SSimon Glass """ 20669f2ed77SSimon Glass if prop.name in ['clocks']: 20769f2ed77SSimon Glass return True 20869f2ed77SSimon Glass return False 20969f2ed77SSimon Glass 21069f2ed77SSimon Glass def ScanStructs(self): 21169f2ed77SSimon Glass """Scan the device tree building up the C structures we will use. 21269f2ed77SSimon Glass 21369f2ed77SSimon Glass Build a dict keyed by C struct name containing a dict of Prop 21469f2ed77SSimon Glass object for each struct field (keyed by property name). Where the 21569f2ed77SSimon Glass same struct appears multiple times, try to use the 'widest' 21669f2ed77SSimon Glass property, i.e. the one with a type which can express all others. 21769f2ed77SSimon Glass 21869f2ed77SSimon Glass Once the widest property is determined, all other properties are 21969f2ed77SSimon Glass updated to match that width. 22069f2ed77SSimon Glass """ 22169f2ed77SSimon Glass structs = {} 22269f2ed77SSimon Glass for node in self._valid_nodes: 22369f2ed77SSimon Glass node_name = self.GetCompatName(node) 22469f2ed77SSimon Glass fields = {} 22569f2ed77SSimon Glass 22669f2ed77SSimon Glass # Get a list of all the valid properties in this node. 22769f2ed77SSimon Glass for name, prop in node.props.iteritems(): 22869f2ed77SSimon Glass if name not in PROP_IGNORE_LIST and name[0] != '#': 22969f2ed77SSimon Glass fields[name] = copy.deepcopy(prop) 23069f2ed77SSimon Glass 23169f2ed77SSimon Glass # If we've seen this node_name before, update the existing struct. 23269f2ed77SSimon Glass if node_name in structs: 23369f2ed77SSimon Glass struct = structs[node_name] 23469f2ed77SSimon Glass for name, prop in fields.iteritems(): 23569f2ed77SSimon Glass oldprop = struct.get(name) 23669f2ed77SSimon Glass if oldprop: 23769f2ed77SSimon Glass oldprop.Widen(prop) 23869f2ed77SSimon Glass else: 23969f2ed77SSimon Glass struct[name] = prop 24069f2ed77SSimon Glass 24169f2ed77SSimon Glass # Otherwise store this as a new struct. 24269f2ed77SSimon Glass else: 24369f2ed77SSimon Glass structs[node_name] = fields 24469f2ed77SSimon Glass 24569f2ed77SSimon Glass upto = 0 24669f2ed77SSimon Glass for node in self._valid_nodes: 24769f2ed77SSimon Glass node_name = self.GetCompatName(node) 24869f2ed77SSimon Glass struct = structs[node_name] 24969f2ed77SSimon Glass for name, prop in node.props.iteritems(): 25069f2ed77SSimon Glass if name not in PROP_IGNORE_LIST and name[0] != '#': 25169f2ed77SSimon Glass prop.Widen(struct[name]) 25269f2ed77SSimon Glass upto += 1 25369f2ed77SSimon Glass return structs 25469f2ed77SSimon Glass 25569f2ed77SSimon Glass def GenerateStructs(self, structs): 25669f2ed77SSimon Glass """Generate struct defintions for the platform data 25769f2ed77SSimon Glass 25869f2ed77SSimon Glass This writes out the body of a header file consisting of structure 25969f2ed77SSimon Glass definitions for node in self._valid_nodes. See the documentation in 26069f2ed77SSimon Glass README.of-plat for more information. 26169f2ed77SSimon Glass """ 26269f2ed77SSimon Glass self.Out('#include <stdbool.h>\n') 26369f2ed77SSimon Glass self.Out('#include <libfdt.h>\n') 26469f2ed77SSimon Glass 26569f2ed77SSimon Glass # Output the struct definition 26669f2ed77SSimon Glass for name in sorted(structs): 26769f2ed77SSimon Glass self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name)); 26869f2ed77SSimon Glass for pname in sorted(structs[name]): 26969f2ed77SSimon Glass prop = structs[name][pname] 27069f2ed77SSimon Glass if self.IsPhandle(prop): 27169f2ed77SSimon Glass # For phandles, include a reference to the target 27269f2ed77SSimon Glass self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'), 27369f2ed77SSimon Glass Conv_name_to_c(prop.name), 27469f2ed77SSimon Glass len(prop.value) / 2)) 27569f2ed77SSimon Glass else: 27669f2ed77SSimon Glass ptype = TYPE_NAMES[prop.type] 27769f2ed77SSimon Glass self.Out('\t%s%s' % (TabTo(2, ptype), 27869f2ed77SSimon Glass Conv_name_to_c(prop.name))) 27969f2ed77SSimon Glass if type(prop.value) == list: 28069f2ed77SSimon Glass self.Out('[%d]' % len(prop.value)) 28169f2ed77SSimon Glass self.Out(';\n') 28269f2ed77SSimon Glass self.Out('};\n') 28369f2ed77SSimon Glass 28469f2ed77SSimon Glass def GenerateTables(self): 28569f2ed77SSimon Glass """Generate device defintions for the platform data 28669f2ed77SSimon Glass 28769f2ed77SSimon Glass This writes out C platform data initialisation data and 28869f2ed77SSimon Glass U_BOOT_DEVICE() declarations for each valid node. See the 28969f2ed77SSimon Glass documentation in README.of-plat for more information. 29069f2ed77SSimon Glass """ 29169f2ed77SSimon Glass self.Out('#include <common.h>\n') 29269f2ed77SSimon Glass self.Out('#include <dm.h>\n') 29369f2ed77SSimon Glass self.Out('#include <dt-structs.h>\n') 29469f2ed77SSimon Glass self.Out('\n') 29569f2ed77SSimon Glass node_txt_list = [] 29669f2ed77SSimon Glass for node in self._valid_nodes: 29769f2ed77SSimon Glass struct_name = self.GetCompatName(node) 29869f2ed77SSimon Glass var_name = Conv_name_to_c(node.name) 29969f2ed77SSimon Glass self.Buf('static struct %s%s %s%s = {\n' % 30069f2ed77SSimon Glass (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name)) 30169f2ed77SSimon Glass for pname, prop in node.props.iteritems(): 30269f2ed77SSimon Glass if pname in PROP_IGNORE_LIST or pname[0] == '#': 30369f2ed77SSimon Glass continue 30469f2ed77SSimon Glass ptype = TYPE_NAMES[prop.type] 30569f2ed77SSimon Glass member_name = Conv_name_to_c(prop.name) 30669f2ed77SSimon Glass self.Buf('\t%s= ' % TabTo(3, '.' + member_name)) 30769f2ed77SSimon Glass 30869f2ed77SSimon Glass # Special handling for lists 30969f2ed77SSimon Glass if type(prop.value) == list: 31069f2ed77SSimon Glass self.Buf('{') 31169f2ed77SSimon Glass vals = [] 31269f2ed77SSimon Glass # For phandles, output a reference to the platform data 31369f2ed77SSimon Glass # of the target node. 31469f2ed77SSimon Glass if self.IsPhandle(prop): 31569f2ed77SSimon Glass # Process the list as pairs of (phandle, id) 31669f2ed77SSimon Glass it = iter(prop.value) 31769f2ed77SSimon Glass for phandle_cell, id_cell in zip(it, it): 31869f2ed77SSimon Glass phandle = fdt_util.fdt32_to_cpu(phandle_cell) 31969f2ed77SSimon Glass id = fdt_util.fdt32_to_cpu(id_cell) 32069f2ed77SSimon Glass target_node = self._phandle_node[phandle] 32169f2ed77SSimon Glass name = Conv_name_to_c(target_node.name) 32269f2ed77SSimon Glass vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id)) 32369f2ed77SSimon Glass else: 32469f2ed77SSimon Glass for val in prop.value: 32569f2ed77SSimon Glass vals.append(self.GetValue(prop.type, val)) 32669f2ed77SSimon Glass self.Buf(', '.join(vals)) 32769f2ed77SSimon Glass self.Buf('}') 32869f2ed77SSimon Glass else: 32969f2ed77SSimon Glass self.Buf(self.GetValue(prop.type, prop.value)) 33069f2ed77SSimon Glass self.Buf(',\n') 33169f2ed77SSimon Glass self.Buf('};\n') 33269f2ed77SSimon Glass 33369f2ed77SSimon Glass # Add a device declaration 33469f2ed77SSimon Glass self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name) 33569f2ed77SSimon Glass self.Buf('\t.name\t\t= "%s",\n' % struct_name) 33669f2ed77SSimon Glass self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name)) 3379fa28190SSimon Glass self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' % 3389fa28190SSimon Glass (VAL_PREFIX, var_name)) 33969f2ed77SSimon Glass self.Buf('};\n') 34069f2ed77SSimon Glass self.Buf('\n') 34169f2ed77SSimon Glass 34269f2ed77SSimon Glass # Output phandle target nodes first, since they may be referenced 34369f2ed77SSimon Glass # by others 34469f2ed77SSimon Glass if 'phandle' in node.props: 34569f2ed77SSimon Glass self.Out(''.join(self.GetBuf())) 34669f2ed77SSimon Glass else: 34769f2ed77SSimon Glass node_txt_list.append(self.GetBuf()) 34869f2ed77SSimon Glass 34969f2ed77SSimon Glass # Output all the nodes which are not phandle targets themselves, but 35069f2ed77SSimon Glass # may reference them. This avoids the need for forward declarations. 35169f2ed77SSimon Glass for node_txt in node_txt_list: 35269f2ed77SSimon Glass self.Out(''.join(node_txt)) 35369f2ed77SSimon Glass 35469f2ed77SSimon Glass 35569f2ed77SSimon Glassif __name__ != "__main__": 35669f2ed77SSimon Glass pass 35769f2ed77SSimon Glass 35869f2ed77SSimon Glassparser = OptionParser() 35969f2ed77SSimon Glassparser.add_option('-d', '--dtb-file', action='store', 36069f2ed77SSimon Glass help='Specify the .dtb input file') 36169f2ed77SSimon Glassparser.add_option('--include-disabled', action='store_true', 36269f2ed77SSimon Glass help='Include disabled nodes') 36369f2ed77SSimon Glassparser.add_option('-o', '--output', action='store', default='-', 36469f2ed77SSimon Glass help='Select output filename') 36569f2ed77SSimon Glass(options, args) = parser.parse_args() 36669f2ed77SSimon Glass 36769f2ed77SSimon Glassif not args: 36869f2ed77SSimon Glass raise ValueError('Please specify a command: struct, platdata') 36969f2ed77SSimon Glass 37069f2ed77SSimon Glassplat = DtbPlatdata(options.dtb_file, options) 37169f2ed77SSimon Glassplat.ScanDtb() 37269f2ed77SSimon Glassplat.ScanTree() 37369f2ed77SSimon Glassplat.SetupOutput(options.output) 37469f2ed77SSimon Glassstructs = plat.ScanStructs() 37569f2ed77SSimon Glass 37669f2ed77SSimon Glassfor cmd in args[0].split(','): 37769f2ed77SSimon Glass if cmd == 'struct': 37869f2ed77SSimon Glass plat.GenerateStructs(structs) 37969f2ed77SSimon Glass elif cmd == 'platdata': 38069f2ed77SSimon Glass plat.GenerateTables() 38169f2ed77SSimon Glass else: 38269f2ed77SSimon Glass raise ValueError("Unknown command '%s': (use: struct, platdata)" % cmd) 383