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