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 if parent and not parent.props:
444 raise ValueError("Parent node '%s' has no properties - do you need u-boot,dm-spl or similar?" %
446 num_addr, num_size = 2, 2
448 addr_prop = parent.props.get('#address-cells')
449 size_prop = parent.props.get('#size-cells')
451 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
453 num_size = fdt_util.fdt32_to_cpu(size_prop.value)
454 return num_addr, num_size
456 def scan_reg_sizes(self):
457 """Scan for 64-bit 'reg' properties and update the values
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
463 for node in self._valid_nodes:
464 reg = node.props.get('reg')
467 num_addr, num_size = self.get_num_cells(node)
468 total = num_addr + num_size
470 if reg.type != fdt.Type.INT:
471 raise ValueError("Node '%s' reg property is not an int" %
473 if not isinstance(reg.value, list):
474 reg.value = [reg.value]
475 if len(reg.value) % total:
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,
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
489 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
491 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
493 new_value += [addr, size]
494 reg.value = new_value
496 def scan_structs(self):
497 """Scan the device tree building up the C structures we will use.
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.
504 Once the widest property is determined, all other properties are
505 updated to match that width.
507 The results are written to self._struct_data
509 structs = self._struct_data
510 for node in self._valid_nodes:
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)
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)
528 # Otherwise store this as a new struct.
530 structs[node.struct_name] = fields
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])
538 def scan_phandles(self):
539 """Figure out what phandles each node uses
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
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.
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] == '#':
554 info = self.get_phandle_argc(prop, node.name)
556 # Process the list as pairs of (phandle, id)
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)
566 def generate_structs(self):
567 """Generate struct defintions for the platform data
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.
573 structs = self._struct_data
574 self.out('#include <stdbool.h>\n')
575 self.out('#include <linux/libfdt.h>\n')
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])
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),
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))
598 def _output_list(self, node, prop):
599 """Output the C code for a devicetree property that holds a list
602 node (fdt.Node): Node to output
603 prop (fdt.Prop): Prop to output
607 # For phandles, output a reference to the platform data
608 # of the target node.
609 info = self.get_phandle_argc(prop, node.name)
611 # Process the list as pairs of (phandle, id)
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]
618 for i in range(args):
620 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
622 vals.append('\t{%d, {%s}}' % (target_node.idx,
623 ', '.join(arg_values)))
625 self.buf('\n\t\t%s,' % val)
627 for val in prop.value:
628 vals.append(get_value(prop.type, val))
630 # Put 8 values per line to avoid very long lines.
631 for i in range(0, len(vals), 8):
634 self.buf(', '.join(vals[i:i + 8]))
637 def _declare_device(self, node):
638 """Add a device declaration to the output
640 This declares a U_BOOT_DRVINFO() for the device being processed
643 node: Node to process
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))
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)
657 def prep_priv(self, struc, name, suffix, section='.priv_data'):
660 var_name = '_%s%s' % (name, suffix)
661 hdr = self._scan._structs.get(struc)
663 self.buf('#include <%s>\n' % hdr.fname)
665 print('Warning: Cannot find header file for struct %s' % struc)
666 attr = '__attribute__ ((section ("%s")))' % section
667 return var_name, struc, attr
669 def alloc_priv(self, info, name, extra, suffix='_priv'):
670 result = self.prep_priv(info, name, suffix)
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)
678 def alloc_plat(self, info, name, extra, node):
679 result = self.prep_priv(info, name, '_plat')
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)
690 return '&%s_%s' % (var_name, extra)
692 def _declare_device_inst(self, node, parent_driver):
693 """Add a device instance declaration to the output
695 This declares a DM_DEVICE_INST() for the device being processed
703 num_lines = len(self._lines)
704 plat_name = self.alloc_plat(driver.plat, driver.name, node.var_name,
706 priv_name = self.alloc_priv(driver.priv, driver.name, node.var_name)
707 parent_plat_name = None
708 parent_priv_name = None
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,
714 parent_priv_name = self.alloc_priv(
715 parent_driver.child_priv, driver.name, node.var_name,
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)
724 # Add a blank line if we emitted any stuff above, for readability
725 if num_lines != len(self._lines):
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)
732 self.buf('\t.plat_\t\t= %s,\n' % plat_name)
734 self.buf('\t.plat_\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
736 self.buf('\t.parent_plat_\t= %s,\n' % parent_plat_name)
738 self.buf('\t.uclass_plat_\t= %s,\n' % uclass_plat_name)
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)
748 self.buf('\t.driver_data\t= %s,\n' % driver_data)
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)
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)
759 self.buf('\t.uclass_priv_ = %s,\n' % uclass_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,
769 self.buf('\t.seq_ = %d,\n' % node.seq)
773 return parent_plat_name
775 def _output_prop(self, node, prop, tabs=1):
776 """Output a line containing the value of a struct member
779 node (Node): Node being output
780 prop (Prop): Prop object to output
782 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
784 member_name = conv_name_to_c(prop.name)
785 self.buf('%s%s= ' % ('\t' * tabs, tab_to(3, '.' + member_name)))
787 # Special handling for lists
788 if isinstance(prop.value, list):
789 self._output_list(node, prop)
791 self.buf(get_value(prop.type, prop.value))
794 def _output_values(self, node):
795 """Output the definition of a device's struct values
798 node (Node): Node to output
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])
806 def list_head(self, head_member, node_member, node_refs, var_name):
807 self.buf('\t.%s\t= {\n' % head_member)
809 last = node_refs[-1].dev_ref
810 first = node_refs[0].dev_ref
813 last = 'DM_DEVICE_REF(%s)' % var_name
816 self.buf('\t\t.prev = &%s->%s,\n' % (last, member))
817 self.buf('\t\t.next = &%s->%s,\n' % (first, member))
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])
826 def generate_uclasses(self):
827 if not self.check_instantiate(True):
830 self.out('#include <common.h>\n')
831 self.out('#include <dm.h>\n')
832 self.out('#include <dt-structs.h>\n')
836 " * uclass declarations, ordered by 'struct uclass' linker_list idx:\n")
837 uclass_list = self._valid_uclasses
838 for seq, uclass in enumerate(uclass_list):
839 self.buf(' * %3d: %s\n' % (seq, uclass.name))
841 self.buf(' * Sequence numbers allocated in each uclass:\n')
842 for uclass in uclass_list:
843 if uclass.alias_num_to_node:
844 self.buf(' * %s: %s\n' % (uclass.name, uclass.uclass_id))
845 for seq, node in uclass.alias_num_to_node.items():
846 self.buf(' * %d: %s\n' % (seq, node.path))
850 for seq, uclass in enumerate(uclass_list):
851 uclass_node[seq] = ('&DM_UCLASS_REF(%s)->sibling_node' %
853 uclass_node[-1] = '&uclass_head'
854 uclass_node[len(uclass_list)] = '&uclass_head'
856 self.buf('struct list_head %s = {\n' % 'uclass_head')
857 self.buf('\t.prev = %s,\n' % uclass_node[len(uclass_list) -1])
858 self.buf('\t.next = %s,\n' % uclass_node[0])
862 for seq, uclass in enumerate(uclass_list):
863 uc_drv = self._scan._uclass.get(uclass.uclass_id)
865 priv_name = self.alloc_priv(uc_drv.priv, uc_drv.name, '')
867 self.buf('DM_UCLASS_INST(%s) = {\n' % uclass.name)
869 self.buf('\t.priv_\t\t= %s,\n' % priv_name)
870 self.buf('\t.uc_drv\t\t= DM_UCLASS_DRIVER_REF(%s),\n' % uclass.name)
871 self.list_node('sibling_node', uclass_node, seq)
872 self.list_head('dev_head', 'uclass_node', uc_drv.devs, None)
875 self.out(''.join(self.get_buf()))
877 def read_aliases(self):
878 """Read the aliases and attach the information to self._alias
881 ValueError: The alias path is not found
883 alias_node = self._fdt.GetNode('/aliases')
886 re_num = re.compile('(^[a-z0-9-]+[a-z]+)([0-9]+)$')
887 for prop in alias_node.props.values():
888 m_alias = re_num.match(prop.name)
890 raise ValueError("Cannot decode alias '%s'" % prop.name)
891 name, num = m_alias.groups()
892 node = self._fdt.GetNode(prop.value)
893 result = self._scan.add_uclass_alias(name, num, node)
895 raise ValueError("Alias '%s' path '%s' not found" %
896 (prop.name, prop.value))
897 elif result is False:
898 print("Could not find uclass for alias '%s'" % prop.name)
900 def generate_decl(self):
901 nodes_to_output = list(self._valid_nodes)
903 self.buf('#include <dm/device-internal.h>\n')
904 self.buf('#include <dm/uclass-internal.h>\n')
907 '/* driver declarations - these allow DM_DRIVER_GET() to be used */\n')
908 for node in nodes_to_output:
909 self.buf('extern U_BOOT_DRIVER(%s);\n' % node.struct_name);
912 if self._instantiate:
914 '/* device declarations - these allow DM_DEVICE_REF() to be used */\n')
915 for node in nodes_to_output:
916 self.buf('extern DM_DEVICE_INST(%s);\n' % node.var_name)
919 uclass_list = self._valid_uclasses
922 '/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */\n')
923 for uclass in uclass_list:
924 self.buf('extern UCLASS_DRIVER(%s);\n' % uclass.name)
926 if self._instantiate:
928 self.buf('/* uclass declarations - needed for DM_UCLASS_REF() */\n')
929 for uclass in uclass_list:
930 self.buf('extern DM_UCLASS_INST(%s);\n' % uclass.name)
931 self.out(''.join(self.get_buf()))
933 def assign_seqs(self):
934 """Assign a sequence number to each node"""
935 for node in self._valid_nodes_unsorted:
936 seq = self._scan.assign_seq(node)
940 def process_nodes(self, need_drivers):
941 nodes_to_output = list(self._valid_nodes)
943 # Figure out which drivers we actually use
944 self._scan.mark_used(nodes_to_output)
946 for node in nodes_to_output:
947 node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name
948 driver = self._scan.get_driver(node.struct_name)
952 raise ValueError("Cannot parse/find driver for '%s'" %
955 uclass = self._scan._uclass.get(driver.uclass_id)
957 raise ValueError("Cannot parse/find uclass '%s' for driver '%s'" %
958 (driver.uclass_id, node.struct_name))
960 node.uclass_seq = len(node.uclass.devs)
961 node.uclass.devs.append(node)
962 uclass.node_refs[node.uclass_seq] = \
963 '&%s->uclass_node' % node.dev_ref
966 if node.parent in self._valid_nodes:
967 parent_driver = self._scan.get_driver(node.parent.struct_name)
968 if not parent_driver:
972 "Cannot parse/find parent driver '%s' for '%s'" %
973 (node.parent.struct_name, node.struct_name))
974 node.parent_seq = len(node.parent.child_devs)
975 node.parent.child_devs.append(node)
976 node.parent.child_refs[node.parent_seq] = \
977 '&%s->sibling_node' % node.dev_ref
978 node.parent_driver = parent_driver
980 for node in nodes_to_output:
981 ref = '&%s->child_head' % node.dev_ref
982 node.child_refs[-1] = ref
983 node.child_refs[len(node.child_devs)] = ref
986 for driver in self._scan._drivers.values():
987 if driver.used and driver.uclass:
988 uclass_set.add(driver.uclass)
989 self._valid_uclasses = sorted(list(uclass_set),
990 key=lambda uc: uc.uclass_id)
992 for seq, uclass in enumerate(uclass_set):
993 ref = '&DM_UCLASS_REF(%s)->dev_head' % uclass.name
994 uclass.node_refs[-1] = ref
995 uclass.node_refs[len(uclass.devs)] = ref
997 def output_node_plat(self, node):
998 """Output the C code for a node
1001 node (fdt.Node): node to output
1003 driver = node.driver
1004 parent_driver = node.parent_driver
1006 line1 = 'Node %s index %d' % (node.path, node.idx)
1009 self.buf(' * %s\n' % line1)
1010 self.buf(' * driver %s parent %s\n' % (driver.name,
1011 parent_driver.name if parent_driver else 'None'))
1014 self.buf('/* %s */\n' % line1)
1016 self._output_values(node)
1017 self._declare_device(node)
1019 self.out(''.join(self.get_buf()))
1021 def output_node_instance(self, node):
1022 """Output the C code for a node
1025 node (fdt.Node): node to output
1027 parent_driver = node.parent_driver
1030 self.buf(' * Node %s index %d\n' % (node.path, node.idx))
1031 self.buf(' * driver %s parent %s\n' % (node.driver.name,
1032 parent_driver.name if parent_driver else 'None'))
1035 if not node.driver.plat:
1036 self._output_values(node)
1037 self._declare_device_inst(node, parent_driver)
1039 self.out(''.join(self.get_buf()))
1041 def check_instantiate(self, require):
1042 """Check if self._instantiate is set to the required value
1044 If not, this outputs a message into the current file
1047 require: True to require --instantiate, False to require that it not
1050 if require != self._instantiate:
1052 '/* This file is not used: --instantiate was %senabled */\n' %
1053 ('not ' if require else ''))
1057 def generate_plat(self):
1058 """Generate device defintions for the platform data
1060 This writes out C platform data initialisation data and
1061 U_BOOT_DRVINFO() declarations for each valid node. Where a node has
1062 multiple compatible strings, a #define is used to make them equivalent.
1064 See the documentation in doc/driver-model/of-plat.rst for more
1067 if not self.check_instantiate(False):
1069 self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
1070 self.out('#define DT_PLAT_C\n')
1072 self.out('#include <common.h>\n')
1073 self.out('#include <dm.h>\n')
1074 self.out('#include <dt-structs.h>\n')
1077 if self._valid_nodes:
1080 " * driver_info declarations, ordered by 'struct driver_info' linker_list idx:\n")
1082 self.out(' * idx %-20s %-s\n' % ('driver_info', 'driver'))
1083 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1084 for node in self._valid_nodes:
1085 self.out(' * %3d: %-20s %-s\n' %
1086 (node.idx, node.var_name, node.struct_name))
1087 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1091 for node in self._valid_nodes:
1092 self.output_node_plat(node)
1094 self.out(''.join(self.get_buf()))
1096 def generate_device(self):
1097 """Generate device instances
1099 This writes out DM_DEVICE_INST() records for each device in the
1102 See the documentation in doc/driver-model/of-plat.rst for more
1105 if not self.check_instantiate(True):
1107 self.out('#include <common.h>\n')
1108 self.out('#include <dm.h>\n')
1109 self.out('#include <dt-structs.h>\n')
1112 if self._valid_nodes:
1115 " * udevice declarations, ordered by 'struct udevice' linker_list position:\n")
1117 self.out(' * idx %-20s %-s\n' % ('udevice', 'driver'))
1118 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1119 for node in self._valid_nodes:
1120 self.out(' * %3d: %-20s %-s\n' %
1121 (node.idx, node.var_name, node.struct_name))
1122 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1126 for node in self._valid_nodes:
1127 self.output_node_instance(node)
1129 self.out(''.join(self.get_buf()))
1132 # Types of output file we understand
1133 # key: Command used to generate this file
1134 # value: OutputFile for this command
1135 OUTPUT_FILES_COMMON = {
1137 OutputFile(Ftype.HEADER, 'dt-decl.h', DtbPlatdata.generate_decl,
1138 'Declares externs for all device/uclass instances'),
1140 OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
1141 DtbPlatdata.generate_structs,
1142 'Defines the structs used to hold devicetree data'),
1145 # File generated without instantiate
1146 OUTPUT_FILES_NOINST = {
1148 OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
1149 'Declares the U_BOOT_DRIVER() records and platform data'),
1152 # File generated with instantiate
1153 OUTPUT_FILES_INST = {
1155 OutputFile(Ftype.SOURCE, 'dt-device.c', DtbPlatdata.generate_device,
1156 'Declares the DM_DEVICE_INST() records'),
1158 OutputFile(Ftype.SOURCE, 'dt-uclass.c', DtbPlatdata.generate_uclasses,
1159 'Declares the uclass instances (struct uclass)'),
1163 def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase,
1164 instantiate, warning_disabled=False, drivers_additional=None,
1165 basedir=None, scan=None):
1166 """Run all the steps of the dtoc tool
1169 args (list): List of non-option arguments provided to the problem
1170 dtb_file (str): Filename of dtb file to process
1171 include_disabled (bool): True to include disabled nodes
1172 output (str): Name of output file (None for stdout)
1173 output_dirs (tuple of str):
1174 Directory to put C output files
1175 Directory to put H output files
1176 phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
1177 or 'tpl'. None if not known
1178 instantiate: Instantiate devices so they don't need to be bound at
1180 warning_disabled (bool): True to avoid showing warnings about missing
1182 drivers_additional (list): List of additional drivers to use during
1184 basedir (str): Base directory of U-Boot source code. Defaults to the
1185 grandparent of this file's directory
1186 scan (src_src.Scanner): Scanner from a previous run. This can help speed
1187 up tests. Use None for normal operation
1193 ValueError: if args has no command, or an unknown command
1196 raise ValueError('Please specify a command: struct, platdata, all')
1197 if output and output_dirs and any(output_dirs):
1198 raise ValueError('Must specify either output or output_dirs, not both')
1201 scan = src_scan.Scanner(basedir, drivers_additional, phase)
1206 plat = DtbPlatdata(scan, dtb_file, include_disabled, instantiate)
1208 plat.scan_tree(add_root=instantiate)
1209 plat.prepare_nodes()
1210 plat.scan_reg_sizes()
1211 plat.setup_output_dirs(output_dirs)
1213 plat.scan_phandles()
1214 plat.process_nodes(instantiate)
1218 # Figure out what output files we plan to generate
1219 output_files = OUTPUT_FILES_COMMON
1221 output_files.update(OUTPUT_FILES_INST)
1223 output_files.update(OUTPUT_FILES_NOINST)
1225 cmds = args[0].split(',')
1227 cmds = sorted(output_files.keys())
1229 outfile = output_files.get(cmd)
1231 raise ValueError("Unknown command '%s': (use: %s)" %
1232 (cmd, ', '.join(sorted(output_files.keys()))))
1233 plat.setup_output(outfile.ftype,
1234 outfile.fname if output_dirs else output)
1235 plat.out_header(outfile)
1236 outfile.method(plat)
1237 plat.finish_output()
1239 if not warning_disabled:
1240 scan.show_warnings()