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