2 # SPDX-License-Identifier: GPL-2.0+
4 # Copyright (C) 2017 Google, Inc
5 # Written by Simon Glass <sjg@chromium.org>
8 """Device tree to platform data class
10 This supports converting device tree data to C structures definitions and
13 See doc/driver-model/of-plat.rst for more informaiton
18 from enum import IntEnum
24 from dtoc import fdt_util
25 from dtoc import src_scan
26 from dtoc.src_scan import conv_name_to_c
28 # When we see these properties we ignore them - i.e. do not create a structure
38 'u-boot,dm-pre-reloc',
43 # C type declarations for the types we support
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',
52 STRUCT_PREFIX = 'dtd_'
55 # Properties which are considered to be phandles
57 # value: name of associated #cells property in the target node
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
64 'clocks': '#clock-cells',
65 'gpios': '#gpio-cells',
66 'sandbox,emul': '#emul-cells',
70 SOURCE, HEADER = range(2)
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
77 OutputFile = collections.namedtuple('OutputFile',
78 ['ftype', 'fname', 'method', 'hdr_comment'])
80 # This holds information about a property which includes phandles.
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.
85 PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
87 # Holds a single phandle link, allowing a C struct value to be assigned to point
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')
92 PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name'])
95 def tab_to(num_tabs, line):
96 """Append tabs to a line of text to reach a tab stop.
99 num_tabs (int): Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
100 line (str): Line of text to append to
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.
106 if len(line) >= num_tabs * 8:
108 return line + '\t' * (num_tabs - len(line) // 8)
110 def get_value(ftype, value):
111 """Get a value as a C expression
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'
119 ftype (fdt.Type): Data type (fdt_util)
120 value (bytes): Data value, as a string of bytes
123 str: String representation of the value
125 if ftype == fdt.Type.INT:
126 val = '%#x' % fdt_util.fdt32_to_cpu(value)
127 elif ftype == fdt.Type.BYTE:
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:
136 else: # ftype == fdt.Type.INT64:
142 """Provide a means to convert device tree binary data to platform data
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.
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
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
173 def __init__(self, scan, dtb_fname, include_disabled, instantiate=False):
176 self._dtb_fname = dtb_fname
177 self._valid_nodes = None
178 self._valid_nodes_unsorted = None
179 self._include_disabled = include_disabled
182 self._dirnames = [None] * len(Ftype)
183 self._struct_data = collections.OrderedDict()
185 self._valid_uclasses = None
186 self._instantiate = instantiate
188 def setup_output_dirs(self, output_dirs):
189 """Set up the output directories
191 This should be done before setup_output() is called
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
200 def process_dir(ftype, dirname):
202 os.makedirs(dirname, exist_ok=True)
203 self._dirnames[ftype] = dirname
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)
211 def setup_output(self, ftype, fname):
212 """Set up the output destination
214 Once this is done, future calls to self.out() will output to this
215 file. The file used is as follows:
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
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.
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
230 dirname = self._dirnames[ftype]
232 pathname = os.path.join(dirname, fname)
234 self._outfile.close()
235 self._outfile = open(pathname, 'w')
237 if not self._outfile:
238 self._outfile = open(fname, 'w')
240 self._outfile = sys.stdout
242 def finish_output(self):
243 """Finish outputing to a file
245 This closes the output file, if one is in use
247 if self._outfile != sys.stdout:
248 self._outfile.close()
252 """Output a string to the output file
255 line (str): String to output
257 self._outfile.write(line)
260 """Buffer up a string to send later
263 line (str): String to add to our 'buffer' list
265 self._lines.append(line)
268 """Get the contents of the output buffer, and clear it
271 list(str): The output buffer, which is then cleared for future use
277 def out_header(self, outfile):
278 """Output a message indicating that this is an auto-generated file
281 outfile: OutputFile describing the file being generated
287 * This was generated by dtoc from a .dtb (device tree binary) file.
290 ''' % outfile.hdr_comment)
292 def get_phandle_argc(self, prop, node_name):
293 """Check if a node contains phandles
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.
299 prop (fdt.Prop): Prop object to check
300 node_name (str): Node name, only used for raising an error
302 int or None: Number of argument cells is this is a phandle,
305 ValueError: if the phandle cannot be parsed or the required property
309 for name, cprop in PHANDLE_PROPS.items():
310 if prop.name.endswith(name):
313 if not isinstance(prop.value, list):
314 prop.value = [prop.value]
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.
328 target = self._fdt.phandle_to_node.get(phandle)
330 raise ValueError("Cannot parse '%s' in node '%s'" %
331 (prop.name, node_name))
332 cells = target.props.get(cells_prop)
334 raise ValueError("Node '%s' has no cells property" %
336 num_args = fdt_util.fdt32_to_cpu(cells.value)
337 max_args = max(max_args, num_args)
338 args.append(num_args)
340 return PhandleInfo(max_args, args)
344 """Scan the device tree to obtain a tree of nodes and properties
346 Once this is done, self._fdt.GetRoot() can be called to obtain the
347 device tree root node, and progress from there.
349 self._fdt = fdt.FdtScan(self._dtb_fname)
351 def scan_node(self, node, valid_nodes):
352 """Scan a node and subnodes to build a tree of node and phandle info
354 This adds each subnode to self._valid_nodes if it is enabled and has a
358 node (Node): Node for scan for subnodes
359 valid_nodes (list of Node): List of Node objects to add to
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)
368 # recurse to handle any subnodes
369 self.scan_node(subnode, valid_nodes)
371 def scan_tree(self, add_root):
372 """Scan the device tree for useful information
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
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)
383 root = self._fdt.GetRoot()
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))
392 def prepare_nodes(self):
393 """Add extra properties to the nodes we are using
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, ...
403 seq: Sequence number of the device (unique within its uclass), or
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,
410 parent_seq: Position of this device within it siblings (0=first,
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
416 for idx, node in enumerate(self._valid_nodes):
418 node.struct_name, _ = self._scan.get_normalized_compat_name(node)
419 node.var_name = conv_name_to_c(node.name)
426 node.uclass_seq = None
427 node.parent_seq = None
428 node.parent_driver = None
431 def get_num_cells(node):
432 """Get the number of cells in addresses and sizes for this node
435 node (fdt.None): Node to check
439 Number of address cells for this node
440 Number of size cells for this node
443 num_addr, num_size = 2, 2
445 addr_prop = parent.props.get('#address-cells')
446 size_prop = parent.props.get('#size-cells')
448 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
450 num_size = fdt_util.fdt32_to_cpu(size_prop.value)
451 return num_addr, num_size
453 def scan_reg_sizes(self):
454 """Scan for 64-bit 'reg' properties and update the values
456 This finds 'reg' properties with 64-bit data and converts the value to
457 an array of 64-values. This allows it to be output in a way that the
460 for node in self._valid_nodes:
461 reg = node.props.get('reg')
464 num_addr, num_size = self.get_num_cells(node)
465 total = num_addr + num_size
467 if reg.type != fdt.Type.INT:
468 raise ValueError("Node '%s' reg property is not an int" %
470 if len(reg.value) % total:
472 "Node '%s' reg property has %d cells "
473 'which is not a multiple of na + ns = %d + %d)' %
474 (node.name, len(reg.value), num_addr, num_size))
475 reg.num_addr = num_addr
476 reg.num_size = num_size
477 if num_addr != 1 or num_size != 1:
478 reg.type = fdt.Type.INT64
482 if not isinstance(val, list):
485 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
487 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
489 new_value += [addr, size]
490 reg.value = new_value
492 def scan_structs(self):
493 """Scan the device tree building up the C structures we will use.
495 Build a dict keyed by C struct name containing a dict of Prop
496 object for each struct field (keyed by property name). Where the
497 same struct appears multiple times, try to use the 'widest'
498 property, i.e. the one with a type which can express all others.
500 Once the widest property is determined, all other properties are
501 updated to match that width.
503 The results are written to self._struct_data
505 structs = self._struct_data
506 for node in self._valid_nodes:
509 # Get a list of all the valid properties in this node.
510 for name, prop in node.props.items():
511 if name not in PROP_IGNORE_LIST and name[0] != '#':
512 fields[name] = copy.deepcopy(prop)
514 # If we've seen this struct_name before, update the existing struct
515 if node.struct_name in structs:
516 struct = structs[node.struct_name]
517 for name, prop in fields.items():
518 oldprop = struct.get(name)
524 # Otherwise store this as a new struct.
526 structs[node.struct_name] = fields
528 for node in self._valid_nodes:
529 struct = structs[node.struct_name]
530 for name, prop in node.props.items():
531 if name not in PROP_IGNORE_LIST and name[0] != '#':
532 prop.Widen(struct[name])
534 def scan_phandles(self):
535 """Figure out what phandles each node uses
537 We need to be careful when outputing nodes that use phandles since
538 they must come after the declaration of the phandles in the C file.
539 Otherwise we get a compiler error since the phandle struct is not yet
542 This function adds to each node a list of phandle nodes that the node
543 depends on. This allows us to output things in the right order.
545 for node in self._valid_nodes:
546 node.phandles = set()
547 for pname, prop in node.props.items():
548 if pname in PROP_IGNORE_LIST or pname[0] == '#':
550 info = self.get_phandle_argc(prop, node.name)
552 # Process the list as pairs of (phandle, id)
554 for args in info.args:
555 phandle_cell = prop.value[pos]
556 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
557 target_node = self._fdt.phandle_to_node[phandle]
558 node.phandles.add(target_node)
562 def generate_structs(self):
563 """Generate struct defintions for the platform data
565 This writes out the body of a header file consisting of structure
566 definitions for node in self._valid_nodes. See the documentation in
567 doc/driver-model/of-plat.rst for more information.
569 structs = self._struct_data
570 self.out('#include <stdbool.h>\n')
571 self.out('#include <linux/libfdt.h>\n')
573 # Output the struct definition
574 for name in sorted(structs):
575 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
576 for pname in sorted(structs[name]):
577 prop = structs[name][pname]
578 info = self.get_phandle_argc(prop, structs[name])
580 # For phandles, include a reference to the target
581 struct_name = 'struct phandle_%d_arg' % info.max_args
582 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
583 conv_name_to_c(prop.name),
586 ptype = TYPE_NAMES[prop.type]
587 self.out('\t%s%s' % (tab_to(2, ptype),
588 conv_name_to_c(prop.name)))
589 if isinstance(prop.value, list):
590 self.out('[%d]' % len(prop.value))
594 def _output_list(self, node, prop):
595 """Output the C code for a devicetree property that holds a list
598 node (fdt.Node): Node to output
599 prop (fdt.Prop): Prop to output
603 # For phandles, output a reference to the platform data
604 # of the target node.
605 info = self.get_phandle_argc(prop, node.name)
607 # Process the list as pairs of (phandle, id)
609 for args in info.args:
610 phandle_cell = prop.value[pos]
611 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
612 target_node = self._fdt.phandle_to_node[phandle]
614 for i in range(args):
616 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
618 vals.append('\t{%d, {%s}}' % (target_node.idx,
619 ', '.join(arg_values)))
621 self.buf('\n\t\t%s,' % val)
623 for val in prop.value:
624 vals.append(get_value(prop.type, val))
626 # Put 8 values per line to avoid very long lines.
627 for i in range(0, len(vals), 8):
630 self.buf(', '.join(vals[i:i + 8]))
633 def _declare_device(self, node):
634 """Add a device declaration to the output
636 This declares a U_BOOT_DRVINFO() for the device being processed
639 node: Node to process
641 self.buf('U_BOOT_DRVINFO(%s) = {\n' % node.var_name)
642 self.buf('\t.name\t\t= "%s",\n' % node.struct_name)
643 self.buf('\t.plat\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
644 self.buf('\t.plat_size\t= sizeof(%s%s),\n' %
645 (VAL_PREFIX, node.var_name))
647 if node.parent and node.parent in self._valid_nodes:
648 idx = node.parent.idx
649 self.buf('\t.parent_idx\t= %d,\n' % idx)
653 def prep_priv(self, struc, name, suffix, section='.priv_data'):
656 var_name = '_%s%s' % (name, suffix)
657 hdr = self._scan._structs.get(struc)
659 self.buf('#include <%s>\n' % hdr.fname)
661 print('Warning: Cannot find header file for struct %s' % struc)
662 attr = '__attribute__ ((section ("%s")))' % section
663 return var_name, struc, attr
665 def alloc_priv(self, info, name, extra, suffix='_priv'):
666 result = self.prep_priv(info, name, suffix)
669 var_name, struc, section = result
670 self.buf('u8 %s_%s[sizeof(struct %s)]\n\t%s;\n' %
671 (var_name, extra, struc.strip(), section))
672 return '%s_%s' % (var_name, extra)
674 def alloc_plat(self, info, name, extra, node):
675 result = self.prep_priv(info, name, '_plat')
678 var_name, struc, section = result
679 self.buf('struct %s %s\n\t%s_%s = {\n' %
680 (struc.strip(), section, var_name, extra))
681 self.buf('\t.dtplat = {\n')
682 for pname in sorted(node.props):
683 self._output_prop(node, node.props[pname], 2)
686 return '&%s_%s' % (var_name, extra)
688 def _declare_device_inst(self, node, parent_driver):
689 """Add a device instance declaration to the output
691 This declares a DM_DEVICE_INST() for the device being processed
699 num_lines = len(self._lines)
700 plat_name = self.alloc_plat(driver.plat, driver.name, node.var_name,
702 priv_name = self.alloc_priv(driver.priv, driver.name, node.var_name)
703 parent_plat_name = None
704 parent_priv_name = None
706 # TODO: deal with uclass providing these values
707 parent_plat_name = self.alloc_priv(
708 parent_driver.child_plat, driver.name, node.var_name,
710 parent_priv_name = self.alloc_priv(
711 parent_driver.child_priv, driver.name, node.var_name,
713 uclass_plat_name = self.alloc_priv(
714 uclass.per_dev_plat, driver.name + '_uc', node.var_name, 'plat')
715 uclass_priv_name = self.alloc_priv(uclass.per_dev_priv,
716 driver.name + '_uc', node.var_name)
717 for hdr in driver.headers:
718 self.buf('#include %s\n' % hdr)
720 # Add a blank line if we emitted any stuff above, for readability
721 if num_lines != len(self._lines):
724 self.buf('DM_DEVICE_INST(%s) = {\n' % node.var_name)
725 self.buf('\t.driver\t\t= DM_DRIVER_REF(%s),\n' % node.struct_name)
726 self.buf('\t.name\t\t= "%s",\n' % node.struct_name)
728 self.buf('\t.plat_\t\t= %s,\n' % plat_name)
730 self.buf('\t.plat_\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
732 self.buf('\t.parent_plat_\t= %s,\n' % parent_plat_name)
734 self.buf('\t.uclass_plat_\t= %s,\n' % uclass_plat_name)
737 if node != self._fdt.GetRoot():
738 compat_list = node.props['compatible'].value
739 if not isinstance(compat_list, list):
740 compat_list = [compat_list]
741 for compat in compat_list:
742 driver_data = driver.compat.get(compat)
744 self.buf('\t.driver_data\t= %s,\n' % driver_data)
747 if node.parent and node.parent.parent:
748 self.buf('\t.parent\t\t= DM_DEVICE_REF(%s),\n' %
749 node.parent.var_name)
751 self.buf('\t.priv_\t\t= %s,\n' % priv_name)
752 self.buf('\t.uclass\t\t= DM_UCLASS_REF(%s),\n' % uclass.name)
755 self.buf('\t.uclass_priv_ = %s,\n' % uclass_priv_name)
757 self.buf('\t.parent_priv_\t= %s,\n' % parent_priv_name)
758 self.list_node('uclass_node', uclass.node_refs, node.uclass_seq)
759 self.list_head('child_head', 'sibling_node', node.child_devs, node.var_name)
760 if node.parent in self._valid_nodes:
761 self.list_node('sibling_node', node.parent.child_refs,
765 self.buf('\t.seq_ = %d,\n' % node.seq)
769 return parent_plat_name
771 def _output_prop(self, node, prop, tabs=1):
772 """Output a line containing the value of a struct member
775 node (Node): Node being output
776 prop (Prop): Prop object to output
778 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
780 member_name = conv_name_to_c(prop.name)
781 self.buf('%s%s= ' % ('\t' * tabs, tab_to(3, '.' + member_name)))
783 # Special handling for lists
784 if isinstance(prop.value, list):
785 self._output_list(node, prop)
787 self.buf(get_value(prop.type, prop.value))
790 def _output_values(self, node):
791 """Output the definition of a device's struct values
794 node (Node): Node to output
796 self.buf('static struct %s%s %s%s = {\n' %
797 (STRUCT_PREFIX, node.struct_name, VAL_PREFIX, node.var_name))
798 for pname in sorted(node.props):
799 self._output_prop(node, node.props[pname])
802 def list_head(self, head_member, node_member, node_refs, var_name):
803 self.buf('\t.%s\t= {\n' % head_member)
805 last = node_refs[-1].dev_ref
806 first = node_refs[0].dev_ref
809 last = 'DM_DEVICE_REF(%s)' % var_name
812 self.buf('\t\t.prev = &%s->%s,\n' % (last, member))
813 self.buf('\t\t.next = &%s->%s,\n' % (first, member))
816 def list_node(self, member, node_refs, seq):
817 self.buf('\t.%s\t= {\n' % member)
818 self.buf('\t\t.prev = %s,\n' % node_refs[seq - 1])
819 self.buf('\t\t.next = %s,\n' % node_refs[seq + 1])
822 def generate_uclasses(self):
823 if not self.check_instantiate(True):
826 self.out('#include <common.h>\n')
827 self.out('#include <dm.h>\n')
828 self.out('#include <dt-structs.h>\n')
832 " * uclass declarations, ordered by 'struct uclass' linker_list idx:\n")
833 uclass_list = self._valid_uclasses
834 for seq, uclass in enumerate(uclass_list):
835 self.buf(' * %3d: %s\n' % (seq, uclass.name))
837 self.buf(' * Sequence numbers allocated in each uclass:\n')
838 for uclass in uclass_list:
839 if uclass.alias_num_to_node:
840 self.buf(' * %s: %s\n' % (uclass.name, uclass.uclass_id))
841 for seq, node in uclass.alias_num_to_node.items():
842 self.buf(' * %d: %s\n' % (seq, node.path))
846 for seq, uclass in enumerate(uclass_list):
847 uclass_node[seq] = ('&DM_UCLASS_REF(%s)->sibling_node' %
849 uclass_node[-1] = '&uclass_head'
850 uclass_node[len(uclass_list)] = '&uclass_head'
852 self.buf('struct list_head %s = {\n' % 'uclass_head')
853 self.buf('\t.prev = %s,\n' % uclass_node[len(uclass_list) -1])
854 self.buf('\t.next = %s,\n' % uclass_node[0])
858 for seq, uclass in enumerate(uclass_list):
859 uc_drv = self._scan._uclass.get(uclass.uclass_id)
861 priv_name = self.alloc_priv(uc_drv.priv, uc_drv.name, '')
863 self.buf('DM_UCLASS_INST(%s) = {\n' % uclass.name)
865 self.buf('\t.priv_\t\t= %s,\n' % priv_name)
866 self.buf('\t.uc_drv\t\t= DM_UCLASS_DRIVER_REF(%s),\n' % uclass.name)
867 self.list_node('sibling_node', uclass_node, seq)
868 self.list_head('dev_head', 'uclass_node', uc_drv.devs, None)
871 self.out(''.join(self.get_buf()))
873 def read_aliases(self):
874 """Read the aliases and attach the information to self._alias
877 ValueError: The alias path is not found
879 alias_node = self._fdt.GetNode('/aliases')
882 re_num = re.compile('(^[a-z0-9-]+[a-z]+)([0-9]+)$')
883 for prop in alias_node.props.values():
884 m_alias = re_num.match(prop.name)
886 raise ValueError("Cannot decode alias '%s'" % prop.name)
887 name, num = m_alias.groups()
888 node = self._fdt.GetNode(prop.value)
889 result = self._scan.add_uclass_alias(name, num, node)
891 raise ValueError("Alias '%s' path '%s' not found" %
892 (prop.name, prop.value))
893 elif result is False:
894 print("Could not find uclass for alias '%s'" % prop.name)
896 def generate_decl(self):
897 nodes_to_output = list(self._valid_nodes)
899 self.buf('#include <dm/device-internal.h>\n')
900 self.buf('#include <dm/uclass-internal.h>\n')
903 '/* driver declarations - these allow DM_DRIVER_GET() to be used */\n')
904 for node in nodes_to_output:
905 self.buf('extern U_BOOT_DRIVER(%s);\n' % node.struct_name);
908 if self._instantiate:
910 '/* device declarations - these allow DM_DEVICE_REF() to be used */\n')
911 for node in nodes_to_output:
912 self.buf('extern DM_DEVICE_INST(%s);\n' % node.var_name)
915 uclass_list = self._valid_uclasses
918 '/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */\n')
919 for uclass in uclass_list:
920 self.buf('extern UCLASS_DRIVER(%s);\n' % uclass.name)
922 if self._instantiate:
924 self.buf('/* uclass declarations - needed for DM_UCLASS_REF() */\n')
925 for uclass in uclass_list:
926 self.buf('extern DM_UCLASS_INST(%s);\n' % uclass.name)
927 self.out(''.join(self.get_buf()))
929 def assign_seqs(self):
930 """Assign a sequence number to each node"""
931 for node in self._valid_nodes_unsorted:
932 seq = self._scan.assign_seq(node)
936 def process_nodes(self, need_drivers):
937 nodes_to_output = list(self._valid_nodes)
939 # Figure out which drivers we actually use
940 self._scan.mark_used(nodes_to_output)
942 for node in nodes_to_output:
943 node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name
944 driver = self._scan.get_driver(node.struct_name)
948 raise ValueError("Cannot parse/find driver for '%s'" %
951 uclass = self._scan._uclass.get(driver.uclass_id)
953 raise ValueError("Cannot parse/find uclass '%s' for driver '%s'" %
954 (driver.uclass_id, node.struct_name))
956 node.uclass_seq = len(node.uclass.devs)
957 node.uclass.devs.append(node)
958 uclass.node_refs[node.uclass_seq] = \
959 '&%s->uclass_node' % node.dev_ref
962 if node.parent in self._valid_nodes:
963 parent_driver = self._scan.get_driver(node.parent.struct_name)
964 if not parent_driver:
968 "Cannot parse/find parent driver '%s' for '%s'" %
969 (node.parent.struct_name, node.struct_name))
970 node.parent_seq = len(node.parent.child_devs)
971 node.parent.child_devs.append(node)
972 node.parent.child_refs[node.parent_seq] = \
973 '&%s->sibling_node' % node.dev_ref
974 node.parent_driver = parent_driver
976 for node in nodes_to_output:
977 ref = '&%s->child_head' % node.dev_ref
978 node.child_refs[-1] = ref
979 node.child_refs[len(node.child_devs)] = ref
982 for driver in self._scan._drivers.values():
983 if driver.used and driver.uclass:
984 uclass_set.add(driver.uclass)
985 self._valid_uclasses = sorted(list(uclass_set),
986 key=lambda uc: uc.uclass_id)
988 for seq, uclass in enumerate(uclass_set):
989 ref = '&DM_UCLASS_REF(%s)->dev_head' % uclass.name
990 uclass.node_refs[-1] = ref
991 uclass.node_refs[len(uclass.devs)] = ref
993 def output_node_plat(self, node):
994 """Output the C code for a node
997 node (fdt.Node): node to output
1000 parent_driver = node.parent_driver
1002 line1 = 'Node %s index %d' % (node.path, node.idx)
1005 self.buf(' * %s\n' % line1)
1006 self.buf(' * driver %s parent %s\n' % (driver.name,
1007 parent_driver.name if parent_driver else 'None'))
1010 self.buf('/* %s */\n' % line1)
1012 self._output_values(node)
1013 self._declare_device(node)
1015 self.out(''.join(self.get_buf()))
1017 def output_node_instance(self, node):
1018 """Output the C code for a node
1021 node (fdt.Node): node to output
1023 parent_driver = node.parent_driver
1026 self.buf(' * Node %s index %d\n' % (node.path, node.idx))
1027 self.buf(' * driver %s parent %s\n' % (node.driver.name,
1028 parent_driver.name if parent_driver else 'None'))
1031 if not node.driver.plat:
1032 self._output_values(node)
1033 self._declare_device_inst(node, parent_driver)
1035 self.out(''.join(self.get_buf()))
1037 def check_instantiate(self, require):
1038 """Check if self._instantiate is set to the required value
1040 If not, this outputs a message into the current file
1043 require: True to require --instantiate, False to require that it not
1046 if require != self._instantiate:
1048 '/* This file is not used: --instantiate was %senabled */\n' %
1049 ('not ' if require else ''))
1053 def generate_plat(self):
1054 """Generate device defintions for the platform data
1056 This writes out C platform data initialisation data and
1057 U_BOOT_DRVINFO() declarations for each valid node. Where a node has
1058 multiple compatible strings, a #define is used to make them equivalent.
1060 See the documentation in doc/driver-model/of-plat.rst for more
1063 if not self.check_instantiate(False):
1065 self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
1066 self.out('#define DT_PLAT_C\n')
1068 self.out('#include <common.h>\n')
1069 self.out('#include <dm.h>\n')
1070 self.out('#include <dt-structs.h>\n')
1073 if self._valid_nodes:
1076 " * driver_info declarations, ordered by 'struct driver_info' linker_list idx:\n")
1078 self.out(' * idx %-20s %-s\n' % ('driver_info', 'driver'))
1079 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1080 for node in self._valid_nodes:
1081 self.out(' * %3d: %-20s %-s\n' %
1082 (node.idx, node.var_name, node.struct_name))
1083 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1087 for node in self._valid_nodes:
1088 self.output_node_plat(node)
1090 self.out(''.join(self.get_buf()))
1092 def generate_device(self):
1093 """Generate device instances
1095 This writes out DM_DEVICE_INST() records for each device in the
1098 See the documentation in doc/driver-model/of-plat.rst for more
1101 if not self.check_instantiate(True):
1103 self.out('#include <common.h>\n')
1104 self.out('#include <dm.h>\n')
1105 self.out('#include <dt-structs.h>\n')
1108 if self._valid_nodes:
1111 " * udevice declarations, ordered by 'struct udevice' linker_list position:\n")
1113 self.out(' * idx %-20s %-s\n' % ('udevice', 'driver'))
1114 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1115 for node in self._valid_nodes:
1116 self.out(' * %3d: %-20s %-s\n' %
1117 (node.idx, node.var_name, node.struct_name))
1118 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1122 for node in self._valid_nodes:
1123 self.output_node_instance(node)
1125 self.out(''.join(self.get_buf()))
1128 # Types of output file we understand
1129 # key: Command used to generate this file
1130 # value: OutputFile for this command
1131 OUTPUT_FILES_COMMON = {
1133 OutputFile(Ftype.HEADER, 'dt-decl.h', DtbPlatdata.generate_decl,
1134 'Declares externs for all device/uclass instances'),
1136 OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
1137 DtbPlatdata.generate_structs,
1138 'Defines the structs used to hold devicetree data'),
1141 # File generated without instantiate
1142 OUTPUT_FILES_NOINST = {
1144 OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
1145 'Declares the U_BOOT_DRIVER() records and platform data'),
1148 # File generated with instantiate
1149 OUTPUT_FILES_INST = {
1151 OutputFile(Ftype.SOURCE, 'dt-device.c', DtbPlatdata.generate_device,
1152 'Declares the DM_DEVICE_INST() records'),
1154 OutputFile(Ftype.SOURCE, 'dt-uclass.c', DtbPlatdata.generate_uclasses,
1155 'Declares the uclass instances (struct uclass)'),
1159 def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase,
1160 instantiate, warning_disabled=False, drivers_additional=None,
1161 basedir=None, scan=None):
1162 """Run all the steps of the dtoc tool
1165 args (list): List of non-option arguments provided to the problem
1166 dtb_file (str): Filename of dtb file to process
1167 include_disabled (bool): True to include disabled nodes
1168 output (str): Name of output file (None for stdout)
1169 output_dirs (tuple of str):
1170 Directory to put C output files
1171 Directory to put H output files
1172 phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
1173 or 'tpl'. None if not known
1174 instantiate: Instantiate devices so they don't need to be bound at
1176 warning_disabled (bool): True to avoid showing warnings about missing
1178 drivers_additional (list): List of additional drivers to use during
1180 basedir (str): Base directory of U-Boot source code. Defaults to the
1181 grandparent of this file's directory
1182 scan (src_src.Scanner): Scanner from a previous run. This can help speed
1183 up tests. Use None for normal operation
1189 ValueError: if args has no command, or an unknown command
1192 raise ValueError('Please specify a command: struct, platdata, all')
1193 if output and output_dirs and any(output_dirs):
1194 raise ValueError('Must specify either output or output_dirs, not both')
1197 scan = src_scan.Scanner(basedir, drivers_additional, phase)
1202 plat = DtbPlatdata(scan, dtb_file, include_disabled, instantiate)
1204 plat.scan_tree(add_root=instantiate)
1205 plat.prepare_nodes()
1206 plat.scan_reg_sizes()
1207 plat.setup_output_dirs(output_dirs)
1209 plat.scan_phandles()
1210 plat.process_nodes(instantiate)
1214 # Figure out what output files we plan to generate
1215 output_files = OUTPUT_FILES_COMMON
1217 output_files.update(OUTPUT_FILES_INST)
1219 output_files.update(OUTPUT_FILES_NOINST)
1221 cmds = args[0].split(',')
1223 cmds = sorted(output_files.keys())
1225 outfile = output_files.get(cmd)
1227 raise ValueError("Unknown command '%s': (use: %s)" %
1228 (cmd, ', '.join(sorted(output_files.keys()))))
1229 plat.setup_output(outfile.ftype,
1230 outfile.fname if output_dirs else output)
1231 plat.out_header(outfile)
1232 outfile.method(plat)
1233 plat.finish_output()
1235 if not warning_disabled:
1236 scan.show_warnings()