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