1 /***************************************************************************//**
2 * @file
3 * @brief SLEEPTIMER hardware abstraction implementation for SYSRTC.
4 *******************************************************************************
5 * # License
6 * <b>Copyright 2018 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 // Define module name for Power Manager debug feature
32 #define CURRENT_MODULE_NAME "SLEEPTIMER_SYSRTC"
33
34 #if defined(SL_COMPONENT_CATALOG_PRESENT)
35 #include "sl_component_catalog.h"
36 #endif
37 #include "sl_hal_sysrtc.h"
38 #include "sl_sleeptimer.h"
39 #include "sli_sleeptimer_hal.h"
40 #include "sl_code_classification.h"
41 #include "sl_core.h"
42 #include "sl_clock_manager.h"
43 #include "sl_interrupt_manager.h"
44 #include "sl_device_peripheral.h"
45
46 #if defined(SL_CATALOG_POWER_MANAGER_PRESENT)
47 #include "sl_power_manager.h"
48 #endif
49
50 #if defined(SL_CATALOG_POWER_MANAGER_PRESENT) || defined(SL_CATALOG_HFXO_MANAGER_PRESENT)
51 #if defined(_SILICON_LABS_32B_SERIES_2)
52 #include "em_prs.h"
53 #else
54 #include "sl_hal_prs.h"
55 #endif
56 #endif
57
58 #if SL_SLEEPTIMER_PERIPHERAL == SL_SLEEPTIMER_PERIPHERAL_SYSRTC
59
60 // Minimum difference between current count value and what the comparator of the timer can be set to.
61 // 1 tick is added to the minimum diff for the algorithm of compensation for the IRQ handler that
62 // triggers when CNT == compare_value + 1. For more details refer to sleeptimer_hal_set_compare() function's header.
63 #define SLEEPTIMER_COMPARE_MIN_DIFF (2 + 1)
64
65 #define SLEEPTIMER_TMR_WIDTH (_SYSRTC_CNT_MASK)
66
67 static bool cc_disabled = true;
68
69 static bool cc1_disabled = true;
70
71 SL_CODE_CLASSIFY(SL_CODE_COMPONENT_SLEEPTIMER, SL_CODE_CLASS_TIME_CRITICAL)
72 __STATIC_INLINE uint32_t get_time_diff(uint32_t a,
73 uint32_t b);
74
75 /******************************************************************************
76 * Initializes SYSRTC sleep timer.
77 *****************************************************************************/
sleeptimer_hal_init_timer(void)78 void sleeptimer_hal_init_timer(void)
79 {
80 sl_hal_sysrtc_config_t sysrtc_config = SYSRTC_CONFIG_DEFAULT;
81 sl_hal_sysrtc_group_config_t group_config = SYSRTC_GROUP_CONFIG_DEFAULT;
82
83 sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_SYSRTC0);
84
85 // Make sure the bus clock enabling is done.
86 __DSB();
87
88 #if (SL_SLEEPTIMER_DEBUGRUN == 1)
89 sysrtc_config.enable_debug_run = true;
90 #endif
91
92 sl_hal_sysrtc_init(&sysrtc_config);
93
94 group_config.compare_channel0_enable = false;
95
96 sl_hal_sysrtc_init_group(0u, &group_config);
97
98 sl_hal_sysrtc_disable_group_interrupts(0u, _SYSRTC_GRP0_IEN_MASK);
99 sl_hal_sysrtc_clear_group_interrupts(0u, _SYSRTC_GRP0_IF_MASK);
100 sl_hal_sysrtc_enable();
101 sl_hal_sysrtc_set_counter(0u);
102
103 sl_interrupt_manager_clear_irq_pending(SYSRTC_APP_IRQn);
104 sl_interrupt_manager_enable_irq(SYSRTC_APP_IRQn);
105 }
106
107 /*******************************************************************************
108 * Hardware Abstraction Layer to perform initialization related to Power Manager.
109 ******************************************************************************/
110 #if defined(SL_CATALOG_POWER_MANAGER_PRESENT)
sli_sleeptimer_hal_power_manager_integration_init(void)111 void sli_sleeptimer_hal_power_manager_integration_init(void)
112 {
113 // Initialize PRS to start HFXO for early wakeup
114 sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_PRS);
115
116 #if defined(_SILICON_LABS_32B_SERIES_2)
117 PRS_ConnectSignal(1UL, prsTypeAsync, prsSignalSYSRTC0_GRP0OUT1);
118 PRS_ConnectConsumer(1UL, prsTypeAsync, prsConsumerHFXO0_OSCREQ);
119 #else
120 sl_hal_prs_async_connect_channel_producer(1UL, SL_HAL_PRS_ASYNC_SYSRTC0_GRP0OUT1);
121 sl_hal_prs_connect_channel_consumer(1UL, SL_HAL_PRS_TYPE_ASYNC, SL_HAL_PRS_CONSUMER_HFXO0_OSCREQ);
122 #endif
123
124 // Set SYSRTC Compare Channel 1
125 SYSRTC0->GRP0_CTRL |= (_SYSRTC_GRP0_CTRL_CMP1CMOA_CMPIF << _SYSRTC_GRP0_CTRL_CMP1CMOA_SHIFT);
126 }
127 #endif
128
129 /*******************************************************************************
130 * Hardware Abstraction Layer to perform initialization related to HFXO Manager.
131 ******************************************************************************/
132 #if defined(SL_CATALOG_HFXO_MANAGER_PRESENT)
sli_sleeptimer_hal_hfxo_manager_integration_init(void)133 void sli_sleeptimer_hal_hfxo_manager_integration_init(void)
134 {
135 // Set PRS signal from HFXO to SYSRTC capture channel
136 sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_PRS);
137
138 #if defined(_SILICON_LABS_32B_SERIES_2)
139 PRS_ConnectSignal(2UL, prsTypeAsync, prsSignalHFXO0L_STATUS1);
140 PRS_ConnectConsumer(2UL, prsTypeAsync, prsConsumerSYSRTC0_SRC0);
141 #else
142 sl_hal_prs_async_connect_channel_producer(2UL, SL_HAL_PRS_ASYNC_SYXO0L_STATUS1);
143 sl_hal_prs_connect_channel_consumer(2UL, SL_HAL_PRS_TYPE_ASYNC, SL_HAL_PRS_CONSUMER_SYSRTC0_IN0);
144 #endif
145
146 // Set SYSRTC Capture Channel
147 SYSRTC0->GRP0_CTRL |= (_SYSRTC_GRP0_CTRL_CAP0EDGE_RISING << _SYSRTC_GRP0_CTRL_CAP0EDGE_SHIFT);
148 }
149 #endif
150
151 /******************************************************************************
152 * Gets SYSRTC counter value.
153 *****************************************************************************/
sleeptimer_hal_get_counter(void)154 uint32_t sleeptimer_hal_get_counter(void)
155 {
156 return sl_hal_sysrtc_get_counter();
157 }
158
159 /******************************************************************************
160 * Gets SYSRTC channel zero's compare value.
161 *****************************************************************************/
sleeptimer_hal_get_compare(void)162 uint32_t sleeptimer_hal_get_compare(void)
163 {
164 return sl_hal_sysrtc_get_group_compare_channel_value(0u, 0u);
165 }
166
167 /******************************************************************************
168 * Sets SYSRTC channel zero's compare value.
169 *
170 * @note Compare match value is set to the requested value - 1. This is done
171 * to compensate for the fact that the SYSRTC compare match interrupt always
172 * triggers at the end of the requested ticks and that the IRQ handler is
173 * executed when current tick count == compare_value + 1.
174 *****************************************************************************/
sleeptimer_hal_set_compare(uint32_t value)175 void sleeptimer_hal_set_compare(uint32_t value)
176 {
177 CORE_DECLARE_IRQ_STATE;
178 uint32_t counter;
179 uint32_t compare;
180 uint32_t compare_value = value;
181
182 CORE_ENTER_CRITICAL();
183 counter = sleeptimer_hal_get_counter();
184 compare = sleeptimer_hal_get_compare();
185
186 if (((sl_hal_sysrtc_get_group_interrupts(0u) & SYSRTC_GRP0_IEN_CMP0) != 0)
187 || get_time_diff(compare, counter) > SLEEPTIMER_COMPARE_MIN_DIFF
188 || compare == counter) {
189 // Add margin if necessary
190 if (get_time_diff(compare_value, counter) < SLEEPTIMER_COMPARE_MIN_DIFF) {
191 compare_value = counter + SLEEPTIMER_COMPARE_MIN_DIFF;
192 }
193 compare_value %= SLEEPTIMER_TMR_WIDTH;
194
195 sl_hal_sysrtc_set_group_compare_channel_value(0u, 0u, compare_value - 1);
196 sleeptimer_hal_enable_int(SLEEPTIMER_EVENT_COMP);
197 }
198 CORE_EXIT_CRITICAL();
199
200 if (cc_disabled) {
201 SYSRTC0->GRP0_CTRL |= SYSRTC_GRP0_CTRL_CMP0EN;
202 cc_disabled = false;
203 }
204 }
205
206 /*******************************************************************************
207 * Sets SYSRTC channel one's compare value.
208 *
209 * @note Compare match value is set to the requested value - 1. This is done
210 * to compensate for the fact that the SYSRTC compare match interrupt always
211 * triggers at the end of the requested ticks and that the IRQ handler is
212 * executed when current tick count == compare_value + 1.
213 ******************************************************************************/
sleeptimer_hal_set_compare_prs_hfxo_startup(int32_t value)214 void sleeptimer_hal_set_compare_prs_hfxo_startup(int32_t value)
215 {
216 CORE_DECLARE_IRQ_STATE;
217 uint32_t counter;
218 uint32_t compare_value;
219
220 CORE_ENTER_CRITICAL();
221
222 counter = sleeptimer_hal_get_counter();
223
224 compare_value = value + counter;
225
226 // Add margin if necessary
227 if (get_time_diff(compare_value, counter) < SLEEPTIMER_COMPARE_MIN_DIFF) {
228 compare_value = counter + SLEEPTIMER_COMPARE_MIN_DIFF;
229 }
230
231 compare_value %= SLEEPTIMER_TMR_WIDTH;
232
233 sl_hal_sysrtc_set_group_compare_channel_value(0u, 1u, compare_value - 1);
234
235 CORE_EXIT_CRITICAL();
236
237 if (cc1_disabled) {
238 SYSRTC0->GRP0_CTRL |= SYSRTC_GRP0_CTRL_CMP1EN;
239 SYSRTC0->GRP0_CTRL |= SYSRTC_GRP0_CTRL_CAP0EN;
240 cc1_disabled = false;
241 }
242 }
243
244 /******************************************************************************
245 * Enables SYSRTC interrupts.
246 *****************************************************************************/
sleeptimer_hal_enable_int(uint8_t local_flag)247 void sleeptimer_hal_enable_int(uint8_t local_flag)
248 {
249 uint32_t sysrtc_ien = 0u;
250
251 if (local_flag & SLEEPTIMER_EVENT_OF) {
252 sysrtc_ien |= SYSRTC_GRP0_IEN_OVF;
253 }
254
255 if (local_flag & SLEEPTIMER_EVENT_COMP) {
256 sysrtc_ien |= SYSRTC_GRP0_IEN_CMP0;
257 }
258
259 sl_hal_sysrtc_enable_group_interrupts(0u, sysrtc_ien);
260 }
261
262 /******************************************************************************
263 * Disables SYSRTC interrupts.
264 *****************************************************************************/
sleeptimer_hal_disable_int(uint8_t local_flag)265 void sleeptimer_hal_disable_int(uint8_t local_flag)
266 {
267 uint32_t sysrtc_int_dis = 0u;
268
269 if (local_flag & SLEEPTIMER_EVENT_OF) {
270 sysrtc_int_dis |= SYSRTC_GRP0_IEN_OVF;
271 }
272
273 if (local_flag & SLEEPTIMER_EVENT_COMP) {
274 sysrtc_int_dis |= SYSRTC_GRP0_IEN_CMP0;
275
276 cc_disabled = true;
277 SYSRTC0->GRP0_CTRL &= ~_SYSRTC_GRP0_CTRL_CMP0EN_MASK;
278 }
279
280 sl_hal_sysrtc_disable_group_interrupts(0u, sysrtc_int_dis);
281 }
282
283 /*******************************************************************************
284 * Hardware Abstraction Layer to set timer interrupts.
285 ******************************************************************************/
sleeptimer_hal_set_int(uint8_t local_flag)286 void sleeptimer_hal_set_int(uint8_t local_flag)
287 {
288 if (local_flag & SLEEPTIMER_EVENT_COMP) {
289 SYSRTC0->GRP0_IF_SET = SYSRTC_GRP0_IF_CMP0;
290 }
291 }
292
293 /******************************************************************************
294 * Gets status of specified interrupt.
295 *
296 * Note: This function must be called with interrupts disabled.
297 *****************************************************************************/
sli_sleeptimer_hal_is_int_status_set(uint8_t local_flag)298 bool sli_sleeptimer_hal_is_int_status_set(uint8_t local_flag)
299 {
300 bool int_is_set = false;
301 uint32_t irq_flag = sl_hal_sysrtc_get_group_interrupts(0u);
302
303 switch (local_flag) {
304 case SLEEPTIMER_EVENT_COMP:
305 int_is_set = ((irq_flag & SYSRTC_GRP0_IF_CMP0) == SYSRTC_GRP0_IF_CMP0);
306 break;
307
308 case SLEEPTIMER_EVENT_OF:
309 int_is_set = ((irq_flag & SYSRTC_GRP0_IF_OVF) == SYSRTC_GRP0_IF_OVF);
310 break;
311
312 default:
313 break;
314 }
315
316 return int_is_set;
317 }
318
319 /*******************************************************************************
320 * SYSRTC interrupt handler.
321 ******************************************************************************/
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_SLEEPTIMER,SL_CODE_CLASS_TIME_CRITICAL)322 SL_CODE_CLASSIFY(SL_CODE_COMPONENT_SLEEPTIMER,
323 SL_CODE_CLASS_TIME_CRITICAL)
324 void SYSRTC_APP_IRQHandler(void)
325 {
326 CORE_DECLARE_IRQ_STATE;
327 uint8_t local_flag = 0;
328 uint32_t irq_flag;
329
330 CORE_ENTER_ATOMIC();
331 irq_flag = sl_hal_sysrtc_get_group_interrupts(0u);
332
333 if (irq_flag & SYSRTC_GRP0_IF_OVF) {
334 local_flag |= SLEEPTIMER_EVENT_OF;
335 }
336
337 if (irq_flag & SYSRTC_GRP0_IF_CMP0) {
338 local_flag |= SLEEPTIMER_EVENT_COMP;
339 }
340 sl_hal_sysrtc_clear_group_interrupts(0u, irq_flag & (SYSRTC_GRP0_IF_OVF | SYSRTC_GRP0_IF_CMP0));
341
342 process_timer_irq(local_flag);
343
344 CORE_EXIT_ATOMIC();
345 }
346
347 /*******************************************************************************
348 * Gets SYSRTC timer frequency.
349 ******************************************************************************/
sleeptimer_hal_get_timer_frequency(void)350 uint32_t sleeptimer_hal_get_timer_frequency(void)
351 {
352 uint32_t frequency;
353 sl_clock_branch_t clock_branch;
354
355 clock_branch = sl_device_peripheral_get_clock_branch(SL_PERIPHERAL_SYSRTC0);
356 sl_clock_manager_get_clock_branch_frequency(clock_branch, &frequency);
357 return frequency;
358 }
359
360 /*******************************************************************************
361 * Computes difference between two times taking into account timer wrap-around.
362 *
363 * @param a Time.
364 * @param b Time to substract from a.
365 *
366 * @return Time difference.
367 ******************************************************************************/
get_time_diff(uint32_t a,uint32_t b)368 __STATIC_INLINE uint32_t get_time_diff(uint32_t a,
369 uint32_t b)
370 {
371 return (a - b);
372 }
373
374 /*******************************************************************************
375 * @brief
376 * Gets the precision (in PPM) of the sleeptimer's clock.
377 *
378 * @return
379 * Clock accuracy, in PPM.
380 *
381 ******************************************************************************/
sleeptimer_hal_get_clock_accuracy(void)382 uint16_t sleeptimer_hal_get_clock_accuracy(void)
383 {
384 uint16_t precision;
385 sl_clock_manager_get_clock_branch_precision(SL_CLOCK_BRANCH_SYSRTCCLK, &precision);
386 return precision;
387 }
388
389 /*******************************************************************************
390 * Hardware Abstraction Layer to get the capture channel value.
391 ******************************************************************************/
sleeptimer_hal_get_capture(void)392 uint32_t sleeptimer_hal_get_capture(void)
393 {
394 if ((sl_hal_sysrtc_get_group_interrupts(0) & _SYSRTC_GRP0_IF_CAP0_MASK) != 0) {
395 sl_hal_sysrtc_clear_group_interrupts(0, _SYSRTC_GRP0_IF_CAP0_MASK);
396 return sl_hal_sysrtc_get_group_capture_channel_value(0);
397 } else {
398 return 0;
399 }
400 }
401
402 /*******************************************************************************
403 * Hardware Abstraction Layer to reset PRS signal triggered by the associated
404 * peripheral.
405 ******************************************************************************/
sleeptimer_hal_reset_prs_signal(void)406 void sleeptimer_hal_reset_prs_signal(void)
407 {
408 sl_hal_sysrtc_clear_group_interrupts(0, SYSRTC_GRP0_IF_CMP1);
409 }
410
411 /*******************************************************************************
412 * Hardware Abstraction Layer to disable PRS compare and capture channel.
413 ******************************************************************************/
sleeptimer_hal_disable_prs_compare_and_capture_channel(void)414 void sleeptimer_hal_disable_prs_compare_and_capture_channel(void)
415 {
416 if (!cc1_disabled) {
417 SYSRTC0->GRP0_CTRL &= ~SYSRTC_GRP0_CTRL_CMP1EN;
418 SYSRTC0->GRP0_CTRL &= ~SYSRTC_GRP0_CTRL_CAP0EN;
419 cc1_disabled = true;
420 }
421 }
422
423 /***************************************************************************//**
424 * Set lowest energy mode based on a project's configurations and clock source
425 *
426 * @note If power_manager_no_deepsleep component is included in a project, the
427 * lowest possible energy mode is EM1, else lowest energy mode is
428 * determined by clock source.
429 ******************************************************************************/
430 #if defined(SL_CATALOG_POWER_MANAGER_PRESENT)
sli_sleeptimer_set_pm_em_requirement(void)431 void sli_sleeptimer_set_pm_em_requirement(void)
432 {
433 switch (CMU->SYSRTC0CLKCTRL & _CMU_SYSRTC0CLKCTRL_CLKSEL_MASK) {
434 case CMU_SYSRTC0CLKCTRL_CLKSEL_LFRCO:
435 case CMU_SYSRTC0CLKCTRL_CLKSEL_LFXO:
436 sl_power_manager_add_em_requirement(SL_POWER_MANAGER_EM2);
437 break;
438 default:
439 break;
440 }
441 }
442 #endif
443 #endif
444