1""" 2Utility to autogenerate pinctrl definitions. 3 4Usage:: 5 python3 gd32pinctrl.py [-i /path/to/configs] [-o /path/to/include] 6 7Copyright (c) 2021 Teslabs Engineering S.L. 8SPDX-License-Identifier: Apache 2.0 9""" 10 11import argparse 12from collections import OrderedDict 13from pathlib import Path 14import re 15 16import yaml 17 18 19REPO_ROOT = Path(__file__).absolute().parents[1] 20"""Repository root.""" 21 22AFIO_REMAP_SUFFIXES = { 23 0: ("NORMP",), 24 2: ("NORMP", "RMP"), 25 3: ("NORMP", "PRMP", "FRMP"), 26 4: ("NORMP", "PRMP1", "PRMP2", "FRMP"), 27} 28"""AFIO remap suffixes (# remap options <> suffix).""" 29 30AFIO_MODE_NAMES = { 31 "analog": "ANALOG", 32 "inp": "GPIO_IN", 33 "out": "ALTERNATE", 34} 35"""AFIO mode names.""" 36 37HEADER = """/* 38 * Autogenerated file 39 * 40 * SPDX-License-Identifier: Apache 2.0 41 */ 42""" 43"""Header for the generated files.""" 44 45 46def get_header_fname(series, variant): 47 """Get header file name. 48 49 Args: 50 series: Series. 51 variant: Variant information. 52 53 Returns: 54 Header file name. 55 """ 56 57 pincode = variant["pincode"].lower() 58 memories = f"({'-'.join((str(m).lower() for m in variant['memories']))})" 59 return f"{series}{pincode}{memories}xx-pinctrl.h" 60 61 62def get_port_pin(pin_name): 63 """Obtain port and pin number from a pin name 64 65 Args: 66 pin_name: Pin name, e.g. PA0 67 68 Returns: 69 Port and pin, e.g. A, 0. 70 """ 71 72 m = re.match(r"P([A-Z])(\d+)", pin_name) 73 if not m: 74 raise ValueError(f"Unexpected pin name: {pin_name}") 75 76 return m.group(1), m.group(2) 77 78 79def generate_afio_header(outdir, variant, series, pin_cfgs): 80 """Generate AFIO header with pin configurations. 81 82 Args: 83 outdir: Output base directory. 84 variant: Variant information. 85 series: Series. 86 pin_cfgs: Pin configurations. 87 """ 88 89 pin_cfgs = OrderedDict(sorted(pin_cfgs.items(), key=lambda kv: kv[0])) 90 91 with open(outdir / get_header_fname(series, variant), "w") as f: 92 f.write(HEADER) 93 f.write(f"\n#include \"{series}xx-afio.h\"\n") 94 for signal, cfg in pin_cfgs.items(): 95 f.write(f"\n/* {signal} */\n") 96 for port, pin, mode, name_suffix, remap in cfg: 97 define = f"#define {signal}_P{port}{pin}{name_suffix}" 98 define_val = f"GD32_PINMUX_AFIO('{port}', {pin}, {mode}, {remap})" 99 f.write(f"{define} \\\n\t{define_val}\n") 100 101 102def generate_af_header(outdir, variant, series, pin_cfgs): 103 """Generate AF header with pin configurations. 104 105 Args: 106 outdir: Output base directory. 107 variant: Variant information. 108 series: Series. 109 pin_cfgs: Pin configurations. 110 """ 111 112 pin_cfgs = OrderedDict(sorted(pin_cfgs.items(), key=lambda kv: kv[0])) 113 114 with open(outdir / get_header_fname(series, variant), "w") as f: 115 f.write(HEADER) 116 f.write("\n#include \"gd32-af.h\"\n") 117 for signal, cfg in pin_cfgs.items(): 118 f.write(f"\n/* {signal} */\n") 119 for port, pin, mode in cfg: 120 define = f"#define {signal}_P{port}{pin}" 121 define_val = f"GD32_PINMUX_AF('{port}', {pin}, {mode})" 122 f.write(f"{define} \\\n\t{define_val}\n") 123 124 125def build_afio_pin_cfgs(variant, signal_configs, pins, remaps): 126 """Build AFIO pin configurations. 127 128 Args: 129 variant: Variant information. 130 signal_configs: Signal configurations. 131 pins: Pins description. 132 remaps: Remaps description. 133 134 Returns: 135 Dictionary with pins configuration. 136 """ 137 138 pin_cfgs = {"ANALOG": []} 139 140 pincode = variant["pincode"] 141 memories = variant["memories"] 142 143 for signal, signal_cfg in signal_configs.items(): 144 # check if signal is excluded from current pincode 145 if pincode in signal_cfg.get("exclude-pincodes", []): 146 continue 147 148 # check if signal is excluded from current list of memories 149 if set(memories).intersection(signal_cfg.get("exclude-memories", [])): 150 continue 151 152 signal_pins = {} 153 154 # collect all afs 155 for pin, pin_cfg in pins.items(): 156 if pincode not in pin_cfg["pincodes"]: 157 continue 158 if signal in pin_cfg["afs"]: 159 signal_pins[pin] = [0] 160 161 # collect all remaps 162 remap_options = 0 163 signal_remaps = remaps.get(signal) 164 if signal_remaps: 165 for pin in signal_remaps["pins"]: 166 if not pin: 167 continue 168 169 if pincode in pins[pin]["pincodes"]: 170 if pin not in signal_pins: 171 signal_pins[pin] = [] 172 173 if remap_options not in signal_pins[pin]: 174 signal_pins[pin].append(remap_options) 175 176 remap_options += 1 177 178 for pin, remap_values in signal_pins.items(): 179 for remap_value in remap_values: 180 for mode in signal_cfg["modes"]: 181 port, pin_number = get_port_pin(pin) 182 remap = AFIO_REMAP_SUFFIXES[remap_options][remap_value] 183 184 name_suffix = "" 185 if len(signal_cfg["modes"]) > 1: 186 name_suffix = f"_{mode.upper()}" 187 188 if remap_options > 0: 189 name_suffix += f"_{remap}" 190 remap = signal.split("_")[0] + f"_{remap}" 191 192 if signal not in pin_cfgs: 193 pin_cfgs[signal] = [] 194 195 pin_cfgs[signal].append( 196 ( 197 port, 198 pin_number, 199 AFIO_MODE_NAMES[mode], 200 name_suffix, 201 remap, 202 ) 203 ) 204 205 # add analog entries (used for low power mode) 206 for pin, pin_cfg in pins.items(): 207 if pincode not in pin_cfg["pincodes"]: 208 continue 209 210 port, pin_number = get_port_pin(pin) 211 pin_cfgs["ANALOG"].append((port, pin_number, "ANALOG", "", "NORMP")) 212 213 return pin_cfgs 214 215 216def build_af_pin_cfgs(variant, signal_configs, pins): 217 """Build AF pin configurations. 218 219 Args: 220 variant: Variant information. 221 signal_configs: Signals description. 222 pins: Pins description. 223 224 Returns: 225 Dictionary with pins configuration. 226 """ 227 228 pin_cfgs = {"ANALOG": []} 229 230 pincode = variant["pincode"] 231 memories = variant["memories"] 232 233 for pin, pin_cfg in pins.items(): 234 if pincode not in pin_cfg["pincodes"]: 235 continue 236 237 port, pin_number = get_port_pin(pin) 238 239 # add analog entry (used for low power mode) 240 pin_cfgs["ANALOG"].append((port, pin_number, "ANALOG")) 241 242 for signal, mode in pin_cfg["afs"].items(): 243 signal_config = signal_configs.get(signal) 244 # check if signal is excluded from current pincode. 245 if signal_config and pincode in signal_config.get( 246 "exclude-pincodes", [] 247 ): 248 continue 249 # check if signal is excluded from current list of memories 250 if signal_config and set(memories).intersection( 251 signal_config.get("exclude-memories", []) 252 ): 253 continue 254 255 if signal not in pin_cfgs: 256 pin_cfgs[signal] = [] 257 258 if mode != "ANALOG": 259 mode = f"AF{mode}" 260 261 pin_cfgs[signal].append((port, pin_number, mode)) 262 263 return pin_cfgs 264 265 266def main(indir, outdir) -> None: 267 """Entry point. 268 269 Args: 270 indir: Directory with pin configuration files. 271 outdir: Output directory 272 """ 273 274 if outdir.exists(): 275 for entry in outdir.glob("gd32*-pinctrl.h"): 276 entry.unlink() 277 else: 278 outdir.mkdir() 279 280 for entry in indir.iterdir(): 281 if not entry.is_file() or entry.suffix not in (".yml", ".yaml"): 282 continue 283 284 config = yaml.load(open(entry), Loader=yaml.Loader) 285 286 model = config["model"] 287 series = config["series"] 288 variants = config["variants"] 289 signal_configs = config.get("signal-configs", {}) 290 pins = config["pins"] 291 292 if model == "afio": 293 remaps = config["remaps"] 294 for variant in variants: 295 pin_cfgs = build_afio_pin_cfgs(variant, signal_configs, pins, remaps) 296 generate_afio_header(outdir, variant, series, pin_cfgs) 297 elif model == "af": 298 for variant in variants: 299 pin_cfgs = build_af_pin_cfgs(variant, signal_configs, pins) 300 generate_af_header(outdir, variant, series, pin_cfgs) 301 else: 302 raise ValueError(f"Unexpected model: {model}") 303 304 305if __name__ == "__main__": 306 parser = argparse.ArgumentParser() 307 parser.add_argument( 308 "-i", 309 "--indir", 310 type=Path, 311 default=REPO_ROOT / "pinconfigs", 312 help="Directory with pin configuration files", 313 ) 314 parser.add_argument( 315 "-o", 316 "--outdir", 317 type=Path, 318 default=REPO_ROOT / "include" / "dt-bindings" / "pinctrl", 319 help="Output directory", 320 ) 321 args = parser.parse_args() 322 323 main(args.indir, args.outdir) 324