From cf877e1535a6d5a3c9400e1c33b711ce1a9ceb9f Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Wed, 19 Jun 2024 11:18:29 +0200 Subject: [PATCH 1/3] scripts: genpinctrl: support non-Git pin data folder This commit adds error checking to the call to `git rev-parse HEAD`, to ensure genpinctrl does not crash when the pin data folder is not a Git repository. In such cases, "" is written to the pinctrl README file. Signed-off-by: Mathieu Choplain --- scripts/genpinctrl/genpinctrl.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/genpinctrl/genpinctrl.py b/scripts/genpinctrl/genpinctrl.py index a7f6d3ab2..a44cedd29 100644 --- a/scripts/genpinctrl/genpinctrl.py +++ b/scripts/genpinctrl/genpinctrl.py @@ -17,7 +17,7 @@ from pathlib import Path import re import shutil -from subprocess import check_output +from subprocess import check_output, STDOUT, CalledProcessError import xml.etree.ElementTree as ET from jinja2 import Environment, FileSystemLoader @@ -598,8 +598,12 @@ def main(data_path, output): ) # write readme file - commit_raw = check_output(["git", "rev-parse", "HEAD"], cwd=data_path) - commit = commit_raw.decode("utf-8").strip() + try: + commit_raw = check_output( + ["git", "rev-parse", "HEAD"], cwd=data_path, stderr=STDOUT) + commit = commit_raw.decode("utf-8").strip() + except CalledProcessError: + commit = "" with open(output / "README.rst", "w") as f: f.write(readme_template.render(commit=commit)) From d39c65b20bca2f1d6ce048864c347693f2010f69 Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Wed, 19 Jun 2024 15:13:38 +0200 Subject: [PATCH 2/3] scripts: genpinctrl: add family filtering support This commit adds a family filtering feature to genpinctrl. Filtering can be used in two modes: * in "inclusive" mode, pinctrl is generated for specified families only * in "exclusive" mode, pinctrl is generated for all but specified families This is useful when introducing new series. Signed-off-by: Mathieu Choplain --- scripts/genpinctrl/genpinctrl.py | 94 +++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 3 deletions(-) diff --git a/scripts/genpinctrl/genpinctrl.py b/scripts/genpinctrl/genpinctrl.py index a44cedd29..aae17fe9c 100644 --- a/scripts/genpinctrl/genpinctrl.py +++ b/scripts/genpinctrl/genpinctrl.py @@ -79,6 +79,57 @@ """Allowed pin modifiers""" +class FamilyFilter(): + def _prepare(self, filters, excluding: bool): + family_list = [] + + self.excluding = excluding + + for filter in filters: + family_name = "STM32" + \ + filter.upper().removeprefix("STM32") + + family_list.append(family_name) + + self.filtered_families_list = family_list + + def __init__(self): + self.filtered_families_list = [] + + def set_filters(self, allow_filter, forbid_filter): + if allow_filter is not None: + self.filter_list = self._prepare( + allow_filter, False) + elif forbid_filter is not None: + self.filter_list = self._prepare( + forbid_filter, True) + + def is_active(self) -> bool: + """Is the filter active?""" + return len(self.filtered_families_list) > 0 + + def should_skip_model(self, model: str) -> bool: + """Should processing of STM32 model be skipped? + + model: + STM32 model string (any string that starts + with 'STM32yyy' where yyy is family code) + """ + if not self.is_active(): + return False + + for family in self.filtered_families_list: + if model.startswith(family): + # Skip if we found and this is exclude list + return self.excluding + # Skip if not found and this is include list + return not self.excluding + + +FAMILY_FILTER = FamilyFilter() +"""STM32 family selection filter""" + + def validate_config_entry(entry, family): """Validates pin configuration entry. @@ -271,6 +322,9 @@ def get_gpio_ip_afs(data_path): m = re.search(r"GPIO-(.*)_Modes.xml", gpio_file.name) gpio_ip = m.group(1) + if FAMILY_FILTER.should_skip_model(gpio_ip): + continue + gpio_ip_entries = dict() results[gpio_ip] = gpio_ip_entries @@ -382,6 +436,9 @@ def get_mcu_signals(data_path, gpio_ip_afs): results = dict() for mcu_file in mcus_path.glob("STM32*.xml"): + if FAMILY_FILTER.should_skip_model(mcu_file.name): + continue + mcu_tree = ET.parse(mcu_file) mcu_root = mcu_tree.getroot() @@ -506,16 +563,19 @@ def main(data_path, output): gpio_ip_afs = get_gpio_ip_afs(data_path) mcu_signals = get_mcu_signals(data_path, gpio_ip_afs) - if output.exists(): + # erase output if we're about to generate for all families + if output.exists() and not FAMILY_FILTER.is_active(): shutil.rmtree(output) - output.mkdir(parents=True) + output.mkdir(parents=True) for family, refs in mcu_signals.items(): # obtain family pinctrl address pinctrl_addr = PINCTRL_ADDRESSES.get(family.lower()) if not pinctrl_addr: - logger.error(f"Unsupported family: {family}") + logger.warning(f"Skipping unsupported family {family}.") continue + else: + logger.info(f"Processing family {family}...") # create directory for each family family_dir = output / "st" / family.lower()[5:] @@ -624,6 +684,34 @@ def main(data_path, output): default=REPO_ROOT / "dts", help="Output directory", ) + parser.add_argument( + "-v", + "--verbose", + action="store_true", + help="Make script verbose" + ) + filter_group = parser.add_mutually_exclusive_group() + filter_group.add_argument( + "-f", + "--only-family", + type=str, + action="append", + help="process only specified STM32 family " + "(can be specified multiple times)" + ) + filter_group.add_argument( + "-nf", + "--not-family", + type=str, + action="append", + help="don't process specified STM32 family " + "(can be specified multiple times)" + ) args = parser.parse_args() + logger.setLevel(logging.INFO if args.verbose else logging.WARN) + logger.addHandler(logging.StreamHandler()) + + FAMILY_FILTER.set_filters(args.only_family, args.not_family) + main(args.data_path, args.output) From 9ad2f2a33004d317cb9ea4f3a78091db919821f1 Mon Sep 17 00:00:00 2001 From: Mathieu Choplain Date: Wed, 19 Jun 2024 15:19:16 +0200 Subject: [PATCH 3/3] scripts: genpinctrl: detect XML namespace automatically This commit modifies the genpinctrl script to detect the XML namespace of pin data files automatically. Signed-off-by: Mathieu Choplain --- scripts/genpinctrl/genpinctrl.py | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/scripts/genpinctrl/genpinctrl.py b/scripts/genpinctrl/genpinctrl.py index aae17fe9c..4f2db6201 100644 --- a/scripts/genpinctrl/genpinctrl.py +++ b/scripts/genpinctrl/genpinctrl.py @@ -536,6 +536,48 @@ def get_mcu_signals(data_path, gpio_ip_afs): return results +def detect_xml_namespace(data_path: Path): + """ + Attempt to detect the XML namespace used in the pindata files automatically. + This removes the need to modify this file when using pin data from sources + other than the official ST repository, which may use a different xmlns. + """ + global NS + + mcus_path = data_path / "mcu" + try: + sampled_file = next(mcus_path.glob("STM32*.xml")) + except StopIteration: + # No STM32*.xml file found. Log a warning but continue script execution. + # If this really isn't a pindata folder, something else will panic later on. + logger.warn(f"No STM32*.xml found in {data_path!s} - XMLNS detection skipped") + return + + with open(sampled_file, "r") as fd: + line = "" + xmlns = None + while len(line) > 0: + line = fd.readline().removeprefix("<").removesuffix(">\n") + + # '' tag sets XML namespace + if line.startswith("Mcu"): + # Find the XML namespace in tag elements + for e in line.split(): + if e.startswith("xmlns="): + xmlns = e + break + break + + if xmlns is None: + logger.info(f"Could not determine XML namespace from {sampled_file}") + return + else: + xml_namespace_url = xmlns.removeprefix('xmlns="').removesuffix('"') + NS = "{" + xml_namespace_url + "}" + + logger.info(f"Using {NS} as XML namespace.") + + def main(data_path, output): """Entry point. @@ -560,6 +602,8 @@ def main(data_path, output): pinctrl_template = env.get_template(PINCTRL_TEMPLATE) readme_template = env.get_template(README_TEMPLATE) + detect_xml_namespace(data_path) + gpio_ip_afs = get_gpio_ip_afs(data_path) mcu_signals = get_mcu_signals(data_path, gpio_ip_afs)