1# Copyright (c) 2024 Gerson Fernando Budke
2# SPDX-License-Identifier: Apache-2.0
3
4"""
5Utility to autogenerate pinctrl definitions.
6
7Usage::
8    python3 bflbpinctrl.py [-i /path/to/configs] [-o /path/to/include]
9"""
10
11import argparse
12from collections import OrderedDict
13from datetime import date
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
24current_date = date.today()
25
26HEADER = """/*
27 * Autogenerated file
28 * Copyright (c) {} BFLB Pinctrl Generator <nandojve@gmail.com>
29 * SPDX-License-Identifier: Apache-2.0
30 */
31""".format(current_date.year)
32"""Header for the generated files."""
33
34
35def get_header_fname(serie):
36    """Get header file name.
37
38    Args:
39        serie: Series.
40
41    Returns:
42        Header file name.
43    """
44
45    return f"bl{serie}x-pinctrl.h"
46
47
48def get_port_pin(pin_name):
49    """Obtain port and pin number from a pin name
50
51    Args:
52        pin_name: Pin name, e.g. GPIO0
53
54    Returns:
55        Port and pin, e.g. GPIO, 0.
56    """
57
58    m = re.match(r"(GPIO)(\d+)", pin_name.upper())
59    if not m:
60        raise ValueError(f"Unexpected pin name: {pin_name}")
61
62    return m.group(1), str(int(m.group(2)))
63
64
65def get_normalized_name(name):
66    sname_norm = name.replace(".", "_")
67    sname_norm = sname_norm.replace("-", "_")
68    return sname_norm.upper()
69
70
71def write_periph_index(f, periph, value):
72    define = f"#define BFLB_PINMUX_FUN_INST_{periph.lower()}"
73    define_val = f"{value:#06x}"
74    f.write(f"{define:<56}{define_val}\n")
75
76
77def write_signal_index(f, signal, value):
78    define = f"#define BFLB_PINMUX_SIGNAL_{signal.lower()}"
79    define_val = f"{value:#04x}"
80    f.write(f"{define:<56}{define_val}\n")
81
82
83def generate_bflb_series_header(outdir, family, peripherals, signals, signal_cfgs):
84    """Generate Bouffalo Lab header with pin configurations.
85
86    Args:
87        outdir: Output base directory.
88        family: Bouffalo Lab family.
89        peripherals: The peripheral itself.
90        signals: Signals.
91        signal_cfgs: Signal configurations.
92    """
93
94    sname = f"{family}-pinctrl.h"
95    sname_norm = get_normalized_name(sname)
96    ofname = outdir / sname
97    with open(ofname, "w") as f:
98        f.write(HEADER)
99        f.write(f"\n#ifndef DT_BINDINGS_PINCTRL_BFLB_{sname_norm}_")
100        f.write(f"\n#define DT_BINDINGS_PINCTRL_BFLB_{sname_norm}_\n\n")
101
102        for periph, index, *instancies in peripherals:
103            if instancies:
104                for inst in instancies:
105                    write_periph_index(f, periph + str(inst), index + inst * 256)
106                continue
107            write_periph_index(f, periph, index)
108
109        f.write("\n")
110
111        for signal in signal_cfgs:
112            value = 0
113            if signal in signals.keys():
114                value = signals[signal]
115            write_signal_index(f, signal, value)
116
117        f.write(f"\n#endif /* DT_BINDINGS_PINCTRL_BFLB_{sname_norm}_ */\n")
118
119
120def write_periph_function(f, port, pin_num, periph, signal, fmap, function):
121    f.write(f"\n/* {port.lower()}{pin_num}_{periph}_{signal} "
122            f"*/\n")
123    define = f"#define {port.upper()}{pin_num.upper()}_{periph.upper()}_{signal.upper()}"
124    define_val = f"{fmap}({pin_num}, {periph.lower()}, {signal.lower()}, {function.lower()})"
125    f.write(f"{define} \\\n\t{define_val}\n")
126
127
128def generate_bflb_bl_header(outdir, family, fmap, serie, pin_cfgs):
129    """Generate Bouffalo Lab header with pin configurations.
130
131    Args:
132        outdir: Output base directory.
133        family: Bouffalo Lab family.
134        fmap: Function to map pinctrl.
135        series: MCU Series.
136        pin_cfgs: Pin configurations.
137    """
138
139    sname = get_header_fname(serie)
140    sname_norm = sname.replace(".", "_")
141    sname_norm = sname_norm.replace("-", "_")
142    sname_norm = sname_norm.upper()
143    ofname = outdir / sname
144    with open(ofname, "w") as f:
145        f.write(HEADER)
146        f.write(f"\n#ifndef DT_BINDINGS_PINCTRL_BFLB_{sname_norm}_")
147        f.write(f"\n#define DT_BINDINGS_PINCTRL_BFLB_{sname_norm}_\n")
148        f.write(f"\n#include <dt-bindings/pinctrl/{family}-pinctrl.h>")
149        f.write("\n#include <dt-bindings/pinctrl/bflb-common-pinctrl.h>\n")
150
151        for port, pin_num, periph, signal, function in pin_cfgs:
152            write_periph_function(f, port, pin_num, periph, str(signal), fmap, function)
153
154        f.write(f"\n#endif /* DT_BINDINGS_PINCTRL_BFLB_{sname_norm}_ */\n")
155
156
157def build_bflb_bl_sets(pin_cfgs, pin, pin_lst, serie, function):
158    """Build Bouffalo Lab pin configurations sets.
159
160    Args:
161        pin_cfgs: Pin Configuration.
162        pin: Pin description.
163        pin_lst: Pins List.
164        serie: MCU Serie.
165        function: Function of the pin.
166
167    Returns:
168        Dictionary with pins configuration.
169    """
170
171    if len(pin_lst[0]) > 0:
172        for periph, signals in pin_lst:
173            port, pin_num = get_port_pin(pin)
174
175            for signal in signals:
176                pin_cfgs.append((port, pin_num, periph, signal, function))
177
178
179def build_bflb_bl_pin_cfgs(serie, pins):
180    """Build Bouffalo Lab BLxxx pin configurations.
181
182    Args:
183        serie: MCU Series.
184        pins: Pins description.
185
186    Returns:
187        Dictionary with pins configuration.
188    """
189
190    pin_cfgs = []
191
192    pins = OrderedDict(natsorted(pins.items(), key=lambda kv: kv[0]))
193
194    for pin, pin_cfg in pins.items():
195        if serie not in pin_cfg["series"]:
196            continue
197
198        if "periph" in pin_cfg.keys():
199            build_bflb_bl_sets(pin_cfgs, pin, pin_cfg["periph"], serie, "periph")
200        if "analog" in pin_cfg.keys():
201            build_bflb_bl_sets(pin_cfgs, pin, pin_cfg["analog"], serie, "analog")
202        if "input" in pin_cfg.keys():
203            build_bflb_bl_sets(pin_cfgs, pin, pin_cfg["input"], serie, "input")
204        if "output" in pin_cfg.keys():
205            build_bflb_bl_sets(pin_cfgs, pin, pin_cfg["output"], serie, "output")
206
207    return pin_cfgs
208
209
210def generate_bflb_generate_signal_list(pins):
211    signal_cfgs = []
212
213    for _, pin_cfg in pins.items():
214        for prop, periph_signal_lst in pin_cfg.items():
215            if prop == "series":
216                continue
217            for _, signal_lst in periph_signal_lst:
218                for signal in signal_lst:
219                    if signal not in signal_cfgs:
220                        signal_cfgs.append(signal)
221
222    signal_cfgs = natsorted(signal_cfgs, key=lambda kv: kv[0])
223
224    return signal_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("bl*-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        peripherals = config["peripherals"]
252        signals = config["signals"]
253        pins = config["pins"]
254
255        if model == "bflb,bl":
256            signal_cfgs = generate_bflb_generate_signal_list(pins)
257            signals = OrderedDict(natsorted(signals, key=lambda kv: kv[0]))
258            generate_bflb_series_header(outdir, family, peripherals, signals, signal_cfgs)
259            for serie in series:
260                pin_cfgs = build_bflb_bl_pin_cfgs(serie, pins)
261                generate_bflb_bl_header(outdir, family, fmap, serie, pin_cfgs)
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" / "zephyr" / "dt-bindings" / "pinctrl",
280        help="Output directory",
281    )
282    args = parser.parse_args()
283
284    main(args.indir, args.outdir)
285