""" Utility to autogenerate pinctrl definitions. Usage:: python3 gd32pinctrl.py [-i /path/to/configs] [-o /path/to/include] Copyright (c) 2021 Teslabs Engineering S.L. SPDX-License-Identifier: Apache 2.0 """ import argparse from collections import OrderedDict from pathlib import Path import re import yaml REPO_ROOT = Path(__file__).absolute().parents[1] """Repository root.""" AFIO_REMAP_SUFFIXES = { 0: ("NORMP",), 2: ("NORMP", "RMP"), 3: ("NORMP", "PRMP", "FRMP"), 4: ("NORMP", "PRMP1", "PRMP2", "FRMP"), } """AFIO remap suffixes (# remap options <> suffix).""" AFIO_MODE_NAMES = { "analog": "ANALOG", "inp": "GPIO_IN", "out": "ALTERNATE", } """AFIO mode names.""" HEADER = """/* * Autogenerated file * * SPDX-License-Identifier: Apache 2.0 */ """ """Header for the generated files.""" def get_header_fname(series, variant): """Get header file name. Args: series: Series. variant: Variant information. Returns: Header file name. """ pincode = variant["pincode"].lower() memories = f"({'-'.join((str(m).lower() for m in variant['memories']))})" return f"{series}{pincode}{memories}xx-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) if not m: raise ValueError(f"Unexpected pin name: {pin_name}") return m.group(1), m.group(2) def generate_afio_header(outdir, variant, series, pin_cfgs): """Generate AFIO header with pin configurations. Args: outdir: Output base directory. variant: Variant information. series: Series. pin_cfgs: Pin configurations. """ pin_cfgs = OrderedDict(sorted(pin_cfgs.items(), key=lambda kv: kv[0])) with open(outdir / get_header_fname(series, variant), "w") as f: f.write(HEADER) f.write(f"\n#include \"{series}xx-afio.h\"\n") for signal, cfg in pin_cfgs.items(): f.write(f"\n/* {signal} */\n") for port, pin, mode, name_suffix, remap in cfg: define = f"#define {signal}_P{port}{pin}{name_suffix}" define_val = f"GD32_PINMUX_AFIO('{port}', {pin}, {mode}, {remap})" f.write(f"{define} \\\n\t{define_val}\n") def generate_af_header(outdir, variant, series, pin_cfgs): """Generate AF header with pin configurations. Args: outdir: Output base directory. variant: Variant information. series: Series. pin_cfgs: Pin configurations. """ pin_cfgs = OrderedDict(sorted(pin_cfgs.items(), key=lambda kv: kv[0])) with open(outdir / get_header_fname(series, variant), "w") as f: f.write(HEADER) f.write("\n#include \"gd32-af.h\"\n") for signal, cfg in pin_cfgs.items(): f.write(f"\n/* {signal} */\n") for port, pin, mode in cfg: define = f"#define {signal}_P{port}{pin}" define_val = f"GD32_PINMUX_AF('{port}', {pin}, {mode})" f.write(f"{define} \\\n\t{define_val}\n") def build_afio_pin_cfgs(variant, signal_configs, pins, remaps): """Build AFIO pin configurations. Args: variant: Variant information. signal_configs: Signal configurations. pins: Pins description. remaps: Remaps description. Returns: Dictionary with pins configuration. """ pin_cfgs = {"ANALOG": []} pincode = variant["pincode"] memories = variant["memories"] for signal, signal_cfg in signal_configs.items(): # check if signal is excluded from current pincode if pincode in signal_cfg.get("exclude-pincodes", []): continue # check if signal is excluded from current list of memories if set(memories).intersection(signal_cfg.get("exclude-memories", [])): continue signal_pins = {} # collect all afs for pin, pin_cfg in pins.items(): if pincode not in pin_cfg["pincodes"]: continue if signal in pin_cfg["afs"]: signal_pins[pin] = [0] # collect all remaps remap_options = 0 signal_remaps = remaps.get(signal) if signal_remaps: for pin in signal_remaps["pins"]: if not pin: continue if pincode in pins[pin]["pincodes"]: if pin not in signal_pins: signal_pins[pin] = [] if remap_options not in signal_pins[pin]: signal_pins[pin].append(remap_options) remap_options += 1 for pin, remap_values in signal_pins.items(): for remap_value in remap_values: for mode in signal_cfg["modes"]: port, pin_number = get_port_pin(pin) remap = AFIO_REMAP_SUFFIXES[remap_options][remap_value] name_suffix = "" if len(signal_cfg["modes"]) > 1: name_suffix = f"_{mode.upper()}" if remap_options > 0: name_suffix += f"_{remap}" remap = signal.split("_")[0] + f"_{remap}" if signal not in pin_cfgs: pin_cfgs[signal] = [] pin_cfgs[signal].append( ( port, pin_number, AFIO_MODE_NAMES[mode], name_suffix, remap, ) ) # add analog entries (used for low power mode) for pin, pin_cfg in pins.items(): if pincode not in pin_cfg["pincodes"]: continue port, pin_number = get_port_pin(pin) pin_cfgs["ANALOG"].append((port, pin_number, "ANALOG", "", "NORMP")) return pin_cfgs def build_af_pin_cfgs(variant, signal_configs, pins): """Build AF pin configurations. Args: variant: Variant information. signal_configs: Signals description. pins: Pins description. Returns: Dictionary with pins configuration. """ pin_cfgs = {"ANALOG": []} pincode = variant["pincode"] memories = variant["memories"] for pin, pin_cfg in pins.items(): if pincode not in pin_cfg["pincodes"]: continue port, pin_number = get_port_pin(pin) # add analog entry (used for low power mode) pin_cfgs["ANALOG"].append((port, pin_number, "ANALOG")) for signal, mode in pin_cfg["afs"].items(): signal_config = signal_configs.get(signal) # check if signal is excluded from current pincode. if signal_config and pincode in signal_config.get( "exclude-pincodes", [] ): continue # check if signal is excluded from current list of memories if signal_config and set(memories).intersection( signal_config.get("exclude-memories", []) ): continue if signal not in pin_cfgs: pin_cfgs[signal] = [] if mode != "ANALOG": mode = f"AF{mode}" pin_cfgs[signal].append((port, pin_number, mode)) 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("gd32*-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"] series = config["series"] variants = config["variants"] signal_configs = config.get("signal-configs", {}) pins = config["pins"] if model == "afio": remaps = config["remaps"] for variant in variants: pin_cfgs = build_afio_pin_cfgs(variant, signal_configs, pins, remaps) generate_afio_header(outdir, variant, series, pin_cfgs) elif model == "af": for variant in variants: pin_cfgs = build_af_pin_cfgs(variant, signal_configs, pins) generate_af_header(outdir, variant, series, pin_cfgs) 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)