1#!/usr/bin/env python3 2# 3# Copyright 2023-2025 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 datetime 19import re 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 26 27HELPSTR = """ 28Processes NXP signal configuration files 29 30Given a processor data pack, generates the SOC level pinctrl DTSI defintions 31required for Zephyr. This tool is intended to be used with the configuration 32data downloaded from NXP's MCUXpresso SDK builder. 33""" 34 35 36def parse_args(): 37 """ 38 Parses arguments, and returns object with parsed arguments 39 as properties 40 """ 41 parser = argparse.ArgumentParser(description=HELPSTR, 42 formatter_class=argparse.RawDescriptionHelpFormatter) 43 parser.add_argument('data_pack', metavar = 'DATA_PACK', 44 type=str, 45 help='Path to downloaded data package zip') 46 parser.add_argument('--copyright', action='store_true', 47 help='Enable default NXP copyright') 48 parser.add_argument('--soc-output', metavar = 'SOC_OUT', type=str, 49 help='Output directory for soc level dtsi files') 50 parser.add_argument('--controller', metavar = 'CTRL', type=str, 51 help=("SOC pin controller type." 52 "Currently supports: [IOMUX, IOCON, PORT]")) 53 54 return parser.parse_args() 55 56 57def processor_to_controller(processor_name): 58 """ 59 Returns pin controller type for processor, or None if 60 processor is unknown 61 """ 62 # Select family of pin controller based on SOC type 63 if "IMXRT1" in processor_name: 64 # Use IMX config tools 65 return 'IOMUX' 66 if "IMXRT7" in processor_name: 67 # LPC config tools 68 return 'IOCON' 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 if "MCX" in processor_name: 82 # Kinetis config tools 83 return 'PORT' 84 # Unknown processor family 85 return "UNKNOWN" 86 87def get_pack_version(pack_dir): 88 """ 89 Gets datapack version 90 @param pack_dir: root directory data pack is in 91 """ 92 # Check version of the config tools archive 93 npi_data = pathlib.Path(pack_dir) / 'npidata.mf' 94 data_version = 0.0 95 with open(npi_data, 'r', encoding='UTF8') as stream: 96 line = stream.readline() 97 while line != '': 98 match = re.search(r'data_version=([\d\.]+)', line) 99 if match: 100 data_version = float(match.group(1)) 101 break 102 line = stream.readline() 103 return data_version 104 105def main(): 106 """ 107 Main entry point. Will process data pack, and generate board pin control 108 headers 109 """ 110 args = parse_args() 111 if not args.soc_output: 112 args.soc_output = "." 113 elif not pathlib.Path(args.soc_output).is_dir(): 114 print('SOC output path must be a directory') 115 sys.exit(255) 116 117 # Extract the Data pack to a temporary directory 118 temp_dir = tempfile.TemporaryDirectory() 119 zipfile.ZipFile(args.data_pack).extractall(temp_dir.name) 120 121 data_version = get_pack_version(temp_dir.name) 122 print(f"Found data pack version {data_version}") 123 if round(data_version) != 16: 124 print("Warning: This tool is only verified for data pack version 16, " 125 "other versions may not work") 126 127 # Attempt to locate the signal XML files we will generate from 128 proc_root = pathlib.Path(temp_dir.name) / 'processors' 129 search_pattern = "*/ksdk2_0/*/signal_configuration.xml" 130 # Pathlib glob returns an iteration, so use sum to count the length 131 package_count = sum(1 for _ in proc_root.glob(search_pattern)) 132 if package_count == 0: 133 search_pattern = "*/i_mx_2_0/*/signal_configuration.xml" 134 package_count = sum(1 for _ in proc_root.glob(search_pattern)) 135 if package_count == 0: 136 print("No signal configuration files were found in this data pack") 137 sys.exit(255) 138 if args.copyright: 139 # Add default copyright 140 nxp_copyright = (f"Copyright {datetime.datetime.today().year}, NXP\n" 141 f" * SPDX-License-Identifier: Apache-2.0") 142 else: 143 nxp_copyright = "" 144 for signal_xml in proc_root.glob(search_pattern): 145 package_root = signal_xml.parent 146 package_name = package_root.name 147 if args.controller is None: 148 # Determine pin controller type using SOC package name 149 args.controller = processor_to_controller(package_name) 150 # If controller is still unknown, error out 151 if args.controller == "UNKNOWN": 152 print(f"Error: package {package_name} is not currently supported by this tool") 153 print("You can try specifying your pin controller family manually, " 154 "but this is unsupported!") 155 sys.exit(255) 156 157 # Select correct config tool script for the signal file 158 out_dir = args.soc_output.rstrip('/') 159 if args.controller == 'IOMUX': 160 cfg_util = imx_cfg_utils.NXPSdkUtil(str(package_root), 161 copyright_header=nxp_copyright) 162 out_path = f"{out_dir}/{cfg_util.get_part_num().lower()}-pinctrl.dtsi" 163 elif args.controller == 'IOCON': 164 cfg_util = lpc_cfg_utils.NXPSdkUtil(str(package_root), 165 copyright_header=nxp_copyright) 166 out_path = f"{out_dir}/{cfg_util.get_part_num().upper()}-pinctrl.h" 167 elif args.controller == 'PORT': 168 cfg_util = kinetis_cfg_utils.NXPSdkUtil(str(package_root), 169 copyright_header=nxp_copyright) 170 out_path = f"{out_dir}/{cfg_util.get_part_num().upper()}-pinctrl.h" 171 else: 172 print("Error: unknown controller type") 173 sys.exit(255) 174 175 cfg_util.write_pinctrl_defs(out_path) 176 print(f"Wrote pinctrl headers to {out_path}") 177 178 179if __name__ == "__main__": 180 main() 181