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