1#!/usr/bin/python 2# 3# Copyright (C) 2017 Google, Inc 4# Written by Simon Glass <sjg@chromium.org> 5# 6# SPDX-License-Identifier: GPL-2.0+ 7# 8 9import copy 10 11import fdt 12import fdt_util 13 14# When we see these properties we ignore them - i.e. do not create a structure member 15PROP_IGNORE_LIST = [ 16 '#address-cells', 17 '#gpio-cells', 18 '#size-cells', 19 'compatible', 20 'linux,phandle', 21 "status", 22 'phandle', 23 'u-boot,dm-pre-reloc', 24 'u-boot,dm-tpl', 25 'u-boot,dm-spl', 26] 27 28# C type declarations for the tyues we support 29TYPE_NAMES = { 30 fdt.TYPE_INT: 'fdt32_t', 31 fdt.TYPE_BYTE: 'unsigned char', 32 fdt.TYPE_STRING: 'const char *', 33 fdt.TYPE_BOOL: 'bool', 34}; 35 36STRUCT_PREFIX = 'dtd_' 37VAL_PREFIX = 'dtv_' 38 39def Conv_name_to_c(name): 40 """Convert a device-tree name to a C identifier 41 42 Args: 43 name: Name to convert 44 Return: 45 String containing the C version of this name 46 """ 47 str = name.replace('@', '_at_') 48 str = str.replace('-', '_') 49 str = str.replace(',', '_') 50 str = str.replace('.', '_') 51 str = str.replace('/', '__') 52 return str 53 54def TabTo(num_tabs, str): 55 if len(str) >= num_tabs * 8: 56 return str + ' ' 57 return str + '\t' * (num_tabs - len(str) // 8) 58 59class DtbPlatdata: 60 """Provide a means to convert device tree binary data to platform data 61 62 The output of this process is C structures which can be used in space- 63 constrained encvironments where the ~3KB code overhead of device tree 64 code is not affordable. 65 66 Properties: 67 fdt: Fdt object, referencing the device tree 68 _dtb_fname: Filename of the input device tree binary file 69 _valid_nodes: A list of Node object with compatible strings 70 _options: Command-line options 71 _phandle_node: A dict of nodes indexed by phandle number (1, 2...) 72 _outfile: The current output file (sys.stdout or a real file) 73 _lines: Stashed list of output lines for outputting in the future 74 _phandle_node: A dict of Nodes indexed by phandle (an integer) 75 """ 76 def __init__(self, dtb_fname, options): 77 self._dtb_fname = dtb_fname 78 self._valid_nodes = None 79 self._options = options 80 self._phandle_node = {} 81 self._outfile = None 82 self._lines = [] 83 self._aliases = {} 84 85 def SetupOutput(self, fname): 86 """Set up the output destination 87 88 Once this is done, future calls to self.Out() will output to this 89 file. 90 91 Args: 92 fname: Filename to send output to, or '-' for stdout 93 """ 94 if fname == '-': 95 self._outfile = sys.stdout 96 else: 97 self._outfile = open(fname, 'w') 98 99 def Out(self, str): 100 """Output a string to the output file 101 102 Args: 103 str: String to output 104 """ 105 self._outfile.write(str) 106 107 def Buf(self, str): 108 """Buffer up a string to send later 109 110 Args: 111 str: String to add to our 'buffer' list 112 """ 113 self._lines.append(str) 114 115 def GetBuf(self): 116 """Get the contents of the output buffer, and clear it 117 118 Returns: 119 The output buffer, which is then cleared for future use 120 """ 121 lines = self._lines 122 self._lines = [] 123 return lines 124 125 def GetValue(self, type, value): 126 """Get a value as a C expression 127 128 For integers this returns a byte-swapped (little-endian) hex string 129 For bytes this returns a hex string, e.g. 0x12 130 For strings this returns a literal string enclosed in quotes 131 For booleans this return 'true' 132 133 Args: 134 type: Data type (fdt_util) 135 value: Data value, as a string of bytes 136 """ 137 if type == fdt.TYPE_INT: 138 return '%#x' % fdt_util.fdt32_to_cpu(value) 139 elif type == fdt.TYPE_BYTE: 140 return '%#x' % ord(value[0]) 141 elif type == fdt.TYPE_STRING: 142 return '"%s"' % value 143 elif type == fdt.TYPE_BOOL: 144 return 'true' 145 146 def GetCompatName(self, node): 147 """Get a node's first compatible string as a C identifier 148 149 Args: 150 node: Node object to check 151 Return: 152 C identifier for the first compatible string 153 """ 154 compat = node.props['compatible'].value 155 aliases = [] 156 if type(compat) == list: 157 compat, aliases = compat[0], compat[1:] 158 return Conv_name_to_c(compat), [Conv_name_to_c(a) for a in aliases] 159 160 def ScanDtb(self): 161 """Scan the device tree to obtain a tree of notes and properties 162 163 Once this is done, self.fdt.GetRoot() can be called to obtain the 164 device tree root node, and progress from there. 165 """ 166 self.fdt = fdt.FdtScan(self._dtb_fname) 167 168 def ScanNode(self, root): 169 for node in root.subnodes: 170 if 'compatible' in node.props: 171 status = node.props.get('status') 172 if (not self._options.include_disabled and not status or 173 status.value != 'disabled'): 174 self._valid_nodes.append(node) 175 phandle_prop = node.props.get('phandle') 176 if phandle_prop: 177 phandle = phandle_prop.GetPhandle() 178 self._phandle_node[phandle] = node 179 180 # recurse to handle any subnodes 181 self.ScanNode(node); 182 183 def ScanTree(self): 184 """Scan the device tree for useful information 185 186 This fills in the following properties: 187 _phandle_node: A dict of Nodes indexed by phandle (an integer) 188 _valid_nodes: A list of nodes we wish to consider include in the 189 platform data 190 """ 191 self._phandle_node = {} 192 self._valid_nodes = [] 193 return self.ScanNode(self.fdt.GetRoot()); 194 195 for node in self.fdt.GetRoot().subnodes: 196 if 'compatible' in node.props: 197 status = node.props.get('status') 198 if (not self._options.include_disabled and not status or 199 status.value != 'disabled'): 200 node_list.append(node) 201 phandle_prop = node.props.get('phandle') 202 if phandle_prop: 203 phandle = phandle_prop.GetPhandle() 204 self._phandle_node[phandle] = node 205 206 self._valid_nodes = node_list 207 208 def IsPhandle(self, prop): 209 """Check if a node contains phandles 210 211 We have no reliable way of detecting whether a node uses a phandle 212 or not. As an interim measure, use a list of known property names. 213 214 Args: 215 prop: Prop object to check 216 Return: 217 True if the object value contains phandles, else False 218 """ 219 if prop.name in ['clocks']: 220 return True 221 return False 222 223 def ScanStructs(self): 224 """Scan the device tree building up the C structures we will use. 225 226 Build a dict keyed by C struct name containing a dict of Prop 227 object for each struct field (keyed by property name). Where the 228 same struct appears multiple times, try to use the 'widest' 229 property, i.e. the one with a type which can express all others. 230 231 Once the widest property is determined, all other properties are 232 updated to match that width. 233 """ 234 structs = {} 235 for node in self._valid_nodes: 236 node_name, _ = self.GetCompatName(node) 237 fields = {} 238 239 # Get a list of all the valid properties in this node. 240 for name, prop in node.props.items(): 241 if name not in PROP_IGNORE_LIST and name[0] != '#': 242 fields[name] = copy.deepcopy(prop) 243 244 # If we've seen this node_name before, update the existing struct. 245 if node_name in structs: 246 struct = structs[node_name] 247 for name, prop in fields.items(): 248 oldprop = struct.get(name) 249 if oldprop: 250 oldprop.Widen(prop) 251 else: 252 struct[name] = prop 253 254 # Otherwise store this as a new struct. 255 else: 256 structs[node_name] = fields 257 258 upto = 0 259 for node in self._valid_nodes: 260 node_name, _ = self.GetCompatName(node) 261 struct = structs[node_name] 262 for name, prop in node.props.items(): 263 if name not in PROP_IGNORE_LIST and name[0] != '#': 264 prop.Widen(struct[name]) 265 upto += 1 266 267 struct_name, aliases = self.GetCompatName(node) 268 for alias in aliases: 269 self._aliases[alias] = struct_name 270 271 return structs 272 273 def ScanPhandles(self): 274 """Figure out what phandles each node uses 275 276 We need to be careful when outputing nodes that use phandles since 277 they must come after the declaration of the phandles in the C file. 278 Otherwise we get a compiler error since the phandle struct is not yet 279 declared. 280 281 This function adds to each node a list of phandle nodes that the node 282 depends on. This allows us to output things in the right order. 283 """ 284 for node in self._valid_nodes: 285 node.phandles = set() 286 for pname, prop in node.props.items(): 287 if pname in PROP_IGNORE_LIST or pname[0] == '#': 288 continue 289 if type(prop.value) == list: 290 if self.IsPhandle(prop): 291 # Process the list as pairs of (phandle, id) 292 it = iter(prop.value) 293 for phandle_cell, id_cell in zip(it, it): 294 phandle = fdt_util.fdt32_to_cpu(phandle_cell) 295 id = fdt_util.fdt32_to_cpu(id_cell) 296 target_node = self._phandle_node[phandle] 297 node.phandles.add(target_node) 298 299 300 def GenerateStructs(self, structs): 301 """Generate struct defintions for the platform data 302 303 This writes out the body of a header file consisting of structure 304 definitions for node in self._valid_nodes. See the documentation in 305 README.of-plat for more information. 306 """ 307 self.Out('#include <stdbool.h>\n') 308 self.Out('#include <libfdt.h>\n') 309 310 # Output the struct definition 311 for name in sorted(structs): 312 self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name)); 313 for pname in sorted(structs[name]): 314 prop = structs[name][pname] 315 if self.IsPhandle(prop): 316 # For phandles, include a reference to the target 317 self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'), 318 Conv_name_to_c(prop.name), 319 len(prop.value) / 2)) 320 else: 321 ptype = TYPE_NAMES[prop.type] 322 self.Out('\t%s%s' % (TabTo(2, ptype), 323 Conv_name_to_c(prop.name))) 324 if type(prop.value) == list: 325 self.Out('[%d]' % len(prop.value)) 326 self.Out(';\n') 327 self.Out('};\n') 328 329 for alias, struct_name in self._aliases.iteritems(): 330 self.Out('#define %s%s %s%s\n'% (STRUCT_PREFIX, alias, 331 STRUCT_PREFIX, struct_name)) 332 333 def OutputNode(self, node): 334 """Output the C code for a node 335 336 Args: 337 node: node to output 338 """ 339 struct_name, _ = self.GetCompatName(node) 340 var_name = Conv_name_to_c(node.name) 341 self.Buf('static struct %s%s %s%s = {\n' % 342 (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name)) 343 for pname, prop in node.props.items(): 344 if pname in PROP_IGNORE_LIST or pname[0] == '#': 345 continue 346 ptype = TYPE_NAMES[prop.type] 347 member_name = Conv_name_to_c(prop.name) 348 self.Buf('\t%s= ' % TabTo(3, '.' + member_name)) 349 350 # Special handling for lists 351 if type(prop.value) == list: 352 self.Buf('{') 353 vals = [] 354 # For phandles, output a reference to the platform data 355 # of the target node. 356 if self.IsPhandle(prop): 357 # Process the list as pairs of (phandle, id) 358 it = iter(prop.value) 359 for phandle_cell, id_cell in zip(it, it): 360 phandle = fdt_util.fdt32_to_cpu(phandle_cell) 361 id = fdt_util.fdt32_to_cpu(id_cell) 362 target_node = self._phandle_node[phandle] 363 name = Conv_name_to_c(target_node.name) 364 vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id)) 365 else: 366 for val in prop.value: 367 vals.append(self.GetValue(prop.type, val)) 368 self.Buf(', '.join(vals)) 369 self.Buf('}') 370 else: 371 self.Buf(self.GetValue(prop.type, prop.value)) 372 self.Buf(',\n') 373 self.Buf('};\n') 374 375 # Add a device declaration 376 self.Buf('U_BOOT_DEVICE(%s) = {\n' % var_name) 377 self.Buf('\t.name\t\t= "%s",\n' % struct_name) 378 self.Buf('\t.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name)) 379 self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' % 380 (VAL_PREFIX, var_name)) 381 self.Buf('};\n') 382 self.Buf('\n') 383 384 self.Out(''.join(self.GetBuf())) 385 386 def GenerateTables(self): 387 """Generate device defintions for the platform data 388 389 This writes out C platform data initialisation data and 390 U_BOOT_DEVICE() declarations for each valid node. Where a node has 391 multiple compatible strings, a #define is used to make them equivalent. 392 393 See the documentation in doc/driver-model/of-plat.txt for more 394 information. 395 """ 396 self.Out('#include <common.h>\n') 397 self.Out('#include <dm.h>\n') 398 self.Out('#include <dt-structs.h>\n') 399 self.Out('\n') 400 nodes_to_output = list(self._valid_nodes) 401 402 # Keep outputing nodes until there is none left 403 while nodes_to_output: 404 node = nodes_to_output[0] 405 # Output all the node's dependencies first 406 for req_node in node.phandles: 407 if req_node in nodes_to_output: 408 self.OutputNode(req_node) 409 nodes_to_output.remove(req_node) 410 self.OutputNode(node) 411 nodes_to_output.remove(node) 412