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