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