1#!/usr/bin/env python3
2#
3# Copyright 2023, NXP
4#
5# SPDX-License-Identifier: Apache-2.0
6
7"""
8Wrapper around support libraries for i.MX RT, LPC, and Kinetis SOCs.
9Allows user to generate pin control board files from MCUXpresso config
10tools (MEX) files
11"""
12# Standard python libraries
13import argparse
14import tempfile
15import zipfile
16import pathlib
17import sys
18import re
19import datetime
20
21# SOC configuration data support libraries
22from imx import imx_cfg_utils
23from kinetis import kinetis_cfg_utils
24from lpc import lpc_cfg_utils
25# Shared MEX utilities
26from shared import mex_utils
27
28
29HELPSTR = """
30Processes NXP MEX configuration files
31Given a mex file, generates the board level pinctrl groups required for
32Zephyr. This tool is intended to be used with the configuration data packs
33downloaded from NXP's MCUXpresso SDK builder, as well as MEX files created
34by the user using MCUXpresso configuration tools
35"""
36
37
38def parse_args():
39    """
40    Parses arguments, and returns object with parsed arguments
41    as properties
42    """
43    parser = argparse.ArgumentParser(description=HELPSTR,
44        formatter_class=argparse.RawDescriptionHelpFormatter)
45
46    parser.add_argument('mex_file', metavar = 'MEX',
47                        type=str, help='path to source MEX file')
48    parser.add_argument('data_pack', metavar = 'DATA_PACK', type=str,
49                        help='Path to downloaded data package zip')
50    parser.add_argument('--copyright', action='store_true',
51                        help='Enable default NXP copyright')
52    parser.add_argument('--board-output', metavar = 'BOARD_OUT', type=str,
53                        help='Output path for board level DTS file')
54    parser.add_argument('--controller', metavar = 'CTRL', type=str,
55                        help=("SOC pin controller type."
56                        "Currently supports: [IOMUX, IOCON, PORT]"))
57
58    return parser.parse_args()
59
60def processor_to_controller(processor_name):
61    """
62    Returns pin controller type for processor, or None if
63    processor is unknown
64    """
65    # Select family of pin controller based on SOC type
66    if "IMXRT1" in processor_name:
67        # Use IMX config tools
68        return 'IOMUX'
69    if "IMXRT6" in processor_name:
70        # LPC config tools
71        return 'IOCON'
72    if "IMXRT5" in processor_name:
73        # LPC config tools
74        return 'IOCON'
75    if "LPC55" in processor_name:
76        # LPC config tools
77        return 'IOCON'
78    if "MK" in processor_name:
79        # Kinetis config tools
80        return 'PORT'
81    # Unknown processor family
82    return "UNKNOWN"
83
84def get_pack_version(pack_dir):
85    """
86    Gets datapack version
87    @param pack_dir: root directory data pack is in
88    """
89    # Check version of the config tools archive
90    npi_data = pathlib.Path(pack_dir) / 'npidata.mf'
91    data_version = 0.0
92    with open(npi_data, 'r', encoding='UTF8') as stream:
93        line = stream.readline()
94        while line != '':
95            match = re.search(r'data_version=([\d\.]+)', line)
96            if match:
97                data_version = float(match.group(1))
98                break
99            line = stream.readline()
100    return data_version
101
102
103def main():
104    """
105    Main entry point. Will process arguments, and generate board pin control
106    files.
107    """
108    args = parse_args()
109    utils = mex_utils.NxpMexUtil(args.mex_file)
110    pins_version = utils.get_pins_version()
111
112    if round(pins_version) != 14:
113        print("Warning: This tool is only verified for MEX files version 14, "
114            "other versions may not work")
115
116    board_name = utils.get_board_name()
117    processor_name = utils.get_processor_name()
118    package_name = utils.get_package_name()
119
120    # Extract the Data pack to a temporary directory
121    temp_dir = tempfile.TemporaryDirectory()
122    zipfile.ZipFile(args.data_pack).extractall(temp_dir.name)
123    proc_root = (pathlib.Path(temp_dir.name) / 'processors'/
124                    processor_name / 'ksdk2_0' / package_name)
125    if not proc_root.exists():
126        print(f"Error: Data pack does not contain processor data for {processor_name}")
127        sys.exit(255)
128    data_version = get_pack_version(temp_dir.name)
129
130    print(f"Found data pack version {data_version}, pins version {pins_version} "
131        f"for processor {processor_name}")
132    if round(data_version) != 14:
133        print("Warning: This tool is only verified for data pack version 14, "
134            "other versions may not work")
135
136    if not args.board_output:
137        args.board_output = f"{board_name.lower().replace('-', '_')}-pinctrl.dtsi"
138
139    # Initialize SOC specific utility, and generate board pin control
140    if args.copyright:
141        # Add default copyright
142        nxp_copyright = (f"Copyright {datetime.datetime.today().year}, NXP\n"
143        f" * SPDX-License-Identifier: Apache-2.0")
144    else:
145        nxp_copyright = ''
146
147    if args.controller is None:
148        args.controller = processor_to_controller(processor_name)
149
150    # If controller is unknown, this is an unknown SOC and user did not
151    # manually select a controller
152    if args.controller == "UNKNOWN":
153        print(f"Error: processor {processor_name} is not currently supported by this tool")
154        print("You can try specifying your pin controller family manually, "
155            "but this is unsupported!")
156        sys.exit(255)
157
158    # Select correct config tool script for the MEX file
159    if args.controller == 'IOMUX':
160        cfg_util = imx_cfg_utils.NXPSdkUtil(str(proc_root), copyright_header=nxp_copyright)
161    elif args.controller == 'IOCON':
162        cfg_util = lpc_cfg_utils.NXPSdkUtil(str(proc_root), copyright_header=nxp_copyright)
163    elif args.controller == 'PORT':
164        cfg_util = kinetis_cfg_utils.NXPSdkUtil(str(proc_root), copyright_header=nxp_copyright)
165    else:
166        print("Error: unknown controller type")
167        sys.exit(255)
168    print(f"Generating configuration for {board_name}")
169    cfg_util.write_pinctrl_groups(args.mex_file, args.board_output)
170    print(f"Wrote board pinctrl dts file to {args.board_output}")
171
172
173
174if __name__ == "__main__":
175    main()
176