diff options
Diffstat (limited to 'tools/dtoc')
-rw-r--r-- | tools/dtoc/dtb_platdata.py | 620 | ||||
-rw-r--r-- | tools/dtoc/dtoc_test_scan_drivers.cxx | 1 | ||||
-rwxr-xr-x | tools/dtoc/main.py | 9 | ||||
-rw-r--r-- | tools/dtoc/src_scan.py | 597 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test.dts (renamed from tools/dtoc/dtoc_test.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_add_prop.dts (renamed from tools/dtoc/dtoc_test_add_prop.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_addr32.dts (renamed from tools/dtoc/dtoc_test_addr32.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_addr32_64.dts (renamed from tools/dtoc/dtoc_test_addr32_64.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_addr64.dts (renamed from tools/dtoc/dtoc_test_addr64.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_addr64_32.dts (renamed from tools/dtoc/dtoc_test_addr64_32.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_alias_bad.dts | 58 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_alias_bad_path.dts | 58 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_alias_bad_uc.dts | 58 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_aliases.dts (renamed from tools/dtoc/dtoc_test_aliases.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_bad_reg.dts (renamed from tools/dtoc/dtoc_test_bad_reg.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_bad_reg2.dts (renamed from tools/dtoc/dtoc_test_bad_reg2.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_driver_alias.dts (renamed from tools/dtoc/dtoc_test_driver_alias.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_empty.dts (renamed from tools/dtoc/dtoc_test_empty.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_inst.dts | 58 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_invalid_driver.dts (renamed from tools/dtoc/dtoc_test_invalid_driver.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_phandle.dts (renamed from tools/dtoc/dtoc_test_phandle.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_phandle_bad.dts (renamed from tools/dtoc/dtoc_test_phandle_bad.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_phandle_bad2.dts (renamed from tools/dtoc/dtoc_test_phandle_bad2.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_phandle_cd_gpios.dts (renamed from tools/dtoc/dtoc_test_phandle_cd_gpios.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_phandle_reorder.dts (renamed from tools/dtoc/dtoc_test_phandle_reorder.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_phandle_single.dts (renamed from tools/dtoc/dtoc_test_phandle_single.dts) | 0 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_scan_drivers.cxx | 5 | ||||
-rw-r--r-- | tools/dtoc/test/dtoc_test_simple.dts (renamed from tools/dtoc/dtoc_test_simple.dts) | 4 | ||||
-rwxr-xr-x | tools/dtoc/test_dtoc.py | 1023 | ||||
-rwxr-xr-x | tools/dtoc/test_fdt.py | 31 | ||||
-rw-r--r-- | tools/dtoc/test_src_scan.py | 397 |
31 files changed, 2753 insertions, 166 deletions
diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index b7abaed..b5c449e 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -52,6 +52,20 @@ TYPE_NAMES = { 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) @@ -136,8 +150,10 @@ class DtbPlatdata(): 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: A list of Node object with compatible strings. The list - is ordered by conv_name_to_c(node.name) + _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 @@ -149,18 +165,25 @@ class DtbPlatdata(): 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): + 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 @@ -223,6 +246,7 @@ class DtbPlatdata(): """ if self._outfile != sys.stdout: self._outfile.close() + self._outfile = None def out(self, line): """Output a string to the output file @@ -281,7 +305,11 @@ class DtbPlatdata(): ValueError: if the phandle cannot be parsed or the required property is not present """ - if prop.name in ['clocks', 'cd-gpios']: + 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 @@ -301,14 +329,10 @@ class DtbPlatdata(): if not target: raise ValueError("Cannot parse '%s' in node '%s'" % (prop.name, node_name)) - cells = None - for prop_name in ['#clock-cells', '#gpio-cells']: - cells = target.props.get(prop_name) - if cells: - break + cells = target.props.get(cells_prop) if not cells: raise ValueError("Node '%s' has no cells property" % - (target.name)) + target.name) num_args = fdt_util.fdt32_to_cpu(cells.value) max_args = max(max_args, num_args) args.append(num_args) @@ -324,38 +348,84 @@ class DtbPlatdata(): """ self._fdt = fdt.FdtScan(self._dtb_fname) - def scan_node(self, root, valid_nodes): + def scan_node(self, node, valid_nodes): """Scan a node and subnodes to build a tree of node and phandle info - This adds each node to self._valid_nodes. + This adds each subnode to self._valid_nodes if it is enabled and has a + compatible string. Args: - root (Node): Root node for scan + node (Node): Node for scan for subnodes valid_nodes (list of Node): List of Node objects to add to """ - for node in root.subnodes: - if 'compatible' in node.props: - status = node.props.get('status') + 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(node) + valid_nodes.append(subnode) # recurse to handle any subnodes - self.scan_node(node, valid_nodes) + self.scan_node(subnode, valid_nodes) - def scan_tree(self): + def scan_tree(self, add_root): """Scan the device tree for useful information This fills in the following properties: - _valid_nodes: A list of nodes we wish to consider include in the - platform data + _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 = [] - self.scan_node(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): @@ -434,7 +504,6 @@ class DtbPlatdata(): """ structs = self._struct_data for node in self._valid_nodes: - node_name, _ = self._scan.get_normalized_compat_name(node) fields = {} # Get a list of all the valid properties in this node. @@ -442,9 +511,9 @@ class DtbPlatdata(): if name not in PROP_IGNORE_LIST and name[0] != '#': fields[name] = copy.deepcopy(prop) - # If we've seen this node_name before, update the existing struct. - if node_name in structs: - struct = structs[node_name] + # 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: @@ -454,11 +523,10 @@ class DtbPlatdata(): # Otherwise store this as a new struct. else: - structs[node_name] = fields + structs[node.struct_name] = fields for node in self._valid_nodes: - node_name, _ = self._scan.get_normalized_compat_name(node) - struct = structs[node_name] + 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]) @@ -562,28 +630,145 @@ class DtbPlatdata(): self.buf(', '.join(vals[i:i + 8])) self.buf('}') - def _declare_device(self, var_name, struct_name, node_parent): + def _declare_device(self, node): """Add a device declaration to the output This declares a U_BOOT_DRVINFO() for the device being processed Args: - var_name (str): C name for the node - struct_name (str): Name for the dt struct associated with the node - node_parent (Node): Parent of the node (or None if none) + node: Node to process """ - self.buf('U_BOOT_DRVINFO(%s) = {\n' % var_name) - self.buf('\t.name\t\t= "%s",\n' % struct_name) - self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, var_name)) - self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name)) + 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 + 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 _output_prop(self, node, prop): + 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: + 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: @@ -593,7 +778,7 @@ class DtbPlatdata(): if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#': return member_name = conv_name_to_c(prop.name) - self.buf('\t%s= ' % tab_to(3, '.' + member_name)) + self.buf('%s%s= ' % ('\t' * tabs, tab_to(3, '.' + member_name))) # Special handling for lists if isinstance(prop.value, list): @@ -602,35 +787,269 @@ class DtbPlatdata(): self.buf(get_value(prop.type, prop.value)) self.buf(',\n') - def _output_values(self, var_name, struct_name, node): + def _output_values(self, node): """Output the definition of a device's struct values Args: - var_name (str): C name for the node - struct_name (str): Name for the dt struct associated with the node - node (Node): Node being output + node (Node): Node to output """ self.buf('static struct %s%s %s%s = {\n' % - (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name)) + (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 output_node(self, node): + 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): + if not self.check_instantiate(True): + return + 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 """ - struct_name, _ = self._scan.get_normalized_compat_name(node) - var_name = conv_name_to_c(node.name) - self.buf('/* Node %s index %d */\n' % (node.path, node.idx)) + 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') - self._output_values(var_name, struct_name, node) - self._declare_device(var_name, struct_name, node.parent) + if not node.driver.plat: + self._output_values(node) + self._declare_device_inst(node, parent_driver) self.out(''.join(self.get_buf())) + def check_instantiate(self, require): + """Check if self._instantiate is set to the required value + + If not, this outputs a message into the current file + + Args: + require: True to require --instantiate, False to require that it not + be enabled + """ + if require != self._instantiate: + self.out( + '/* This file is not used: --instantiate was %senabled */\n' % + ('not ' if require else '')) + return False + return True + def generate_plat(self): """Generate device defintions for the platform data @@ -641,6 +1060,8 @@ class DtbPlatdata(): See the documentation in doc/driver-model/of-plat.rst for more information. """ + if not self.check_instantiate(False): + return self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n') self.out('#define DT_PLAT_C\n') self.out('\n') @@ -649,8 +1070,57 @@ class DtbPlatdata(): self.out('#include <dt-structs.h>\n') self.out('\n') - for node in self._valid_nodes: - self.output_node(node) + 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. + """ + if not self.check_instantiate(True): + return + 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())) @@ -659,6 +1129,9 @@ class DtbPlatdata(): # key: Command used to generate this file # value: OutputFile for this command OUTPUT_FILES = { + '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, @@ -666,11 +1139,18 @@ OUTPUT_FILES = { 'platdata': OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat, 'Declares the U_BOOT_DRIVER() records and platform data'), + '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, - warning_disabled=False, drivers_additional=None, basedir=None): +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: @@ -681,12 +1161,22 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, 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 """ @@ -695,15 +1185,24 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, if output and output_dirs and any(output_dirs): raise ValueError('Must specify either output or output_dirs, not both') - scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional) - plat = DtbPlatdata(scan, dtb_file, include_disabled) - scan.scan_drivers() + if not scan: + scan = src_scan.Scanner(basedir, warning_disabled, 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() + 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() cmds = args[0].split(',') if 'all' in cmds: @@ -718,3 +1217,4 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, plat.out_header(outfile) outfile.method(plat) plat.finish_output() + return plat diff --git a/tools/dtoc/dtoc_test_scan_drivers.cxx b/tools/dtoc/dtoc_test_scan_drivers.cxx deleted file mode 100644 index f448767..0000000 --- a/tools/dtoc/dtoc_test_scan_drivers.cxx +++ /dev/null @@ -1 +0,0 @@ -DM_DRIVER_ALIAS(sandbox_gpio, sandbox_gpio_alias2) diff --git a/tools/dtoc/main.py b/tools/dtoc/main.py index b0ad0f3..93706de 100755 --- a/tools/dtoc/main.py +++ b/tools/dtoc/main.py @@ -53,6 +53,8 @@ def run_tests(processes, args): sys.argv = [sys.argv[0]] test_name = args and args[0] or None + test_dtoc.setup() + test_util.RunTestSuites( result, debug=True, verbosity=1, test_preserve_dirs=False, processes=processes, test_name=test_name, toolpath=[], @@ -79,10 +81,14 @@ parser.add_option('-C', '--h-output-dir', action='store', help='Select output directory for H files (defaults to --c-output-di)') parser.add_option('-d', '--dtb-file', action='store', help='Specify the .dtb input file') +parser.add_option('-i', '--instantiate', action='store_true', default=False, + help='Instantiate devices to avoid needing device_bind()') parser.add_option('--include-disabled', action='store_true', help='Include disabled nodes') parser.add_option('-o', '--output', action='store', help='Select output filename') +parser.add_option('-p', '--phase', type=str, + help='set phase of U-Boot this invocation is for (spl/tpl)') parser.add_option('-P', '--processes', type=int, help='set number of processes to use for running tests') parser.add_option('-t', '--test', action='store_true', dest='test', @@ -102,4 +108,5 @@ elif options.test_coverage: else: dtb_platdata.run_steps(args, options.dtb_file, options.include_disabled, options.output, - [options.c_output_dir, options.h_output_dir]) + [options.c_output_dir, options.h_output_dir], + options.phase, instantiate=options.instantiate) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index f63c9fc..114212c 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -33,6 +33,8 @@ def conv_name_to_c(name): new = new.replace('-', '_') new = new.replace(',', '_') new = new.replace('.', '_') + if new == '/': + return 'root' return new def get_compat_name(node): @@ -54,15 +56,123 @@ class Driver: Attributes: name: Name of driver. For U_BOOT_DRIVER(x) this is 'x' + fname: Filename where the driver was found + uclass_id: Name of uclass, e.g. 'UCLASS_I2C' + compat: Driver data for each compatible string: + key: Compatible string, e.g. 'rockchip,rk3288-grf' + value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None + fname: Filename where the driver was found + priv (str): struct name of the priv_auto member, e.g. 'serial_priv' + plat (str): struct name of the plat_auto member, e.g. 'serial_plat' + child_priv (str): struct name of the per_child_auto member, + e.g. 'pci_child_priv' + child_plat (str): struct name of the per_child_plat_auto member, + e.g. 'pci_child_plat' + used (bool): True if the driver is used by the structs being output + phase (str): Which phase of U-Boot to use this driver + headers (list): List of header files needed for this driver (each a str) + e.g. ['<asm/cpu.h>'] + dups (list): Driver objects with the same name as this one, that were + found after this one + warn_dups (bool): True if the duplicates are not distinguisble using + the phase + uclass (Uclass): uclass for this driver + """ + def __init__(self, name, fname): + self.name = name + self.fname = fname + self.uclass_id = None + self.compat = None + self.priv = '' + self.plat = '' + self.child_priv = '' + self.child_plat = '' + self.used = False + self.phase = '' + self.headers = [] + self.dups = [] + self.warn_dups = False + self.uclass = None + + def __eq__(self, other): + return (self.name == other.name and + self.uclass_id == other.uclass_id and + self.compat == other.compat and + self.priv == other.priv and + self.plat == other.plat and + self.used == other.used) + + def __repr__(self): + return ("Driver(name='%s', used=%s, uclass_id='%s', compat=%s, priv=%s)" % + (self.name, self.used, self.uclass_id, self.compat, self.priv)) + + +class UclassDriver: + """Holds information about a uclass driver + + Attributes: + name: Uclass name, e.g. 'i2c' if the driver is for UCLASS_I2C + uclass_id: Uclass ID, e.g. 'UCLASS_I2C' + priv: struct name of the private data, e.g. 'i2c_priv' + per_dev_priv (str): struct name of the priv_auto member, e.g. 'spi_info' + per_dev_plat (str): struct name of the plat_auto member, e.g. 'i2c_chip' + per_child_priv (str): struct name of the per_child_auto member, + e.g. 'pci_child_priv' + per_child_plat (str): struct name of the per_child_plat_auto member, + e.g. 'pci_child_plat' + alias_num_to_node (dict): Aliases for this uclasses (for sequence + numbers) + key (int): Alias number, e.g. 2 for "pci2" + value (str): Node the alias points to + alias_path_to_num (dict): Convert a path to an alias number + key (str): Full path to node (e.g. '/soc/pci') + seq (int): Alias number, e.g. 2 for "pci2" + devs (list): List of devices in this uclass, each a Node + node_refs (dict): References in the linked list of devices: + key (int): Sequence number (0=first, n-1=last, -1=head, n=tail) + value (str): Reference to the device at that position """ def __init__(self, name): self.name = name + self.uclass_id = None + self.priv = '' + self.per_dev_priv = '' + self.per_dev_plat = '' + self.per_child_priv = '' + self.per_child_plat = '' + self.alias_num_to_node = {} + self.alias_path_to_num = {} + self.devs = [] + self.node_refs = {} def __eq__(self, other): - return self.name == other.name + return (self.name == other.name and + self.uclass_id == other.uclass_id and + self.priv == other.priv) def __repr__(self): - return "Driver(name='%s')" % self.name + return ("UclassDriver(name='%s', uclass_id='%s')" % + (self.name, self.uclass_id)) + + def __hash__(self): + # We can use the uclass ID since it is unique among uclasses + return hash(self.uclass_id) + + +class Struct: + """Holds information about a struct definition + + Attributes: + name: Struct name, e.g. 'fred' if the struct is 'struct fred' + fname: Filename containing the struct, in a format that C files can + include, e.g. 'asm/clk.h' + """ + def __init__(self, name, fname): + self.name = name + self.fname =fname + + def __repr__(self): + return ("Struct(name='%s', fname='%s')" % (self.name, self.fname)) class Scanner: @@ -81,8 +191,22 @@ class Scanner: _warning_disabled: true to disable warnings about driver names not found _drivers_additional (list or str): List of additional drivers to use during scanning + _of_match: Dict holding information about compatible strings + key: Name of struct udevice_id variable + value: Dict of compatible info in that variable: + key: Compatible string, e.g. 'rockchip,rk3288-grf' + value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None + _compat_to_driver: Maps compatible strings to Driver + _uclass: Dict of uclass information + key: uclass name, e.g. 'UCLASS_I2C' + value: UClassDriver + _structs: Dict of all structs found in U-Boot: + key: Name of struct + value: Struct object + _phase: The phase of U-Boot that we are generating data for, e.g. 'spl' + or 'tpl'. None if not known """ - def __init__(self, basedir, warning_disabled, drivers_additional): + def __init__(self, basedir, warning_disabled, drivers_additional, phase=''): """Set up a new Scanner """ if not basedir: @@ -94,6 +218,22 @@ class Scanner: self._driver_aliases = {} self._drivers_additional = drivers_additional or [] self._warning_disabled = warning_disabled + self._of_match = {} + self._compat_to_driver = {} + self._uclass = {} + self._structs = {} + self._phase = phase + + def get_driver(self, name): + """Get a driver given its name + + Args: + name (str): Driver name + + Returns: + Driver: Driver or None if not found + """ + return self._drivers.get(name) def get_normalized_compat_name(self, node): """Get a node's normalized compat name @@ -112,7 +252,10 @@ class Scanner: In case of no match found, the return will be the same as get_compat_name() """ - compat_list_c = get_compat_name(node) + if not node.parent: + compat_list_c = ['root_driver'] + else: + compat_list_c = get_compat_name(node) for compat_c in compat_list_c: if not compat_c in self._drivers.keys(): @@ -131,10 +274,319 @@ class Scanner: return compat_list_c[0], compat_list_c[1:] + def _parse_structs(self, fname, buff): + """Parse a H file to extract struct definitions contained within + + This parses 'struct xx {' definitions to figure out what structs this + header defines. + + Args: + buff (str): Contents of file + fname (str): Filename (to use when printing errors) + """ + structs = {} + + re_struct = re.compile('^struct ([a-z0-9_]+) {$') + re_asm = re.compile('../arch/[a-z0-9]+/include/asm/(.*)') + prefix = '' + for line in buff.splitlines(): + # Handle line continuation + if prefix: + line = prefix + line + prefix = '' + if line.endswith('\\'): + prefix = line[:-1] + continue + + m_struct = re_struct.match(line) + if m_struct: + name = m_struct.group(1) + include_dir = os.path.join(self._basedir, 'include') + rel_fname = os.path.relpath(fname, include_dir) + m_asm = re_asm.match(rel_fname) + if m_asm: + rel_fname = 'asm/' + m_asm.group(1) + structs[name] = Struct(name, rel_fname) + self._structs.update(structs) + + @classmethod + def _get_re_for_member(cls, member): + """_get_re_for_member: Get a compiled regular expression + + Args: + member (str): Struct member name, e.g. 'priv_auto' + + Returns: + re.Pattern: Compiled regular expression that parses: + + .member = sizeof(struct fred), + + and returns "fred" as group 1 + """ + return re.compile(r'^\s*.%s\s*=\s*sizeof\(struct\s+(.*)\),$' % member) + + def _parse_uclass_driver(self, fname, buff): + """Parse a C file to extract uclass driver information contained within + + This parses UCLASS_DRIVER() structs to obtain various pieces of useful + information. + + It updates the following member: + _uclass: Dict of uclass information + key: uclass name, e.g. 'UCLASS_I2C' + value: UClassDriver + + Args: + fname (str): Filename being parsed (used for warnings) + buff (str): Contents of file + """ + uc_drivers = {} + + # Collect the driver name and associated Driver + driver = None + re_driver = re.compile(r'^UCLASS_DRIVER\((.*)\)') + + # Collect the uclass ID, e.g. 'UCLASS_SPI' + re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)') + + # Matches the header/size information for uclass-private data + re_priv = self._get_re_for_member('priv_auto') + + # Set up parsing for the auto members + re_per_device_priv = self._get_re_for_member('per_device_auto') + re_per_device_plat = self._get_re_for_member('per_device_plat_auto') + re_per_child_priv = self._get_re_for_member('per_child_auto') + re_per_child_plat = self._get_re_for_member('per_child_plat_auto') + + prefix = '' + for line in buff.splitlines(): + # Handle line continuation + if prefix: + line = prefix + line + prefix = '' + if line.endswith('\\'): + prefix = line[:-1] + continue + + driver_match = re_driver.search(line) + + # If we have seen UCLASS_DRIVER()... + if driver: + m_id = re_id.search(line) + m_priv = re_priv.match(line) + m_per_dev_priv = re_per_device_priv.match(line) + m_per_dev_plat = re_per_device_plat.match(line) + m_per_child_priv = re_per_child_priv.match(line) + m_per_child_plat = re_per_child_plat.match(line) + if m_id: + driver.uclass_id = m_id.group(1) + elif m_priv: + driver.priv = m_priv.group(1) + elif m_per_dev_priv: + driver.per_dev_priv = m_per_dev_priv.group(1) + elif m_per_dev_plat: + driver.per_dev_plat = m_per_dev_plat.group(1) + elif m_per_child_priv: + driver.per_child_priv = m_per_child_priv.group(1) + elif m_per_child_plat: + driver.per_child_plat = m_per_child_plat.group(1) + elif '};' in line: + if not driver.uclass_id: + raise ValueError( + "%s: Cannot parse uclass ID in driver '%s'" % + (fname, driver.name)) + uc_drivers[driver.uclass_id] = driver + driver = None + + elif driver_match: + driver_name = driver_match.group(1) + driver = UclassDriver(driver_name) + + self._uclass.update(uc_drivers) + + def _parse_driver(self, fname, buff): + """Parse a C file to extract driver information contained within + + This parses U_BOOT_DRIVER() structs to obtain various pieces of useful + information. + + It updates the following members: + _drivers - updated with new Driver records for each driver found + in the file + _of_match - updated with each compatible string found in the file + _compat_to_driver - Maps compatible string to Driver + _driver_aliases - Maps alias names to driver name + + Args: + fname (str): Filename being parsed (used for warnings) + buff (str): Contents of file + + Raises: + ValueError: Compatible variable is mentioned in .of_match in + U_BOOT_DRIVER() but not found in the file + """ + # Dict holding information about compatible strings collected in this + # function so far + # key: Name of struct udevice_id variable + # value: Dict of compatible info in that variable: + # key: Compatible string, e.g. 'rockchip,rk3288-grf' + # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None + of_match = {} + + # Dict holding driver information collected in this function so far + # key: Driver name (C name as in U_BOOT_DRIVER(xxx)) + # value: Driver + drivers = {} + + # Collect the driver info + driver = None + re_driver = re.compile(r'^U_BOOT_DRIVER\((.*)\)') + + # Collect the uclass ID, e.g. 'UCLASS_SPI' + re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)') + + # Collect the compatible string, e.g. 'rockchip,rk3288-grf' + compat = None + re_compat = re.compile(r'{\s*.compatible\s*=\s*"(.*)"\s*' + r'(,\s*.data\s*=\s*(\S*))?\s*},') + + # This is a dict of compatible strings that were found: + # key: Compatible string, e.g. 'rockchip,rk3288-grf' + # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None + compat_dict = {} + + # Holds the var nane of the udevice_id list, e.g. + # 'rk3288_syscon_ids_noc' in + # static const struct udevice_id rk3288_syscon_ids_noc[] = { + ids_name = None + re_ids = re.compile(r'struct udevice_id (.*)\[\]\s*=') + + # Matches the references to the udevice_id list + re_of_match = re.compile( + r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)(\))?,') + + re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$') + re_hdr = re.compile('^\s*DM_HEADER\((.*)\).*$') + re_alias = re.compile(r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)') + + # Matches the struct name for priv, plat + re_priv = self._get_re_for_member('priv_auto') + re_plat = self._get_re_for_member('plat_auto') + re_child_priv = self._get_re_for_member('per_child_auto') + re_child_plat = self._get_re_for_member('per_child_plat_auto') + + prefix = '' + for line in buff.splitlines(): + # Handle line continuation + if prefix: + line = prefix + line + prefix = '' + if line.endswith('\\'): + prefix = line[:-1] + continue + + driver_match = re_driver.search(line) + + # If this line contains U_BOOT_DRIVER()... + if driver: + m_id = re_id.search(line) + m_of_match = re_of_match.search(line) + m_priv = re_priv.match(line) + m_plat = re_plat.match(line) + m_cplat = re_child_plat.match(line) + m_cpriv = re_child_priv.match(line) + m_phase = re_phase.match(line) + m_hdr = re_hdr.match(line) + if m_priv: + driver.priv = m_priv.group(1) + elif m_plat: + driver.plat = m_plat.group(1) + elif m_cplat: + driver.child_plat = m_cplat.group(1) + elif m_cpriv: + driver.child_priv = m_cpriv.group(1) + elif m_id: + driver.uclass_id = m_id.group(1) + elif m_of_match: + compat = m_of_match.group(2) + elif m_phase: + driver.phase = m_phase.group(1) + elif m_hdr: + driver.headers.append(m_hdr.group(1)) + elif '};' in line: + is_root = driver.name == 'root_driver' + if driver.uclass_id and (compat or is_root): + if not is_root: + if compat not in of_match: + raise ValueError( + "%s: Unknown compatible var '%s' (found: %s)" % + (fname, compat, ','.join(of_match.keys()))) + driver.compat = of_match[compat] + + # This needs to be deterministic, since a driver may + # have multiple compatible strings pointing to it. + # We record the one earliest in the alphabet so it + # will produce the same result on all machines. + for compat_id in of_match[compat]: + old = self._compat_to_driver.get(compat_id) + if not old or driver.name < old.name: + self._compat_to_driver[compat_id] = driver + drivers[driver.name] = driver + else: + # The driver does not have a uclass or compat string. + # The first is required but the second is not, so just + # ignore this. + pass + driver = None + ids_name = None + compat = None + compat_dict = {} + + elif ids_name: + compat_m = re_compat.search(line) + if compat_m: + compat_dict[compat_m.group(1)] = compat_m.group(3) + elif '};' in line: + of_match[ids_name] = compat_dict + ids_name = None + elif driver_match: + driver_name = driver_match.group(1) + driver = Driver(driver_name, fname) + else: + ids_m = re_ids.search(line) + m_alias = re_alias.match(line) + if ids_m: + ids_name = ids_m.group(1) + elif m_alias: + self._driver_aliases[m_alias[2]] = m_alias[1] + + # Make the updates based on what we found + for driver in drivers.values(): + if driver.name in self._drivers: + orig = self._drivers[driver.name] + if self._phase: + # If the original driver matches our phase, use it + if orig.phase == self._phase: + orig.dups.append(driver) + continue + + # Otherwise use the new driver, which is assumed to match + else: + # We have no way of distinguishing them + driver.warn_dups = True + driver.dups.append(orig) + self._drivers[driver.name] = driver + self._of_match.update(of_match) + def scan_driver(self, fname): """Scan a driver file to build a list of driver names and aliases - This procedure will populate self._drivers and self._driver_aliases + It updates the following members: + _drivers - updated with new Driver records for each driver found + in the file + _of_match - updated with each compatible string found in the file + _compat_to_driver - Maps compatible string to Driver + _driver_aliases - Maps alias names to driver name Args fname: Driver filename to scan @@ -147,23 +599,35 @@ class Scanner: print("Skipping file '%s' due to unicode error" % fname) return - # The following re will search for driver names declared as - # U_BOOT_DRIVER(driver_name) - drivers = re.findall(r'U_BOOT_DRIVER\((.*)\)', buff) + # If this file has any U_BOOT_DRIVER() declarations, process it to + # obtain driver information + if 'U_BOOT_DRIVER' in buff: + self._parse_driver(fname, buff) + if 'UCLASS_DRIVER' in buff: + self._parse_uclass_driver(fname, buff) - for driver in drivers: - self._drivers[driver] = Driver(driver) + def scan_header(self, fname): + """Scan a header file to build a list of struct definitions - # The following re will search for driver aliases declared as - # DM_DRIVER_ALIAS(alias, driver_name) - driver_aliases = re.findall( - r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)', - buff) + It updates the following members: + _structs - updated with new Struct records for each struct found + in the file - for alias in driver_aliases: # pragma: no cover - if len(alias) != 2: - continue - self._driver_aliases[alias[1]] = alias[0] + Args + fname: header filename to scan + """ + with open(fname, encoding='utf-8') as inf: + try: + buff = inf.read() + except UnicodeDecodeError: + # This seems to happen on older Python versions + print("Skipping file '%s' due to unicode error" % fname) + return + + # If this file has any U_BOOT_DRIVER() declarations, process it to + # obtain driver information + if 'struct' in buff: + self._parse_structs(fname, buff) def scan_drivers(self): """Scan the driver folders to build a list of driver names and aliases @@ -171,11 +635,17 @@ class Scanner: This procedure will populate self._drivers and self._driver_aliases """ for (dirpath, _, filenames) in os.walk(self._basedir): + rel_path = dirpath[len(self._basedir):] + if rel_path.startswith('/'): + rel_path = rel_path[1:] + if rel_path.startswith('build') or rel_path.startswith('.git'): + continue for fname in filenames: - if not fname.endswith('.c'): - continue - self.scan_driver(dirpath + '/' + fname) - + pathname = dirpath + '/' + fname + if fname.endswith('.c'): + self.scan_driver(pathname) + elif fname.endswith('.h'): + self.scan_header(pathname) for fname in self._drivers_additional: if not isinstance(fname, str) or len(fname) == 0: continue @@ -183,3 +653,84 @@ class Scanner: self.scan_driver(fname) else: self.scan_driver(self._basedir + '/' + fname) + + # Get the uclass for each driver + # TODO: Can we just get the uclass for the ones we use, e.g. in + # mark_used()? + for driver in self._drivers.values(): + driver.uclass = self._uclass.get(driver.uclass_id) + + def mark_used(self, nodes): + """Mark the drivers associated with a list of nodes as 'used' + + This takes a list of nodes, finds the driver for each one and marks it + as used. + + If two used drivers have the same name, issue a warning. + + Args: + nodes (list of None): Nodes that are in use + """ + # Figure out which drivers we actually use + for node in nodes: + struct_name, _ = self.get_normalized_compat_name(node) + driver = self._drivers.get(struct_name) + if driver: + driver.used = True + if driver.dups and driver.warn_dups: + print("Warning: Duplicate driver name '%s' (orig=%s, dups=%s)" % + (driver.name, driver.fname, + ', '.join([drv.fname for drv in driver.dups]))) + + def add_uclass_alias(self, name, num, node): + """Add an alias to a uclass + + Args: + name: Name of uclass, e.g. 'i2c' + num: Alias number, e.g. 2 for alias 'i2c2' + node: Node the alias points to, or None if None + + Returns: + True if the node was added + False if the node was not added (uclass of that name not found) + None if the node could not be added because it was None + """ + for uclass in self._uclass.values(): + if uclass.name == name: + if node is None: + return None + uclass.alias_num_to_node[int(num)] = node + uclass.alias_path_to_num[node.path] = int(num) + return True + return False + + def assign_seq(self, node): + """Figure out the sequence number for a node + + This looks in the node's uclass and assigns a sequence number if needed, + based on the aliases and other nodes in that uclass. + + It updates the uclass alias_path_to_num and alias_num_to_node + + Args: + node (Node): Node object to look up + """ + if node.driver and node.seq == -1 and node.uclass: + uclass = node.uclass + num = uclass.alias_path_to_num.get(node.path) + if num is not None: + return num + else: + # Dynamically allocate the next available value after all + # existing ones + if uclass.alias_num_to_node: + start = max(uclass.alias_num_to_node.keys()) + else: + start = -1 + for seq in range(start + 1, 1000): + if seq not in uclass.alias_num_to_node: + break + uclass.alias_path_to_num[node.path] = seq + uclass.alias_num_to_node[seq] = node + return seq + return None diff --git a/tools/dtoc/dtoc_test.dts b/tools/dtoc/test/dtoc_test.dts index b225948..b225948 100644 --- a/tools/dtoc/dtoc_test.dts +++ b/tools/dtoc/test/dtoc_test.dts diff --git a/tools/dtoc/dtoc_test_add_prop.dts b/tools/dtoc/test/dtoc_test_add_prop.dts index fa296e5..fa296e5 100644 --- a/tools/dtoc/dtoc_test_add_prop.dts +++ b/tools/dtoc/test/dtoc_test_add_prop.dts diff --git a/tools/dtoc/dtoc_test_addr32.dts b/tools/dtoc/test/dtoc_test_addr32.dts index 2390454..2390454 100644 --- a/tools/dtoc/dtoc_test_addr32.dts +++ b/tools/dtoc/test/dtoc_test_addr32.dts diff --git a/tools/dtoc/dtoc_test_addr32_64.dts b/tools/dtoc/test/dtoc_test_addr32_64.dts index 7599d5b..7599d5b 100644 --- a/tools/dtoc/dtoc_test_addr32_64.dts +++ b/tools/dtoc/test/dtoc_test_addr32_64.dts diff --git a/tools/dtoc/dtoc_test_addr64.dts b/tools/dtoc/test/dtoc_test_addr64.dts index 263d251..263d251 100644 --- a/tools/dtoc/dtoc_test_addr64.dts +++ b/tools/dtoc/test/dtoc_test_addr64.dts diff --git a/tools/dtoc/dtoc_test_addr64_32.dts b/tools/dtoc/test/dtoc_test_addr64_32.dts index 85e4f5f..85e4f5f 100644 --- a/tools/dtoc/dtoc_test_addr64_32.dts +++ b/tools/dtoc/test/dtoc_test_addr64_32.dts diff --git a/tools/dtoc/test/dtoc_test_alias_bad.dts b/tools/dtoc/test/dtoc_test_alias_bad.dts new file mode 100644 index 0000000..d4f502a --- /dev/null +++ b/tools/dtoc/test/dtoc_test_alias_bad.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + aliases { + testbus2 = &bus2; + testfdt1 = &testfdt_1; + i2c4- = &i2c; + }; + + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + }; + + i2c: i2c { + u-boot,dm-pre-reloc; + compatible = "sandbox,i2c"; + intval = <3>; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; + }; + + bus2: some-bus { + #address-cells = <1>; + #size-cells = <0>; + compatible = "denx,u-boot-test-bus"; + reg = <3 1>; + ping-expect = <4>; + ping-add = <4>; + testfdt_1: test { + compatible = "denx,u-boot-fdt-test", "google,another-fdt-test"; + reg = <5>; + ping-expect = <5>; + ping-add = <5>; + }; + + test0 { + compatible = "google,another-fdt-test"; + }; + }; +}; diff --git a/tools/dtoc/test/dtoc_test_alias_bad_path.dts b/tools/dtoc/test/dtoc_test_alias_bad_path.dts new file mode 100644 index 0000000..0beca4f --- /dev/null +++ b/tools/dtoc/test/dtoc_test_alias_bad_path.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + aliases { + testbus2 = &bus2; + testfdt1 = &testfdt_1; + i2c4 = "/does/not/exist"; + }; + + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + }; + + i2c: i2c { + u-boot,dm-pre-reloc; + compatible = "sandbox,i2c"; + intval = <3>; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; + }; + + bus2: some-bus { + #address-cells = <1>; + #size-cells = <0>; + compatible = "denx,u-boot-test-bus"; + reg = <3 1>; + ping-expect = <4>; + ping-add = <4>; + testfdt_1: test { + compatible = "denx,u-boot-fdt-test", "google,another-fdt-test"; + reg = <5>; + ping-expect = <5>; + ping-add = <5>; + }; + + test0 { + compatible = "google,another-fdt-test"; + }; + }; +}; diff --git a/tools/dtoc/test/dtoc_test_alias_bad_uc.dts b/tools/dtoc/test/dtoc_test_alias_bad_uc.dts new file mode 100644 index 0000000..ae64f5b --- /dev/null +++ b/tools/dtoc/test/dtoc_test_alias_bad_uc.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + aliases { + testbus2 = &bus2; + testfdt1 = &testfdt_1; + other1 = &testfdt_1; + }; + + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + }; + + i2c: i2c { + u-boot,dm-pre-reloc; + compatible = "sandbox,i2c"; + intval = <3>; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; + }; + + bus2: some-bus { + #address-cells = <1>; + #size-cells = <0>; + compatible = "denx,u-boot-test-bus"; + reg = <3 1>; + ping-expect = <4>; + ping-add = <4>; + testfdt_1: test { + compatible = "denx,u-boot-fdt-test", "google,another-fdt-test"; + reg = <5>; + ping-expect = <5>; + ping-add = <5>; + }; + + test0 { + compatible = "google,another-fdt-test"; + }; + }; +}; diff --git a/tools/dtoc/dtoc_test_aliases.dts b/tools/dtoc/test/dtoc_test_aliases.dts index ae33716..ae33716 100644 --- a/tools/dtoc/dtoc_test_aliases.dts +++ b/tools/dtoc/test/dtoc_test_aliases.dts diff --git a/tools/dtoc/dtoc_test_bad_reg.dts b/tools/dtoc/test/dtoc_test_bad_reg.dts index 1312acb..1312acb 100644 --- a/tools/dtoc/dtoc_test_bad_reg.dts +++ b/tools/dtoc/test/dtoc_test_bad_reg.dts diff --git a/tools/dtoc/dtoc_test_bad_reg2.dts b/tools/dtoc/test/dtoc_test_bad_reg2.dts index 3e9efa4..3e9efa4 100644 --- a/tools/dtoc/dtoc_test_bad_reg2.dts +++ b/tools/dtoc/test/dtoc_test_bad_reg2.dts diff --git a/tools/dtoc/dtoc_test_driver_alias.dts b/tools/dtoc/test/dtoc_test_driver_alias.dts index da7973b..da7973b 100644 --- a/tools/dtoc/dtoc_test_driver_alias.dts +++ b/tools/dtoc/test/dtoc_test_driver_alias.dts diff --git a/tools/dtoc/dtoc_test_empty.dts b/tools/dtoc/test/dtoc_test_empty.dts index b225948..b225948 100644 --- a/tools/dtoc/dtoc_test_empty.dts +++ b/tools/dtoc/test/dtoc_test_empty.dts diff --git a/tools/dtoc/test/dtoc_test_inst.dts b/tools/dtoc/test/dtoc_test_inst.dts new file mode 100644 index 0000000..b8177fc --- /dev/null +++ b/tools/dtoc/test/dtoc_test_inst.dts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device tree file for dtoc + * + * Copyright 2017 Google, Inc + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + aliases { + testbus2 = &bus2; + testfdt1 = &testfdt_1; + i2c4 = &i2c; + }; + + spl-test { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + boolval; + intval = <1>; + }; + + i2c: i2c { + u-boot,dm-pre-reloc; + compatible = "sandbox,i2c"; + intval = <3>; + }; + + spl-test3 { + u-boot,dm-pre-reloc; + compatible = "sandbox,spl-test"; + stringarray = "one"; + longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; + }; + + bus2: some-bus { + #address-cells = <1>; + #size-cells = <0>; + compatible = "denx,u-boot-test-bus"; + reg = <3 1>; + ping-expect = <4>; + ping-add = <4>; + testfdt_1: test { + compatible = "denx,u-boot-fdt-test", "google,another-fdt-test"; + reg = <5>; + ping-expect = <5>; + ping-add = <5>; + }; + + test0 { + compatible = "google,another-fdt-test"; + }; + }; +}; diff --git a/tools/dtoc/dtoc_test_invalid_driver.dts b/tools/dtoc/test/dtoc_test_invalid_driver.dts index 914ac3e..914ac3e 100644 --- a/tools/dtoc/dtoc_test_invalid_driver.dts +++ b/tools/dtoc/test/dtoc_test_invalid_driver.dts diff --git a/tools/dtoc/dtoc_test_phandle.dts b/tools/dtoc/test/dtoc_test_phandle.dts index a71acff..a71acff 100644 --- a/tools/dtoc/dtoc_test_phandle.dts +++ b/tools/dtoc/test/dtoc_test_phandle.dts diff --git a/tools/dtoc/dtoc_test_phandle_bad.dts b/tools/dtoc/test/dtoc_test_phandle_bad.dts index a3ddc59..a3ddc59 100644 --- a/tools/dtoc/dtoc_test_phandle_bad.dts +++ b/tools/dtoc/test/dtoc_test_phandle_bad.dts diff --git a/tools/dtoc/dtoc_test_phandle_bad2.dts b/tools/dtoc/test/dtoc_test_phandle_bad2.dts index fe25f56..fe25f56 100644 --- a/tools/dtoc/dtoc_test_phandle_bad2.dts +++ b/tools/dtoc/test/dtoc_test_phandle_bad2.dts diff --git a/tools/dtoc/dtoc_test_phandle_cd_gpios.dts b/tools/dtoc/test/dtoc_test_phandle_cd_gpios.dts index 241743e..241743e 100644 --- a/tools/dtoc/dtoc_test_phandle_cd_gpios.dts +++ b/tools/dtoc/test/dtoc_test_phandle_cd_gpios.dts diff --git a/tools/dtoc/dtoc_test_phandle_reorder.dts b/tools/dtoc/test/dtoc_test_phandle_reorder.dts index aa71d56..aa71d56 100644 --- a/tools/dtoc/dtoc_test_phandle_reorder.dts +++ b/tools/dtoc/test/dtoc_test_phandle_reorder.dts diff --git a/tools/dtoc/dtoc_test_phandle_single.dts b/tools/dtoc/test/dtoc_test_phandle_single.dts index aacd0b1..aacd0b1 100644 --- a/tools/dtoc/dtoc_test_phandle_single.dts +++ b/tools/dtoc/test/dtoc_test_phandle_single.dts diff --git a/tools/dtoc/test/dtoc_test_scan_drivers.cxx b/tools/dtoc/test/dtoc_test_scan_drivers.cxx new file mode 100644 index 0000000..f370b89 --- /dev/null +++ b/tools/dtoc/test/dtoc_test_scan_drivers.cxx @@ -0,0 +1,5 @@ +/* Aliases must be in driver files */ +U_BOOT_DRIVER(sandbox_gpio) { +}; + +DM_DRIVER_ALIAS(sandbox_gpio, sandbox_gpio_alias2) diff --git a/tools/dtoc/dtoc_test_simple.dts b/tools/dtoc/test/dtoc_test_simple.dts index 1c87b89..d8ab861 100644 --- a/tools/dtoc/dtoc_test_simple.dts +++ b/tools/dtoc/test/dtoc_test_simple.dts @@ -45,12 +45,12 @@ }; i2c@0 { - compatible = "sandbox,i2c-test"; + compatible = "sandbox,i2c"; u-boot,dm-pre-reloc; #address-cells = <1>; #size-cells = <0>; pmic@9 { - compatible = "sandbox,pmic-test"; + compatible = "sandbox,pmic"; u-boot,dm-pre-reloc; reg = <9>; low-power; diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index d961d67..1912a87 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -10,16 +10,19 @@ tool. """ import collections +import copy import glob import os import struct import unittest +from dtb_platdata import Ftype from dtb_platdata import get_value from dtb_platdata import tab_to from dtoc import dtb_platdata from dtoc import fdt from dtoc import fdt_util +from dtoc import src_scan from dtoc.src_scan import conv_name_to_c from dtoc.src_scan import get_compat_name from patman import test_util @@ -38,13 +41,23 @@ HEADER = '''/* #include <stdbool.h> #include <linux/libfdt.h>''' -C_HEADER = '''/* +DECL_HEADER = '''/* + * DO NOT MODIFY + * + * Declares externs for all device/uclass instances. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ +''' + +C_HEADER_PRE = '''/* * DO NOT MODIFY * * Declares the U_BOOT_DRIVER() records and platform data. * This was generated by dtoc from a .dtb (device tree binary) file. */ +''' +C_HEADER = C_HEADER_PRE + ''' /* Allow use of U_BOOT_DRVINFO() in this file */ #define DT_PLAT_C @@ -53,6 +66,21 @@ C_HEADER = '''/* #include <dt-structs.h> ''' +UCLASS_HEADER_COMMON = '''/* + * DO NOT MODIFY + * + * Declares the uclass instances (struct uclass). + * This was generated by dtoc from a .dtb (device tree binary) file. + */ +''' + +UCLASS_HEADER = UCLASS_HEADER_COMMON + ''' +/* This file is not used: --instantiate was not enabled */ +''' + +# Scanner saved from a previous run of the tests (to speed things up) +saved_scan = None + # This is a test so is allowed to access private things in the module it is # testing # pylint: disable=W0212 @@ -67,10 +95,23 @@ def get_dtb_file(dts_fname, capture_stderr=False): Returns: str: Filename of compiled file in output directory """ - return fdt_util.EnsureCompiled(os.path.join(OUR_PATH, dts_fname), + return fdt_util.EnsureCompiled(os.path.join(OUR_PATH, 'test', dts_fname), capture_stderr=capture_stderr) +def setup(): + global saved_scan + + # Disable warnings so that calls to get_normalized_compat_name() will not + # output things. + saved_scan = src_scan.Scanner(None, True, False) + saved_scan.scan_drivers() + +def copy_scan(): + """Get a copy of saved_scan so that each test can start clean""" + return copy.deepcopy(saved_scan) + + class TestDtoc(unittest.TestCase): """Tests for dtoc""" @classmethod @@ -112,15 +153,22 @@ class TestDtoc(unittest.TestCase): self.assertEqual(expected, actual) @staticmethod - def run_test(args, dtb_file, output): + def run_test(args, dtb_file, output, instantiate=False): """Run a test using dtoc Args: args (list of str): List of arguments for dtoc dtb_file (str): Filename of .dtb file output (str): Filename of output file + + Returns: + DtbPlatdata object """ - dtb_platdata.run_steps(args, dtb_file, False, output, [], True) + # Make a copy of the 'scan' object, since it includes uclasses and + # drivers, which get updated during execution. + return dtb_platdata.run_steps( + args, dtb_file, False, output, [], None, instantiate, + warning_disabled=True, scan=copy_scan()) def test_name(self): """Test conversion of device tree names to C identifiers""" @@ -175,7 +223,10 @@ class TestDtoc(unittest.TestCase): """Test output from a device tree file with no nodes""" dtb_file = get_dtb_file('dtoc_test_empty.dts') output = tools.GetOutputFilename('output') - self.run_test(['struct'], dtb_file, output) + + # Run this one without saved_scan to complete test coverage + dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], None, + False) with open(output) as infile: lines = infile.read().splitlines() self.assertEqual(HEADER.splitlines(), lines) @@ -185,10 +236,62 @@ class TestDtoc(unittest.TestCase): lines = infile.read().splitlines() self.assertEqual(C_HEADER.splitlines() + [''], lines) + decl_text = DECL_HEADER + ''' +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> + +/* driver declarations - these allow DM_DRIVER_GET() to be used */ +extern U_BOOT_DRIVER(sandbox_i2c); +extern U_BOOT_DRIVER(sandbox_pmic); +extern U_BOOT_DRIVER(sandbox_spl_test); +extern U_BOOT_DRIVER(sandbox_spl_test); +extern U_BOOT_DRIVER(sandbox_spl_test); + +/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */ +extern UCLASS_DRIVER(i2c); +extern UCLASS_DRIVER(misc); +extern UCLASS_DRIVER(pmic); +''' + decl_text_inst = DECL_HEADER + ''' +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> + +/* driver declarations - these allow DM_DRIVER_GET() to be used */ +extern U_BOOT_DRIVER(sandbox_i2c); +extern U_BOOT_DRIVER(root_driver); +extern U_BOOT_DRIVER(denx_u_boot_test_bus); +extern U_BOOT_DRIVER(sandbox_spl_test); +extern U_BOOT_DRIVER(sandbox_spl_test); +extern U_BOOT_DRIVER(denx_u_boot_fdt_test); +extern U_BOOT_DRIVER(denx_u_boot_fdt_test); + +/* device declarations - these allow DM_DEVICE_REF() to be used */ +extern DM_DEVICE_INST(i2c); +extern DM_DEVICE_INST(root); +extern DM_DEVICE_INST(some_bus); +extern DM_DEVICE_INST(spl_test); +extern DM_DEVICE_INST(spl_test3); +extern DM_DEVICE_INST(test); +extern DM_DEVICE_INST(test0); + +/* uclass driver declarations - needed for DM_UCLASS_DRIVER_REF() */ +extern UCLASS_DRIVER(i2c); +extern UCLASS_DRIVER(misc); +extern UCLASS_DRIVER(root); +extern UCLASS_DRIVER(testbus); +extern UCLASS_DRIVER(testfdt); + +/* uclass declarations - needed for DM_UCLASS_REF() */ +extern DM_UCLASS_INST(i2c); +extern DM_UCLASS_INST(misc); +extern DM_UCLASS_INST(root); +extern DM_UCLASS_INST(testbus); +extern DM_UCLASS_INST(testfdt); +''' struct_text = HEADER + ''' -struct dtd_sandbox_i2c_test { +struct dtd_sandbox_i2c { }; -struct dtd_sandbox_pmic_test { +struct dtd_sandbox_pmic { \tbool\t\tlow_power; \tfdt64_t\t\treg[2]; }; @@ -205,31 +308,52 @@ struct dtd_sandbox_spl_test { \tconst char *\tstringval; }; ''' - platdata_text = C_HEADER + ''' -/* Node /i2c@0 index 0 */ -static struct dtd_sandbox_i2c_test dtv_i2c_at_0 = { +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: i2c_at_0 sandbox_i2c + * 1: pmic_at_9 sandbox_pmic + * 2: spl_test sandbox_spl_test + * 3: spl_test2 sandbox_spl_test + * 4: spl_test3 sandbox_spl_test + * --- -------------------- -------------------- + */ + +/* + * Node /i2c@0 index 0 + * driver sandbox_i2c parent None + */ +static struct dtd_sandbox_i2c dtv_i2c_at_0 = { }; U_BOOT_DRVINFO(i2c_at_0) = { -\t.name\t\t= "sandbox_i2c_test", -\t.plat\t= &dtv_i2c_at_0, +\t.name\t\t= "sandbox_i2c", +\t.plat\t\t= &dtv_i2c_at_0, \t.plat_size\t= sizeof(dtv_i2c_at_0), \t.parent_idx\t= -1, }; -/* Node /i2c@0/pmic@9 index 1 */ -static struct dtd_sandbox_pmic_test dtv_pmic_at_9 = { +/* + * Node /i2c@0/pmic@9 index 1 + * driver sandbox_pmic parent sandbox_i2c + */ +static struct dtd_sandbox_pmic dtv_pmic_at_9 = { \t.low_power\t\t= true, \t.reg\t\t\t= {0x9, 0x0}, }; U_BOOT_DRVINFO(pmic_at_9) = { -\t.name\t\t= "sandbox_pmic_test", -\t.plat\t= &dtv_pmic_at_9, +\t.name\t\t= "sandbox_pmic", +\t.plat\t\t= &dtv_pmic_at_9, \t.plat_size\t= sizeof(dtv_pmic_at_9), \t.parent_idx\t= 0, }; -/* Node /spl-test index 2 */ +/* + * Node /spl-test index 2 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test = { \t.boolval\t\t= true, \t.bytearray\t\t= {0x6, 0x0, 0x0}, @@ -244,12 +368,15 @@ static struct dtd_sandbox_spl_test dtv_spl_test = { }; U_BOOT_DRVINFO(spl_test) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test, +\t.plat\t\t= &dtv_spl_test, \t.plat_size\t= sizeof(dtv_spl_test), \t.parent_idx\t= -1, }; -/* Node /spl-test2 index 3 */ +/* + * Node /spl-test2 index 3 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test2 = { \t.acpi_name\t\t= "\\\\_SB.GPO0", \t.bytearray\t\t= {0x1, 0x23, 0x34}, @@ -263,12 +390,15 @@ static struct dtd_sandbox_spl_test dtv_spl_test2 = { }; U_BOOT_DRVINFO(spl_test2) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test2, +\t.plat\t\t= &dtv_spl_test2, \t.plat_size\t= sizeof(dtv_spl_test2), \t.parent_idx\t= -1, }; -/* Node /spl-test3 index 4 */ +/* + * Node /spl-test3 index 4 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test3 = { \t.longbytearray\t\t= {0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, \t\t0x0}, @@ -276,12 +406,406 @@ static struct dtd_sandbox_spl_test dtv_spl_test3 = { }; U_BOOT_DRVINFO(spl_test3) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test3, +\t.plat\t\t= &dtv_spl_test3, \t.plat_size\t= sizeof(dtv_spl_test3), \t.parent_idx\t= -1, }; ''' + uclass_text = UCLASS_HEADER + uclass_text_inst = ''' + +#include <common.h> +#include <dm.h> +#include <dt-structs.h> + +/* + * uclass declarations, ordered by 'struct uclass' linker_list idx: + * 0: i2c + * 1: misc + * 2: root + * 3: testbus + * 4: testfdt + * + * Sequence numbers allocated in each uclass: + * i2c: UCLASS_I2C + * 4: /i2c + * misc: UCLASS_MISC + * 0: /spl-test + * 1: /spl-test3 + * root: UCLASS_ROOT + * 0: / + * testbus: UCLASS_TEST_BUS + * 2: /some-bus + * testfdt: UCLASS_TEST_FDT + * 1: /some-bus/test + * 2: /some-bus/test0 + */ + +struct list_head uclass_head = { + .prev = &DM_UCLASS_REF(testfdt)->sibling_node, + .next = &DM_UCLASS_REF(i2c)->sibling_node, +}; + +DM_UCLASS_INST(i2c) = { + .uc_drv = DM_UCLASS_DRIVER_REF(i2c), + .sibling_node = { + .prev = &uclass_head, + .next = &DM_UCLASS_REF(misc)->sibling_node, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(i2c)->uclass_node, + .next = &DM_DEVICE_REF(i2c)->uclass_node, + }, +}; + +DM_UCLASS_INST(misc) = { + .uc_drv = DM_UCLASS_DRIVER_REF(misc), + .sibling_node = { + .prev = &DM_UCLASS_REF(i2c)->sibling_node, + .next = &DM_UCLASS_REF(root)->sibling_node, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(spl_test3)->uclass_node, + .next = &DM_DEVICE_REF(spl_test)->uclass_node, + }, +}; + +DM_UCLASS_INST(root) = { + .uc_drv = DM_UCLASS_DRIVER_REF(root), + .sibling_node = { + .prev = &DM_UCLASS_REF(misc)->sibling_node, + .next = &DM_UCLASS_REF(testbus)->sibling_node, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(root)->uclass_node, + .next = &DM_DEVICE_REF(root)->uclass_node, + }, +}; + +DM_UCLASS_INST(testbus) = { + .uc_drv = DM_UCLASS_DRIVER_REF(testbus), + .sibling_node = { + .prev = &DM_UCLASS_REF(root)->sibling_node, + .next = &DM_UCLASS_REF(testfdt)->sibling_node, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(some_bus)->uclass_node, + .next = &DM_DEVICE_REF(some_bus)->uclass_node, + }, +}; + +#include <dm/test.h> +u8 _testfdt_priv_[sizeof(struct dm_test_uc_priv)] + __attribute__ ((section (".priv_data"))); +DM_UCLASS_INST(testfdt) = { + .priv_ = _testfdt_priv_, + .uc_drv = DM_UCLASS_DRIVER_REF(testfdt), + .sibling_node = { + .prev = &DM_UCLASS_REF(testbus)->sibling_node, + .next = &uclass_head, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(test0)->uclass_node, + .next = &DM_DEVICE_REF(test)->uclass_node, + }, +}; + +''' + device_text = '''/* + * DO NOT MODIFY + * + * Declares the DM_DEVICE_INST() records. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ + +/* This file is not used: --instantiate was not enabled */ +''' + device_text_inst = '''/* + * DO NOT MODIFY + * + * Declares the DM_DEVICE_INST() records. + * This was generated by dtoc from a .dtb (device tree binary) file. + */ + +#include <common.h> +#include <dm.h> +#include <dt-structs.h> + +/* + * udevice declarations, ordered by 'struct udevice' linker_list position: + * + * idx udevice driver + * --- -------------------- -------------------- + * 0: i2c sandbox_i2c + * 1: root root_driver + * 2: some_bus denx_u_boot_test_bus + * 3: spl_test sandbox_spl_test + * 4: spl_test3 sandbox_spl_test + * 5: test denx_u_boot_fdt_test + * 6: test0 denx_u_boot_fdt_test + * --- -------------------- -------------------- + */ + +/* + * Node /i2c index 0 + * driver sandbox_i2c parent root_driver +*/ +static struct dtd_sandbox_i2c dtv_i2c = { +\t.intval\t\t\t= 0x3, +}; + +#include <asm/i2c.h> +u8 _sandbox_i2c_priv_i2c[sizeof(struct sandbox_i2c_priv)] +\t__attribute__ ((section (".priv_data"))); +#include <i2c.h> +u8 _sandbox_i2c_uc_priv_i2c[sizeof(struct dm_i2c_bus)] +\t__attribute__ ((section (".priv_data"))); + +DM_DEVICE_INST(i2c) = { +\t.driver\t\t= DM_DRIVER_REF(sandbox_i2c), +\t.name\t\t= "sandbox_i2c", +\t.plat_\t\t= &dtv_i2c, +\t.priv_\t\t= _sandbox_i2c_priv_i2c, +\t.uclass\t\t= DM_UCLASS_REF(i2c), +\t.uclass_priv_ = _sandbox_i2c_uc_priv_i2c, +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(i2c)->dev_head, +\t\t.next = &DM_UCLASS_REF(i2c)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(i2c)->child_head, +\t\t.next = &DM_DEVICE_REF(i2c)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(root)->child_head, +\t\t.next = &DM_DEVICE_REF(some_bus)->sibling_node, +\t}, +\t.seq_ = 4, +}; + +/* + * Node / index 1 + * driver root_driver parent None +*/ +static struct dtd_root_driver dtv_root = { +}; + +DM_DEVICE_INST(root) = { +\t.driver\t\t= DM_DRIVER_REF(root_driver), +\t.name\t\t= "root_driver", +\t.plat_\t\t= &dtv_root, +\t.uclass\t\t= DM_UCLASS_REF(root), +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(root)->dev_head, +\t\t.next = &DM_UCLASS_REF(root)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test3)->sibling_node, +\t\t.next = &DM_DEVICE_REF(i2c)->sibling_node, +\t}, +\t.seq_ = 0, +}; + +/* + * Node /some-bus index 2 + * driver denx_u_boot_test_bus parent root_driver +*/ + +#include <dm/test.h> +struct dm_test_pdata __attribute__ ((section (".priv_data"))) +\t_denx_u_boot_test_bus_plat_some_bus = { +\t.dtplat = { +\t\t.ping_add\t\t= 0x4, +\t\t.ping_expect\t\t= 0x4, +\t\t.reg\t\t\t= {0x3, 0x1}, +\t}, +}; +#include <dm/test.h> +u8 _denx_u_boot_test_bus_priv_some_bus[sizeof(struct dm_test_priv)] +\t__attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_test_bus_ucplat_some_bus[sizeof(struct dm_test_uclass_priv)] +\t__attribute__ ((section (".priv_data"))); +#include <test.h> + +DM_DEVICE_INST(some_bus) = { +\t.driver\t\t= DM_DRIVER_REF(denx_u_boot_test_bus), +\t.name\t\t= "denx_u_boot_test_bus", +\t.plat_\t\t= &_denx_u_boot_test_bus_plat_some_bus, +\t.uclass_plat_\t= _denx_u_boot_test_bus_ucplat_some_bus, +\t.driver_data\t= DM_TEST_TYPE_FIRST, +\t.priv_\t\t= _denx_u_boot_test_bus_priv_some_bus, +\t.uclass\t\t= DM_UCLASS_REF(testbus), +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(testbus)->dev_head, +\t\t.next = &DM_UCLASS_REF(testbus)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(test0)->sibling_node, +\t\t.next = &DM_DEVICE_REF(test)->sibling_node, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(i2c)->sibling_node, +\t\t.next = &DM_DEVICE_REF(spl_test)->sibling_node, +\t}, +\t.seq_ = 2, +}; + +/* + * Node /spl-test index 3 + * driver sandbox_spl_test parent root_driver +*/ +static struct dtd_sandbox_spl_test dtv_spl_test = { +\t.boolval\t\t= true, +\t.intval\t\t\t= 0x1, +}; + +DM_DEVICE_INST(spl_test) = { +\t.driver\t\t= DM_DRIVER_REF(sandbox_spl_test), +\t.name\t\t= "sandbox_spl_test", +\t.plat_\t\t= &dtv_spl_test, +\t.uclass\t\t= DM_UCLASS_REF(misc), +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(misc)->dev_head, +\t\t.next = &DM_DEVICE_REF(spl_test3)->uclass_node, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test)->child_head, +\t\t.next = &DM_DEVICE_REF(spl_test)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(some_bus)->sibling_node, +\t\t.next = &DM_DEVICE_REF(spl_test3)->sibling_node, +\t}, +\t.seq_ = 0, +}; + +/* + * Node /spl-test3 index 4 + * driver sandbox_spl_test parent root_driver +*/ +static struct dtd_sandbox_spl_test dtv_spl_test3 = { +\t.longbytearray\t\t= {0x90a0b0c, 0xd0e0f10}, +\t.stringarray\t\t= "one", +}; + +DM_DEVICE_INST(spl_test3) = { +\t.driver\t\t= DM_DRIVER_REF(sandbox_spl_test), +\t.name\t\t= "sandbox_spl_test", +\t.plat_\t\t= &dtv_spl_test3, +\t.uclass\t\t= DM_UCLASS_REF(misc), +\t.uclass_node\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test)->uclass_node, +\t\t.next = &DM_UCLASS_REF(misc)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test3)->child_head, +\t\t.next = &DM_DEVICE_REF(spl_test3)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(spl_test)->sibling_node, +\t\t.next = &DM_DEVICE_REF(root)->child_head, +\t}, +\t.seq_ = 1, +}; + +/* + * Node /some-bus/test index 5 + * driver denx_u_boot_fdt_test parent denx_u_boot_test_bus +*/ + +#include <dm/test.h> +struct dm_test_pdata __attribute__ ((section (".priv_data"))) +\t_denx_u_boot_fdt_test_plat_test = { +\t.dtplat = { +\t\t.ping_add\t\t= 0x5, +\t\t.ping_expect\t\t= 0x5, +\t\t.reg\t\t\t= {0x5, 0x0}, +\t}, +}; +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_priv_test[sizeof(struct dm_test_priv)] +\t__attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_parent_plat_test[sizeof(struct dm_test_parent_plat)] +\t__attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_parent_priv_test[sizeof(struct dm_test_parent_data)] +\t__attribute__ ((section (".priv_data"))); + +DM_DEVICE_INST(test) = { +\t.driver\t\t= DM_DRIVER_REF(denx_u_boot_fdt_test), +\t.name\t\t= "denx_u_boot_fdt_test", +\t.plat_\t\t= &_denx_u_boot_fdt_test_plat_test, +\t.parent_plat_\t= _denx_u_boot_fdt_test_parent_plat_test, +\t.driver_data\t= DM_TEST_TYPE_FIRST, +\t.parent\t\t= DM_DEVICE_REF(some_bus), +\t.priv_\t\t= _denx_u_boot_fdt_test_priv_test, +\t.uclass\t\t= DM_UCLASS_REF(testfdt), +\t.parent_priv_\t= _denx_u_boot_fdt_test_parent_priv_test, +\t.uclass_node\t= { +\t\t.prev = &DM_UCLASS_REF(testfdt)->dev_head, +\t\t.next = &DM_DEVICE_REF(test0)->uclass_node, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(test)->child_head, +\t\t.next = &DM_DEVICE_REF(test)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(some_bus)->child_head, +\t\t.next = &DM_DEVICE_REF(test0)->sibling_node, +\t}, +\t.seq_ = 1, +}; + +/* + * Node /some-bus/test0 index 6 + * driver denx_u_boot_fdt_test parent denx_u_boot_test_bus +*/ + +#include <dm/test.h> +struct dm_test_pdata __attribute__ ((section (".priv_data"))) +\t_denx_u_boot_fdt_test_plat_test0 = { +\t.dtplat = { +\t}, +}; +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_priv_test0[sizeof(struct dm_test_priv)] +\t__attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_parent_plat_test0[sizeof(struct dm_test_parent_plat)] +\t__attribute__ ((section (".priv_data"))); +#include <dm/test.h> +u8 _denx_u_boot_fdt_test_parent_priv_test0[sizeof(struct dm_test_parent_data)] +\t__attribute__ ((section (".priv_data"))); + +DM_DEVICE_INST(test0) = { +\t.driver\t\t= DM_DRIVER_REF(denx_u_boot_fdt_test), +\t.name\t\t= "denx_u_boot_fdt_test", +\t.plat_\t\t= &_denx_u_boot_fdt_test_plat_test0, +\t.parent_plat_\t= _denx_u_boot_fdt_test_parent_plat_test0, +\t.driver_data\t= DM_TEST_TYPE_SECOND, +\t.parent\t\t= DM_DEVICE_REF(some_bus), +\t.priv_\t\t= _denx_u_boot_fdt_test_priv_test0, +\t.uclass\t\t= DM_UCLASS_REF(testfdt), +\t.parent_priv_\t= _denx_u_boot_fdt_test_parent_priv_test0, +\t.uclass_node\t= { +\t\t.prev = &DM_DEVICE_REF(test)->uclass_node, +\t\t.next = &DM_UCLASS_REF(testfdt)->dev_head, +\t}, +\t.child_head\t= { +\t\t.prev = &DM_DEVICE_REF(test0)->child_head, +\t\t.next = &DM_DEVICE_REF(test0)->child_head, +\t}, +\t.sibling_node\t= { +\t\t.prev = &DM_DEVICE_REF(test)->sibling_node, +\t\t.next = &DM_DEVICE_REF(some_bus)->child_head, +\t}, +\t.seq_ = 2, +}; + +''' def test_simple(self): """Test output from some simple nodes with various types of data""" @@ -299,10 +823,18 @@ U_BOOT_DRVINFO(spl_test3) = { self._check_strings(self.platdata_text, data) + self.run_test(['decl'], dtb_file, output) + with open(output) as infile: + data = infile.read() + + self._check_strings(self.decl_text, data) + # Try the 'all' command self.run_test(['all'], dtb_file, output) data = tools.ReadFile(output, binary=False) - self._check_strings(self.platdata_text + self.struct_text, data) + self._check_strings( + self.decl_text + self.device_text + self.platdata_text + + self.struct_text + self.uclass_text, data) def test_driver_alias(self): """Test output from a device tree file with a driver alias""" @@ -323,7 +855,19 @@ struct dtd_sandbox_gpio { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' -/* Node /gpios@0 index 0 */ +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: gpios_at_0 sandbox_gpio + * --- -------------------- -------------------- + */ + +/* + * Node /gpios@0 index 0 + * driver sandbox_gpio parent None + */ static struct dtd_sandbox_gpio dtv_gpios_at_0 = { \t.gpio_bank_name\t\t= "a", \t.gpio_controller\t= true, @@ -331,7 +875,7 @@ static struct dtd_sandbox_gpio dtv_gpios_at_0 = { }; U_BOOT_DRVINFO(gpios_at_0) = { \t.name\t\t= "sandbox_gpio", -\t.plat\t= &dtv_gpios_at_0, +\t.plat\t\t= &dtv_gpios_at_0, \t.plat_size\t= sizeof(dtv_gpios_at_0), \t.parent_idx\t= -1, }; @@ -343,7 +887,9 @@ U_BOOT_DRVINFO(gpios_at_0) = { dtb_file = get_dtb_file('dtoc_test_invalid_driver.dts') output = tools.GetOutputFilename('output') with test_util.capture_sys_output() as _: - dtb_platdata.run_steps(['struct'], dtb_file, False, output, []) + dtb_platdata.run_steps( + ['struct'], dtb_file, False, output, [], None, False, + scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(HEADER + ''' @@ -352,16 +898,27 @@ struct dtd_invalid { ''', data) with test_util.capture_sys_output() as _: - dtb_platdata.run_steps(['platdata'], dtb_file, False, output, []) + dtb_platdata.run_steps( + ['platdata'], dtb_file, False, output, [], None, False, + scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: spl_test invalid + * --- -------------------- -------------------- + */ + /* Node /spl-test index 0 */ static struct dtd_invalid dtv_spl_test = { }; U_BOOT_DRVINFO(spl_test) = { \t.name\t\t= "invalid", -\t.plat\t= &dtv_spl_test, +\t.plat\t\t= &dtv_spl_test, \t.plat_size\t= sizeof(dtv_spl_test), \t.parent_idx\t= -1, }; @@ -388,13 +945,26 @@ struct dtd_target { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: phandle2_target target + * 1: phandle3_target target + * 2: phandle_source source + * 3: phandle_source2 source + * 4: phandle_target target + * --- -------------------- -------------------- + */ + /* Node /phandle2-target index 0 */ static struct dtd_target dtv_phandle2_target = { \t.intval\t\t\t= 0x1, }; U_BOOT_DRVINFO(phandle2_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle2_target, +\t.plat\t\t= &dtv_phandle2_target, \t.plat_size\t= sizeof(dtv_phandle2_target), \t.parent_idx\t= -1, }; @@ -405,7 +975,7 @@ static struct dtd_target dtv_phandle3_target = { }; U_BOOT_DRVINFO(phandle3_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle3_target, +\t.plat\t\t= &dtv_phandle3_target, \t.plat_size\t= sizeof(dtv_phandle3_target), \t.parent_idx\t= -1, }; @@ -420,7 +990,7 @@ static struct dtd_source dtv_phandle_source = { }; U_BOOT_DRVINFO(phandle_source) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source, +\t.plat\t\t= &dtv_phandle_source, \t.plat_size\t= sizeof(dtv_phandle_source), \t.parent_idx\t= -1, }; @@ -432,7 +1002,7 @@ static struct dtd_source dtv_phandle_source2 = { }; U_BOOT_DRVINFO(phandle_source2) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source2, +\t.plat\t\t= &dtv_phandle_source2, \t.plat_size\t= sizeof(dtv_phandle_source2), \t.parent_idx\t= -1, }; @@ -443,7 +1013,7 @@ static struct dtd_target dtv_phandle_target = { }; U_BOOT_DRVINFO(phandle_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle_target, +\t.plat\t\t= &dtv_phandle_target, \t.plat_size\t= sizeof(dtv_phandle_target), \t.parent_idx\t= -1, }; @@ -474,6 +1044,16 @@ struct dtd_target { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: phandle_source2 source + * 1: phandle_target target + * --- -------------------- -------------------- + */ + /* Node /phandle-source2 index 0 */ static struct dtd_source dtv_phandle_source2 = { \t.clocks\t\t\t= { @@ -481,7 +1061,7 @@ static struct dtd_source dtv_phandle_source2 = { }; U_BOOT_DRVINFO(phandle_source2) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source2, +\t.plat\t\t= &dtv_phandle_source2, \t.plat_size\t= sizeof(dtv_phandle_source2), \t.parent_idx\t= -1, }; @@ -491,7 +1071,7 @@ static struct dtd_target dtv_phandle_target = { }; U_BOOT_DRVINFO(phandle_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle_target, +\t.plat\t\t= &dtv_phandle_target, \t.plat_size\t= sizeof(dtv_phandle_target), \t.parent_idx\t= -1, }; @@ -502,17 +1082,32 @@ U_BOOT_DRVINFO(phandle_target) = { """Test that phandle targets are generated when unsing cd-gpios""" dtb_file = get_dtb_file('dtoc_test_phandle_cd_gpios.dts') output = tools.GetOutputFilename('output') - dtb_platdata.run_steps(['platdata'], dtb_file, False, output, [], True) + dtb_platdata.run_steps( + ['platdata'], dtb_file, False, output, [], None, False, + warning_disabled=True, scan=copy_scan()) with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: phandle2_target target + * 1: phandle3_target target + * 2: phandle_source source + * 3: phandle_source2 source + * 4: phandle_target target + * --- -------------------- -------------------- + */ + /* Node /phandle2-target index 0 */ static struct dtd_target dtv_phandle2_target = { \t.intval\t\t\t= 0x1, }; U_BOOT_DRVINFO(phandle2_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle2_target, +\t.plat\t\t= &dtv_phandle2_target, \t.plat_size\t= sizeof(dtv_phandle2_target), \t.parent_idx\t= -1, }; @@ -523,7 +1118,7 @@ static struct dtd_target dtv_phandle3_target = { }; U_BOOT_DRVINFO(phandle3_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle3_target, +\t.plat\t\t= &dtv_phandle3_target, \t.plat_size\t= sizeof(dtv_phandle3_target), \t.parent_idx\t= -1, }; @@ -538,7 +1133,7 @@ static struct dtd_source dtv_phandle_source = { }; U_BOOT_DRVINFO(phandle_source) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source, +\t.plat\t\t= &dtv_phandle_source, \t.plat_size\t= sizeof(dtv_phandle_source), \t.parent_idx\t= -1, }; @@ -550,7 +1145,7 @@ static struct dtd_source dtv_phandle_source2 = { }; U_BOOT_DRVINFO(phandle_source2) = { \t.name\t\t= "source", -\t.plat\t= &dtv_phandle_source2, +\t.plat\t\t= &dtv_phandle_source2, \t.plat_size\t= sizeof(dtv_phandle_source2), \t.parent_idx\t= -1, }; @@ -561,7 +1156,7 @@ static struct dtd_target dtv_phandle_target = { }; U_BOOT_DRVINFO(phandle_target) = { \t.name\t\t= "target", -\t.plat\t= &dtv_phandle_target, +\t.plat\t\t= &dtv_phandle_target, \t.plat_size\t= sizeof(dtv_phandle_target), \t.parent_idx\t= -1, }; @@ -611,13 +1206,24 @@ struct dtd_test3 { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: test1 test1 + * 1: test2 test2 + * 2: test3 test3 + * --- -------------------- -------------------- + */ + /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x5678}, }; U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", -\t.plat\t= &dtv_test1, +\t.plat\t\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), \t.parent_idx\t= -1, }; @@ -628,7 +1234,7 @@ static struct dtd_test2 dtv_test2 = { }; U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", -\t.plat\t= &dtv_test2, +\t.plat\t\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), \t.parent_idx\t= -1, }; @@ -639,7 +1245,7 @@ static struct dtd_test3 dtv_test3 = { }; U_BOOT_DRVINFO(test3) = { \t.name\t\t= "test3", -\t.plat\t= &dtv_test3, +\t.plat\t\t= &dtv_test3, \t.plat_size\t= sizeof(dtv_test3), \t.parent_idx\t= -1, }; @@ -666,13 +1272,23 @@ struct dtd_test2 { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: test1 test1 + * 1: test2 test2 + * --- -------------------- -------------------- + */ + /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x5678}, }; U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", -\t.plat\t= &dtv_test1, +\t.plat\t\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), \t.parent_idx\t= -1, }; @@ -683,7 +1299,7 @@ static struct dtd_test2 dtv_test2 = { }; U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", -\t.plat\t= &dtv_test2, +\t.plat\t\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), \t.parent_idx\t= -1, }; @@ -713,13 +1329,24 @@ struct dtd_test3 { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: test1 test1 + * 1: test2 test2 + * 2: test3 test3 + * --- -------------------- -------------------- + */ + /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x123400000000, 0x5678}, }; U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", -\t.plat\t= &dtv_test1, +\t.plat\t\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), \t.parent_idx\t= -1, }; @@ -730,7 +1357,7 @@ static struct dtd_test2 dtv_test2 = { }; U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", -\t.plat\t= &dtv_test2, +\t.plat\t\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), \t.parent_idx\t= -1, }; @@ -741,7 +1368,7 @@ static struct dtd_test3 dtv_test3 = { }; U_BOOT_DRVINFO(test3) = { \t.name\t\t= "test3", -\t.plat\t= &dtv_test3, +\t.plat\t\t= &dtv_test3, \t.plat_size\t= sizeof(dtv_test3), \t.parent_idx\t= -1, }; @@ -771,13 +1398,24 @@ struct dtd_test3 { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: test1 test1 + * 1: test2 test2 + * 2: test3 test3 + * --- -------------------- -------------------- + */ + /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x567800000000}, }; U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", -\t.plat\t= &dtv_test1, +\t.plat\t\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), \t.parent_idx\t= -1, }; @@ -788,7 +1426,7 @@ static struct dtd_test2 dtv_test2 = { }; U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", -\t.plat\t= &dtv_test2, +\t.plat\t\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), \t.parent_idx\t= -1, }; @@ -799,7 +1437,7 @@ static struct dtd_test3 dtv_test3 = { }; U_BOOT_DRVINFO(test3) = { \t.name\t\t= "test3", -\t.plat\t= &dtv_test3, +\t.plat\t\t= &dtv_test3, \t.plat_size\t= sizeof(dtv_test3), \t.parent_idx\t= -1, }; @@ -845,24 +1483,40 @@ struct dtd_sandbox_spl_test { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' -/* Node /spl-test index 0 */ +/* + * driver_info declarations, ordered by 'struct driver_info' linker_list idx: + * + * idx driver_info driver + * --- -------------------- -------------------- + * 0: spl_test sandbox_spl_test + * 1: spl_test2 sandbox_spl_test + * --- -------------------- -------------------- + */ + +/* + * Node /spl-test index 0 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test = { \t.intval\t\t\t= 0x1, }; U_BOOT_DRVINFO(spl_test) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test, +\t.plat\t\t= &dtv_spl_test, \t.plat_size\t= sizeof(dtv_spl_test), \t.parent_idx\t= -1, }; -/* Node /spl-test2 index 1 */ +/* + * Node /spl-test2 index 1 + * driver sandbox_spl_test parent None + */ static struct dtd_sandbox_spl_test dtv_spl_test2 = { \t.intarray\t\t= 0x5, }; U_BOOT_DRVINFO(spl_test2) = { \t.name\t\t= "sandbox_spl_test", -\t.plat\t= &dtv_spl_test2, +\t.plat\t\t= &dtv_spl_test2, \t.plat_size\t= sizeof(dtv_spl_test2), \t.parent_idx\t= -1, }; @@ -882,7 +1536,9 @@ U_BOOT_DRVINFO(spl_test2) = { output = tools.GetOutputFilename('output') self.run_test(['all'], dtb_file, output) data = tools.ReadFile(output, binary=False) - self._check_strings(self.platdata_text + self.struct_text, data) + self._check_strings( + self.decl_text + self.device_text + self.platdata_text + + self.struct_text + self.uclass_text, data) def test_no_command(self): """Test running dtoc without a command""" @@ -897,13 +1553,16 @@ U_BOOT_DRVINFO(spl_test2) = { output = tools.GetOutputFilename('output') with self.assertRaises(ValueError) as exc: self.run_test(['invalid-cmd'], dtb_file, output) - self.assertIn("Unknown command 'invalid-cmd': (use: platdata, struct)", - str(exc.exception)) + self.assertIn( + "Unknown command 'invalid-cmd': (use: decl, device, platdata, struct, uclass)", + str(exc.exception)) def test_output_conflict(self): """Test a conflict between and output dirs and output file""" with self.assertRaises(ValueError) as exc: - dtb_platdata.run_steps(['all'], None, False, 'out', ['cdir'], True) + dtb_platdata.run_steps( + ['all'], None, False, 'out', ['cdir'], None, False, + warning_disabled=True, scan=copy_scan()) self.assertIn("Must specify either output or output_dirs, not both", str(exc.exception)) @@ -919,11 +1578,249 @@ U_BOOT_DRVINFO(spl_test2) = { fnames = glob.glob(outdir + '/*') self.assertEqual(2, len(fnames)) - dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], True) + dtb_platdata.run_steps( + ['all'], dtb_file, False, None, [outdir], None, False, + warning_disabled=True, scan=copy_scan()) fnames = glob.glob(outdir + '/*') - self.assertEqual(4, len(fnames)) + self.assertEqual(7, len(fnames)) leafs = set(os.path.basename(fname) for fname in fnames) self.assertEqual( - {'dt-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb'}, + {'dt-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb', + 'dt-uclass.c', 'dt-decl.h', 'dt-device.c'}, leafs) + + def setup_process_test(self): + """Set up a test of process_nodes() + + This uses saved_scan but returns a deep copy of it, so it is safe to + modify it in these tests + + Returns: + tuple: + DtbPlatdata: object to test + Scanner: scanner to use + """ + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + + # Take a copy before messing with it + scan = copy_scan() + plat = dtb_platdata.DtbPlatdata(scan, dtb_file, False) + plat.scan_dtb() + plat.scan_tree(False) + plat.prepare_nodes() + return plat, scan + + def test_process_nodes(self): + """Test processing nodes to add various info""" + plat, scan = self.setup_process_test() + plat.process_nodes(True) + + i2c_node = plat._fdt.GetNode('/i2c@0') + pmic_node = plat._fdt.GetNode('/i2c@0/pmic@9') + pmic = scan._drivers['sandbox_pmic'] + i2c = scan._drivers['sandbox_i2c'] + self.assertEqual('DM_DEVICE_REF(pmic_at_9)', pmic_node.dev_ref) + self.assertEqual(pmic, pmic_node.driver) + self.assertEqual(i2c_node, pmic_node.parent) + self.assertEqual(i2c, pmic_node.parent_driver) + + # The pmic is the only child + self.assertEqual(pmic_node.parent_seq, 0) + self.assertEqual([pmic_node], i2c_node.child_devs) + + # Start and end of the list should be the child_head + ref = '&DM_DEVICE_REF(i2c_at_0)->child_head' + self.assertEqual( + {-1: ref, 0: '&DM_DEVICE_REF(pmic_at_9)->sibling_node', 1: ref}, + i2c_node.child_refs) + + def test_process_nodes_bad_parent(self): + # Pretend that i2c has a parent (the pmic) and delete that driver + plat, scan = self.setup_process_test() + + i2c_node = plat._fdt.GetNode('/i2c@0') + pmic_node = plat._fdt.GetNode('/i2c@0/pmic@9') + del scan._drivers['sandbox_pmic'] + i2c_node.parent = pmic_node + + # Process twice, the second time to generate an exception + plat.process_nodes(False) + with self.assertRaises(ValueError) as exc: + plat.process_nodes(True) + self.assertIn( + "Cannot parse/find parent driver 'sandbox_pmic' for 'sandbox_i2c", + str(exc.exception)) + + def test_process_nodes_bad_node(self): + plat, scan = self.setup_process_test() + + # Now remove the pmic driver + del scan._drivers['sandbox_pmic'] + + # Process twice, the second time to generate an exception + plat.process_nodes(False) + with self.assertRaises(ValueError) as exc: + plat.process_nodes(True) + self.assertIn("Cannot parse/find driver for 'sandbox_pmic", + str(exc.exception)) + + def test_process_nodes_bad_uclass(self): + plat, scan = self.setup_process_test() + + self.assertIn('UCLASS_I2C', scan._uclass) + del scan._uclass['UCLASS_I2C'] + with self.assertRaises(ValueError) as exc: + plat.process_nodes(True) + self.assertIn("Cannot parse/find uclass 'UCLASS_I2C' for driver 'sandbox_i2c'", + str(exc.exception)) + + def test_process_nodes_used(self): + """Test processing nodes to add various info""" + plat, scan = self.setup_process_test() + plat.process_nodes(True) + + pmic = scan._drivers['sandbox_pmic'] + self.assertTrue(pmic.used) + + gpio = scan._drivers['sandbox_gpio'] + self.assertFalse(gpio.used) + + def test_alias_read(self): + """Test obtaining aliases""" + dtb_file = get_dtb_file('dtoc_test_inst.dts') + output = tools.GetOutputFilename('output') + plat = self.run_test(['struct'], dtb_file, output) + + scan = plat._scan + testfdt_node = plat._fdt.GetNode('/some-bus/test') + test0_node = plat._fdt.GetNode('/some-bus/test0') + self.assertIn('UCLASS_TEST_FDT', scan._uclass) + uc = scan._uclass['UCLASS_TEST_FDT'] + self.assertEqual({1: testfdt_node, 2: test0_node}, + uc.alias_num_to_node) + self.assertEqual({'/some-bus/test': 1, '/some-bus/test0': 2}, + uc.alias_path_to_num) + + # Try adding an alias that doesn't exist + self.assertFalse(scan.add_uclass_alias('fred', 3, None)) + + # Try adding an alias for a missing node + self.assertIsNone(scan.add_uclass_alias('testfdt', 3, None)) + + def test_alias_read_bad(self): + """Test invalid alias property name""" + dtb_file = get_dtb_file('dtoc_test_alias_bad.dts') + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as exc: + plat = self.run_test(['struct'], dtb_file, output) + self.assertIn("Cannot decode alias 'i2c4-'", str(exc.exception)) + + def test_alias_read_bad_path(self): + """Test alias pointing to a non-existent node""" + # This line may produce a warning, so capture it: + # Warning (alias_paths): /aliases:i2c4: aliases property is not a valid + # node (/does/not/exist) + dtb_file = get_dtb_file('dtoc_test_alias_bad_path.dts', True) + + output = tools.GetOutputFilename('output') + with self.assertRaises(ValueError) as exc: + plat = self.run_test(['struct'], dtb_file, output) + self.assertIn("Alias 'i2c4' path '/does/not/exist' not found", + str(exc.exception)) + + def test_alias_read_bad_uclass(self): + """Test alias for a uclass that doesn't exist""" + dtb_file = get_dtb_file('dtoc_test_alias_bad_uc.dts') + output = tools.GetOutputFilename('output') + with test_util.capture_sys_output() as (stdout, _): + plat = self.run_test(['struct'], dtb_file, output) + self.assertEqual("Could not find uclass for alias 'other1'", + stdout.getvalue().strip()) + + def test_sequence(self): + """Test assignment of sequence numnbers""" + dtb_file = get_dtb_file('dtoc_test_inst.dts') + output = tools.GetOutputFilename('output') + plat = self.run_test(['struct'], dtb_file, output) + + scan = plat._scan + testfdt = plat._fdt.GetNode('/some-bus/test') + self.assertEqual(1, testfdt.seq) + i2c = plat._fdt.GetNode('/i2c') + + # For now this uclass is not compiled in, so no sequence is assigned + self.assertEqual(4, i2c.seq) + spl = plat._fdt.GetNode('/spl-test') + self.assertEqual(0, spl.seq) + + def test_process_root(self): + """Test assignment of sequence numnbers""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + + # Take a copy before messing with it + scan = copy_scan() + plat = dtb_platdata.DtbPlatdata(scan, dtb_file, False) + plat.scan_dtb() + root = plat._fdt.GetRoot() + + plat.scan_tree(False) + self.assertNotIn(root, plat._valid_nodes) + + plat.scan_tree(True) + self.assertIn(root, plat._valid_nodes) + self.assertEqual('root_driver', + scan.get_normalized_compat_name(root)[0]) + + def test_simple_inst(self): + """Test output from some simple nodes with instantiate enabled""" + dtb_file = get_dtb_file('dtoc_test_inst.dts') + output = tools.GetOutputFilename('output') + + self.run_test(['decl'], dtb_file, output, True) + with open(output) as infile: + data = infile.read() + + self._check_strings(self.decl_text_inst, data) + + self.run_test(['platdata'], dtb_file, output, True) + with open(output) as infile: + data = infile.read() + + self._check_strings(C_HEADER_PRE + ''' +/* This file is not used: --instantiate was enabled */ +''', data) + + self.run_test(['uclass'], dtb_file, output, True) + with open(output) as infile: + data = infile.read() + + self._check_strings(UCLASS_HEADER_COMMON + self.uclass_text_inst, data) + + self.run_test(['device'], dtb_file, output, True) + with open(output) as infile: + data = infile.read() + + self._check_strings(self.device_text_inst, data) + + def test_inst_no_hdr(self): + """Test dealing with a struct tsssshat has no header""" + dtb_file = get_dtb_file('dtoc_test_inst.dts') + output = tools.GetOutputFilename('output') + + # Run it once to set everything up + plat = self.run_test(['decl'], dtb_file, output, True) + scan = plat._scan + + # Restart the output file and delete any record of the uclass' struct + plat.setup_output(Ftype.SOURCE, output) + del scan._structs['dm_test_uc_priv'] + + # Now generate the uclasses, which should provide a warning + with test_util.capture_sys_output() as (stdout, _): + plat.generate_uclasses() + self.assertEqual( + 'Warning: Cannot find header file for struct dm_test_uc_priv', + stdout.getvalue().strip()) diff --git a/tools/dtoc/test_fdt.py b/tools/dtoc/test_fdt.py index e8fbbd5..1c3a8a2 100755 --- a/tools/dtoc/test_fdt.py +++ b/tools/dtoc/test_fdt.py @@ -48,6 +48,17 @@ def _GetPropertyValue(dtb, node, prop_name): data = dtb.GetContents()[offset:offset + len(prop.value)] return prop, [chr(x) for x in data] +def find_dtb_file(dts_fname): + """Locate a test file in the test/ directory + + Args: + dts_fname (str): Filename to find, e.g. 'dtoc_test_simple.dts] + + Returns: + str: Path to the test filename + """ + return os.path.join('tools/dtoc/test', dts_fname) + class TestFdt(unittest.TestCase): """Tests for the Fdt module @@ -64,7 +75,7 @@ class TestFdt(unittest.TestCase): tools.FinaliseOutputDir() def setUp(self): - self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) def testFdt(self): """Test that we can open an Fdt""" @@ -141,7 +152,7 @@ class TestNode(unittest.TestCase): tools.FinaliseOutputDir() def setUp(self): - self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) self.node = self.dtb.GetNode('/spl-test') def testOffset(self): @@ -203,7 +214,7 @@ class TestNode(unittest.TestCase): def testLookupPhandle(self): """Test looking up a single phandle""" - dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts') + dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts')) node = dtb.GetNode('/phandle-source2') prop = node.props['clocks'] target = dtb.GetNode('/phandle-target') @@ -222,7 +233,7 @@ class TestProp(unittest.TestCase): tools.FinaliseOutputDir() def setUp(self): - self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) self.node = self.dtb.GetNode('/spl-test') self.fdt = self.dtb.GetFdtObj() @@ -230,7 +241,7 @@ class TestProp(unittest.TestCase): self.assertEqual(None, self.dtb.GetNode('missing')) def testPhandle(self): - dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts') + dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts')) node = dtb.GetNode('/phandle-source2') prop = node.props['clocks'] self.assertTrue(fdt32_to_cpu(prop.value) > 0) @@ -488,7 +499,7 @@ class TestFdtUtil(unittest.TestCase): tools.FinaliseOutputDir() def setUp(self): - self.dtb = fdt.FdtScan('tools/dtoc/dtoc_test_simple.dts') + self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts')) self.node = self.dtb.GetNode('/spl-test') def testGetInt(self): @@ -531,7 +542,7 @@ class TestFdtUtil(unittest.TestCase): str(e.exception)) def testGetPhandleList(self): - dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts') + dtb = fdt.FdtScan(find_dtb_file('dtoc_test_phandle.dts')) node = dtb.GetNode('/phandle-source2') self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks')) node = dtb.GetNode('/phandle-source') @@ -551,7 +562,7 @@ class TestFdtUtil(unittest.TestCase): self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0)) self.assertEqual(2, fdt_util.fdt_cells_to_cpu(val, 1)) - dtb2 = fdt.FdtScan('tools/dtoc/dtoc_test_addr64.dts') + dtb2 = fdt.FdtScan(find_dtb_file('dtoc_test_addr64.dts')) node1 = dtb2.GetNode('/test1') val = node1.props['reg'].value self.assertEqual(0x1234, fdt_util.fdt_cells_to_cpu(val, 2)) @@ -565,7 +576,7 @@ class TestFdtUtil(unittest.TestCase): def testEnsureCompiled(self): """Test a degenerate case of this function (file already compiled)""" - dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts') + dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts')) self.assertEqual(dtb, fdt_util.EnsureCompiled(dtb)) def testEnsureCompiledTmpdir(self): @@ -574,7 +585,7 @@ class TestFdtUtil(unittest.TestCase): old_outdir = tools.outdir tools.outdir= None tmpdir = tempfile.mkdtemp(prefix='test_fdt.') - dtb = fdt_util.EnsureCompiled('tools/dtoc/dtoc_test_simple.dts', + dtb = fdt_util.EnsureCompiled(find_dtb_file('dtoc_test_simple.dts'), tmpdir) self.assertEqual(tmpdir, os.path.dirname(dtb)) shutil.rmtree(tmpdir) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index 7d68653..0af86dc 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -7,6 +7,7 @@ This includes unit tests for scanning of the source code """ +import copy import os import shutil import tempfile @@ -17,6 +18,20 @@ from dtoc import src_scan from patman import test_util from patman import tools +OUR_PATH = os.path.dirname(os.path.realpath(__file__)) + +class FakeNode: + """Fake Node object for testing""" + def __init__(self): + self.name = None + self.props = {} + +class FakeProp: + """Fake Prop object for testing""" + def __init__(self): + self.name = None + self.value = None + # This is a test so is allowed to access private things in the module it is # testing # pylint: disable=W0212 @@ -44,7 +59,8 @@ class TestSrcScan(unittest.TestCase): def test_additional(self): """Test with additional drivers to scan""" scan = src_scan.Scanner( - None, True, [None, '', 'tools/dtoc/dtoc_test_scan_drivers.cxx']) + None, True, + [None, '', 'tools/dtoc/test/dtoc_test_scan_drivers.cxx']) scan.scan_drivers() self.assertIn('sandbox_gpio_alias2', scan._driver_aliases) self.assertEqual('sandbox_gpio', @@ -69,10 +85,22 @@ class TestSrcScan(unittest.TestCase): def test_driver(self): """Test the Driver class""" - drv1 = src_scan.Driver('fred') - drv2 = src_scan.Driver('mary') - drv3 = src_scan.Driver('fred') - self.assertEqual("Driver(name='fred')", str(drv1)) + i2c = 'I2C_UCLASS' + compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', + 'rockchip,rk3288-srf': None} + drv1 = src_scan.Driver('fred', 'fred.c') + drv2 = src_scan.Driver('mary', 'mary.c') + drv3 = src_scan.Driver('fred', 'fred.c') + drv1.uclass_id = i2c + drv1.compat = compat + drv2.uclass_id = i2c + drv2.compat = compat + drv3.uclass_id = i2c + drv3.compat = compat + self.assertEqual( + "Driver(name='fred', used=False, uclass_id='I2C_UCLASS', " + "compat={'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', " + "'rockchip,rk3288-srf': None}, priv=)", str(drv1)) self.assertEqual(drv1, drv3) self.assertNotEqual(drv1, drv2) self.assertNotEqual(drv2, drv3) @@ -91,7 +119,9 @@ class TestSrcScan(unittest.TestCase): fname_list = [] add_file('fname.c') + add_file('.git/ignoreme.c') add_file('dir/fname2.c') + add_file('build-sandbox/ignoreme2.c') # Mock out scan_driver and check that it is called with the # expected files @@ -101,7 +131,362 @@ class TestSrcScan(unittest.TestCase): self.assertEqual(2, len(mocked.mock_calls)) self.assertEqual(mock.call(fname_list[0]), mocked.mock_calls[0]) - self.assertEqual(mock.call(fname_list[1]), + # .git file should be ignored + self.assertEqual(mock.call(fname_list[2]), mocked.mock_calls[1]) finally: shutil.rmtree(indir) + + def test_scan(self): + """Test scanning of a driver""" + fname = os.path.join(OUR_PATH, '..', '..', 'drivers/i2c/tegra_i2c.c') + buff = tools.ReadFile(fname, False) + scan = src_scan.Scanner(None, False, None) + scan._parse_driver(fname, buff) + self.assertIn('i2c_tegra', scan._drivers) + drv = scan._drivers['i2c_tegra'] + self.assertEqual('i2c_tegra', drv.name) + self.assertEqual('UCLASS_I2C', drv.uclass_id) + self.assertEqual( + {'nvidia,tegra114-i2c': 'TYPE_114', + 'nvidia,tegra20-i2c': 'TYPE_STD', + 'nvidia,tegra20-i2c-dvc': 'TYPE_DVC'}, drv.compat) + self.assertEqual('i2c_bus', drv.priv) + self.assertEqual(1, len(scan._drivers)) + + def test_normalized_name(self): + """Test operation of get_normalized_compat_name()""" + prop = FakeProp() + prop.name = 'compatible' + prop.value = 'rockchip,rk3288-grf' + node = FakeNode() + node.props = {'compatible': prop} + + # get_normalized_compat_name() uses this to check for root node + node.parent = FakeNode() + + scan = src_scan.Scanner(None, False, None) + with test_util.capture_sys_output() as (stdout, _): + name, aliases = scan.get_normalized_compat_name(node) + self.assertEqual('rockchip_rk3288_grf', name) + self.assertEqual([], aliases) + self.assertEqual( + 'WARNING: the driver rockchip_rk3288_grf was not found in the driver list', + stdout.getvalue().strip()) + + i2c = 'I2C_UCLASS' + compat = {'rockchip,rk3288-grf': 'ROCKCHIP_SYSCON_GRF', + 'rockchip,rk3288-srf': None} + drv = src_scan.Driver('fred', 'fred.c') + drv.uclass_id = i2c + drv.compat = compat + scan._drivers['rockchip_rk3288_grf'] = drv + + scan._driver_aliases['rockchip_rk3288_srf'] = 'rockchip_rk3288_grf' + + with test_util.capture_sys_output() as (stdout, _): + name, aliases = scan.get_normalized_compat_name(node) + self.assertEqual('', stdout.getvalue().strip()) + self.assertEqual('rockchip_rk3288_grf', name) + self.assertEqual([], aliases) + + prop.value = 'rockchip,rk3288-srf' + with test_util.capture_sys_output() as (stdout, _): + name, aliases = scan.get_normalized_compat_name(node) + self.assertEqual('', stdout.getvalue().strip()) + self.assertEqual('rockchip_rk3288_grf', name) + self.assertEqual(['rockchip_rk3288_srf'], aliases) + + def test_scan_errors(self): + """Test detection of scanning errors""" + buff = ''' +static const struct udevice_id tegra_i2c_ids2[] = { + { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, + { } +}; + +U_BOOT_DRIVER(i2c_tegra) = { + .name = "i2c_tegra", + .id = UCLASS_I2C, + .of_match = tegra_i2c_ids, +}; +''' + scan = src_scan.Scanner(None, False, None) + with self.assertRaises(ValueError) as exc: + scan._parse_driver('file.c', buff) + self.assertIn( + "file.c: Unknown compatible var 'tegra_i2c_ids' (found: tegra_i2c_ids2)", + str(exc.exception)) + + def test_of_match(self): + """Test detection of of_match_ptr() member""" + buff = ''' +static const struct udevice_id tegra_i2c_ids[] = { + { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, + { } +}; + +U_BOOT_DRIVER(i2c_tegra) = { + .name = "i2c_tegra", + .id = UCLASS_I2C, + .of_match = of_match_ptr(tegra_i2c_ids), +}; +''' + scan = src_scan.Scanner(None, False, None) + scan._parse_driver('file.c', buff) + self.assertIn('i2c_tegra', scan._drivers) + drv = scan._drivers['i2c_tegra'] + self.assertEqual('i2c_tegra', drv.name) + self.assertEqual('', drv.phase) + self.assertEqual([], drv.headers) + + def test_priv(self): + """Test collection of struct info from drivers""" + buff = ''' +static const struct udevice_id test_ids[] = { + { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, + { } +}; + +U_BOOT_DRIVER(testing) = { + .name = "testing", + .id = UCLASS_I2C, + .of_match = test_ids, + .priv_auto = sizeof(struct some_priv), + .plat_auto = sizeof(struct some_plat), + .per_child_auto = sizeof(struct some_cpriv), + .per_child_plat_auto = sizeof(struct some_cplat), + DM_PHASE(tpl) + DM_HEADER(<i2c.h>) + DM_HEADER(<asm/clk.h>) +}; +''' + scan = src_scan.Scanner(None, False, None) + scan._parse_driver('file.c', buff) + self.assertIn('testing', scan._drivers) + drv = scan._drivers['testing'] + self.assertEqual('testing', drv.name) + self.assertEqual('UCLASS_I2C', drv.uclass_id) + self.assertEqual( + {'nvidia,tegra114-i2c': 'TYPE_114'}, drv.compat) + self.assertEqual('some_priv', drv.priv) + self.assertEqual('some_plat', drv.plat) + self.assertEqual('some_cpriv', drv.child_priv) + self.assertEqual('some_cplat', drv.child_plat) + self.assertEqual('tpl', drv.phase) + self.assertEqual(['<i2c.h>', '<asm/clk.h>'], drv.headers) + self.assertEqual(1, len(scan._drivers)) + + def test_uclass_scan(self): + """Test collection of uclass-driver info""" + buff = ''' +UCLASS_DRIVER(i2c) = { + .id = UCLASS_I2C, + .name = "i2c", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .priv_auto = sizeof(struct some_priv), + .per_device_auto = sizeof(struct per_dev_priv), + .per_device_plat_auto = sizeof(struct per_dev_plat), + .per_child_auto = sizeof(struct per_child_priv), + .per_child_plat_auto = sizeof(struct per_child_plat), + .child_post_bind = i2c_child_post_bind, +}; + +''' + scan = src_scan.Scanner(None, False, None) + scan._parse_uclass_driver('file.c', buff) + self.assertIn('UCLASS_I2C', scan._uclass) + drv = scan._uclass['UCLASS_I2C'] + self.assertEqual('i2c', drv.name) + self.assertEqual('UCLASS_I2C', drv.uclass_id) + self.assertEqual('some_priv', drv.priv) + self.assertEqual('per_dev_priv', drv.per_dev_priv) + self.assertEqual('per_dev_plat', drv.per_dev_plat) + self.assertEqual('per_child_priv', drv.per_child_priv) + self.assertEqual('per_child_plat', drv.per_child_plat) + self.assertEqual(1, len(scan._uclass)) + + drv2 = copy.deepcopy(drv) + self.assertEqual(drv, drv2) + drv2.priv = 'other_priv' + self.assertNotEqual(drv, drv2) + + # The hashes only depend on the uclass ID, so should be equal + self.assertEqual(drv.__hash__(), drv2.__hash__()) + + self.assertEqual("UclassDriver(name='i2c', uclass_id='UCLASS_I2C')", + str(drv)) + + def test_uclass_scan_errors(self): + """Test detection of uclass scanning errors""" + buff = ''' +UCLASS_DRIVER(i2c) = { + .name = "i2c", +}; + +''' + scan = src_scan.Scanner(None, False, None) + with self.assertRaises(ValueError) as exc: + scan._parse_uclass_driver('file.c', buff) + self.assertIn("file.c: Cannot parse uclass ID in driver 'i2c'", + str(exc.exception)) + + def test_struct_scan(self): + """Test collection of struct info""" + buff = ''' +/* some comment */ +struct some_struct1 { + struct i2c_msg *msgs; + uint nmsgs; +}; +''' + scan = src_scan.Scanner(None, False, None) + scan._basedir = os.path.join(OUR_PATH, '..', '..') + scan._parse_structs('arch/arm/include/asm/file.h', buff) + self.assertIn('some_struct1', scan._structs) + struc = scan._structs['some_struct1'] + self.assertEqual('some_struct1', struc.name) + self.assertEqual('asm/file.h', struc.fname) + + buff = ''' +/* another comment */ +struct another_struct { + int speed_hz; + int max_transaction_bytes; +}; +''' + scan._parse_structs('include/file2.h', buff) + self.assertIn('another_struct', scan._structs) + struc = scan._structs['another_struct'] + self.assertEqual('another_struct', struc.name) + self.assertEqual('file2.h', struc.fname) + + self.assertEqual(2, len(scan._structs)) + + self.assertEqual("Struct(name='another_struct', fname='file2.h')", + str(struc)) + + def test_struct_scan_errors(self): + """Test scanning a header file with an invalid unicode file""" + output = tools.GetOutputFilename('output.h') + tools.WriteFile(output, b'struct this is a test \x81 of bad unicode') + + scan = src_scan.Scanner(None, False, None) + with test_util.capture_sys_output() as (stdout, _): + scan.scan_header(output) + self.assertIn('due to unicode error', stdout.getvalue()) + + def setup_dup_drivers(self, name, phase=''): + """Set up for a duplcate test + + Returns: + tuple: + Scanner to use + Driver record for first driver + Text of second driver declaration + Node for driver 1 + """ + driver1 = ''' +static const struct udevice_id test_ids[] = { + { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, + { } +}; + +U_BOOT_DRIVER(%s) = { + .name = "testing", + .id = UCLASS_I2C, + .of_match = test_ids, + %s +}; +''' % (name, 'DM_PHASE(%s)' % phase if phase else '') + driver2 = ''' +static const struct udevice_id test_ids[] = { + { .compatible = "nvidia,tegra114-dvc" }, + { } +}; + +U_BOOT_DRIVER(%s) = { + .name = "testing", + .id = UCLASS_RAM, + .of_match = test_ids, +}; +''' % name + scan = src_scan.Scanner(None, False, None, phase) + scan._parse_driver('file1.c', driver1) + self.assertIn(name, scan._drivers) + drv1 = scan._drivers[name] + + prop = FakeProp() + prop.name = 'compatible' + prop.value = 'nvidia,tegra114-i2c' + node = FakeNode() + node.name = 'testing' + node.props = {'compatible': prop} + + # get_normalized_compat_name() uses this to check for root node + node.parent = FakeNode() + + return scan, drv1, driver2, node + + def test_dup_drivers(self): + """Test handling of duplicate drivers""" + name = 'nvidia_tegra114_i2c' + scan, drv1, driver2, node = self.setup_dup_drivers(name) + self.assertEqual('', drv1.phase) + + # The driver should not have a duplicate yet + self.assertEqual([], drv1.dups) + + scan._parse_driver('file2.c', driver2) + + # The first driver should now be a duplicate of the second + drv2 = scan._drivers[name] + self.assertEqual('', drv2.phase) + self.assertEqual(1, len(drv2.dups)) + self.assertEqual([drv1], drv2.dups) + + # There is no way to distinguish them, so we should expect a warning + self.assertTrue(drv2.warn_dups) + + # We should see a warning + with test_util.capture_sys_output() as (stdout, _): + scan.mark_used([node]) + self.assertEqual( + "Warning: Duplicate driver name 'nvidia_tegra114_i2c' (orig=file2.c, dups=file1.c)", + stdout.getvalue().strip()) + + def test_dup_drivers_phase(self): + """Test handling of duplicate drivers but with different phases""" + name = 'nvidia_tegra114_i2c' + scan, drv1, driver2, node = self.setup_dup_drivers(name, 'spl') + scan._parse_driver('file2.c', driver2) + self.assertEqual('spl', drv1.phase) + + # The second driver should now be a duplicate of the second + self.assertEqual(1, len(drv1.dups)) + drv2 = drv1.dups[0] + + # The phase is different, so we should not warn of dups + self.assertFalse(drv1.warn_dups) + + # We should not see a warning + with test_util.capture_sys_output() as (stdout, _): + scan.mark_used([node]) + self.assertEqual('', stdout.getvalue().strip()) + + def test_sequence(self): + """Test assignment of sequence numnbers""" + scan = src_scan.Scanner(None, False, None, '') + node = FakeNode() + uc = src_scan.UclassDriver('UCLASS_I2C') + node.uclass = uc + node.driver = True + node.seq = -1 + node.path = 'mypath' + uc.alias_num_to_node[2] = node + + # This should assign 3 (after the 2 that exists) + seq = scan.assign_seq(node) + self.assertEqual(3, seq) + self.assertEqual({'mypath': 3}, uc.alias_path_to_num) + self.assertEqual({2: node, 3: node}, uc.alias_num_to_node) |