1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2021 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_tpm.h"
10 
11 /*
12  * $Coverage Justification Reference$
13  *
14  * $Justification tpm_c_ref_1$
15  * Hardware limitations make this code impossible to implement.
16  *
17  * $Justification tpm_c_ref_2$
18  * Because the incoming base is invalid, the second judgment is not continued after the first condition
19  * is established.
20  *
21  * $Justification tpm_c_ref_3$
22  * Hardware limitations, the 32-bit counter register makes counterMax = 0xffffffffu,
23  * and the mod vlue can't be greater than or equal to counterMax after the operation.
24  *
25  * $Justification tpm_c_ref_4$
26  * If the incoming base can not make (1U == (uint8_t)FSL_FEATURE_TPM_POL_HAS_EFFECTn(base) true,
27  * the subsequent register operation is incorrect.
28  *
29  */
30 
31 /*******************************************************************************
32  * Definitions
33  ******************************************************************************/
34 
35 /* Component ID definition, used by tools. */
36 #ifndef FSL_COMPONENT_ID
37 #define FSL_COMPONENT_ID "platform.drivers.tpm"
38 #endif
39 
40 #define TPM_COMBINE_SHIFT (8U)
41 
42 /*******************************************************************************
43  * Prototypes
44  ******************************************************************************/
45 /*!
46  * @brief Gets the instance from the base address
47  *
48  * @param base TPM peripheral base address
49  *
50  * @return The TPM instance
51  */
52 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
53 static uint32_t TPM_GetInstance(TPM_Type *base);
54 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
55 
56 /*******************************************************************************
57  * Variables
58  ******************************************************************************/
59 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
60 /*! @brief Pointers to TPM bases for each instance. */
61 static TPM_Type *const s_tpmBases[] = TPM_BASE_PTRS;
62 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
63 
64 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
65 /*! @brief Pointers to TPM clocks for each instance. */
66 static const clock_ip_name_t s_tpmClocks[] = TPM_CLOCKS;
67 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
68 
69 /*******************************************************************************
70  * Code
71  ******************************************************************************/
72 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
TPM_GetInstance(TPM_Type * base)73 static uint32_t TPM_GetInstance(TPM_Type *base)
74 {
75     uint32_t instance;
76     uint32_t tpmArrayCount = (sizeof(s_tpmBases) / sizeof(s_tpmBases[0]));
77 
78     /* Find the instance index from base address mappings. */
79     /*
80      * $Branch Coverage Justification$
81      * (instance >= tpmArrayCount) not covered.
82      * The peripheral base address is always valid.
83      */
84     for (instance = 0; instance < tpmArrayCount; instance++)
85     {
86         if (s_tpmBases[instance] == base)
87         {
88             break;
89         }
90     }
91 
92     assert(instance < tpmArrayCount);
93 
94     return instance;
95 }
96 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
97 
98 /*!
99  * brief Ungates the TPM clock and configures the peripheral for basic operation.
100  *
101  * note This API should be called at the beginning of the application using the TPM driver.
102  *
103  * param base   TPM peripheral base address
104  * param config Pointer to user's TPM config structure.
105  */
TPM_Init(TPM_Type * base,const tpm_config_t * config)106 void TPM_Init(TPM_Type *base, const tpm_config_t *config)
107 {
108     assert(NULL != config);
109 
110 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
111     /* Enable the module clock */
112     (void)CLOCK_EnableClock(s_tpmClocks[TPM_GetInstance(base)]);
113 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
114 
115 #if defined(FSL_FEATURE_TPM_HAS_GLOBAL) && FSL_FEATURE_TPM_HAS_GLOBAL
116     /* TPM reset is available on certain SoC's */
117     TPM_Reset(base);
118 #endif
119 
120     /* Set the clock prescale factor */
121     base->SC = TPM_SC_PS(config->prescale);
122 #if !(defined(FSL_FEATURE_TPM_HAS_NO_CONF) && FSL_FEATURE_TPM_HAS_NO_CONF)
123     /* Setup the counter operation */
124     base->CONF = TPM_CONF_DOZEEN(config->enableDoze) | TPM_CONF_GTBEEN(config->useGlobalTimeBase) |
125                  TPM_CONF_GTBSYNC(config->syncGlobalTimeBase) | TPM_CONF_CROT(config->enableReloadOnTrigger) |
126                  TPM_CONF_CSOT(config->enableStartOnTrigger) | TPM_CONF_CSOO(config->enableStopOnOverflow) |
127 #if defined(FSL_FEATURE_TPM_HAS_PAUSE_COUNTER_ON_TRIGGER) && FSL_FEATURE_TPM_HAS_PAUSE_COUNTER_ON_TRIGGER
128                  TPM_CONF_CPOT(config->enablePauseOnTrigger) |
129 #endif
130 #if defined(FSL_FEATURE_TPM_HAS_EXTERNAL_TRIGGER_SELECTION) && FSL_FEATURE_TPM_HAS_EXTERNAL_TRIGGER_SELECTION
131                  TPM_CONF_TRGSRC(config->triggerSource) | TPM_CONF_TRGPOL(config->extTriggerPolarity) |
132 #endif
133                  TPM_CONF_TRGSEL(config->triggerSelect);
134     if (true == config->enableDebugMode)
135     {
136         base->CONF |= TPM_CONF_DBGMODE_MASK;
137     }
138     else
139     {
140         base->CONF &= ~TPM_CONF_DBGMODE_MASK;
141     }
142 #endif
143 #if defined(FSL_FEATURE_TPM_HAS_POL) && FSL_FEATURE_TPM_HAS_POL
144     base->POL = config->chnlPolarity;
145 #endif
146 }
147 
148 /*!
149  * brief Stops the counter and gates the TPM clock
150  *
151  * param base TPM peripheral base address
152  */
TPM_Deinit(TPM_Type * base)153 void TPM_Deinit(TPM_Type *base)
154 {
155 #if defined(FSL_FEATURE_TPM_HAS_SC_CLKS) && FSL_FEATURE_TPM_HAS_SC_CLKS
156     /* Stop the counter */
157     base->SC &= ~TPM_SC_CLKS_MASK;
158 #else
159     /* Stop the counter */
160     base->SC &= ~TPM_SC_CMOD_MASK;
161 #endif
162 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
163     /* Gate the TPM clock */
164     (void)CLOCK_DisableClock(s_tpmClocks[TPM_GetInstance(base)]);
165 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
166 }
167 
168 /*!
169  * brief  Fill in the TPM config struct with the default settings
170  *
171  * The default values are:
172  * code
173  *     config->prescale = kTPM_Prescale_Divide_1;
174  *     config->useGlobalTimeBase = false;
175  *     config->syncGlobalTimeBase = false;
176  *     config->dozeEnable = false;
177  *     config->dbgMode = false;
178  *     config->enableReloadOnTrigger = false;
179  *     config->enableStopOnOverflow = false;
180  *     config->enableStartOnTrigger = false;
181  *#if FSL_FEATURE_TPM_HAS_PAUSE_COUNTER_ON_TRIGGER
182  *     config->enablePauseOnTrigger = false;
183  *#endif
184  *     config->triggerSelect = kTPM_Trigger_Select_0;
185  *#if FSL_FEATURE_TPM_HAS_EXTERNAL_TRIGGER_SELECTION
186  *     config->triggerSource = kTPM_TriggerSource_External;
187  *     config->extTriggerPolarity = kTPM_ExtTrigger_Active_High;
188  *#endif
189  *#if defined(FSL_FEATURE_TPM_HAS_POL) && FSL_FEATURE_TPM_HAS_POL
190  *     config->chnlPolarity = 0U;
191  *#endif
192  * endcode
193  * param config Pointer to user's TPM config structure.
194  */
TPM_GetDefaultConfig(tpm_config_t * config)195 void TPM_GetDefaultConfig(tpm_config_t *config)
196 {
197     assert(NULL != config);
198 
199     /* Initializes the configure structure to zero. */
200     (void)memset(config, 0, sizeof(*config));
201 
202     /* TPM clock divide by 1 */
203     config->prescale = kTPM_Prescale_Divide_1;
204 #if !(defined(FSL_FEATURE_TPM_HAS_NO_CONF) && FSL_FEATURE_TPM_HAS_NO_CONF)
205     /* Use internal TPM counter as timebase */
206     config->useGlobalTimeBase = false;
207     /* Disable internal TPM counter sync with global timebase */
208     config->syncGlobalTimeBase = false;
209     /* TPM counter continues in doze mode */
210     config->enableDoze = false;
211     /* TPM counter pauses when in debug mode */
212     config->enableDebugMode = false;
213     /* TPM counter will not be reloaded on input trigger */
214     config->enableReloadOnTrigger = false;
215     /* TPM counter continues running after overflow */
216     config->enableStopOnOverflow = false;
217     /* TPM counter starts immediately once it is enabled */
218     config->enableStartOnTrigger = false;
219 #if defined(FSL_FEATURE_TPM_HAS_PAUSE_COUNTER_ON_TRIGGER) && FSL_FEATURE_TPM_HAS_PAUSE_COUNTER_ON_TRIGGER
220     config->enablePauseOnTrigger = false;
221 #endif
222     /* Choose trigger select 0 as input trigger for controlling counter operation */
223     config->triggerSelect = kTPM_Trigger_Select_0;
224 #if defined(FSL_FEATURE_TPM_HAS_EXTERNAL_TRIGGER_SELECTION) && FSL_FEATURE_TPM_HAS_EXTERNAL_TRIGGER_SELECTION
225     /* Choose external trigger source (high active) to control counter operation */
226     config->triggerSource      = kTPM_TriggerSource_External;
227     config->extTriggerPolarity = kTPM_ExtTrigger_Active_High;
228 #endif
229 #if defined(FSL_FEATURE_TPM_HAS_POL) && FSL_FEATURE_TPM_HAS_POL
230     /* Default channel polarity is active high */
231     config->chnlPolarity = 0U;
232 #endif
233 #endif
234 }
235 
236 /*!
237  * brief Calculates the counter clock prescaler.
238  *
239  * This function calculates the values for SC[PS].
240  *
241  * param base                TPM peripheral base address
242  * param counterPeriod_Hz    The desired frequency in Hz which corresponding to the time when the counter reaches the
243  * mod value param srcClock_Hz   TPM counter clock in Hz
244  *
245  * return Calculated clock prescaler value.
246  */
TPM_CalculateCounterClkDiv(TPM_Type * base,uint32_t counterPeriod_Hz,uint32_t srcClock_Hz)247 tpm_clock_prescale_t TPM_CalculateCounterClkDiv(TPM_Type *base, uint32_t counterPeriod_Hz, uint32_t srcClock_Hz)
248 {
249     uint32_t counterMax = TPM_MAX_COUNTER_VALUE(base);
250     uint32_t i;
251     assert(((srcClock_Hz / 2U) > counterPeriod_Hz) && ((srcClock_Hz / 128U / counterMax) <= counterPeriod_Hz));
252 
253     for (i = 0U; i < (uint32_t)kTPM_Prescale_Divide_128; i++)
254     {
255         if ((srcClock_Hz / (1UL << i) / counterMax) < counterPeriod_Hz)
256         {
257             break;
258         }
259     }
260     return (tpm_clock_prescale_t)i;
261 }
262 
263 /*!
264  * brief Set parameters for PWM duty cycle and edges for a single tpm channel
265  *
266  * User calls this function to configure the mode, duty cycle and edge of a single PWM signal.
267  *
268  * param base        TPM peripheral base address
269  * param mod         PWM period
270  * param mode        PWM operation mode, options available in enumeration ::tpm_pwm_mode_t
271  * param chnlParams  A structure for configuring PWM channel parameters, used to configure the channel.
272  *
273  * return kStatus_Success if the PWM setup was successful,
274  *         kStatus_Error on failure
275  */
TPM_SetupSinglePwmChannel(TPM_Type * base,uint32_t mod,tpm_pwm_mode_t mode,tpm_chnl_pwm_signal_param_t chnlParams)276 static status_t TPM_SetupSinglePwmChannel(TPM_Type *base,
277                                           uint32_t mod,
278                                           tpm_pwm_mode_t mode,
279                                           tpm_chnl_pwm_signal_param_t chnlParams)
280 {
281     uint32_t cnv;
282     uint32_t counterMax = TPM_MAX_COUNTER_VALUE(base);
283     uint8_t controlBits;
284     uint8_t chnlId;
285 
286     /* MSnB:MSnA field value always be 10, ELSnB:ELSnA field value should config according to the channel params */
287 #if defined(FSL_FEATURE_TPM_HAS_PAUSE_LEVEL_SELECT) && FSL_FEATURE_TPM_HAS_PAUSE_LEVEL_SELECT
288     controlBits =
289         (uint8_t)((uint32_t)kTPM_ChnlMSBMask | TPM_CnSC_ELSB(chnlParams.pauseLevel) | TPM_CnSC_ELSA(chnlParams.level));
290 #else
291     controlBits = ((uint8_t)kTPM_ChnlMSBMask | ((uint8_t)chnlParams.level << TPM_CnSC_ELSA_SHIFT));
292 #endif
293     chnlId = (uint8_t)chnlParams.chnlNumber;
294     /* Return error if requested dutycycle/chnlNumber is greater than the max allowed */
295     /*
296      * $Branch Coverage Justification$
297      * (-1 == (int8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base)) not covered.
298      * This function is called inside the TPM_SetupPwm() function.
299      * If you enter an invalid base, the function will not work properly.
300      */
301     if ((chnlId >= (uint8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base)) ||
302         /*
303          * $Branch Coverage Justification$
304          * (chnlId >= (uint8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base)) not covered.  $ref tpm_c_ref_2$.
305          */
306         (-1 == (int8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base)))
307     {
308         return kStatus_InvalidArgument;
309     }
310     /* Return error if requested dutycycle is greater than the max allowed or MOD equal to 0xFFFF when it want get a
311      * 100% duty cycle PWM signal*/
312     /*
313      * $Branch Coverage Justification$
314      * (mod == counterMax) not covered. $ref tpm_c_ref_3$.
315      */
316     if (((chnlParams.dutyCyclePercent == 100U) && (mod == counterMax)) || (chnlParams.dutyCyclePercent > 100U))
317     {
318         return kStatus_OutOfRange;
319     }
320 
321 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
322     if (mode == kTPM_CombinedPwm)
323     {
324         /* Check added for combined mode */
325         /*
326          * $Branch Coverage Justification$
327          * (chnlId >= ((uint8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base) / 2U)) not covered. $ref tpm_c_ref_2$.
328          */
329         if ((chnlId >= ((uint8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base) / 2U)) ||
330             (1U != (uint8_t)FSL_FEATURE_TPM_COMBINE_HAS_EFFECTn(base)))
331         {
332             /* The instance should support combine mode and the channel number should be the pair number */
333             return kStatus_InvalidArgument;
334         }
335         if (((chnlParams.firstEdgeDelayPercent + chnlParams.dutyCyclePercent) > 100U) ||
336             ((chnlParams.firstEdgeDelayPercent > 0U) && (chnlParams.dutyCyclePercent == 0U)) ||
337             ((chnlParams.firstEdgeDelayPercent == 0U) && (chnlParams.deadTimeValue[0] != 0U)))
338         {
339             /* Return error if the following situation occurs :
340              * firstEdgeDelayPercent + dutyCyclePercent > 100
341              * firstEdgeDelayPercent > 0 and dutyCyclePercent == 0
342              * firstEdgeDelayPercent == 0 and deadTimeValue[0] != 0
343              */
344             return kStatus_OutOfRange;
345         }
346         /* Configure delay of the first edge */
347         uint32_t cnvFirstEdge;
348         /* Configure dutycycle */
349         if (chnlParams.dutyCyclePercent == 0U)
350         {
351             cnvFirstEdge = mod + 1U;
352             cnv          = 0;
353         }
354         else if (chnlParams.dutyCyclePercent == 100U)
355         {
356             cnvFirstEdge = 0U;
357             cnv          = mod + 1U;
358         }
359         else
360         {
361             cnvFirstEdge = (mod * chnlParams.firstEdgeDelayPercent) / 100U;
362             cnv          = (mod * chnlParams.dutyCyclePercent) / 100U;
363         }
364 
365         /* Set the combine bit for the channel pair */
366         base->COMBINE |= 1UL << (TPM_COMBINE_SHIFT * chnlId);
367 
368         chnlId *= 2U;
369         /* Set deadtime insertion for the channel pair using channel filter register */
370         uint32_t filterVal = base->FILTER;
371         /* Clear the channel pair's filter values */
372         filterVal &=
373             ~(((uint32_t)TPM_FILTER_CH0FVAL_MASK | TPM_FILTER_CH1FVAL_MASK) << (chnlId * TPM_FILTER_CH1FVAL_SHIFT));
374         /* Shift the deadtime insertion value to the right place in the register */
375         filterVal |= (TPM_FILTER_CH0FVAL(chnlParams.deadTimeValue[0]) | TPM_FILTER_CH1FVAL(chnlParams.deadTimeValue[1]))
376                      << (chnlId * TPM_FILTER_CH1FVAL_SHIFT);
377         base->FILTER = filterVal;
378 
379         /* When switching mode, disable channel n first */
380         TPM_DisableChannel(base, (tpm_chnl_t)chnlId);
381         /* Set the requested PWM mode for channel n, under combine PWM mode, the active level is opposite of
382          * edge-aligned mode */
383         TPM_EnableChannel(base, (tpm_chnl_t)chnlId, controlBits ^ TPM_CnSC_ELSA_MASK);
384         /* Set the channel n value */
385         do
386         {
387             base->CONTROLS[chnlId].CnV = cnvFirstEdge;
388             /*
389              * $Branch Coverage Justification$
390              * (cnvFirstEdge != base->CONTROLS[chnlId].CnV) not covered. $ref tpm_c_ref_1$.
391              */
392         } while (cnvFirstEdge != base->CONTROLS[chnlId].CnV);
393 
394         chnlId += 1U;
395         /* When switching mode, disable channel n + 1 */
396         TPM_DisableChannel(base, (tpm_chnl_t)chnlId);
397 #if defined(FSL_FEATURE_TPM_HAS_PAUSE_LEVEL_SELECT) && FSL_FEATURE_TPM_HAS_PAUSE_LEVEL_SELECT
398         /* Select the pause level for second channel */
399         controlBits = (uint8_t)((uint32_t)kTPM_ChnlMSBMask | TPM_CnSC_ELSB(chnlParams.secPauseLevel) |
400                                 TPM_CnSC_ELSA(chnlParams.level));
401 #endif
402         /* Set the requested PWM mode for channel n + 1 */
403         if (chnlParams.enableComplementary)
404         {
405             /* Change the polarity on the second channel get complementary PWM signals */
406             TPM_EnableChannel(base, (tpm_chnl_t)chnlId, controlBits);
407         }
408         else
409         {
410             /* Second channel use same control bits as first channel */
411             TPM_EnableChannel(base, (tpm_chnl_t)chnlId, controlBits ^ TPM_CnSC_ELSA_MASK);
412         }
413 
414         /* Set the channel n+1 value */
415         do
416         {
417             base->CONTROLS[chnlId].CnV = cnvFirstEdge + cnv;
418             /*
419              * $Branch Coverage Justification$
420              * ((cnvFirstEdge + cnv) != base->CONTROLS[chnlId].CnV) not covered. $ref tpm_c_ref_1$.
421              */
422         } while ((cnvFirstEdge + cnv) != base->CONTROLS[chnlId].CnV);
423     }
424     else
425     {
426 #endif
427         /* Configure dutycycle */
428         if (chnlParams.dutyCyclePercent == 100U)
429         {
430             cnv = mod + 1U;
431         }
432         else
433         {
434             cnv = (mod * chnlParams.dutyCyclePercent) / 100U;
435         }
436         /* Fix ERROR050050 When TPM is configured in EPWM mode as PS = 0, the compare event is missed on
437         the first reload/overflow after writing 1 to the CnV register and causes an incorrect duty output.*/
438 #if (defined(FSL_FEATURE_TPM_HAS_ERRATA_050050) && FSL_FEATURE_TPM_HAS_ERRATA_050050)
439         assert(!(mode == kTPM_EdgeAlignedPwm && cnv == 1U && (base->SC & TPM_SC_PS_MASK) == kTPM_Prescale_Divide_1));
440 #endif
441         /* When switching mode, disable channel first */
442         TPM_DisableChannel(base, (tpm_chnl_t)chnlId);
443         /* Set the requested PWM mode, output mode MSnB:MSnA field value set to 10 */
444         TPM_EnableChannel(base, (tpm_chnl_t)chnlId, controlBits);
445         do
446         {
447             base->CONTROLS[chnlId].CnV = cnv;
448             /*
449              * $Branch Coverage Justification$
450              * (cnv != base->CONTROLS[chnlId].CnV) not covered. $ref tpm_c_ref_1$.
451              */
452         } while (cnv != base->CONTROLS[chnlId].CnV);
453 
454 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
455     }
456 #endif
457     return kStatus_Success;
458 }
459 
460 /*!
461  * brief Configures the PWM signal parameters
462  *
463  * User calls this function to configure the PWM signals period, mode, dutycycle and edge. Use this
464  * function to configure all the TPM channels that will be used to output a PWM signal
465  *
466  * param base        TPM peripheral base address
467  * param chnlParams  Array of PWM channel parameters to configure the channel(s)
468  * param numOfChnls  Number of channels to configure, this should be the size of the array passed in
469  * param mode        PWM operation mode, options available in enumeration ::tpm_pwm_mode_t
470  * param pwmFreq_Hz  PWM signal frequency in Hz
471  * param srcClock_Hz TPM counter clock in Hz
472  *
473  * return kStatus_Success if the PWM setup was successful,
474  *         kStatus_Error on failure
475  */
TPM_SetupPwm(TPM_Type * base,const tpm_chnl_pwm_signal_param_t * chnlParams,uint8_t numOfChnls,tpm_pwm_mode_t mode,uint32_t pwmFreq_Hz,uint32_t srcClock_Hz)476 status_t TPM_SetupPwm(TPM_Type *base,
477                       const tpm_chnl_pwm_signal_param_t *chnlParams,
478                       uint8_t numOfChnls,
479                       tpm_pwm_mode_t mode,
480                       uint32_t pwmFreq_Hz,
481                       uint32_t srcClock_Hz)
482 {
483     assert(NULL != chnlParams);
484 
485     uint32_t mod        = 0U;
486     uint32_t counterMax = TPM_MAX_COUNTER_VALUE(base);
487     uint32_t tpmClock   = (srcClock_Hz / (1UL << (base->SC & TPM_SC_PS_MASK)));
488     status_t status     = kStatus_Success;
489 
490     if ((0U == pwmFreq_Hz) || (0U == srcClock_Hz) || (0U == numOfChnls) || (tpmClock < pwmFreq_Hz))
491     {
492         return kStatus_InvalidArgument;
493     }
494 
495     switch (mode)
496     {
497 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
498         case kTPM_CombinedPwm:
499 #endif
500         case kTPM_EdgeAlignedPwm:
501             base->SC &= ~TPM_SC_CPWMS_MASK;
502             mod = (tpmClock / pwmFreq_Hz) - 1U;
503 
504             /*
505              * $Branch Coverage Justification$
506              * (mod > counterMax) not covered. $ref tpm_c_tpm_3$.
507              */
508             if ((mod > counterMax) || (mod == 0U))
509             {
510                 /*  The MOD greater than the maximum allowed (some instanse only support 16-bit counter) or smaller than
511                 1, probably would require changing clock source to get the desired frequency. */
512                 status = kStatus_OutOfRange;
513             }
514             break;
515         case kTPM_CenterAlignedPwm:
516             base->SC |= TPM_SC_CPWMS_MASK;
517             mod = tpmClock / (pwmFreq_Hz * 2u);
518             /*
519              * $Branch Coverage Justification$
520              * (mod > counterMax >> 1U) not covered. $ref tpm_c_tpm_3$.
521              */
522             if ((mod > (counterMax >> 1U)) || (mod == 0U))
523             {
524                 /* MOD values have additional requirements under center-aligned MODE, it must be kept in the range
525                  * of 0x1 ~ 0x7FFF (under 16-bit counter). */
526                 status = kStatus_OutOfRange;
527             }
528             break;
529         default:
530             /* All the cease have been listed above, the default case should not be reached. */
531             status = kStatus_InvalidArgument;
532             break;
533     }
534     if (kStatus_Success != status)
535     {
536         return status;
537     }
538     /* Set the PWM period */
539     base->MOD = mod;
540 
541     /* Setup each TPM channel */
542     for (uint8_t i = 0; i < numOfChnls; i++)
543     {
544         /* Setup a single PWM channel */
545         status = TPM_SetupSinglePwmChannel(base, mod, mode, *chnlParams);
546         if (status != kStatus_Success)
547         {
548             return status;
549         }
550         chnlParams++;
551     }
552 
553 #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL
554     /* The TPM's QDCTRL register required to be effective */
555     if (1U == (uint8_t)FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base))
556     {
557         /* Clear quadrature Decoder mode because in quadrature Decoder mode PWM doesn't operate*/
558         base->QDCTRL &= ~TPM_QDCTRL_QUADEN_MASK;
559     }
560 #endif
561 
562     return kStatus_Success;
563 }
564 
565 /*!
566  * brief Update the duty cycle of an active PWM signal
567  *
568  * param base              TPM peripheral base address
569  * param chnlNumber        The channel number. In combined mode, this represents
570  *                          the channel pair number
571  * param currentPwmMode    The current PWM mode set during PWM setup
572  * param dutyCyclePercent  New PWM pulse width, value should be between 0 to 100
573  *                          0=inactive signal(0% duty cycle)...
574  *                          100=active signal (100% duty cycle)
575  * return kStatus_Success if the PWM setup was successful,
576  *        kStatus_Error on failure
577  */
TPM_UpdatePwmDutycycle(TPM_Type * base,tpm_chnl_t chnlNumber,tpm_pwm_mode_t currentPwmMode,uint8_t dutyCyclePercent)578 status_t TPM_UpdatePwmDutycycle(TPM_Type *base,
579                                 tpm_chnl_t chnlNumber,
580                                 tpm_pwm_mode_t currentPwmMode,
581                                 uint8_t dutyCyclePercent)
582 {
583     uint32_t cnv, mod;
584     uint32_t counterMax = TPM_MAX_COUNTER_VALUE(base);
585     uint8_t chnlId      = (uint8_t)chnlNumber;
586 
587     /* Return error if requested chnlNumber is greater than the max allowed */
588     /* Return error if requested dutycycle/chnlNumber is greater than the max allowed */
589     if ((chnlId >= (uint8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base)) ||
590         (-1 == (int8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base)))
591     {
592         return kStatus_InvalidArgument;
593     }
594     /* Get the PWM period */
595     mod = base->MOD & counterMax;
596     /* Return error if requested dutycycle is greater than the max allowed */
597     if (((dutyCyclePercent == 100U) && (mod == counterMax)) || (dutyCyclePercent > 100U))
598     {
599         /* MOD can't equal to 0xFFFF otherwise it can't get a 100% duty cycle PWM signal. */
600         return kStatus_OutOfRange;
601     }
602 
603 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
604     if (currentPwmMode == kTPM_CombinedPwm)
605     {
606         /* Check added for combined mode */
607         /*
608          * $Branch Coverage Justification$
609          * (chnlId >= ((uint8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base) / 2U)) not covered. $ref tpm_c_ref_2$.
610          */
611         if ((chnlId >= ((uint8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base) / 2U)) ||
612             (1U != (uint8_t)FSL_FEATURE_TPM_COMBINE_HAS_EFFECTn(base)))
613         {
614             /* The instance should support combine mode and the channel number should be the pair number */
615             return kStatus_InvalidArgument;
616         }
617         uint32_t cnvFirstEdge;
618         cnv = (mod * dutyCyclePercent) / 100U;
619         if ((base->CONTROLS[chnlId * 2U].CnV & counterMax) > mod)
620         {
621             cnvFirstEdge = 0U;
622         }
623         else
624         {
625             cnvFirstEdge = base->CONTROLS[chnlId * 2U].CnV & counterMax;
626         }
627 
628         if (((cnvFirstEdge + cnv) > mod) || ((cnv == 0U) && (cnvFirstEdge > 0U)))
629         {
630             /* Return error if the following situation occurs :
631              * firstEdgeDelayPercent + dutyCyclePercent > 100
632              * firstEdgeDelayPercent > 0 and dutyCyclePercent == 0
633              */
634             return kStatus_OutOfRange;
635         }
636         if (cnv == mod)
637         {
638             /* 100% duty cycle */
639             cnv = mod + 1U;
640         }
641         else if (cnv == 0U)
642         {
643             /* 0% duty cycle */
644             cnvFirstEdge = mod + 1U;
645         }
646         else
647         {
648             ; /* Intentional empty */
649         }
650 
651         do
652         {
653             base->CONTROLS[chnlId * 2U].CnV = cnvFirstEdge;
654             /*
655              * $Branch Coverage Justification$
656              * (cnvFirstEdge != base->CONTROLS[chnlId * 2U].CnV) not covered. $ref tpm_c_ref_1$.
657              */
658         } while (cnvFirstEdge != base->CONTROLS[chnlId * 2U].CnV);
659         do
660         {
661             base->CONTROLS[(chnlId * 2U) + 1U].CnV = cnvFirstEdge + cnv;
662         } while ((cnvFirstEdge + cnv) != base->CONTROLS[(chnlId * 2U) + 1U].CnV);
663     }
664     else
665     {
666 #endif
667         if (dutyCyclePercent == 100U)
668         {
669             cnv = mod + 1U;
670         }
671         else
672         {
673             cnv = (mod * dutyCyclePercent) / 100U;
674         }
675         /* Fix ERROR050050 */
676 #if (defined(FSL_FEATURE_TPM_HAS_ERRATA_050050) && FSL_FEATURE_TPM_HAS_ERRATA_050050)
677         assert(!(currentPwmMode == kTPM_EdgeAlignedPwm && cnv == 1U &&
678                  (base->SC & TPM_SC_PS_MASK) == kTPM_Prescale_Divide_1));
679 #endif
680 
681         do
682         {
683             base->CONTROLS[chnlId].CnV = cnv;
684             /*
685              * $Branch Coverage Justification$
686              * (cnv != base->CONTROLS[chnlId].CnV) not covered. $ref tpm_c_ref_1$.
687              */
688         } while (cnv != base->CONTROLS[chnlId].CnV);
689 
690 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
691     }
692 #endif
693     return kStatus_Success;
694 }
695 
696 /*!
697  * brief Update the edge level selection for a channel
698  *
699  * note When the TPM has PWM pause level select feature (FSL_FEATURE_TPM_HAS_PAUSE_LEVEL_SELECT = 1), the PWM output
700  *      cannot be turned off by selecting the output level. In this case, must use TPM_DisableChannel API to close
701  * the PWM output.
702  *
703  * param base       TPM peripheral base address
704  * param chnlNumber The channel number
705  * param level      The level to be set to the ELSnB:ELSnA field; valid values are 00, 01, 10, 11.
706  *                   See the appropriate SoC reference manual for details about this field.
707  */
TPM_UpdateChnlEdgeLevelSelect(TPM_Type * base,tpm_chnl_t chnlNumber,uint8_t level)708 void TPM_UpdateChnlEdgeLevelSelect(TPM_Type *base, tpm_chnl_t chnlNumber, uint8_t level)
709 {
710     assert(((uint8_t)chnlNumber < (uint8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base)) &&
711            (-1 != (int8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base)));
712 
713     uint8_t control = TPM_GetChannelContorlBits(base, chnlNumber);
714 
715     /* When switching mode, disable channel first */
716     TPM_DisableChannel(base, chnlNumber);
717 
718     /* Clear the field and write the new level value */
719     control &= ~(uint8_t)(TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
720     control |= ((uint8_t)level << TPM_CnSC_ELSA_SHIFT) & (TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
721 
722     /* Enable channle with new level value */
723     TPM_EnableChannel(base, chnlNumber, control);
724 }
725 
726 /*!
727  * brief Enables capturing an input signal on the channel using the function parameters.
728  *
729  * When the edge specified in the captureMode argument occurs on the channel, the TPM counter is captured into
730  * the CnV register. The user has to read the CnV register separately to get this value.
731  *
732  * param base        TPM peripheral base address
733  * param chnlNumber  The channel number
734  * param captureMode Specifies which edge to capture
735  */
TPM_SetupInputCapture(TPM_Type * base,tpm_chnl_t chnlNumber,tpm_input_capture_edge_t captureMode)736 void TPM_SetupInputCapture(TPM_Type *base, tpm_chnl_t chnlNumber, tpm_input_capture_edge_t captureMode)
737 {
738     assert(((uint8_t)chnlNumber < (uint8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base)) &&
739            (-1 != (int8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base)));
740 
741 #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL
742     /* The TPM's QDCTRL register required to be effective */
743     if (1U == (uint8_t)FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base))
744     {
745         /* Clear quadrature Decoder mode for channel 0 or 1*/
746         if (((uint32_t)chnlNumber == 0u) || ((uint32_t)chnlNumber == 1u))
747         {
748             base->QDCTRL &= ~TPM_QDCTRL_QUADEN_MASK;
749         }
750     }
751 #endif
752 
753 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
754     /* The TPM's COMBINE register required to be effective */
755     if (1U == (uint8_t)FSL_FEATURE_TPM_COMBINE_HAS_EFFECTn(base))
756     {
757         /* Clear the combine bit for chnlNumber */
758         base->COMBINE &= ~((uint32_t)1U << (((uint32_t)chnlNumber / 2U) * TPM_COMBINE_SHIFT));
759     }
760 #endif
761     /*Clear CPWMS bit when the input capture mode is selected */
762     base->SC &= ~TPM_SC_CPWMS_MASK;
763     /* When switching mode, disable channel first */
764     TPM_DisableChannel(base, chnlNumber);
765     /* Enable channel with new requested input capture mode */
766     TPM_EnableChannel(base, chnlNumber, (uint8_t)captureMode);
767 }
768 
769 /*!
770  * brief Configures the TPM to generate timed pulses.
771  *
772  * When the TPM counter matches the value of compareVal argument (this is written into CnV reg), the channel
773  * output is changed based on what is specified in the compareMode argument.
774  *
775  * param base         TPM peripheral base address
776  * param chnlNumber   The channel number
777  * param compareMode  Action to take on the channel output when the compare condition is met
778  * param compareValue Value to be programmed in the CnV register.
779  */
TPM_SetupOutputCompare(TPM_Type * base,tpm_chnl_t chnlNumber,tpm_output_compare_mode_t compareMode,uint32_t compareValue)780 void TPM_SetupOutputCompare(TPM_Type *base,
781                             tpm_chnl_t chnlNumber,
782                             tpm_output_compare_mode_t compareMode,
783                             uint32_t compareValue)
784 {
785     assert(((uint8_t)chnlNumber < (uint8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base)) &&
786            (-1 != (int8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base)));
787 
788 #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL
789     /* The TPM's QDCTRL register required to be effective */
790     if (1U == (uint8_t)FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base))
791     {
792         /* Clear quadrature Decoder mode for channel 0 or 1 */
793         if (((uint32_t)chnlNumber == 0U) || ((uint32_t)chnlNumber == 1U))
794         {
795             base->QDCTRL &= ~TPM_QDCTRL_QUADEN_MASK;
796         }
797     }
798 #endif
799 
800     /*Clear CPWMS bit when the output compare mode is selected */
801     base->SC &= ~TPM_SC_CPWMS_MASK;
802     /* When switching mode, disable channel first  */
803     TPM_DisableChannel(base, chnlNumber);
804     /* Enable channel with new requested compare mode */
805     TPM_EnableChannel(base, chnlNumber, (uint8_t)compareMode);
806 
807     /* Setup the compare value */
808     do
809     {
810         base->CONTROLS[chnlNumber].CnV = compareValue;
811         /*
812          * $Branch Coverage Justification$
813          * (compareValue != base->CONTROLS[chnlNumber].CnV) not covered. $ref tpm_c_ref_1$.
814          */
815     } while (compareValue != base->CONTROLS[chnlNumber].CnV);
816 }
817 
818 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
819 /*!
820  * brief Configures the dual edge capture mode of the TPM.
821  *
822  * This function allows to measure a pulse width of the signal on the input of channel of a
823  * channel pair. The filter function is disabled if the filterVal argument passed is zero.
824  *
825  * param base           TPM peripheral base address
826  * param chnlPairNumber The TPM channel pair number; options are 0, 1, 2, 3
827  * param edgeParam      Sets up the dual edge capture function
828  * param filterValue    Filter value, specify 0 to disable filter.
829  */
TPM_SetupDualEdgeCapture(TPM_Type * base,tpm_chnl_t chnlPairNumber,const tpm_dual_edge_capture_param_t * edgeParam,uint32_t filterValue)830 void TPM_SetupDualEdgeCapture(TPM_Type *base,
831                               tpm_chnl_t chnlPairNumber,
832                               const tpm_dual_edge_capture_param_t *edgeParam,
833                               uint32_t filterValue)
834 {
835     assert(NULL != edgeParam);
836     assert(((uint8_t)chnlPairNumber < (uint8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base) / 2U) &&
837            (-1 != (int8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base)));
838     assert(1U == (uint8_t)FSL_FEATURE_TPM_COMBINE_HAS_EFFECTn(base));
839 
840     uint32_t reg;
841     uint32_t u32flag;
842     uint8_t chnlId = (uint8_t)chnlPairNumber * 2U;
843 
844 #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL
845     /* The TPM's QDCTRL register required to be effective */
846     if (1U == (uint8_t)FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base))
847     {
848         /* Clear quadrature Decoder mode for channel 0 or 1*/
849         if (chnlId == 0u)
850         {
851             base->QDCTRL &= ~TPM_QDCTRL_QUADEN_MASK;
852         }
853     }
854 #endif
855 
856     /* When switching mode, disable channel first */
857     TPM_DisableChannel(base, (tpm_chnl_t)chnlId);
858     chnlId++;
859     TPM_DisableChannel(base, (tpm_chnl_t)chnlId);
860     chnlId--;
861 
862     /* Now, the registers for input mode can be operated. */
863     if (true == edgeParam->enableSwap)
864     {
865         u32flag = TPM_COMBINE_COMBINE0_MASK | TPM_COMBINE_COMSWAP0_MASK;
866         /* Set the combine and swap bits for the channel pair */
867         base->COMBINE |= u32flag << (TPM_COMBINE_SHIFT * (uint32_t)chnlPairNumber);
868 
869         /* Input filter setup for channel n+1 input */
870         reg = base->FILTER;
871         reg &= ~((uint32_t)TPM_FILTER_CH0FVAL_MASK << (TPM_FILTER_CH1FVAL_SHIFT * (chnlId + 1U)));
872         reg |= (filterValue << (TPM_FILTER_CH1FVAL_SHIFT * (chnlId + 1U)));
873         base->FILTER = reg;
874     }
875     else
876     {
877         reg = base->COMBINE;
878         /* Clear the swap bit for the channel pair */
879         reg &= ~((uint32_t)TPM_COMBINE_COMSWAP0_MASK << ((uint32_t)chnlPairNumber * TPM_COMBINE_COMSWAP0_SHIFT));
880         u32flag = TPM_COMBINE_COMBINE0_MASK;
881 
882         /* Set the combine bit for the channel pair */
883         reg |= u32flag << (TPM_COMBINE_SHIFT * (uint32_t)chnlPairNumber);
884         base->COMBINE = reg;
885 
886         /* Input filter setup for channel n input */
887         reg = base->FILTER;
888         reg &= ~((uint32_t)TPM_FILTER_CH0FVAL_MASK << (TPM_FILTER_CH1FVAL_SHIFT * chnlId));
889         reg |= (filterValue << (TPM_FILTER_CH1FVAL_SHIFT * chnlId));
890         base->FILTER = reg;
891     }
892 
893     /* Setup the edge detection from channel n and n+1*/
894     TPM_EnableChannel(base, (tpm_chnl_t)chnlId, (uint8_t)edgeParam->currChanEdgeMode);
895     chnlId++;
896     TPM_EnableChannel(base, (tpm_chnl_t)chnlId, (uint8_t)edgeParam->nextChanEdgeMode);
897 }
898 #endif
899 
900 #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL
901 /*!
902  * brief Configures the parameters and activates the quadrature decode mode.
903  *
904  * param base         TPM peripheral base address
905  * param phaseAParams Phase A configuration parameters
906  * param phaseBParams Phase B configuration parameters
907  * param quadMode     Selects encoding mode used in quadrature decoder mode
908  */
TPM_SetupQuadDecode(TPM_Type * base,const tpm_phase_params_t * phaseAParams,const tpm_phase_params_t * phaseBParams,tpm_quad_decode_mode_t quadMode)909 void TPM_SetupQuadDecode(TPM_Type *base,
910                          const tpm_phase_params_t *phaseAParams,
911                          const tpm_phase_params_t *phaseBParams,
912                          tpm_quad_decode_mode_t quadMode)
913 {
914     assert(NULL != phaseAParams);
915     assert(NULL != phaseBParams);
916     assert(1U == (uint8_t)FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base));
917 
918     /* Disable channel 0 */
919     TPM_DisableChannel(base, kTPM_Chnl_0);
920 
921     uint32_t reg;
922 
923     /* Set Phase A filter value */
924     reg = base->FILTER;
925     reg &= ~(TPM_FILTER_CH0FVAL_MASK);
926     reg |= TPM_FILTER_CH0FVAL(phaseAParams->phaseFilterVal);
927     base->FILTER = reg;
928 
929 #if defined(FSL_FEATURE_TPM_HAS_POL) && FSL_FEATURE_TPM_HAS_POL
930     /*
931      * $Branch Coverage Justification$
932      * (1U != FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base)) not covered. $ref tpm_c_ref_4$.
933      */
934     if (1U == (uint8_t)FSL_FEATURE_TPM_POL_HAS_EFFECTn(base))
935     {
936         /* Set Phase A polarity */
937         if (kTPM_QuadPhaseInvert == phaseAParams->phasePolarity)
938         {
939             base->POL |= TPM_POL_POL0_MASK;
940         }
941         else
942         {
943             base->POL &= ~TPM_POL_POL0_MASK;
944         }
945     }
946 #endif
947 
948     /* Disable channel 1 */
949     TPM_DisableChannel(base, kTPM_Chnl_0);
950 
951     /* Set Phase B filter value */
952     reg = base->FILTER;
953     reg &= ~(TPM_FILTER_CH1FVAL_MASK);
954     reg |= TPM_FILTER_CH1FVAL(phaseBParams->phaseFilterVal);
955     base->FILTER = reg;
956 #if defined(FSL_FEATURE_TPM_HAS_POL) && FSL_FEATURE_TPM_HAS_POL
957     /*
958      * $Branch Coverage Justification$
959      * (1U != FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base)) not covered. $ref tpm_c_ref_4$.
960      */
961     if (1U == (uint8_t)FSL_FEATURE_TPM_POL_HAS_EFFECTn(base))
962     {
963         /* Set Phase B polarity */
964         if (kTPM_QuadPhaseInvert == phaseBParams->phasePolarity)
965         {
966             base->POL |= TPM_POL_POL1_MASK;
967         }
968         else
969         {
970             base->POL &= ~TPM_POL_POL1_MASK;
971         }
972     }
973 #endif
974 
975     /* Set Quadrature mode */
976     reg = base->QDCTRL;
977     reg &= ~(TPM_QDCTRL_QUADMODE_MASK);
978     reg |= TPM_QDCTRL_QUADMODE(quadMode);
979     base->QDCTRL = reg;
980 
981     /* Enable Quad decode */
982     base->QDCTRL |= TPM_QDCTRL_QUADEN_MASK;
983 }
984 
985 #endif
986 
987 /*!
988  * brief Enables the selected TPM interrupts.
989  *
990  * param base TPM peripheral base address
991  * param mask The interrupts to enable. This is a logical OR of members of the
992  *             enumeration ::tpm_interrupt_enable_t
993  */
TPM_EnableInterrupts(TPM_Type * base,uint32_t mask)994 void TPM_EnableInterrupts(TPM_Type *base, uint32_t mask)
995 {
996     uint32_t chnlInterrupts = (mask & 0xFFU);
997     uint8_t chnlNumber      = 0;
998 
999     /* Enable the timer overflow interrupt */
1000     if ((uint32_t)kTPM_TimeOverflowInterruptEnable == (mask & (uint32_t)kTPM_TimeOverflowInterruptEnable))
1001     {
1002         base->SC |= TPM_SC_TOIE_MASK;
1003     }
1004 
1005     /* Enable the channel interrupts */
1006     while (0U != chnlInterrupts)
1007     {
1008         if (0U != (chnlInterrupts & 0x1u))
1009         {
1010             base->CONTROLS[chnlNumber].CnSC |= TPM_CnSC_CHIE_MASK;
1011         }
1012         chnlNumber++;
1013         chnlInterrupts = chnlInterrupts >> 1U;
1014     }
1015 }
1016 
1017 /*!
1018  * brief Disables the selected TPM interrupts.
1019  *
1020  * param base TPM peripheral base address
1021  * param mask The interrupts to disable. This is a logical OR of members of the
1022  *             enumeration ::tpm_interrupt_enable_t
1023  */
TPM_DisableInterrupts(TPM_Type * base,uint32_t mask)1024 void TPM_DisableInterrupts(TPM_Type *base, uint32_t mask)
1025 {
1026     uint32_t chnlInterrupts = (mask & 0xFFU);
1027     uint8_t chnlNumber      = 0;
1028 
1029     /* Disable the timer overflow interrupt */
1030     if ((uint32_t)kTPM_TimeOverflowInterruptEnable == (mask & (uint32_t)kTPM_TimeOverflowInterruptEnable))
1031     {
1032         base->SC &= ~TPM_SC_TOIE_MASK;
1033     }
1034 
1035     /* Disable the channel interrupts */
1036     while (0U != chnlInterrupts)
1037     {
1038         if (0U != (chnlInterrupts & 0x1u))
1039         {
1040             base->CONTROLS[chnlNumber].CnSC &= ~TPM_CnSC_CHIE_MASK;
1041         }
1042         chnlNumber++;
1043         chnlInterrupts = chnlInterrupts >> 1U;
1044     }
1045 }
1046 
1047 /*!
1048  * brief Gets the enabled TPM interrupts.
1049  *
1050  * param base TPM peripheral base address
1051  *
1052  * return The enabled interrupts. This is the logical OR of members of the
1053  *         enumeration ::tpm_interrupt_enable_t
1054  */
TPM_GetEnabledInterrupts(TPM_Type * base)1055 uint32_t TPM_GetEnabledInterrupts(TPM_Type *base)
1056 {
1057     uint32_t enabledInterrupts = 0;
1058     uint32_t u32flag           = 1;
1059     int8_t chnlCount           = FSL_FEATURE_TPM_CHANNEL_COUNTn(base);
1060 
1061     /* The CHANNEL_COUNT macro returns -1 if it cannot match the TPM instance */
1062     assert(chnlCount != -1);
1063 
1064     /* Check if timer overflow interrupt is enabled */
1065     if (0U != (base->SC & TPM_SC_TOIE_MASK))
1066     {
1067         enabledInterrupts |= (uint32_t)kTPM_TimeOverflowInterruptEnable;
1068     }
1069 
1070     /* Check if the channel interrupts are enabled */
1071     while (chnlCount > 0)
1072     {
1073         chnlCount--;
1074         if (0U != (base->CONTROLS[chnlCount].CnSC & TPM_CnSC_CHIE_MASK))
1075         {
1076             enabledInterrupts |= (u32flag << (uint8_t)chnlCount);
1077         }
1078     }
1079 
1080     return enabledInterrupts;
1081 }
1082