# Copyright (c) 2021 Teslabs Engineering S.L. # Copyright (c) 2022 Gerson Fernando Budke # SPDX-License-Identifier: Apache-2.0 """ Utility to autogenerate pinctrl definitions. Usage:: python3 sampinctrl.py [-i /path/to/configs] [-o /path/to/include] """ import argparse from collections import OrderedDict from pathlib import Path import re from natsort import natsorted import yaml REPO_ROOT = Path(__file__).absolute().parents[1] """Repository root.""" HEADER = """/* * Autogenerated file * * SPDX-License-Identifier: Apache-2.0 */ """ """Header for the generated files.""" EXCEPTION = """ /* * WARNING: this variant has package exception. * * Read datasheet topics related to I/O Multiplexing and Considerations or * Peripheral Signal Multiplexing on I/O Lines for more information. */ """ def get_header_fname(serie, variant, revision): """Get header file name. Args: family: Atmel SAM family. serie: Series. variant: Variant information. Returns: Header file name. """ sufix = "" if revision: sufix = f"X{revision}" return f"sam{serie}{variant}{sufix}-pinctrl.h" def get_port_pin(pin_name): """Obtain port and pin number from a pin name Args: pin_name: Pin name, e.g. PA0 Returns: Port and pin, e.g. A, 0. """ m = re.match(r"P([A-Z])(\d+)", pin_name.upper()) if not m: raise ValueError(f"Unexpected pin name: {pin_name}") return m.group(1), str(int(m.group(2))) def write_gpio_function(f, port, pin_num, fmap, function): f.write(f"\n/* p{port.lower()}{pin_num}_{function.lower()} */\n") define = f"#define P{port.upper()}{pin_num.upper()}_{function.upper()}" define_val = f"{fmap}({port.lower()}, {pin_num}, {function.lower()}, " \ f"{function.lower()})" f.write(f"{define} \\\n\t{define_val}\n") def write_wakeup_function(f, port, pin_num, pinmux, periph, signal, fmap, function): f.write(f"\n/* p{port.lower()}{pin_num}{pinmux}_{periph}_{signal} " f"*/\n") define = f"#define P{port.upper()}{pin_num.upper()}" \ f"{pinmux.upper()}_{periph.upper()}_{signal.upper()}" define_val = f"{fmap}({port.lower()}, {pin_num}, " \ f"{signal.lower()}, {function.lower()})" f.write(f"{define} \\\n\t{define_val}\n") def write_periph_function(f, port, pin_num, pinmux, periph, signal, fmap, function): f.write(f"\n/* p{port.lower()}{pin_num}{pinmux}_{periph}_{signal} " f"*/\n") define = f"#define P{port.upper()}{pin_num.upper()}" \ f"{pinmux.upper()}_{periph.upper()}_{signal.upper()}" define_val = f"{fmap}({port.lower()}, {pin_num}, " \ f"{pinmux.lower()}, {function.lower()})" f.write(f"{define} \\\n\t{define_val}\n") def generate_atmel_sam_header(outdir, family, fmap, serie, variant, pin_cfgs, revision): """Generate Atmel SAM header with pin configurations. Args: outdir: Output base directory. family: Atmel SAM family. fmap: Function to map pinctrl. series: MCU Series. variant: Variant information. pin_cfgs: Pin configurations. """ ofname = outdir / get_header_fname(serie, variant["pincode"], revision) with open(ofname, "w") as f: f.write(HEADER) f.write(f'\n{"#include "}\n') if len(variant) > 2: if variant["exception"]: f.write(EXCEPTION) for port, pin_num, pinmux, periph, signal, function in pin_cfgs: if function in ["gpio", "lpm"]: write_gpio_function(f, port, pin_num, fmap, function) continue if function in ["wakeup"]: write_wakeup_function(f, port, pin_num, pinmux, periph, signal, fmap, function) continue write_periph_function(f, port, pin_num, pinmux, periph, signal, fmap, function) def build_atmel_sam_gpio_sets(pin_cfgs, pin): """Build Atmel SAM pin configurations sets. Args: pins: Pins description. Returns: Dictionary with pins configuration. """ port, pin_num = get_port_pin(pin) new_item = (port, pin_num, "a", "gpio", "gpio", "gpio") if new_item not in pin_cfgs: pin_cfgs.append(new_item) def build_atmel_sam_sets(pin_cfgs, pin, pin_lst, serie, variant, function): """Build Atmel SAM pin configurations sets. Args: serie: MCU Serie. variant: Variant information. pins: Pins description. Returns: Dictionary with pins configuration. """ if len(pin_lst[0]) > 0: for pinmux, periph, signal, *excludes in pin_lst: if len(excludes) > 0: if serie in excludes[0]: continue if variant["pincode"] in excludes[0]: continue port, pin_num = get_port_pin(pin) pin_cfgs.append((port, pin_num, pinmux, periph, signal, function)) def build_atmel_sam_pin_cfgs(serie, variant, pins): """Build Atmel SAM pin configurations. Args: serie: MCU Serie. variant: Variant information. pins: Pins description. Returns: Dictionary with pins configuration. """ pin_cfgs = [] pins = OrderedDict(natsorted(pins.items(), key=lambda kv: kv[0])) for pin, pin_cfg in pins.items(): if variant["pincode"] not in pin_cfg["pincodes"]: continue build_atmel_sam_gpio_sets(pin_cfgs, pin) if "periph" in pin_cfg.keys(): build_atmel_sam_sets(pin_cfgs, pin, pin_cfg["periph"], serie, variant, "periph") if "extra" in pin_cfg.keys(): build_atmel_sam_sets(pin_cfgs, pin, pin_cfg["extra"], serie, variant, "extra") if "system" in pin_cfg.keys(): build_atmel_sam_sets(pin_cfgs, pin, pin_cfg["system"], serie, variant, "system") if "lpm" in pin_cfg.keys(): build_atmel_sam_sets(pin_cfgs, pin, pin_cfg["lpm"], serie, variant, "lpm") if "wakeup" in pin_cfg.keys(): build_atmel_sam_sets(pin_cfgs, pin, pin_cfg["wakeup"], serie, variant, "wakeup") return pin_cfgs def main(indir, outdir) -> None: """Entry point. Args: indir: Directory with pin configuration files. outdir: Output directory """ if outdir.exists(): for entry in outdir.glob("sam*-pinctrl.h"): entry.unlink() else: outdir.mkdir() for entry in indir.iterdir(): if not entry.is_file() or entry.suffix not in (".yml", ".yaml"): continue config = yaml.load(open(entry), Loader=yaml.Loader) model = config["model"] family = config["family"] fmap = config["map"] series = config["series"] variants = config["variants"] has_rev = "revisions" in config.keys() pins = config["pins"] if model == "atmel,sam": for serie in series: for variant in [v for v in variants if serie in v["series"]]: pin_cfgs = build_atmel_sam_pin_cfgs(serie, variant, pins) rev = config["revisions"].get(serie) if has_rev else None generate_atmel_sam_header(outdir, family, fmap, serie, variant, pin_cfgs, rev) else: raise ValueError(f"Unexpected model: {model}") if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument( "-i", "--indir", type=Path, default=REPO_ROOT / "pinconfigs", help="Directory with pin configuration files", ) parser.add_argument( "-o", "--outdir", type=Path, default=REPO_ROOT / "include" / "dt-bindings" / "pinctrl", help="Output directory", ) args = parser.parse_args() main(args.indir, args.outdir)