hal_atmel/scripts/sampinctrl.py

285 lines
8.1 KiB
Python

# 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 <dt-bindings/pinctrl/atmel_sam_pinctrl.h>"}\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)