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