1 /*
2  * Copyright 2018-2020 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "fsl_ostimer.h"
9 
10 /*******************************************************************************
11  * Definitions
12  ******************************************************************************/
13 
14 /* Component ID definition, used by tools. */
15 #ifndef FSL_COMPONENT_ID
16 #define FSL_COMPONENT_ID "platform.drivers.ostimer"
17 #endif
18 
19 /* Typedef for interrupt handler. */
20 typedef void (*ostimer_isr_t)(OSTIMER_Type *base, ostimer_callback_t cb);
21 
22 /*******************************************************************************
23  * Prototypes
24  ******************************************************************************/
25 /*!
26  * @brief Gets the instance from the base address
27  *
28  * @param base OSTIMER peripheral base address
29  *
30  * @return The OSTIMER instance
31  */
32 static uint32_t OSTIMER_GetInstance(OSTIMER_Type *base);
33 
34 #if (defined(FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY) && FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY)
35 /* @brief Translate the value from gray-code to decimal by the Code Gray in SYSCTL.
36  *
37  * @param gray The gray value input.
38  *
39  * @return the decimal value.
40  */
41 static uint64_t OSTIMER_GrayToDecimalbyCodeGray(uint64_t gray);
42 #endif /* FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY. */
43 
44 /* @brief Translate the value from gray-code to decimal. */
45 /*
46  * @param gray The gray value input.
47  *
48  * @return the decimal value.
49  */
50 static uint64_t OSTIMER_GrayToDecimal(uint64_t gray);
51 
52 /*******************************************************************************
53  * Variables
54  ******************************************************************************/
55 /* Array of OSTIMER handle. */
56 static ostimer_callback_t s_ostimerHandle[FSL_FEATURE_SOC_OSTIMER_COUNT];
57 /* Array of OSTIMER peripheral base address. */
58 static OSTIMER_Type *const s_ostimerBases[] = OSTIMER_BASE_PTRS;
59 /* Array of OSTIMER IRQ number. */
60 static const IRQn_Type s_ostimerIRQ[] = OSTIMER_IRQS;
61 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
62 /* Array of OSTIMER clock name. */
63 static const clock_ip_name_t s_ostimerClock[] = OSTIMER_CLOCKS;
64 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
65 
66 /* OSTIMER ISR for transactional APIs. */
67 #if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
68 static ostimer_isr_t s_ostimerIsr = (ostimer_isr_t)DefaultISR;
69 #else
70 static ostimer_isr_t s_ostimerIsr;
71 #endif
72 
73 /*******************************************************************************
74  * Code
75  ******************************************************************************/
76 
77 /* @brief Function for getting the instance number of OS timer. */
OSTIMER_GetInstance(OSTIMER_Type * base)78 static uint32_t OSTIMER_GetInstance(OSTIMER_Type *base)
79 {
80     uint32_t instance;
81 
82     /* Find the instance index from base address mappings. */
83     for (instance = 0; instance < ARRAY_SIZE(s_ostimerBases); instance++)
84     {
85         if (s_ostimerBases[instance] == base)
86         {
87             break;
88         }
89     }
90 
91     assert(instance < ARRAY_SIZE(s_ostimerBases));
92 
93     return instance;
94 }
95 
96 #if (defined(FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY) && FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY)
97 /* @brief Translate the value from gray-code to decimal by the Code Gray in SYSCTL.
98  *
99  * @param gray The gray value input.
100  *
101  * @return the decimal value.
102  */
OSTIMER_GrayToDecimalbyCodeGray(uint64_t gray)103 static uint64_t OSTIMER_GrayToDecimalbyCodeGray(uint64_t gray)
104 {
105     uint64_t decOut;
106 
107     SYSCTL->CODE_GRAY_LSB = (uint32_t)(gray & 0xFFFFFFFFU);
108     SYSCTL->CODE_GRAY_MSB = (uint32_t)((gray >> 32U) & 0x3FFU); // limit to 42bits as OSevent timer
109     __NOP();
110     decOut = ((uint64_t)(SYSCTL->CODE_BIN_MSB) & 0x3FFU) << 32U;
111     decOut |= (uint64_t)(SYSCTL->CODE_BIN_LSB);
112 
113     return decOut;
114 }
115 #endif /* FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY. */
116 
117 /* @brief Translate the value from gray-code to decimal. */
118 /*
119  * @param gray The gray value input.
120  *
121  * @return the decimal value.
122  */
OSTIMER_GrayToDecimal(uint64_t gray)123 static uint64_t OSTIMER_GrayToDecimal(uint64_t gray)
124 {
125 #if (defined(FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY) && FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY)
126     return OSTIMER_GrayToDecimalbyCodeGray(gray);
127 #else
128     uint64_t temp = gray;
129     while (temp != 0U)
130     {
131         temp >>= 1U;
132         gray ^= temp;
133     }
134 
135     return gray;
136 #endif /* FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY. */
137 }
138 
139 /* @brief Translate the value from decimal to gray-code. */
OSTIMER_DecimalToGray(uint64_t dec)140 static uint64_t OSTIMER_DecimalToGray(uint64_t dec)
141 {
142     return (dec ^ (dec >> 1U));
143 }
144 
145 /* @brief Enable the OSTIMER interrupt.
146  *
147  * After calling this function, the OSTIMER driver will enable/disable the IRQ and module interrupt enablement.
148  *
149  * @param base OSTIMER peripheral base address.
150  * @param enable enable/disable the IRQ and module interrupt enablement.
151  *               - true: Disable the IRQ and module interrupt enablement.
152  *               - false: Disable the IRQ and module interrupt enablement.
153  * @return none
154  */
OSTIMER_EnableInterrupt(OSTIMER_Type * base,bool enable)155 static void OSTIMER_EnableInterrupt(OSTIMER_Type *base, bool enable)
156 {
157     assert(NULL != base);
158 
159     if (enable)
160     {
161         /* Enable the IRQ and module interrupt enablement. */
162         (void)EnableIRQ(s_ostimerIRQ[OSTIMER_GetInstance(base)]);
163         base->OSEVENT_CTRL |= OSTIMER_OSEVENT_CTRL_OSTIMER_INTENA_MASK;
164 #if !(defined(FSL_FEATURE_PMC_HAS_NO_OSTIMER_REG) && FSL_FEATURE_PMC_HAS_NO_OSTIMER_REG)
165         PMC->OSTIMERr |= PMC_OSTIMER_DPDWAKEUPENABLE_MASK;
166 #endif
167     }
168     else
169     {
170         /* Clear interrupt flag, disable the IRQ and module interrupt enablement. */
171         (void)DisableIRQ(s_ostimerIRQ[OSTIMER_GetInstance(base)]);
172         base->OSEVENT_CTRL &= ~OSTIMER_OSEVENT_CTRL_OSTIMER_INTENA_MASK; /* Clear interrupt flag by writing 1. */
173 #if !(defined(FSL_FEATURE_PMC_HAS_NO_OSTIMER_REG) && FSL_FEATURE_PMC_HAS_NO_OSTIMER_REG)
174         PMC->OSTIMERr &= ~PMC_OSTIMER_DPDWAKEUPENABLE_MASK;
175 #endif
176     }
177 }
178 
179 /*!
180  * @brief Initializes an OSTIMER by turning it's clock on.
181  *
182  */
OSTIMER_Init(OSTIMER_Type * base)183 void OSTIMER_Init(OSTIMER_Type *base)
184 {
185     assert(NULL != base);
186 
187     uint32_t instance = OSTIMER_GetInstance(base);
188 
189 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
190 #if !(defined(FSL_FEATURE_PMC_HAS_NO_OSTIMER_REG) && FSL_FEATURE_PMC_HAS_NO_OSTIMER_REG)
191     /* Enable the OSTIMER 32k clock in PMC module. */
192     PMC->OSTIMERr |= PMC_OSTIMER_CLOCKENABLE_MASK;
193     PMC->OSTIMERr &= ~PMC_OSTIMER_OSC32KPD_MASK;
194 #endif
195     /* Enable clock for OSTIMER. */
196     CLOCK_EnableClock(s_ostimerClock[instance]);
197 #if (defined(FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY) && FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY)
198     CLOCK_EnableClock(kCLOCK_Sysctl);
199 #endif /* FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY. */
200 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
201 }
202 
203 /*!
204  * @brief Deinitializes a OSTIMER instance.
205  *
206  * This function shuts down OSTIMER clock
207  *
208  * @param base OSTIMER peripheral base address.
209  */
OSTIMER_Deinit(OSTIMER_Type * base)210 void OSTIMER_Deinit(OSTIMER_Type *base)
211 {
212 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
213     /* Enable clock for OSTIMER. */
214     CLOCK_DisableClock(s_ostimerClock[OSTIMER_GetInstance(base)]);
215 #if (defined(FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY) && FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY)
216     CLOCK_DisableClock(kCLOCK_Sysctl);
217 #endif /* FSL_FEATURE_SYSCTRL_HAS_CODE_GRAY. */
218 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
219 }
220 
221 /*!
222  * @brief Get OSTIMER status Flags.
223  *
224  * This returns the status flag.
225  * Currently, only match interrupt flag can be got.
226  *
227  * @param base OSTIMER peripheral base address.
228  * @return status register value
229  */
OSTIMER_GetStatusFlags(OSTIMER_Type * base)230 uint32_t OSTIMER_GetStatusFlags(OSTIMER_Type *base)
231 {
232     return base->OSEVENT_CTRL & OSTIMER_OSEVENT_CTRL_OSTIMER_INTRFLAG_MASK;
233 }
234 
235 /*!
236  * @brief Clear Status Interrupt Flags.
237  *
238  * This clears intr status flag.
239  * Currently, only match interrupt flag can be cleared.
240  *
241  * @param base OSTIMER peripheral base address.
242  * @param mask Clear bit mask.
243  * @return none
244  */
OSTIMER_ClearStatusFlags(OSTIMER_Type * base,uint32_t mask)245 void OSTIMER_ClearStatusFlags(OSTIMER_Type *base, uint32_t mask)
246 {
247     base->OSEVENT_CTRL |= mask;
248 }
249 
250 /*!
251  * @brief Set the match raw value for OSTIMER.
252  *
253  * This function will set a match value for OSTIMER with an optional callback. And this callback
254  * will be called while the data in dedicated pair match register is equals to the value of central EVTIMER.
255  * Please note that, the data format is gray-code, if decimal data was desired, please using OSTIMER_SetMatchValue().
256  *
257  * @param base   OSTIMER peripheral base address.
258  * @param count  OSTIMER timer match value.(Value is gray-code format)
259  *
260  * @param cb     OSTIMER callback (can be left as NULL if none, otherwise should be a void func(void)).
261  * @retval kStatus_Success - Set match raw value and enable interrupt Successfully.
262  * @retval kStatus_Fail    - Set match raw value fail.
263  */
OSTIMER_SetMatchRawValue(OSTIMER_Type * base,uint64_t count,ostimer_callback_t cb)264 status_t OSTIMER_SetMatchRawValue(OSTIMER_Type *base, uint64_t count, ostimer_callback_t cb)
265 {
266 #ifdef OSTIMER_OSEVENT_CTRL_MATCH_WR_RDY_MASK
267     uint64_t decValueTimer;
268 #endif
269     status_t status;
270     uint64_t tmp      = count;
271     uint32_t instance = OSTIMER_GetInstance(base);
272 
273     /* Clear interrupt flag, disable the IRQ and module interrupt enablement. */
274     OSTIMER_EnableInterrupt(base, false);
275 
276     s_ostimerIsr              = OSTIMER_HandleIRQ;
277     s_ostimerHandle[instance] = cb;
278 
279     /* Set the match value. */
280     base->MATCH_L = (uint32_t)tmp;
281     base->MATCH_H = (uint32_t)(tmp >> 32U);
282 
283 #ifdef OSTIMER_OSEVENT_CTRL_MATCH_WR_RDY_MASK
284     /* Workaround-2019-12-30:
285      * Since OSTimer's counter register is Gray-encoded, it would cost more time to write register. When EVTimer Match
286      * Write Ready bit is low, which means the previous match value has been updated successfully by that time, it is
287      * safe to reload (write) the Match Registers. Even if there is the RM comment that "In typical applications, it
288      * should not be necessary to test this bit", but we found the interruption would not be reported when the delta
289      * timer user added is smaller(IE: RT595 11us in 1MHz typical application) in release version." To prevent such
290      * issue from happening, we'd better wait for the match value to update successfully before enabling IRQ.
291      */
292     while (0U != (base->OSEVENT_CTRL & OSTIMER_OSEVENT_CTRL_MATCH_WR_RDY_MASK))
293     {
294     }
295 
296     /* After the WR_RDY bit became low, we need to check whether current time goes ahead of the match value we set.
297      * (1) If current timer value has gone ahead of the match value, the interrupt will not be reported before 64-bit
298      * timer value over flow. We need to check whether the interrupt flag has been set or not: if yes, we will enable
299      * interrupt and return success; if not, we will return fail directly.
300      * (2) If current timer value has not gone ahead of match value, we will enable interrupt and return success.
301      */
302     decValueTimer = OSTIMER_GetCurrentTimerValue(base);
303     if ((decValueTimer >= OSTIMER_GrayToDecimal(tmp)) &&
304         (0U == (base->OSEVENT_CTRL & (uint32_t)kOSTIMER_MatchInterruptFlag)))
305     {
306         status = kStatus_Fail;
307     }
308     else
309 #endif /* #ifdef OSTIMER_OSEVENT_CTRL_MATCH_WR_RDY_MASK */
310     {
311         /* Enable the module interrupt enablement. */
312         OSTIMER_EnableInterrupt(base, true);
313         status = kStatus_Success;
314     }
315 
316     return status;
317 }
318 
319 /*!
320  * @brief Set the match value for OSTIMER.
321  *
322  * This function will set a match value for OSTIMER with an optional callback. And this callback
323  * will be called while the data in dedicated pair match register is equals to the value of central EVTIMER.
324  *
325  * @param base   OSTIMER peripheral base address.
326  * @param count  OSTIMER timer match value.(Value is decimal format, and this value will be translate to Gray code in
327  * API. )
328  * @param cb  OSTIMER callback (can be left as NULL if none, otherwise should be a void func(void)).
329  * @retval kStatus_Success - Set match value and enable interrupt Successfully.
330  * @retval kStatus_Fail    - Set match value fail.
331  */
OSTIMER_SetMatchValue(OSTIMER_Type * base,uint64_t count,ostimer_callback_t cb)332 status_t OSTIMER_SetMatchValue(OSTIMER_Type *base, uint64_t count, ostimer_callback_t cb)
333 {
334     uint64_t tmp = OSTIMER_DecimalToGray(count);
335 
336     return OSTIMER_SetMatchRawValue(base, tmp, cb);
337 }
338 
339 /*!
340  * @brief Get current timer count value from OSTIMER.
341  *
342  * This function will get a decimal timer count value.
343  * The RAW value of timer count is gray code format, will be translated to decimal data internally.
344  *
345  * @param base   OSTIMER peripheral base address.
346  * @return Value of OSTIMER which will formated to decimal value.
347  */
OSTIMER_GetCurrentTimerValue(OSTIMER_Type * base)348 uint64_t OSTIMER_GetCurrentTimerValue(OSTIMER_Type *base)
349 {
350     uint64_t tmp = 0U;
351 
352     tmp = OSTIMER_GetCurrentTimerRawValue(base);
353 
354     return OSTIMER_GrayToDecimal(tmp);
355 }
356 
357 /*!
358  * @brief Get the capture value from OSTIMER.
359  *
360  * This function will get a capture decimal-value from OSTIMER.
361  * The RAW value of timer capture is gray code format, will be translated to decimal data internally.
362  *
363  * @param base   OSTIMER peripheral base address.
364  * @return Value of capture register, data format is decimal.
365  */
OSTIMER_GetCaptureValue(OSTIMER_Type * base)366 uint64_t OSTIMER_GetCaptureValue(OSTIMER_Type *base)
367 {
368     uint64_t tmp = 0U;
369 
370     tmp = OSTIMER_GetCaptureRawValue(base);
371 
372     return OSTIMER_GrayToDecimal(tmp);
373 }
374 
OSTIMER_HandleIRQ(OSTIMER_Type * base,ostimer_callback_t cb)375 void OSTIMER_HandleIRQ(OSTIMER_Type *base, ostimer_callback_t cb)
376 {
377     /* Clear interrupt flag, disable the IRQ and module interrupt enablement. */
378     OSTIMER_EnableInterrupt(base, false);
379 
380     if (cb != NULL)
381     {
382         cb();
383     }
384 }
385 
386 #if defined(OSTIMER0)
387 void OS_EVENT_DriverIRQHandler(void);
OS_EVENT_DriverIRQHandler(void)388 void OS_EVENT_DriverIRQHandler(void)
389 {
390     s_ostimerIsr(OSTIMER0, s_ostimerHandle[0]);
391     SDK_ISR_EXIT_BARRIER;
392 }
393 #endif
394 
395 #if defined(OSTIMER)
396 void OS_EVENT_DriverIRQHandler(void);
OS_EVENT_DriverIRQHandler(void)397 void OS_EVENT_DriverIRQHandler(void)
398 {
399     s_ostimerIsr(OSTIMER, s_ostimerHandle[0]);
400     SDK_ISR_EXIT_BARRIER;
401 }
402 #endif
403