1 /***************************************************************************//**
2 * @file
3 * @brief SLEEPTIMER hardware abstraction implementation for RTCC.
4 *******************************************************************************
5 * # License
6 * <b>Copyright 2019 Silicon Laboratories Inc. www.silabs.com</b>
7 *******************************************************************************
8 *
9 * SPDX-License-Identifier: Zlib
10 *
11 * The licensor of this software is Silicon Laboratories Inc.
12 *
13 * This software is provided 'as-is', without any express or implied
14 * warranty. In no event will the authors be held liable for any damages
15 * arising from the use of this software.
16 *
17 * Permission is granted to anyone to use this software for any purpose,
18 * including commercial applications, and to alter it and redistribute it
19 * freely, subject to the following restrictions:
20 *
21 * 1. The origin of this software must not be misrepresented; you must not
22 * claim that you wrote the original software. If you use this software
23 * in a product, an acknowledgment in the product documentation would be
24 * appreciated but is not required.
25 * 2. Altered source versions must be plainly marked as such, and must not be
26 * misrepresented as being the original software.
27 * 3. This notice may not be removed or altered from any source distribution.
28 *
29 ******************************************************************************/
30
31 #include "em_rtcc.h"
32 #include "sl_sleeptimer.h"
33 #include "sli_sleeptimer_hal.h"
34 #include "em_core.h"
35 #include "em_cmu.h"
36
37 #if SL_SLEEPTIMER_PERIPHERAL == SL_SLEEPTIMER_PERIPHERAL_RTCC
38
39 // Minimum difference between current count value and what the comparator of the timer can be set to.
40 // 1 tick is added to the minimum diff for the algorithm of compensation for the IRQ handler that
41 // triggers when CNT == compare_value + 1. For more details refer to sleeptimer_hal_set_compare() function's header.
42 #define SLEEPTIMER_COMPARE_MIN_DIFF (2 + 1)
43
44 #define SLEEPTIMER_TMR_WIDTH (_RTCC_CNT_MASK)
45
46 static bool cc_disabled = true;
47
48 __STATIC_INLINE uint32_t get_time_diff(uint32_t a,
49 uint32_t b);
50
51 /******************************************************************************
52 * Initializes RTCC sleep timer.
53 *****************************************************************************/
sleeptimer_hal_init_timer(void)54 void sleeptimer_hal_init_timer(void)
55 {
56 RTCC_Init_TypeDef rtcc_init = RTCC_INIT_DEFAULT;
57 RTCC_CCChConf_TypeDef channel = RTCC_CH_INIT_COMPARE_DEFAULT;
58
59 CMU_ClockEnable(cmuClock_RTCC, true);
60
61 rtcc_init.enable = false;
62 rtcc_init.presc = (RTCC_CntPresc_TypeDef)(CMU_PrescToLog2(SL_SLEEPTIMER_FREQ_DIVIDER - 1));
63 #if (SL_SLEEPTIMER_DEBUGRUN == 1)
64 rtcc_init.debugRun = true;
65 #endif
66
67 RTCC_Init(&rtcc_init);
68
69 // Compare channel starts disabled and is enabled only when compare match interrupt is enabled.
70 channel.chMode = rtccCapComChModeOff;
71 RTCC_ChannelInit(1u, &channel);
72
73 RTCC_IntDisable(_RTCC_IEN_MASK);
74 RTCC_IntClear(_RTCC_IF_MASK);
75 RTCC_CounterSet(0u);
76
77 RTCC_Enable(true);
78
79 NVIC_ClearPendingIRQ(RTCC_IRQn);
80 NVIC_EnableIRQ(RTCC_IRQn);
81 }
82
83 /******************************************************************************
84 * Gets RTCC counter value.
85 *****************************************************************************/
sleeptimer_hal_get_counter(void)86 uint32_t sleeptimer_hal_get_counter(void)
87 {
88 return RTCC_CounterGet();
89 }
90
91 /******************************************************************************
92 * Gets RTCC compare value.
93 *****************************************************************************/
sleeptimer_hal_get_compare(void)94 uint32_t sleeptimer_hal_get_compare(void)
95 {
96 return RTCC_ChannelCCVGet(1u);
97 }
98
99 /******************************************************************************
100 * Sets RTCC compare value.
101 *
102 * @note Compare match value is set to the requested value - 1. This is done
103 * to compensate for the fact that the RTCC compare match interrupt always
104 * triggers at the end of the requested ticks and that the IRQ handler is
105 * executed when current tick count == compare_value + 1.
106 *****************************************************************************/
sleeptimer_hal_set_compare(uint32_t value)107 void sleeptimer_hal_set_compare(uint32_t value)
108 {
109 CORE_DECLARE_IRQ_STATE;
110 uint32_t counter;
111 uint32_t compare;
112 uint32_t compare_value = value;
113
114 CORE_ENTER_CRITICAL();
115 counter = sleeptimer_hal_get_counter();
116 compare = sleeptimer_hal_get_compare();
117 if (((RTCC_IntGet() & RTCC_IEN_CC1) != 0)
118 || get_time_diff(compare, counter) > SLEEPTIMER_COMPARE_MIN_DIFF
119 || compare == counter) {
120 // Add margin if necessary
121 if (get_time_diff(compare_value, counter) < SLEEPTIMER_COMPARE_MIN_DIFF) {
122 compare_value = counter + SLEEPTIMER_COMPARE_MIN_DIFF;
123 }
124 compare_value %= SLEEPTIMER_TMR_WIDTH;
125
126 RTCC_ChannelCCVSet(1u, compare_value - 1u);
127 sleeptimer_hal_enable_int(SLEEPTIMER_EVENT_COMP);
128 }
129 CORE_EXIT_CRITICAL();
130
131 if (cc_disabled) {
132 RTCC->CC[1].CTRL |= RTCC_CC_CTRL_MODE_OUTPUTCOMPARE;
133 cc_disabled = false;
134 }
135 }
136
137 /******************************************************************************
138 * Enables RTCC interrupts.
139 *****************************************************************************/
sleeptimer_hal_enable_int(uint8_t local_flag)140 void sleeptimer_hal_enable_int(uint8_t local_flag)
141 {
142 uint32_t rtcc_ien = 0u;
143
144 if (local_flag & SLEEPTIMER_EVENT_OF) {
145 rtcc_ien |= RTCC_IEN_OF;
146 }
147
148 if (local_flag & SLEEPTIMER_EVENT_COMP) {
149 rtcc_ien |= RTCC_IEN_CC1;
150 }
151
152 RTCC_IntEnable(rtcc_ien);
153 }
154
155 /******************************************************************************
156 * Disables RTCC interrupts.
157 *****************************************************************************/
sleeptimer_hal_disable_int(uint8_t local_flag)158 void sleeptimer_hal_disable_int(uint8_t local_flag)
159 {
160 uint32_t rtcc_int_dis = 0u;
161
162 if (local_flag & SLEEPTIMER_EVENT_OF) {
163 rtcc_int_dis |= RTCC_IEN_OF;
164 }
165
166 if (local_flag & SLEEPTIMER_EVENT_COMP) {
167 rtcc_int_dis |= RTCC_IEN_CC1;
168
169 cc_disabled = true;
170 RTCC->CC[1].CTRL &= ~_RTCC_CC_CTRL_MODE_MASK;
171 }
172
173 RTCC_IntDisable(rtcc_int_dis);
174 }
175
176 /*******************************************************************************
177 * Hardware Abstraction Layer to set timer interrupts.
178 ******************************************************************************/
sleeptimer_hal_set_int(uint8_t local_flag)179 void sleeptimer_hal_set_int(uint8_t local_flag)
180 {
181 if (local_flag & SLEEPTIMER_EVENT_COMP) {
182 RTCC_IntSet(RTCC_IF_CC1);
183 }
184 }
185
186 /******************************************************************************
187 * Gets status of specified interrupt.
188 *
189 * Note: This function must be called with interrupts disabled.
190 *****************************************************************************/
sli_sleeptimer_hal_is_int_status_set(uint8_t local_flag)191 bool sli_sleeptimer_hal_is_int_status_set(uint8_t local_flag)
192 {
193 bool int_is_set = false;
194 uint32_t irq_flag = RTCC_IntGet();
195
196 switch (local_flag) {
197 case SLEEPTIMER_EVENT_COMP:
198 int_is_set = ((irq_flag & RTCC_IF_CC1) == RTCC_IF_CC1);
199 break;
200
201 case SLEEPTIMER_EVENT_OF:
202 int_is_set = ((irq_flag & RTCC_IF_OF) == RTCC_IF_OF);
203 break;
204
205 default:
206 break;
207 }
208
209 return int_is_set;
210 }
211
212 /*******************************************************************************
213 * RTCC interrupt handler.
214 ******************************************************************************/
RTCC_IRQHandler(void)215 void RTCC_IRQHandler(void)
216 {
217 CORE_DECLARE_IRQ_STATE;
218 uint8_t local_flag = 0;
219 uint32_t irq_flag;
220
221 CORE_ENTER_ATOMIC();
222 irq_flag = RTCC_IntGet();
223
224 if (irq_flag & RTCC_IF_OF) {
225 local_flag |= SLEEPTIMER_EVENT_OF;
226 }
227 if (irq_flag & RTCC_IF_CC1) {
228 local_flag |= SLEEPTIMER_EVENT_COMP;
229 }
230 RTCC_IntClear(irq_flag & (RTCC_IF_OF | RTCC_IF_CC1 | RTCC_IF_CC0));
231
232 process_timer_irq(local_flag);
233
234 CORE_EXIT_ATOMIC();
235 }
236
237 /*******************************************************************************
238 * Gets RTCC timer frequency.
239 ******************************************************************************/
sleeptimer_hal_get_timer_frequency(void)240 uint32_t sleeptimer_hal_get_timer_frequency(void)
241 {
242 return (CMU_ClockFreqGet(cmuClock_RTCC) >> (CMU_PrescToLog2(SL_SLEEPTIMER_FREQ_DIVIDER - 1)));
243 }
244
245 /*******************************************************************************
246 * Computes difference between two times taking into account timer wrap-around.
247 *
248 * @param a Time.
249 * @param b Time to substract from a.
250 *
251 * @return Time difference.
252 ******************************************************************************/
get_time_diff(uint32_t a,uint32_t b)253 __STATIC_INLINE uint32_t get_time_diff(uint32_t a,
254 uint32_t b)
255 {
256 return (a - b);
257 }
258
259 #endif
260