1#!/usr/bin/python 2# SPDX-License-Identifier: GPL-2.0+ 3# 4# Copyright (C) 2017 Google, Inc 5# Written by Simon Glass <sjg@chromium.org> 6# 7 8"""Device tree to platform data class 9 10This supports converting device tree data to C structures definitions and 11static data. 12 13See doc/driver-model/of-plat.rst for more informaiton 14""" 15 16import collections 17import copy 18from enum import IntEnum 19import os 20import re 21import sys 22 23from dtoc import fdt 24from dtoc import fdt_util 25from dtoc import src_scan 26from dtoc.src_scan import conv_name_to_c 27 28# When we see these properties we ignore them - i.e. do not create a structure 29# member 30PROP_IGNORE_LIST = [ 31 '#address-cells', 32 '#gpio-cells', 33 '#size-cells', 34 'compatible', 35 'linux,phandle', 36 "status", 37 'phandle', 38 'u-boot,dm-pre-reloc', 39 'u-boot,dm-tpl', 40 'u-boot,dm-spl', 41] 42 43# C type declarations for the types we support 44TYPE_NAMES = { 45 fdt.Type.INT: 'fdt32_t', 46 fdt.Type.BYTE: 'unsigned char', 47 fdt.Type.STRING: 'const char *', 48 fdt.Type.BOOL: 'bool', 49 fdt.Type.INT64: 'fdt64_t', 50} 51 52STRUCT_PREFIX = 'dtd_' 53VAL_PREFIX = 'dtv_' 54 55# Properties which are considered to be phandles 56# key: property name 57# value: name of associated #cells property in the target node 58# 59# New phandle properties must be added here; otherwise they will come through as 60# simple integers and finding devices by phandle will not work. 61# Any property that ends with one of these (e.g. 'cd-gpios') will be considered 62# a phandle property. 63PHANDLE_PROPS = { 64 'clocks': '#clock-cells', 65 'interrupts-extended': '#interrupt-cells', 66 'gpios': '#gpio-cells', 67 'sandbox,emul': '#emul-cells', 68 } 69 70class Ftype(IntEnum): 71 SOURCE, HEADER = range(2) 72 73 74# This holds information about each type of output file dtoc can create 75# ftype: Type of file (Ftype) 76# fname: Filename excluding directory, e.g. 'dt-plat.c' 77# hdr_comment: Comment explaining the purpose of the file 78OutputFile = collections.namedtuple('OutputFile', 79 ['ftype', 'fname', 'method', 'hdr_comment']) 80 81# This holds information about a property which includes phandles. 82# 83# max_args: integer: Maximum number or arguments that any phandle uses (int). 84# args: Number of args for each phandle in the property. The total number of 85# phandles is len(args). This is a list of integers. 86PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args']) 87 88# Holds a single phandle link, allowing a C struct value to be assigned to point 89# to a device 90# 91# var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node') 92# dev_name: Name of device to assign to (e.g. 'clock') 93PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name']) 94 95 96def tab_to(num_tabs, line): 97 """Append tabs to a line of text to reach a tab stop. 98 99 Args: 100 num_tabs (int): Tab stop to obtain (0 = column 0, 1 = column 8, etc.) 101 line (str): Line of text to append to 102 103 Returns: 104 str: line with the correct number of tabs appeneded. If the line already 105 extends past that tab stop then a single space is appended. 106 """ 107 if len(line) >= num_tabs * 8: 108 return line + ' ' 109 return line + '\t' * (num_tabs - len(line) // 8) 110 111def get_value(ftype, value): 112 """Get a value as a C expression 113 114 For integers this returns a byte-swapped (little-endian) hex string 115 For bytes this returns a hex string, e.g. 0x12 116 For strings this returns a literal string enclosed in quotes 117 For booleans this return 'true' 118 119 Args: 120 ftype (fdt.Type): Data type (fdt_util) 121 value (bytes): Data value, as a string of bytes 122 123 Returns: 124 str: String representation of the value 125 """ 126 if ftype == fdt.Type.INT: 127 val = '%#x' % fdt_util.fdt32_to_cpu(value) 128 elif ftype == fdt.Type.BYTE: 129 char = value[0] 130 val = '%#x' % (ord(char) if isinstance(char, str) else char) 131 elif ftype == fdt.Type.STRING: 132 # Handle evil ACPI backslashes by adding another backslash before them. 133 # So "\\_SB.GPO0" in the device tree effectively stays like that in C 134 val = '"%s"' % value.replace('\\', '\\\\') 135 elif ftype == fdt.Type.BOOL: 136 val = 'true' 137 else: # ftype == fdt.Type.INT64: 138 val = '%#x' % value 139 return val 140 141 142class DtbPlatdata(): 143 """Provide a means to convert device tree binary data to platform data 144 145 The output of this process is C structures which can be used in space- 146 constrained encvironments where the ~3KB code overhead of device tree 147 code is not affordable. 148 149 Properties: 150 _scan: Scan object, for scanning and reporting on useful information 151 from the U-Boot source code 152 _fdt: Fdt object, referencing the device tree 153 _dtb_fname: Filename of the input device tree binary file 154 _valid_nodes_unsorted: A list of Node object with compatible strings, 155 ordered by devicetree node order 156 _valid_nodes: A list of Node object with compatible strings, ordered by 157 conv_name_to_c(node.name) 158 _include_disabled: true to include nodes marked status = "disabled" 159 _outfile: The current output file (sys.stdout or a real file) 160 _lines: Stashed list of output lines for outputting in the future 161 _dirname: Directory to hold output files, or None for none (all files 162 go to stdout) 163 _struct_data (dict): OrderedDict of dtplat structures to output 164 key (str): Node name, as a C identifier 165 value: dict containing structure fields: 166 key (str): Field name 167 value: Prop object with field information 168 _basedir (str): Base directory of source tree 169 _valid_uclasses (list of src_scan.Uclass): List of uclasses needed for 170 the selected devices (see _valid_node), in alphabetical order 171 _instantiate: Instantiate devices so they don't need to be bound at 172 run-time 173 """ 174 def __init__(self, scan, dtb_fname, include_disabled, instantiate=False): 175 self._scan = scan 176 self._fdt = None 177 self._dtb_fname = dtb_fname 178 self._valid_nodes = None 179 self._valid_nodes_unsorted = None 180 self._include_disabled = include_disabled 181 self._outfile = None 182 self._lines = [] 183 self._dirnames = [None] * len(Ftype) 184 self._struct_data = collections.OrderedDict() 185 self._basedir = None 186 self._valid_uclasses = None 187 self._instantiate = instantiate 188 189 def setup_output_dirs(self, output_dirs): 190 """Set up the output directories 191 192 This should be done before setup_output() is called 193 194 Args: 195 output_dirs (tuple of str): 196 Directory to use for C output files. 197 Use None to write files relative current directory 198 Directory to use for H output files. 199 Defaults to the C output dir 200 """ 201 def process_dir(ftype, dirname): 202 if dirname: 203 os.makedirs(dirname, exist_ok=True) 204 self._dirnames[ftype] = dirname 205 206 if output_dirs: 207 c_dirname = output_dirs[0] 208 h_dirname = output_dirs[1] if len(output_dirs) > 1 else c_dirname 209 process_dir(Ftype.SOURCE, c_dirname) 210 process_dir(Ftype.HEADER, h_dirname) 211 212 def setup_output(self, ftype, fname): 213 """Set up the output destination 214 215 Once this is done, future calls to self.out() will output to this 216 file. The file used is as follows: 217 218 self._dirnames[ftype] is None: output to fname, or stdout if None 219 self._dirnames[ftype] is not None: output to fname in that directory 220 221 Calling this function multiple times will close the old file and open 222 the new one. If they are the same file, nothing happens and output will 223 continue to the same file. 224 225 Args: 226 ftype (str): Type of file to create ('c' or 'h') 227 fname (str): Filename to send output to. If there is a directory in 228 self._dirnames for this file type, it will be put in that 229 directory 230 """ 231 dirname = self._dirnames[ftype] 232 if dirname: 233 pathname = os.path.join(dirname, fname) 234 if self._outfile: 235 self._outfile.close() 236 self._outfile = open(pathname, 'w') 237 elif fname: 238 if not self._outfile: 239 self._outfile = open(fname, 'w') 240 else: 241 self._outfile = sys.stdout 242 243 def finish_output(self): 244 """Finish outputing to a file 245 246 This closes the output file, if one is in use 247 """ 248 if self._outfile != sys.stdout: 249 self._outfile.close() 250 self._outfile = None 251 252 def out(self, line): 253 """Output a string to the output file 254 255 Args: 256 line (str): String to output 257 """ 258 self._outfile.write(line) 259 260 def buf(self, line): 261 """Buffer up a string to send later 262 263 Args: 264 line (str): String to add to our 'buffer' list 265 """ 266 self._lines.append(line) 267 268 def get_buf(self): 269 """Get the contents of the output buffer, and clear it 270 271 Returns: 272 list(str): The output buffer, which is then cleared for future use 273 """ 274 lines = self._lines 275 self._lines = [] 276 return lines 277 278 def out_header(self, outfile): 279 """Output a message indicating that this is an auto-generated file 280 281 Args: 282 outfile: OutputFile describing the file being generated 283 """ 284 self.out('''/* 285 * DO NOT MODIFY 286 * 287 * %s. 288 * This was generated by dtoc from a .dtb (device tree binary) file. 289 */ 290 291''' % outfile.hdr_comment) 292 293 def get_phandle_argc(self, prop, node_name): 294 """Check if a node contains phandles 295 296 We have no reliable way of detecting whether a node uses a phandle 297 or not. As an interim measure, use a list of known property names. 298 299 Args: 300 prop (fdt.Prop): Prop object to check 301 node_name (str): Node name, only used for raising an error 302 Returns: 303 int or None: Number of argument cells is this is a phandle, 304 else None 305 Raises: 306 ValueError: if the phandle cannot be parsed or the required property 307 is not present 308 """ 309 cells_prop = None 310 for name, cprop in PHANDLE_PROPS.items(): 311 if prop.name.endswith(name): 312 cells_prop = cprop 313 if cells_prop: 314 if not isinstance(prop.value, list): 315 prop.value = [prop.value] 316 val = prop.value 317 i = 0 318 319 max_args = 0 320 args = [] 321 while i < len(val): 322 phandle = fdt_util.fdt32_to_cpu(val[i]) 323 # If we get to the end of the list, stop. This can happen 324 # since some nodes have more phandles in the list than others, 325 # but we allocate enough space for the largest list. So those 326 # nodes with shorter lists end up with zeroes at the end. 327 if not phandle: 328 break 329 target = self._fdt.phandle_to_node.get(phandle) 330 if not target: 331 raise ValueError("Cannot parse '%s' in node '%s'" % 332 (prop.name, node_name)) 333 cells = target.props.get(cells_prop) 334 if not cells: 335 raise ValueError("Node '%s' has no cells property" % 336 target.name) 337 num_args = fdt_util.fdt32_to_cpu(cells.value) 338 max_args = max(max_args, num_args) 339 args.append(num_args) 340 i += 1 + num_args 341 return PhandleInfo(max_args, args) 342 return None 343 344 def scan_dtb(self): 345 """Scan the device tree to obtain a tree of nodes and properties 346 347 Once this is done, self._fdt.GetRoot() can be called to obtain the 348 device tree root node, and progress from there. 349 """ 350 self._fdt = fdt.FdtScan(self._dtb_fname) 351 352 def scan_node(self, node, valid_nodes): 353 """Scan a node and subnodes to build a tree of node and phandle info 354 355 This adds each subnode to self._valid_nodes if it is enabled and has a 356 compatible string. 357 358 Args: 359 node (Node): Node for scan for subnodes 360 valid_nodes (list of Node): List of Node objects to add to 361 """ 362 for subnode in node.subnodes: 363 if 'compatible' in subnode.props: 364 status = subnode.props.get('status') 365 if (not self._include_disabled and not status or 366 status.value != 'disabled'): 367 valid_nodes.append(subnode) 368 369 # recurse to handle any subnodes 370 self.scan_node(subnode, valid_nodes) 371 372 def scan_tree(self, add_root): 373 """Scan the device tree for useful information 374 375 This fills in the following properties: 376 _valid_nodes_unsorted: A list of nodes we wish to consider include 377 in the platform data (in devicetree node order) 378 _valid_nodes: Sorted version of _valid_nodes_unsorted 379 380 Args: 381 add_root: True to add the root node also (which wouldn't normally 382 be added as it may not have a compatible string) 383 """ 384 root = self._fdt.GetRoot() 385 valid_nodes = [] 386 if add_root: 387 valid_nodes.append(root) 388 self.scan_node(root, valid_nodes) 389 self._valid_nodes_unsorted = valid_nodes 390 self._valid_nodes = sorted(valid_nodes, 391 key=lambda x: conv_name_to_c(x.name)) 392 393 def prepare_nodes(self): 394 """Add extra properties to the nodes we are using 395 396 The following properties are added for use by dtoc: 397 idx: Index number of this node (0=first, etc.) 398 struct_name: Name of the struct dtd used by this node 399 var_name: C name for this node 400 child_devs: List of child devices for this node, each a None 401 child_refs: Dict of references for each child: 402 key: Position in child list (-1=head, 0=first, 1=second, ... 403 n-1=last, n=head) 404 seq: Sequence number of the device (unique within its uclass), or 405 -1 not not known yet 406 dev_ref: Reference to this device, e.g. 'DM_DEVICE_REF(serial)' 407 driver: Driver record for this node, or None if not known 408 uclass: Uclass record for this node, or None if not known 409 uclass_seq: Position of this device within the uclass list (0=first, 410 n-1=last) 411 parent_seq: Position of this device within it siblings (0=first, 412 n-1=last) 413 parent_driver: Driver record of the node's parent, or None if none. 414 We don't use node.parent.driver since node.parent may not be in 415 the list of valid nodes 416 """ 417 for idx, node in enumerate(self._valid_nodes): 418 node.idx = idx 419 node.struct_name, _ = self._scan.get_normalized_compat_name(node) 420 node.var_name = conv_name_to_c(node.name) 421 node.child_devs = [] 422 node.child_refs = {} 423 node.seq = -1 424 node.dev_ref = None 425 node.driver = None 426 node.uclass = None 427 node.uclass_seq = None 428 node.parent_seq = None 429 node.parent_driver = None 430 431 @staticmethod 432 def get_num_cells(node): 433 """Get the number of cells in addresses and sizes for this node 434 435 Args: 436 node (fdt.None): Node to check 437 438 Returns: 439 Tuple: 440 Number of address cells for this node 441 Number of size cells for this node 442 """ 443 parent = node.parent 444 if parent and not parent.props: 445 raise ValueError("Parent node '%s' has no properties - do you need u-boot,dm-spl or similar?" % 446 parent.path) 447 num_addr, num_size = 2, 2 448 if parent: 449 addr_prop = parent.props.get('#address-cells') 450 size_prop = parent.props.get('#size-cells') 451 if addr_prop: 452 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value) 453 if size_prop: 454 num_size = fdt_util.fdt32_to_cpu(size_prop.value) 455 return num_addr, num_size 456 457 def scan_reg_sizes(self): 458 """Scan for 64-bit 'reg' properties and update the values 459 460 This finds 'reg' properties with 64-bit data and converts the value to 461 an array of 64-values. This allows it to be output in a way that the 462 C code can read. 463 """ 464 for node in self._valid_nodes: 465 reg = node.props.get('reg') 466 if not reg: 467 continue 468 num_addr, num_size = self.get_num_cells(node) 469 total = num_addr + num_size 470 471 if reg.type != fdt.Type.INT: 472 raise ValueError("Node '%s' reg property is not an int" % 473 node.name) 474 if not isinstance(reg.value, list): 475 reg.value = [reg.value] 476 if len(reg.value) % total: 477 raise ValueError( 478 "Node '%s' (parent '%s') reg property has %d cells " 479 'which is not a multiple of na + ns = %d + %d)' % 480 (node.name, node.parent.name, len(reg.value), num_addr, 481 num_size)) 482 reg.num_addr = num_addr 483 reg.num_size = num_size 484 if num_addr > 1 or num_size > 1: 485 reg.type = fdt.Type.INT64 486 i = 0 487 new_value = [] 488 val = reg.value 489 while i < len(val): 490 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr) 491 i += num_addr 492 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size) 493 i += num_size 494 new_value += [addr, size] 495 reg.value = new_value 496 497 def scan_structs(self): 498 """Scan the device tree building up the C structures we will use. 499 500 Build a dict keyed by C struct name containing a dict of Prop 501 object for each struct field (keyed by property name). Where the 502 same struct appears multiple times, try to use the 'widest' 503 property, i.e. the one with a type which can express all others. 504 505 Once the widest property is determined, all other properties are 506 updated to match that width. 507 508 The results are written to self._struct_data 509 """ 510 structs = self._struct_data 511 for node in self._valid_nodes: 512 fields = {} 513 514 # Get a list of all the valid properties in this node. 515 for name, prop in node.props.items(): 516 if name not in PROP_IGNORE_LIST and name[0] != '#': 517 fields[name] = copy.deepcopy(prop) 518 519 # If we've seen this struct_name before, update the existing struct 520 if node.struct_name in structs: 521 struct = structs[node.struct_name] 522 for name, prop in fields.items(): 523 oldprop = struct.get(name) 524 if oldprop: 525 oldprop.Widen(prop) 526 else: 527 struct[name] = prop 528 529 # Otherwise store this as a new struct. 530 else: 531 structs[node.struct_name] = fields 532 533 for node in self._valid_nodes: 534 struct = structs[node.struct_name] 535 for name, prop in node.props.items(): 536 if name not in PROP_IGNORE_LIST and name[0] != '#': 537 prop.Widen(struct[name]) 538 539 def scan_phandles(self): 540 """Figure out what phandles each node uses 541 542 We need to be careful when outputing nodes that use phandles since 543 they must come after the declaration of the phandles in the C file. 544 Otherwise we get a compiler error since the phandle struct is not yet 545 declared. 546 547 This function adds to each node a list of phandle nodes that the node 548 depends on. This allows us to output things in the right order. 549 """ 550 for node in self._valid_nodes: 551 node.phandles = set() 552 for pname, prop in node.props.items(): 553 if pname in PROP_IGNORE_LIST or pname[0] == '#': 554 continue 555 info = self.get_phandle_argc(prop, node.name) 556 if info: 557 # Process the list as pairs of (phandle, id) 558 pos = 0 559 for args in info.args: 560 phandle_cell = prop.value[pos] 561 phandle = fdt_util.fdt32_to_cpu(phandle_cell) 562 target_node = self._fdt.phandle_to_node[phandle] 563 node.phandles.add(target_node) 564 pos += 1 + args 565 566 567 def generate_structs(self): 568 """Generate struct defintions for the platform data 569 570 This writes out the body of a header file consisting of structure 571 definitions for node in self._valid_nodes. See the documentation in 572 doc/driver-model/of-plat.rst for more information. 573 """ 574 structs = self._struct_data 575 self.out('#include <stdbool.h>\n') 576 self.out('#include <linux/libfdt.h>\n') 577 578 # Output the struct definition 579 for name in sorted(structs): 580 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name)) 581 for pname in sorted(structs[name]): 582 prop = structs[name][pname] 583 info = self.get_phandle_argc(prop, structs[name]) 584 if info: 585 # For phandles, include a reference to the target 586 struct_name = 'struct phandle_%d_arg' % info.max_args 587 self.out('\t%s%s[%d]' % (tab_to(2, struct_name), 588 conv_name_to_c(prop.name), 589 len(info.args))) 590 else: 591 ptype = TYPE_NAMES[prop.type] 592 self.out('\t%s%s' % (tab_to(2, ptype), 593 conv_name_to_c(prop.name))) 594 if isinstance(prop.value, list): 595 self.out('[%d]' % len(prop.value)) 596 self.out(';\n') 597 self.out('};\n') 598 599 def _output_list(self, node, prop): 600 """Output the C code for a devicetree property that holds a list 601 602 Args: 603 node (fdt.Node): Node to output 604 prop (fdt.Prop): Prop to output 605 """ 606 self.buf('{') 607 vals = [] 608 # For phandles, output a reference to the platform data 609 # of the target node. 610 info = self.get_phandle_argc(prop, node.name) 611 if info: 612 # Process the list as pairs of (phandle, id) 613 pos = 0 614 for args in info.args: 615 phandle_cell = prop.value[pos] 616 phandle = fdt_util.fdt32_to_cpu(phandle_cell) 617 target_node = self._fdt.phandle_to_node[phandle] 618 arg_values = [] 619 for i in range(args): 620 arg_values.append( 621 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i]))) 622 pos += 1 + args 623 vals.append('\t{%d, {%s}}' % (target_node.idx, 624 ', '.join(arg_values))) 625 for val in vals: 626 self.buf('\n\t\t%s,' % val) 627 else: 628 for val in prop.value: 629 vals.append(get_value(prop.type, val)) 630 631 # Put 8 values per line to avoid very long lines. 632 for i in range(0, len(vals), 8): 633 if i: 634 self.buf(',\n\t\t') 635 self.buf(', '.join(vals[i:i + 8])) 636 self.buf('}') 637 638 def _declare_device(self, node): 639 """Add a device declaration to the output 640 641 This declares a U_BOOT_DRVINFO() for the device being processed 642 643 Args: 644 node: Node to process 645 """ 646 self.buf('U_BOOT_DRVINFO(%s) = {\n' % node.var_name) 647 self.buf('\t.name\t\t= "%s",\n' % node.struct_name) 648 self.buf('\t.plat\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name)) 649 self.buf('\t.plat_size\t= sizeof(%s%s),\n' % 650 (VAL_PREFIX, node.var_name)) 651 idx = -1 652 if node.parent and node.parent in self._valid_nodes: 653 idx = node.parent.idx 654 self.buf('\t.parent_idx\t= %d,\n' % idx) 655 self.buf('};\n') 656 self.buf('\n') 657 658 def prep_priv(self, struc, name, suffix, section='.priv_data'): 659 if not struc: 660 return None 661 var_name = '_%s%s' % (name, suffix) 662 hdr = self._scan._structs.get(struc) 663 if hdr: 664 self.buf('#include <%s>\n' % hdr.fname) 665 else: 666 print('Warning: Cannot find header file for struct %s' % struc) 667 attr = '__attribute__ ((section ("%s")))' % section 668 return var_name, struc, attr 669 670 def alloc_priv(self, info, name, extra, suffix='_priv'): 671 result = self.prep_priv(info, name, suffix) 672 if not result: 673 return None 674 var_name, struc, section = result 675 self.buf('u8 %s_%s[sizeof(struct %s)]\n\t%s;\n' % 676 (var_name, extra, struc.strip(), section)) 677 return '%s_%s' % (var_name, extra) 678 679 def alloc_plat(self, info, name, extra, node): 680 result = self.prep_priv(info, name, '_plat') 681 if not result: 682 return None 683 var_name, struc, section = result 684 self.buf('struct %s %s\n\t%s_%s = {\n' % 685 (struc.strip(), section, var_name, extra)) 686 self.buf('\t.dtplat = {\n') 687 for pname in sorted(node.props): 688 self._output_prop(node, node.props[pname], 2) 689 self.buf('\t},\n') 690 self.buf('};\n') 691 return '&%s_%s' % (var_name, extra) 692 693 def _declare_device_inst(self, node, parent_driver): 694 """Add a device instance declaration to the output 695 696 This declares a DM_DEVICE_INST() for the device being processed 697 698 Args: 699 node: Node to output 700 """ 701 driver = node.driver 702 uclass = node.uclass 703 self.buf('\n') 704 num_lines = len(self._lines) 705 plat_name = self.alloc_plat(driver.plat, driver.name, node.var_name, 706 node) 707 priv_name = self.alloc_priv(driver.priv, driver.name, node.var_name) 708 parent_plat_name = None 709 parent_priv_name = None 710 if parent_driver: 711 # TODO: deal with uclass providing these values 712 parent_plat_name = self.alloc_priv( 713 parent_driver.child_plat, driver.name, node.var_name, 714 '_parent_plat') 715 parent_priv_name = self.alloc_priv( 716 parent_driver.child_priv, driver.name, node.var_name, 717 '_parent_priv') 718 uclass_plat_name = self.alloc_priv( 719 uclass.per_dev_plat, driver.name + '_uc', node.var_name, 'plat') 720 uclass_priv_name = self.alloc_priv(uclass.per_dev_priv, 721 driver.name + '_uc', node.var_name) 722 for hdr in driver.headers: 723 self.buf('#include %s\n' % hdr) 724 725 # Add a blank line if we emitted any stuff above, for readability 726 if num_lines != len(self._lines): 727 self.buf('\n') 728 729 self.buf('DM_DEVICE_INST(%s) = {\n' % node.var_name) 730 self.buf('\t.driver\t\t= DM_DRIVER_REF(%s),\n' % node.struct_name) 731 self.buf('\t.name\t\t= "%s",\n' % node.struct_name) 732 if plat_name: 733 self.buf('\t.plat_\t\t= %s,\n' % plat_name) 734 else: 735 self.buf('\t.plat_\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name)) 736 if parent_plat_name: 737 self.buf('\t.parent_plat_\t= %s,\n' % parent_plat_name) 738 if uclass_plat_name: 739 self.buf('\t.uclass_plat_\t= %s,\n' % uclass_plat_name) 740 driver_date = None 741 742 if node != self._fdt.GetRoot(): 743 compat_list = node.props['compatible'].value 744 if not isinstance(compat_list, list): 745 compat_list = [compat_list] 746 for compat in compat_list: 747 driver_data = driver.compat.get(compat) 748 if driver_data: 749 self.buf('\t.driver_data\t= %s,\n' % driver_data) 750 break 751 752 if node.parent and node.parent.parent: 753 if node.parent not in self._valid_nodes: 754 # This might indicate that the parent node is not in the 755 # SPL/TPL devicetree but the child is. For example if we are 756 # dealing with of-platdata in TPL, the parent has a 757 # u-boot,dm-tpl tag but the child has u-boot,dm-pre-reloc. In 758 # this case the child node exists in TPL but the parent does 759 # not. 760 raise ValueError("Node '%s' requires parent node '%s' but it is not in the valid list" % 761 (node.path, node.parent.path)) 762 self.buf('\t.parent\t\t= DM_DEVICE_REF(%s),\n' % 763 node.parent.var_name) 764 if priv_name: 765 self.buf('\t.priv_\t\t= %s,\n' % priv_name) 766 self.buf('\t.uclass\t\t= DM_UCLASS_REF(%s),\n' % uclass.name) 767 768 if uclass_priv_name: 769 self.buf('\t.uclass_priv_ = %s,\n' % uclass_priv_name) 770 if parent_priv_name: 771 self.buf('\t.parent_priv_\t= %s,\n' % parent_priv_name) 772 self.list_node('uclass_node', uclass.node_refs, node.uclass_seq) 773 self.list_head('child_head', 'sibling_node', node.child_devs, node.var_name) 774 if node.parent in self._valid_nodes: 775 self.list_node('sibling_node', node.parent.child_refs, 776 node.parent_seq) 777 # flags is left as 0 778 779 self.buf('\t.seq_ = %d,\n' % node.seq) 780 781 self.buf('};\n') 782 self.buf('\n') 783 return parent_plat_name 784 785 def _output_prop(self, node, prop, tabs=1): 786 """Output a line containing the value of a struct member 787 788 Args: 789 node (Node): Node being output 790 prop (Prop): Prop object to output 791 """ 792 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#': 793 return 794 member_name = conv_name_to_c(prop.name) 795 self.buf('%s%s= ' % ('\t' * tabs, tab_to(3, '.' + member_name))) 796 797 # Special handling for lists 798 if isinstance(prop.value, list): 799 self._output_list(node, prop) 800 else: 801 self.buf(get_value(prop.type, prop.value)) 802 self.buf(',\n') 803 804 def _output_values(self, node): 805 """Output the definition of a device's struct values 806 807 Args: 808 node (Node): Node to output 809 """ 810 self.buf('static struct %s%s %s%s = {\n' % 811 (STRUCT_PREFIX, node.struct_name, VAL_PREFIX, node.var_name)) 812 for pname in sorted(node.props): 813 self._output_prop(node, node.props[pname]) 814 self.buf('};\n') 815 816 def list_head(self, head_member, node_member, node_refs, var_name): 817 self.buf('\t.%s\t= {\n' % head_member) 818 if node_refs: 819 last = node_refs[-1].dev_ref 820 first = node_refs[0].dev_ref 821 member = node_member 822 else: 823 last = 'DM_DEVICE_REF(%s)' % var_name 824 first = last 825 member = head_member 826 self.buf('\t\t.prev = &%s->%s,\n' % (last, member)) 827 self.buf('\t\t.next = &%s->%s,\n' % (first, member)) 828 self.buf('\t},\n') 829 830 def list_node(self, member, node_refs, seq): 831 self.buf('\t.%s\t= {\n' % member) 832 self.buf('\t\t.prev = %s,\n' % node_refs[seq - 1]) 833 self.buf('\t\t.next = %s,\n' % node_refs[seq + 1]) 834 self.buf('\t},\n') 835 836 def generate_uclasses(self): 837 self.out('\n') 838 self.out('#include <common.h>\n') 839 self.out('#include <dm.h>\n') 840 self.out('#include <dt-structs.h>\n') 841 self.out('\n') 842 self.buf('/*\n') 843 self.buf( 844 " * uclass declarations, ordered by 'struct uclass' linker_list idx:\n") 845 uclass_list = self._valid_uclasses 846 for seq, uclass in enumerate(uclass_list): 847 self.buf(' * %3d: %s\n' % (seq, uclass.name)) 848 self.buf(' *\n') 849 self.buf(' * Sequence numbers allocated in each uclass:\n') 850 for uclass in uclass_list: 851 if uclass.alias_num_to_node: 852 self.buf(' * %s: %s\n' % (uclass.name, uclass.uclass_id)) 853 for seq, node in uclass.alias_num_to_node.items(): 854 self.buf(' * %d: %s\n' % (seq, node.path)) 855 self.buf(' */\n') 856 857 uclass_node = {} 858 for seq, uclass in enumerate(uclass_list): 859 uclass_node[seq] = ('&DM_UCLASS_REF(%s)->sibling_node' % 860 uclass.name) 861 uclass_node[-1] = '&uclass_head' 862 uclass_node[len(uclass_list)] = '&uclass_head' 863 self.buf('\n') 864 self.buf('struct list_head %s = {\n' % 'uclass_head') 865 self.buf('\t.prev = %s,\n' % uclass_node[len(uclass_list) -1]) 866 self.buf('\t.next = %s,\n' % uclass_node[0]) 867 self.buf('};\n') 868 self.buf('\n') 869 870 for seq, uclass in enumerate(uclass_list): 871 uc_drv = self._scan._uclass.get(uclass.uclass_id) 872 873 priv_name = self.alloc_priv(uc_drv.priv, uc_drv.name, '') 874 875 self.buf('DM_UCLASS_INST(%s) = {\n' % uclass.name) 876 if priv_name: 877 self.buf('\t.priv_\t\t= %s,\n' % priv_name) 878 self.buf('\t.uc_drv\t\t= DM_UCLASS_DRIVER_REF(%s),\n' % uclass.name) 879 self.list_node('sibling_node', uclass_node, seq) 880 self.list_head('dev_head', 'uclass_node', uc_drv.devs, None) 881 self.buf('};\n') 882 self.buf('\n') 883 self.out(''.join(self.get_buf())) 884 885 def read_aliases(self): 886 """Read the aliases and attach the information to self._alias 887 888 Raises: 889 ValueError: The alias path is not found 890 """ 891 alias_node = self._fdt.GetNode('/aliases') 892 if not alias_node: 893 return 894 re_num = re.compile('(^[a-z0-9-]+[a-z]+)([0-9]+)$') 895 for prop in alias_node.props.values(): 896 m_alias = re_num.match(prop.name) 897 if not m_alias: 898 raise ValueError("Cannot decode alias '%s'" % prop.name) 899 name, num = m_alias.groups() 900 node = self._fdt.GetNode(prop.value) 901 result = self._scan.add_uclass_alias(name, num, node) 902 if result is None: 903 raise ValueError("Alias '%s' path '%s' not found" % 904 (prop.name, prop.value)) 905 elif result is False: 906 print("Could not find uclass for alias '%s'" % prop.name) 907 908 def generate_decl(self): 909 nodes_to_output = list(self._valid_nodes) 910 911 self.buf('#include <dm/device-internal.h>\n') 912 self.buf('#include <dm/uclass-internal.h>\n') 913 self.buf('\n') 914 self.buf( 915 '/* driver declarations - these allow DM_DRIVER_GET() to be used */\n') 916 for node in nodes_to_output: 917 self.buf('extern U_BOOT_DRIVER(%s);\n' % node.struct_name); 918 self.buf('\n') 919 920 if self._instantiate: 921 self.buf( 922 '/* device declarations - these allow DM_DEVICE_REF() to be used */\n') 923 for node in nodes_to_output: 924 self.buf('extern DM_DEVICE_INST(%s);\n' % node.var_name) 925 self.buf('\n') 926 927 uclass_list = self._valid_uclasses 928 929 self.buf( 930 '/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */\n') 931 for uclass in uclass_list: 932 self.buf('extern UCLASS_DRIVER(%s);\n' % uclass.name) 933 934 if self._instantiate: 935 self.buf('\n') 936 self.buf('/* uclass declarations - needed for DM_UCLASS_REF() */\n') 937 for uclass in uclass_list: 938 self.buf('extern DM_UCLASS_INST(%s);\n' % uclass.name) 939 self.out(''.join(self.get_buf())) 940 941 def assign_seqs(self): 942 """Assign a sequence number to each node""" 943 for node in self._valid_nodes_unsorted: 944 seq = self._scan.assign_seq(node) 945 if seq is not None: 946 node.seq = seq 947 948 def process_nodes(self, need_drivers): 949 nodes_to_output = list(self._valid_nodes) 950 951 # Figure out which drivers we actually use 952 self._scan.mark_used(nodes_to_output) 953 954 for node in nodes_to_output: 955 node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name 956 driver = self._scan.get_driver(node.struct_name) 957 if not driver: 958 if not need_drivers: 959 continue 960 raise ValueError("Cannot parse/find driver for '%s'" % 961 node.struct_name) 962 node.driver = driver 963 uclass = self._scan._uclass.get(driver.uclass_id) 964 if not uclass: 965 raise ValueError("Cannot parse/find uclass '%s' for driver '%s'" % 966 (driver.uclass_id, node.struct_name)) 967 node.uclass = uclass 968 node.uclass_seq = len(node.uclass.devs) 969 node.uclass.devs.append(node) 970 uclass.node_refs[node.uclass_seq] = \ 971 '&%s->uclass_node' % node.dev_ref 972 973 parent_driver = None 974 if node.parent in self._valid_nodes: 975 parent_driver = self._scan.get_driver(node.parent.struct_name) 976 if not parent_driver: 977 if not need_drivers: 978 continue 979 raise ValueError( 980 "Cannot parse/find parent driver '%s' for '%s'" % 981 (node.parent.struct_name, node.struct_name)) 982 node.parent_seq = len(node.parent.child_devs) 983 node.parent.child_devs.append(node) 984 node.parent.child_refs[node.parent_seq] = \ 985 '&%s->sibling_node' % node.dev_ref 986 node.parent_driver = parent_driver 987 988 for node in nodes_to_output: 989 ref = '&%s->child_head' % node.dev_ref 990 node.child_refs[-1] = ref 991 node.child_refs[len(node.child_devs)] = ref 992 993 uclass_set = set() 994 for driver in self._scan._drivers.values(): 995 if driver.used and driver.uclass: 996 uclass_set.add(driver.uclass) 997 self._valid_uclasses = sorted(list(uclass_set), 998 key=lambda uc: uc.uclass_id) 999 1000 for seq, uclass in enumerate(uclass_set):
1001 ref = '&DM_UCLASS_REF(%s)->dev_head' % uclass.name 1002 uclass.node_refs[-1] = ref 1003 uclass.node_refs[len(uclass.devs)] = ref 1004 1005 def output_node_plat(self, node): 1006 """Output the C code for a node 1007 1008 Args: 1009 node (fdt.Node): node to output 1010 """ 1011 driver = node.driver 1012 parent_driver = node.parent_driver 1013 1014 line1 = 'Node %s index %d' % (node.path, node.idx) 1015 if driver: 1016 self.buf('/*\n') 1017 self.buf(' * %s\n' % line1) 1018 self.buf(' * driver %s parent %s\n' % (driver.name, 1019 parent_driver.name if parent_driver else 'None')) 1020 self.buf(' */\n') 1021 else: 1022 self.buf('/* %s */\n' % line1) 1023 1024 self._output_values(node) 1025 self._declare_device(node) 1026 1027 self.out(''.join(self.get_buf())) 1028 1029 def output_node_instance(self, node): 1030 """Output the C code for a node 1031 1032 Args: 1033 node (fdt.Node): node to output 1034 """ 1035 parent_driver = node.parent_driver 1036 1037 self.buf('/*\n') 1038 self.buf(' * Node %s index %d\n' % (node.path, node.idx)) 1039 self.buf(' * driver %s parent %s\n' % (node.driver.name, 1040 parent_driver.name if parent_driver else 'None')) 1041 self.buf('*/\n') 1042 1043 if not node.driver.plat: 1044 self._output_values(node) 1045 self._declare_device_inst(node, parent_driver) 1046 1047 self.out(''.join(self.get_buf())) 1048 1049 def generate_plat(self): 1050 """Generate device defintions for the platform data 1051 1052 This writes out C platform data initialisation data and 1053 U_BOOT_DRVINFO() declarations for each valid node. Where a node has 1054 multiple compatible strings, a #define is used to make them equivalent. 1055 1056 See the documentation in doc/driver-model/of-plat.rst for more 1057 information. 1058 """ 1059 self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n') 1060 self.out('#define DT_PLAT_C\n') 1061 self.out('\n') 1062 self.out('#include <common.h>\n') 1063 self.out('#include <dm.h>\n') 1064 self.out('#include <dt-structs.h>\n') 1065 self.out('\n') 1066 1067 if self._valid_nodes: 1068 self.out('/*\n') 1069 self.out( 1070 " * driver_info declarations, ordered by 'struct driver_info' linker_list idx:\n") 1071 self.out(' *\n') 1072 self.out(' * idx %-20s %-s\n' % ('driver_info', 'driver')) 1073 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20)) 1074 for node in self._valid_nodes: 1075 self.out(' * %3d: %-20s %-s\n' % 1076 (node.idx, node.var_name, node.struct_name)) 1077 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20)) 1078 self.out(' */\n') 1079 self.out('\n') 1080 1081 for node in self._valid_nodes: 1082 self.output_node_plat(node) 1083 1084 self.out(''.join(self.get_buf())) 1085 1086 def generate_device(self): 1087 """Generate device instances 1088 1089 This writes out DM_DEVICE_INST() records for each device in the 1090 build. 1091 1092 See the documentation in doc/driver-model/of-plat.rst for more 1093 information. 1094 """ 1095 self.out('#include <common.h>\n') 1096 self.out('#include <dm.h>\n') 1097 self.out('#include <dt-structs.h>\n') 1098 self.out('\n') 1099 1100 if self._valid_nodes: 1101 self.out('/*\n') 1102 self.out( 1103 " * udevice declarations, ordered by 'struct udevice' linker_list position:\n") 1104 self.out(' *\n') 1105 self.out(' * idx %-20s %-s\n' % ('udevice', 'driver')) 1106 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20)) 1107 for node in self._valid_nodes: 1108 self.out(' * %3d: %-20s %-s\n' % 1109 (node.idx, node.var_name, node.struct_name)) 1110 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20)) 1111 self.out(' */\n') 1112 self.out('\n') 1113 1114 for node in self._valid_nodes: 1115 self.output_node_instance(node) 1116 1117 self.out(''.join(self.get_buf())) 1118 1119 1120# Types of output file we understand 1121# key: Command used to generate this file 1122# value: OutputFile for this command 1123OUTPUT_FILES_COMMON = { 1124 'decl': 1125 OutputFile(Ftype.HEADER, 'dt-decl.h', DtbPlatdata.generate_decl, 1126 'Declares externs for all device/uclass instances'), 1127 'struct': 1128 OutputFile(Ftype.HEADER, 'dt-structs-gen.h', 1129 DtbPlatdata.generate_structs, 1130 'Defines the structs used to hold devicetree data'), 1131 } 1132 1133# File generated without instantiate 1134OUTPUT_FILES_NOINST = { 1135 'platdata': 1136 OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat, 1137 'Declares the U_BOOT_DRIVER() records and platform data'), 1138 } 1139 1140# File generated with instantiate 1141OUTPUT_FILES_INST = { 1142 'device': 1143 OutputFile(Ftype.SOURCE, 'dt-device.c', DtbPlatdata.generate_device, 1144 'Declares the DM_DEVICE_INST() records'), 1145 'uclass': 1146 OutputFile(Ftype.SOURCE, 'dt-uclass.c', DtbPlatdata.generate_uclasses, 1147 'Declares the uclass instances (struct uclass)'), 1148 } 1149 1150 1151def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase, 1152 instantiate, warning_disabled=False, drivers_additional=None, 1153 basedir=None, scan=None): 1154 """Run all the steps of the dtoc tool 1155 1156 Args: 1157 args (list): List of non-option arguments provided to the problem 1158 dtb_file (str): Filename of dtb file to process 1159 include_disabled (bool): True to include disabled nodes 1160 output (str): Name of output file (None for stdout) 1161 output_dirs (tuple of str): 1162 Directory to put C output files 1163 Directory to put H output files 1164 phase: The phase of U-Boot that we are generating data for, e.g. 'spl' 1165 or 'tpl'. None if not known 1166 instantiate: Instantiate devices so they don't need to be bound at 1167 run-time 1168 warning_disabled (bool): True to avoid showing warnings about missing 1169 drivers 1170 drivers_additional (list): List of additional drivers to use during 1171 scanning 1172 basedir (str): Base directory of U-Boot source code. Defaults to the 1173 grandparent of this file's directory 1174 scan (src_src.Scanner): Scanner from a previous run. This can help speed 1175 up tests. Use None for normal operation 1176 1177 Returns: 1178 DtbPlatdata object 1179 1180 Raises: 1181 ValueError: if args has no command, or an unknown command 1182 """ 1183 if not args: 1184 raise ValueError('Please specify a command: struct, platdata, all') 1185 if output and output_dirs and any(output_dirs): 1186 raise ValueError('Must specify either output or output_dirs, not both') 1187 1188 if not scan: 1189 scan = src_scan.Scanner(basedir, drivers_additional, phase) 1190 scan.scan_drivers() 1191 do_process = True 1192 else: 1193 do_process = False 1194 plat = DtbPlatdata(scan, dtb_file, include_disabled, instantiate) 1195 plat.scan_dtb() 1196 plat.scan_tree(add_root=instantiate) 1197 plat.prepare_nodes() 1198 plat.scan_reg_sizes() 1199 plat.setup_output_dirs(output_dirs) 1200 plat.scan_structs() 1201 plat.scan_phandles() 1202 plat.process_nodes(instantiate) 1203 plat.read_aliases() 1204 plat.assign_seqs() 1205 1206 # Figure out what output files we plan to generate 1207 output_files = dict(OUTPUT_FILES_COMMON) 1208 if instantiate: 1209 output_files.update(OUTPUT_FILES_INST) 1210 else: 1211 output_files.update(OUTPUT_FILES_NOINST) 1212 1213 cmds = args[0].split(',') 1214 if 'all' in cmds: 1215 cmds = sorted(output_files.keys()) 1216 for cmd in cmds: 1217 outfile = output_files.get(cmd) 1218 if not outfile: 1219 raise ValueError("Unknown command '%s': (use: %s)" % 1220 (cmd, ', '.join(sorted(output_files.keys())))) 1221 plat.setup_output(outfile.ftype, 1222 outfile.fname if output_dirs else output) 1223 plat.out_header(outfile) 1224 outfile.method(plat) 1225 plat.finish_output() 1226 1227 if not warning_disabled: 1228 scan.show_warnings() 1229 return plat 1230

