# Copyright (c) 2024 Gerson Fernando Budke # SPDX-License-Identifier: Apache-2.0 """ Utility to autogenerate pinctrl definitions. Usage:: python3 bflbpinctrl.py [-i /path/to/configs] [-o /path/to/include] """ import argparse from collections import OrderedDict from datetime import date from pathlib import Path import re from natsort import natsorted import yaml REPO_ROOT = Path(__file__).absolute().parents[1] """Repository root.""" current_date = date.today() HEADER = """/* * Autogenerated file * Copyright (c) {} BFLB Pinctrl Generator * SPDX-License-Identifier: Apache-2.0 */ """.format(current_date.year) """Header for the generated files.""" def get_header_fname(serie): """Get header file name. Args: serie: Series. Returns: Header file name. """ return f"bl{serie}x-pinctrl.h" def get_port_pin(pin_name): """Obtain port and pin number from a pin name Args: pin_name: Pin name, e.g. GPIO0 Returns: Port and pin, e.g. GPIO, 0. """ m = re.match(r"(GPIO)(\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 get_normalized_name(name): sname_norm = name.replace(".", "_") sname_norm = sname_norm.replace("-", "_") return sname_norm.upper() def write_periph_index(f, periph, value): define = f"#define BFLB_PINMUX_FUN_INST_{periph.lower()}" define_val = f"{value:#06x}" f.write(f"{define:<56}{define_val}\n") def write_signal_index(f, signal, value): define = f"#define BFLB_PINMUX_SIGNAL_{signal.lower()}" define_val = f"{value:#04x}" f.write(f"{define:<56}{define_val}\n") def generate_bflb_series_header(outdir, family, peripherals, signals, signal_cfgs): """Generate Bouffalo Lab header with pin configurations. Args: outdir: Output base directory. family: Bouffalo Lab family. peripherals: The peripheral itself. signals: Signals. signal_cfgs: Signal configurations. """ sname = f"{family}-pinctrl.h" sname_norm = get_normalized_name(sname) ofname = outdir / sname with open(ofname, "w") as f: f.write(HEADER) f.write(f"\n#ifndef DT_BINDINGS_PINCTRL_BFLB_{sname_norm}_") f.write(f"\n#define DT_BINDINGS_PINCTRL_BFLB_{sname_norm}_\n\n") for periph, index, *instancies in peripherals: if instancies: for inst in instancies: write_periph_index(f, periph + str(inst), index + inst * 256) continue write_periph_index(f, periph, index) f.write("\n") for signal in signal_cfgs: value = 0 if signal in signals.keys(): value = signals[signal] write_signal_index(f, signal, value) f.write(f"\n#endif /* DT_BINDINGS_PINCTRL_BFLB_{sname_norm}_ */\n") def write_periph_function(f, port, pin_num, periph, signal, fmap, function): f.write(f"\n/* {port.lower()}{pin_num}_{periph}_{signal} " f"*/\n") define = f"#define {port.upper()}{pin_num.upper()}_{periph.upper()}_{signal.upper()}" define_val = f"{fmap}({pin_num}, {periph.lower()}, {signal.lower()}, {function.lower()})" f.write(f"{define} \\\n\t{define_val}\n") def generate_bflb_bl_header(outdir, family, fmap, serie, pin_cfgs): """Generate Bouffalo Lab header with pin configurations. Args: outdir: Output base directory. family: Bouffalo Lab family. fmap: Function to map pinctrl. series: MCU Series. pin_cfgs: Pin configurations. """ sname = get_header_fname(serie) sname_norm = sname.replace(".", "_") sname_norm = sname_norm.replace("-", "_") sname_norm = sname_norm.upper() ofname = outdir / sname with open(ofname, "w") as f: f.write(HEADER) f.write(f"\n#ifndef DT_BINDINGS_PINCTRL_BFLB_{sname_norm}_") f.write(f"\n#define DT_BINDINGS_PINCTRL_BFLB_{sname_norm}_\n") f.write(f"\n#include ") f.write("\n#include \n") for port, pin_num, periph, signal, function in pin_cfgs: write_periph_function(f, port, pin_num, periph, str(signal), fmap, function) f.write(f"\n#endif /* DT_BINDINGS_PINCTRL_BFLB_{sname_norm}_ */\n") def build_bflb_bl_sets(pin_cfgs, pin, pin_lst, serie, function): """Build Bouffalo Lab pin configurations sets. Args: pin_cfgs: Pin Configuration. pin: Pin description. pin_lst: Pins List. serie: MCU Serie. function: Function of the pin. Returns: Dictionary with pins configuration. """ if len(pin_lst[0]) > 0: for periph, signals in pin_lst: port, pin_num = get_port_pin(pin) for signal in signals: pin_cfgs.append((port, pin_num, periph, signal, function)) def build_bflb_bl_pin_cfgs(serie, pins): """Build Bouffalo Lab BLxxx pin configurations. Args: serie: MCU Series. 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 serie not in pin_cfg["series"]: continue if "periph" in pin_cfg.keys(): build_bflb_bl_sets(pin_cfgs, pin, pin_cfg["periph"], serie, "periph") if "analog" in pin_cfg.keys(): build_bflb_bl_sets(pin_cfgs, pin, pin_cfg["analog"], serie, "analog") if "input" in pin_cfg.keys(): build_bflb_bl_sets(pin_cfgs, pin, pin_cfg["input"], serie, "input") if "output" in pin_cfg.keys(): build_bflb_bl_sets(pin_cfgs, pin, pin_cfg["output"], serie, "output") return pin_cfgs def generate_bflb_generate_signal_list(pins): signal_cfgs = [] for _, pin_cfg in pins.items(): for prop, periph_signal_lst in pin_cfg.items(): if prop == "series": continue for _, signal_lst in periph_signal_lst: for signal in signal_lst: if signal not in signal_cfgs: signal_cfgs.append(signal) signal_cfgs = natsorted(signal_cfgs, key=lambda kv: kv[0]) return signal_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("bl*-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"] peripherals = config["peripherals"] signals = config["signals"] pins = config["pins"] if model == "bflb,bl": signal_cfgs = generate_bflb_generate_signal_list(pins) signals = OrderedDict(natsorted(signals, key=lambda kv: kv[0])) generate_bflb_series_header(outdir, family, peripherals, signals, signal_cfgs) for serie in series: pin_cfgs = build_bflb_bl_pin_cfgs(serie, pins) generate_bflb_bl_header(outdir, family, fmap, serie, 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" / "zephyr" / "dt-bindings" / "pinctrl", help="Output directory", ) args = parser.parse_args() main(args.indir, args.outdir)