From 889eeb416bc06af472e56acb2767885e31121a27 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Sat, 10 Aug 2019 16:56:41 +0200 Subject: [PATCH 1/9] dts: edtlib: issue same warning only once Issue a warning only on first occurence. Suppress all following warnings having the identical warning text. Signed-off-by: Bobby Noelte --- scripts/dts/edtlib.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/dts/edtlib.py b/scripts/dts/edtlib.py index 8be11a1214206..0cfdef47af8c6 100644 --- a/scripts/dts/edtlib.py +++ b/scripts/dts/edtlib.py @@ -145,6 +145,7 @@ def __init__(self, dts, bindings_dirs, warn_file=None): # Do this indirection with None in case sys.stderr is deliberately # overridden self._warn_file = sys.stderr if warn_file is None else warn_file + self._warnings = [] self.dts_path = dts self.bindings_dirs = bindings_dirs @@ -667,7 +668,9 @@ def _check_binding_properties(self, binding, binding_path): .format(binding_path, prop_name)) def _warn(self, msg): - print("warning: " + msg, file=self._warn_file) + if msg not in self._warnings: + self._warnings.append(msg) + print("warning: " + msg, file=self._warn_file) class Node: From 372e2216d79493d14b2f3ffec5c2273be0f4b196 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Fri, 24 Jan 2020 18:40:56 +0100 Subject: [PATCH 2/9] dts: edtlib: enable bindings for special nodes without compatibles Associate a default compatible to special nodes that by specification do not have or can not have a compatible. The default compatible allows to use bindings for nodes that otherwise can not be controlled by a binding. Default compatible for special DTS nodes: - '/aliases' node : 'aliases' - '/chosen' node : 'chosen' - pin controller pinctrl state node : 'pinctrl-state' - pin controller pin configuration node : 'pincfg' - fixed partion node : 'fixed-partition' - gpio led node : 'gpio-led' Default compatibles are only associated if no compatible is given. Signed-off-by: Bobby Noelte --- scripts/dts/edtlib.py | 54 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/scripts/dts/edtlib.py b/scripts/dts/edtlib.py index 0cfdef47af8c6..3ca2ed978fa8f 100644 --- a/scripts/dts/edtlib.py +++ b/scripts/dts/edtlib.py @@ -931,6 +931,9 @@ def _init_binding(self): # Initializes Node.matching_compat, Node._binding, and # Node.binding_path. # + # Attach a default binding to the /chosen node, /aliases node and the + # pin controller pinctrl state and pin configuration nodes. + # # Node._binding holds the data from the node's binding file, in the # format returned by PyYAML (plain Python lists, dicts, etc.), or None # if the node has no binding. @@ -941,6 +944,43 @@ def _init_binding(self): if "compatible" in self._node.props: self.compats = self._node.props["compatible"].to_strings() + elif self._node.name == "chosen": + # use default compatible for chosen node + self.compats = ["chosen"] + elif self._node.name == "aliases": + # use default compatible for chosen node + self.compats = ["aliases"] + elif self._node.parent and "compatible" in self._node.parent.props and \ + "fixed-partitions" in self._node.parent.props.get("compatible") \ + .to_strings(): + # Child of a 'fixed-partions' node + # Assume it is a fixed partion node (note singular) + self.compats = ["fixed-partition"] + elif self._node.parent and "compatible" in self._node.parent.props and \ + "gpio-leds" in self._node.parent.props.get("compatible") \ + .to_strings(): + # Child of a 'gpio-leds' node + # Assume it is a gpio-led node (note singular) + self.compats = ["gpio-led"] + elif self._node.parent and \ + self._node.parent.props.get("pin-controller", False): + # child of pin controller node + if self._node.nodes: + # the pin controller sub nodes has sub nodes in itself + # Assume it is a pinctr state node + self.compats = ["pinctrl-state"] + else: + # No sub nodes - assume a pin configuration node + self.compats = ["pincfg"] + elif self._node.parent and self._node.parent.parent and \ + self._node.parent.parent.props.get("pin-controller", False): + # Grand child of pin controller node + # Assume a pin configuration node + self.compats = ["pincfg"] + else: + self.compats = [] + + if self.compats: on_bus = self.on_bus for compat in self.compats: @@ -949,7 +989,6 @@ def _init_binding(self): self.matching_compat = compat self._binding, self.binding_path = \ self.edt._compat2binding[compat, on_bus] - return else: # No 'compatible' property. See if the parent binding has a @@ -1536,10 +1575,15 @@ def _dt_compats(dt): # Returns a set() with all 'compatible' strings in the devicetree # represented by dt (a dtlib.DT instance) - return {compat - for node in dt.node_iter() - if "compatible" in node.props - for compat in node.props["compatible"].to_strings()} + compats = [compat + for node in dt.node_iter() + if "compatible" in node.props + for compat in node.props["compatible"].to_strings()] + # Append generic compatibles for special nodes without compatibles + compats.extend(["aliases", "chosen", "fixed-partition", "gpio-led", + "pinctrl-state", "pincfg"]) + + return set(compats) def _binding_paths(bindings_dirs): From e4ec84ce4ed193037f6f6c2bc9ad2c9de2e76543 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Fri, 24 Jan 2020 18:42:59 +0100 Subject: [PATCH 3/9] dts: edtlib: allow binding override by binding file name Allow to override a binding by file name. Take the first file given by the binding directories provided to edtlib. This allows applications and out of tree drivers to amend a binding already given by Zephyr. Warn in case there are duplicate binding file names. Signed-off-by: Bobby Noelte --- scripts/dts/edtlib.py | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/scripts/dts/edtlib.py b/scripts/dts/edtlib.py index 3ca2ed978fa8f..d6bb948032888 100644 --- a/scripts/dts/edtlib.py +++ b/scripts/dts/edtlib.py @@ -244,6 +244,30 @@ def _define_order(self): # Node. self.scc_order() + def _init_binding_paths(self, bindings_dirs): + # Creates self._binding_paths. A list with the paths to all bindings + # (.yaml files) in 'bindings_dirs' + # + # In case of duplicate file names uses the first found. + # This allows to override bindings based on file names. + + self._binding_paths = [] + binding_filenames = {} + + for bindings_dir in bindings_dirs: + for root, _, filenames in os.walk(bindings_dir): + for filename in filenames: + if filename.endswith(".yaml"): + binding_path = os.path.join(root, filename) + if filename not in binding_filenames: + self._binding_paths.append(binding_path) + binding_filenames[filename] = binding_path + else: + self._warn("multiple candidates for binding file " + "'{}': skipping '{}' and using '{}'" + .format(filename, binding_path, + binding_filenames[filename])) + def _init_compat2binding(self, bindings_dirs): # Creates self._compat2binding. This is a dictionary that maps # (, ) tuples (both strings) to (, ) @@ -267,7 +291,7 @@ def _init_compat2binding(self, bindings_dirs): "|".join(re.escape(compat) for compat in dt_compats) ).search - self._binding_paths = _binding_paths(bindings_dirs) + self._init_binding_paths(bindings_dirs) self._compat2binding = {} for binding_path in self._binding_paths: @@ -1585,22 +1609,6 @@ def _dt_compats(dt): return set(compats) - -def _binding_paths(bindings_dirs): - # Returns a list with the paths to all bindings (.yaml files) in - # 'bindings_dirs' - - binding_paths = [] - - for bindings_dir in bindings_dirs: - for root, _, filenames in os.walk(bindings_dir): - for filename in filenames: - if filename.endswith(".yaml"): - binding_paths.append(os.path.join(root, filename)) - - return binding_paths - - def _on_bus_from_binding(binding): # Returns the bus specified by 'on-bus:' in the binding (or the # legacy 'parent-bus:' and 'parent: bus:'), or None if missing From a9468966088a109c028fe0d4c76e41222921cb4b Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Fri, 24 Jan 2020 18:39:13 +0100 Subject: [PATCH 4/9] dts: edtlib: allow extraction of gpio-ranges properties gpio-ranges properties expect a phandle value list. Extend _standard_phandle_val_list() to also work on gpio-ranges. Signed-off-by: Bobby Noelte --- scripts/dts/edtlib.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/scripts/dts/edtlib.py b/scripts/dts/edtlib.py index d6bb948032888..413558e6a2738 100644 --- a/scripts/dts/edtlib.py +++ b/scripts/dts/edtlib.py @@ -1339,6 +1339,47 @@ def _standard_phandle_val_list(self, prop): # # Returns a list of ControllerAndData instances. + if prop.name == "gpio-ranges": + # Special case + cell_names = ("gpio-base", "pinctrl-base", "count") + names_ident = "gpio-ranges-groups" + + n_cells = len(cell_names) + phandle_val_list = [] + raw = prop.value + while raw: + if len(raw) < 4: + # Not enough room for phandle + _err("bad value for " + repr(prop)) + phandle = to_num(raw[:4]) + raw = raw[4:] + + node = prop.node.dt.phandle2node.get(phandle) + if not node: + _err("bad phandle in " + repr(prop)) + + if len(raw) < 4*n_cells: + _err("missing data after phandle in " + repr(prop)) + + phandle_val_list.append((node, raw[:4*n_cells])) + raw = raw[4*n_cells:] + + res = [] + for controller_node, data in phandle_val_list: + data_list = [int.from_bytes(data[i:i + 4], "big", + signed=False) + for i in range(0, len(data), 4)] + + entry = ControllerAndData() + entry.node = self + entry.controller = self.edt._node2enode[controller_node] + entry.data = OrderedDict(zip(cell_names, data_list)) + res.append(entry) + + _add_names(self._node, names_ident, res) + + return res + if prop.name.endswith("gpios"): # There's some slight special-casing for *-gpios properties in that # e.g. foo-gpios still maps to #gpio-cells rather than From 0a1702a7d5a188415904a274c9362e33d2cb59b4 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Sat, 25 Jan 2020 08:42:33 +0100 Subject: [PATCH 5/9] dts: edtlib: extract partitions nodes Extract the partitions child node of flash nodes. Extend the Node class with properties for: partitions: A list of Partition objects of the partitions of the 'partitions' node of a flash. Only meaningful for nodes representing a flash - otherwise an empty list. Signed-off-by: Bobby Noelte --- scripts/dts/edtlib.py | 153 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 149 insertions(+), 4 deletions(-) diff --git a/scripts/dts/edtlib.py b/scripts/dts/edtlib.py index 413558e6a2738..3b27aa46eb0bf 100644 --- a/scripts/dts/edtlib.py +++ b/scripts/dts/edtlib.py @@ -510,6 +510,7 @@ def _init_nodes(self): node._init_props() node._init_interrupts() node._init_pinctrls() + node._init_partitions() def _init_compat2enabled(self): # Creates self.compat2enabled @@ -805,6 +806,11 @@ class Node: flash_controller: The flash controller for the node. Only meaningful for nodes representing flash partitions. + + partitions: + A list of Partition objects of the partitions of the 'partitions' node of + a flash. Only meaningful for nodes representing a flash - otherwise an + empty list. """ global dtc_flags @@ -940,10 +946,7 @@ def flash_controller(self): _err("flash partition {!r} lacks parent or grandparent node" .format(self)) - controller = self.parent.parent - if controller.matching_compat == "soc-nv-flash": - return controller.parent - return controller + return _flash_controller(self.parent.parent) def __repr__(self): return "".format( @@ -1273,6 +1276,23 @@ def _init_regs(self): _add_names(node, "reg", self.regs) + def _init_partitions(self): + # Initializes self.partitions + + self.partitions = [] + + if 'partitions' not in self.children: + # This node does not have partitions + return + + if self.name == "partitions" or \ + (self.parent and self.parent.name == "partitions"): + # This node is a partitions node or the child of a + # partitions node - but we expect a flash node + return + + self.partitions = _partitions_list(self.children['partitions'], self) + def _init_pinctrls(self): # Initializes self.pinctrls from any pinctrl- properties @@ -1510,6 +1530,57 @@ def __repr__(self): return "".format(", ".join(fields)) +class Partition: + """ + Represents a partition node. + + These attributes are available on Partition objects: + + node: + The Node instance of the partition. + + name: + The name of the partition taken from the 'label' property of the node. If + omitted, the name is taken from the node name (excluding the unit + address). + + flash: + The Node instance of the flash this partition belongs to. + + controller: + The Node instance of the flash controller that controls the flash this + partition belongs to. Might be same as the flash. + + partitions: + A list of Partition objects of the partition. An empty list if there are + None. + + addr: + The starting address of the partition, in the parent flash/ partition + address space. Taken from the 'reg' property of the node. 0 in case + there is no 'reg' property. + + size: + The size of the partition. Taken from the 'reg' property of the node. + 0 in case there is no 'reg' property. + + attributes: + A dictionary of partition property values indexed by the property name. + """ + def __repr__(self): + fields = [] + + fields.append("name: " + self.name) + fields.append("flash: {}".format(self.flash)) + fields.append("controller: {}".format(self.controller)) + fields.append("partitions: {}".format(self.partitions)) + fields.append("addr: {}".format(self.addr)) + fields.append("size: {}".format(self.size)) + fields.append("attributes: {}".format(self.attributes)) + + return "".format(", ".join(fields)) + + class PinCtrl: """ Represents a pin control configuration for a set of pins on a device, @@ -2203,6 +2274,80 @@ def _phandle_val_list(prop, n_cells_name): return res +def _flash_controller(node): + # The flash controller for the node. Only meaningful for nodes representing + # - a flash '.../flash@0' or + # - a partitions list '.../flash@0/partitions' or + # - a partition '.../flash@0/partitions/partition@fc000' + # - None otherwise. + + # The flash controller might be the flash itself (for cases like NOR + # flashes). For the case of 'soc-nv-flash', we assume the controller is the + # parent of the flash node. + if node.matching_compat == "soc-nv-flash": + # This is a flash + controller = node.parent + elif node.name == 'partitions': + # This is a partitions list + if not node.parent: + # A partitions list must have a parent. + _err("flash partitions {!r} lack parent node".format(node)) + controller = _flash_controller(node.parent) + elif 'partitions' in node.children: + # This might be a flash or a partition + if not node.parent or not 'partitions' in node.parent.children: + # This is a flash + controller = node + else: + # This is a partition node. + if not node.parent.parent: + # A partition must have a grandparent. + _err("flash partition {!r} lacks grandparent node".format(node)) + controller =_flash_controller(node.parent.parent) + elif node.parent and 'partitions' in node.parent.children: + # This is a partition within a partitions node. + if not node.parent.parent: + # A partition must have a grandparent. + _err("flash partition {!r} lacks grandparent node".format(node)) + controller = _flash_controller(node.parent.parent) + else: + controller = None + return controller + +def _partitions_list(partitions_node, flash_node): + # Returns a list of Partition objects for partitions + + partitions_list = [] + for partition_node in partitions_node.children.values(): + partition = Partition() + partition.node = partition_node + partition.flash = flash_node + partition.controller = _flash_controller(flash_node) + partition.attributes = {} + partition.name = None + partition.addr = 0 + partition.size = 0 + for prop_name, prop in partition_node.props.items(): + if prop_name == 'reg': + partition.addr = prop.val[0] + partition.size = prop.val[1] + elif prop_name == 'label': + partition.name = prop.val + else: + partition.attributes[prop_name] = prop.val + partition.partitions = [] + if 'partitions' in partition_node.children: + child_partitions = partition_node.children['partitions'] + partition.partitions = _partitions_list(child_partitions, + flash_node) + # Assure the partition gets a name + if not partition.name: + # From Linux partitions.txt: "If omitted, the label is taken from + # the node name (excluding the unit address)." + partition.name = partition_node.name.split('@')[0] + partitions_list.append(partition) + return partitions_list + def _address_cells(node): # Returns the #address-cells setting for 'node', giving the number of From d3ce72cd185db4d005f27f36ee79f61fee8ac000 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Fri, 24 Jan 2020 18:45:39 +0100 Subject: [PATCH 6/9] dts: edtlib: extract pinctrl state and pin configuration nodes Extract the pin controller child nodes for pinctrl state and pin configuration. Extract gpio ranges controlled by the pin controller. Provide a PinCfg object that represents the pin configuration data for easy access. Extend the Node class with properties for: pinctrl_states: A list with the Node instances for the 'pinctrl-state' children of a pin controller node. The list is empty if the node does not have any pinctrl state children. pincfgs: A list of PinCfg objects for the 'pinctrl-state' node. The list is empty if the node is not a 'pinctrl-state' node. pinctrl_gpio_ranges: A list of ControllerAndData objects of the gpio ranges a pin controller controls. The list is empty if the node is not a pin controller or no 'gpio-ranges' are defined by the gpio nodes. pin_controller: The pin controller for the node. Only meaningful for nodes representing pinctrl states. Signed-off-by: Bobby Noelte --- scripts/dts/edtlib.py | 296 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 295 insertions(+), 1 deletion(-) diff --git a/scripts/dts/edtlib.py b/scripts/dts/edtlib.py index 3b27aa46eb0bf..246d918875362 100644 --- a/scripts/dts/edtlib.py +++ b/scripts/dts/edtlib.py @@ -509,6 +509,10 @@ def _init_nodes(self): # run them separately node._init_props() node._init_interrupts() + node._init_gpio_ranges() + + for node in self.nodes: + # These depend on the referenced nodes' props already initialized. node._init_pinctrls() node._init_partitions() @@ -788,6 +792,24 @@ class Node: node, sorted by index. The list is empty if the node does not have any pinctrl- properties. + pinctrl_states: + A list with the Node instances for the 'pinctrl-state' children of + a pin controller node. The list is empty if the node does not have any + pinctrl state children. + + pinctrl_gpio_ranges: + A list of ControllerAndData objects of the gpio ranges a pin controller + controls. The list is empty if the node is not a pin controller or no + 'gpio-ranges' are defined by the gpio nodes. + + pincfgs: + A list of PinCfg objects for the 'pinctrl-state' node. The list is + empty if the node is not a 'pinctrl-state' node. + + pin_controller: + The pin controller for the node. Only meaningful for nodes representing + pinctrl states. + bus: If the node is a bus node (has a 'bus:' key in its binding), then this attribute holds the bus type, e.g. "i2c" or "spi". If the node is not a @@ -1247,6 +1269,26 @@ def _check_undeclared_props(self): .format(prop_name, self._node.path, self.edt.dts_path, self.binding_path)) + def _init_gpio_ranges(self): + # Initialize self.pinctrl_gpio_ranges + + if not hasattr(self, 'pinctrl_gpio_ranges'): + self.pinctrl_gpio_ranges = [] + + if 'gpio-ranges' not in self.props: + return + + for prop_value in self.props['gpio-ranges'].val: + entry = ControllerAndData() + entry.node = prop_value.node + entry.name = prop_value.name + entry.controller = self + entry.data = prop_value.data + # Add to pin controller + if not hasattr(prop_value.controller, 'pinctrl_gpio_ranges'): + prop_value.controller.pinctrl_gpio_ranges = [] + prop_value.controller.pinctrl_gpio_ranges.append(entry) + def _init_regs(self): # Initializes self.regs @@ -1293,6 +1335,84 @@ def _init_partitions(self): self.partitions = _partitions_list(self.children['partitions'], self) + def _init_pinctrl_states_append_pincfg_entry(self, pinctrl_node, + pinctrl_state_node, pincfg_node): + # Append a pin configuration to a pin control state of a pin controller + # The pin configuration was requested by the pinctrl node. + + pincfg = PinCfg() + pincfg.node = pincfg_node + pincfg.name = pincfg_node.name + pincfg.pinctrl_state_node = pinctrl_state_node + pincfg.pinctrl_nodes = [pinctrl_node] + pincfg.pin_controller_node = self + pincfg.groups = [] + pincfg.pins = [] + pincfg.muxes = [] + pincfg.function = None + pincfg.configs = {} + + for prop_name, prop in pincfg_node.props.items(): + _pincfg_check_type(pincfg_node, prop) + if prop_name in PinCfg.pinspec_props: + _pincfg_set_pinspec(pincfg_node, pincfg, prop) + elif prop_name in PinCfg.bool_props: + if prop.type == 'int': + if prop.val == 0: + pincfg.configs[prop_name] = False + else: + pincfg.configs[prop_name] = True + else: # boolean + pincfg.configs[prop_name] = prop.val + elif prop_name in PinCfg.bool_or_value_props: + pincfg.configs[prop_name] = prop.val + elif prop_name in PinCfg.value_props: + pincfg.configs[prop_name] = prop.val + else: # Unknown property name + self.edt._warn("{!r} uses unexpected property '{}'" + .format(pincfg_node, prop_name)) + pincfg.configs[prop_name] = prop.val + + if not pincfg.pins and not pincfg.groups: + _err("{!r} misses one of 'pins', 'pinmux', 'groups' property" + .format(pincfg_node)) + pinctrl_state_node.pincfgs.append(pincfg) + + def _init_pinctrl_states(self, pinctrl_node, pinctrl_state_node): + # Initialise self.pinctrl_states and self.pincfgs from sub-nodes + # of a pin controller device node. + + # Add a pin control state to self.pinctrl_states + if not hasattr(self, 'pinctrl_states'): + self.pinctrl_states = [] + if pinctrl_state_node in self.pinctrl_states: + # Already in pinctrl states. + # Make the pin configurations aware that they are referenced by a + # pinctrl client node. + for pincfg in pinctrl_state_node.pincfgs: + pincfg.pinctrl_nodes.append(pinctrl_node) + return + self.pinctrl_states.append(pinctrl_state_node) + + # Remember the pin controller that owns the pin control state + pinctrl_state_node.pin_controller = self + + # Add pin configurations to pinctrl_state_node.pincfgs + if not hasattr(pinctrl_state_node, 'pincfgs'): + pinctrl_state_node.pincfgs = [] + if pinctrl_state_node.children: + # There are pin configuration child nodes underneath + # the pinctrl state node + pinctrl_state_node_children = pinctrl_state_node.children + else: + # The pinctrl state node does not have children. Assume it is it's own + # pin configuration node. + pinctrl_state_node_children = { pinctrl_state_node.name : pinctrl_state_node } + + for pincfg_node in pinctrl_state_node_children.values(): + self._init_pinctrl_states_append_pincfg_entry(pincfg_node, + pinctrl_state_node, pincfg_node) + def _init_pinctrls(self): # Initializes self.pinctrls from any pinctrl- properties @@ -1310,6 +1430,11 @@ def _init_pinctrls(self): _err("missing 'pinctrl-{}' property on {!r} - indices should " "be contiguous and start from zero".format(i, node)) + # Assure node has pinctrl related properties + if not hasattr(self, 'pinctrl_states'): + self.pinctrl_states = [] + if not hasattr(self, 'pincfgs'): + self.pincfgs = [] self.pinctrls = [] for prop in pinctrl_props: pinctrl = PinCtrl() @@ -1318,7 +1443,12 @@ def _init_pinctrls(self): self.edt._node2enode[node] for node in prop.to_nodes() ] self.pinctrls.append(pinctrl) - + # Make pin controller node aware of the pinctrl state. + for pinctrl_state_node in pinctrl.conf_nodes: + # pin controller node: pinctrl_state_node.parent + # pinctrl client node: self + pinctrl_state_node.parent._init_pinctrl_states(self, + pinctrl_state_node) _add_names(node, "pinctrl", self.pinctrls) def _init_interrupts(self): @@ -1612,6 +1742,83 @@ def __repr__(self): return "".format(", ".join(fields)) +class PinCfg: + """ + Represents a pin configuration for a set of pins on a device. + + These attributes are available on PinCfg objects: + + name: + The name of the configuration, as given by the node label. + + node: + The Node instance the pin configuration is on. + + pin_controller_node: + The node instance of the pin controller that may apply this pin + configuration. + + pinctrl_nodes: + A list of Node instances that use this configuration in their pinctrl. + + pinctrl_state_node: + The pinctrl state node this pin configuration belongs to. + + groups: + A list of names of the groups (of pins) this configuration applies to. + If no group name is given the list is empty. + + pins: + A list of pin identifiers that this configuration applies to. + The pin identifiers are the ones used by the pin controller. + + muxes: + A list of numerical multiplex settings for the pins. Multiplex settings + follow the sequence of the pins list. If no mux settings are given the + list is empty. + + function: + The name of the function the pins shall be multiplexed to. If no function + is given the value is None. + + configs: + A dictionary of pin configuration values indexed by the property name. + """ + ## + # @brief Properties to specify the pins the configuration applies to. + pinspec_props = [ + "pins", "pinmux", "groups", "pinctrl-pin-array", "function"] + ## + # @brief Boolean type properties for pin configuration by pinctrl. + bool_props = [ + "bias-disable", "bias-high-impedance", "bias-bus-hold", + "drive-push-pull", "drive-open-drain", "drive-open-source", + "input-enable", "input-disable", "input-schmitt-enable", + "input-schmitt-disable", "low-power-enable", "low-power-disable", + "output-disable", "output-enable", "output-low","output-high"] + ## + # @brief Boolean or value type properties for pin configuration by pinctrl. + bool_or_value_props = [ + "bias-pull-up", "bias-pull-down", "bias-pull-pin-default"] + ## + # @brief Value type properties for pin configuration by pinctrl. + value_props = [ + "drive-strength", "input-debounce", "power-source", "slew-rate", + "skew-delay"] + + def __repr__(self): + fields = [] + + fields.append("name: " + str(self.name)) + fields.append("groups: " + str(self.groups)) + fields.append("pins: " + str(self.pins)) + fields.append("muxes: " + str(self.muxes)) + fields.append("function: " + str(self.function)) + fields.append("configs: " + str(self.configs)) + + return "".format(", ".join(fields)) + + class Property: """ Represents a property on a Node, as set in its DT node and with @@ -1978,6 +2185,93 @@ def _add_names(node, names_ident, objs): obj.name = None +def _pincfg_check_type(pincfg_node, prop): + # Check pin configuration property type to be inline with expectations. + # Bindings may specify different types for certain properties. + + type_ok = True + if prop.name in PinCfg.pinspec_props: + if (prop.name == "groups" + and prop.type not in ("string-array", "string")) \ + or (prop.name == "pinmux" + and prop.type not in "array") \ + or (prop.name == "pins" + and prop.type not in ("int", "array") \ + or (prop.name == "pinctrl-pin-array" + and prop.type not in "array") \ + or (prop.name == "function" + and prop.type not in "string")): + type_ok = False + elif prop.name in PinCfg.bool_props: + if prop.type not in ("boolean", "int"): + type_ok = False + elif prop.name in PinCfg.bool_or_value_props: + if prop.type not in ("boolean", "int"): + type_ok = False + elif prop.name in PinCfg.value_props: + if prop.type not in "int": + type_ok = False + else: + # unknown property name - accept any type + pass + if not type_ok: + _err("{!r} uses unsupported type '{}' for property '{}'" + .format(pincfg_node, prop.type, prop.name)) + + +def _pincfg_set_pinspec(pincfg_node, pincfg, prop): + # Helper to set the pin specification of a pin configuration object. + # + # pincfg_node: + # edtlib.Node instance + # + # pincfg: + # PinCfg instance + # + # prop: + # Property that is one of the PinCtrl.pinspec_props. + + # Expects property to be checked with _pincfg_check_type() + if prop.name == "groups": + if pincfg.pins: + # pins already set by either pins or pinmux + _err("{!r} uses 'groups' and one of 'pins', 'pinmux' property" + .format(pincfg_node)) + if prop.type == 'string-array': + pincfg.groups = prop.val + elif prop.type == 'string': + pincfg.groups = [prop.val] + else: + raise TypeError + elif prop.name == 'pinmux': + if pincfg.pins or pincfg.groups: + # pins or groups already set by either pins or groups + _err("{!r} uses 'pinmux' and one of 'pins', 'groups' property" + .format(pincfg_node)) + pincfg.pins = [prop.val[i] for i in range(0, len(prop.val), 2)] + pincfg.muxes = [prop.val[i] for i in range(1, len(prop.val), 2)] + elif prop.name == 'pins': + if pincfg.pins or pincfg.groups: + # pins or groups already set by either pinmux or groups + _err("{!r} uses 'pins' and one of 'pinmux', 'groups' property" + .format(pincfg_node)) + if prop.type == 'int': + pincfg.pins = [prop.val] + elif prop.type == 'array': + pincfg.pins = prop.val + else: + raise TypeError + elif prop.name == "function": + pincfg.function = prop.val + elif prop.name == "pinctrl-pin-array": + ## @TODO Support pinctrl-pin-array + _err("{!r} uses unsupported '{}' property" + .format(pincfg_node, prop.name)) + else: + # should not happen + raise ValueError + + def _interrupt_parent(node): # Returns the node pointed at by the closest 'interrupt-parent', searching # the parents of 'node'. As of writing, this behavior isn't specified in From 21790b3b278301e7bc23c86e9c129ad938f814e3 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Sat, 25 Jan 2020 08:33:34 +0100 Subject: [PATCH 7/9] dts: edtlib: extract clock inputs and clock outputs Extract clock inputs ('clocks') and clock outputs ('clock-output-names') properties. Extend the Node class with properties for: clocks: A list of ControllerAndData objects for the clock inputs on the node, sorted by index. The list is empty if the node does not have a clocks property. clock_outputs: A list of ControllerAndData objects for the clock outputs on the node, sorted by index. The list is empty if the node does not have a \#clock-cells property. Signed-off-by: Bobby Noelte --- scripts/dts/edtlib.py | 116 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/scripts/dts/edtlib.py b/scripts/dts/edtlib.py index 246d918875362..e1e9f9c6419fa 100644 --- a/scripts/dts/edtlib.py +++ b/scripts/dts/edtlib.py @@ -509,6 +509,7 @@ def _init_nodes(self): # run them separately node._init_props() node._init_interrupts() + node._init_clock_outputs() node._init_gpio_ranges() for node in self.nodes: @@ -783,6 +784,16 @@ class Node: aliases: A list of aliases for the node. This is fetched from the /aliases node. + clocks: + A list of ControllerAndData objects for the clock inputs on the + node, sorted by index. The list is empty if the node does not have a + clocks property. + + clock_outputs: + A list of ControllerAndData objects for the clock outputs on the + node, sorted by index. The list is empty if the node does not have a + #clock-cells property. + interrupts: A list of ControllerAndData objects for the interrupts generated by the node. The list is empty if the node does not generate interrupts. @@ -947,6 +958,14 @@ def bus(self): return None + @property + def clocks(self): + "See the class docstring" + + if "clocks" not in self.props: + return [] + return self.props["clocks"].val + @property def on_bus(self): "See the class docstring" @@ -1289,6 +1308,84 @@ def _init_gpio_ranges(self): prop_value.controller.pinctrl_gpio_ranges = [] prop_value.controller.pinctrl_gpio_ranges.append(entry) + def _init_clock_outputs_append_entry(self, clock_index, + clock_outputs_name_indices, clock_output_names, + clock_frequency, clock_accuracy): + # Append entry to clock_outputs + + entry = ControllerAndData() + entry.node = self + entry.controller = self + entry.data = {} + clock_output_name_index = clock_outputs_name_indices[clock_index] + if clock_output_name_index is None: + entry.name = None + else: + entry.name = clock_output_names[clock_output_name_index] + if clock_frequency is not None: + entry.data["clock-frequency"] = clock_frequency + if clock_accuracy is not None: + entry.data["clock-accuracy"] = clock_accuracy + self.clock_outputs.append(entry) + + def _init_clock_outputs(self): + # Initialize self.clock_outputs + + self.clock_outputs = [] + + node = self._node + if "#clock-cells" not in node.props: + return + + clock_cells = node.props["#clock-cells"].to_num() + + if "clock-output-names" in node.props: + clock_output_names = node.props.get("clock-output-names") \ + .to_strings() + clock_count = len(clock_output_names) + else: + clock_output_names = [] + if clock_cells == 0: + # clock-cells of 0 indicates one clock + clock_count = 1 + else: + # unknown number of output clocks -> assume one + clock_count = 1 + self.edt._warn("no clock-output-names property in clock " + "provider {!r} - assuming one clock output".format(node)) + + if "clock-indices" in node.props: + clock_indices = clock_indices.to_nums() + if not clock_output_names: + _err("clock-indices ({}) given without clock-output-names" + " in clock provider {!r}.".format(clock_indices, self.node)) + if len(clock_output_names) != len(clock_indices): + _err("clock-output-names count ({}) does not match" + " clock-indices count ({}) in clock provider {!r}." + .format(len(clock_output_names), len(clock_indices), + self.node)) + # Increase clock count in case there is a higher clock index + clock_count = max(clock_count, max(clock_indices) + 1) + else: + clock_indices = None + + if "clock-frequency" in node.props: + clock_frequency = node.props.get("clock-frequency").to_num() + else: + clock_frequency = None + + if "clock-accuracy" in node.props: + clock_accuracy = node.props.get("clock-accuracy").to_num() + else: + clock_accuracy = None + + clock_outputs_name_indices = _clock_outputs_name_indices(clock_count, + clock_indices, clock_output_names) + for clock_index in range(0, clock_count): + self._init_clock_outputs_append_entry(clock_index, + clock_outputs_name_indices, clock_output_names, + clock_frequency, clock_accuracy) + def _init_regs(self): # Initializes self.regs @@ -2185,6 +2282,25 @@ def _add_names(node, names_ident, objs): obj.name = None +def _clock_outputs_name_indices(clock_count, clock_indices, clock_output_names): + # Create a table that indexes to clock output name by clock output index + + if clock_indices is not None: + # Make full output name indices table from sparse table given by DTS + clock_outputs_name_indices = [None for x in range(0, clock_count)] + for clock_output_name_index, clock_index in enumerate(clock_indices): + clock_outputs_name_indices[clock_index] = clock_output_name_index + else: + clock_outputs_name_indices = [] + for clock_index in range(0, clock_count): + if clock_index < len(clock_output_names): + clock_outputs_name_indices.append(clock_index) + else: + clock_outputs_name_indices.append(None) + + return clock_outputs_name_indices + + def _pincfg_check_type(pincfg_node, prop): # Check pin configuration property type to be inline with expectations. # Bindings may specify different types for certain properties. From d65e943c3e9c6f6e16060e8805e7752250b311b9 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Sun, 2 Feb 2020 10:38:56 +0100 Subject: [PATCH 8/9] dts: edtlib: extract gpio led nodes Extract the gpio led child node of gpio leds controller nodes. Extend the Node class with properties for: gpio_leds: A list of ControllerAndData objects of the leds a gpio leds controller controls. The list is empty if the node is not a gpio leds controller or it does not have gpio led children. Signed-off-by: Bobby Noelte --- scripts/dts/edtlib.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/scripts/dts/edtlib.py b/scripts/dts/edtlib.py index e1e9f9c6419fa..f2a07c9f25a55 100644 --- a/scripts/dts/edtlib.py +++ b/scripts/dts/edtlib.py @@ -516,6 +516,7 @@ def _init_nodes(self): # These depend on the referenced nodes' props already initialized. node._init_pinctrls() node._init_partitions() + node._init_gpio_leds() def _init_compat2enabled(self): # Creates self.compat2enabled @@ -794,6 +795,11 @@ class Node: node, sorted by index. The list is empty if the node does not have a #clock-cells property. + gpio_leds: + A list of ControllerAndData objects of the leds a gpio leds controller + controls. The list is empty if the node is not a gpio leds controller or + it does not have and gpio led children. + interrupts: A list of ControllerAndData objects for the interrupts generated by the node. The list is empty if the node does not generate interrupts. @@ -1386,6 +1392,40 @@ def _init_clock_outputs(self): clock_outputs_name_indices, clock_output_names, clock_frequency, clock_accuracy) + def _init_gpio_leds(self): + # Initializes self.gpio_leds + + if not hasattr(self, 'gpio_leds'): + self.gpio_leds = [] + + if not self.parent or "gpio-leds" not in self.parent.compats: + # Not a child of a gpio leds controller + return + + gpio_leds_controller = self.parent + + gpio_led = ControllerAndData() + gpio_led.node = self + gpio_led.name = None + gpio_led.controller = gpio_leds_controller + gpio_led.data = {} + for prop_name, prop_value in self.props.items(): + if prop_name == 'label': + gpio_led.name = prop_value.val + else: + gpio_led.data[prop_name] = prop_value.val + + # Assure the gpio led gets a name + if not gpio_led.name: + # From Linux leds/common.txt: "If omitted, the label is taken from + # the node name (excluding the unit address)." + gpio_led.name = self.name.split('@')[0] + + # Append led to the list of leds of the gpio leds controller + if not hasattr(gpio_leds_controller, 'gpio_leds'): + gpio_leds_controller.gpio_leds = [] + gpio_leds_controller.gpio_leds.append(gpio_led) + def _init_regs(self): # Initializes self.regs From c761f922d0270f6e1c82e2453f6157750aa62e81 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Sat, 1 Feb 2020 08:12:13 +0100 Subject: [PATCH 9/9] dts: edtlib: test pin and clock controller, partitions, special nodes Tests for edtlib features: - enable bindings for special nodes without compatibles - allow binding override by binding file name - allow extraction of gpio-ranges properties - extract pinctrl state and pin configuration nodes - extract clock inputs and clock outputs - extract partitions nodes - extract gpio led nodes Signed-off-by: Bobby Noelte --- scripts/dts/test-bindings-2/multidir.yaml | 2 +- scripts/dts/test-bindings/chosen.yaml | 70 ++++++++ scripts/dts/test-bindings/clock-consumer.yaml | 58 +++++++ .../dts/test-bindings/clock-controller.yaml | 40 +++++ .../dts/test-bindings/fixed-partition.yaml | 24 +++ scripts/dts/test-bindings/gpio-2-cell.yaml | 14 ++ scripts/dts/test-bindings/gpio-led.yaml | 47 ++++++ scripts/dts/test-bindings/gpio-leds.yaml | 6 + scripts/dts/test-bindings/multidir.yaml | 2 +- scripts/dts/test-bindings/partition.yaml | 25 +++ scripts/dts/test-bindings/pincfg.yaml | 159 ++++++++++++++++++ scripts/dts/test-bindings/pinctrl-gpio.yaml | 21 +++ scripts/dts/test-bindings/pinctrl-state.yaml | 8 + scripts/dts/test-bindings/soc-nv-flash.yaml | 3 + scripts/dts/test-multidir.dts | 4 +- scripts/dts/test.dts | 125 +++++++++++++- scripts/dts/testedtlib.py | 123 +++++++++++++- 17 files changed, 722 insertions(+), 9 deletions(-) create mode 100644 scripts/dts/test-bindings/chosen.yaml create mode 100644 scripts/dts/test-bindings/clock-consumer.yaml create mode 100644 scripts/dts/test-bindings/clock-controller.yaml create mode 100644 scripts/dts/test-bindings/fixed-partition.yaml create mode 100644 scripts/dts/test-bindings/gpio-2-cell.yaml create mode 100644 scripts/dts/test-bindings/gpio-led.yaml create mode 100644 scripts/dts/test-bindings/gpio-leds.yaml create mode 100644 scripts/dts/test-bindings/partition.yaml create mode 100644 scripts/dts/test-bindings/pincfg.yaml create mode 100644 scripts/dts/test-bindings/pinctrl-gpio.yaml create mode 100644 scripts/dts/test-bindings/pinctrl-state.yaml create mode 100644 scripts/dts/test-bindings/soc-nv-flash.yaml diff --git a/scripts/dts/test-bindings-2/multidir.yaml b/scripts/dts/test-bindings-2/multidir.yaml index 19721b2ae21e1..9bc73dbf910ba 100644 --- a/scripts/dts/test-bindings-2/multidir.yaml +++ b/scripts/dts/test-bindings-2/multidir.yaml @@ -2,4 +2,4 @@ description: Binding in test-bindings-2/ -compatible: "in-dir-2" +compatible: "in-dir" diff --git a/scripts/dts/test-bindings/chosen.yaml b/scripts/dts/test-bindings/chosen.yaml new file mode 100644 index 0000000000000..569cd4e4bf3c7 --- /dev/null +++ b/scripts/dts/test-bindings/chosen.yaml @@ -0,0 +1,70 @@ +# Copyright (c) 2019..2020 Bobby Noelte +# SPDX-License-Identifier: Apache-2.0 + +description: Chosen properties + +compatible: "chosen" + +properties: + stdout-path: + type: string + required: false + description: > + Device to be used for boot console output + If the character ":" is present in the value, this terminates the path. + The meaning of any characters following the ":" is device-specific, and + must be specified in the relevant binding documentation. + For UART devices, the preferred binding is a string in the form: + path:{{{}}} + where + path - path of uart device + baud - baud rate in decimal + parity - 'n' (none), 'o', (odd) or 'e' (even) + bits - number of data bits + flow - 'r' (rts) + For example: 115200n8r + + zephyr,flash: + type: path + required: false + description: TBD + + zephyr,sram: + type: path + required: false + description: TBD + + zephyr,ccm: + type: path + required: false + description: TBD + + zephyr,console: + type: path + required: false + description: TBD + + zephyr,shell-uart: + type: path + required: false + description: TBD + + zephyr,bt-uart: + type: path + required: false + description: TBD + + zephyr,uart-pipe: + type: path + required: false + description: TBD + + zephyr,bt-mon-uart: + type: path + required: false + description: TBD + + zephyr,uart-mcumgr: + type: path + required: false + description: TBD diff --git a/scripts/dts/test-bindings/clock-consumer.yaml b/scripts/dts/test-bindings/clock-consumer.yaml new file mode 100644 index 0000000000000..2d237655d79cb --- /dev/null +++ b/scripts/dts/test-bindings/clock-consumer.yaml @@ -0,0 +1,58 @@ +# Copyright (c) 2018..2020 Bobby Noelte +# SPDX-License-Identifier: Apache-2.0 + +# -- Assigned clock parents and rates -- +# Some platforms may require initial configuration of default parent clocks +# and clock frequencies. Such a configuration can be specified in a device tree +# node through assigned-clocks, assigned-clock-parents and assigned-clock-rates +# properties. + +description: Clock consumer + +compatible: "clock-consumer" + +properties: + clocks: + type: phandle-array + required: true + description: > + List of phandle and clock specifier pairs, one pair for each clock + input to the device. Note - if the clock provider specifies '0' for + clock-cells, then only the phandle portion of the pair will appear. + + clock-names: + type: string-array + required: false + description: > + List of clock input name strings sorted in the same order as the clocks + property. + + clock-ranges: + type: boolean + required: false + description: > + Empty property indicating that child nodes can inherit named clocks from + this node. Useful for bus nodes to provide a clock to their children. + + assigned-clocks: + type: phandle-array + required: false + description: > + List of phandle and clock specifier pairs, one pair for each assigned + clock input. Note - if the clock provider specifies '0' for + clock-cells, then only the phandle portion of the pair will appear. + + assigned-clock-parents: + type: phandle-array + required: false + description: > + List of parent clocks in the form of a phandle and clock + specifier pair. The list shall correspond to the clocks listed in the + assigned-clocks directive. + + assigned-clock-rates: + type: array + required: false + description: > + List of frequencies in Hz. The list shall correspond to the clocks + listed in the assigned-clocks directive. diff --git a/scripts/dts/test-bindings/clock-controller.yaml b/scripts/dts/test-bindings/clock-controller.yaml new file mode 100644 index 0000000000000..7614ccd3bef15 --- /dev/null +++ b/scripts/dts/test-bindings/clock-controller.yaml @@ -0,0 +1,40 @@ +# Copyright (c) 2019, Linaro Limited +# Copyright (c) 2019..2020 Bobby Noelte +# SPDX-License-Identifier: Apache-2.0 + +# Common fields for clock controllers + +description: Clock provider + +compatible: "clock-provider" + +properties: + "#clock-cells": + type: int + required: true + description: Number of items to expect in a Clock specifier + + clock-output-names: + type: string-array + required: false + description: > + A list of strings of clock output signal names indexed by the first + cell in the clock specifier. + + clock-indices: + type: array + required: false + description: > + The identifying number for the clocks in the node. If it is not linear + from zero, then this allows the mapping of identifiers into the + clock-output-names array. + + protected-clocks: + type: phandles + required: false + description: > + Clocks that are not fully exposed, such as in situations where those + clocks are used by drivers running in ARM secure execution levels. + +clock-cells: + - clock-id diff --git a/scripts/dts/test-bindings/fixed-partition.yaml b/scripts/dts/test-bindings/fixed-partition.yaml new file mode 100644 index 0000000000000..6551b89e14f64 --- /dev/null +++ b/scripts/dts/test-bindings/fixed-partition.yaml @@ -0,0 +1,24 @@ +# Copyright (c) 2018..2020 Bobby Noelte +# SPDX-License-Identifier: Apache-2.0 + +description: Fixed partition (one of the partions of a 'fixed-partitions' node). + +compatible: "fixed-partition" + +properties: + label: + type: string + required: false + description: The label / name for this partition. If omitted, the label is taken + from the node name (excluding the unit address). + + read-only: + type: boolean + required: false + description: This parameter, if present, is a hint that this + partition should/ can only be used read-only. + + reg: + type: array + required: false + description: partition offset (address) and size within flash diff --git a/scripts/dts/test-bindings/gpio-2-cell.yaml b/scripts/dts/test-bindings/gpio-2-cell.yaml new file mode 100644 index 0000000000000..2c39f44a7a480 --- /dev/null +++ b/scripts/dts/test-bindings/gpio-2-cell.yaml @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: BSD-3-Clause + +description: GPIO controller with two cells + +compatible: "gpio-two-cell" + +properties: + "#gpio-cells": + type: int + required: true + +gpio-cells: + - gpio_cell_one + - gpio_cell_two diff --git a/scripts/dts/test-bindings/gpio-led.yaml b/scripts/dts/test-bindings/gpio-led.yaml new file mode 100644 index 0000000000000..50d585dd243ce --- /dev/null +++ b/scripts/dts/test-bindings/gpio-led.yaml @@ -0,0 +1,47 @@ +# Copyright (c) 2020 Bobby Noelte +# SPDX-License-Identifier: Apache-2.0 + +description: GPIO LED + +compatible: "gpio-led" + +properties: + gpios: + type: phandle-array + required: true + function: + required: false + type: int + description: Numerical LED functon identifier. + color: + required: false + type: int + description: Numerical LED color identifier + default-state: + required: false + type: int + default: 0 + description: > + The initial state of the LED. Valid values are LED_STATE_OFF (0), + LED_STATE_ON (1), and LED_STATE_KEEP (2). If the LED is already on or + off and the default-state property is set the to same value, then no + glitch should be produced where the LED momentarily turns off (or on). + The LED_STATE_KEEP setting will keep the LED at whatever its current + state is, without producing a glitch. The default is LED_STATE_OFF (0) + if this property is not present. + led-pattern: + required: false + type: array + description: Array of integers with default pattern for certain triggers. + retain-state-suspended: + required: false + type: boolean + description: Retain the state of the LED in suspend state. + retain-state-shutdown: + required: false + type: boolean + description: Retain the state of the LED on shutdown. + panic-indicator: + required: false + type: boolean + description: The LED should be used as a panic indicator. diff --git a/scripts/dts/test-bindings/gpio-leds.yaml b/scripts/dts/test-bindings/gpio-leds.yaml new file mode 100644 index 0000000000000..7e4a910ce9f98 --- /dev/null +++ b/scripts/dts/test-bindings/gpio-leds.yaml @@ -0,0 +1,6 @@ +# Copyright (c) 2020 Bobby Noelte +# SPDX-License-Identifier: Apache-2.0 + +description: GPIO LEDs controller + +compatible: "gpio-leds" diff --git a/scripts/dts/test-bindings/multidir.yaml b/scripts/dts/test-bindings/multidir.yaml index 8504475397f2a..6d4d186c79cf7 100644 --- a/scripts/dts/test-bindings/multidir.yaml +++ b/scripts/dts/test-bindings/multidir.yaml @@ -2,4 +2,4 @@ description: Binding in test-bindings/ -compatible: "in-dir-1" +compatible: "in-dir" diff --git a/scripts/dts/test-bindings/partition.yaml b/scripts/dts/test-bindings/partition.yaml new file mode 100644 index 0000000000000..4239347e04281 --- /dev/null +++ b/scripts/dts/test-bindings/partition.yaml @@ -0,0 +1,25 @@ +# Copyright (c) 2018,2019 Bobby Noelte +# SPDX-License-Identifier: Apache-2.0 + +description: Flash partition definition for fixed partitions + +compatible: "fixed-partitions" + +properties: + "#address-cells": + type: int + required: true + description: > + <1>: for partitions that require a single 32-bit cell to represent their + size/address (aka the value is below 4 GiB) + <2>: for partitions that require two 32-bit cells to represent their + size/address (aka the value is 4 GiB or greater). + + "#size-cells": + type: int + required: false + description: > + <1>: for partitions that require a single 32-bit cell to represent their + size/address (aka the value is below 4 GiB) + <2>: for partitions that require two 32-bit cells to represent their + size/address (aka the value is 4 GiB or greater). diff --git a/scripts/dts/test-bindings/pincfg.yaml b/scripts/dts/test-bindings/pincfg.yaml new file mode 100644 index 0000000000000..ef9386d07818c --- /dev/null +++ b/scripts/dts/test-bindings/pincfg.yaml @@ -0,0 +1,159 @@ +# Copyright (c) 2018..2020 Bobby Noelte +# SPDX-License-Identifier: Apache-2.0 + +description: Pin configuration + +compatible: "pincfg" + +properties: + + groups: + description: > + The list of names of the groups that properties in the node apply to + (either this, "pins", "pinmux" or "pinctrl-pin-array" have to be + specified). + required: false + type: string-array + + pins: + description: > + The list of numeric pin ids that properties in the node apply to + (either this, "groups", "pinmux" or "pinctrl-pin-array" have to be + specified). + required: false + type: array + + function: + required: false + type: string + + pinmux: + description: > + The list of numeric pin ids and their mux settings that properties + in the node apply to (either this, "pins", "groups" or + "pinctrl-pin-array" have to be specified). + required: false + type: array + + pinctrl-pin-array: + description: > + The list of pin controller register index and values (either this, + "pins", "pinmux" or "groups" have to be specified). pinctrl-cells + specifies the number of value cells in addition to the index of the + registers. No other properties shall be in the node. + required: false + type: array + + bias-disable: + description: Disable any pin bias. + required: false + type: boolean + + bias-high-impedance: + required: false + type: boolean + + bias-bus-hold: + required: false + type: boolean + + bias-pull-up: + description: Pull up strength in Ohm. 0 to disable pull up. + required: false + type: int + + bias-pull-down: + description: Pull down strength in Ohm. 0 to disable pull down. + required: false + type: int + + bias-pull-pin-default: + required: false + type: int + + drive-push-pull: + required: false + type: boolean + + drive-open-drain: + required: false + type: boolean + + drive-open-source: + required: false + type: boolean + + drive-strength: + description: > + Sink or source at most X mA. 0 to disable drive strength control. + required: false + type: int + + input-enable: + required: false + type: boolean + + input-debounce: + description: Debounce time in usec. 0 to disable debouncing. + required: false + type: int + + input-disable: + required: false + type: boolean + + input-schmitt-enable: + required: false + type: boolean + + input-schmitt-disable: + required: false + type: boolean + + low-power-enable: + description: Enable low power mode. + required: false + type: boolean + + low-power-disable: + description: Disable low power mode. + required: false + type: boolean + + output-disable: + required: false + type: boolean + + output-enable: + required: false + type: boolean + + output-low: + required: false + type: boolean + + output-high: + required: false + type: boolean + + power-source: + description: Numerical ID of power source to select. + required: false + type: int + default: 0 + + slew-rate: + description: Slew rate + required: false + type: int + default: 0 + + skew-delay: + description: > + Expected clock skew on input pins and the delay before latching + a value to an output pin. + Typically indicated by the number of double-inverters used to + delay the signal. + required: false + type: int + default: 0 diff --git a/scripts/dts/test-bindings/pinctrl-gpio.yaml b/scripts/dts/test-bindings/pinctrl-gpio.yaml new file mode 100644 index 0000000000000..82a2994716903 --- /dev/null +++ b/scripts/dts/test-bindings/pinctrl-gpio.yaml @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: BSD-3-Clause + +description: GPIO for pinctrl test + +compatible: "pinctrl-gpio" + +properties: + "gpio-controller": + type: boolean + required: true + description: Convey's this node is a GPIO controller + + gpio-ranges: + type: phandle-array + required: false + description: gpio range in pin controller + + gpio-ranges-group-names: + type: string-array + required: false + description: gpio range names diff --git a/scripts/dts/test-bindings/pinctrl-state.yaml b/scripts/dts/test-bindings/pinctrl-state.yaml new file mode 100644 index 0000000000000..8e32167581752 --- /dev/null +++ b/scripts/dts/test-bindings/pinctrl-state.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2018..2020 Bobby Noelte +# SPDX-License-Identifier: Apache-2.0 + +description: > + Pinctrl state. A node that bundles a set of pin configuration + nodes that shall be applied in a single pin control operation. + +compatible: "pinctrl-state" diff --git a/scripts/dts/test-bindings/soc-nv-flash.yaml b/scripts/dts/test-bindings/soc-nv-flash.yaml new file mode 100644 index 0000000000000..929045dbe4c88 --- /dev/null +++ b/scripts/dts/test-bindings/soc-nv-flash.yaml @@ -0,0 +1,3 @@ +description: Flash node + +compatible: "soc-nv-flash" diff --git a/scripts/dts/test-multidir.dts b/scripts/dts/test-multidir.dts index 4bd046317d2dd..ee13500aec1ad 100644 --- a/scripts/dts/test-multidir.dts +++ b/scripts/dts/test-multidir.dts @@ -11,9 +11,9 @@ / { in-dir-1 { - compatible = "in-dir-1"; + compatible = "in-dir"; }; in-dir-2 { - compatible = "in-dir-2"; + compatible = "in-dir"; }; }; diff --git a/scripts/dts/test.dts b/scripts/dts/test.dts index 695f925cfe2a7..0bae88ca80295 100644 --- a/scripts/dts/test.dts +++ b/scripts/dts/test.dts @@ -1,6 +1,6 @@ /* * Copyright (c) 2019, Nordic Semiconductor - * + * Copyright (c) 2020, Bobby Noelte * SPDX-License-Identifier: BSD-3-Clause */ @@ -9,6 +9,62 @@ /dts-v1/; / { + // + // Chosen + // + + chosen { + stdout-path = "/a/b"; + }; + + // + // Clocks + // + + clock-test { + clock-provider { + compatible = "clock-provider"; + #clock-cells = <1>; + clock-output-names = "clkout-0", "clkout-1", "clkout-2"; + }; + clock-consumer { + compatible = "clock-consumer"; + clocks = <&{/clock-test/clock-provider} 2 + &{/clock-test/clock-provider} 1>; + clock-names = "clkin-0", "clkin-1"; + }; + }; + + // + // Gpio leds + // + + gpio-leds-test { + leds-controller { + compatible = "gpio-leds"; + + led0 { + gpios = <&{/gpio-leds-test/gpio-0} 0 1>; + default-state = <1>; + color = <99>; + function = <1>; + led-pattern = <255 1000 0 1000>; + }; + led1 { + gpios = <&{/gpio-leds-test/gpio-0} 1 0>; + default-state = <1>; + color = <77>; + function = <2>; + led-pattern = <255 2000 0 2000>; + }; + }; + gpio-0 { + compatible = "gpio-two-cell"; + gpio-controller; + #gpio-cells = <2>; + }; + }; + // // Interrupts // @@ -185,6 +241,51 @@ }; }; + // + // For testing partitions + // + + partition { + flash-controller { + #address-cells = <1>; + #size-cells = <1>; + + flash-0 { + compatible = "soc-nv-flash"; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition-0-0 { + label = "flash-0 partition-0"; + reg = <0 100>; + }; + partition-0-1 { + label = "flash-0 partition-1"; + reg = <100 100>; + }; + }; + }; + }; + flash-1 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition-1-0 { + label = "flash-1 partition-0"; + reg = <0 1000>; + }; + partition-replace-label { + reg = <1000 1000>; + status = "okay"; + }; + }; + }; + }; + // // 'pinctrl-' // @@ -198,9 +299,31 @@ pinctrl-names = "zero", "one", "two"; }; pincontroller { + pin-controller; + gpio-0 { + compatible = "pinctrl-gpio"; + gpio-controller; + gpio-ranges = <&{/pinctrl/pincontroller} 0 0 16>; + gpio-ranges-group-names = "gpio-0"; + }; + gpio-1 { + compatible = "pinctrl-gpio"; + gpio-controller; + gpio-ranges = <&{/pinctrl/pincontroller} 0 16 16>; + gpio-ranges-group-names = "gpio-1"; + }; state-1 { + pincfg-1-1 { + pinmux = <0 1>; + }; + pincfg-1-2 { + pinmux = <1 2>; + }; }; state-2 { + pincfg-2-1 { + groups = "group"; + }; }; }; }; diff --git a/scripts/dts/testedtlib.py b/scripts/dts/testedtlib.py index 4d14209ee6549..d51b2085bfe18 100755 --- a/scripts/dts/testedtlib.py +++ b/scripts/dts/testedtlib.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # Copyright (c) 2019 Nordic Semiconductor ASA +# Copyright (c) 2020 Bobby Noelte # SPDX-License-Identifier: BSD-3-Clause import io @@ -39,6 +40,26 @@ def run(): warning: "#cells:" in test-bindings/deprecated.yaml is deprecated and will be removed - please put 'interrupt-cells:', 'pwm-cells:', 'gpio-cells:', etc., instead. The name should match the name of the corresponding phandle-array property (see binding-template.yaml) """) + # + # Test clocks + # + + verify_streq(edt.get_node("/clock-test/clock-consumer").clocks, + "[, data: OrderedDict([('clock-id', 2)])>, , data: OrderedDict([('clock-id', 1)])>]") + + verify_streq(edt.get_node("/clock-test/clock-provider").clock_outputs, + "[, data: {}>, , data: {}>, , data: {}>]") + + # + # Test gio leds + # + + verify_streq(edt.get_node("/gpio-leds-test/leds-controller").gpio_leds, + "[, data: {'gpios': [, data: OrderedDict([('gpio_cell_one', 0), ('gpio_cell_two', 1)])>], 'function': 1, 'color': 99, 'default-state': 1, 'led-pattern': [255, 1000, 0, 1000], 'retain-state-suspended': False, 'retain-state-shutdown': False, 'panic-indicator': False}>, , data: {'gpios': [, data: OrderedDict([('gpio_cell_one', 1), ('gpio_cell_two', 0)])>], 'function': 2, 'color': 77, 'default-state': 1, 'led-pattern': [255, 2000, 0, 2000], 'retain-state-suspended': False, 'retain-state-shutdown': False, 'panic-indicator': False}>]") + + verify_streq(edt.get_node("/gpio-leds-test/gpio-0").gpio_leds, + "[]") + # # Test interrupts # @@ -74,12 +95,74 @@ def run(): verify_streq(edt.get_node("/reg-nested-ranges/grandparent/parent/node").regs, "[]") + # + # Test partitions + # + + verify_streq(edt.get_node("/partition/flash-controller/flash-0").partitions, + "[, controller: , partitions: [], addr: 0, size: 100, attributes: {'read-only': False}>, , controller: , partitions: [], addr: 100, size: 100, attributes: {'read-only': False}>]") + + verify_streq(edt.get_node("/partition/flash-controller/flash-0/partitions/partition-0-0").flash_controller, + "") + + verify_streq(edt.get_node("/partition/flash-controller").flash_controller, + "None") + + verify_streq(edt.get_node("/partition/flash-1").partitions, + "[, controller: , partitions: [], addr: 0, size: 1000, attributes: {'read-only': False}>, , controller: , partitions: [], addr: 1000, size: 1000, attributes: {'read-only': False}>]") + + verify_streq(edt.get_node("/partition/flash-1/partitions/partition-1-0").flash_controller, + "") + # # Test 'pinctrl-' # verify_streq(edt.get_node("/pinctrl/dev").pinctrls, - "[, ]>, , ]>]") + "[, ]>, , ]>]") + + # + # Test pinctrl states of pin controller node (and other) + # + + verify_streq(edt.get_node("/pinctrl/pincontroller").pinctrl_states, + "[, ]") + + verify_streq(edt.get_node("/pinctrl/dev").pinctrl_states, + "[]") + + # + # Test pinctrl states of pin control gpio ranges node (and other) + # + + verify_streq(edt.get_node("/pinctrl/pincontroller").pinctrl_gpio_ranges, + "[, data: OrderedDict([('gpio-base', 0), ('pinctrl-base', 0), ('count', 16)])>, , data: OrderedDict([('gpio-base', 0), ('pinctrl-base', 16), ('count', 16)])>]") + + verify_streq(edt.get_node("/pinctrl/pincontroller/gpio-0").pinctrl_gpio_ranges, + "[]") + + verify_streq(edt.get_node("/pinctrl/dev").pinctrl_gpio_ranges, + "[]") + + # + # Test pin controller of pin control state node + # + + verify_streq(edt.get_node("/pinctrl/pincontroller/state-1").pin_controller, + "") + + # + # Test pincfgs of pinctrl state node (and other) + # + + verify_streq(edt.get_node("/pinctrl/pincontroller/state-1").pincfgs, + "[, ]") + + verify_streq(edt.get_node("/pinctrl/pincontroller/state-2").pincfgs, + "[]") + + verify_streq(edt.get_node("/pinctrl/pincontroller").pincfgs, + "[]") # # Test Node.parent and Node.children @@ -230,12 +313,29 @@ def run(): # # Test having multiple directories with bindings, with a different .dts file # + warnings = io.StringIO() + edt = edtlib.EDT("test-multidir.dts", ["test-bindings", "test-bindings-2"], warnings) - edt = edtlib.EDT("test-multidir.dts", ["test-bindings", "test-bindings-2"]) + verify_eq(warnings.getvalue(), """\ +warning: multiple candidates for binding file 'multidir.yaml': skipping 'test-bindings-2/multidir.yaml' and using 'test-bindings/multidir.yaml' +""") verify_streq(edt.get_node("/in-dir-1").binding_path, "test-bindings/multidir.yaml") + verify_streq(edt.get_node("/in-dir-2").binding_path, + "test-bindings/multidir.yaml") + + warnings = io.StringIO() + edt = edtlib.EDT("test-multidir.dts", ["test-bindings-2", "test-bindings"], warnings) + + verify_eq(warnings.getvalue(), """\ +warning: multiple candidates for binding file 'multidir.yaml': skipping 'test-bindings/multidir.yaml' and using 'test-bindings-2/multidir.yaml' +""") + + verify_streq(edt.get_node("/in-dir-1").binding_path, + "test-bindings-2/multidir.yaml") + verify_streq(edt.get_node("/in-dir-2").binding_path, "test-bindings-2/multidir.yaml") @@ -250,6 +350,21 @@ def run(): if edt.get_node("/in-dir-1") not in edt.get_node("/").required_by: fail("/in-dir-1 should directly depend on /") + # + # Test error messages from chosen binding + # + + verify_error(""" +/dts-v1/; + +/ { + chosen { + error = "error out"; + }; +}; +""", "'error' appears in /chosen in error.dts, but is not declared in 'properties:' in test-bindings/chosen.yaml", + ["test-bindings"]) + # # Test error messages from _slice() # @@ -303,7 +418,7 @@ def run(): print("all tests passed") -def verify_error(dts, error): +def verify_error(dts, error, bindings = []): # Verifies that parsing a file with the contents 'dts' (a string) raises an # EDTError with the message 'error' @@ -315,7 +430,7 @@ def verify_error(dts, error): f.write(dts) f.flush() # Can't have unbuffered text IO, so flush() instead try: - edtlib.EDT("error.dts", []) + edtlib.EDT("error.dts", bindings) except edtlib.EDTError as e: if str(e) != error: fail(f"expected the EDTError '{error}', got the EDTError '{e}'")