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