1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228 |
- #!/usr/bin/python
- # SPDX-License-Identifier: GPL-2.0+
- #
- # Copyright (C) 2017 Google, Inc
- # Written by Simon Glass <sjg@chromium.org>
- #
- """Device tree to platform data class
- This supports converting device tree data to C structures definitions and
- static data.
- See doc/driver-model/of-plat.rst for more informaiton
- """
- import collections
- import copy
- from enum import IntEnum
- import os
- import re
- import sys
- from dtoc import fdt
- from dtoc import fdt_util
- from dtoc import src_scan
- from dtoc.src_scan import conv_name_to_c
- # When we see these properties we ignore them - i.e. do not create a structure
- # member
- PROP_IGNORE_LIST = [
- '#address-cells',
- '#gpio-cells',
- '#size-cells',
- 'compatible',
- 'linux,phandle',
- "status",
- 'phandle',
- 'u-boot,dm-pre-reloc',
- 'u-boot,dm-tpl',
- 'u-boot,dm-spl',
- ]
- # C type declarations for the types we support
- TYPE_NAMES = {
- fdt.Type.INT: 'fdt32_t',
- fdt.Type.BYTE: 'unsigned char',
- fdt.Type.STRING: 'const char *',
- fdt.Type.BOOL: 'bool',
- fdt.Type.INT64: 'fdt64_t',
- }
- STRUCT_PREFIX = 'dtd_'
- VAL_PREFIX = 'dtv_'
- # Properties which are considered to be phandles
- # key: property name
- # value: name of associated #cells property in the target node
- #
- # New phandle properties must be added here; otherwise they will come through as
- # simple integers and finding devices by phandle will not work.
- # Any property that ends with one of these (e.g. 'cd-gpios') will be considered
- # a phandle property.
- PHANDLE_PROPS = {
- 'clocks': '#clock-cells',
- 'gpios': '#gpio-cells',
- 'sandbox,emul': '#emul-cells',
- }
- class Ftype(IntEnum):
- SOURCE, HEADER = range(2)
- # This holds information about each type of output file dtoc can create
- # type: Type of file (Ftype)
- # fname: Filename excluding directory, e.g. 'dt-plat.c'
- # hdr_comment: Comment explaining the purpose of the file
- OutputFile = collections.namedtuple('OutputFile',
- ['ftype', 'fname', 'method', 'hdr_comment'])
- # This holds information about a property which includes phandles.
- #
- # max_args: integer: Maximum number or arguments that any phandle uses (int).
- # args: Number of args for each phandle in the property. The total number of
- # phandles is len(args). This is a list of integers.
- PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args'])
- # Holds a single phandle link, allowing a C struct value to be assigned to point
- # to a device
- #
- # var_node: C variable to assign (e.g. 'dtv_mmc.clocks[0].node')
- # dev_name: Name of device to assign to (e.g. 'clock')
- PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name'])
- def tab_to(num_tabs, line):
- """Append tabs to a line of text to reach a tab stop.
- Args:
- num_tabs (int): Tab stop to obtain (0 = column 0, 1 = column 8, etc.)
- line (str): Line of text to append to
- Returns:
- str: line with the correct number of tabs appeneded. If the line already
- extends past that tab stop then a single space is appended.
- """
- if len(line) >= num_tabs * 8:
- return line + ' '
- return line + '\t' * (num_tabs - len(line) // 8)
- def get_value(ftype, value):
- """Get a value as a C expression
- For integers this returns a byte-swapped (little-endian) hex string
- For bytes this returns a hex string, e.g. 0x12
- For strings this returns a literal string enclosed in quotes
- For booleans this return 'true'
- Args:
- ftype (fdt.Type): Data type (fdt_util)
- value (bytes): Data value, as a string of bytes
- Returns:
- str: String representation of the value
- """
- if ftype == fdt.Type.INT:
- val = '%#x' % fdt_util.fdt32_to_cpu(value)
- elif ftype == fdt.Type.BYTE:
- char = value[0]
- val = '%#x' % (ord(char) if isinstance(char, str) else char)
- elif ftype == fdt.Type.STRING:
- # Handle evil ACPI backslashes by adding another backslash before them.
- # So "\\_SB.GPO0" in the device tree effectively stays like that in C
- val = '"%s"' % value.replace('\\', '\\\\')
- elif ftype == fdt.Type.BOOL:
- val = 'true'
- else: # ftype == fdt.Type.INT64:
- val = '%#x' % value
- return val
- class DtbPlatdata():
- """Provide a means to convert device tree binary data to platform data
- The output of this process is C structures which can be used in space-
- constrained encvironments where the ~3KB code overhead of device tree
- code is not affordable.
- Properties:
- _scan: Scan object, for scanning and reporting on useful information
- from the U-Boot source code
- _fdt: Fdt object, referencing the device tree
- _dtb_fname: Filename of the input device tree binary file
- _valid_nodes_unsorted: A list of Node object with compatible strings,
- ordered by devicetree node order
- _valid_nodes: A list of Node object with compatible strings, ordered by
- conv_name_to_c(node.name)
- _include_disabled: true to include nodes marked status = "disabled"
- _outfile: The current output file (sys.stdout or a real file)
- _lines: Stashed list of output lines for outputting in the future
- _dirname: Directory to hold output files, or None for none (all files
- go to stdout)
- _struct_data (dict): OrderedDict of dtplat structures to output
- key (str): Node name, as a C identifier
- value: dict containing structure fields:
- key (str): Field name
- value: Prop object with field information
- _basedir (str): Base directory of source tree
- _valid_uclasses (list of src_scan.Uclass): List of uclasses needed for
- the selected devices (see _valid_node), in alphabetical order
- _instantiate: Instantiate devices so they don't need to be bound at
- run-time
- """
- def __init__(self, scan, dtb_fname, include_disabled, instantiate=False):
- self._scan = scan
- self._fdt = None
- self._dtb_fname = dtb_fname
- self._valid_nodes = None
- self._valid_nodes_unsorted = None
- self._include_disabled = include_disabled
- self._outfile = None
- self._lines = []
- self._dirnames = [None] * len(Ftype)
- self._struct_data = collections.OrderedDict()
- self._basedir = None
- self._valid_uclasses = None
- self._instantiate = instantiate
- def setup_output_dirs(self, output_dirs):
- """Set up the output directories
- This should be done before setup_output() is called
- Args:
- output_dirs (tuple of str):
- Directory to use for C output files.
- Use None to write files relative current directory
- Directory to use for H output files.
- Defaults to the C output dir
- """
- def process_dir(ftype, dirname):
- if dirname:
- os.makedirs(dirname, exist_ok=True)
- self._dirnames[ftype] = dirname
- if output_dirs:
- c_dirname = output_dirs[0]
- h_dirname = output_dirs[1] if len(output_dirs) > 1 else c_dirname
- process_dir(Ftype.SOURCE, c_dirname)
- process_dir(Ftype.HEADER, h_dirname)
- def setup_output(self, ftype, fname):
- """Set up the output destination
- Once this is done, future calls to self.out() will output to this
- file. The file used is as follows:
- self._dirnames[ftype] is None: output to fname, or stdout if None
- self._dirnames[ftype] is not None: output to fname in that directory
- Calling this function multiple times will close the old file and open
- the new one. If they are the same file, nothing happens and output will
- continue to the same file.
- Args:
- ftype (str): Type of file to create ('c' or 'h')
- fname (str): Filename to send output to. If there is a directory in
- self._dirnames for this file type, it will be put in that
- directory
- """
- dirname = self._dirnames[ftype]
- if dirname:
- pathname = os.path.join(dirname, fname)
- if self._outfile:
- self._outfile.close()
- self._outfile = open(pathname, 'w')
- elif fname:
- if not self._outfile:
- self._outfile = open(fname, 'w')
- else:
- self._outfile = sys.stdout
- def finish_output(self):
- """Finish outputing to a file
- This closes the output file, if one is in use
- """
- if self._outfile != sys.stdout:
- self._outfile.close()
- self._outfile = None
- def out(self, line):
- """Output a string to the output file
- Args:
- line (str): String to output
- """
- self._outfile.write(line)
- def buf(self, line):
- """Buffer up a string to send later
- Args:
- line (str): String to add to our 'buffer' list
- """
- self._lines.append(line)
- def get_buf(self):
- """Get the contents of the output buffer, and clear it
- Returns:
- list(str): The output buffer, which is then cleared for future use
- """
- lines = self._lines
- self._lines = []
- return lines
- def out_header(self, outfile):
- """Output a message indicating that this is an auto-generated file
- Args:
- outfile: OutputFile describing the file being generated
- """
- self.out('''/*
- * DO NOT MODIFY
- *
- * %s.
- * This was generated by dtoc from a .dtb (device tree binary) file.
- */
- ''' % outfile.hdr_comment)
- def get_phandle_argc(self, prop, node_name):
- """Check if a node contains phandles
- We have no reliable way of detecting whether a node uses a phandle
- or not. As an interim measure, use a list of known property names.
- Args:
- prop (fdt.Prop): Prop object to check
- node_name (str): Node name, only used for raising an error
- Returns:
- int or None: Number of argument cells is this is a phandle,
- else None
- Raises:
- ValueError: if the phandle cannot be parsed or the required property
- is not present
- """
- cells_prop = None
- for name, cprop in PHANDLE_PROPS.items():
- if prop.name.endswith(name):
- cells_prop = cprop
- if cells_prop:
- if not isinstance(prop.value, list):
- prop.value = [prop.value]
- val = prop.value
- i = 0
- max_args = 0
- args = []
- while i < len(val):
- phandle = fdt_util.fdt32_to_cpu(val[i])
- # If we get to the end of the list, stop. This can happen
- # since some nodes have more phandles in the list than others,
- # but we allocate enough space for the largest list. So those
- # nodes with shorter lists end up with zeroes at the end.
- if not phandle:
- break
- target = self._fdt.phandle_to_node.get(phandle)
- if not target:
- raise ValueError("Cannot parse '%s' in node '%s'" %
- (prop.name, node_name))
- cells = target.props.get(cells_prop)
- if not cells:
- raise ValueError("Node '%s' has no cells property" %
- target.name)
- num_args = fdt_util.fdt32_to_cpu(cells.value)
- max_args = max(max_args, num_args)
- args.append(num_args)
- i += 1 + num_args
- return PhandleInfo(max_args, args)
- return None
- def scan_dtb(self):
- """Scan the device tree to obtain a tree of nodes and properties
- Once this is done, self._fdt.GetRoot() can be called to obtain the
- device tree root node, and progress from there.
- """
- self._fdt = fdt.FdtScan(self._dtb_fname)
- def scan_node(self, node, valid_nodes):
- """Scan a node and subnodes to build a tree of node and phandle info
- This adds each subnode to self._valid_nodes if it is enabled and has a
- compatible string.
- Args:
- node (Node): Node for scan for subnodes
- valid_nodes (list of Node): List of Node objects to add to
- """
- for subnode in node.subnodes:
- if 'compatible' in subnode.props:
- status = subnode.props.get('status')
- if (not self._include_disabled and not status or
- status.value != 'disabled'):
- valid_nodes.append(subnode)
- # recurse to handle any subnodes
- self.scan_node(subnode, valid_nodes)
- def scan_tree(self, add_root):
- """Scan the device tree for useful information
- This fills in the following properties:
- _valid_nodes_unsorted: A list of nodes we wish to consider include
- in the platform data (in devicetree node order)
- _valid_nodes: Sorted version of _valid_nodes_unsorted
- Args:
- add_root: True to add the root node also (which wouldn't normally
- be added as it may not have a compatible string)
- """
- root = self._fdt.GetRoot()
- valid_nodes = []
- if add_root:
- valid_nodes.append(root)
- self.scan_node(root, valid_nodes)
- self._valid_nodes_unsorted = valid_nodes
- self._valid_nodes = sorted(valid_nodes,
- key=lambda x: conv_name_to_c(x.name))
- def prepare_nodes(self):
- """Add extra properties to the nodes we are using
- The following properties are added for use by dtoc:
- idx: Index number of this node (0=first, etc.)
- struct_name: Name of the struct dtd used by this node
- var_name: C name for this node
- child_devs: List of child devices for this node, each a None
- child_refs: Dict of references for each child:
- key: Position in child list (-1=head, 0=first, 1=second, ...
- n-1=last, n=head)
- seq: Sequence number of the device (unique within its uclass), or
- -1 not not known yet
- dev_ref: Reference to this device, e.g. 'DM_DEVICE_REF(serial)'
- driver: Driver record for this node, or None if not known
- uclass: Uclass record for this node, or None if not known
- uclass_seq: Position of this device within the uclass list (0=first,
- n-1=last)
- parent_seq: Position of this device within it siblings (0=first,
- n-1=last)
- parent_driver: Driver record of the node's parent, or None if none.
- We don't use node.parent.driver since node.parent may not be in
- the list of valid nodes
- """
- for idx, node in enumerate(self._valid_nodes):
- node.idx = idx
- node.struct_name, _ = self._scan.get_normalized_compat_name(node)
- node.var_name = conv_name_to_c(node.name)
- node.child_devs = []
- node.child_refs = {}
- node.seq = -1
- node.dev_ref = None
- node.driver = None
- node.uclass = None
- node.uclass_seq = None
- node.parent_seq = None
- node.parent_driver = None
- @staticmethod
- def get_num_cells(node):
- """Get the number of cells in addresses and sizes for this node
- Args:
- node (fdt.None): Node to check
- Returns:
- Tuple:
- Number of address cells for this node
- Number of size cells for this node
- """
- parent = node.parent
- if parent and not parent.props:
- raise ValueError("Parent node '%s' has no properties - do you need u-boot,dm-spl or similar?" %
- parent.path)
- num_addr, num_size = 2, 2
- if parent:
- addr_prop = parent.props.get('#address-cells')
- size_prop = parent.props.get('#size-cells')
- if addr_prop:
- num_addr = fdt_util.fdt32_to_cpu(addr_prop.value)
- if size_prop:
- num_size = fdt_util.fdt32_to_cpu(size_prop.value)
- return num_addr, num_size
- def scan_reg_sizes(self):
- """Scan for 64-bit 'reg' properties and update the values
- This finds 'reg' properties with 64-bit data and converts the value to
- an array of 64-values. This allows it to be output in a way that the
- C code can read.
- """
- for node in self._valid_nodes:
- reg = node.props.get('reg')
- if not reg:
- continue
- num_addr, num_size = self.get_num_cells(node)
- total = num_addr + num_size
- if reg.type != fdt.Type.INT:
- raise ValueError("Node '%s' reg property is not an int" %
- node.name)
- if not isinstance(reg.value, list):
- reg.value = [reg.value]
- if len(reg.value) % total:
- raise ValueError(
- "Node '%s' (parent '%s') reg property has %d cells "
- 'which is not a multiple of na + ns = %d + %d)' %
- (node.name, node.parent.name, len(reg.value), num_addr,
- num_size))
- reg.num_addr = num_addr
- reg.num_size = num_size
- if num_addr > 1 or num_size > 1:
- reg.type = fdt.Type.INT64
- i = 0
- new_value = []
- val = reg.value
- while i < len(val):
- addr = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_addr)
- i += num_addr
- size = fdt_util.fdt_cells_to_cpu(val[i:], reg.num_size)
- i += num_size
- new_value += [addr, size]
- reg.value = new_value
- def scan_structs(self):
- """Scan the device tree building up the C structures we will use.
- Build a dict keyed by C struct name containing a dict of Prop
- object for each struct field (keyed by property name). Where the
- same struct appears multiple times, try to use the 'widest'
- property, i.e. the one with a type which can express all others.
- Once the widest property is determined, all other properties are
- updated to match that width.
- The results are written to self._struct_data
- """
- structs = self._struct_data
- for node in self._valid_nodes:
- fields = {}
- # Get a list of all the valid properties in this node.
- for name, prop in node.props.items():
- if name not in PROP_IGNORE_LIST and name[0] != '#':
- fields[name] = copy.deepcopy(prop)
- # If we've seen this struct_name before, update the existing struct
- if node.struct_name in structs:
- struct = structs[node.struct_name]
- for name, prop in fields.items():
- oldprop = struct.get(name)
- if oldprop:
- oldprop.Widen(prop)
- else:
- struct[name] = prop
- # Otherwise store this as a new struct.
- else:
- structs[node.struct_name] = fields
- for node in self._valid_nodes:
- struct = structs[node.struct_name]
- for name, prop in node.props.items():
- if name not in PROP_IGNORE_LIST and name[0] != '#':
- prop.Widen(struct[name])
- def scan_phandles(self):
- """Figure out what phandles each node uses
- We need to be careful when outputing nodes that use phandles since
- they must come after the declaration of the phandles in the C file.
- Otherwise we get a compiler error since the phandle struct is not yet
- declared.
- This function adds to each node a list of phandle nodes that the node
- depends on. This allows us to output things in the right order.
- """
- for node in self._valid_nodes:
- node.phandles = set()
- for pname, prop in node.props.items():
- if pname in PROP_IGNORE_LIST or pname[0] == '#':
- continue
- info = self.get_phandle_argc(prop, node.name)
- if info:
- # Process the list as pairs of (phandle, id)
- pos = 0
- for args in info.args:
- phandle_cell = prop.value[pos]
- phandle = fdt_util.fdt32_to_cpu(phandle_cell)
- target_node = self._fdt.phandle_to_node[phandle]
- node.phandles.add(target_node)
- pos += 1 + args
- def generate_structs(self):
- """Generate struct defintions for the platform data
- This writes out the body of a header file consisting of structure
- definitions for node in self._valid_nodes. See the documentation in
- doc/driver-model/of-plat.rst for more information.
- """
- structs = self._struct_data
- self.out('#include <stdbool.h>\n')
- self.out('#include <linux/libfdt.h>\n')
- # Output the struct definition
- for name in sorted(structs):
- self.out('struct %s%s {\n' % (STRUCT_PREFIX, name))
- for pname in sorted(structs[name]):
- prop = structs[name][pname]
- info = self.get_phandle_argc(prop, structs[name])
- if info:
- # For phandles, include a reference to the target
- struct_name = 'struct phandle_%d_arg' % info.max_args
- self.out('\t%s%s[%d]' % (tab_to(2, struct_name),
- conv_name_to_c(prop.name),
- len(info.args)))
- else:
- ptype = TYPE_NAMES[prop.type]
- self.out('\t%s%s' % (tab_to(2, ptype),
- conv_name_to_c(prop.name)))
- if isinstance(prop.value, list):
- self.out('[%d]' % len(prop.value))
- self.out(';\n')
- self.out('};\n')
- def _output_list(self, node, prop):
- """Output the C code for a devicetree property that holds a list
- Args:
- node (fdt.Node): Node to output
- prop (fdt.Prop): Prop to output
- """
- self.buf('{')
- vals = []
- # For phandles, output a reference to the platform data
- # of the target node.
- info = self.get_phandle_argc(prop, node.name)
- if info:
- # Process the list as pairs of (phandle, id)
- pos = 0
- for args in info.args:
- phandle_cell = prop.value[pos]
- phandle = fdt_util.fdt32_to_cpu(phandle_cell)
- target_node = self._fdt.phandle_to_node[phandle]
- arg_values = []
- for i in range(args):
- arg_values.append(
- str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i])))
- pos += 1 + args
- vals.append('\t{%d, {%s}}' % (target_node.idx,
- ', '.join(arg_values)))
- for val in vals:
- self.buf('\n\t\t%s,' % val)
- else:
- for val in prop.value:
- vals.append(get_value(prop.type, val))
- # Put 8 values per line to avoid very long lines.
- for i in range(0, len(vals), 8):
- if i:
- self.buf(',\n\t\t')
- self.buf(', '.join(vals[i:i + 8]))
- self.buf('}')
- def _declare_device(self, node):
- """Add a device declaration to the output
- This declares a U_BOOT_DRVINFO() for the device being processed
- Args:
- node: Node to process
- """
- self.buf('U_BOOT_DRVINFO(%s) = {\n' % node.var_name)
- self.buf('\t.name\t\t= "%s",\n' % node.struct_name)
- self.buf('\t.plat\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
- self.buf('\t.plat_size\t= sizeof(%s%s),\n' %
- (VAL_PREFIX, node.var_name))
- idx = -1
- if node.parent and node.parent in self._valid_nodes:
- idx = node.parent.idx
- self.buf('\t.parent_idx\t= %d,\n' % idx)
- self.buf('};\n')
- self.buf('\n')
- def prep_priv(self, struc, name, suffix, section='.priv_data'):
- if not struc:
- return None
- var_name = '_%s%s' % (name, suffix)
- hdr = self._scan._structs.get(struc)
- if hdr:
- self.buf('#include <%s>\n' % hdr.fname)
- else:
- print('Warning: Cannot find header file for struct %s' % struc)
- attr = '__attribute__ ((section ("%s")))' % section
- return var_name, struc, attr
- def alloc_priv(self, info, name, extra, suffix='_priv'):
- result = self.prep_priv(info, name, suffix)
- if not result:
- return None
- var_name, struc, section = result
- self.buf('u8 %s_%s[sizeof(struct %s)]\n\t%s;\n' %
- (var_name, extra, struc.strip(), section))
- return '%s_%s' % (var_name, extra)
- def alloc_plat(self, info, name, extra, node):
- result = self.prep_priv(info, name, '_plat')
- if not result:
- return None
- var_name, struc, section = result
- self.buf('struct %s %s\n\t%s_%s = {\n' %
- (struc.strip(), section, var_name, extra))
- self.buf('\t.dtplat = {\n')
- for pname in sorted(node.props):
- self._output_prop(node, node.props[pname], 2)
- self.buf('\t},\n')
- self.buf('};\n')
- return '&%s_%s' % (var_name, extra)
- def _declare_device_inst(self, node, parent_driver):
- """Add a device instance declaration to the output
- This declares a DM_DEVICE_INST() for the device being processed
- Args:
- node: Node to output
- """
- driver = node.driver
- uclass = node.uclass
- self.buf('\n')
- num_lines = len(self._lines)
- plat_name = self.alloc_plat(driver.plat, driver.name, node.var_name,
- node)
- priv_name = self.alloc_priv(driver.priv, driver.name, node.var_name)
- parent_plat_name = None
- parent_priv_name = None
- if parent_driver:
- # TODO: deal with uclass providing these values
- parent_plat_name = self.alloc_priv(
- parent_driver.child_plat, driver.name, node.var_name,
- '_parent_plat')
- parent_priv_name = self.alloc_priv(
- parent_driver.child_priv, driver.name, node.var_name,
- '_parent_priv')
- uclass_plat_name = self.alloc_priv(
- uclass.per_dev_plat, driver.name + '_uc', node.var_name, 'plat')
- uclass_priv_name = self.alloc_priv(uclass.per_dev_priv,
- driver.name + '_uc', node.var_name)
- for hdr in driver.headers:
- self.buf('#include %s\n' % hdr)
- # Add a blank line if we emitted any stuff above, for readability
- if num_lines != len(self._lines):
- self.buf('\n')
- self.buf('DM_DEVICE_INST(%s) = {\n' % node.var_name)
- self.buf('\t.driver\t\t= DM_DRIVER_REF(%s),\n' % node.struct_name)
- self.buf('\t.name\t\t= "%s",\n' % node.struct_name)
- if plat_name:
- self.buf('\t.plat_\t\t= %s,\n' % plat_name)
- else:
- self.buf('\t.plat_\t\t= &%s%s,\n' % (VAL_PREFIX, node.var_name))
- if parent_plat_name:
- self.buf('\t.parent_plat_\t= %s,\n' % parent_plat_name)
- if uclass_plat_name:
- self.buf('\t.uclass_plat_\t= %s,\n' % uclass_plat_name)
- driver_date = None
- if node != self._fdt.GetRoot():
- compat_list = node.props['compatible'].value
- if not isinstance(compat_list, list):
- compat_list = [compat_list]
- for compat in compat_list:
- driver_data = driver.compat.get(compat)
- if driver_data:
- self.buf('\t.driver_data\t= %s,\n' % driver_data)
- break
- if node.parent and node.parent.parent:
- if node.parent not in self._valid_nodes:
- # This might indicate that the parent node is not in the
- # SPL/TPL devicetree but the child is. For example if we are
- # dealing with of-platdata in TPL, the parent has a
- # u-boot,dm-tpl tag but the child has u-boot,dm-pre-reloc. In
- # this case the child node exists in TPL but the parent does
- # not.
- raise ValueError("Node '%s' requires parent node '%s' but it is not in the valid list" %
- (node.path, node.parent.path))
- self.buf('\t.parent\t\t= DM_DEVICE_REF(%s),\n' %
- node.parent.var_name)
- if priv_name:
- self.buf('\t.priv_\t\t= %s,\n' % priv_name)
- self.buf('\t.uclass\t\t= DM_UCLASS_REF(%s),\n' % uclass.name)
- if uclass_priv_name:
- self.buf('\t.uclass_priv_ = %s,\n' % uclass_priv_name)
- if parent_priv_name:
- self.buf('\t.parent_priv_\t= %s,\n' % parent_priv_name)
- self.list_node('uclass_node', uclass.node_refs, node.uclass_seq)
- self.list_head('child_head', 'sibling_node', node.child_devs, node.var_name)
- if node.parent in self._valid_nodes:
- self.list_node('sibling_node', node.parent.child_refs,
- node.parent_seq)
- # flags is left as 0
- self.buf('\t.seq_ = %d,\n' % node.seq)
- self.buf('};\n')
- self.buf('\n')
- return parent_plat_name
- def _output_prop(self, node, prop, tabs=1):
- """Output a line containing the value of a struct member
- Args:
- node (Node): Node being output
- prop (Prop): Prop object to output
- """
- if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#':
- return
- member_name = conv_name_to_c(prop.name)
- self.buf('%s%s= ' % ('\t' * tabs, tab_to(3, '.' + member_name)))
- # Special handling for lists
- if isinstance(prop.value, list):
- self._output_list(node, prop)
- else:
- self.buf(get_value(prop.type, prop.value))
- self.buf(',\n')
- def _output_values(self, node):
- """Output the definition of a device's struct values
- Args:
- node (Node): Node to output
- """
- self.buf('static struct %s%s %s%s = {\n' %
- (STRUCT_PREFIX, node.struct_name, VAL_PREFIX, node.var_name))
- for pname in sorted(node.props):
- self._output_prop(node, node.props[pname])
- self.buf('};\n')
- def list_head(self, head_member, node_member, node_refs, var_name):
- self.buf('\t.%s\t= {\n' % head_member)
- if node_refs:
- last = node_refs[-1].dev_ref
- first = node_refs[0].dev_ref
- member = node_member
- else:
- last = 'DM_DEVICE_REF(%s)' % var_name
- first = last
- member = head_member
- self.buf('\t\t.prev = &%s->%s,\n' % (last, member))
- self.buf('\t\t.next = &%s->%s,\n' % (first, member))
- self.buf('\t},\n')
- def list_node(self, member, node_refs, seq):
- self.buf('\t.%s\t= {\n' % member)
- self.buf('\t\t.prev = %s,\n' % node_refs[seq - 1])
- self.buf('\t\t.next = %s,\n' % node_refs[seq + 1])
- self.buf('\t},\n')
- def generate_uclasses(self):
- self.out('\n')
- self.out('#include <common.h>\n')
- self.out('#include <dm.h>\n')
- self.out('#include <dt-structs.h>\n')
- self.out('\n')
- self.buf('/*\n')
- self.buf(
- " * uclass declarations, ordered by 'struct uclass' linker_list idx:\n")
- uclass_list = self._valid_uclasses
- for seq, uclass in enumerate(uclass_list):
- self.buf(' * %3d: %s\n' % (seq, uclass.name))
- self.buf(' *\n')
- self.buf(' * Sequence numbers allocated in each uclass:\n')
- for uclass in uclass_list:
- if uclass.alias_num_to_node:
- self.buf(' * %s: %s\n' % (uclass.name, uclass.uclass_id))
- for seq, node in uclass.alias_num_to_node.items():
- self.buf(' * %d: %s\n' % (seq, node.path))
- self.buf(' */\n')
- uclass_node = {}
- for seq, uclass in enumerate(uclass_list):
- uclass_node[seq] = ('&DM_UCLASS_REF(%s)->sibling_node' %
- uclass.name)
- uclass_node[-1] = '&uclass_head'
- uclass_node[len(uclass_list)] = '&uclass_head'
- self.buf('\n')
- self.buf('struct list_head %s = {\n' % 'uclass_head')
- self.buf('\t.prev = %s,\n' % uclass_node[len(uclass_list) -1])
- self.buf('\t.next = %s,\n' % uclass_node[0])
- self.buf('};\n')
- self.buf('\n')
- for seq, uclass in enumerate(uclass_list):
- uc_drv = self._scan._uclass.get(uclass.uclass_id)
- priv_name = self.alloc_priv(uc_drv.priv, uc_drv.name, '')
- self.buf('DM_UCLASS_INST(%s) = {\n' % uclass.name)
- if priv_name:
- self.buf('\t.priv_\t\t= %s,\n' % priv_name)
- self.buf('\t.uc_drv\t\t= DM_UCLASS_DRIVER_REF(%s),\n' % uclass.name)
- self.list_node('sibling_node', uclass_node, seq)
- self.list_head('dev_head', 'uclass_node', uc_drv.devs, None)
- self.buf('};\n')
- self.buf('\n')
- self.out(''.join(self.get_buf()))
- def read_aliases(self):
- """Read the aliases and attach the information to self._alias
- Raises:
- ValueError: The alias path is not found
- """
- alias_node = self._fdt.GetNode('/aliases')
- if not alias_node:
- return
- re_num = re.compile('(^[a-z0-9-]+[a-z]+)([0-9]+)$')
- for prop in alias_node.props.values():
- m_alias = re_num.match(prop.name)
- if not m_alias:
- raise ValueError("Cannot decode alias '%s'" % prop.name)
- name, num = m_alias.groups()
- node = self._fdt.GetNode(prop.value)
- result = self._scan.add_uclass_alias(name, num, node)
- if result is None:
- raise ValueError("Alias '%s' path '%s' not found" %
- (prop.name, prop.value))
- elif result is False:
- print("Could not find uclass for alias '%s'" % prop.name)
- def generate_decl(self):
- nodes_to_output = list(self._valid_nodes)
- self.buf('#include <dm/device-internal.h>\n')
- self.buf('#include <dm/uclass-internal.h>\n')
- self.buf('\n')
- self.buf(
- '/* driver declarations - these allow DM_DRIVER_GET() to be used */\n')
- for node in nodes_to_output:
- self.buf('extern U_BOOT_DRIVER(%s);\n' % node.struct_name);
- self.buf('\n')
- if self._instantiate:
- self.buf(
- '/* device declarations - these allow DM_DEVICE_REF() to be used */\n')
- for node in nodes_to_output:
- self.buf('extern DM_DEVICE_INST(%s);\n' % node.var_name)
- self.buf('\n')
- uclass_list = self._valid_uclasses
- self.buf(
- '/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */\n')
- for uclass in uclass_list:
- self.buf('extern UCLASS_DRIVER(%s);\n' % uclass.name)
- if self._instantiate:
- self.buf('\n')
- self.buf('/* uclass declarations - needed for DM_UCLASS_REF() */\n')
- for uclass in uclass_list:
- self.buf('extern DM_UCLASS_INST(%s);\n' % uclass.name)
- self.out(''.join(self.get_buf()))
- def assign_seqs(self):
- """Assign a sequence number to each node"""
- for node in self._valid_nodes_unsorted:
- seq = self._scan.assign_seq(node)
- if seq is not None:
- node.seq = seq
- def process_nodes(self, need_drivers):
- nodes_to_output = list(self._valid_nodes)
- # Figure out which drivers we actually use
- self._scan.mark_used(nodes_to_output)
- for node in nodes_to_output:
- node.dev_ref = 'DM_DEVICE_REF(%s)' % node.var_name
- driver = self._scan.get_driver(node.struct_name)
- if not driver:
- if not need_drivers:
- continue
- raise ValueError("Cannot parse/find driver for '%s'" %
- node.struct_name)
- node.driver = driver
- uclass = self._scan._uclass.get(driver.uclass_id)
- if not uclass:
- raise ValueError("Cannot parse/find uclass '%s' for driver '%s'" %
- (driver.uclass_id, node.struct_name))
- node.uclass = uclass
- node.uclass_seq = len(node.uclass.devs)
- node.uclass.devs.append(node)
- uclass.node_refs[node.uclass_seq] = \
- '&%s->uclass_node' % node.dev_ref
- parent_driver = None
- if node.parent in self._valid_nodes:
- parent_driver = self._scan.get_driver(node.parent.struct_name)
- if not parent_driver:
- if not need_drivers:
- continue
- raise ValueError(
- "Cannot parse/find parent driver '%s' for '%s'" %
- (node.parent.struct_name, node.struct_name))
- node.parent_seq = len(node.parent.child_devs)
- node.parent.child_devs.append(node)
- node.parent.child_refs[node.parent_seq] = \
- '&%s->sibling_node' % node.dev_ref
- node.parent_driver = parent_driver
- for node in nodes_to_output:
- ref = '&%s->child_head' % node.dev_ref
- node.child_refs[-1] = ref
- node.child_refs[len(node.child_devs)] = ref
- uclass_set = set()
- for driver in self._scan._drivers.values():
- if driver.used and driver.uclass:
- uclass_set.add(driver.uclass)
- self._valid_uclasses = sorted(list(uclass_set),
- key=lambda uc: uc.uclass_id)
- for seq, uclass in enumerate(uclass_set):
- ref = '&DM_UCLASS_REF(%s)->dev_head' % uclass.name
- uclass.node_refs[-1] = ref
- uclass.node_refs[len(uclass.devs)] = ref
- def output_node_plat(self, node):
- """Output the C code for a node
- Args:
- node (fdt.Node): node to output
- """
- driver = node.driver
- parent_driver = node.parent_driver
- line1 = 'Node %s index %d' % (node.path, node.idx)
- if driver:
- self.buf('/*\n')
- self.buf(' * %s\n' % line1)
- self.buf(' * driver %s parent %s\n' % (driver.name,
- parent_driver.name if parent_driver else 'None'))
- self.buf(' */\n')
- else:
- self.buf('/* %s */\n' % line1)
- self._output_values(node)
- self._declare_device(node)
- self.out(''.join(self.get_buf()))
- def output_node_instance(self, node):
- """Output the C code for a node
- Args:
- node (fdt.Node): node to output
- """
- parent_driver = node.parent_driver
- self.buf('/*\n')
- self.buf(' * Node %s index %d\n' % (node.path, node.idx))
- self.buf(' * driver %s parent %s\n' % (node.driver.name,
- parent_driver.name if parent_driver else 'None'))
- self.buf('*/\n')
- if not node.driver.plat:
- self._output_values(node)
- self._declare_device_inst(node, parent_driver)
- self.out(''.join(self.get_buf()))
- def generate_plat(self):
- """Generate device defintions for the platform data
- This writes out C platform data initialisation data and
- U_BOOT_DRVINFO() declarations for each valid node. Where a node has
- multiple compatible strings, a #define is used to make them equivalent.
- See the documentation in doc/driver-model/of-plat.rst for more
- information.
- """
- self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n')
- self.out('#define DT_PLAT_C\n')
- self.out('\n')
- self.out('#include <common.h>\n')
- self.out('#include <dm.h>\n')
- self.out('#include <dt-structs.h>\n')
- self.out('\n')
- if self._valid_nodes:
- self.out('/*\n')
- self.out(
- " * driver_info declarations, ordered by 'struct driver_info' linker_list idx:\n")
- self.out(' *\n')
- self.out(' * idx %-20s %-s\n' % ('driver_info', 'driver'))
- self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
- for node in self._valid_nodes:
- self.out(' * %3d: %-20s %-s\n' %
- (node.idx, node.var_name, node.struct_name))
- self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
- self.out(' */\n')
- self.out('\n')
- for node in self._valid_nodes:
- self.output_node_plat(node)
- self.out(''.join(self.get_buf()))
- def generate_device(self):
- """Generate device instances
- This writes out DM_DEVICE_INST() records for each device in the
- build.
- See the documentation in doc/driver-model/of-plat.rst for more
- information.
- """
- self.out('#include <common.h>\n')
- self.out('#include <dm.h>\n')
- self.out('#include <dt-structs.h>\n')
- self.out('\n')
- if self._valid_nodes:
- self.out('/*\n')
- self.out(
- " * udevice declarations, ordered by 'struct udevice' linker_list position:\n")
- self.out(' *\n')
- self.out(' * idx %-20s %-s\n' % ('udevice', 'driver'))
- self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
- for node in self._valid_nodes:
- self.out(' * %3d: %-20s %-s\n' %
- (node.idx, node.var_name, node.struct_name))
- self.out(' * --- %-20s %-s\n' % ('-' * 20, '-' * 20))
- self.out(' */\n')
- self.out('\n')
- for node in self._valid_nodes:
- self.output_node_instance(node)
- self.out(''.join(self.get_buf()))
- # Types of output file we understand
- # key: Command used to generate this file
- # value: OutputFile for this command
- OUTPUT_FILES_COMMON = {
- 'decl':
- OutputFile(Ftype.HEADER, 'dt-decl.h', DtbPlatdata.generate_decl,
- 'Declares externs for all device/uclass instances'),
- 'struct':
- OutputFile(Ftype.HEADER, 'dt-structs-gen.h',
- DtbPlatdata.generate_structs,
- 'Defines the structs used to hold devicetree data'),
- }
- # File generated without instantiate
- OUTPUT_FILES_NOINST = {
- 'platdata':
- OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat,
- 'Declares the U_BOOT_DRIVER() records and platform data'),
- }
- # File generated with instantiate
- OUTPUT_FILES_INST = {
- 'device':
- OutputFile(Ftype.SOURCE, 'dt-device.c', DtbPlatdata.generate_device,
- 'Declares the DM_DEVICE_INST() records'),
- 'uclass':
- OutputFile(Ftype.SOURCE, 'dt-uclass.c', DtbPlatdata.generate_uclasses,
- 'Declares the uclass instances (struct uclass)'),
- }
- def run_steps(args, dtb_file, include_disabled, output, output_dirs, phase,
- instantiate, warning_disabled=False, drivers_additional=None,
- basedir=None, scan=None):
- """Run all the steps of the dtoc tool
- Args:
- args (list): List of non-option arguments provided to the problem
- dtb_file (str): Filename of dtb file to process
- include_disabled (bool): True to include disabled nodes
- output (str): Name of output file (None for stdout)
- output_dirs (tuple of str):
- Directory to put C output files
- Directory to put H output files
- phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
- or 'tpl'. None if not known
- instantiate: Instantiate devices so they don't need to be bound at
- run-time
- warning_disabled (bool): True to avoid showing warnings about missing
- drivers
- drivers_additional (list): List of additional drivers to use during
- scanning
- basedir (str): Base directory of U-Boot source code. Defaults to the
- grandparent of this file's directory
- scan (src_src.Scanner): Scanner from a previous run. This can help speed
- up tests. Use None for normal operation
- Returns:
- DtbPlatdata object
- Raises:
- ValueError: if args has no command, or an unknown command
- """
- if not args:
- raise ValueError('Please specify a command: struct, platdata, all')
- if output and output_dirs and any(output_dirs):
- raise ValueError('Must specify either output or output_dirs, not both')
- if not scan:
- scan = src_scan.Scanner(basedir, drivers_additional, phase)
- scan.scan_drivers()
- do_process = True
- else:
- do_process = False
- plat = DtbPlatdata(scan, dtb_file, include_disabled, instantiate)
- plat.scan_dtb()
- plat.scan_tree(add_root=instantiate)
- plat.prepare_nodes()
- plat.scan_reg_sizes()
- plat.setup_output_dirs(output_dirs)
- plat.scan_structs()
- plat.scan_phandles()
- plat.process_nodes(instantiate)
- plat.read_aliases()
- plat.assign_seqs()
- # Figure out what output files we plan to generate
- output_files = dict(OUTPUT_FILES_COMMON)
- if instantiate:
- output_files.update(OUTPUT_FILES_INST)
- else:
- output_files.update(OUTPUT_FILES_NOINST)
- cmds = args[0].split(',')
- if 'all' in cmds:
- cmds = sorted(output_files.keys())
- for cmd in cmds:
- outfile = output_files.get(cmd)
- if not outfile:
- raise ValueError("Unknown command '%s': (use: %s)" %
- (cmd, ', '.join(sorted(output_files.keys()))))
- plat.setup_output(outfile.ftype,
- outfile.fname if output_dirs else output)
- plat.out_header(outfile)
- outfile.method(plat)
- plat.finish_output()
- if not warning_disabled:
- scan.show_warnings()
- return plat
|