1# Copyright (c) 2021 Teslabs Engineering S.L. 2# Copyright (c) 2022 Gerson Fernando Budke 3# SPDX-License-Identifier: Apache-2.0 4 5""" 6Utility to autogenerate pinctrl definitions. 7 8Usage:: 9 python3 sampinctrl.py [-i /path/to/configs] [-o /path/to/include] 10""" 11 12import argparse 13from collections import OrderedDict 14from pathlib import Path 15import re 16 17from natsort import natsorted 18import yaml 19 20 21REPO_ROOT = Path(__file__).absolute().parents[1] 22"""Repository root.""" 23 24HEADER = """/* 25 * Autogenerated file 26 * 27 * SPDX-License-Identifier: Apache-2.0 28 */ 29""" 30"""Header for the generated files.""" 31 32EXCEPTION = """ 33/* 34 * WARNING: this variant has package exception. 35 * 36 * Read datasheet topics related to I/O Multiplexing and Considerations or 37 * Peripheral Signal Multiplexing on I/O Lines for more information. 38 */ 39""" 40 41 42def get_header_fname(serie, variant, revision): 43 """Get header file name. 44 45 Args: 46 family: Atmel SAM family. 47 serie: Series. 48 variant: Variant information. 49 50 Returns: 51 Header file name. 52 """ 53 54 sufix = "" 55 if revision: 56 sufix = f"X{revision}" 57 58 return f"sam{serie}{variant}{sufix}-pinctrl.h" 59 60 61def get_port_pin(pin_name): 62 """Obtain port and pin number from a pin name 63 64 Args: 65 pin_name: Pin name, e.g. PA0 66 67 Returns: 68 Port and pin, e.g. A, 0. 69 """ 70 71 m = re.match(r"P([A-Z])(\d+)", pin_name.upper()) 72 if not m: 73 raise ValueError(f"Unexpected pin name: {pin_name}") 74 75 return m.group(1), str(int(m.group(2))) 76 77 78def write_gpio_function(f, port, pin_num, fmap, function): 79 f.write(f"\n/* p{port.lower()}{pin_num}_{function.lower()} */\n") 80 define = f"#define P{port.upper()}{pin_num.upper()}_{function.upper()}" 81 define_val = f"{fmap}({port.lower()}, {pin_num}, {function.lower()}, " \ 82 f"{function.lower()})" 83 f.write(f"{define} \\\n\t{define_val}\n") 84 85 86def write_wakeup_function(f, port, pin_num, pinmux, periph, 87 signal, fmap, function): 88 f.write(f"\n/* p{port.lower()}{pin_num}{pinmux}_{periph}_{signal} " 89 f"*/\n") 90 define = f"#define P{port.upper()}{pin_num.upper()}" \ 91 f"{pinmux.upper()}_{periph.upper()}_{signal.upper()}" 92 define_val = f"{fmap}({port.lower()}, {pin_num}, " \ 93 f"{signal.lower()}, {function.lower()})" 94 f.write(f"{define} \\\n\t{define_val}\n") 95 96 97def write_periph_function(f, port, pin_num, pinmux, periph, 98 signal, fmap, function): 99 f.write(f"\n/* p{port.lower()}{pin_num}{pinmux}_{periph}_{signal} " 100 f"*/\n") 101 define = f"#define P{port.upper()}{pin_num.upper()}" \ 102 f"{pinmux.upper()}_{periph.upper()}_{signal.upper()}" 103 define_val = f"{fmap}({port.lower()}, {pin_num}, " \ 104 f"{pinmux.lower()}, {function.lower()})" 105 f.write(f"{define} \\\n\t{define_val}\n") 106 107 108def generate_atmel_sam_header(outdir, family, fmap, serie, 109 variant, pin_cfgs, revision): 110 """Generate Atmel SAM header with pin configurations. 111 112 Args: 113 outdir: Output base directory. 114 family: Atmel SAM family. 115 fmap: Function to map pinctrl. 116 series: MCU Series. 117 variant: Variant information. 118 pin_cfgs: Pin configurations. 119 """ 120 121 ofname = outdir / get_header_fname(serie, variant["pincode"], revision) 122 with open(ofname, "w") as f: 123 f.write(HEADER) 124 f.write(f'\n{"#include <dt-bindings/pinctrl/atmel_sam_pinctrl.h>"}\n') 125 126 if len(variant) > 2: 127 if variant["exception"]: 128 f.write(EXCEPTION) 129 130 for port, pin_num, pinmux, periph, signal, function in pin_cfgs: 131 if function in ["gpio", "lpm"]: 132 write_gpio_function(f, port, pin_num, fmap, function) 133 continue 134 135 if function in ["wakeup"]: 136 write_wakeup_function(f, port, pin_num, pinmux, periph, 137 signal, fmap, function) 138 continue 139 140 write_periph_function(f, port, pin_num, pinmux, periph, 141 signal, fmap, function) 142 143 144def build_atmel_sam_gpio_sets(pin_cfgs, pin): 145 """Build Atmel SAM pin configurations sets. 146 147 Args: 148 pins: Pins description. 149 150 Returns: 151 Dictionary with pins configuration. 152 """ 153 154 port, pin_num = get_port_pin(pin) 155 new_item = (port, pin_num, "a", "gpio", "gpio", "gpio") 156 157 if new_item not in pin_cfgs: 158 pin_cfgs.append(new_item) 159 160 161def build_atmel_sam_sets(pin_cfgs, pin, pin_lst, serie, variant, function): 162 """Build Atmel SAM pin configurations sets. 163 164 Args: 165 serie: MCU Serie. 166 variant: Variant information. 167 pins: Pins description. 168 169 Returns: 170 Dictionary with pins configuration. 171 """ 172 173 if len(pin_lst[0]) > 0: 174 for pinmux, periph, signal, *excludes in pin_lst: 175 if len(excludes) > 0: 176 if serie in excludes[0]: 177 continue 178 if variant["pincode"] in excludes[0]: 179 continue 180 181 port, pin_num = get_port_pin(pin) 182 183 pin_cfgs.append((port, pin_num, pinmux, periph, signal, function)) 184 185 186def build_atmel_sam_pin_cfgs(serie, variant, pins): 187 """Build Atmel SAM pin configurations. 188 189 Args: 190 serie: MCU Serie. 191 variant: Variant information. 192 pins: Pins description. 193 194 Returns: 195 Dictionary with pins configuration. 196 """ 197 198 pin_cfgs = [] 199 200 pins = OrderedDict(natsorted(pins.items(), key=lambda kv: kv[0])) 201 202 for pin, pin_cfg in pins.items(): 203 if variant["pincode"] not in pin_cfg["pincodes"]: 204 continue 205 206 build_atmel_sam_gpio_sets(pin_cfgs, pin) 207 208 if "periph" in pin_cfg.keys(): 209 build_atmel_sam_sets(pin_cfgs, pin, pin_cfg["periph"], 210 serie, variant, "periph") 211 if "extra" in pin_cfg.keys(): 212 build_atmel_sam_sets(pin_cfgs, pin, pin_cfg["extra"], 213 serie, variant, "extra") 214 if "system" in pin_cfg.keys(): 215 build_atmel_sam_sets(pin_cfgs, pin, pin_cfg["system"], 216 serie, variant, "system") 217 if "lpm" in pin_cfg.keys(): 218 build_atmel_sam_sets(pin_cfgs, pin, pin_cfg["lpm"], 219 serie, variant, "lpm") 220 if "wakeup" in pin_cfg.keys(): 221 build_atmel_sam_sets(pin_cfgs, pin, pin_cfg["wakeup"], 222 serie, variant, "wakeup") 223 224 return pin_cfgs 225 226 227def main(indir, outdir) -> None: 228 """Entry point. 229 230 Args: 231 indir: Directory with pin configuration files. 232 outdir: Output directory 233 """ 234 235 if outdir.exists(): 236 for entry in outdir.glob("sam*-pinctrl.h"): 237 entry.unlink() 238 else: 239 outdir.mkdir() 240 241 for entry in indir.iterdir(): 242 if not entry.is_file() or entry.suffix not in (".yml", ".yaml"): 243 continue 244 245 config = yaml.load(open(entry), Loader=yaml.Loader) 246 247 model = config["model"] 248 family = config["family"] 249 fmap = config["map"] 250 series = config["series"] 251 variants = config["variants"] 252 has_rev = "revisions" in config.keys() 253 pins = config["pins"] 254 255 if model == "atmel,sam": 256 for serie in series: 257 for variant in [v for v in variants if serie in v["series"]]: 258 pin_cfgs = build_atmel_sam_pin_cfgs(serie, variant, pins) 259 rev = config["revisions"].get(serie) if has_rev else None 260 generate_atmel_sam_header(outdir, family, fmap, serie, 261 variant, pin_cfgs, rev) 262 else: 263 raise ValueError(f"Unexpected model: {model}") 264 265 266if __name__ == "__main__": 267 parser = argparse.ArgumentParser() 268 parser.add_argument( 269 "-i", 270 "--indir", 271 type=Path, 272 default=REPO_ROOT / "pinconfigs", 273 help="Directory with pin configuration files", 274 ) 275 parser.add_argument( 276 "-o", 277 "--outdir", 278 type=Path, 279 default=REPO_ROOT / "include" / "dt-bindings" / "pinctrl", 280 help="Output directory", 281 ) 282 args = parser.parse_args() 283 284 main(args.indir, args.outdir) 285