blob: dd97a6be705eaa26cfa53e03b5b1aa58f5942a2f [file] [log] [blame]
Simon Glass7581c012017-06-18 22:08:58 -06001#!/usr/bin/python
Tom Rini83d290c2018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Simon Glass7581c012017-06-18 22:08:58 -06003#
4# Copyright (C) 2017 Google, Inc
5# Written by Simon Glass <sjg@chromium.org>
6#
Simon Glass7581c012017-06-18 22:08:58 -06007
Simon Glass2be282c2017-06-18 22:08:59 -06008"""Device tree to platform data class
9
10This supports converting device tree data to C structures definitions and
11static data.
Simon Glass9b330382020-11-08 20:36:21 -070012
13See doc/driver-model/of-plat.rst for more informaiton
Simon Glass2be282c2017-06-18 22:08:59 -060014"""
15
Simon Glass8fed2eb2017-08-29 14:15:55 -060016import collections
Simon Glass7581c012017-06-18 22:08:58 -060017import copy
Simon Glassbe44f272020-12-28 20:34:51 -070018from enum import IntEnum
Walter Lozanodac82282020-07-03 08:07:17 -030019import os
20import re
Simon Glass2be282c2017-06-18 22:08:59 -060021import sys
Simon Glass7581c012017-06-18 22:08:58 -060022
Simon Glassbf776672020-04-17 18:09:04 -060023from dtoc import fdt
24from dtoc import fdt_util
Simon Glassa542a702020-12-28 20:35:06 -070025from dtoc import src_scan
26from dtoc.src_scan import conv_name_to_c
Simon Glass7581c012017-06-18 22:08:58 -060027
Simon Glass9b330382020-11-08 20:36:21 -070028# When we see these properties we ignore them - i.e. do not create a structure
29# member
Simon Glass7581c012017-06-18 22:08:58 -060030PROP_IGNORE_LIST = [
31 '#address-cells',
32 '#gpio-cells',
33 '#size-cells',
34 'compatible',
35 'linux,phandle',
36 "status",
37 'phandle',
38 'u-boot,dm-pre-reloc',
39 'u-boot,dm-tpl',
40 'u-boot,dm-spl',
41]
42
Simon Glass5ea9dcc2020-11-08 20:36:17 -070043# C type declarations for the types we support
Simon Glass7581c012017-06-18 22:08:58 -060044TYPE_NAMES = {
Simon Glass5ea9dcc2020-11-08 20:36:17 -070045 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',
Simon Glass2be282c2017-06-18 22:08:59 -060050}
Simon Glass7581c012017-06-18 22:08:58 -060051
52STRUCT_PREFIX = 'dtd_'
53VAL_PREFIX = 'dtv_'
54
Simon Glass8840bc52021-02-03 06:01:18 -070055# Properties which are considered to be phandles
56# key: property name
57# value: name of associated #cells property in the target node
58#
59# New phandle properties must be added here; otherwise they will come through as
60# simple integers and finding devices by phandle will not work.
61# Any property that ends with one of these (e.g. 'cd-gpios') will be considered
62# a phandle property.
63PHANDLE_PROPS = {
64 'clocks': '#clock-cells',
65 'gpios': '#gpio-cells',
66 'sandbox,emul': '#emul-cells',
67 }
68
Simon Glassbe44f272020-12-28 20:34:51 -070069class Ftype(IntEnum):
70 SOURCE, HEADER = range(2)
71
72
73# This holds information about each type of output file dtoc can create
74# type: Type of file (Ftype)
Simon Glassd1055d62020-12-28 20:35:00 -070075# fname: Filename excluding directory, e.g. 'dt-plat.c'
76# hdr_comment: Comment explaining the purpose of the file
77OutputFile = collections.namedtuple('OutputFile',
Simon Glassa7d5f962020-12-28 20:35:02 -070078 ['ftype', 'fname', 'method', 'hdr_comment'])
Simon Glassbe44f272020-12-28 20:34:51 -070079
Simon Glass8fed2eb2017-08-29 14:15:55 -060080# This holds information about a property which includes phandles.
81#
82# max_args: integer: Maximum number or arguments that any phandle uses (int).
83# args: Number of args for each phandle in the property. The total number of
84# phandles is len(args). This is a list of integers.
85PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
86
Simon Glass97136eb2020-10-03 09:25:19 -060087# Holds a single phandle link, allowing a C struct value to be assigned to point
88# to a device
89#
90# var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node')
91# dev_name: Name of device to assign to (e.g. 'clock')
92PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name'])
93
Simon Glass8fed2eb2017-08-29 14:15:55 -060094
Simon Glass2be282c2017-06-18 22:08:59 -060095def tab_to(num_tabs, line):
96 """Append tabs to a line of text to reach a tab stop.
Simon Glass7581c012017-06-18 22:08:58 -060097
Simon Glass2be282c2017-06-18 22:08:59 -060098 Args:
Simon Glass9b330382020-11-08 20:36:21 -070099 num_tabs (int): Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
100 line (str): Line of text to append to
Simon Glass2be282c2017-06-18 22:08:59 -0600101
102 Returns:
Simon Glass9b330382020-11-08 20:36:21 -0700103 str: line with the correct number of tabs appeneded. If the line already
Simon Glass2be282c2017-06-18 22:08:59 -0600104 extends past that tab stop then a single space is appended.
105 """
106 if len(line) >= num_tabs * 8:
107 return line + ' '
108 return line + '\t' * (num_tabs - len(line) // 8)
109
Simon Glass56e0bbe2017-06-18 22:09:02 -0600110def get_value(ftype, value):
111 """Get a value as a C expression
112
113 For integers this returns a byte-swapped (little-endian) hex string
114 For bytes this returns a hex string, e.g. 0x12
115 For strings this returns a literal string enclosed in quotes
116 For booleans this return 'true'
117
118 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700119 ftype (fdt.Type): Data type (fdt_util)
120 value (bytes): Data value, as a string of bytes
121
122 Returns:
123 str: String representation of the value
Simon Glass56e0bbe2017-06-18 22:09:02 -0600124 """
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700125 if ftype == fdt.Type.INT:
Simon Glassccc3da72020-12-23 08:11:19 -0700126 val = '%#x' % fdt_util.fdt32_to_cpu(value)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700127 elif ftype == fdt.Type.BYTE:
Simon Glass78128d52020-12-03 16:55:16 -0700128 char = value[0]
Simon Glassccc3da72020-12-23 08:11:19 -0700129 val = '%#x' % (ord(char) if isinstance(char, str) else char)
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700130 elif ftype == fdt.Type.STRING:
Simon Glassf02d0eb2020-07-07 21:32:06 -0600131 # Handle evil ACPI backslashes by adding another backslash before them.
132 # So "\\_SB.GPO0" in the device tree effectively stays like that in C
Simon Glassccc3da72020-12-23 08:11:19 -0700133 val = '"%s"' % value.replace('\\', '\\\\')
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700134 elif ftype == fdt.Type.BOOL:
Simon Glassccc3da72020-12-23 08:11:19 -0700135 val = 'true'
Simon Glass9b330382020-11-08 20:36:21 -0700136 else: # ftype == fdt.Type.INT64:
Simon Glassccc3da72020-12-23 08:11:19 -0700137 val = '%#x' % value
138 return val
Simon Glass56e0bbe2017-06-18 22:09:02 -0600139
Simon Glass56e0bbe2017-06-18 22:09:02 -0600140
Simon Glassccc3da72020-12-23 08:11:19 -0700141class DtbPlatdata():
Simon Glass7581c012017-06-18 22:08:58 -0600142 """Provide a means to convert device tree binary data to platform data
143
144 The output of this process is C structures which can be used in space-
145 constrained encvironments where the ~3KB code overhead of device tree
146 code is not affordable.
147
148 Properties:
Simon Glassa542a702020-12-28 20:35:06 -0700149 _scan: Scan object, for scanning and reporting on useful information
150 from the U-Boot source code
Simon Glass2be282c2017-06-18 22:08:59 -0600151 _fdt: Fdt object, referencing the device tree
Simon Glass7581c012017-06-18 22:08:58 -0600152 _dtb_fname: Filename of the input device tree binary file
Simon Glass074197a2021-02-03 06:01:09 -0700153 _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)
Simon Glasse36024b2017-06-18 22:09:01 -0600157 _include_disabled: true to include nodes marked status = "disabled"
Simon Glass7581c012017-06-18 22:08:58 -0600158 _outfile: The current output file (sys.stdout or a real file)
159 _lines: Stashed list of output lines for outputting in the future
Simon Glassbe44f272020-12-28 20:34:51 -0700160 _dirname: Directory to hold output files, or None for none (all files
161 go to stdout)
Simon Glassa7d5f962020-12-28 20:35:02 -0700162 _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
Simon Glass1e0f3f42020-12-28 20:35:03 -0700167 _basedir (str): Base directory of source tree
Simon Glass337d6972021-02-03 06:01:10 -0700168 _valid_uclasses (list of src_scan.Uclass): List of uclasses needed for
169 the selected devices (see _valid_node), in alphabetical order
Simon Glass4a092352021-02-03 06:01:12 -0700170 _instantiate: Instantiate devices so they don't need to be bound at
171 run-time
Simon Glass7581c012017-06-18 22:08:58 -0600172 """
Simon Glass4a092352021-02-03 06:01:12 -0700173 def __init__(self, scan, dtb_fname, include_disabled, instantiate=False):
Simon Glassa542a702020-12-28 20:35:06 -0700174 self._scan = scan
Simon Glass2be282c2017-06-18 22:08:59 -0600175 self._fdt = None
Simon Glass7581c012017-06-18 22:08:58 -0600176 self._dtb_fname = dtb_fname
177 self._valid_nodes = None
Simon Glass074197a2021-02-03 06:01:09 -0700178 self._valid_nodes_unsorted = None
Simon Glasse36024b2017-06-18 22:09:01 -0600179 self._include_disabled = include_disabled
Simon Glass7581c012017-06-18 22:08:58 -0600180 self._outfile = None
181 self._lines = []
Simon Glassbe44f272020-12-28 20:34:51 -0700182 self._dirnames = [None] * len(Ftype)
Simon Glassa7d5f962020-12-28 20:35:02 -0700183 self._struct_data = collections.OrderedDict()
Simon Glass1e0f3f42020-12-28 20:35:03 -0700184 self._basedir = None
Simon Glass337d6972021-02-03 06:01:10 -0700185 self._valid_uclasses = None
Simon Glass4a092352021-02-03 06:01:12 -0700186 self._instantiate = instantiate
Walter Lozanodac82282020-07-03 08:07:17 -0300187
Simon Glassbe44f272020-12-28 20:34:51 -0700188 def setup_output_dirs(self, output_dirs):
189 """Set up the output directories
190
191 This should be done before setup_output() is called
192
193 Args:
194 output_dirs (tuple of str):
195 Directory to use for C output files.
196 Use None to write files relative current directory
197 Directory to use for H output files.
198 Defaults to the C output dir
199 """
200 def process_dir(ftype, dirname):
201 if dirname:
202 os.makedirs(dirname, exist_ok=True)
203 self._dirnames[ftype] = dirname
204
205 if output_dirs:
206 c_dirname = output_dirs[0]
207 h_dirname = output_dirs[1] if len(output_dirs) > 1 else c_dirname
208 process_dir(Ftype.SOURCE, c_dirname)
209 process_dir(Ftype.HEADER, h_dirname)
210
211 def setup_output(self, ftype, fname):
Simon Glass7581c012017-06-18 22:08:58 -0600212 """Set up the output destination
213
Simon Glass2be282c2017-06-18 22:08:59 -0600214 Once this is done, future calls to self.out() will output to this
Simon Glassbe44f272020-12-28 20:34:51 -0700215 file. The file used is as follows:
216
217 self._dirnames[ftype] is None: output to fname, or stdout if None
218 self._dirnames[ftype] is not None: output to fname in that directory
219
220 Calling this function multiple times will close the old file and open
221 the new one. If they are the same file, nothing happens and output will
222 continue to the same file.
Simon Glass7581c012017-06-18 22:08:58 -0600223
224 Args:
Simon Glassbe44f272020-12-28 20:34:51 -0700225 ftype (str): Type of file to create ('c' or 'h')
226 fname (str): Filename to send output to. If there is a directory in
227 self._dirnames for this file type, it will be put in that
228 directory
Simon Glass7581c012017-06-18 22:08:58 -0600229 """
Simon Glassbe44f272020-12-28 20:34:51 -0700230 dirname = self._dirnames[ftype]
231 if dirname:
232 pathname = os.path.join(dirname, fname)
233 if self._outfile:
234 self._outfile.close()
235 self._outfile = open(pathname, 'w')
236 elif fname:
237 if not self._outfile:
238 self._outfile = open(fname, 'w')
Simon Glassf62cea02020-12-28 20:34:48 -0700239 else:
240 self._outfile = sys.stdout
Simon Glass7581c012017-06-18 22:08:58 -0600241
Simon Glassbe44f272020-12-28 20:34:51 -0700242 def finish_output(self):
243 """Finish outputing to a file
244
245 This closes the output file, if one is in use
246 """
247 if self._outfile != sys.stdout:
248 self._outfile.close()
Simon Glassea74c952021-02-03 06:01:20 -0700249 self._outfile = None
Simon Glassbe44f272020-12-28 20:34:51 -0700250
Simon Glass2be282c2017-06-18 22:08:59 -0600251 def out(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600252 """Output a string to the output file
253
254 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700255 line (str): String to output
Simon Glass7581c012017-06-18 22:08:58 -0600256 """
Simon Glass2be282c2017-06-18 22:08:59 -0600257 self._outfile.write(line)
Simon Glass7581c012017-06-18 22:08:58 -0600258
Simon Glass2be282c2017-06-18 22:08:59 -0600259 def buf(self, line):
Simon Glass7581c012017-06-18 22:08:58 -0600260 """Buffer up a string to send later
261
262 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700263 line (str): String to add to our 'buffer' list
Simon Glass7581c012017-06-18 22:08:58 -0600264 """
Simon Glass2be282c2017-06-18 22:08:59 -0600265 self._lines.append(line)
Simon Glass7581c012017-06-18 22:08:58 -0600266
Simon Glass2be282c2017-06-18 22:08:59 -0600267 def get_buf(self):
Simon Glass7581c012017-06-18 22:08:58 -0600268 """Get the contents of the output buffer, and clear it
269
270 Returns:
Simon Glass9b330382020-11-08 20:36:21 -0700271 list(str): The output buffer, which is then cleared for future use
Simon Glass7581c012017-06-18 22:08:58 -0600272 """
273 lines = self._lines
274 self._lines = []
275 return lines
276
Simon Glassd1055d62020-12-28 20:35:00 -0700277 def out_header(self, outfile):
278 """Output a message indicating that this is an auto-generated file
279
280 Args:
281 outfile: OutputFile describing the file being generated
282 """
Simon Glassd5031142017-08-29 14:16:01 -0600283 self.out('''/*
284 * DO NOT MODIFY
285 *
Simon Glassd1055d62020-12-28 20:35:00 -0700286 * %s.
287 * This was generated by dtoc from a .dtb (device tree binary) file.
Simon Glassd5031142017-08-29 14:16:01 -0600288 */
289
Simon Glassd1055d62020-12-28 20:35:00 -0700290''' % outfile.hdr_comment)
Simon Glassd5031142017-08-29 14:16:01 -0600291
Simon Glass8fed2eb2017-08-29 14:15:55 -0600292 def get_phandle_argc(self, prop, node_name):
293 """Check if a node contains phandles
Simon Glass2925c262017-08-29 14:15:54 -0600294
Simon Glass8fed2eb2017-08-29 14:15:55 -0600295 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.
Simon Glass2925c262017-08-29 14:15:54 -0600297
Simon Glass8fed2eb2017-08-29 14:15:55 -0600298 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700299 prop (fdt.Prop): Prop object to check
300 node_name (str): Node name, only used for raising an error
301 Returns:
302 int or None: Number of argument cells is this is a phandle,
303 else None
304 Raises:
305 ValueError: if the phandle cannot be parsed or the required property
306 is not present
Simon Glass8fed2eb2017-08-29 14:15:55 -0600307 """
Simon Glass8840bc52021-02-03 06:01:18 -0700308 cells_prop = None
309 for name, cprop in PHANDLE_PROPS.items():
310 if prop.name.endswith(name):
311 cells_prop = cprop
312 if cells_prop:
Simon Glass760b7172018-07-06 10:27:31 -0600313 if not isinstance(prop.value, list):
314 prop.value = [prop.value]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600315 val = prop.value
Simon Glass8fed2eb2017-08-29 14:15:55 -0600316 i = 0
317
318 max_args = 0
319 args = []
320 while i < len(val):
321 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass760b7172018-07-06 10:27:31 -0600322 # If we get to the end of the list, stop. This can happen
323 # since some nodes have more phandles in the list than others,
324 # but we allocate enough space for the largest list. So those
325 # nodes with shorter lists end up with zeroes at the end.
326 if not phandle:
327 break
Simon Glass8fed2eb2017-08-29 14:15:55 -0600328 target = self._fdt.phandle_to_node.get(phandle)
329 if not target:
330 raise ValueError("Cannot parse '%s' in node '%s'" %
331 (prop.name, node_name))
Simon Glass8840bc52021-02-03 06:01:18 -0700332 cells = target.props.get(cells_prop)
Simon Glass8fed2eb2017-08-29 14:15:55 -0600333 if not cells:
Walter Lozanoad340172020-06-25 01:10:16 -0300334 raise ValueError("Node '%s' has no cells property" %
Simon Glass8840bc52021-02-03 06:01:18 -0700335 target.name)
Simon Glass8fed2eb2017-08-29 14:15:55 -0600336 num_args = fdt_util.fdt32_to_cpu(cells.value)
337 max_args = max(max_args, num_args)
338 args.append(num_args)
339 i += 1 + num_args
340 return PhandleInfo(max_args, args)
341 return None
Simon Glass2925c262017-08-29 14:15:54 -0600342
Simon Glass2be282c2017-06-18 22:08:59 -0600343 def scan_dtb(self):
Anatolij Gustschinf1a7ba12017-08-18 17:58:51 +0200344 """Scan the device tree to obtain a tree of nodes and properties
Simon Glass7581c012017-06-18 22:08:58 -0600345
Simon Glass2be282c2017-06-18 22:08:59 -0600346 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glass7581c012017-06-18 22:08:58 -0600347 device tree root node, and progress from there.
348 """
Simon Glass2be282c2017-06-18 22:08:59 -0600349 self._fdt = fdt.FdtScan(self._dtb_fname)
Simon Glass7581c012017-06-18 22:08:58 -0600350
Simon Glass074197a2021-02-03 06:01:09 -0700351 def scan_node(self, node, valid_nodes):
Simon Glass2be282c2017-06-18 22:08:59 -0600352 """Scan a node and subnodes to build a tree of node and phandle info
353
Simon Glass074197a2021-02-03 06:01:09 -0700354 This adds each subnode to self._valid_nodes if it is enabled and has a
355 compatible string.
Simon Glass2be282c2017-06-18 22:08:59 -0600356
357 Args:
Simon Glass074197a2021-02-03 06:01:09 -0700358 node (Node): Node for scan for subnodes
Simon Glassccc3da72020-12-23 08:11:19 -0700359 valid_nodes (list of Node): List of Node objects to add to
Simon Glass2be282c2017-06-18 22:08:59 -0600360 """
Simon Glass074197a2021-02-03 06:01:09 -0700361 for subnode in node.subnodes:
362 if 'compatible' in subnode.props:
363 status = subnode.props.get('status')
Simon Glasse36024b2017-06-18 22:09:01 -0600364 if (not self._include_disabled and not status or
Simon Glass2be282c2017-06-18 22:08:59 -0600365 status.value != 'disabled'):
Simon Glass074197a2021-02-03 06:01:09 -0700366 valid_nodes.append(subnode)
Simon Glass7581c012017-06-18 22:08:58 -0600367
368 # recurse to handle any subnodes
Simon Glass074197a2021-02-03 06:01:09 -0700369 self.scan_node(subnode, valid_nodes)
Simon Glass7581c012017-06-18 22:08:58 -0600370
Simon Glass50aae3e2021-02-03 06:01:11 -0700371 def scan_tree(self, add_root):
Simon Glass7581c012017-06-18 22:08:58 -0600372 """Scan the device tree for useful information
373
374 This fills in the following properties:
Simon Glass074197a2021-02-03 06:01:09 -0700375 _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
Simon Glass50aae3e2021-02-03 06:01:11 -0700378
379 Args:
380 add_root: True to add the root node also (which wouldn't normally
381 be added as it may not have a compatible string)
Simon Glass7581c012017-06-18 22:08:58 -0600382 """
Simon Glass074197a2021-02-03 06:01:09 -0700383 root = self._fdt.GetRoot()
Simon Glass1b272732020-10-03 11:31:25 -0600384 valid_nodes = []
Simon Glass50aae3e2021-02-03 06:01:11 -0700385 if add_root:
386 valid_nodes.append(root)
Simon Glass074197a2021-02-03 06:01:09 -0700387 self.scan_node(root, valid_nodes)
388 self._valid_nodes_unsorted = valid_nodes
Simon Glass1b272732020-10-03 11:31:25 -0600389 self._valid_nodes = sorted(valid_nodes,
390 key=lambda x: conv_name_to_c(x.name))
Simon Glass51d5d052021-02-03 06:00:58 -0700391
392 def prepare_nodes(self):
393 """Add extra properties to the nodes we are using
394
395 The following properties are added for use by dtoc:
396 idx: Index number of this node (0=first, etc.)
397 struct_name: Name of the struct dtd used by this node
398 var_name: C name for this node
399 child_devs: List of child devices for this node, each a None
400 child_refs: Dict of references for each child:
401 key: Position in child list (-1=head, 0=first, 1=second, ...
402 n-1=last, n=head)
403 seq: Sequence number of the device (unique within its uclass), or
404 -1 not not known yet
405 dev_ref: Reference to this device, e.g. 'DM_DEVICE_REF(serial)'
406 driver: Driver record for this node, or None if not known
407 uclass: Uclass record for this node, or None if not known
408 uclass_seq: Position of this device within the uclass list (0=first,
409 n-1=last)
410 parent_seq: Position of this device within it siblings (0=first,
411 n-1=last)
412 parent_driver: Driver record of the node's parent, or None if none.
413 We don't use node.parent.driver since node.parent may not be in
414 the list of valid nodes
415 """
Simon Glass1b272732020-10-03 11:31:25 -0600416 for idx, node in enumerate(self._valid_nodes):
417 node.idx = idx
Simon Glass51d5d052021-02-03 06:00:58 -0700418 node.struct_name, _ = self._scan.get_normalized_compat_name(node)
419 node.var_name = conv_name_to_c(node.name)
420 node.child_devs = []
421 node.child_refs = {}
422 node.seq = -1
423 node.dev_ref = None
424 node.driver = None
425 node.uclass = None
426 node.uclass_seq = None
427 node.parent_seq = None
428 node.parent_driver = None
Simon Glass7581c012017-06-18 22:08:58 -0600429
Simon Glassc20ee0e2017-08-29 14:15:50 -0600430 @staticmethod
431 def get_num_cells(node):
432 """Get the number of cells in addresses and sizes for this node
433
434 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700435 node (fdt.None): Node to check
Simon Glassc20ee0e2017-08-29 14:15:50 -0600436
437 Returns:
438 Tuple:
439 Number of address cells for this node
440 Number of size cells for this node
441 """
442 parent = node.parent
Simon Glass78128d52020-12-03 16:55:16 -0700443 num_addr, num_size = 2, 2
Simon Glassc20ee0e2017-08-29 14:15:50 -0600444 if parent:
Simon Glass78128d52020-12-03 16:55:16 -0700445 addr_prop = parent.props.get('#address-cells')
446 size_prop = parent.props.get('#size-cells')
447 if addr_prop:
448 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
449 if size_prop:
450 num_size = fdt_util.fdt32_to_cpu(size_prop.value)
451 return num_addr, num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600452
453 def scan_reg_sizes(self):
454 """Scan for 64-bit 'reg' properties and update the values
455
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
458 C code can read.
459 """
460 for node in self._valid_nodes:
461 reg = node.props.get('reg')
462 if not reg:
463 continue
Simon Glass78128d52020-12-03 16:55:16 -0700464 num_addr, num_size = self.get_num_cells(node)
465 total = num_addr + num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600466
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700467 if reg.type != fdt.Type.INT:
Simon Glassdfe5f5b2018-07-06 10:27:32 -0600468 raise ValueError("Node '%s' reg property is not an int" %
469 node.name)
Simon Glassc20ee0e2017-08-29 14:15:50 -0600470 if len(reg.value) % total:
Simon Glass9b330382020-11-08 20:36:21 -0700471 raise ValueError(
472 "Node '%s' reg property has %d cells "
473 'which is not a multiple of na + ns = %d + %d)' %
Simon Glass78128d52020-12-03 16:55:16 -0700474 (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:
Simon Glass5ea9dcc2020-11-08 20:36:17 -0700478 reg.type = fdt.Type.INT64
Simon Glassc20ee0e2017-08-29 14:15:50 -0600479 i = 0
480 new_value = []
481 val = reg.value
482 if not isinstance(val, list):
483 val = [val]
484 while i < len(val):
Simon Glass78128d52020-12-03 16:55:16 -0700485 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
486 i += num_addr
487 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
488 i += num_size
Simon Glassc20ee0e2017-08-29 14:15:50 -0600489 new_value += [addr, size]
490 reg.value = new_value
491
Simon Glass2be282c2017-06-18 22:08:59 -0600492 def scan_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600493 """Scan the device tree building up the C structures we will use.
494
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.
499
500 Once the widest property is determined, all other properties are
501 updated to match that width.
Simon Glasse4fb5fa2020-10-03 11:31:24 -0600502
Simon Glassa7d5f962020-12-28 20:35:02 -0700503 The results are written to self._struct_data
Simon Glass7581c012017-06-18 22:08:58 -0600504 """
Simon Glassa7d5f962020-12-28 20:35:02 -0700505 structs = self._struct_data
Simon Glass7581c012017-06-18 22:08:58 -0600506 for node in self._valid_nodes:
Simon Glass7581c012017-06-18 22:08:58 -0600507 fields = {}
508
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)
513
Simon Glasse525fea2021-02-03 06:00:59 -0700514 # 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]
Simon Glass7581c012017-06-18 22:08:58 -0600517 for name, prop in fields.items():
518 oldprop = struct.get(name)
519 if oldprop:
520 oldprop.Widen(prop)
521 else:
522 struct[name] = prop
523
524 # Otherwise store this as a new struct.
525 else:
Simon Glasse525fea2021-02-03 06:00:59 -0700526 structs[node.struct_name] = fields
Simon Glass7581c012017-06-18 22:08:58 -0600527
Simon Glass7581c012017-06-18 22:08:58 -0600528 for node in self._valid_nodes:
Simon Glasse525fea2021-02-03 06:00:59 -0700529 struct = structs[node.struct_name]
Simon Glass7581c012017-06-18 22:08:58 -0600530 for name, prop in node.props.items():
531 if name not in PROP_IGNORE_LIST and name[0] != '#':
532 prop.Widen(struct[name])
Simon Glass7581c012017-06-18 22:08:58 -0600533
Simon Glass2be282c2017-06-18 22:08:59 -0600534 def scan_phandles(self):
Simon Glass7581c012017-06-18 22:08:58 -0600535 """Figure out what phandles each node uses
536
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
540 declared.
541
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.
544 """
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] == '#':
549 continue
Simon Glass8fed2eb2017-08-29 14:15:55 -0600550 info = self.get_phandle_argc(prop, node.name)
551 if info:
Simon Glass8fed2eb2017-08-29 14:15:55 -0600552 # Process the list as pairs of (phandle, id)
Simon Glass634eba42017-08-29 14:15:59 -0600553 pos = 0
554 for args in info.args:
555 phandle_cell = prop.value[pos]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600556 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
557 target_node = self._fdt.phandle_to_node[phandle]
558 node.phandles.add(target_node)
Simon Glass634eba42017-08-29 14:15:59 -0600559 pos += 1 + args
Simon Glass7581c012017-06-18 22:08:58 -0600560
561
Simon Glassa7d5f962020-12-28 20:35:02 -0700562 def generate_structs(self):
Simon Glass7581c012017-06-18 22:08:58 -0600563 """Generate struct defintions for the platform data
564
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
Heinrich Schuchardt2799a692020-02-25 21:35:39 +0100567 doc/driver-model/of-plat.rst for more information.
Simon Glass7581c012017-06-18 22:08:58 -0600568 """
Simon Glassa7d5f962020-12-28 20:35:02 -0700569 structs = self._struct_data
Simon Glass2be282c2017-06-18 22:08:59 -0600570 self.out('#include <stdbool.h>\n')
Masahiro Yamadab08c8c42018-03-05 01:20:11 +0900571 self.out('#include <linux/libfdt.h>\n')
Simon Glass7581c012017-06-18 22:08:58 -0600572
573 # Output the struct definition
574 for name in sorted(structs):
Simon Glass2be282c2017-06-18 22:08:59 -0600575 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glass7581c012017-06-18 22:08:58 -0600576 for pname in sorted(structs[name]):
577 prop = structs[name][pname]
Simon Glass8fed2eb2017-08-29 14:15:55 -0600578 info = self.get_phandle_argc(prop, structs[name])
579 if info:
Simon Glass7581c012017-06-18 22:08:58 -0600580 # For phandles, include a reference to the target
Simon Glass0d154632017-08-29 14:15:56 -0600581 struct_name = 'struct phandle_%d_arg' % info.max_args
582 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass2be282c2017-06-18 22:08:59 -0600583 conv_name_to_c(prop.name),
Simon Glass634eba42017-08-29 14:15:59 -0600584 len(info.args)))
Simon Glass7581c012017-06-18 22:08:58 -0600585 else:
586 ptype = TYPE_NAMES[prop.type]
Simon Glass2be282c2017-06-18 22:08:59 -0600587 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))
591 self.out(';\n')
592 self.out('};\n')
Simon Glass7581c012017-06-18 22:08:58 -0600593
Simon Glassabf0c802020-12-23 08:11:20 -0700594 def _output_list(self, node, prop):
595 """Output the C code for a devicetree property that holds a list
596
597 Args:
598 node (fdt.Node): Node to output
599 prop (fdt.Prop): Prop to output
600 """
601 self.buf('{')
602 vals = []
603 # For phandles, output a reference to the platform data
604 # of the target node.
605 info = self.get_phandle_argc(prop, node.name)
606 if info:
607 # Process the list as pairs of (phandle, id)
608 pos = 0
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]
613 arg_values = []
614 for i in range(args):
615 arg_values.append(
616 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
617 pos += 1 + args
618 vals.append('\t{%d, {%s}}' % (target_node.idx,
619 ', '.join(arg_values)))
620 for val in vals:
621 self.buf('\n\t\t%s,' % val)
622 else:
623 for val in prop.value:
624 vals.append(get_value(prop.type, val))
625
626 # Put 8 values per line to avoid very long lines.
627 for i in range(0, len(vals), 8):
628 if i:
629 self.buf(',\n\t\t')
630 self.buf(', '.join(vals[i:i + 8]))
631 self.buf('}')
632
Simon Glasse525fea2021-02-03 06:00:59 -0700633 def _declare_device(self, node):
Simon Glass221ddc12020-12-23 08:11:21 -0700634 """Add a device declaration to the output
635
Simon Glass20e442a2020-12-28 20:34:54 -0700636 This declares a U_BOOT_DRVINFO() for the device being processed
Simon Glass221ddc12020-12-23 08:11:21 -0700637
638 Args:
Simon Glasse525fea2021-02-03 06:00:59 -0700639 node: Node to process
Simon Glass221ddc12020-12-23 08:11:21 -0700640 """
Simon Glasse525fea2021-02-03 06:00:59 -0700641 self.buf('U_BOOT_DRVINFO(%s) = {\n' % node.var_name)
642 self.buf('\t.name\t\t= "%s",\n' % node.struct_name)
Simon Glass9763e4e2021-02-03 06:01:19 -0700643 self.buf('\t.plat\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
Simon Glasse525fea2021-02-03 06:00:59 -0700644 self.buf('\t.plat_size\t= sizeof(%s%s),\n' %
645 (VAL_PREFIX, node.var_name))
Simon Glass221ddc12020-12-23 08:11:21 -0700646 idx = -1
Simon Glasse525fea2021-02-03 06:00:59 -0700647 if node.parent and node.parent in self._valid_nodes:
648 idx = node.parent.idx
Simon Glass221ddc12020-12-23 08:11:21 -0700649 self.buf('\t.parent_idx\t= %d,\n' % idx)
650 self.buf('};\n')
651 self.buf('\n')
652
Simon Glassea74c952021-02-03 06:01:20 -0700653 def prep_priv(self, struc, name, suffix, section='.priv_data'):
654 if not struc:
655 return None
656 var_name = '_%s%s' % (name, suffix)
657 hdr = self._scan._structs.get(struc)
658 if hdr:
659 self.buf('#include <%s>\n' % hdr.fname)
660 else:
661 print('Warning: Cannot find header file for struct %s' % struc)
662 attr = '__attribute__ ((section ("%s")))' % section
663 return var_name, struc, attr
664
665 def alloc_priv(self, info, name, extra, suffix='_priv'):
666 result = self.prep_priv(info, name, suffix)
667 if not result:
668 return None
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)
673
Simon Glassd392d322021-02-03 06:01:21 -0700674 def alloc_plat(self, info, name, extra, node):
675 result = self.prep_priv(info, name, '_plat')
676 if not result:
677 return None
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)
684 self.buf('\t},\n')
685 self.buf('};\n')
686 return '&%s_%s' % (var_name, extra)
687
688 def _declare_device_inst(self, node, parent_driver):
689 """Add a device instance declaration to the output
690
691 This declares a DM_DEVICE_INST() for the device being processed
692
693 Args:
694 node: Node to output
695 """
696 driver = node.driver
697 uclass = node.uclass
698 self.buf('\n')
699 num_lines = len(self._lines)
700 plat_name = self.alloc_plat(driver.plat, driver.name, node.var_name,
701 node)
702 priv_name = self.alloc_priv(driver.priv, driver.name, node.var_name)
703 parent_plat_name = None
704 parent_priv_name = None
705 if parent_driver:
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,
709 '_parent_plat')
710 parent_priv_name = self.alloc_priv(
711 parent_driver.child_priv, driver.name, node.var_name,
712 '_parent_priv')
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)
719
720 # Add a blank line if we emitted any stuff above, for readability
721 if num_lines != len(self._lines):
722 self.buf('\n')
723
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)
727 if plat_name:
728 self.buf('\t.plat_\t\t= %s,\n' % plat_name)
729 else:
730 self.buf('\t.plat_\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
731 if parent_plat_name:
732 self.buf('\t.parent_plat_\t= %s,\n' % parent_plat_name)
733 if uclass_plat_name:
734 self.buf('\t.uclass_plat_\t= %s,\n' % uclass_plat_name)
735 driver_date = None
736
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)
743 if driver_data:
744 self.buf('\t.driver_data\t= %s,\n' % driver_data)
745 break
746
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)
750 if priv_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)
753
754 if uclass_priv_name:
755 self.buf('\t.uclass_priv_ = %s,\n' % uclass_priv_name)
756 if parent_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,
762 node.parent_seq)
763 # flags is left as 0
764
765 self.buf('\t.seq_ = %d,\n' % node.seq)
766
767 self.buf('};\n')
768 self.buf('\n')
769 return parent_plat_name
770
771 def _output_prop(self, node, prop, tabs=1):
Simon Glass161dac12020-12-23 08:11:22 -0700772 """Output a line containing the value of a struct member
773
774 Args:
775 node (Node): Node being output
776 prop (Prop): Prop object to output
777 """
778 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
779 return
780 member_name = conv_name_to_c(prop.name)
Simon Glassd392d322021-02-03 06:01:21 -0700781 self.buf('%s%s= ' % ('\t' * tabs, tab_to(3, '.' + member_name)))
Simon Glass161dac12020-12-23 08:11:22 -0700782
783 # Special handling for lists
784 if isinstance(prop.value, list):
785 self._output_list(node, prop)
786 else:
787 self.buf(get_value(prop.type, prop.value))
788 self.buf(',\n')
789
Simon Glasse525fea2021-02-03 06:00:59 -0700790 def _output_values(self, node):
Simon Glass161dac12020-12-23 08:11:22 -0700791 """Output the definition of a device's struct values
792
793 Args:
Simon Glasse525fea2021-02-03 06:00:59 -0700794 node (Node): Node to output
Simon Glass161dac12020-12-23 08:11:22 -0700795 """
796 self.buf('static struct %s%s %s%s = {\n' %
Simon Glasse525fea2021-02-03 06:00:59 -0700797 (STRUCT_PREFIX, node.struct_name, VAL_PREFIX, node.var_name))
Simon Glass161dac12020-12-23 08:11:22 -0700798 for pname in sorted(node.props):
799 self._output_prop(node, node.props[pname])
800 self.buf('};\n')
801
Simon Glassea74c952021-02-03 06:01:20 -0700802 def list_head(self, head_member, node_member, node_refs, var_name):
803 self.buf('\t.%s\t= {\n' % head_member)
804 if node_refs:
805 last = node_refs[-1].dev_ref
806 first = node_refs[0].dev_ref
807 member = node_member
808 else:
809 last = 'DM_DEVICE_REF(%s)' % var_name
810 first = last
811 member = head_member
812 self.buf('\t\t.prev = &%s->%s,\n' % (last, member))
813 self.buf('\t\t.next = &%s->%s,\n' % (first, member))
814 self.buf('\t},\n')
815
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])
820 self.buf('\t},\n')
821
822 def generate_uclasses(self):
823 if not self.check_instantiate(True):
824 return
825 self.out('\n')
826 self.out('#include <common.h>\n')
827 self.out('#include <dm.h>\n')
828 self.out('#include <dt-structs.h>\n')
829 self.out('\n')
830 self.buf('/*\n')
Simon Glassd392d322021-02-03 06:01:21 -0700831 self.buf(
832 " * uclass declarations, ordered by 'struct uclass' linker_list idx:\n")
Simon Glassea74c952021-02-03 06:01:20 -0700833 uclass_list = self._valid_uclasses
Simon Glassd392d322021-02-03 06:01:21 -0700834 for seq, uclass in enumerate(uclass_list):
835 self.buf(' * %3d: %s\n' % (seq, uclass.name))
836 self.buf(' *\n')
837 self.buf(' * Sequence numbers allocated in each uclass:\n')
Simon Glassea74c952021-02-03 06:01:20 -0700838 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))
843 self.buf(' */\n')
844
845 uclass_node = {}
846 for seq, uclass in enumerate(uclass_list):
847 uclass_node[seq] = ('&DM_UCLASS_REF(%s)->sibling_node' %
848 uclass.name)
849 uclass_node[-1] = '&uclass_head'
850 uclass_node[len(uclass_list)] = '&uclass_head'
851 self.buf('\n')
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])
855 self.buf('};\n')
856 self.buf('\n')
857
858 for seq, uclass in enumerate(uclass_list):
859 uc_drv = self._scan._uclass.get(uclass.uclass_id)
860
861 priv_name = self.alloc_priv(uc_drv.priv, uc_drv.name, '')
862
863 self.buf('DM_UCLASS_INST(%s) = {\n' % uclass.name)
864 if priv_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)
869 self.buf('};\n')
870 self.buf('\n')
871 self.out(''.join(self.get_buf()))
872
Simon Glass05953522021-02-03 06:01:07 -0700873 def read_aliases(self):
874 """Read the aliases and attach the information to self._alias
875
876 Raises:
877 ValueError: The alias path is not found
878 """
879 alias_node = self._fdt.GetNode('/aliases')
880 if not alias_node:
881 return
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)
885 if not m_alias:
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)
890 if result is None:
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)
895
Simon Glass426d12f2021-02-03 06:01:14 -0700896 def generate_decl(self):
897 nodes_to_output = list(self._valid_nodes)
898
899 self.buf('#include <dm/device-internal.h>\n')
900 self.buf('#include <dm/uclass-internal.h>\n')
901 self.buf('\n')
902 self.buf(
903 '/* driver declarations - these allow DM_DRIVER_GET() to be used */\n')
904 for node in nodes_to_output:
Simon Glasscff7dcf2021-03-15 17:25:11 +1300905 self.buf('extern U_BOOT_DRIVER(%s);\n' % node.struct_name);
Simon Glass426d12f2021-02-03 06:01:14 -0700906 self.buf('\n')
907
908 if self._instantiate:
909 self.buf(
910 '/* device declarations - these allow DM_DEVICE_REF() to be used */\n')
911 for node in nodes_to_output:
Simon Glasscff7dcf2021-03-15 17:25:11 +1300912 self.buf('extern DM_DEVICE_INST(%s);\n' % node.var_name)
Simon Glass426d12f2021-02-03 06:01:14 -0700913 self.buf('\n')
914
915 uclass_list = self._valid_uclasses
916
917 self.buf(
918 '/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */\n')
919 for uclass in uclass_list:
Simon Glasscff7dcf2021-03-15 17:25:11 +1300920 self.buf('extern UCLASS_DRIVER(%s);\n' % uclass.name)
Simon Glass426d12f2021-02-03 06:01:14 -0700921
922 if self._instantiate:
923 self.buf('\n')
924 self.buf('/* uclass declarations - needed for DM_UCLASS_REF() */\n')
925 for uclass in uclass_list:
Simon Glasscff7dcf2021-03-15 17:25:11 +1300926 self.buf('extern DM_UCLASS_INST(%s);\n' % uclass.name)
Simon Glass426d12f2021-02-03 06:01:14 -0700927 self.out(''.join(self.get_buf()))
928
Simon Glass337d6972021-02-03 06:01:10 -0700929 def assign_seqs(self):
Simon Glass074197a2021-02-03 06:01:09 -0700930 """Assign a sequence number to each node"""
931 for node in self._valid_nodes_unsorted:
Simon Glass337d6972021-02-03 06:01:10 -0700932 seq = self._scan.assign_seq(node)
933 if seq is not None:
934 node.seq = seq
Simon Glass074197a2021-02-03 06:01:09 -0700935
Simon Glassfd471e22021-02-03 06:01:00 -0700936 def process_nodes(self, need_drivers):
937 nodes_to_output = list(self._valid_nodes)
938
Simon Glassb9319c42021-02-03 06:01:01 -0700939 # Figure out which drivers we actually use
940 self._scan.mark_used(nodes_to_output)
941
Simon Glassfd471e22021-02-03 06:01:00 -0700942 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)
945 if not driver:
946 if not need_drivers:
947 continue
948 raise ValueError("Cannot parse/find driver for '%s'" %
949 node.struct_name)
950 node.driver = driver
Simon Glass337d6972021-02-03 06:01:10 -0700951 uclass = self._scan._uclass.get(driver.uclass_id)
952 if not uclass:
953 raise ValueError("Cannot parse/find uclass '%s' for driver '%s'" %
954 (driver.uclass_id, node.struct_name))
955 node.uclass = uclass
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
960
Simon Glassfd471e22021-02-03 06:01:00 -0700961 parent_driver = None
962 if node.parent in self._valid_nodes:
963 parent_driver = self._scan.get_driver(node.parent.struct_name)
964 if not parent_driver:
965 if not need_drivers:
966 continue
967 raise ValueError(
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
975
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
980
Simon Glass337d6972021-02-03 06:01:10 -0700981 uclass_set = set()
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)
987
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
992
Simon Glass4b91be22021-02-03 06:01:15 -0700993 def output_node_plat(self, node):
Simon Glass7581c012017-06-18 22:08:58 -0600994 """Output the C code for a node
995
996 Args:
Simon Glass9b330382020-11-08 20:36:21 -0700997 node (fdt.Node): node to output
Simon Glass7581c012017-06-18 22:08:58 -0600998 """
Simon Glass4b91be22021-02-03 06:01:15 -0700999 driver = node.driver
1000 parent_driver = node.parent_driver
1001
1002 line1 = 'Node %s index %d' % (node.path, node.idx)
1003 if driver:
1004 self.buf('/*\n')
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'))
1008 self.buf(' */\n')
1009 else:
1010 self.buf('/* %s */\n' % line1)
Simon Glass7581c012017-06-18 22:08:58 -06001011
Simon Glasse525fea2021-02-03 06:00:59 -07001012 self._output_values(node)
1013 self._declare_device(node)
Simon Glass7581c012017-06-18 22:08:58 -06001014
Simon Glass2be282c2017-06-18 22:08:59 -06001015 self.out(''.join(self.get_buf()))
Simon Glass7581c012017-06-18 22:08:58 -06001016
Simon Glassd392d322021-02-03 06:01:21 -07001017 def output_node_instance(self, node):
1018 """Output the C code for a node
1019
1020 Args:
1021 node (fdt.Node): node to output
1022 """
1023 parent_driver = node.parent_driver
1024
1025 self.buf('/*\n')
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'))
1029 self.buf('*/\n')
1030
1031 if not node.driver.plat:
1032 self._output_values(node)
1033 self._declare_device_inst(node, parent_driver)
1034
1035 self.out(''.join(self.get_buf()))
1036
Simon Glass4b91be22021-02-03 06:01:15 -07001037 def check_instantiate(self, require):
1038 """Check if self._instantiate is set to the required value
1039
1040 If not, this outputs a message into the current file
1041
1042 Args:
1043 require: True to require --instantiate, False to require that it not
1044 be enabled
1045 """
1046 if require != self._instantiate:
1047 self.out(
1048 '/* This file is not used: --instantiate was %senabled */\n' %
1049 ('not ' if require else ''))
1050 return False
1051 return True
1052
Simon Glassa7d5f962020-12-28 20:35:02 -07001053 def generate_plat(self):
Simon Glass7581c012017-06-18 22:08:58 -06001054 """Generate device defintions for the platform data
1055
1056 This writes out C platform data initialisation data and
Simon Glass20e442a2020-12-28 20:34:54 -07001057 U_BOOT_DRVINFO() declarations for each valid node. Where a node has
Simon Glass7581c012017-06-18 22:08:58 -06001058 multiple compatible strings, a #define is used to make them equivalent.
1059
Heinrich Schuchardt2799a692020-02-25 21:35:39 +01001060 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glass7581c012017-06-18 22:08:58 -06001061 information.
1062 """
Simon Glass4b91be22021-02-03 06:01:15 -07001063 if not self.check_instantiate(False):
1064 return
Simon Glass20e442a2020-12-28 20:34:54 -07001065 self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
Simon Glassf31fa992020-12-28 20:35:01 -07001066 self.out('#define DT_PLAT_C\n')
Simon Glasscb43ac12020-10-03 11:31:41 -06001067 self.out('\n')
Simon Glass2be282c2017-06-18 22:08:59 -06001068 self.out('#include <common.h>\n')
1069 self.out('#include <dm.h>\n')
1070 self.out('#include <dt-structs.h>\n')
1071 self.out('\n')
Simon Glass7581c012017-06-18 22:08:58 -06001072
Simon Glass9763e4e2021-02-03 06:01:19 -07001073 if self._valid_nodes:
1074 self.out('/*\n')
1075 self.out(
1076 " * driver_info declarations, ordered by 'struct driver_info' linker_list idx:\n")
1077 self.out(' *\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))
1084 self.out(' */\n')
1085 self.out('\n')
1086
1087 for node in self._valid_nodes:
1088 self.output_node_plat(node)
Simon Glassfa0ea5b2017-06-18 22:09:03 -06001089
Walter Lozano51f12632020-06-25 01:10:13 -03001090 self.out(''.join(self.get_buf()))
Simon Glassfa0ea5b2017-06-18 22:09:03 -06001091
Simon Glassd392d322021-02-03 06:01:21 -07001092 def generate_device(self):
1093 """Generate device instances
1094
1095 This writes out DM_DEVICE_INST() records for each device in the
1096 build.
1097
1098 See the documentation in doc/driver-model/of-plat.rst for more
1099 information.
1100 """
1101 if not self.check_instantiate(True):
1102 return
1103 self.out('#include <common.h>\n')
1104 self.out('#include <dm.h>\n')
1105 self.out('#include <dt-structs.h>\n')
1106 self.out('\n')
1107
1108 if self._valid_nodes:
1109 self.out('/*\n')
1110 self.out(
1111 " * udevice declarations, ordered by 'struct udevice' linker_list position:\n")
1112 self.out(' *\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))
1119 self.out(' */\n')
1120 self.out('\n')
1121
1122 for node in self._valid_nodes:
1123 self.output_node_instance(node)
1124
1125 self.out(''.join(self.get_buf()))
1126
Simon Glass192c1112020-12-28 20:34:50 -07001127
Simon Glassbe44f272020-12-28 20:34:51 -07001128# Types of output file we understand
1129# key: Command used to generate this file
1130# value: OutputFile for this command
Simon Glass8490c572021-03-25 06:40:51 +13001131OUTPUT_FILES_COMMON = {
Simon Glass426d12f2021-02-03 06:01:14 -07001132 'decl':
1133 OutputFile(Ftype.HEADER, 'dt-decl.h', DtbPlatdata.generate_decl,
1134 'Declares externs for all device/uclass instances'),
Simon Glassd1055d62020-12-28 20:35:00 -07001135 'struct':
1136 OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
Simon Glassa7d5f962020-12-28 20:35:02 -07001137 DtbPlatdata.generate_structs,
Simon Glassd1055d62020-12-28 20:35:00 -07001138 'Defines the structs used to hold devicetree data'),
Simon Glass8490c572021-03-25 06:40:51 +13001139 }
1140
1141# File generated without instantiate
1142OUTPUT_FILES_NOINST = {
Simon Glassd1055d62020-12-28 20:35:00 -07001143 'platdata':
Simon Glassa7d5f962020-12-28 20:35:02 -07001144 OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
Simon Glassd1055d62020-12-28 20:35:00 -07001145 'Declares the U_BOOT_DRIVER() records and platform data'),
Simon Glass8490c572021-03-25 06:40:51 +13001146 }
1147
1148# File generated with instantiate
1149OUTPUT_FILES_INST = {
Simon Glassd392d322021-02-03 06:01:21 -07001150 'device':
1151 OutputFile(Ftype.SOURCE, 'dt-device.c', DtbPlatdata.generate_device,
1152 'Declares the DM_DEVICE_INST() records'),
Simon Glassea74c952021-02-03 06:01:20 -07001153 'uclass':
1154 OutputFile(Ftype.SOURCE, 'dt-uclass.c', DtbPlatdata.generate_uclasses,
1155 'Declares the uclass instances (struct uclass)'),
Simon Glassbe44f272020-12-28 20:34:51 -07001156 }
1157
1158
Simon Glassb00f0062021-02-03 06:01:02 -07001159def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase,
Simon Glass4a092352021-02-03 06:01:12 -07001160 instantiate, warning_disabled=False, drivers_additional=None,
1161 basedir=None, scan=None):
Simon Glassfa0ea5b2017-06-18 22:09:03 -06001162 """Run all the steps of the dtoc tool
1163
1164 Args:
Simon Glass9b330382020-11-08 20:36:21 -07001165 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
Simon Glassf62cea02020-12-28 20:34:48 -07001168 output (str): Name of output file (None for stdout)
Simon Glass192c1112020-12-28 20:34:50 -07001169 output_dirs (tuple of str):
1170 Directory to put C output files
1171 Directory to put H output files
Simon Glassb00f0062021-02-03 06:01:02 -07001172 phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
1173 or 'tpl'. None if not known
Simon Glass4a092352021-02-03 06:01:12 -07001174 instantiate: Instantiate devices so they don't need to be bound at
1175 run-time
Simon Glass78128d52020-12-03 16:55:16 -07001176 warning_disabled (bool): True to avoid showing warnings about missing
1177 drivers
Simon Glassccc3da72020-12-23 08:11:19 -07001178 drivers_additional (list): List of additional drivers to use during
Simon Glass78128d52020-12-03 16:55:16 -07001179 scanning
Simon Glass1e0f3f42020-12-28 20:35:03 -07001180 basedir (str): Base directory of U-Boot source code. Defaults to the
1181 grandparent of this file's directory
Simon Glassa32eb7d2021-02-03 06:00:51 -07001182 scan (src_src.Scanner): Scanner from a previous run. This can help speed
1183 up tests. Use None for normal operation
1184
Simon Glass05953522021-02-03 06:01:07 -07001185 Returns:
1186 DtbPlatdata object
1187
Simon Glass9b330382020-11-08 20:36:21 -07001188 Raises:
1189 ValueError: if args has no command, or an unknown command
Simon Glassfa0ea5b2017-06-18 22:09:03 -06001190 """
1191 if not args:
Simon Glassbe44f272020-12-28 20:34:51 -07001192 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')
Simon Glassfa0ea5b2017-06-18 22:09:03 -06001195
Simon Glassa32eb7d2021-02-03 06:00:51 -07001196 if not scan:
Simon Glass0c59ace2021-03-26 16:17:25 +13001197 scan = src_scan.Scanner(basedir, drivers_additional, phase)
Simon Glassa32eb7d2021-02-03 06:00:51 -07001198 scan.scan_drivers()
Simon Glassfd471e22021-02-03 06:01:00 -07001199 do_process = True
1200 else:
1201 do_process = False
Simon Glass4a092352021-02-03 06:01:12 -07001202 plat = DtbPlatdata(scan, dtb_file, include_disabled, instantiate)
Simon Glassfa0ea5b2017-06-18 22:09:03 -06001203 plat.scan_dtb()
Simon Glass4a092352021-02-03 06:01:12 -07001204 plat.scan_tree(add_root=instantiate)
Simon Glass51d5d052021-02-03 06:00:58 -07001205 plat.prepare_nodes()
Simon Glassc20ee0e2017-08-29 14:15:50 -06001206 plat.scan_reg_sizes()
Simon Glassbe44f272020-12-28 20:34:51 -07001207 plat.setup_output_dirs(output_dirs)
Simon Glassa7d5f962020-12-28 20:35:02 -07001208 plat.scan_structs()
Simon Glassfa0ea5b2017-06-18 22:09:03 -06001209 plat.scan_phandles()
Simon Glass4a092352021-02-03 06:01:12 -07001210 plat.process_nodes(instantiate)
Simon Glass05953522021-02-03 06:01:07 -07001211 plat.read_aliases()
Simon Glass337d6972021-02-03 06:01:10 -07001212 plat.assign_seqs()
Simon Glassfa0ea5b2017-06-18 22:09:03 -06001213
Simon Glass8490c572021-03-25 06:40:51 +13001214 # Figure out what output files we plan to generate
1215 output_files = OUTPUT_FILES_COMMON
1216 if instantiate:
1217 output_files.update(OUTPUT_FILES_INST)
1218 else:
1219 output_files.update(OUTPUT_FILES_NOINST)
1220
Simon Glass10cbd3b2020-12-28 20:34:52 -07001221 cmds = args[0].split(',')
1222 if 'all' in cmds:
Simon Glass8490c572021-03-25 06:40:51 +13001223 cmds = sorted(output_files.keys())
Simon Glass10cbd3b2020-12-28 20:34:52 -07001224 for cmd in cmds:
Simon Glass8490c572021-03-25 06:40:51 +13001225 outfile = output_files.get(cmd)
Simon Glassbe44f272020-12-28 20:34:51 -07001226 if not outfile:
1227 raise ValueError("Unknown command '%s': (use: %s)" %
Simon Glass8490c572021-03-25 06:40:51 +13001228 (cmd, ', '.join(sorted(output_files.keys()))))
Simon Glassbe44f272020-12-28 20:34:51 -07001229 plat.setup_output(outfile.ftype,
1230 outfile.fname if output_dirs else output)
Simon Glassd1055d62020-12-28 20:35:00 -07001231 plat.out_header(outfile)
Simon Glassa7d5f962020-12-28 20:35:02 -07001232 outfile.method(plat)
Simon Glassbe44f272020-12-28 20:34:51 -07001233 plat.finish_output()
Simon Glass0c59ace2021-03-26 16:17:25 +13001234
1235 if not warning_disabled:
1236 scan.show_warnings()
Simon Glass05953522021-02-03 06:01:07 -07001237 return plat