]>
git.ipfire.org Git - thirdparty/u-boot.git/blob - tools/dtoc/dtb_platdata.py
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 'interrupts-extended': '#interrupt-cells',
66 'gpios': '#gpio-cells',
67 'sandbox,emul': '#emul-cells',
71 SOURCE
, HEADER
= range(2)
74 # This holds information about each type of output file dtoc can create
75 # ftype: Type of file (Ftype)
76 # fname: Filename excluding directory, e.g. 'dt-plat.c'
77 # hdr_comment: Comment explaining the purpose of the file
78 OutputFile
= collections
.namedtuple('OutputFile',
79 ['ftype', 'fname', 'method', 'hdr_comment'])
81 # This holds information about a property which includes phandles.
83 # max_args: integer: Maximum number or arguments that any phandle uses (int).
84 # args: Number of args for each phandle in the property. The total number of
85 # phandles is len(args). This is a list of integers.
86 PhandleInfo
= collections
.namedtuple('PhandleInfo', ['max_args', 'args'])
88 # Holds a single phandle link, allowing a C struct value to be assigned to point
91 # var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node')
92 # dev_name: Name of device to assign to (e.g. 'clock')
93 PhandleLink
= collections
.namedtuple('PhandleLink', ['var_node', 'dev_name'])
96 def tab_to(num_tabs
, line
):
97 """Append tabs to a line of text to reach a tab stop.
100 num_tabs (int): Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
101 line (str): Line of text to append to
104 str: line with the correct number of tabs appeneded. If the line already
105 extends past that tab stop then a single space is appended.
107 if len(line
) >= num_tabs
* 8:
109 return line
+ '\t' * (num_tabs
- len(line
) // 8)
111 def get_value(ftype
, value
):
112 """Get a value as a C expression
114 For integers this returns a byte-swapped (little-endian) hex string
115 For bytes this returns a hex string, e.g. 0x12
116 For strings this returns a literal string enclosed in quotes
117 For booleans this return 'true'
120 ftype (fdt.Type): Data type (fdt_util)
121 value (bytes): Data value, as a string of bytes
124 str: String representation of the value
126 if ftype
== fdt
.Type
.INT
:
127 val
= '%#x' % fdt_util
.fdt32_to_cpu(value
)
128 elif ftype
== fdt
.Type
.BYTE
:
130 val
= '%#x' % (ord(char
) if isinstance(char
, str) else char
)
131 elif ftype
== fdt
.Type
.STRING
:
132 # Handle evil ACPI backslashes by adding another backslash before them.
133 # So "\\_SB.GPO0" in the device tree effectively stays like that in C
134 val
= '"%s"' % value
.replace('\\', '\\\\')
135 elif ftype
== fdt
.Type
.BOOL
:
137 else: # ftype == fdt.Type.INT64:
143 """Provide a means to convert device tree binary data to platform data
145 The output of this process is C structures which can be used in space-
146 constrained encvironments where the ~3KB code overhead of device tree
147 code is not affordable.
150 _scan: Scan object, for scanning and reporting on useful information
151 from the U-Boot source code
152 _fdt: Fdt object, referencing the device tree
153 _dtb_fname: Filename of the input device tree binary file
154 _valid_nodes_unsorted: A list of Node object with compatible strings,
155 ordered by devicetree node order
156 _valid_nodes: A list of Node object with compatible strings, ordered by
157 conv_name_to_c(node.name)
158 _include_disabled: true to include nodes marked status = "disabled"
159 _outfile: The current output file (sys.stdout or a real file)
160 _lines: Stashed list of output lines for outputting in the future
161 _dirname: Directory to hold output files, or None for none (all files
163 _struct_data (dict): OrderedDict of dtplat structures to output
164 key (str): Node name, as a C identifier
165 value: dict containing structure fields:
166 key (str): Field name
167 value: Prop object with field information
168 _basedir (str): Base directory of source tree
169 _valid_uclasses (list of src_scan.Uclass): List of uclasses needed for
170 the selected devices (see _valid_node), in alphabetical order
171 _instantiate: Instantiate devices so they don't need to be bound at
174 def __init__(self
, scan
, dtb_fname
, include_disabled
, instantiate
=False):
177 self
._dtb
_fname
= dtb_fname
178 self
._valid
_nodes
= None
179 self
._valid
_nodes
_unsorted
= None
180 self
._include
_disabled
= include_disabled
183 self
._dirnames
= [None] * len(Ftype
)
184 self
._struct
_data
= collections
.OrderedDict()
186 self
._valid
_uclasses
= None
187 self
._instantiate
= instantiate
189 def setup_output_dirs(self
, output_dirs
):
190 """Set up the output directories
192 This should be done before setup_output() is called
195 output_dirs (tuple of str):
196 Directory to use for C output files.
197 Use None to write files relative current directory
198 Directory to use for H output files.
199 Defaults to the C output dir
201 def process_dir(ftype
, dirname
):
203 os
.makedirs(dirname
, exist_ok
=True)
204 self
._dirnames
[ftype
] = dirname
207 c_dirname
= output_dirs
[0]
208 h_dirname
= output_dirs
[1] if len(output_dirs
) > 1 else c_dirname
209 process_dir(Ftype
.SOURCE
, c_dirname
)
210 process_dir(Ftype
.HEADER
, h_dirname
)
212 def setup_output(self
, ftype
, fname
):
213 """Set up the output destination
215 Once this is done, future calls to self.out() will output to this
216 file. The file used is as follows:
218 self._dirnames[ftype] is None: output to fname, or stdout if None
219 self._dirnames[ftype] is not None: output to fname in that directory
221 Calling this function multiple times will close the old file and open
222 the new one. If they are the same file, nothing happens and output will
223 continue to the same file.
226 ftype (str): Type of file to create ('c' or 'h')
227 fname (str): Filename to send output to. If there is a directory in
228 self._dirnames for this file type, it will be put in that
231 dirname
= self
._dirnames
[ftype
]
233 pathname
= os
.path
.join(dirname
, fname
)
235 self
._outfile
.close()
236 self
._outfile
= open(pathname
, 'w')
238 if not self
._outfile
:
239 self
._outfile
= open(fname
, 'w')
241 self
._outfile
= sys
.stdout
243 def finish_output(self
):
244 """Finish outputing to a file
246 This closes the output file, if one is in use
248 if self
._outfile
!= sys
.stdout
:
249 self
._outfile
.close()
253 """Output a string to the output file
256 line (str): String to output
258 self
._outfile
.write(line
)
261 """Buffer up a string to send later
264 line (str): String to add to our 'buffer' list
266 self
._lines
.append(line
)
269 """Get the contents of the output buffer, and clear it
272 list(str): The output buffer, which is then cleared for future use
278 def out_header(self
, outfile
):
279 """Output a message indicating that this is an auto-generated file
282 outfile: OutputFile describing the file being generated
288 * This was generated by dtoc from a .dtb (device tree binary) file.
291 ''' % outfile
.hdr_comment
)
293 def get_phandle_argc(self
, prop
, node_name
):
294 """Check if a node contains phandles
296 We have no reliable way of detecting whether a node uses a phandle
297 or not. As an interim measure, use a list of known property names.
300 prop (fdt.Prop): Prop object to check
301 node_name (str): Node name, only used for raising an error
303 int or None: Number of argument cells is this is a phandle,
306 ValueError: if the phandle cannot be parsed or the required property
310 for name
, cprop
in PHANDLE_PROPS
.items():
311 if prop
.name
.endswith(name
):
314 if not isinstance(prop
.value
, list):
315 prop
.value
= [prop
.value
]
322 phandle
= fdt_util
.fdt32_to_cpu(val
[i
])
323 # If we get to the end of the list, stop. This can happen
324 # since some nodes have more phandles in the list than others,
325 # but we allocate enough space for the largest list. So those
326 # nodes with shorter lists end up with zeroes at the end.
329 target
= self
._fdt
.phandle_to_node
.get(phandle
)
331 raise ValueError("Cannot parse '%s' in node '%s'" %
332 (prop
.name
, node_name
))
333 cells
= target
.props
.get(cells_prop
)
335 raise ValueError("Node '%s' has no cells property" %
337 num_args
= fdt_util
.fdt32_to_cpu(cells
.value
)
338 max_args
= max(max_args
, num_args
)
339 args
.append(num_args
)
341 return PhandleInfo(max_args
, args
)
345 """Scan the device tree to obtain a tree of nodes and properties
347 Once this is done, self._fdt.GetRoot() can be called to obtain the
348 device tree root node, and progress from there.
350 self
._fdt
= fdt
.FdtScan(self
._dtb
_fname
)
352 def scan_node(self
, node
, valid_nodes
):
353 """Scan a node and subnodes to build a tree of node and phandle info
355 This adds each subnode to self._valid_nodes if it is enabled and has a
359 node (Node): Node for scan for subnodes
360 valid_nodes (list of Node): List of Node objects to add to
362 for subnode
in node
.subnodes
:
363 if 'compatible' in subnode
.props
:
364 status
= subnode
.props
.get('status')
365 if (not self
._include
_disabled
and not status
or
366 status
.value
!= 'disabled'):
367 valid_nodes
.append(subnode
)
369 # recurse to handle any subnodes
370 self
.scan_node(subnode
, valid_nodes
)
372 def scan_tree(self
, add_root
):
373 """Scan the device tree for useful information
375 This fills in the following properties:
376 _valid_nodes_unsorted: A list of nodes we wish to consider include
377 in the platform data (in devicetree node order)
378 _valid_nodes: Sorted version of _valid_nodes_unsorted
381 add_root: True to add the root node also (which wouldn't normally
382 be added as it may not have a compatible string)
384 root
= self
._fdt
.GetRoot()
387 valid_nodes
.append(root
)
388 self
.scan_node(root
, valid_nodes
)
389 self
._valid
_nodes
_unsorted
= valid_nodes
390 self
._valid
_nodes
= sorted(valid_nodes
,
391 key
=lambda x
: conv_name_to_c(x
.name
))
393 def prepare_nodes(self
):
394 """Add extra properties to the nodes we are using
396 The following properties are added for use by dtoc:
397 idx: Index number of this node (0=first, etc.)
398 struct_name: Name of the struct dtd used by this node
399 var_name: C name for this node
400 child_devs: List of child devices for this node, each a None
401 child_refs: Dict of references for each child:
402 key: Position in child list (-1=head, 0=first, 1=second, ...
404 seq: Sequence number of the device (unique within its uclass), or
406 dev_ref: Reference to this device, e.g. 'DM_DEVICE_REF(serial)'
407 driver: Driver record for this node, or None if not known
408 uclass: Uclass record for this node, or None if not known
409 uclass_seq: Position of this device within the uclass list (0=first,
411 parent_seq: Position of this device within it siblings (0=first,
413 parent_driver: Driver record of the node's parent, or None if none.
414 We don't use node.parent.driver since node.parent may not be in
415 the list of valid nodes
417 for idx
, node
in enumerate(self
._valid
_nodes
):
419 node
.struct_name
, _
= self
._scan
.get_normalized_compat_name(node
)
420 node
.var_name
= conv_name_to_c(node
.name
)
427 node
.uclass_seq
= None
428 node
.parent_seq
= None
429 node
.parent_driver
= None
432 def get_num_cells(node
):
433 """Get the number of cells in addresses and sizes for this node
436 node (fdt.None): Node to check
440 Number of address cells for this node
441 Number of size cells for this node
444 if parent
and not parent
.props
:
445 raise ValueError("Parent node '%s' has no properties - do you need u-boot,dm-spl or similar?" %
447 num_addr
, num_size
= 2, 2
449 addr_prop
= parent
.props
.get('#address-cells')
450 size_prop
= parent
.props
.get('#size-cells')
452 num_addr
= fdt_util
.fdt32_to_cpu(addr_prop
.value
)
454 num_size
= fdt_util
.fdt32_to_cpu(size_prop
.value
)
455 return num_addr
, num_size
457 def scan_reg_sizes(self
):
458 """Scan for 64-bit 'reg' properties and update the values
460 This finds 'reg' properties with 64-bit data and converts the value to
461 an array of 64-values. This allows it to be output in a way that the
464 for node
in self
._valid
_nodes
:
465 reg
= node
.props
.get('reg')
468 num_addr
, num_size
= self
.get_num_cells(node
)
469 total
= num_addr
+ num_size
471 if reg
.type != fdt
.Type
.INT
:
472 raise ValueError("Node '%s' reg property is not an int" %
474 if not isinstance(reg
.value
, list):
475 reg
.value
= [reg
.value
]
476 if len(reg
.value
) % total
:
478 "Node '%s' (parent '%s') reg property has %d cells "
479 'which is not a multiple of na + ns = %d + %d)' %
480 (node
.name
, node
.parent
.name
, len(reg
.value
), num_addr
,
482 reg
.num_addr
= num_addr
483 reg
.num_size
= num_size
484 if num_addr
> 1 or num_size
> 1:
485 reg
.type = fdt
.Type
.INT64
490 addr
= fdt_util
.fdt_cells_to_cpu(val
[i
:], reg
.num_addr
)
492 size
= fdt_util
.fdt_cells_to_cpu(val
[i
:], reg
.num_size
)
494 new_value
+= [addr
, size
]
495 reg
.value
= new_value
497 def scan_structs(self
):
498 """Scan the device tree building up the C structures we will use.
500 Build a dict keyed by C struct name containing a dict of Prop
501 object for each struct field (keyed by property name). Where the
502 same struct appears multiple times, try to use the 'widest'
503 property, i.e. the one with a type which can express all others.
505 Once the widest property is determined, all other properties are
506 updated to match that width.
508 The results are written to self._struct_data
510 structs
= self
._struct
_data
511 for node
in self
._valid
_nodes
:
514 # Get a list of all the valid properties in this node.
515 for name
, prop
in node
.props
.items():
516 if name
not in PROP_IGNORE_LIST
and name
[0] != '#':
517 fields
[name
] = copy
.deepcopy(prop
)
519 # If we've seen this struct_name before, update the existing struct
520 if node
.struct_name
in structs
:
521 struct
= structs
[node
.struct_name
]
522 for name
, prop
in fields
.items():
523 oldprop
= struct
.get(name
)
529 # Otherwise store this as a new struct.
531 structs
[node
.struct_name
] = fields
533 for node
in self
._valid
_nodes
:
534 struct
= structs
[node
.struct_name
]
535 for name
, prop
in node
.props
.items():
536 if name
not in PROP_IGNORE_LIST
and name
[0] != '#':
537 prop
.Widen(struct
[name
])
539 def scan_phandles(self
):
540 """Figure out what phandles each node uses
542 We need to be careful when outputing nodes that use phandles since
543 they must come after the declaration of the phandles in the C file.
544 Otherwise we get a compiler error since the phandle struct is not yet
547 This function adds to each node a list of phandle nodes that the node
548 depends on. This allows us to output things in the right order.
550 for node
in self
._valid
_nodes
:
551 node
.phandles
= set()
552 for pname
, prop
in node
.props
.items():
553 if pname
in PROP_IGNORE_LIST
or pname
[0] == '#':
555 info
= self
.get_phandle_argc(prop
, node
.name
)
557 # Process the list as pairs of (phandle, id)
559 for args
in info
.args
:
560 phandle_cell
= prop
.value
[pos
]
561 phandle
= fdt_util
.fdt32_to_cpu(phandle_cell
)
562 target_node
= self
._fdt
.phandle_to_node
[phandle
]
563 node
.phandles
.add(target_node
)
567 def generate_structs(self
):
568 """Generate struct defintions for the platform data
570 This writes out the body of a header file consisting of structure
571 definitions for node in self._valid_nodes. See the documentation in
572 doc/driver-model/of-plat.rst for more information.
574 structs
= self
._struct
_data
575 self
.out('#include <stdbool.h>\n')
576 self
.out('#include <linux/libfdt.h>\n')
578 # Output the struct definition
579 for name
in sorted(structs
):
580 self
.out('struct %s%s {\n' % (STRUCT_PREFIX
, name
))
581 for pname
in sorted(structs
[name
]):
582 prop
= structs
[name
][pname
]
583 info
= self
.get_phandle_argc(prop
, structs
[name
])
585 # For phandles, include a reference to the target
586 struct_name
= 'struct phandle_%d_arg' % info
.max_args
587 self
.out('\t%s%s[%d]' % (tab_to(2, struct_name
),
588 conv_name_to_c(prop
.name
),
591 ptype
= TYPE_NAMES
[prop
.type]
592 self
.out('\t%s%s' % (tab_to(2, ptype
),
593 conv_name_to_c(prop
.name
)))
594 if isinstance(prop
.value
, list):
595 self
.out('[%d]' % len(prop
.value
))
599 def _output_list(self
, node
, prop
):
600 """Output the C code for a devicetree property that holds a list
603 node (fdt.Node): Node to output
604 prop (fdt.Prop): Prop to output
608 # For phandles, output a reference to the platform data
609 # of the target node.
610 info
= self
.get_phandle_argc(prop
, node
.name
)
612 # Process the list as pairs of (phandle, id)
614 for args
in info
.args
:
615 phandle_cell
= prop
.value
[pos
]
616 phandle
= fdt_util
.fdt32_to_cpu(phandle_cell
)
617 target_node
= self
._fdt
.phandle_to_node
[phandle
]
619 for i
in range(args
):
621 str(fdt_util
.fdt32_to_cpu(prop
.value
[pos
+ 1 + i
])))
623 vals
.append('\t{%d, {%s}}' % (target_node
.idx
,
624 ', '.join(arg_values
)))
626 self
.buf('\n\t\t%s,' % val
)
628 for val
in prop
.value
:
629 vals
.append(get_value(prop
.type, val
))
631 # Put 8 values per line to avoid very long lines.
632 for i
in range(0, len(vals
), 8):
635 self
.buf(', '.join(vals
[i
:i
+ 8]))
638 def _declare_device(self
, node
):
639 """Add a device declaration to the output
641 This declares a U_BOOT_DRVINFO() for the device being processed
644 node: Node to process
646 self
.buf('U_BOOT_DRVINFO(%s) = {\n' % node
.var_name
)
647 self
.buf('\t.name\t\t= "%s",\n' % node
.struct_name
)
648 self
.buf('\t.plat\t\t= &%s%s,\n' % (VAL_PREFIX
, node
.var_name
))
649 self
.buf('\t.plat_size\t= sizeof(%s%s),\n' %
650 (VAL_PREFIX
, node
.var_name
))
652 if node
.parent
and node
.parent
in self
._valid
_nodes
:
653 idx
= node
.parent
.idx
654 self
.buf('\t.parent_idx\t= %d,\n' % idx
)
658 def prep_priv(self
, struc
, name
, suffix
, section
='.priv_data'):
661 var_name
= '_%s%s' % (name
, suffix
)
662 hdr
= self
._scan
._structs
.get(struc
)
664 self
.buf('#include <%s>\n' % hdr
.fname
)
666 print('Warning: Cannot find header file for struct %s' % struc
)
667 attr
= '__attribute__ ((section ("%s")))' % section
668 return var_name
, struc
, attr
670 def alloc_priv(self
, info
, name
, extra
, suffix
='_priv'):
671 result
= self
.prep_priv(info
, name
, suffix
)
674 var_name
, struc
, section
= result
675 self
.buf('u8 %s_%s[sizeof(struct %s)]\n\t%s;\n' %
676 (var_name
, extra
, struc
.strip(), section
))
677 return '%s_%s' % (var_name
, extra
)
679 def alloc_plat(self
, info
, name
, extra
, node
):
680 result
= self
.prep_priv(info
, name
, '_plat')
683 var_name
, struc
, section
= result
684 self
.buf('struct %s %s\n\t%s_%s = {\n' %
685 (struc
.strip(), section
, var_name
, extra
))
686 self
.buf('\t.dtplat = {\n')
687 for pname
in sorted(node
.props
):
688 self
._output
_prop
(node
, node
.props
[pname
], 2)
691 return '&%s_%s' % (var_name
, extra
)
693 def _declare_device_inst(self
, node
, parent_driver
):
694 """Add a device instance declaration to the output
696 This declares a DM_DEVICE_INST() for the device being processed
704 num_lines
= len(self
._lines
)
705 plat_name
= self
.alloc_plat(driver
.plat
, driver
.name
, node
.var_name
,
707 priv_name
= self
.alloc_priv(driver
.priv
, driver
.name
, node
.var_name
)
708 parent_plat_name
= None
709 parent_priv_name
= None
711 # TODO: deal with uclass providing these values
712 parent_plat_name
= self
.alloc_priv(
713 parent_driver
.child_plat
, driver
.name
, node
.var_name
,
715 parent_priv_name
= self
.alloc_priv(
716 parent_driver
.child_priv
, driver
.name
, node
.var_name
,
718 uclass_plat_name
= self
.alloc_priv(
719 uclass
.per_dev_plat
, driver
.name
+ '_uc', node
.var_name
, 'plat')
720 uclass_priv_name
= self
.alloc_priv(uclass
.per_dev_priv
,
721 driver
.name
+ '_uc', node
.var_name
)
722 for hdr
in driver
.headers
:
723 self
.buf('#include %s\n' % hdr
)
725 # Add a blank line if we emitted any stuff above, for readability
726 if num_lines
!= len(self
._lines
):
729 self
.buf('DM_DEVICE_INST(%s) = {\n' % node
.var_name
)
730 self
.buf('\t.driver\t\t= DM_DRIVER_REF(%s),\n' % node
.struct_name
)
731 self
.buf('\t.name\t\t= "%s",\n' % node
.struct_name
)
733 self
.buf('\t.plat_\t\t= %s,\n' % plat_name
)
735 self
.buf('\t.plat_\t\t= &%s%s,\n' % (VAL_PREFIX
, node
.var_name
))
737 self
.buf('\t.parent_plat_\t= %s,\n' % parent_plat_name
)
739 self
.buf('\t.uclass_plat_\t= %s,\n' % uclass_plat_name
)
742 if node
!= self
._fdt
.GetRoot():
743 compat_list
= node
.props
['compatible'].value
744 if not isinstance(compat_list
, list):
745 compat_list
= [compat_list
]
746 for compat
in compat_list
:
747 driver_data
= driver
.compat
.get(compat
)
749 self
.buf('\t.driver_data\t= %s,\n' % driver_data
)
752 if node
.parent
and node
.parent
.parent
:
753 if node
.parent
not in self
._valid
_nodes
:
754 # This might indicate that the parent node is not in the
755 # SPL/TPL devicetree but the child is. For example if we are
756 # dealing with of-platdata in TPL, the parent has a
757 # u-boot,dm-tpl tag but the child has u-boot,dm-pre-reloc. In
758 # this case the child node exists in TPL but the parent does
760 raise ValueError("Node '%s' requires parent node '%s' but it is not in the valid list" %
761 (node
.path
, node
.parent
.path
))
762 self
.buf('\t.parent\t\t= DM_DEVICE_REF(%s),\n' %
763 node
.parent
.var_name
)
765 self
.buf('\t.priv_\t\t= %s,\n' % priv_name
)
766 self
.buf('\t.uclass\t\t= DM_UCLASS_REF(%s),\n' % uclass
.name
)
769 self
.buf('\t.uclass_priv_ = %s,\n' % uclass_priv_name
)
771 self
.buf('\t.parent_priv_\t= %s,\n' % parent_priv_name
)
772 self
.list_node('uclass_node', uclass
.node_refs
, node
.uclass_seq
)
773 self
.list_head('child_head', 'sibling_node', node
.child_devs
, node
.var_name
)
774 if node
.parent
in self
._valid
_nodes
:
775 self
.list_node('sibling_node', node
.parent
.child_refs
,
779 self
.buf('\t.seq_ = %d,\n' % node
.seq
)
783 return parent_plat_name
785 def _output_prop(self
, node
, prop
, tabs
=1):
786 """Output a line containing the value of a struct member
789 node (Node): Node being output
790 prop (Prop): Prop object to output
792 if prop
.name
in PROP_IGNORE_LIST
or prop
.name
[0] == '#':
794 member_name
= conv_name_to_c(prop
.name
)
795 self
.buf('%s%s= ' % ('\t' * tabs
, tab_to(3, '.' + member_name
)))
797 # Special handling for lists
798 if isinstance(prop
.value
, list):
799 self
._output
_list
(node
, prop
)
801 self
.buf(get_value(prop
.type, prop
.value
))
804 def _output_values(self
, node
):
805 """Output the definition of a device's struct values
808 node (Node): Node to output
810 self
.buf('static struct %s%s %s%s = {\n' %
811 (STRUCT_PREFIX
, node
.struct_name
, VAL_PREFIX
, node
.var_name
))
812 for pname
in sorted(node
.props
):
813 self
._output
_prop
(node
, node
.props
[pname
])
816 def list_head(self
, head_member
, node_member
, node_refs
, var_name
):
817 self
.buf('\t.%s\t= {\n' % head_member
)
819 last
= node_refs
[-1].dev_ref
820 first
= node_refs
[0].dev_ref
823 last
= 'DM_DEVICE_REF(%s)' % var_name
826 self
.buf('\t\t.prev = &%s->%s,\n' % (last
, member
))
827 self
.buf('\t\t.next = &%s->%s,\n' % (first
, member
))
830 def list_node(self
, member
, node_refs
, seq
):
831 self
.buf('\t.%s\t= {\n' % member
)
832 self
.buf('\t\t.prev = %s,\n' % node_refs
[seq
- 1])
833 self
.buf('\t\t.next = %s,\n' % node_refs
[seq
+ 1])
836 def generate_uclasses(self
):
838 self
.out('#include <common.h>\n')
839 self
.out('#include <dm.h>\n')
840 self
.out('#include <dt-structs.h>\n')
844 " * uclass declarations, ordered by 'struct uclass' linker_list idx:\n")
845 uclass_list
= self
._valid
_uclasses
846 for seq
, uclass
in enumerate(uclass_list
):
847 self
.buf(' * %3d: %s\n' % (seq
, uclass
.name
))
849 self
.buf(' * Sequence numbers allocated in each uclass:\n')
850 for uclass
in uclass_list
:
851 if uclass
.alias_num_to_node
:
852 self
.buf(' * %s: %s\n' % (uclass
.name
, uclass
.uclass_id
))
853 for seq
, node
in uclass
.alias_num_to_node
.items():
854 self
.buf(' * %d: %s\n' % (seq
, node
.path
))
858 for seq
, uclass
in enumerate(uclass_list
):
859 uclass_node
[seq
] = ('&DM_UCLASS_REF(%s)->sibling_node' %
861 uclass_node
[-1] = '&uclass_head'
862 uclass_node
[len(uclass_list
)] = '&uclass_head'
864 self
.buf('struct list_head %s = {\n' % 'uclass_head')
865 self
.buf('\t.prev = %s,\n' % uclass_node
[len(uclass_list
) -1])
866 self
.buf('\t.next = %s,\n' % uclass_node
[0])
870 for seq
, uclass
in enumerate(uclass_list
):
871 uc_drv
= self
._scan
._uclass
.get(uclass
.uclass_id
)
873 priv_name
= self
.alloc_priv(uc_drv
.priv
, uc_drv
.name
, '')
875 self
.buf('DM_UCLASS_INST(%s) = {\n' % uclass
.name
)
877 self
.buf('\t.priv_\t\t= %s,\n' % priv_name
)
878 self
.buf('\t.uc_drv\t\t= DM_UCLASS_DRIVER_REF(%s),\n' % uclass
.name
)
879 self
.list_node('sibling_node', uclass_node
, seq
)
880 self
.list_head('dev_head', 'uclass_node', uc_drv
.devs
, None)
883 self
.out(''.join(self
.get_buf()))
885 def read_aliases(self
):
886 """Read the aliases and attach the information to self._alias
889 ValueError: The alias path is not found
891 alias_node
= self
._fdt
.GetNode('/aliases')
894 re_num
= re
.compile('(^[a-z0-9-]+[a-z]+)([0-9]+)$')
895 for prop
in alias_node
.props
.values():
896 m_alias
= re_num
.match(prop
.name
)
898 raise ValueError("Cannot decode alias '%s'" % prop
.name
)
899 name
, num
= m_alias
.groups()
900 node
= self
._fdt
.GetNode(prop
.value
)
901 result
= self
._scan
.add_uclass_alias(name
, num
, node
)
903 raise ValueError("Alias '%s' path '%s' not found" %
904 (prop
.name
, prop
.value
))
905 elif result
is False:
906 print("Could not find uclass for alias '%s'" % prop
.name
)
908 def generate_decl(self
):
909 nodes_to_output
= list(self
._valid
_nodes
)
911 self
.buf('#include <dm/device-internal.h>\n')
912 self
.buf('#include <dm/uclass-internal.h>\n')
915 '/* driver declarations - these allow DM_DRIVER_GET() to be used */\n')
916 for node
in nodes_to_output
:
917 self
.buf('extern U_BOOT_DRIVER(%s);\n' % node
.struct_name
);
920 if self
._instantiate
:
922 '/* device declarations - these allow DM_DEVICE_REF() to be used */\n')
923 for node
in nodes_to_output
:
924 self
.buf('extern DM_DEVICE_INST(%s);\n' % node
.var_name
)
927 uclass_list
= self
._valid
_uclasses
930 '/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */\n')
931 for uclass
in uclass_list
:
932 self
.buf('extern UCLASS_DRIVER(%s);\n' % uclass
.name
)
934 if self
._instantiate
:
936 self
.buf('/* uclass declarations - needed for DM_UCLASS_REF() */\n')
937 for uclass
in uclass_list
:
938 self
.buf('extern DM_UCLASS_INST(%s);\n' % uclass
.name
)
939 self
.out(''.join(self
.get_buf()))
941 def assign_seqs(self
):
942 """Assign a sequence number to each node"""
943 for node
in self
._valid
_nodes
_unsorted
:
944 seq
= self
._scan
.assign_seq(node
)
948 def process_nodes(self
, need_drivers
):
949 nodes_to_output
= list(self
._valid
_nodes
)
951 # Figure out which drivers we actually use
952 self
._scan
.mark_used(nodes_to_output
)
954 for node
in nodes_to_output
:
955 node
.dev_ref
= 'DM_DEVICE_REF(%s)' % node
.var_name
956 driver
= self
._scan
.get_driver(node
.struct_name
)
960 raise ValueError("Cannot parse/find driver for '%s'" %
963 uclass
= self
._scan
._uclass
.get(driver
.uclass_id
)
965 raise ValueError("Cannot parse/find uclass '%s' for driver '%s'" %
966 (driver
.uclass_id
, node
.struct_name
))
968 node
.uclass_seq
= len(node
.uclass
.devs
)
969 node
.uclass
.devs
.append(node
)
970 uclass
.node_refs
[node
.uclass_seq
] = \
971 '&%s->uclass_node' % node
.dev_ref
974 if node
.parent
in self
._valid
_nodes
:
975 parent_driver
= self
._scan
.get_driver(node
.parent
.struct_name
)
976 if not parent_driver
:
980 "Cannot parse/find parent driver '%s' for '%s'" %
981 (node
.parent
.struct_name
, node
.struct_name
))
982 node
.parent_seq
= len(node
.parent
.child_devs
)
983 node
.parent
.child_devs
.append(node
)
984 node
.parent
.child_refs
[node
.parent_seq
] = \
985 '&%s->sibling_node' % node
.dev_ref
986 node
.parent_driver
= parent_driver
988 for node
in nodes_to_output
:
989 ref
= '&%s->child_head' % node
.dev_ref
990 node
.child_refs
[-1] = ref
991 node
.child_refs
[len(node
.child_devs
)] = ref
994 for driver
in self
._scan
._drivers
.values():
995 if driver
.used
and driver
.uclass
:
996 uclass_set
.add(driver
.uclass
)
997 self
._valid
_uclasses
= sorted(list(uclass_set
),
998 key
=lambda uc
: uc
.uclass_id
)
1000 for seq
, uclass
in enumerate(uclass_set
):
1001 ref
= '&DM_UCLASS_REF(%s)->dev_head' % uclass
.name
1002 uclass
.node_refs
[-1] = ref
1003 uclass
.node_refs
[len(uclass
.devs
)] = ref
1005 def output_node_plat(self
, node
):
1006 """Output the C code for a node
1009 node (fdt.Node): node to output
1011 driver
= node
.driver
1012 parent_driver
= node
.parent_driver
1014 line1
= 'Node %s index %d' % (node
.path
, node
.idx
)
1017 self
.buf(' * %s\n' % line1
)
1018 self
.buf(' * driver %s parent %s\n' % (driver
.name
,
1019 parent_driver
.name
if parent_driver
else 'None'))
1022 self
.buf('/* %s */\n' % line1
)
1024 self
._output
_values
(node
)
1025 self
._declare
_device
(node
)
1027 self
.out(''.join(self
.get_buf()))
1029 def output_node_instance(self
, node
):
1030 """Output the C code for a node
1033 node (fdt.Node): node to output
1035 parent_driver
= node
.parent_driver
1038 self
.buf(' * Node %s index %d\n' % (node
.path
, node
.idx
))
1039 self
.buf(' * driver %s parent %s\n' % (node
.driver
.name
,
1040 parent_driver
.name
if parent_driver
else 'None'))
1043 if not node
.driver
.plat
:
1044 self
._output
_values
(node
)
1045 self
._declare
_device
_inst
(node
, parent_driver
)
1047 self
.out(''.join(self
.get_buf()))
1049 def generate_plat(self
):
1050 """Generate device defintions for the platform data
1052 This writes out C platform data initialisation data and
1053 U_BOOT_DRVINFO() declarations for each valid node. Where a node has
1054 multiple compatible strings, a #define is used to make them equivalent.
1056 See the documentation in doc/driver-model/of-plat.rst for more
1059 self
.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
1060 self
.out('#define DT_PLAT_C\n')
1062 self
.out('#include <common.h>\n')
1063 self
.out('#include <dm.h>\n')
1064 self
.out('#include <dt-structs.h>\n')
1067 if self
._valid
_nodes
:
1070 " * driver_info declarations, ordered by 'struct driver_info' linker_list idx:\n")
1072 self
.out(' * idx %-20s %-s\n' % ('driver_info', 'driver'))
1073 self
.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1074 for node
in self
._valid
_nodes
:
1075 self
.out(' * %3d: %-20s %-s\n' %
1076 (node
.idx
, node
.var_name
, node
.struct_name
))
1077 self
.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1081 for node
in self
._valid
_nodes
:
1082 self
.output_node_plat(node
)
1084 self
.out(''.join(self
.get_buf()))
1086 def generate_device(self
):
1087 """Generate device instances
1089 This writes out DM_DEVICE_INST() records for each device in the
1092 See the documentation in doc/driver-model/of-plat.rst for more
1095 self
.out('#include <common.h>\n')
1096 self
.out('#include <dm.h>\n')
1097 self
.out('#include <dt-structs.h>\n')
1100 if self
._valid
_nodes
:
1103 " * udevice declarations, ordered by 'struct udevice' linker_list position:\n")
1105 self
.out(' * idx %-20s %-s\n' % ('udevice', 'driver'))
1106 self
.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1107 for node
in self
._valid
_nodes
:
1108 self
.out(' * %3d: %-20s %-s\n' %
1109 (node
.idx
, node
.var_name
, node
.struct_name
))
1110 self
.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1114 for node
in self
._valid
_nodes
:
1115 self
.output_node_instance(node
)
1117 self
.out(''.join(self
.get_buf()))
1120 # Types of output file we understand
1121 # key: Command used to generate this file
1122 # value: OutputFile for this command
1123 OUTPUT_FILES_COMMON
= {
1125 OutputFile(Ftype
.HEADER
, 'dt-decl.h', DtbPlatdata
.generate_decl
,
1126 'Declares externs for all device/uclass instances'),
1128 OutputFile(Ftype
.HEADER
, 'dt-structs-gen.h',
1129 DtbPlatdata
.generate_structs
,
1130 'Defines the structs used to hold devicetree data'),
1133 # File generated without instantiate
1134 OUTPUT_FILES_NOINST
= {
1136 OutputFile(Ftype
.SOURCE
, 'dt-plat.c', DtbPlatdata
.generate_plat
,
1137 'Declares the U_BOOT_DRIVER() records and platform data'),
1140 # File generated with instantiate
1141 OUTPUT_FILES_INST
= {
1143 OutputFile(Ftype
.SOURCE
, 'dt-device.c', DtbPlatdata
.generate_device
,
1144 'Declares the DM_DEVICE_INST() records'),
1146 OutputFile(Ftype
.SOURCE
, 'dt-uclass.c', DtbPlatdata
.generate_uclasses
,
1147 'Declares the uclass instances (struct uclass)'),
1151 def run_steps(args
, dtb_file
, include_disabled
, output
, output_dirs
, phase
,
1152 instantiate
, warning_disabled
=False, drivers_additional
=None,
1153 basedir
=None, scan
=None):
1154 """Run all the steps of the dtoc tool
1157 args (list): List of non-option arguments provided to the problem
1158 dtb_file (str): Filename of dtb file to process
1159 include_disabled (bool): True to include disabled nodes
1160 output (str): Name of output file (None for stdout)
1161 output_dirs (tuple of str):
1162 Directory to put C output files
1163 Directory to put H output files
1164 phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
1165 or 'tpl'. None if not known
1166 instantiate: Instantiate devices so they don't need to be bound at
1168 warning_disabled (bool): True to avoid showing warnings about missing
1170 drivers_additional (list): List of additional drivers to use during
1172 basedir (str): Base directory of U-Boot source code. Defaults to the
1173 grandparent of this file's directory
1174 scan (src_src.Scanner): Scanner from a previous run. This can help speed
1175 up tests. Use None for normal operation
1181 ValueError: if args has no command, or an unknown command
1184 raise ValueError('Please specify a command: struct, platdata, all')
1185 if output
and output_dirs
and any(output_dirs
):
1186 raise ValueError('Must specify either output or output_dirs, not both')
1189 scan
= src_scan
.Scanner(basedir
, drivers_additional
, phase
)
1194 plat
= DtbPlatdata(scan
, dtb_file
, include_disabled
, instantiate
)
1196 plat
.scan_tree(add_root
=instantiate
)
1197 plat
.prepare_nodes()
1198 plat
.scan_reg_sizes()
1199 plat
.setup_output_dirs(output_dirs
)
1201 plat
.scan_phandles()
1202 plat
.process_nodes(instantiate
)
1206 # Figure out what output files we plan to generate
1207 output_files
= dict(OUTPUT_FILES_COMMON
)
1209 output_files
.update(OUTPUT_FILES_INST
)
1211 output_files
.update(OUTPUT_FILES_NOINST
)
1213 cmds
= args
[0].split(',')
1215 cmds
= sorted(output_files
.keys())
1217 outfile
= output_files
.get(cmd
)
1219 raise ValueError("Unknown command '%s': (use: %s)" %
1220 (cmd
, ', '.join(sorted(output_files
.keys()))))
1221 plat
.setup_output(outfile
.ftype
,
1222 outfile
.fname
if output_dirs
else output
)
1223 plat
.out_header(outfile
)
1224 outfile
.method(plat
)
1225 plat
.finish_output()
1227 if not warning_disabled
:
1228 scan
.show_warnings()