@@ -39,14 +39,18 @@ def __init__(self, connection):
3939 self ._port = None
4040 for periph in connection .iter ('peripheral_signal_ref' ):
4141 self ._periph = periph .attrib .get ('peripheral' )
42+ self ._signal = periph .attrib .get ('signal' )
43+ self ._channel = periph .attrib .get ('channel' )
4244 for assign in connection .iter ('assign' ):
4345 reg = assign .attrib .get ('register' )
4446 val = assign .attrib .get ('bit_field_value' )
4547 logging .debug ('\t \t \t [ASSIGN] %s %s' , reg , val )
4648 # Only process PCR registers
4749 match = re .match (r'PORT([A-Z])_PCR(\d+)' , reg )
4850 if match :
49- self ._name += f"_PT{ match .group (1 )} { match .group (2 )} "
51+ # For muxes like PTC5, do not append peripheral name
52+ if re .match (r'PT[A-Z]\d+' , self ._name ) is None :
53+ self ._name += f"_PT{ match .group (1 )} { match .group (2 )} "
5054 self ._port = match .group (1 )
5155 self ._pin = int (match .group (2 ))
5256 self ._mux = int (val , 16 )
@@ -72,6 +76,12 @@ def get_port(self):
7276 """
7377 return self ._port
7478
79+ def get_signal (self ):
80+ """
81+ Get mux signal name
82+ """
83+ return self ._signal
84+
7585 def get_pin (self ):
7686 """
7787 Get mux pin
@@ -90,6 +100,12 @@ def get_periph(self):
90100 """
91101 return self ._periph
92102
103+ def get_channel (self ):
104+ """
105+ Get channel number
106+ """
107+ return self ._channel
108+
93109 def __hash__ (self ):
94110 """
95111 Override hash method to return pin name as hash
@@ -136,7 +152,13 @@ def __init__(self, pin):
136152 mux_opt = MUXOption (connections )
137153 # Only append mux options with a valid name
138154 if mux_opt .get_name () != '' :
139- self ._mux_options [mux_opt .get_name ()] = mux_opt
155+ # Use a string of the peripheral name and signal here, since that
156+ # is what the MEX file uses as a key to find this mux option
157+ key = f"{ mux_opt .get_periph ()} _{ mux_opt .get_signal ()} "
158+ if mux_opt .get_channel () is not None :
159+ # Special case, use channel for key
160+ key = f"{ mux_opt .get_periph ()} _{ mux_opt .get_channel ()} "
161+ self ._mux_options [key ] = mux_opt
140162
141163 def __repr__ (self ):
142164 """
@@ -251,6 +273,112 @@ def _get_pin_properties(self, props):
251273 prop_mapping [prop_id ][state .attrib ['id' ]] = bit_value
252274 return prop_mapping
253275
276+ class PinGroup :
277+ """
278+ Internal class representing pin group
279+ """
280+ def __init__ (self , function , signal_map ):
281+ """
282+ Creates a pin group
283+ @param function: function xml structure from MEX configuration file
284+ @param signal_map: Signal mapping, maps signal names to signal pins
285+ """
286+ self ._name = function .attrib .get ('name' )
287+ pins = function .find ('mex:pins' , NAMESPACES )
288+ signal_regex = re .compile (r'PT[A-Z]\d+' )
289+ # Build dictionary mapping pin properties to pins. This allows us to
290+ # group pins based on shared configuration
291+ self ._pin_groups = collections .defaultdict (lambda : [])
292+ for pin in pins :
293+ # find signal defintion for this pin
294+ signal_name = signal_regex .search (pin .attrib .get ('pin_signal' ))
295+ if signal_name is None or (not signal_name .group (0 ) in signal_map ):
296+ # No definition for this pin mapping
297+ logging .warning ('Signal %s not matched' , pin .attrib .get ('pin_signal' ))
298+ continue
299+ signal = signal_map [signal_name .group (0 )]
300+ # Get mux option for this pin
301+ key = f"{ pin .attrib .get ('peripheral' )} _{ pin .attrib .get ('signal' )} "
302+ channel = re .search (r'(\d+)' , pin .attrib .get ('signal' ))
303+ # Special case, use channel for key
304+ if channel :
305+ key = f"{ pin .attrib .get ('peripheral' )} _{ channel .group (1 )} "
306+ mux = signal .get_mux_connection (key )
307+ if mux is None :
308+ # Do not add pinmux option to group
309+ continue
310+ # Get pin defaults for this pin
311+ defaults = signal .get_pin_defaults ()
312+ # Get pin overrides
313+ features = pin .find ('mex:pin_features' , NAMESPACES )
314+ if features is not None :
315+ pin_overrides = {}
316+ for feature in pin .find ('mex:pin_features' , NAMESPACES ):
317+ pin_overrides [feature .attrib .get ('name' )] = feature .attrib .get ('value' )
318+ pin_props = self ._props_to_dts (pin_overrides , defaults )
319+ self ._pin_groups [pin_props ].append (mux )
320+
321+ def __repr__ (self ):
322+ """
323+ Get string representation of the object
324+ """
325+ return "PinGroup(%s)" % (self ._name )
326+
327+ def get_pin_props (self ):
328+ """
329+ Get all unique pin properties
330+ """
331+ return self ._pin_groups .keys ()
332+
333+ def get_pins (self , props ):
334+ """
335+ Get all pins with a provided set of properties
336+ @param props: property set
337+ """
338+ return self ._pin_groups [props ]
339+
340+ def get_name (self ):
341+ """
342+ Get pin group name
343+ """
344+ return self ._name
345+
346+ def _props_to_dts (self , props , defaults ):
347+ """
348+ Remap dictionary of property names from NXP defined values to
349+ Zephyr ones
350+ @param props: Dictionary of NXP property names and values
351+ @param defaults: Dictionary of NXP property names and default pin values
352+ @return array of strings suitable for writing to DTS
353+ """
354+ zephyr_props = []
355+ prop_mapping = {
356+ 'fast' : 'fast' ,
357+ 'slow' : 'slow' ,
358+ 'low' : 'low' ,
359+ 'high' : 'high' ,
360+ }
361+ # Lambda to convert property names to zephyr formatted strings
362+ sanitize = lambda x : "\" " + prop_mapping [x ] + "\" " if (x in prop_mapping ) else ""
363+ # Lambda to get property value or fallback on default
364+ prop_val = lambda x : props [x ] if x in props else defaults [x ]
365+ # Check pin defaults and overrides to see if the pin will have a pull
366+ pull_enable = prop_val ('pull_enable' ) == 'enable'
367+ # For each property, append the provided override or the default
368+ zephyr_props .append (f"drive-strength = { sanitize (prop_val ('drive_strength' ))} " )
369+ if prop_val ('open_drain' ) == 'enable' :
370+ zephyr_props .append ('drive-open-drain' )
371+ if pull_enable :
372+ # If pull is enabled, select pull up or pull down
373+ if prop_val ('pull_select' ) == 'up' :
374+ zephyr_props .append ('bias-pull-up' )
375+ else :
376+ zephyr_props .append ('bias-pull-down' )
377+ zephyr_props .append (f"slew-rate = { sanitize (prop_val ('slew_rate' ))} " )
378+ if prop_val ('passive_filter' ) == 'enable' :
379+ zephyr_props .append ("nxp,passive-filter" )
380+ return tuple (zephyr_props )
381+
254382
255383class NXPSdkUtil :
256384 """
@@ -271,7 +399,7 @@ def __init__(self, signal_file, copyright_header = "", log_level = logging.ERROR
271399 self ._logger .setLevel (log_level )
272400 self ._parse_signal_xml (signal_file )
273401 self ._copyright = copyright_header
274- logging .info ("Loaded %d pin defs" , len (self ._pins ))
402+ logging .info ("Loaded %d configurable pin defs" , len (self ._pins ))
275403
276404 def _parse_signal_xml (self , signal_fn ):
277405 """
@@ -381,4 +509,66 @@ def write_pinctrl_defs(self, outputfile):
381509 self ._write_pins ('c' , pcr_pins , file )
382510 self ._write_pins ('d' , pcr_pins , file )
383511 self ._write_pins ('e' , pcr_pins , file )
384-
512+
513+ def _parse_mex_cfg (self , mexfile ):
514+ """
515+ Parses mex configuration into pin groups.
516+ @param mexfile: mex configuration file to parse
517+ @return parsed pin groups
518+ """
519+ pin_groups = {}
520+ try :
521+ mex_xml = ET .parse (mexfile )
522+ for function in mex_xml .findall (
523+ 'mex:tools/mex:pins/mex:functions_list/mex:function' , NAMESPACES ):
524+ group = PinGroup (function , self ._pins )
525+ pin_groups [group .get_name ()] = group
526+ return pin_groups
527+ except ET .ParseError :
528+ logging .error ("Could not parse mex file %s" , mex_xml )
529+ return None
530+
531+ def write_pinctrl_groups (self , mexfile , outputfile ):
532+ """
533+ Write pinctrl groups to disk as a parsed DTS file. Intended for use
534+ with the output of @ref write_pinctrl_defs
535+ @param mexfile: mex file to parse
536+ @param outputfile: DTS pinctrl file to write pin groups to
537+ """
538+ file_header = ("/*\n "
539+ " * NOTE: Autogenerated file by kinetis_signal2dts.py\n "
540+ f" * for { self ._part_num } /signal_configuration.xml\n "
541+ " *\n "
542+ f" * { self ._copyright } \n "
543+ " * SPDX-License-Identifier: Apache-2.0\n "
544+ " */\n "
545+ "\n " )
546+ pin_groups = self ._parse_mex_cfg (mexfile )
547+ with open (outputfile , "w" , encoding = "utf8" ) as file :
548+ file .write (file_header )
549+ file .write ("&pinctrl {\n " )
550+ # Write pin groups back out to disk
551+ for group in pin_groups .values ():
552+ pin_props = group .get_pin_props ()
553+ if len (pin_props ) == 0 :
554+ # Do not write to disk
555+ continue
556+ logging .info ("Writing pin group %s to disk" , group .get_name ())
557+ file .write (f"\t { group .get_name ().lower ()} :{ group .get_name ().lower ()} : {{\n " )
558+ idx = 0
559+ for pin_prop in sorted (pin_props ):
560+ group_str = f"\t \t group{ idx } {{\n "
561+ # Write all pin names
562+ group_str += "\t \t \t pins = <"
563+ for pin in group .get_pins (pin_prop ):
564+ group_str += f"&{ pin .get_name ()} \n \t \t \t \t "
565+ # Strip out last 3 tabs and close pin name list
566+ group_str = re .sub (r'\n\t\t\t\t$' , '>;\n ' , group_str )
567+ idx += 1
568+ # Write all pin props
569+ for prop in pin_prop :
570+ group_str += f"\t \t \t { prop } \n "
571+ group_str += "\t \t };\n "
572+ file .write (group_str )
573+ file .write ("\t };\n \n " )
574+ file .write ("};\n " )
0 commit comments