1#!/usr/bin/env python3 2# 3# Copyright (c) 2022,2024 NXP 4# 5# SPDX-License-Identifier: Apache-2.0 6 7""" 8Implements a configuration file parser for LPC and RT6xx/5xx MCUs, 9which can generate pinctrl definitions for Zephyr 10""" 11 12import xml.etree.ElementTree as ET 13import re 14import collections 15import logging 16import os 17import pathlib 18import __main__ 19 20NAMESPACES = {'mex': 'http://mcuxpresso.nxp.com/XSD/mex_configuration_14'} 21 22class MUXOption: 23 """ 24 Internal class representing a mux option on the SOC 25 """ 26 def __init__(self, connection, imx_rt = ''): 27 """ 28 Initializes a mux option 29 @param connection XML connection option from signal_configuration.xml 30 """ 31 self._name = connection.attrib.get('name_part') 32 logging.debug("\t\t %s", self._name) 33 if self._name is None: 34 self._name = '' 35 return 36 # Get MUX settings 37 self._offset = -1 38 # Get default instance index 39 self._index = 0 40 for periph in connection.iter('peripheral_signal_ref'): 41 self._periph = periph.attrib.get('peripheral') 42 self._signal = periph.attrib.get('signal') 43 self._channel = periph.attrib.get('channel') 44 self._mux_overrides = {} 45 if imx_rt == 'MIMXRT7XX': 46 # RT 7xx series has different function register and instance number 47 func_name = 'FSEL' 48 pio_regex = re.compile(r'IOPCTL(\d+)_PIO(\d+)_(\d+)') 49 elif imx_rt == 'MIMXRT5/6XX': 50 # RT 6xx/5xx series has different function register 51 func_name = 'FSEL' 52 pio_regex = re.compile(r'IOPCTL_PIO(\d+)_(\d+)') 53 else: 54 func_name = 'FUNC' 55 pio_regex = re.compile(r'IOCON_PIO(\d)_*(\d+)') 56 for assign in connection.iter('assign'): 57 reg = assign.attrib.get('register') 58 field = assign.attrib.get('bit_field') 59 val = assign.attrib.get('bit_field_value') 60 logging.debug('\t\t\t [ASSIGN] %s %s', reg, val) 61 # Only process PIO register FUNC setting 62 match = pio_regex.match(reg) 63 if match and (field == func_name): 64 if self._channel: 65 # For mux options with channels, format pin name as: 66 # {Peripheral}_{Signal}{Channel}_{Pin} 67 self._name = f"{self._periph}_{self._signal}{self._channel}" 68 # Append name of pin 69 if imx_rt == 'MIMXRT7XX': 70 self._name += f"_PIO{match.group(2)}_{match.group(3)}" 71 self._index = int(match.group(1)) 72 port = int(match.group(2)) 73 pin = int(match.group(3)) 74 if port < 4: 75 self._offset = (port * 32) + pin 76 elif port < 8: 77 self._offset = ((port - 4) * 32) + pin 78 else: 79 self._offset = ((port - 8) * 32) + pin 80 else: 81 self._name += f"_PIO{match.group(1)}_{match.group(2)}" 82 port = int(match.group(1)) 83 pin = int(match.group(2)) 84 self._offset = (port * 32) + pin 85 self._mux = int(val, 16) 86 elif match and field == 'MODE': 87 # MUX overrides pullup/pulldown mode 88 if val == '0': 89 self._mux_overrides['mode'] = 'inactive' 90 elif val == '1': 91 self._mux_overrides['mode'] = 'pullDown' 92 elif val == '2': 93 self._mux_overrides['mode'] = 'pullUp' 94 elif val == '3': 95 self._mux_overrides['mode'] = 'repeater' 96 elif match and field == 'ASW' and not imx_rt: 97 # MUX override analog switch setting 98 if val == '0x1': 99 self._mux_overrides['asw'] = 'enabled' 100 self._mux_overrides['digimode'] = 'disabled' 101 elif match and field == 'ASW0' and not imx_rt: 102 # LPC553x has two ASW bits 103 if val == '0x1': 104 self._mux_overrides['asw0'] = 'enabled' 105 self._mux_overrides['digimode'] = 'disabled' 106 elif match and field == 'ASW1' and not imx_rt: 107 # LPC553x has two ASW bits 108 if val == '0x1': 109 self._mux_overrides['asw1'] = 'enabled' 110 self._mux_overrides['digimode'] = 'disabled' 111 elif match and field == 'AMENA' and imx_rt: 112 # MUX override analog switch setting 113 if val == '0x1': 114 self._mux_overrides['amena'] = 'enabled' 115 116 if self._name == 'PMIC_I2C_SCL' and imx_rt == "MIMXRT5/6XX": 117 # RT600/500 have special pmic I2C pins 118 self._offset = 0x100 119 self._mux = 0 120 elif self._name == 'PMIC_I2C_SDA' and imx_rt == "MIMXRT5/6XX": 121 self._offset = 0x101 122 self._mux = 0 123 elif self._name == 'PMIC_I2C_SCL' and imx_rt == "MIMXRT7XX": 124 self._index = 1 125 self._offset = 0x96 126 self._mux = 0 127 elif self._name == 'PMIC_I2C_SDA' and imx_rt == "MIMXRT7XX": 128 self._index = 1 129 self._offset = 0x97 130 self._mux = 0 131 if re.match(r'^\d', self._name): 132 # If string starts with a digit, it will not be a valid C name 133 self._name = f"PIN_{self._name}" 134 if self._offset == -1: 135 # Not a valid port mapping. Clear name 136 self._name = '' 137 138 def __repr__(self): 139 """ 140 String representation of object 141 """ 142 return "MUXOption(%s)" % (self._name) 143 144 def get_name(self): 145 """ 146 Get mux option name 147 """ 148 return self._name 149 150 def get_mux_name(self): 151 """ 152 Get name of the mux option, without pin name 153 """ 154 if self._channel: 155 return f"{self._periph}_{self._signal}, {self._channel}" 156 return f"{self._periph}_{self._signal}" 157 158 def get_mux_overrides(self): 159 """ 160 Some MUX options define specific pin property overrides. Get them here 161 if they exist 162 """ 163 return self._mux_overrides 164 165 def get_port(self): 166 """ 167 Get mux port 168 """ 169 return self._port 170 171 def get_signal(self): 172 """ 173 Get mux signal name 174 """ 175 return self._signal 176 177 def get_offset(self): 178 """ 179 Get mux register offset 180 """ 181 return self._offset 182 183 def get_pin(self): 184 """ 185 Get mux pin 186 """ 187 return self._pin 188 189 def get_index(self): 190 """ 191 Get mux instance index 192 """ 193 return self._index 194 195 def get_mux(self): 196 """ 197 Get mux register write value 198 """ 199 return self._mux 200 201 def get_periph(self): 202 """ 203 Get peripheral name 204 """ 205 return self._periph 206 207 def get_channel(self): 208 """ 209 Get channel number 210 """ 211 return self._channel 212 213 def __hash__(self): 214 """ 215 Override hash method to return pin name as hash 216 """ 217 return hash(self._name) 218 219 def __eq__(self, obj): 220 """ 221 Like the hash method, we override the eq method to return true if two 222 objects have the same pin name 223 """ 224 return isinstance(obj, MUXOption) and self._name == obj._name 225 226 def __lt__(self, obj): 227 """ 228 Compare objects based on name 229 """ 230 if not isinstance(obj, MUXOption): 231 return True 232 return self._name < obj._name 233 234 235class SignalPin: 236 """ 237 Internal class representing a signal on the SOC 238 """ 239 def __init__(self, pin, imx_rt = ''): 240 """ 241 Initializes a SignalPin object 242 @param pin: pin XML object from signal_configuration.xml 243 """ 244 # lpc pin names are formatted as PIOx_y 245 pin_regex = re.search(r'PIO(\d+)_(\d+)', pin.attrib['name']) 246 if (imx_rt and (pin.attrib['name'] == 'PMIC_I2C_SCL' or 247 pin.attrib['name'] == 'PMIC_I2C_SDA')): 248 # iMX RT has special pins without a mux setting 249 self._name = pin.attrib['name'] 250 self._port = 0 251 self._pin = 0 252 elif pin_regex is None: 253 logging.debug('Could not match pin name %s', pin.attrib['name']) 254 self._name = '' 255 return 256 else: 257 self._name = pin.attrib['name'] 258 self._port = int(pin_regex.group(1)) 259 self._pin = int(pin_regex.group(2)) 260 self._properties = self._get_pin_properties(pin.find('functional_properties')) 261 self._mux_options = {} 262 for connections in pin.findall('connections'): 263 mux_opt = MUXOption(connections, imx_rt = imx_rt) 264 # Only append mux options with a valid name 265 if mux_opt.get_name() != '': 266 self._mux_options[mux_opt.get_mux_name()] = mux_opt 267 268 def __repr__(self): 269 """ 270 String representation of object 271 """ 272 return "SignalPin(%s)" % (self._name) 273 274 def __hash__(self): 275 """ 276 Override hash method to return pin name as hash 277 """ 278 return hash(self._name) 279 280 def __eq__(self, obj): 281 """ 282 Like the hash method, we override the eq method to return true if two 283 objects have the same pin and port 284 """ 285 return isinstance(obj, SignalPin) and self._name == obj._name 286 287 def __lt__(self, obj): 288 """ 289 Compare objects based on port and pin 290 """ 291 if not isinstance(obj, SignalPin): 292 return True 293 if self._port == obj._port: 294 return self._pin < obj._pin 295 return self._port < obj._port 296 297 def get_name(self): 298 """ 299 Get name of pin 300 """ 301 return self._name 302 303 def get_port(self): 304 """ 305 Get PORT this signal is defined for 306 """ 307 return self._port 308 309 def get_pin(self): 310 """ 311 Get pin this signal is defined for 312 """ 313 return self._pin 314 315 def get_mux_connection(self, signal): 316 """ 317 Gets an MUXOption object for the relevant signal name 318 @param signal: Signal name on pin to get mux option for 319 """ 320 if signal in self._mux_options: 321 return self._mux_options[signal] 322 return None 323 324 def get_mux_options(self): 325 """ 326 Gets all unique settings for IOMUX on the specific pin 327 """ 328 return set(self._mux_options.values()) 329 330 def get_pin_properties(self): 331 """ 332 Gets array of pin property names 333 """ 334 return self._properties.keys() 335 336 def get_pin_property_default(self, prop): 337 """ 338 Gets name of default pin property 339 @param prop: name of pin property 340 """ 341 return self._properties[prop]['default'] 342 343 def get_pin_defaults(self): 344 """ 345 Gets mapping of all pin property names to default value names 346 """ 347 pin_defaults = {} 348 for prop in self.get_pin_properties(): 349 pin_default = self.get_pin_property_default(prop) 350 pin_defaults[prop] = pin_default 351 return pin_defaults 352 353 def get_pin_property_value(self, prop, selection): 354 """ 355 Gets bit value for pin property 356 @param prop: name of pin property 357 @param selection: name of option selected for property 358 """ 359 return self._properties[prop][selection] 360 361 def _get_pin_properties(self, props): 362 """ 363 Builds dictionary with all pin properties 364 @param props: pin function_properties XML object in signal_configuration.xml 365 """ 366 prop_mapping = {} 367 for prop in props.findall('functional_property'): 368 prop_id = prop.attrib['id'] 369 if not 'default' in prop.attrib: 370 # No default property. Skip 371 continue 372 prop_mapping[prop_id] = {} 373 prop_mapping[prop_id]['default'] = prop.attrib['default'] 374 for state in prop.findall('state'): 375 reg_assign = state.find('configuration/assign') 376 if reg_assign: 377 bit_value = int(reg_assign.attrib['bit_field_value'], 0) 378 else: 379 # Assume writing zero to register will select default 380 bit_value = 0 381 prop_mapping[prop_id][state.attrib['id']] = bit_value 382 return prop_mapping 383 384class PinGroup: 385 """ 386 Internal class representing pin group 387 """ 388 def __init__(self, function, signal_map, imx_rt = ''): 389 """ 390 Creates a pin group 391 @param function: function xml structure from MEX configuration file 392 @param signal_map: Signal mapping, maps signal names to signal pins 393 """ 394 self._name = function.attrib.get('name') 395 pins = function.find('mex:pins', NAMESPACES) 396 description = function.find('mex:description', NAMESPACES) 397 if description is not None and description.text is not None: 398 # Replace <br> html tag with newline 399 self._description = description.text.replace("<br/>", "\n") 400 else: 401 self._description = "" 402 # Build dictionary mapping pin properties to pins. This allows us to 403 # group pins based on shared configuration 404 self._pin_groups = collections.defaultdict(lambda: []) 405 for pin in pins: 406 # find signal defintion for this pin 407 signal_name = pin.attrib.get('pin_signal') 408 if not signal_name in signal_map: 409 logging.warning('Signal name %s not present in mapping', signal_name) 410 # No way to find mux option 411 continue 412 # Get mux option for this signal 413 signal = signal_map[signal_name] 414 mux_option = f"{pin.attrib.get('peripheral')}_{pin.attrib.get('signal')}" 415 mux = signal.get_mux_connection(mux_option) 416 if mux is None: 417 logging.warning('Signal name %s has no mux', mux_option) 418 # Do not add pinmux option to group 419 continue 420 # Get pin defaults for this pin 421 defaults = signal.get_pin_defaults() 422 # Get pin overrides 423 features = pin.find('mex:pin_features', NAMESPACES) 424 pin_overrides = {} 425 if features is not None: 426 for feature in pin.find('mex:pin_features', NAMESPACES): 427 pin_overrides[feature.attrib.get('name')] = feature.attrib.get('value') 428 # Get pin mux option overrides 429 for (override, value) in mux.get_mux_overrides().items(): 430 pin_overrides[override] = value 431 if imx_rt: 432 pin_props = self._imx_rt_props_to_dts(pin_overrides, defaults) 433 else: 434 pin_props = self._lpc_props_to_dts(pin_overrides, defaults) 435 self._pin_groups[pin_props].append(mux) 436 437 def __repr__(self): 438 """ 439 Get string representation of the object 440 """ 441 return "PinGroup(%s)" % (self._name) 442 443 def __eq__(self, obj): 444 """ 445 return true if two objects have the same pin group name 446 """ 447 return isinstance(obj, PinGroup) and self._name == obj._name 448 449 def __lt__(self, obj): 450 """ 451 Compare objects based on name 452 """ 453 if not isinstance(obj, PinGroup): 454 return True 455 return self._name < obj._name 456 457 def get_pin_props(self): 458 """ 459 Get all unique pin properties 460 """ 461 return self._pin_groups.keys() 462 463 def get_pins(self, props): 464 """ 465 Get all pins with a provided set of properties 466 @param props: property set 467 """ 468 return self._pin_groups[props] 469 470 def get_description(self): 471 """ 472 Get description of the pin group, if present. If no description present, 473 description will be "" 474 """ 475 return self._description 476 477 def get_name(self): 478 """ 479 Get pin group name 480 """ 481 return self._name 482 483 def _imx_rt_props_to_dts(self, props, defaults): 484 """ 485 Remap dictionary of property names from NXP defined values to 486 Zephyr ones (applies to RT600/RT500 properties) 487 @param props: Dictionary of NXP property names and values 488 @param defaults: Dictionary of NXP property names and default pin values 489 @return array of strings suitable for writing to DTS 490 """ 491 zephyr_props = [] 492 prop_mapping = { 493 # Slew rate property mappings 494 'normal': 'normal', 495 'slow': 'slow', 496 # Drive strength property mappings 497 'normal': 'normal', 498 'full': 'high' 499 } 500 # Lambda to convert property names to zephyr formatted strings 501 sanitize = lambda x: "\"" + prop_mapping[x] + "\"" if (x in prop_mapping) else "" 502 # Lambda to get property value or fallback on default 503 prop_val = lambda x: props[x] if x in props else defaults[x] 504 # For each property, append the provided override or the default 505 # Check pull settings 506 pull_enable = prop_val('pupdena') == 'enabled' 507 if pull_enable: 508 if prop_val('pupdsel') == 'pullDown': 509 zephyr_props.append('bias-pull-down') 510 else: 511 zephyr_props.append('bias-pull-up') 512 # input buffer 513 if prop_val('ibena') == 'enabled': 514 zephyr_props.append('input-enable') 515 # Slew rate settings 516 zephyr_props.append(f"slew-rate = {sanitize(prop_val('slew_rate'))}") 517 # Drive strength 518 zephyr_props.append(f"drive-strength = {sanitize(prop_val('drive'))}") 519 # analog switch 520 if prop_val('amena') == 'enabled': 521 zephyr_props.append('nxp,analog-mode') 522 # open drain 523 if prop_val('odena') == 'enabled': 524 zephyr_props.append('drive-open-drain') 525 # Pin invert settings 526 if prop_val('iiena') == 'enabled': 527 zephyr_props.append('nxp,invert') 528 529 return tuple(zephyr_props) 530 531 def _lpc_props_to_dts(self, props, defaults): 532 """ 533 Remap dictionary of property names from NXP defined values to 534 Zephyr ones (applies to LPC properties only) 535 @param props: Dictionary of NXP property names and values 536 @param defaults: Dictionary of NXP property names and default pin values 537 @return array of strings suitable for writing to DTS 538 """ 539 zephyr_props = [] 540 prop_mapping = { 541 # Slew rate property mappings 542 'standard': 'standard', 543 'fast': 'fast', 544 # power source property mappings 545 'signal3v3': '3v3', 546 'signal1v8': '1v8', 547 # i2cfilter property mappings 548 'nonhighspeedmode': 'slow', 549 'highspeedmode': 'fast' 550 } 551 # Lambda to convert property names to zephyr formatted strings 552 sanitize = lambda x: "\"" + prop_mapping[x] + "\"" if (x in prop_mapping) else "" 553 # Lambda to get property value or fallback on default 554 prop_val = lambda x: props[x] if x in props else defaults[x] if x in defaults else "" 555 # For each property, append the provided override or the default 556 # Check pull settings 557 if prop_val('mode') == 'pullUp': 558 zephyr_props.append('bias-pull-up') 559 elif prop_val('mode') == 'pullDown': 560 zephyr_props.append('bias-pull-down') 561 elif prop_val('mode') == 'repeater': 562 # Repeater latches the pin to the last input, to keep it from floating 563 zephyr_props.append('drive-push-pull') 564 # Slew rate settings 565 if 'slew_rate' in defaults: 566 zephyr_props.append(f"slew-rate = {sanitize(prop_val('slew_rate'))}") 567 # Pin invert settings 568 if prop_val('invert') == 'enabled': 569 zephyr_props.append('nxp,invert') 570 # open drain settings 571 if prop_val('open_drain') == 'enabled': 572 zephyr_props.append('drive-open-drain') 573 if 'asw' in defaults: 574 # analog switch setting (ASW bit for type A pins) 575 if prop_val('asw') == 'enabled' and prop_val('digimode') == 'disabled': 576 # Note we only respect the ASW setting if digimode is false, 577 # This condition can only occur when a mux specific override sets 578 # DIGIMODE=0, ASW=1. 579 zephyr_props.append('nxp,analog-mode') 580 if prop_val('asw0') == 'enabled' and prop_val('digimode') == 'disabled': 581 # analog switch setting 0 (LPC553x has two ASW bits) 582 zephyr_props.append('nxp,analog-mode') 583 if prop_val('asw1') == 'enabled' and prop_val('digimode') == 'disabled': 584 # analog switch setting 0 (LPC553x has two ASW bits) 585 zephyr_props.append('nxp,analog-alt-mode') 586 if 'ssel' in defaults: 587 # i2c power source select (SSEL bit for type I pins) 588 zephyr_props.append(f"power-source = {sanitize(prop_val('ssel'))}") 589 # i2c filter (FILTEROFF bit for type I pins) 590 # Note that when filter_off == 'enabled', the filter is actually on 591 if prop_val('filter_off') == 'enabled': 592 # Check i2c filter speed bit (I2CFILTER bit for type I pins) 593 zephyr_props.append(f"nxp,i2c-filter = {sanitize(prop_val('i2cfilter'))}") 594 # i2c pullup (ECS bit for type I pins) 595 if prop_val('ecs') == 'enabled': 596 zephyr_props.append('nxp,i2c-pullup') 597 # i2c mode (EGP bit for type I pins) 598 if prop_val('egp') == 'i2c': 599 zephyr_props.append('nxp,i2c-mode') 600 601 return tuple(zephyr_props) 602 603 604class NXPSdkUtil: 605 """ 606 Class for lpc configuration file parser 607 """ 608 def __init__(self, cfg_root, copyright_header = "", log_level = logging.ERROR): 609 """ 610 Initialize SDK utilities. 611 Providing a signal file will enable this class to parse MEX files, 612 and generate output DTS 613 @param cfg_root processor configuration folder root 614 @param copyright_header: copyright string to add to any generated file header 615 @param log_level: log level for SDK utility 616 """ 617 # Load the signal XML data 618 619 self._logger = logging.getLogger('') 620 self._logger.setLevel(log_level) 621 self._parse_signal_xml(pathlib.Path(cfg_root)/'signal_configuration.xml') 622 self._copyright = copyright_header 623 logging.info("Loaded %d configurable pin defs", len(self._pins)) 624 625 def _parse_signal_xml(self, signal_fn): 626 """ 627 Parses signal XML configuration file. Builds a list of pins, which can 628 be used to generate soc level DTSI file. 629 @param signal_fn: signal_configuration.xml file to parse 630 """ 631 self._pins = {} 632 try: 633 signal_tree = ET.parse(signal_fn) 634 except ET.ParseError: 635 logging.error("Could not parse provided signal file: %s", signal_fn) 636 return 637 638 signal_root = signal_tree.getroot() 639 640 self._part_num = signal_root.find("./part_information/part_number").get('id') 641 if 'MIMXRT7' in self._part_num: 642 # IMX RT600/500 series part. Different register layout and pin names 643 self._imx_rt = 'MIMXRT7XX' 644 elif 'MIMXRT' in self._part_num: 645 # IMX RT600/500 series part. Different register layout and pin names 646 self._imx_rt = 'MIMXRT5/6XX' 647 else: 648 self._imx_rt = '' 649 650 logging.info("Loaded XML for %s", self._part_num) 651 652 pins_node = signal_root.find("pins") 653 for pin in pins_node: 654 signal = SignalPin(pin, self._imx_rt) 655 # Only add valid signal pins to list 656 if signal.get_name() != '': 657 self._pins[signal.get_name()] = signal 658 659 def get_part_num(self): 660 """ 661 Return the part number this class is instantiated for 662 """ 663 return self._part_num 664 665 def write_pinctrl_defs(self, outputfile): 666 """ 667 Writes all pin mux options into pinctrl header file. Board level pin 668 groups can include this pinctrl header file to access pin control 669 defintions. 670 @param outputfile: file to write output pinctrl defs to 671 """ 672 file_header = ("/*\n" 673 f" * NOTE: File generated by {os.path.basename(__main__.__file__)}\n" 674 f" * from {self._part_num}/signal_configuration.xml\n" 675 " *\n" 676 f" * {self._copyright}\n" 677 " */\n" 678 "\n") 679 680 if self._imx_rt: 681 # Notes on the below macro: 682 # Due to IOPCTL instance number is nonunique, index variable is 683 # introduced to represent the label of IOPCTL instance. We use 684 # 4 bits to store index value. 685 # We store the pin and port values as an offset, because some pins 686 # do not follow a consistent offset. We use 12 bits to store this 687 # offset. 688 # Mux values range from 0-15, so we give 4 bits 689 # shift the offset to the MSBs of the mux value, so they 690 # don't conflict with pin configuration settings 691 # Store the mux value at the offset it will actually be written to the 692 # configuration register 693 mux_macro = ("#define IOPCTL_MUX(index, offset, mux)\t\t\\\n" 694 "\t((((index) & 0xF) << 16) |\t\t\\\n" 695 "\t(((offset) & 0xFFF) << 20) |\t\t\\\n" 696 "\t(((mux) & 0xF) << 0))\n\n") 697 else: 698 # Notes on the below macro: 699 # We store the pin and port values as an offset, because some pins 700 # do not follow a consistent offset. We use 12 bits to store this 701 # offset. 702 # Mux values range from 0-15, so we give 4 bits 703 # type values range from 0-2, so we give 3 bits 704 # shift the offset to the MSBs of the mux value, so they 705 # don't conflict with pin configuration settings 706 # Store the mux value at the offset it will actually be written to the 707 # configuration register 708 mux_macro = ("#define IOCON_MUX(offset, type, mux)\t\t\\\n" 709 "\t(((offset & 0xFFF) << 20) |\t\t\\\n" 710 "\t(((type) & 0x3) << 18) |\t\t\\\n" 711 "\t(((mux) & 0xF) << 0))\n\n" 712 "#define IOCON_TYPE_D 0x0\n" 713 "#define IOCON_TYPE_I 0x1\n" 714 "#define IOCON_TYPE_A 0x2\n\n") 715 with open(outputfile, "w", encoding="utf8") as file: 716 file.write(file_header) 717 # ifdef guard 718 file.write(f"#ifndef _ZEPHYR_DTS_BINDING_{self._part_num.upper()}_\n") 719 file.write(f"#define _ZEPHYR_DTS_BINDING_{self._part_num.upper()}_\n\n") 720 # Write macro to make port name 721 file.write(mux_macro) 722 # Write pins 723 for pin in sorted(self._pins.values()): 724 if not self._imx_rt: 725 # LPC IOCON has analog and I2C type pins, iMX RT does not 726 if 'asw' in pin.get_pin_defaults(): 727 pin_type = 'IOCON_TYPE_A' # Analog pin type 728 elif 'asw0' in pin.get_pin_defaults(): 729 pin_type = 'IOCON_TYPE_A' # LPC553x has ASW0 and ASW1 bits 730 elif 'ssel' in pin.get_pin_defaults(): 731 pin_type = 'IOCON_TYPE_I' 732 else: 733 pin_type = 'IOCON_TYPE_D' 734 sig_port = pin.get_port() 735 sig_pin = pin.get_pin() 736 for mux in sorted(pin.get_mux_options()): 737 index = mux.get_index() 738 offset = mux.get_offset() 739 label = mux.get_name() 740 mux = mux.get_mux() 741 if self._imx_rt: 742 file.write(f"#define {label} IOPCTL_MUX({index}, {offset}, {mux}) " 743 f"/* PIO{sig_port}_{sig_pin} */\n") 744 else: 745 file.write(f"#define {label} IOCON_MUX({offset}, {pin_type}, {mux}) " 746 f"/* PIO{sig_port}_{sig_pin} */\n") 747 748 file.write("\n#endif\n") 749 750 def _parse_mex_cfg(self, mexfile): 751 """ 752 Parses mex configuration into pin groups. 753 @param mexfile: mex configuration file to parse 754 @return parsed pin groups 755 """ 756 pin_groups = {} 757 try: 758 mex_xml = ET.parse(mexfile) 759 for function in mex_xml.findall( 760 'mex:tools/mex:pins/mex:functions_list/mex:function', NAMESPACES): 761 group = PinGroup(function, self._pins, self._imx_rt) 762 pin_groups[group.get_name()] = group 763 return pin_groups 764 except ET.ParseError: 765 logging.error("Could not parse mex file %s", mex_xml) 766 return None 767 768 def write_pinctrl_groups(self, mexfile, outputfile): 769 """ 770 Write pinctrl groups to disk as a parsed DTS file. Intended for use 771 with the output of @ref write_pinctrl_defs 772 @param mexfile: mex file to parse 773 @param outputfile: DTS pinctrl file to write pin groups to 774 """ 775 776 file_header = ("/*\n" 777 f" * NOTE: File generated by {os.path.basename(__main__.__file__)}\n" 778 f" * from {os.path.basename(mexfile)}\n" 779 " *\n" 780 f" * {self._copyright}\n" 781 " */\n" 782 "\n") 783 pin_groups = self._parse_mex_cfg(mexfile) 784 with open(outputfile, "w", encoding="utf8") as file: 785 file.write(file_header) 786 if self._imx_rt: 787 file.write(f"\n#include <nxp/nxp_imx/rt/{get_package_name(mexfile)}-pinctrl.h>\n\n") 788 else: 789 file.write(f"\n#include <nxp/lpc/{get_package_name(mexfile)}-pinctrl.h>\n\n") 790 file.write("&pinctrl {\n") 791 # Write pin groups back out to disk 792 for group in pin_groups.values(): 793 pin_props = group.get_pin_props() 794 description = group.get_description() 795 # if a description is present, write it 796 if description != "": 797 description_lines = description.split("\n") 798 if len(description_lines) == 1: 799 file.write(f"\t/* {description} */\n") 800 else: 801 file.write("\t/*\n") 802 for line in description_lines: 803 file.write(f"\t * {line}\n") 804 file.write("\t */\n") 805 logging.info("Writing pin group %s to disk", group.get_name()) 806 file.write(f"\t{group.get_name().lower()}: {group.get_name().lower()} {{\n") 807 idx = 0 808 for pin_prop in sorted(pin_props): 809 group_str = f"\t\tgroup{idx} {{\n" 810 # Write all pin names 811 group_str += "\t\t\tpinmux = " 812 for pin in group.get_pins(pin_prop): 813 group_str += f"<{pin.get_name()}>,\n\t\t\t\t" 814 # Strip out last 3 tabs and close pin name list 815 group_str = re.sub(r',\n\t\t\t\t$', ';\n', group_str) 816 idx += 1 817 # Write all pin props 818 if pin_prop is None: 819 logging.error("No pin properties present") 820 for prop in pin_prop: 821 group_str += f"\t\t\t{prop};\n" 822 group_str += "\t\t};\n" 823 file.write(group_str) 824 file.write("\t};\n\n") 825 file.write("};\n") 826 827""" 828Utility functions used to get details about board/processor from MEX file 829""" 830 831def get_board_name(mexfile): 832 """ 833 Extracts board name from a mex file 834 @param mexfile: mex file to parse for board name 835 """ 836 try: 837 config_tree = ET.parse(mexfile) 838 if config_tree.getroot().find('mex:common/mex:board', NAMESPACES) is None: 839 return get_processor_name(mexfile) + '-board' 840 return config_tree.getroot().find('mex:common/mex:board', 841 NAMESPACES).text 842 except ET.ParseError: 843 print(f"Malformed XML tree {mexfile}") 844 return None 845 except IOError: 846 print(f"File {mexfile} could not be opened") 847 return None 848 849def get_processor_name(mexfile): 850 """ 851 Extracts processor name from a mex file 852 @param mexfile: mex file to parse for processor name 853 """ 854 try: 855 config_tree = ET.parse(mexfile) 856 processor = config_tree.getroot().find('mex:common/mex:processor', 857 NAMESPACES) 858 if processor is None: 859 raise RuntimeError("Cannot locate processor name in MEX file. " 860 "Are you using v12 of the MCUXpresso configuration tools?") 861 return processor.text 862 except ET.ParseError: 863 print(f"Malformed XML tree {mexfile}") 864 return None 865 except IOError: 866 print(f"File {mexfile} could not be opened") 867 return None 868 869def get_package_name(mexfile): 870 """ 871 Extracts package name from a mex file 872 @param mexfile: mex file to parse for package name 873 """ 874 try: 875 config_tree = ET.parse(mexfile) 876 return config_tree.getroot().find('mex:common/mex:package', 877 NAMESPACES).text 878 except ET.ParseError: 879 print(f"Malformed XML tree {mexfile}") 880 return None 881 except IOError: 882 print(f"File {mexfile} could not be opened") 883 return None 884