1 /***************************************************************************//**
2 * \file cyhal_lptimer.c
3 *
4 * \brief
5 * Provides a high level interface for interacting with the Infineon Low-Power Timer.
6 * This interface abstracts out the chip specific details. If any chip specific
7 * functionality is necessary, or performance is critical the low level functions
8 * can be used directly.
9 *
10 ********************************************************************************
11 * \copyright
12 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
13 * an affiliate of Cypress Semiconductor Corporation
14 *
15 * SPDX-License-Identifier: Apache-2.0
16 *
17 * Licensed under the Apache License, Version 2.0 (the "License");
18 * you may not use this file except in compliance with the License.
19 * You may obtain a copy of the License at
20 *
21 *     http://www.apache.org/licenses/LICENSE-2.0
22 *
23 * Unless required by applicable law or agreed to in writing, software
24 * distributed under the License is distributed on an "AS IS" BASIS,
25 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26 * See the License for the specific language governing permissions and
27 * limitations under the License.
28 *******************************************************************************/
29 
30 /**
31 * \addtogroup group_hal_impl_lptimer LPTimer (Low-Power Timer)
32 * \ingroup group_hal_impl
33 * \{
34 * The maximum number of ticks that can set to an LPTimer is 0xFFF0FFFF for non CAT1C devices.
35 * It is not recommended to use 0xFFFFFFFF. This is to avoid overflowing both C0 and C1.
36 * For CAT1C devices (XMC), the maximum number of ticks that can be set to an LPTimer is 0xFFFFFFFF
37 * since C0 and C1 do not cascade.
38 *
39 * \section section_cat1c_lptimer_set_match LPTimer Set Match
40 * For CAT1C devices (XMC), \ref cyhal_lptimer_set_match will only trigger a single interrupt.
41 *
42 * \} group_hal_impl_lptimer
43 */
44 
45 #include "cmsis_compiler.h"
46 #include "cy_syslib.h"
47 #include "cy_sysint.h"
48 #include "cyhal_lptimer.h"
49 #include "cyhal_hwmgr.h"
50 #include "cyhal_system_impl.h"
51 #include "cyhal_utils.h"
52 #include "cyhal_irq_impl.h"
53 #include "cyhal_clock.h"
54 
55 #if (CYHAL_DRIVER_AVAILABLE_LPTIMER)
56 
57 #if defined(CY_IP_MXS40SRSS) || defined(CY_IP_MXS40SSRSS) || defined(CY_IP_MXS28SRSS) || defined(CY_IP_MXS22SRSS)
58 #include "cy_mcwdt.h"
59 #define _CYHAL_LPTIMER_MCWDT
60 #if (defined (CY_IP_MXS40SRSS) && (CY_IP_MXS40SRSS_VERSION >= 3)) || ((SRSS_NUM_MCWDT_B) > 0)
61 #define _CYHAL_LPTIMER_MCWDT_B
62 #if !defined(SRSS_NUM_MCWDT_B)
63 #define SRSS_NUM_MCWDT_B (SRSS_NUM_MCWDT)
64 #endif
65 #endif
66 #elif defined (CY_IP_M0S8WCO)
67 #include "cy_wdc.h"
68 #define Cy_MCWDT_Init               Cy_WDC_Init
69 #define Cy_MCWDT_DeInit             Cy_WDC_DeInit
70 #define Cy_MCWDT_Enable             Cy_WDC_Enable
71 #define Cy_MCWDT_Disable            Cy_WDC_Disable
72 #define Cy_MCWDT_GetInterruptStatus Cy_WDC_GetInterruptStatus
73 #define Cy_MCWDT_ClearInterrupt     Cy_WDC_ClearInterrupt
74 #define Cy_MCWDT_GetCount           Cy_WDC_GetCount
75 #define Cy_MCWDT_SetMatch           Cy_WDC_SetMatch
76 #define Cy_MCWDT_GetMatch           Cy_WDC_GetMatch
77 #define Cy_MCWDT_ResetCounters      Cy_WDC_ResetCounters
78 #define Cy_MCWDT_GetEnabledStatus   Cy_WDC_GetEnabledStatus
79 #define CY_MCWDT_COUNTER0           CY_WDC_COUNTER0
80 #define CY_MCWDT_COUNTER1           CY_WDC_COUNTER1
81 #define CY_MCWDT_COUNTER2           CY_WDC_COUNTER2
82 #define CY_MCWDT_CTR0               CY_WDC_COUNTER0_Msk
83 #define CY_MCWDT_CTR1               CY_WDC_COUNTER1_Msk
84 #define CY_MCWDT_CTR2               CY_WDC_COUNTER2_Msk
85 #define srss_interrupt_mcwdt_0_IRQn wco_interrupt_IRQn
86 #else
87 #error "Current HW block is not supported"
88 #endif
89 
90 #if defined(__cplusplus)
91 extern "C" {
92 #endif
93 
94 #if defined(_CYHAL_LPTIMER_MCWDT)
95 #if defined (_CYHAL_LPTIMER_MCWDT_B)
96 #define _CYHAL_LPTIMER_INSTANCES    (SRSS_NUM_MCWDT_B)
97 static MCWDT_Type * const _CYHAL_LPTIMER_BASE_ADDRESSES[] = {
98 #if SRSS_NUM_MCWDT >= 1
99     MCWDT0,
100 #endif
101 #if SRSS_NUM_MCWDT >= 2
102     MCWDT1,
103 #endif
104 #if SRSS_NUM_MCWDT >= 3
105     MCWDT2,
106 #endif
107 #else
108 #define _CYHAL_LPTIMER_INSTANCES    SRSS_NUM_MCWDT
109 static MCWDT_STRUCT_Type * const _CYHAL_LPTIMER_BASE_ADDRESSES[] = {
110 #if SRSS_NUM_MCWDT >= 1
111     MCWDT_STRUCT0,
112 #endif
113 #if SRSS_NUM_MCWDT >= 2
114     MCWDT_STRUCT1,
115 #endif
116 #if SRSS_NUM_MCWDT >= 3
117 #error "SRSS_NUM_MCWDT > 2 not supported"
118 #endif
119 #endif
120 };
121 #elif defined (CY_IP_M0S8WCO_INSTANCES)
122 #define _CYHAL_LPTIMER_INSTANCES CY_IP_M0S8WCO_INSTANCES
123 static WCO_Type * const _CYHAL_LPTIMER_BASE_ADDRESSES[] = {
124 #if CY_IP_M0S8WCO_INSTANCES >= 1
125     WCO,
126 #endif
127 #if CY_IP_M0S8WCO_INSTANCES >= 2
128 #error "CY_IP_M0S8WCO_INSTANCES > 1 not supported"
129 #endif
130 };
131 #else
132 #error "Current HW block is not supported"
133 #endif
134 
135 #define _CYHAL_LPTIMER_CTRL               (CY_MCWDT_CTR0 | CY_MCWDT_CTR1 | CY_MCWDT_CTR2)
136 #define _CYHAL_LPTIMER_MIN_DELAY          (3U) /* minimum amount of lfclk cycles of that LPTIMER can delay for. */
137 #if defined (_CYHAL_LPTIMER_MCWDT_B)
138 // The max sleep time for CAT1C devices is ~17.5 hours. This is because the toggle bit that is set for Counter2
139 // match value toggles every time the bit changes from 0 to 1 or from 1 to 0.
140 #define _CYHAL_LPTIMER_MAX_DELAY_TICKS    (0xffffffffUL) /* ~36hours, set to 0xffffffff since C0 and C1 do not cascade */
141 #else
142 #define _CYHAL_LPTIMER_MAX_DELAY_TICKS    (0xfff0ffffUL) /* ~36hours, Not set to 0xffffffff to avoid C0 and C1 both overflowing */
143 #endif
144 #define _CYHAL_LPTIMER_MAX_COUNTER_VAL    (0xffffffffUL) /* Maximum value of the counter before it rolls over */
145 
146 #define _CYHAL_LPTIMER_DEFAULT_PRIORITY   (3U)
147 
148 #define _CYHAL_LPTIMER_ISR_CALL_USER_CB_MASK            (0x01)
149 #define _CYHAL_LPTIMER_ISR_CRITICAL_SECTION_MASK        (0x02)
150 
151 #if defined(_CYHAL_LPTIMER_MCWDT)
152 static cyhal_lptimer_t *_cyhal_lptimer_config_structs[_CYHAL_LPTIMER_INSTANCES];
153 #if defined (_CYHAL_LPTIMER_MCWDT_B)
154 static const uint16_t _CYHAL_LPTIMER_RESET_TIME_US = 93;
155 static const uint16_t _CYHAL_LPTIMER_SETMATCH_TIME_US = 93;
156 static const cy_stc_mcwdt_config_t default_cfg = {
157                 .c0UpperLimit = 0xFFFF,
158                 .c0WarnLimit =  0xFFFF,
159                 .c0WarnAction = CY_MCWDT_WARN_ACTION_NONE,
160                 .c0UpperAction = CY_MCWDT_ACTION_NONE,
161                 .c0DebugRun = CY_MCWDT_ENABLE,
162                 .c1UpperLimit = 0xFFFF,
163                 .c1UpperAction = CY_MCWDT_ACTION_NONE,
164                 .c1DebugRun = CY_MCWDT_ENABLE,
165                 .c1WarnLimit =  0xFFFF,
166                 .c1WarnAction = CY_MCWDT_WARN_ACTION_INT,
167                 .c1AutoService = CY_MCWDT_ENABLE,
168                 .c2ToggleBit = 0,
169                 .c2Action = CY_MCWDT_CNT2_ACTION_INT,
170                 .coreSelect = CY_MCWDT_PAUSED_BY_NO_CORE,
171         };
172 #else
173 #if defined(CY_IP_MXS40SSRSS)
174 static const uint16_t _CYHAL_LPTIMER_RESET_TIME_US = 93; // Recommended value per PDL
175 #else
176 static const uint16_t _CYHAL_LPTIMER_RESET_TIME_US = 62;
177 #endif
178 static const uint16_t _CYHAL_LPTIMER_SETMATCH_TIME_US = 0;
179 static const cy_stc_mcwdt_config_t default_cfg = {
180                 .c0Match = 0xFFFF,
181                 .c1Match = 0xFFFF,
182                 .c0Mode = CY_MCWDT_MODE_INT,
183                 .c1Mode = CY_MCWDT_MODE_INT,
184                 .c2Mode = CY_MCWDT_MODE_NONE,
185                 .c2ToggleBit = 0,
186                 .c0ClearOnMatch = false,
187                 .c1ClearOnMatch = false,
188                 .c0c1Cascade = true,
189                 .c1c2Cascade = false
190         };
191 #endif
192 #elif defined (CY_IP_M0S8WCO)
193 static cyhal_lptimer_t *_cyhal_lptimer_config_structs[CY_IP_M0S8WCO_INSTANCES];
194 static const uint16_t _CYHAL_LPTIMER_RESET_TIME_US = 180;
195 static const uint16_t _CYHAL_LPTIMER_SETMATCH_TIME_US = 180;
196 static const cy_stc_wdc_config_t default_cfg = {
197                 .counter0Match = 0xFFFF,
198                 .counter1Match = 0xFFFF,
199                 .counter2ToggleBit = 0,
200                 .counter0Interrupt = false,
201                 .counter1Interrupt = true,
202                 .counter2Interrupt = false,
203                 .counter0ClearOnMatch = false,
204                 .counter1ClearOnMatch = true,
205                 .countersCascade = CY_WDC_CASCADE_COUNTERS01,
206                 .clockSource = CY_WDC_CLOCK_ILO
207         };
208 #else
209 #error "Current HW block is not supported"
210 #endif
211 
212 #if (_CYHAL_IRQ_MUXING)
213 static const _cyhal_system_irq_t _CYHAL_MCWDT_DISCONNECTED_IRQ = disconnected_IRQn;
214 #else
215 static const _cyhal_system_irq_t _CYHAL_MCWDT_DISCONNECTED_IRQ = unconnected_IRQn;
216 #endif
217 
218 #if defined(_CYHAL_LPTIMER_INSTANCES)
219 static const _cyhal_system_irq_t _CYHAL_MCWDT_IRQS[_CYHAL_LPTIMER_INSTANCES] =
220 {
221 #if (defined(COMPONENT_CM55) && defined(CY_IP_MXS22SRSS))
222 //For CM0P/CM55 cores for cat1d, there is no 0 irqn.
223     unconnected_IRQn,
224 #else
225     srss_interrupt_mcwdt_0_IRQn,
226 #endif
227 #if (_CYHAL_LPTIMER_INSTANCES >=2)
228 #if (defined(COMPONENT_CM33) && defined(CY_IP_MXS22SRSS))
229 //For CM0P/CM33 cores for cat1d, there is no 1 irqn
230     unconnected_IRQn,
231 #else
232     srss_interrupt_mcwdt_1_IRQn,
233 #endif
234 #endif
235 #if (_CYHAL_LPTIMER_INSTANCES ==3)
236     srss_interrupt_mcwdt_2_IRQn,
237 #endif
238 #if (_CYHAL_LPTIMER_INSTANCES > 3)
239 #error "_CYHAL_LPTIMER_INSTANCES > 3 not supported"
240 #endif
241 };
242 #elif defined (CY_IP_M0S8WCO_INSTANCES)//MOS8WCO only has one instance
243 static const _cyhal_system_irq_t _CYHAL_MCWDT_IRQS[1] =
244 {
245     wco_interrupt_IRQn,
246 };
247 #else
248 #error "HW Block not supported"
249 #endif
250 
_cyhal_lptimer_get_instance_from_irq(void)251 static uint32_t _cyhal_lptimer_get_instance_from_irq(void)
252 /* Gets instance from the IRQ array*/
253 {
254     _cyhal_system_irq_t irqn = _cyhal_irq_get_active();
255     for(uint8_t i = 0; i < sizeof(_CYHAL_MCWDT_IRQS)/sizeof(_CYHAL_MCWDT_IRQS[0]); ++i)
256     {
257         if (_CYHAL_MCWDT_IRQS[i] == irqn)
258         {
259             return i;
260         }
261     }
262 
263     /* The IRQ may be muxed and may be triggered by other sources */
264     return _CYHAL_MCWDT_DISCONNECTED_IRQ;
265 }
266 
267 #if defined (_CYHAL_LPTIMER_MCWDT_B)
268 /* Get the mask (for e.g. ClearInterrupt) associated with a particular counter */
_cyhal_lptimer_counter_to_mask(cy_en_mcwdtctr_t counter)269 uint32_t _cyhal_lptimer_counter_to_mask(cy_en_mcwdtctr_t counter)
270 {
271     return 1u << ((uint8_t)counter);
272 }
273 #endif
274 
_cyhal_lptimer_irq_handler(void)275 static void _cyhal_lptimer_irq_handler(void)
276 {
277     uint32_t instance = (uint32_t)_cyhal_lptimer_get_instance_from_irq();
278     if (instance != _CYHAL_MCWDT_DISCONNECTED_IRQ)
279     {
280         cyhal_lptimer_t *obj = _cyhal_lptimer_config_structs[instance];
281 #if defined (_CYHAL_LPTIMER_MCWDT_B)
282         uint32_t c2_count = Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER2);
283         //Since CAT1C does not use Counter0, there is no need to clear the interrupt
284         Cy_MCWDT_ClearInterrupt(obj->base, (CY_MCWDT_CTR1 | CY_MCWDT_CTR2));
285 #else
286         Cy_MCWDT_ClearInterrupt(obj->base, (CY_MCWDT_CTR0 | CY_MCWDT_CTR1 | CY_MCWDT_CTR2));
287 #endif
288 #if defined (_CYHAL_LPTIMER_MCWDT_B)
289         // We want to clear the interrupt mask everytime we enter the IRQ handler
290         // for CAT1C devices regardless if set_delay or set_match was called.
291         // This means if cyhal_lptimer_set_match was called, a single interrupt will be triggered.
292         Cy_MCWDT_SetInterruptMask(obj->base, Cy_MCWDT_GetInterruptMask(obj->base) & ~_cyhal_lptimer_counter_to_mask(obj->counter));
293 #endif
294         /* Clear interrupt mask if set only from cyhal_lptimer_set_delay() function */
295         if (obj->clear_int_mask)
296         {
297 #if defined (_CYHAL_LPTIMER_MCWDT)
298             Cy_MCWDT_SetInterruptMask(obj->base, 0);
299 #else
300             Cy_WDC_InterruptDisable(obj->base, CY_WDC_COUNTER1);
301 #endif
302         }
303 #if defined (_CYHAL_LPTIMER_MCWDT_B)
304         if(obj->final_time - c2_count > 0 && obj->final_time > c2_count)
305         {
306             cyhal_lptimer_set_delay(obj, obj->final_time - c2_count);
307         }
308         else
309 #endif
310         {
311             if (NULL != obj->callback_data.callback && ((obj->isr_instruction & _CYHAL_LPTIMER_ISR_CALL_USER_CB_MASK) != 0))
312             {
313                 cyhal_lptimer_event_callback_t callback = (cyhal_lptimer_event_callback_t) obj->callback_data.callback;
314                 (callback)(obj->callback_data.callback_arg, CYHAL_LPTIMER_COMPARE_MATCH);
315             }
316         }
317 #if (_CYHAL_IRQ_MUXING)
318         /* Check if the event flag should have been cleared when inside a critical situation */
319         if ((obj->isr_instruction & _CYHAL_LPTIMER_ISR_CRITICAL_SECTION_MASK) != 0)
320         {
321             #if defined (_CYHAL_LPTIMER_MCWDT_B)
322                 Cy_MCWDT_ClearInterrupt(obj->base, _cyhal_lptimer_counter_to_mask(obj->counter));
323                 Cy_MCWDT_SetInterruptMask(obj->base, Cy_MCWDT_GetInterruptMask(obj->base) & ~_cyhal_lptimer_counter_to_mask(obj->counter));
324             #elif defined(_CYHAL_LPTIMER_MCWDT)
325                 Cy_MCWDT_ClearInterrupt(obj->base, CY_MCWDT_CTR1);
326                 Cy_MCWDT_SetInterruptMask(obj->base, 0);
327             #elif defined (CY_IP_M0S8WCO)
328                 _cyhal_irq_disable(irqn);
329             #endif
330             obj->isr_instruction &= ~_CYHAL_LPTIMER_ISR_CRITICAL_SECTION_MASK;
331         }
332 #endif
333     }
334 }
335 #if defined (_CYHAL_LPTIMER_MCWDT_B)
_cyhal_lptimer_get_toggle_bit(uint32_t c2_current,uint32_t delay)336 uint32_t _cyhal_lptimer_get_toggle_bit(uint32_t c2_current, uint32_t delay)
337 {
338     uint32_t val = c2_current ^ (c2_current + delay);
339     int bit = 0;
340     while (val > 0)
341     {
342         bit++;
343         val >>= 1;
344     }
345     return bit - 1;
346 }
_cyhal_lptimer_set_delay_common(cyhal_lptimer_t * obj,uint32_t delay)347 static uint32_t _cyhal_lptimer_set_delay_common(cyhal_lptimer_t *obj, uint32_t delay)
348 {
349     // Check to see if Counter1 or Counter2 is enabled.
350     // If neither is enabled, return Error Disabled.
351     // We do not check to see if Counter0 is enabled as it is not used
352     // for this IP implementation.
353     if ((Cy_MCWDT_GetEnabledStatus(obj->base, CY_MCWDT_COUNTER1) == 0UL)
354         || (Cy_MCWDT_GetEnabledStatus(obj->base, CY_MCWDT_COUNTER2) == 0UL))
355     {
356         return CYHAL_LPTIMER_RSLT_ERR_DISABLED;
357     }
358     if (delay <= _CYHAL_LPTIMER_MIN_DELAY)
359     {
360         delay = _CYHAL_LPTIMER_MIN_DELAY;
361     }
362 #if (_CYHAL_LPTIMER_MAX_DELAY_TICKS != 0xffffffffUL)
363     /* No point in this check if uint32_t can't exceed the max */
364     if (delay > _CYHAL_LPTIMER_MAX_DELAY_TICKS)
365     {
366         delay = _CYHAL_LPTIMER_MAX_DELAY_TICKS;
367     }
368 #endif
369     // If the delay is greater than 2^16 we will use Counter2 for interrupts
370     // we must then clear the Counter2 interrupt before setting the new match.
371     obj->counter = (delay > ((1 << 16)-1)) ? CY_MCWDT_COUNTER2 : CY_MCWDT_COUNTER1;
372     uint32_t critical_section = cyhal_system_critical_section_enter();
373     uint32_t counter_value = Cy_MCWDT_GetCount(obj->base, obj->counter);
374     // if the counter value + the delay exceeds 32 bits, it is expected
375     // that the wrap around will be handled by the uin32_t variable
376     uint32_t match_value = counter_value + delay;
377     Cy_MCWDT_Unlock(obj->base);
378     if(obj->counter == CY_MCWDT_COUNTER1)
379     {
380         if(match_value > ((1 << 16)-1))
381         {
382             // Wait 3 LFClk cycles for reset
383             Cy_MCWDT_ResetCounters(obj->base, _cyhal_lptimer_counter_to_mask(obj->counter), _CYHAL_LPTIMER_RESET_TIME_US);
384             // Since we waited 3 LFClk cycles to reset the counter, we want to deduct that from our delay
385             match_value = delay - _CYHAL_LPTIMER_MIN_DELAY;
386             if(match_value < _CYHAL_LPTIMER_MIN_DELAY)
387             {
388                 // After resetting the counter and deducting the number of clock cycles it took
389                 // The delay is below our minumum delay.
390                 Cy_MCWDT_Lock(obj->base);
391                 cyhal_system_critical_section_exit(critical_section);
392                 return CYHAL_LPTIMER_RSLT_ERR_BAD_ARGUMENT;
393             }
394         }
395         Cy_MCWDT_SetWarnLimit(obj->base, obj->counter, (uint16_t)match_value, _CYHAL_LPTIMER_SETMATCH_TIME_US);
396     }
397     else
398     {
399         // We want to get the monitored bit to set for the toggle point
400         uint32_t toggle_bit = _cyhal_lptimer_get_toggle_bit(counter_value, delay);
401         // We want to set an interrupt for Counter2 to the 2^n value closest to the match value without going over
402         // If there is a delta between our power of value of 2^n and the match value
403         // We set the remaining delay time on the object which will trigger a follow up interrupt
404         // In the IRQ handler
405         Cy_MCWDT_SetToggleBit(obj->base, toggle_bit);
406         // Wait 3 LFClk cycles to make sure the toggle bit was updated
407         cyhal_system_delay_us(_CYHAL_LPTIMER_SETMATCH_TIME_US);
408     }
409     Cy_MCWDT_Lock(obj->base);
410     cyhal_system_critical_section_exit(critical_section);
411     Cy_MCWDT_ClearInterrupt(obj->base, _cyhal_lptimer_counter_to_mask(obj->counter));
412     Cy_MCWDT_SetInterruptMask(obj->base, _cyhal_lptimer_counter_to_mask(obj->counter));
413     return CY_RSLT_SUCCESS;
414 }
415 #else
_cyhal_lptimer_set_delay_common(cyhal_lptimer_t * obj,uint32_t delay)416 static uint32_t _cyhal_lptimer_set_delay_common(cyhal_lptimer_t *obj, uint32_t delay)
417 {
418     if ((Cy_MCWDT_GetEnabledStatus(obj->base, CY_MCWDT_COUNTER0) == 0UL)
419         || (Cy_MCWDT_GetEnabledStatus(obj->base, CY_MCWDT_COUNTER1) == 0UL)
420         || (Cy_MCWDT_GetEnabledStatus(obj->base, CY_MCWDT_COUNTER2) == 0UL))
421     {
422         return CYHAL_LPTIMER_RSLT_ERR_DISABLED;
423     }
424 
425     /**
426      * - 16 bit Counter0 (C0) & Counter1 (C1) are cascaded to generated a 32 bit counter.
427      * - Counter2 (C2) is a free running counter.
428      * - C0 continues counting after reaching its match value. On PSoC™ 4 Counter1 is reset on
429      * match. On PSoC™ 6 it continues counting.
430      * - An interrupt is generated when C1 reaches the match value. On PSoC™ 4 this happens when
431      * the counter increments to the same value as match. On PSoC™ 6 this happens when it increments
432      * past the match value.
433      *
434      * EXAMPLE:
435      * Supposed T=C0=C1=0, and we need to trigger an interrupt at T=0x18000.
436      * We set C0_match to 0x8000 and C1 match to 2 on PSoC™ 4 and 1 on PSoC™ 6.
437      * At T = 0x8000, C0_value matches C0_match so C1 get incremented. C1/C0=0x18000.
438      * At T = 0x18000, C0_value matches C0_match again so C1 get incremented from 1 to 2.
439      * When C1 get incremented from 1 to 2 the interrupt is generated.
440      * At T = 0x18000, C1/C0 = 0x28000.
441      */
442 
443     if (delay <= _CYHAL_LPTIMER_MIN_DELAY)
444     {
445         delay = _CYHAL_LPTIMER_MIN_DELAY;
446     }
447     if (delay > _CYHAL_LPTIMER_MAX_DELAY_TICKS)
448     {
449         delay = _CYHAL_LPTIMER_MAX_DELAY_TICKS;
450     }
451 
452     Cy_MCWDT_ClearInterrupt(obj->base, CY_MCWDT_CTR1);
453     uint16_t c0_old_match = (uint16_t)Cy_MCWDT_GetMatch(obj->base, CY_MCWDT_COUNTER0);
454 
455     uint32_t critical_section = cyhal_system_critical_section_enter();
456 
457     /* Cascading from C0 match into C1 is queued and can take 1 full LF clk cycle.
458      * There are 3 cases:
459      * Case 1: if c0 = match0 then the cascade into C1 will happen 1 cycle from now. The value c1_current_ticks is 1 lower than expected.
460      * Case 2: if c0 = match0 -1 then cascade may or not happen before new match value would occur. Match occurs on rising clock edge.
461      *          Synchronizing match value occurs on falling edge. Wait until c0 = match0 to ensure cascade occurs.
462      * Case 3: everything works as expected.
463      *
464      * Note: timeout is needed here just in case the LFCLK source gives out. This avoids device lockup.
465      *
466      * ((2 * Cycles_LFClk) / Cycles_cpu_iteration) * (HFCLk_max / LFClk_min) = Iterations_required
467      * Typical case: (2 / 100) * ((150x10^6)/33576) = 89 iterations
468      * Worst case: (2 / 100) * ((150x10^6)/1) = 3x10^6 iterations
469      * Compromise: (2 / 100) * ((150x10^6)/0xFFFF iterations) = 45 Hz = LFClk_min
470      */
471     const uint32_t DEFAULT_TIMEOUT = 0xFFFFUL;
472     uint32_t timeout = DEFAULT_TIMEOUT;
473     uint16_t c0_current_ticks = (uint16_t)Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER0);
474     // Wait until the cascade has definitively happened. It takes a clock cycle for the cascade to happen, and potentially another a full
475     // LFCLK clock cycle for the cascade to propagate up to the HFCLK-domain registers that the CPU reads.
476     while (((((uint16_t)(c0_old_match - 1)) == c0_current_ticks) ||
477                         (c0_old_match       == c0_current_ticks) ||
478             (((uint16_t)(c0_old_match + 1)) == c0_current_ticks)) && (timeout != 0UL))
479     {
480         c0_current_ticks = (uint16_t)Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER0);
481         timeout--;
482     }
483     if (timeout == 0UL)
484     {
485         // Timeout has occurred. There could have been a clock failure while waiting for the count value to update.
486         cyhal_system_critical_section_exit(critical_section);
487         return CYHAL_LPTIMER_RSLT_ERR_DISABLED;
488     }
489 
490     uint16_t c0_match = (uint16_t)(c0_current_ticks + delay);
491     // Changes can take up to 2 clk_lf cycles to propagate. If we set the match within this window of the current value,
492     // then it is nondeterministic whether the first cascade will trigger immediately or after 2^16 cycles. Wait until
493     // c0 is in a more predictable state.
494     timeout = DEFAULT_TIMEOUT;
495     uint32_t c0_new_ticks = c0_current_ticks;
496     while(((c0_new_ticks == c0_match) ||
497           (c0_new_ticks == ((uint16_t)(c0_match + 1))) ||
498           (c0_new_ticks == ((uint16_t)(c0_match + 2))))
499         && (timeout != 0UL))
500     {
501         c0_new_ticks = (uint16_t)Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER0);
502         timeout--;
503     }
504 
505     delay -= (c0_new_ticks >= c0_current_ticks)
506         ? (c0_new_ticks - c0_current_ticks)
507         : ((0xFFFFU - c0_current_ticks) + c0_new_ticks);
508 
509     c0_match = (uint16_t)(c0_current_ticks + delay);
510     uint16_t c1_current_ticks = (uint16_t)Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER1);
511     uint16_t c1_match = (uint16_t)(c1_current_ticks + (delay >> 16));
512 
513 #if defined(CY_IP_M0S8WCO)
514     c1_match += 1;
515     // Increase away from rollover glitch; can't go backward or c1 interrupt will not trigger
516     if (c0_match == 0)
517         c0_match = 1;
518 #endif
519     Cy_MCWDT_SetMatch(obj->base, CY_MCWDT_COUNTER0, c0_match, _CYHAL_LPTIMER_SETMATCH_TIME_US);
520     Cy_MCWDT_SetMatch(obj->base, CY_MCWDT_COUNTER1, c1_match, _CYHAL_LPTIMER_SETMATCH_TIME_US);
521     cyhal_system_critical_section_exit(critical_section);
522     #ifdef _CYHAL_LPTIMER_MCWDT
523     Cy_MCWDT_SetInterruptMask(obj->base, CY_MCWDT_CTR1);
524     #else
525     Cy_WDC_InterruptEnable(obj->base, CY_MCWDT_COUNTER1);
526     #endif
527     return CY_RSLT_SUCCESS;
528 }
529 #endif
530 
cyhal_lptimer_init(cyhal_lptimer_t * obj)531 cy_rslt_t cyhal_lptimer_init(cyhal_lptimer_t *obj)
532 {
533     CY_ASSERT(NULL != obj);
534 
535     obj->resource.type = CYHAL_RSC_INVALID;
536     obj->clear_int_mask = false;
537     obj->isr_instruction = 0;
538     cy_rslt_t rslt = CYHAL_LPTIMER_RSLT_ERR_NOT_SUPPORTED;
539     for(uint8_t inst=0; inst < _CYHAL_LPTIMER_INSTANCES; ++inst)
540     {
541         if(_CYHAL_MCWDT_IRQS[inst] != _CYHAL_MCWDT_DISCONNECTED_IRQ)
542         {
543             // Make a temp LPTIMER cyhal_resource_inst_t with block_num = inst
544             cyhal_resource_inst_t temp = {
545                 .type = CYHAL_RSC_LPTIMER,
546                 .block_num = inst,
547                 .channel_num = 0,
548             };
549 
550             rslt = cyhal_hwmgr_reserve(&temp);
551             if(rslt == CY_RSLT_SUCCESS)
552             {
553                 obj->resource = temp;
554                 break;
555             }
556         }
557     }
558     if (CY_RSLT_SUCCESS == rslt)
559     {
560         obj->base = _CYHAL_LPTIMER_BASE_ADDRESSES[obj->resource.block_num];
561 #if defined (_CYHAL_LPTIMER_MCWDT_B)
562         obj->offset = 0;
563         obj->final_time = 0;
564         obj->counter = CY_MCWDT_COUNTER1;
565         cy_stc_mcwdt_config_t cfg = default_cfg;
566 #elif defined (_CYHAL_LPTIMER_MCWDT)
567         cy_stc_mcwdt_config_t cfg = default_cfg;
568 #elif defined (CY_IP_M0S8WCO)
569         cy_stc_wdc_config_t cfg = default_cfg;
570         // The WDC_SEL clock source is populated into the config
571         // register by the clock driver. Extract it so that the
572         // PDL init doesn't revert it to default
573         cfg.clockSource = Cy_WDC_GetClockSource(WCO);
574 #endif
575         rslt = (cy_rslt_t) Cy_MCWDT_Init(obj->base, &cfg);
576     }
577 
578     if (CY_RSLT_SUCCESS == rslt)
579     {
580         obj->callback_data.callback = NULL;
581         obj->callback_data.callback_arg = NULL;
582         _cyhal_lptimer_config_structs[obj->resource.block_num] = obj;
583     }
584 
585     if (CY_RSLT_SUCCESS == rslt)
586     {
587         _cyhal_system_irq_t irqn = _CYHAL_MCWDT_IRQS[obj->resource.block_num];
588         _cyhal_irq_register(irqn, _CYHAL_LPTIMER_DEFAULT_PRIORITY, _cyhal_lptimer_irq_handler);
589 
590         if (CY_RSLT_SUCCESS == rslt)
591         {
592             _cyhal_irq_enable(irqn);
593         #if defined (_CYHAL_LPTIMER_MCWDT_B)
594             Cy_MCWDT_Unlock(obj->base);
595             Cy_MCWDT_Enable(obj->base, (CY_MCWDT_CTR1 | CY_MCWDT_CTR2), _CYHAL_LPTIMER_RESET_TIME_US);
596             Cy_MCWDT_Lock(obj->base);
597         #else
598             Cy_MCWDT_Enable(obj->base, _CYHAL_LPTIMER_CTRL, _CYHAL_LPTIMER_RESET_TIME_US);
599         #endif
600         }
601     }
602 
603     if (CY_RSLT_SUCCESS != rslt)
604     {
605         cyhal_lptimer_free(obj);
606     }
607 
608     return rslt;
609 }
610 
cyhal_lptimer_free(cyhal_lptimer_t * obj)611 void cyhal_lptimer_free(cyhal_lptimer_t *obj)
612 {
613     if (CYHAL_RSC_INVALID != obj->resource.type)
614     {
615         _cyhal_system_irq_t irqn = _CYHAL_MCWDT_IRQS[obj->resource.block_num];
616         _cyhal_irq_free(irqn);
617 
618         cyhal_hwmgr_free(&(obj->resource));
619         obj->resource.type = CYHAL_RSC_INVALID;
620     }
621     if (NULL != obj->base)
622     {
623 #if defined (_CYHAL_LPTIMER_MCWDT_B)
624         Cy_MCWDT_Unlock(obj->base);
625         // only need to disable counter1 and counter2 since those are the only counters
626         // used for the IP.
627         Cy_MCWDT_Disable(obj->base, (CY_MCWDT_CTR1 | CY_MCWDT_CTR2), _CYHAL_LPTIMER_RESET_TIME_US);
628         Cy_MCWDT_Lock(obj->base);
629 #else
630         Cy_MCWDT_Disable(obj->base, _CYHAL_LPTIMER_CTRL, _CYHAL_LPTIMER_RESET_TIME_US);
631 #endif
632         // When we're using the WDC, the clock source selection is stored in the WDC_CONFIG
633         // register and we want that to persist across free/init calls. So don't call deinit
634         // because that will clear it out.
635 #if !defined(CY_IP_M0S8WCO)
636         Cy_MCWDT_DeInit(obj->base);
637 #endif
638         obj->base = NULL;
639     }
640 }
641 
cyhal_lptimer_reload(cyhal_lptimer_t * obj)642 cy_rslt_t cyhal_lptimer_reload(cyhal_lptimer_t *obj)
643 {
644 #if defined (_CYHAL_LPTIMER_MCWDT_B)
645     // we save the current value for counter2 as our offset.
646     // This is done because for CAT1C devices we cannot reset counter2.
647     // This gives us the ability to mimic a reset by keeping track of
648     // the last time reload was called. The offset will be deducted
649     // from the current counter2 value when cyhal_lptimer_read is called
650     obj->offset = Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER2);
651     Cy_MCWDT_Unlock(obj->base);
652     Cy_MCWDT_ResetCounters(obj->base, CY_MCWDT_CTR1, _CYHAL_LPTIMER_RESET_TIME_US);
653     Cy_MCWDT_Lock(obj->base);
654 
655 #else
656     Cy_MCWDT_ResetCounters(obj->base, (CY_MCWDT_CTR0 | CY_MCWDT_CTR1 | CY_MCWDT_CTR2), 2 * _CYHAL_LPTIMER_RESET_TIME_US);
657 #endif
658     return CY_RSLT_SUCCESS;
659 }
660 
cyhal_lptimer_set_match(cyhal_lptimer_t * obj,uint32_t ticks)661 cy_rslt_t cyhal_lptimer_set_match(cyhal_lptimer_t *obj, uint32_t ticks)
662 {
663     obj->clear_int_mask = false;
664     uint32_t c2_current = cyhal_lptimer_read(obj);
665 #if defined (_CYHAL_LPTIMER_MCWDT_B)
666     // The reason we add the offset to ticks is because we want to get
667     // the current value of counter2 without the offset value.
668     // Since our delay is ticks - c2_current, this will get us our
669     // current time + delay
670     obj->final_time = ticks + obj->offset;
671 #endif
672     return _cyhal_lptimer_set_delay_common(obj, ticks - c2_current);
673 }
674 
cyhal_lptimer_set_delay(cyhal_lptimer_t * obj,uint32_t delay)675 cy_rslt_t cyhal_lptimer_set_delay(cyhal_lptimer_t *obj, uint32_t delay)
676 {
677     obj->clear_int_mask = true;
678 #if defined (_CYHAL_LPTIMER_MCWDT_B)
679     obj->final_time = (Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER2) + delay);
680 #endif
681     return _cyhal_lptimer_set_delay_common(obj, delay);
682 }
683 
cyhal_lptimer_read(const cyhal_lptimer_t * obj)684 uint32_t cyhal_lptimer_read(const cyhal_lptimer_t *obj)
685 {
686 #if defined (_CYHAL_LPTIMER_MCWDT_B)
687     uint32_t ctr2_count = Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER2);
688     if(obj->offset > ctr2_count)
689     {
690         return (uint32_t)((((uint64_t)1 << 32) - obj->offset) + ctr2_count);
691     }
692     else
693     {
694         return ctr2_count - obj->offset;
695     }
696 #else
697     return Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER2);
698 #endif
699 }
700 
cyhal_lptimer_register_callback(cyhal_lptimer_t * obj,cyhal_lptimer_event_callback_t callback,void * callback_arg)701 void cyhal_lptimer_register_callback(cyhal_lptimer_t *obj, cyhal_lptimer_event_callback_t callback, void *callback_arg)
702 {
703     CY_ASSERT(CYHAL_RSC_INVALID != obj->resource.block_num);
704 
705     uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
706     obj->callback_data.callback = (cy_israddress) callback;
707     obj->callback_data.callback_arg = callback_arg;
708     cyhal_system_critical_section_exit(savedIntrStatus);
709 }
710 
cyhal_lptimer_enable_event(cyhal_lptimer_t * obj,cyhal_lptimer_event_t event,uint8_t intr_priority,bool enable)711 void cyhal_lptimer_enable_event(cyhal_lptimer_t *obj, cyhal_lptimer_event_t event, uint8_t intr_priority, bool enable)
712 {
713     CY_UNUSED_PARAMETER(event);
714     CY_ASSERT(event == CYHAL_LPTIMER_COMPARE_MATCH);
715 
716     obj->isr_instruction &= ~_CYHAL_LPTIMER_ISR_CALL_USER_CB_MASK;
717     obj->isr_instruction |= (uint8_t)enable;
718 
719     _cyhal_system_irq_t irqn =_CYHAL_MCWDT_IRQS[obj->resource.block_num];
720     _cyhal_irq_set_priority(irqn, intr_priority);
721 
722     if (enable)
723     {
724         #if defined (_CYHAL_LPTIMER_MCWDT_B)
725             Cy_MCWDT_ClearInterrupt(obj->base, _cyhal_lptimer_counter_to_mask(obj->counter));
726             Cy_MCWDT_SetInterruptMask(obj->base, _cyhal_lptimer_counter_to_mask(obj->counter));
727         #elif defined(_CYHAL_LPTIMER_MCWDT)
728             Cy_MCWDT_ClearInterrupt(obj->base, CY_MCWDT_CTR1);
729             Cy_MCWDT_SetInterruptMask(obj->base, CY_MCWDT_CTR1);
730         #elif defined (CY_IP_M0S8WCO)
731             _cyhal_irq_enable(irqn);
732         #endif
733     }
734     else
735     {
736 #if (_CYHAL_IRQ_MUXING)
737         /* We may be in a critical section. Only clear the interrupt status if there isn't a pending interrupt */
738         if (Cy_MCWDT_GetInterruptStatus(obj->base) != 0)
739         {
740             obj->isr_instruction |= _CYHAL_LPTIMER_ISR_CRITICAL_SECTION_MASK;
741         }
742         else
743 #endif
744         {
745             #if defined (_CYHAL_LPTIMER_MCWDT_B)
746                 Cy_MCWDT_ClearInterrupt(obj->base, _cyhal_lptimer_counter_to_mask(obj->counter));
747                 Cy_MCWDT_SetInterruptMask(obj->base, Cy_MCWDT_GetInterruptMask(obj->base) & ~_cyhal_lptimer_counter_to_mask(obj->counter));
748             #elif defined(_CYHAL_LPTIMER_MCWDT)
749                 Cy_MCWDT_ClearInterrupt(obj->base, CY_MCWDT_CTR1);
750                 Cy_MCWDT_SetInterruptMask(obj->base, 0);
751             #elif defined (CY_IP_M0S8WCO)
752                 _cyhal_irq_disable(irqn);
753             #endif
754         }
755     }
756 }
757 
cyhal_lptimer_irq_trigger(cyhal_lptimer_t * obj)758 void cyhal_lptimer_irq_trigger(cyhal_lptimer_t *obj)
759 {
760     CY_ASSERT(CYHAL_RSC_INVALID != obj->resource.block_num);
761     cyhal_lptimer_event_callback_t callback = (cyhal_lptimer_event_callback_t) obj->callback_data.callback;
762     (callback)(obj->callback_data.callback_arg, CYHAL_LPTIMER_COMPARE_MATCH);
763 }
764 
cyhal_lptimer_get_info(cyhal_lptimer_t * obj,cyhal_lptimer_info_t * info)765 void cyhal_lptimer_get_info(cyhal_lptimer_t *obj, cyhal_lptimer_info_t *info)
766 {
767     CY_UNUSED_PARAMETER(obj);
768     CY_ASSERT(info != NULL);
769 
770 #if (WCO_WDT_EN == 1)
771     const cyhal_clock_t *lf_obj = &CYHAL_CLOCK_WDCSEL;
772 #else
773     const cyhal_clock_t *lf_obj = &CYHAL_CLOCK_LF;
774 #endif
775     info->frequency_hz = cyhal_clock_get_frequency(lf_obj);
776     info->min_set_delay = _CYHAL_LPTIMER_MIN_DELAY;
777     info->max_counter_value = _CYHAL_LPTIMER_MAX_COUNTER_VAL;
778 }
779 
780 #if defined(__cplusplus)
781 }
782 #endif
783 
784 #endif // CYHAL_DRIVER_AVAILABLE_LPTIMER
785