blob: 869c92b49bfcc4ebbf9a763c197bdd12e8b20f5e [file] [log] [blame]
Simon Glassd570dec2017-06-18 22:08:58 -06001#!/usr/bin/python
Tom Rini10e47792018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Simon Glassd570dec2017-06-18 22:08:58 -06003#
4# Copyright (C) 2017 Google, Inc
5# Written by Simon Glass <sjg@chromium.org>
6#
Simon Glassd570dec2017-06-18 22:08:58 -06007
Simon Glass1f730c82017-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 Glass94ee3bd2020-11-08 20:36:21 -070012
13See doc/driver-model/of-plat.rst for more informaiton
Simon Glass1f730c82017-06-18 22:08:59 -060014"""
15
Simon Glassec3b5e42017-08-29 14:15:55 -060016import collections
Simon Glassd570dec2017-06-18 22:08:58 -060017import copy
Simon Glassc3a310a82020-12-28 20:34:51 -070018from enum import IntEnum
Walter Lozanoe675d962020-07-03 08:07:17 -030019import os
20import re
Simon Glass1f730c82017-06-18 22:08:59 -060021import sys
Simon Glassd570dec2017-06-18 22:08:58 -060022
Simon Glassa997ea52020-04-17 18:09:04 -060023from dtoc import fdt
24from dtoc import fdt_util
Simon Glass9065bc92020-12-28 20:35:06 -070025from dtoc import src_scan
26from dtoc.src_scan import conv_name_to_c
Simon Glassd570dec2017-06-18 22:08:58 -060027
Simon Glass94ee3bd2020-11-08 20:36:21 -070028# When we see these properties we ignore them - i.e. do not create a structure
29# member
Simon Glassd570dec2017-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 Glassc9a032c2020-11-08 20:36:17 -070043# C type declarations for the types we support
Simon Glassd570dec2017-06-18 22:08:58 -060044TYPE_NAMES = {
Simon Glassc9a032c2020-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 Glass1f730c82017-06-18 22:08:59 -060050}
Simon Glassd570dec2017-06-18 22:08:58 -060051
52STRUCT_PREFIX = 'dtd_'
53VAL_PREFIX = 'dtv_'
54
Simon Glass4e430a22021-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 Glassc3a310a82020-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 Glass6b208842020-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 Glass55526782020-12-28 20:35:02 -070078 ['ftype', 'fname', 'method', 'hdr_comment'])
Simon Glassc3a310a82020-12-28 20:34:51 -070079
Simon Glassec3b5e42017-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 Glassc1622112020-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 Glassec3b5e42017-08-29 14:15:55 -060094
Simon Glass1f730c82017-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 Glassd570dec2017-06-18 22:08:58 -060097
Simon Glass1f730c82017-06-18 22:08:59 -060098 Args:
Simon Glass94ee3bd2020-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 Glass1f730c82017-06-18 22:08:59 -0600101
102 Returns:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700103 str: line with the correct number of tabs appeneded. If the line already
Simon Glass1f730c82017-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 Glass7a2add12017-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 Glass94ee3bd2020-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 Glass7a2add12017-06-18 22:09:02 -0600124 """
Simon Glassc9a032c2020-11-08 20:36:17 -0700125 if ftype == fdt.Type.INT:
Simon Glass27511232020-12-23 08:11:19 -0700126 val = '%#x' % fdt_util.fdt32_to_cpu(value)
Simon Glassc9a032c2020-11-08 20:36:17 -0700127 elif ftype == fdt.Type.BYTE:
Simon Glass93daa012020-12-03 16:55:16 -0700128 char = value[0]
Simon Glass27511232020-12-23 08:11:19 -0700129 val = '%#x' % (ord(char) if isinstance(char, str) else char)
Simon Glassc9a032c2020-11-08 20:36:17 -0700130 elif ftype == fdt.Type.STRING:
Simon Glass7f5e2262020-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 Glass27511232020-12-23 08:11:19 -0700133 val = '"%s"' % value.replace('\\', '\\\\')
Simon Glassc9a032c2020-11-08 20:36:17 -0700134 elif ftype == fdt.Type.BOOL:
Simon Glass27511232020-12-23 08:11:19 -0700135 val = 'true'
Simon Glass94ee3bd2020-11-08 20:36:21 -0700136 else: # ftype == fdt.Type.INT64:
Simon Glass27511232020-12-23 08:11:19 -0700137 val = '%#x' % value
138 return val
Simon Glass7a2add12017-06-18 22:09:02 -0600139
Simon Glass7a2add12017-06-18 22:09:02 -0600140
Simon Glass27511232020-12-23 08:11:19 -0700141class DtbPlatdata():
Simon Glassd570dec2017-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 Glass9065bc92020-12-28 20:35:06 -0700149 _scan: Scan object, for scanning and reporting on useful information
150 from the U-Boot source code
Simon Glass1f730c82017-06-18 22:08:59 -0600151 _fdt: Fdt object, referencing the device tree
Simon Glassd570dec2017-06-18 22:08:58 -0600152 _dtb_fname: Filename of the input device tree binary file
Simon Glassdf56e0b2021-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 Glasseab3f622017-06-18 22:09:01 -0600157 _include_disabled: true to include nodes marked status = "disabled"
Simon Glassd570dec2017-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 Glassc3a310a82020-12-28 20:34:51 -0700160 _dirname: Directory to hold output files, or None for none (all files
161 go to stdout)
Simon Glass55526782020-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 Glass4f2059b2020-12-28 20:35:03 -0700167 _basedir (str): Base directory of source tree
Simon Glass80d782c42021-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 Glass3809ad92021-02-03 06:01:12 -0700170 _instantiate: Instantiate devices so they don't need to be bound at
171 run-time
Simon Glassd570dec2017-06-18 22:08:58 -0600172 """
Simon Glass3809ad92021-02-03 06:01:12 -0700173 def __init__(self, scan, dtb_fname, include_disabled, instantiate=False):
Simon Glass9065bc92020-12-28 20:35:06 -0700174 self._scan = scan
Simon Glass1f730c82017-06-18 22:08:59 -0600175 self._fdt = None
Simon Glassd570dec2017-06-18 22:08:58 -0600176 self._dtb_fname = dtb_fname
177 self._valid_nodes = None
Simon Glassdf56e0b2021-02-03 06:01:09 -0700178 self._valid_nodes_unsorted = None
Simon Glasseab3f622017-06-18 22:09:01 -0600179 self._include_disabled = include_disabled
Simon Glassd570dec2017-06-18 22:08:58 -0600180 self._outfile = None
181 self._lines = []
Simon Glassc3a310a82020-12-28 20:34:51 -0700182 self._dirnames = [None] * len(Ftype)
Simon Glass55526782020-12-28 20:35:02 -0700183 self._struct_data = collections.OrderedDict()
Simon Glass4f2059b2020-12-28 20:35:03 -0700184 self._basedir = None
Simon Glass80d782c42021-02-03 06:01:10 -0700185 self._valid_uclasses = None
Simon Glass3809ad92021-02-03 06:01:12 -0700186 self._instantiate = instantiate
Walter Lozanoe675d962020-07-03 08:07:17 -0300187
Simon Glassc3a310a82020-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 Glassd570dec2017-06-18 22:08:58 -0600212 """Set up the output destination
213
Simon Glass1f730c82017-06-18 22:08:59 -0600214 Once this is done, future calls to self.out() will output to this
Simon Glassc3a310a82020-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 Glassd570dec2017-06-18 22:08:58 -0600223
224 Args:
Simon Glassc3a310a82020-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 Glassd570dec2017-06-18 22:08:58 -0600229 """
Simon Glassc3a310a82020-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 Glass6ca0c7a2020-12-28 20:34:48 -0700239 else:
240 self._outfile = sys.stdout
Simon Glassd570dec2017-06-18 22:08:58 -0600241
Simon Glassc3a310a82020-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 Glassc7b4b832021-02-03 06:01:20 -0700249 self._outfile = None
Simon Glassc3a310a82020-12-28 20:34:51 -0700250
Simon Glass1f730c82017-06-18 22:08:59 -0600251 def out(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600252 """Output a string to the output file
253
254 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700255 line (str): String to output
Simon Glassd570dec2017-06-18 22:08:58 -0600256 """
Simon Glass1f730c82017-06-18 22:08:59 -0600257 self._outfile.write(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600258
Simon Glass1f730c82017-06-18 22:08:59 -0600259 def buf(self, line):
Simon Glassd570dec2017-06-18 22:08:58 -0600260 """Buffer up a string to send later
261
262 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700263 line (str): String to add to our 'buffer' list
Simon Glassd570dec2017-06-18 22:08:58 -0600264 """
Simon Glass1f730c82017-06-18 22:08:59 -0600265 self._lines.append(line)
Simon Glassd570dec2017-06-18 22:08:58 -0600266
Simon Glass1f730c82017-06-18 22:08:59 -0600267 def get_buf(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600268 """Get the contents of the output buffer, and clear it
269
270 Returns:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700271 list(str): The output buffer, which is then cleared for future use
Simon Glassd570dec2017-06-18 22:08:58 -0600272 """
273 lines = self._lines
274 self._lines = []
275 return lines
276
Simon Glass6b208842020-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 Glass68d32c72017-08-29 14:16:01 -0600283 self.out('''/*
284 * DO NOT MODIFY
285 *
Simon Glass6b208842020-12-28 20:35:00 -0700286 * %s.
287 * This was generated by dtoc from a .dtb (device tree binary) file.
Simon Glass68d32c72017-08-29 14:16:01 -0600288 */
289
Simon Glass6b208842020-12-28 20:35:00 -0700290''' % outfile.hdr_comment)
Simon Glass68d32c72017-08-29 14:16:01 -0600291
Simon Glassec3b5e42017-08-29 14:15:55 -0600292 def get_phandle_argc(self, prop, node_name):
293 """Check if a node contains phandles
Simon Glassce5621d2017-08-29 14:15:54 -0600294
Simon Glassec3b5e42017-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.
297
298 Args:
Simon Glass94ee3bd2020-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 Glassec3b5e42017-08-29 14:15:55 -0600307 """
Simon Glass4e430a22021-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 Glass609e2b12018-07-06 10:27:31 -0600313 if not isinstance(prop.value, list):
314 prop.value = [prop.value]
Simon Glassec3b5e42017-08-29 14:15:55 -0600315 val = prop.value
Simon Glassec3b5e42017-08-29 14:15:55 -0600316 i = 0
Simon Glassce5621d2017-08-29 14:15:54 -0600317
Simon Glassec3b5e42017-08-29 14:15:55 -0600318 max_args = 0
319 args = []
320 while i < len(val):
321 phandle = fdt_util.fdt32_to_cpu(val[i])
Simon Glass609e2b12018-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 Glassec3b5e42017-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 Glass4e430a22021-02-03 06:01:18 -0700332 cells = target.props.get(cells_prop)
Simon Glassec3b5e42017-08-29 14:15:55 -0600333 if not cells:
Walter Lozano179f0b62020-06-25 01:10:16 -0300334 raise ValueError("Node '%s' has no cells property" %
Simon Glass4e430a22021-02-03 06:01:18 -0700335 target.name)
Simon Glassec3b5e42017-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 Glassce5621d2017-08-29 14:15:54 -0600342
Simon Glass1f730c82017-06-18 22:08:59 -0600343 def scan_dtb(self):
Anatolij Gustschinda707d42017-08-18 17:58:51 +0200344 """Scan the device tree to obtain a tree of nodes and properties
Simon Glassd570dec2017-06-18 22:08:58 -0600345
Simon Glass1f730c82017-06-18 22:08:59 -0600346 Once this is done, self._fdt.GetRoot() can be called to obtain the
Simon Glassd570dec2017-06-18 22:08:58 -0600347 device tree root node, and progress from there.
348 """
Simon Glass1f730c82017-06-18 22:08:59 -0600349 self._fdt = fdt.FdtScan(self._dtb_fname)
350
Simon Glassdf56e0b2021-02-03 06:01:09 -0700351 def scan_node(self, node, valid_nodes):
Simon Glass1f730c82017-06-18 22:08:59 -0600352 """Scan a node and subnodes to build a tree of node and phandle info
Simon Glassd570dec2017-06-18 22:08:58 -0600353
Simon Glassdf56e0b2021-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 Glass1f730c82017-06-18 22:08:59 -0600356
357 Args:
Simon Glassdf56e0b2021-02-03 06:01:09 -0700358 node (Node): Node for scan for subnodes
Simon Glass27511232020-12-23 08:11:19 -0700359 valid_nodes (list of Node): List of Node objects to add to
Simon Glass1f730c82017-06-18 22:08:59 -0600360 """
Simon Glassdf56e0b2021-02-03 06:01:09 -0700361 for subnode in node.subnodes:
362 if 'compatible' in subnode.props:
363 status = subnode.props.get('status')
Simon Glasseab3f622017-06-18 22:09:01 -0600364 if (not self._include_disabled and not status or
Simon Glass1f730c82017-06-18 22:08:59 -0600365 status.value != 'disabled'):
Simon Glassdf56e0b2021-02-03 06:01:09 -0700366 valid_nodes.append(subnode)
Simon Glassd570dec2017-06-18 22:08:58 -0600367
368 # recurse to handle any subnodes
Simon Glassdf56e0b2021-02-03 06:01:09 -0700369 self.scan_node(subnode, valid_nodes)
Simon Glassd570dec2017-06-18 22:08:58 -0600370
Simon Glassc14fd0c2021-02-03 06:01:11 -0700371 def scan_tree(self, add_root):
Simon Glassd570dec2017-06-18 22:08:58 -0600372 """Scan the device tree for useful information
373
374 This fills in the following properties:
Simon Glassdf56e0b2021-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 Glassc14fd0c2021-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 Glassd570dec2017-06-18 22:08:58 -0600382 """
Simon Glassdf56e0b2021-02-03 06:01:09 -0700383 root = self._fdt.GetRoot()
Simon Glass192f8132020-10-03 11:31:25 -0600384 valid_nodes = []
Simon Glassc14fd0c2021-02-03 06:01:11 -0700385 if add_root:
386 valid_nodes.append(root)
Simon Glassdf56e0b2021-02-03 06:01:09 -0700387 self.scan_node(root, valid_nodes)
388 self._valid_nodes_unsorted = valid_nodes
Simon Glass192f8132020-10-03 11:31:25 -0600389 self._valid_nodes = sorted(valid_nodes,
390 key=lambda x: conv_name_to_c(x.name))
Simon Glass44479782021-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 Glass192f8132020-10-03 11:31:25 -0600416 for idx, node in enumerate(self._valid_nodes):
417 node.idx = idx
Simon Glass44479782021-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 Glassd570dec2017-06-18 22:08:58 -0600429
Simon Glass1b1fe412017-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 Glass94ee3bd2020-11-08 20:36:21 -0700435 node (fdt.None): Node to check
Simon Glass1b1fe412017-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 Glass4415dc12021-03-26 16:17:27 +1300443 if parent and not parent.props:
444 raise ValueError("Parent node '%s' has no properties - do you need u-boot,dm-spl or similar?" %
445 parent.path)
Simon Glass93daa012020-12-03 16:55:16 -0700446 num_addr, num_size = 2, 2
Simon Glass1b1fe412017-08-29 14:15:50 -0600447 if parent:
Simon Glass93daa012020-12-03 16:55:16 -0700448 addr_prop = parent.props.get('#address-cells')
449 size_prop = parent.props.get('#size-cells')
450 if addr_prop:
451 num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
452 if size_prop:
453 num_size = fdt_util.fdt32_to_cpu(size_prop.value)
454 return num_addr, num_size
Simon Glass1b1fe412017-08-29 14:15:50 -0600455
456 def scan_reg_sizes(self):
457 """Scan for 64-bit 'reg' properties and update the values
458
459 This finds 'reg' properties with 64-bit data and converts the value to
460 an array of 64-values. This allows it to be output in a way that the
461 C code can read.
462 """
463 for node in self._valid_nodes:
464 reg = node.props.get('reg')
465 if not reg:
466 continue
Simon Glass93daa012020-12-03 16:55:16 -0700467 num_addr, num_size = self.get_num_cells(node)
468 total = num_addr + num_size
Simon Glass1b1fe412017-08-29 14:15:50 -0600469
Simon Glassc9a032c2020-11-08 20:36:17 -0700470 if reg.type != fdt.Type.INT:
Simon Glassc38fee042018-07-06 10:27:32 -0600471 raise ValueError("Node '%s' reg property is not an int" %
472 node.name)
Simon Glass94ba59e2021-03-26 16:17:26 +1300473 if not isinstance(reg.value, list):
474 reg.value = [reg.value]
Simon Glass1b1fe412017-08-29 14:15:50 -0600475 if len(reg.value) % total:
Simon Glass94ee3bd2020-11-08 20:36:21 -0700476 raise ValueError(
Simon Glass4415dc12021-03-26 16:17:27 +1300477 "Node '%s' (parent '%s') reg property has %d cells "
Simon Glass94ee3bd2020-11-08 20:36:21 -0700478 'which is not a multiple of na + ns = %d + %d)' %
Simon Glass4415dc12021-03-26 16:17:27 +1300479 (node.name, node.parent.name, len(reg.value), num_addr,
480 num_size))
Simon Glass93daa012020-12-03 16:55:16 -0700481 reg.num_addr = num_addr
482 reg.num_size = num_size
Simon Glass94ba59e2021-03-26 16:17:26 +1300483 if num_addr > 1 or num_size > 1:
Simon Glassc9a032c2020-11-08 20:36:17 -0700484 reg.type = fdt.Type.INT64
Simon Glass1b1fe412017-08-29 14:15:50 -0600485 i = 0
486 new_value = []
487 val = reg.value
Simon Glass1b1fe412017-08-29 14:15:50 -0600488 while i < len(val):
Simon Glass93daa012020-12-03 16:55:16 -0700489 addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
490 i += num_addr
491 size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
492 i += num_size
Simon Glass1b1fe412017-08-29 14:15:50 -0600493 new_value += [addr, size]
494 reg.value = new_value
495
Simon Glass1f730c82017-06-18 22:08:59 -0600496 def scan_structs(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600497 """Scan the device tree building up the C structures we will use.
498
499 Build a dict keyed by C struct name containing a dict of Prop
500 object for each struct field (keyed by property name). Where the
501 same struct appears multiple times, try to use the 'widest'
502 property, i.e. the one with a type which can express all others.
503
504 Once the widest property is determined, all other properties are
505 updated to match that width.
Simon Glass941f8f02020-10-03 11:31:24 -0600506
Simon Glass55526782020-12-28 20:35:02 -0700507 The results are written to self._struct_data
Simon Glassd570dec2017-06-18 22:08:58 -0600508 """
Simon Glass55526782020-12-28 20:35:02 -0700509 structs = self._struct_data
Simon Glassd570dec2017-06-18 22:08:58 -0600510 for node in self._valid_nodes:
Simon Glassd570dec2017-06-18 22:08:58 -0600511 fields = {}
512
513 # Get a list of all the valid properties in this node.
514 for name, prop in node.props.items():
515 if name not in PROP_IGNORE_LIST and name[0] != '#':
516 fields[name] = copy.deepcopy(prop)
517
Simon Glass62a95372021-02-03 06:00:59 -0700518 # If we've seen this struct_name before, update the existing struct
519 if node.struct_name in structs:
520 struct = structs[node.struct_name]
Simon Glassd570dec2017-06-18 22:08:58 -0600521 for name, prop in fields.items():
522 oldprop = struct.get(name)
523 if oldprop:
524 oldprop.Widen(prop)
525 else:
526 struct[name] = prop
527
528 # Otherwise store this as a new struct.
529 else:
Simon Glass62a95372021-02-03 06:00:59 -0700530 structs[node.struct_name] = fields
Simon Glassd570dec2017-06-18 22:08:58 -0600531
Simon Glassd570dec2017-06-18 22:08:58 -0600532 for node in self._valid_nodes:
Simon Glass62a95372021-02-03 06:00:59 -0700533 struct = structs[node.struct_name]
Simon Glassd570dec2017-06-18 22:08:58 -0600534 for name, prop in node.props.items():
535 if name not in PROP_IGNORE_LIST and name[0] != '#':
536 prop.Widen(struct[name])
Simon Glassd570dec2017-06-18 22:08:58 -0600537
Simon Glass1f730c82017-06-18 22:08:59 -0600538 def scan_phandles(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600539 """Figure out what phandles each node uses
540
541 We need to be careful when outputing nodes that use phandles since
542 they must come after the declaration of the phandles in the C file.
543 Otherwise we get a compiler error since the phandle struct is not yet
544 declared.
545
546 This function adds to each node a list of phandle nodes that the node
547 depends on. This allows us to output things in the right order.
548 """
549 for node in self._valid_nodes:
550 node.phandles = set()
551 for pname, prop in node.props.items():
552 if pname in PROP_IGNORE_LIST or pname[0] == '#':
553 continue
Simon Glassec3b5e42017-08-29 14:15:55 -0600554 info = self.get_phandle_argc(prop, node.name)
555 if info:
Simon Glassec3b5e42017-08-29 14:15:55 -0600556 # Process the list as pairs of (phandle, id)
Simon Glass3deeb472017-08-29 14:15:59 -0600557 pos = 0
558 for args in info.args:
559 phandle_cell = prop.value[pos]
Simon Glassec3b5e42017-08-29 14:15:55 -0600560 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
561 target_node = self._fdt.phandle_to_node[phandle]
562 node.phandles.add(target_node)
Simon Glass3deeb472017-08-29 14:15:59 -0600563 pos += 1 + args
Simon Glassd570dec2017-06-18 22:08:58 -0600564
565
Simon Glass55526782020-12-28 20:35:02 -0700566 def generate_structs(self):
Simon Glassd570dec2017-06-18 22:08:58 -0600567 """Generate struct defintions for the platform data
568
569 This writes out the body of a header file consisting of structure
570 definitions for node in self._valid_nodes. See the documentation in
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +0100571 doc/driver-model/of-plat.rst for more information.
Simon Glassd570dec2017-06-18 22:08:58 -0600572 """
Simon Glass55526782020-12-28 20:35:02 -0700573 structs = self._struct_data
Simon Glass1f730c82017-06-18 22:08:59 -0600574 self.out('#include <stdbool.h>\n')
Masahiro Yamada75f82d02018-03-05 01:20:11 +0900575 self.out('#include <linux/libfdt.h>\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600576
577 # Output the struct definition
578 for name in sorted(structs):
Simon Glass1f730c82017-06-18 22:08:59 -0600579 self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
Simon Glassd570dec2017-06-18 22:08:58 -0600580 for pname in sorted(structs[name]):
581 prop = structs[name][pname]
Simon Glassec3b5e42017-08-29 14:15:55 -0600582 info = self.get_phandle_argc(prop, structs[name])
583 if info:
Simon Glassd570dec2017-06-18 22:08:58 -0600584 # For phandles, include a reference to the target
Simon Glasse94414b2017-08-29 14:15:56 -0600585 struct_name = 'struct phandle_%d_arg' % info.max_args
586 self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
Simon Glass1f730c82017-06-18 22:08:59 -0600587 conv_name_to_c(prop.name),
Simon Glass3deeb472017-08-29 14:15:59 -0600588 len(info.args)))
Simon Glassd570dec2017-06-18 22:08:58 -0600589 else:
590 ptype = TYPE_NAMES[prop.type]
Simon Glass1f730c82017-06-18 22:08:59 -0600591 self.out('\t%s%s' % (tab_to(2, ptype),
592 conv_name_to_c(prop.name)))
593 if isinstance(prop.value, list):
594 self.out('[%d]' % len(prop.value))
595 self.out(';\n')
596 self.out('};\n')
Simon Glassd570dec2017-06-18 22:08:58 -0600597
Simon Glass9d98b6e2020-12-23 08:11:20 -0700598 def _output_list(self, node, prop):
599 """Output the C code for a devicetree property that holds a list
Simon Glassd570dec2017-06-18 22:08:58 -0600600
601 Args:
Simon Glass9d98b6e2020-12-23 08:11:20 -0700602 node (fdt.Node): Node to output
603 prop (fdt.Prop): Prop to output
Simon Glassd570dec2017-06-18 22:08:58 -0600604 """
Simon Glass9d98b6e2020-12-23 08:11:20 -0700605 self.buf('{')
606 vals = []
607 # For phandles, output a reference to the platform data
608 # of the target node.
609 info = self.get_phandle_argc(prop, node.name)
610 if info:
611 # Process the list as pairs of (phandle, id)
612 pos = 0
613 for args in info.args:
614 phandle_cell = prop.value[pos]
615 phandle = fdt_util.fdt32_to_cpu(phandle_cell)
616 target_node = self._fdt.phandle_to_node[phandle]
617 arg_values = []
618 for i in range(args):
619 arg_values.append(
620 str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
621 pos += 1 + args
622 vals.append('\t{%d, {%s}}' % (target_node.idx,
623 ', '.join(arg_values)))
624 for val in vals:
625 self.buf('\n\t\t%s,' % val)
626 else:
627 for val in prop.value:
628 vals.append(get_value(prop.type, val))
Simon Glassbc9e2682020-10-03 09:25:18 -0600629
Simon Glass9d98b6e2020-12-23 08:11:20 -0700630 # Put 8 values per line to avoid very long lines.
631 for i in range(0, len(vals), 8):
632 if i:
633 self.buf(',\n\t\t')
634 self.buf(', '.join(vals[i:i + 8]))
635 self.buf('}')
Simon Glassbc9e2682020-10-03 09:25:18 -0600636
Simon Glass62a95372021-02-03 06:00:59 -0700637 def _declare_device(self, node):
Simon Glass9fdd0c32020-12-23 08:11:21 -0700638 """Add a device declaration to the output
639
Simon Glass1d8364a2020-12-28 20:34:54 -0700640 This declares a U_BOOT_DRVINFO() for the device being processed
Simon Glass9fdd0c32020-12-23 08:11:21 -0700641
642 Args:
Simon Glass62a95372021-02-03 06:00:59 -0700643 node: Node to process
Simon Glass9fdd0c32020-12-23 08:11:21 -0700644 """
Simon Glass62a95372021-02-03 06:00:59 -0700645 self.buf('U_BOOT_DRVINFO(%s) = {\n' % node.var_name)
646 self.buf('\t.name\t\t= "%s",\n' % node.struct_name)
Simon Glasse2119082021-02-03 06:01:19 -0700647 self.buf('\t.plat\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
Simon Glass62a95372021-02-03 06:00:59 -0700648 self.buf('\t.plat_size\t= sizeof(%s%s),\n' %
649 (VAL_PREFIX, node.var_name))
Simon Glass9fdd0c32020-12-23 08:11:21 -0700650 idx = -1
Simon Glass62a95372021-02-03 06:00:59 -0700651 if node.parent and node.parent in self._valid_nodes:
652 idx = node.parent.idx
Simon Glass9fdd0c32020-12-23 08:11:21 -0700653 self.buf('\t.parent_idx\t= %d,\n' % idx)
654 self.buf('};\n')
655 self.buf('\n')
656
Simon Glassc7b4b832021-02-03 06:01:20 -0700657 def prep_priv(self, struc, name, suffix, section='.priv_data'):
658 if not struc:
659 return None
660 var_name = '_%s%s' % (name, suffix)
661 hdr = self._scan._structs.get(struc)
662 if hdr:
663 self.buf('#include <%s>\n' % hdr.fname)
664 else:
665 print('Warning: Cannot find header file for struct %s' % struc)
666 attr = '__attribute__ ((section ("%s")))' % section
667 return var_name, struc, attr
668
669 def alloc_priv(self, info, name, extra, suffix='_priv'):
670 result = self.prep_priv(info, name, suffix)
671 if not result:
672 return None
673 var_name, struc, section = result
674 self.buf('u8 %s_%s[sizeof(struct %s)]\n\t%s;\n' %
675 (var_name, extra, struc.strip(), section))
676 return '%s_%s' % (var_name, extra)
677
Simon Glassfea2f252021-02-03 06:01:21 -0700678 def alloc_plat(self, info, name, extra, node):
679 result = self.prep_priv(info, name, '_plat')
680 if not result:
681 return None
682 var_name, struc, section = result
683 self.buf('struct %s %s\n\t%s_%s = {\n' %
684 (struc.strip(), section, var_name, extra))
685 self.buf('\t.dtplat = {\n')
686 for pname in sorted(node.props):
687 self._output_prop(node, node.props[pname], 2)
688 self.buf('\t},\n')
689 self.buf('};\n')
690 return '&%s_%s' % (var_name, extra)
691
692 def _declare_device_inst(self, node, parent_driver):
693 """Add a device instance declaration to the output
694
695 This declares a DM_DEVICE_INST() for the device being processed
696
697 Args:
698 node: Node to output
699 """
700 driver = node.driver
701 uclass = node.uclass
702 self.buf('\n')
703 num_lines = len(self._lines)
704 plat_name = self.alloc_plat(driver.plat, driver.name, node.var_name,
705 node)
706 priv_name = self.alloc_priv(driver.priv, driver.name, node.var_name)
707 parent_plat_name = None
708 parent_priv_name = None
709 if parent_driver:
710 # TODO: deal with uclass providing these values
711 parent_plat_name = self.alloc_priv(
712 parent_driver.child_plat, driver.name, node.var_name,
713 '_parent_plat')
714 parent_priv_name = self.alloc_priv(
715 parent_driver.child_priv, driver.name, node.var_name,
716 '_parent_priv')
717 uclass_plat_name = self.alloc_priv(
718 uclass.per_dev_plat, driver.name + '_uc', node.var_name, 'plat')
719 uclass_priv_name = self.alloc_priv(uclass.per_dev_priv,
720 driver.name + '_uc', node.var_name)
721 for hdr in driver.headers:
722 self.buf('#include %s\n' % hdr)
723
724 # Add a blank line if we emitted any stuff above, for readability
725 if num_lines != len(self._lines):
726 self.buf('\n')
727
728 self.buf('DM_DEVICE_INST(%s) = {\n' % node.var_name)
729 self.buf('\t.driver\t\t= DM_DRIVER_REF(%s),\n' % node.struct_name)
730 self.buf('\t.name\t\t= "%s",\n' % node.struct_name)
731 if plat_name:
732 self.buf('\t.plat_\t\t= %s,\n' % plat_name)
733 else:
734 self.buf('\t.plat_\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
735 if parent_plat_name:
736 self.buf('\t.parent_plat_\t= %s,\n' % parent_plat_name)
737 if uclass_plat_name:
738 self.buf('\t.uclass_plat_\t= %s,\n' % uclass_plat_name)
739 driver_date = None
740
741 if node != self._fdt.GetRoot():
742 compat_list = node.props['compatible'].value
743 if not isinstance(compat_list, list):
744 compat_list = [compat_list]
745 for compat in compat_list:
746 driver_data = driver.compat.get(compat)
747 if driver_data:
748 self.buf('\t.driver_data\t= %s,\n' % driver_data)
749 break
750
751 if node.parent and node.parent.parent:
Simon Glass9ebd5512021-06-27 17:51:10 -0600752 if node.parent not in self._valid_nodes:
753 # This might indicate that the parent node is not in the
754 # SPL/TPL devicetree but the child is. For example if we are
755 # dealing with of-platdata in TPL, the parent has a
756 # u-boot,dm-tpl tag but the child has u-boot,dm-pre-reloc. In
757 # this case the child node exists in TPL but the parent does
758 # not.
759 raise ValueError("Node '%s' requires parent node '%s' but it is not in the valid list" %
760 (node.path, node.parent.path))
Simon Glassfea2f252021-02-03 06:01:21 -0700761 self.buf('\t.parent\t\t= DM_DEVICE_REF(%s),\n' %
762 node.parent.var_name)
763 if priv_name:
764 self.buf('\t.priv_\t\t= %s,\n' % priv_name)
765 self.buf('\t.uclass\t\t= DM_UCLASS_REF(%s),\n' % uclass.name)
766
767 if uclass_priv_name:
768 self.buf('\t.uclass_priv_ = %s,\n' % uclass_priv_name)
769 if parent_priv_name:
770 self.buf('\t.parent_priv_\t= %s,\n' % parent_priv_name)
771 self.list_node('uclass_node', uclass.node_refs, node.uclass_seq)
772 self.list_head('child_head', 'sibling_node', node.child_devs, node.var_name)
773 if node.parent in self._valid_nodes:
774 self.list_node('sibling_node', node.parent.child_refs,
775 node.parent_seq)
776 # flags is left as 0
777
778 self.buf('\t.seq_ = %d,\n' % node.seq)
779
780 self.buf('};\n')
781 self.buf('\n')
782 return parent_plat_name
783
784 def _output_prop(self, node, prop, tabs=1):
Simon Glass9829eea2020-12-23 08:11:22 -0700785 """Output a line containing the value of a struct member
786
787 Args:
788 node (Node): Node being output
789 prop (Prop): Prop object to output
790 """
791 if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
792 return
793 member_name = conv_name_to_c(prop.name)
Simon Glassfea2f252021-02-03 06:01:21 -0700794 self.buf('%s%s= ' % ('\t' * tabs, tab_to(3, '.' + member_name)))
Simon Glass9829eea2020-12-23 08:11:22 -0700795
796 # Special handling for lists
797 if isinstance(prop.value, list):
798 self._output_list(node, prop)
799 else:
800 self.buf(get_value(prop.type, prop.value))
801 self.buf(',\n')
802
Simon Glass62a95372021-02-03 06:00:59 -0700803 def _output_values(self, node):
Simon Glass9829eea2020-12-23 08:11:22 -0700804 """Output the definition of a device's struct values
805
806 Args:
Simon Glass62a95372021-02-03 06:00:59 -0700807 node (Node): Node to output
Simon Glass9829eea2020-12-23 08:11:22 -0700808 """
809 self.buf('static struct %s%s %s%s = {\n' %
Simon Glass62a95372021-02-03 06:00:59 -0700810 (STRUCT_PREFIX, node.struct_name, VAL_PREFIX, node.var_name))
Simon Glass9829eea2020-12-23 08:11:22 -0700811 for pname in sorted(node.props):
812 self._output_prop(node, node.props[pname])
813 self.buf('};\n')
Simon Glassc7b4b832021-02-03 06:01:20 -0700814
815 def list_head(self, head_member, node_member, node_refs, var_name):
816 self.buf('\t.%s\t= {\n' % head_member)
817 if node_refs:
818 last = node_refs[-1].dev_ref
819 first = node_refs[0].dev_ref
820 member = node_member
821 else:
822 last = 'DM_DEVICE_REF(%s)' % var_name
823 first = last
824 member = head_member
825 self.buf('\t\t.prev = &%s->%s,\n' % (last, member))
826 self.buf('\t\t.next = &%s->%s,\n' % (first, member))
827 self.buf('\t},\n')
828
829 def list_node(self, member, node_refs, seq):
830 self.buf('\t.%s\t= {\n' % member)
831 self.buf('\t\t.prev = %s,\n' % node_refs[seq - 1])
832 self.buf('\t\t.next = %s,\n' % node_refs[seq + 1])
833 self.buf('\t},\n')
834
835 def generate_uclasses(self):
Simon Glassc7b4b832021-02-03 06:01:20 -0700836 self.out('\n')
837 self.out('#include <common.h>\n')
838 self.out('#include <dm.h>\n')
839 self.out('#include <dt-structs.h>\n')
840 self.out('\n')
841 self.buf('/*\n')
Simon Glassfea2f252021-02-03 06:01:21 -0700842 self.buf(
843 " * uclass declarations, ordered by 'struct uclass' linker_list idx:\n")
Simon Glassc7b4b832021-02-03 06:01:20 -0700844 uclass_list = self._valid_uclasses
Simon Glassfea2f252021-02-03 06:01:21 -0700845 for seq, uclass in enumerate(uclass_list):
846 self.buf(' * %3d: %s\n' % (seq, uclass.name))
847 self.buf(' *\n')
848 self.buf(' * Sequence numbers allocated in each uclass:\n')
Simon Glassc7b4b832021-02-03 06:01:20 -0700849 for uclass in uclass_list:
850 if uclass.alias_num_to_node:
851 self.buf(' * %s: %s\n' % (uclass.name, uclass.uclass_id))
852 for seq, node in uclass.alias_num_to_node.items():
853 self.buf(' * %d: %s\n' % (seq, node.path))
854 self.buf(' */\n')
855
856 uclass_node = {}
857 for seq, uclass in enumerate(uclass_list):
858 uclass_node[seq] = ('&DM_UCLASS_REF(%s)->sibling_node' %
859 uclass.name)
860 uclass_node[-1] = '&uclass_head'
861 uclass_node[len(uclass_list)] = '&uclass_head'
862 self.buf('\n')
863 self.buf('struct list_head %s = {\n' % 'uclass_head')
864 self.buf('\t.prev = %s,\n' % uclass_node[len(uclass_list) -1])
865 self.buf('\t.next = %s,\n' % uclass_node[0])
866 self.buf('};\n')
867 self.buf('\n')
868
869 for seq, uclass in enumerate(uclass_list):
870 uc_drv = self._scan._uclass.get(uclass.uclass_id)
871
872 priv_name = self.alloc_priv(uc_drv.priv, uc_drv.name, '')
873
874 self.buf('DM_UCLASS_INST(%s) = {\n' % uclass.name)
875 if priv_name:
876 self.buf('\t.priv_\t\t= %s,\n' % priv_name)
877 self.buf('\t.uc_drv\t\t= DM_UCLASS_DRIVER_REF(%s),\n' % uclass.name)
878 self.list_node('sibling_node', uclass_node, seq)
879 self.list_head('dev_head', 'uclass_node', uc_drv.devs, None)
880 self.buf('};\n')
881 self.buf('\n')
882 self.out(''.join(self.get_buf()))
Simon Glass9829eea2020-12-23 08:11:22 -0700883
Simon Glassbe88d2f2021-02-03 06:01:07 -0700884 def read_aliases(self):
885 """Read the aliases and attach the information to self._alias
886
887 Raises:
888 ValueError: The alias path is not found
889 """
890 alias_node = self._fdt.GetNode('/aliases')
891 if not alias_node:
892 return
893 re_num = re.compile('(^[a-z0-9-]+[a-z]+)([0-9]+)$')
894 for prop in alias_node.props.values():
895 m_alias = re_num.match(prop.name)
896 if not m_alias:
897 raise ValueError("Cannot decode alias '%s'" % prop.name)
898 name, num = m_alias.groups()
899 node = self._fdt.GetNode(prop.value)
900 result = self._scan.add_uclass_alias(name, num, node)
901 if result is None:
902 raise ValueError("Alias '%s' path '%s' not found" %
903 (prop.name, prop.value))
904 elif result is False:
905 print("Could not find uclass for alias '%s'" % prop.name)
906
Simon Glass3fa3bbb2021-02-03 06:01:14 -0700907 def generate_decl(self):
908 nodes_to_output = list(self._valid_nodes)
909
910 self.buf('#include <dm/device-internal.h>\n')
911 self.buf('#include <dm/uclass-internal.h>\n')
912 self.buf('\n')
913 self.buf(
914 '/* driver declarations - these allow DM_DRIVER_GET() to be used */\n')
915 for node in nodes_to_output:
Simon Glassed5e6ae2021-03-15 17:25:11 +1300916 self.buf('extern U_BOOT_DRIVER(%s);\n' % node.struct_name);
Simon Glass3fa3bbb2021-02-03 06:01:14 -0700917 self.buf('\n')
918
919 if self._instantiate:
920 self.buf(
921 '/* device declarations - these allow DM_DEVICE_REF() to be used */\n')
922 for node in nodes_to_output:
Simon Glassed5e6ae2021-03-15 17:25:11 +1300923 self.buf('extern DM_DEVICE_INST(%s);\n' % node.var_name)
Simon Glass3fa3bbb2021-02-03 06:01:14 -0700924 self.buf('\n')
925
926 uclass_list = self._valid_uclasses
927
928 self.buf(
929 '/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */\n')
930 for uclass in uclass_list:
Simon Glassed5e6ae2021-03-15 17:25:11 +1300931 self.buf('extern UCLASS_DRIVER(%s);\n' % uclass.name)
Simon Glass3fa3bbb2021-02-03 06:01:14 -0700932
933 if self._instantiate:
934 self.buf('\n')
935 self.buf('/* uclass declarations - needed for DM_UCLASS_REF() */\n')
936 for uclass in uclass_list:
Simon Glassed5e6ae2021-03-15 17:25:11 +1300937 self.buf('extern DM_UCLASS_INST(%s);\n' % uclass.name)
Simon Glass3fa3bbb2021-02-03 06:01:14 -0700938 self.out(''.join(self.get_buf()))
939
Simon Glass80d782c42021-02-03 06:01:10 -0700940 def assign_seqs(self):
Simon Glassdf56e0b2021-02-03 06:01:09 -0700941 """Assign a sequence number to each node"""
942 for node in self._valid_nodes_unsorted:
Simon Glass80d782c42021-02-03 06:01:10 -0700943 seq = self._scan.assign_seq(node)
944 if seq is not None:
945 node.seq = seq
Simon Glassdf56e0b2021-02-03 06:01:09 -0700946
Simon Glass047a4802021-02-03 06:01:00 -0700947 def process_nodes(self, need_drivers):
948 nodes_to_output = list(self._valid_nodes)
949
Simon Glasseb3c2492021-02-03 06:01:01 -0700950 # Figure out which drivers we actually use
951 self._scan.mark_used(nodes_to_output)
952
Simon Glass047a4802021-02-03 06:01:00 -0700953 for node in nodes_to_output:
954 node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name
955 driver = self._scan.get_driver(node.struct_name)
956 if not driver:
957 if not need_drivers:
958 continue
959 raise ValueError("Cannot parse/find driver for '%s'" %
960 node.struct_name)
961 node.driver = driver
Simon Glass80d782c42021-02-03 06:01:10 -0700962 uclass = self._scan._uclass.get(driver.uclass_id)
963 if not uclass:
964 raise ValueError("Cannot parse/find uclass '%s' for driver '%s'" %
965 (driver.uclass_id, node.struct_name))
966 node.uclass = uclass
967 node.uclass_seq = len(node.uclass.devs)
968 node.uclass.devs.append(node)
969 uclass.node_refs[node.uclass_seq] = \
970 '&%s->uclass_node' % node.dev_ref
971
Simon Glass047a4802021-02-03 06:01:00 -0700972 parent_driver = None
973 if node.parent in self._valid_nodes:
974 parent_driver = self._scan.get_driver(node.parent.struct_name)
975 if not parent_driver:
976 if not need_drivers:
977 continue
978 raise ValueError(
979 "Cannot parse/find parent driver '%s' for '%s'" %
980 (node.parent.struct_name, node.struct_name))
981 node.parent_seq = len(node.parent.child_devs)
982 node.parent.child_devs.append(node)
983 node.parent.child_refs[node.parent_seq] = \
984 '&%s->sibling_node' % node.dev_ref
985 node.parent_driver = parent_driver
986
987 for node in nodes_to_output:
988 ref = '&%s->child_head' % node.dev_ref
989 node.child_refs[-1] = ref
990 node.child_refs[len(node.child_devs)] = ref
991
Simon Glass80d782c42021-02-03 06:01:10 -0700992 uclass_set = set()
993 for driver in self._scan._drivers.values():
994 if driver.used and driver.uclass:
995 uclass_set.add(driver.uclass)
996 self._valid_uclasses = sorted(list(uclass_set),
997 key=lambda uc: uc.uclass_id)
998
999 for seq, uclass in enumerate(uclass_set):
1000 ref = '&DM_UCLASS_REF(%s)->dev_head' % uclass.name
1001 uclass.node_refs[-1] = ref
1002 uclass.node_refs[len(uclass.devs)] = ref
1003
Simon Glassbe749002021-02-03 06:01:15 -07001004 def output_node_plat(self, node):
Simon Glass9d98b6e2020-12-23 08:11:20 -07001005 """Output the C code for a node
Simon Glassbc9e2682020-10-03 09:25:18 -06001006
Simon Glass9d98b6e2020-12-23 08:11:20 -07001007 Args:
1008 node (fdt.Node): node to output
1009 """
Simon Glassbe749002021-02-03 06:01:15 -07001010 driver = node.driver
1011 parent_driver = node.parent_driver
1012
1013 line1 = 'Node %s index %d' % (node.path, node.idx)
1014 if driver:
1015 self.buf('/*\n')
1016 self.buf(' * %s\n' % line1)
1017 self.buf(' * driver %s parent %s\n' % (driver.name,
1018 parent_driver.name if parent_driver else 'None'))
1019 self.buf(' */\n')
1020 else:
1021 self.buf('/* %s */\n' % line1)
Simon Glassd570dec2017-06-18 22:08:58 -06001022
Simon Glass62a95372021-02-03 06:00:59 -07001023 self._output_values(node)
1024 self._declare_device(node)
Simon Glassd570dec2017-06-18 22:08:58 -06001025
Simon Glass1f730c82017-06-18 22:08:59 -06001026 self.out(''.join(self.get_buf()))
Simon Glassd570dec2017-06-18 22:08:58 -06001027
Simon Glassfea2f252021-02-03 06:01:21 -07001028 def output_node_instance(self, node):
1029 """Output the C code for a node
1030
1031 Args:
1032 node (fdt.Node): node to output
1033 """
1034 parent_driver = node.parent_driver
1035
1036 self.buf('/*\n')
1037 self.buf(' * Node %s index %d\n' % (node.path, node.idx))
1038 self.buf(' * driver %s parent %s\n' % (node.driver.name,
1039 parent_driver.name if parent_driver else 'None'))
1040 self.buf('*/\n')
1041
1042 if not node.driver.plat:
1043 self._output_values(node)
1044 self._declare_device_inst(node, parent_driver)
1045
1046 self.out(''.join(self.get_buf()))
1047
Simon Glass55526782020-12-28 20:35:02 -07001048 def generate_plat(self):
Simon Glassd570dec2017-06-18 22:08:58 -06001049 """Generate device defintions for the platform data
1050
1051 This writes out C platform data initialisation data and
Simon Glass1d8364a2020-12-28 20:34:54 -07001052 U_BOOT_DRVINFO() declarations for each valid node. Where a node has
Simon Glassd570dec2017-06-18 22:08:58 -06001053 multiple compatible strings, a #define is used to make them equivalent.
1054
Heinrich Schuchardtc79f03c2020-02-25 21:35:39 +01001055 See the documentation in doc/driver-model/of-plat.rst for more
Simon Glassd570dec2017-06-18 22:08:58 -06001056 information.
1057 """
Simon Glass1d8364a2020-12-28 20:34:54 -07001058 self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
Simon Glassbeddd7a2020-12-28 20:35:01 -07001059 self.out('#define DT_PLAT_C\n')
Simon Glass4c73d7b2020-10-03 11:31:41 -06001060 self.out('\n')
Simon Glass1f730c82017-06-18 22:08:59 -06001061 self.out('#include <common.h>\n')
1062 self.out('#include <dm.h>\n')
1063 self.out('#include <dt-structs.h>\n')
1064 self.out('\n')
Simon Glassd570dec2017-06-18 22:08:58 -06001065
Simon Glasse2119082021-02-03 06:01:19 -07001066 if self._valid_nodes:
1067 self.out('/*\n')
1068 self.out(
1069 " * driver_info declarations, ordered by 'struct driver_info' linker_list idx:\n")
1070 self.out(' *\n')
1071 self.out(' * idx %-20s %-s\n' % ('driver_info', 'driver'))
1072 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1073 for node in self._valid_nodes:
1074 self.out(' * %3d: %-20s %-s\n' %
1075 (node.idx, node.var_name, node.struct_name))
1076 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1077 self.out(' */\n')
1078 self.out('\n')
1079
1080 for node in self._valid_nodes:
1081 self.output_node_plat(node)
Simon Glass3fa797a2017-06-18 22:09:03 -06001082
Walter Lozanodc5b4372020-06-25 01:10:13 -03001083 self.out(''.join(self.get_buf()))
Simon Glass3fa797a2017-06-18 22:09:03 -06001084
Simon Glassfea2f252021-02-03 06:01:21 -07001085 def generate_device(self):
1086 """Generate device instances
1087
1088 This writes out DM_DEVICE_INST() records for each device in the
1089 build.
1090
1091 See the documentation in doc/driver-model/of-plat.rst for more
1092 information.
1093 """
Simon Glassfea2f252021-02-03 06:01:21 -07001094 self.out('#include <common.h>\n')
1095 self.out('#include <dm.h>\n')
1096 self.out('#include <dt-structs.h>\n')
1097 self.out('\n')
1098
1099 if self._valid_nodes:
1100 self.out('/*\n')
1101 self.out(
1102 " * udevice declarations, ordered by 'struct udevice' linker_list position:\n")
1103 self.out(' *\n')
1104 self.out(' * idx %-20s %-s\n' % ('udevice', 'driver'))
1105 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1106 for node in self._valid_nodes:
1107 self.out(' * %3d: %-20s %-s\n' %
1108 (node.idx, node.var_name, node.struct_name))
1109 self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
1110 self.out(' */\n')
1111 self.out('\n')
1112
1113 for node in self._valid_nodes:
1114 self.output_node_instance(node)
1115
1116 self.out(''.join(self.get_buf()))
1117
Simon Glassc3a310a82020-12-28 20:34:51 -07001118
1119# Types of output file we understand
1120# key: Command used to generate this file
1121# value: OutputFile for this command
Simon Glassdb2b9ca2021-03-25 06:40:51 +13001122OUTPUT_FILES_COMMON = {
Simon Glass3fa3bbb2021-02-03 06:01:14 -07001123 'decl':
1124 OutputFile(Ftype.HEADER, 'dt-decl.h', DtbPlatdata.generate_decl,
1125 'Declares externs for all device/uclass instances'),
Simon Glass6b208842020-12-28 20:35:00 -07001126 'struct':
1127 OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
Simon Glass55526782020-12-28 20:35:02 -07001128 DtbPlatdata.generate_structs,
Simon Glass6b208842020-12-28 20:35:00 -07001129 'Defines the structs used to hold devicetree data'),
Simon Glassdb2b9ca2021-03-25 06:40:51 +13001130 }
1131
1132# File generated without instantiate
1133OUTPUT_FILES_NOINST = {
Simon Glass6b208842020-12-28 20:35:00 -07001134 'platdata':
Simon Glass55526782020-12-28 20:35:02 -07001135 OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
Simon Glass6b208842020-12-28 20:35:00 -07001136 'Declares the U_BOOT_DRIVER() records and platform data'),
Simon Glassdb2b9ca2021-03-25 06:40:51 +13001137 }
1138
1139# File generated with instantiate
1140OUTPUT_FILES_INST = {
Simon Glassfea2f252021-02-03 06:01:21 -07001141 'device':
1142 OutputFile(Ftype.SOURCE, 'dt-device.c', DtbPlatdata.generate_device,
1143 'Declares the DM_DEVICE_INST() records'),
Simon Glassc7b4b832021-02-03 06:01:20 -07001144 'uclass':
1145 OutputFile(Ftype.SOURCE, 'dt-uclass.c', DtbPlatdata.generate_uclasses,
1146 'Declares the uclass instances (struct uclass)'),
Simon Glassc3a310a82020-12-28 20:34:51 -07001147 }
1148
Simon Glass6a65d8a2020-12-28 20:34:50 -07001149
Simon Glassf303ee72021-02-03 06:01:02 -07001150def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase,
Simon Glass3809ad92021-02-03 06:01:12 -07001151 instantiate, warning_disabled=False, drivers_additional=None,
1152 basedir=None, scan=None):
Simon Glass3fa797a2017-06-18 22:09:03 -06001153 """Run all the steps of the dtoc tool
1154
1155 Args:
Simon Glass94ee3bd2020-11-08 20:36:21 -07001156 args (list): List of non-option arguments provided to the problem
1157 dtb_file (str): Filename of dtb file to process
1158 include_disabled (bool): True to include disabled nodes
Simon Glass6ca0c7a2020-12-28 20:34:48 -07001159 output (str): Name of output file (None for stdout)
Simon Glass6a65d8a2020-12-28 20:34:50 -07001160 output_dirs (tuple of str):
1161 Directory to put C output files
1162 Directory to put H output files
Simon Glassf303ee72021-02-03 06:01:02 -07001163 phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
1164 or 'tpl'. None if not known
Simon Glass3809ad92021-02-03 06:01:12 -07001165 instantiate: Instantiate devices so they don't need to be bound at
1166 run-time
Simon Glass93daa012020-12-03 16:55:16 -07001167 warning_disabled (bool): True to avoid showing warnings about missing
1168 drivers
Simon Glass27511232020-12-23 08:11:19 -07001169 drivers_additional (list): List of additional drivers to use during
Simon Glass93daa012020-12-03 16:55:16 -07001170 scanning
Simon Glass4f2059b2020-12-28 20:35:03 -07001171 basedir (str): Base directory of U-Boot source code. Defaults to the
1172 grandparent of this file's directory
Simon Glass768ff0a2021-02-03 06:00:51 -07001173 scan (src_src.Scanner): Scanner from a previous run. This can help speed
1174 up tests. Use None for normal operation
1175
Simon Glassbe88d2f2021-02-03 06:01:07 -07001176 Returns:
1177 DtbPlatdata object
1178
Simon Glass94ee3bd2020-11-08 20:36:21 -07001179 Raises:
1180 ValueError: if args has no command, or an unknown command
Simon Glass3fa797a2017-06-18 22:09:03 -06001181 """
1182 if not args:
Simon Glassc3a310a82020-12-28 20:34:51 -07001183 raise ValueError('Please specify a command: struct, platdata, all')
1184 if output and output_dirs and any(output_dirs):
1185 raise ValueError('Must specify either output or output_dirs, not both')
Simon Glass3fa797a2017-06-18 22:09:03 -06001186
Simon Glass768ff0a2021-02-03 06:00:51 -07001187 if not scan:
Simon Glass695077d2021-03-26 16:17:25 +13001188 scan = src_scan.Scanner(basedir, drivers_additional, phase)
Simon Glass768ff0a2021-02-03 06:00:51 -07001189 scan.scan_drivers()
Simon Glass047a4802021-02-03 06:01:00 -07001190 do_process = True
1191 else:
1192 do_process = False
Simon Glass3809ad92021-02-03 06:01:12 -07001193 plat = DtbPlatdata(scan, dtb_file, include_disabled, instantiate)
Simon Glass3fa797a2017-06-18 22:09:03 -06001194 plat.scan_dtb()
Simon Glass3809ad92021-02-03 06:01:12 -07001195 plat.scan_tree(add_root=instantiate)
Simon Glass44479782021-02-03 06:00:58 -07001196 plat.prepare_nodes()
Simon Glass1b1fe412017-08-29 14:15:50 -06001197 plat.scan_reg_sizes()
Simon Glassc3a310a82020-12-28 20:34:51 -07001198 plat.setup_output_dirs(output_dirs)
Simon Glass55526782020-12-28 20:35:02 -07001199 plat.scan_structs()
Simon Glass3fa797a2017-06-18 22:09:03 -06001200 plat.scan_phandles()
Simon Glass3809ad92021-02-03 06:01:12 -07001201 plat.process_nodes(instantiate)
Simon Glassbe88d2f2021-02-03 06:01:07 -07001202 plat.read_aliases()
Simon Glass80d782c42021-02-03 06:01:10 -07001203 plat.assign_seqs()
Simon Glass3fa797a2017-06-18 22:09:03 -06001204
Simon Glassdb2b9ca2021-03-25 06:40:51 +13001205 # Figure out what output files we plan to generate
Simon Glass705b84b2021-04-27 08:19:48 +12001206 output_files = dict(OUTPUT_FILES_COMMON)
Simon Glassdb2b9ca2021-03-25 06:40:51 +13001207 if instantiate:
1208 output_files.update(OUTPUT_FILES_INST)
1209 else:
1210 output_files.update(OUTPUT_FILES_NOINST)
1211
Simon Glass4e8e8462020-12-28 20:34:52 -07001212 cmds = args[0].split(',')
1213 if 'all' in cmds:
Simon Glassdb2b9ca2021-03-25 06:40:51 +13001214 cmds = sorted(output_files.keys())
Simon Glass4e8e8462020-12-28 20:34:52 -07001215 for cmd in cmds:
Simon Glassdb2b9ca2021-03-25 06:40:51 +13001216 outfile = output_files.get(cmd)
Simon Glassc3a310a82020-12-28 20:34:51 -07001217 if not outfile:
1218 raise ValueError("Unknown command '%s': (use: %s)" %
Simon Glassdb2b9ca2021-03-25 06:40:51 +13001219 (cmd, ', '.join(sorted(output_files.keys()))))
Simon Glassc3a310a82020-12-28 20:34:51 -07001220 plat.setup_output(outfile.ftype,
1221 outfile.fname if output_dirs else output)
Simon Glass6b208842020-12-28 20:35:00 -07001222 plat.out_header(outfile)
Simon Glass55526782020-12-28 20:35:02 -07001223 outfile.method(plat)
Simon Glassc3a310a82020-12-28 20:34:51 -07001224 plat.finish_output()
Simon Glass695077d2021-03-26 16:17:25 +13001225
1226 if not warning_disabled:
1227 scan.show_warnings()
Simon Glassbe88d2f2021-02-03 06:01:07 -07001228 return plat