1 /***************************************************************************//**
2  * @file
3  * @brief SLEEPTIMER Hardware abstraction implementation for BURTC.
4  *******************************************************************************
5  * # License
6  * <b>Copyright 2020 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_device.h"
32 #if defined(_SILICON_LABS_32B_SERIES_2) || defined(_SILICON_LABS_32B_SERIES_3)
33 
34 // Define module name for Power Manager debug feature
35 #define CURRENT_MODULE_NAME    "SLEEPTIMER_BURTC"
36 
37 #include "sl_sleeptimer.h"
38 #include "sli_sleeptimer_hal.h"
39 
40 #include "sl_core.h"
41 
42 #if defined(_SILICON_LABS_32B_SERIES_2)
43 #include "em_burtc.h"
44 
45 #define sleeptimer_hal_burtc_get_counter() BURTC_CounterGet()
46 #define sleeptimer_hal_burtc_get_compare() BURTC_CompareGet(0U)
47 #define sleeptimer_hal_burtc_set_compare(compare) BURTC_CompareSet(0, compare)
48 #define sleeptimer_hal_burtc_get_interrupts() BURTC_IntGet()
49 #define sleeptimer_hal_burtc_set_interrupts(flags) BURTC_IntSet(flags)
50 #define sleeptimer_hal_burtc_enable_interrupts(interrupts) BURTC_IntEnable(interrupts)
51 #define sleeptimer_hal_burtc_disable_interrupts(interrupts) BURTC_IntDisable(interrupts)
52 #define sleeptimer_hal_burtc_clear_interrupts(flags) BURTC_IntClear(flags)
53 
54 #elif defined(_SILICON_LABS_32B_SERIES_3)
55 #include "sl_hal_burtc.h"
56 
57 #define sleeptimer_hal_burtc_get_counter() sl_hal_burtc_get_counter()
58 #define sleeptimer_hal_burtc_get_compare() sl_hal_burtc_get_compare()
59 #define sleeptimer_hal_burtc_set_compare(compare) sl_hal_burtc_set_compare(compare)
60 #define sleeptimer_hal_burtc_get_interrupts() sl_hal_burtc_get_pending_interrupts()
61 #define sleeptimer_hal_burtc_set_interrupts(flags) sl_hal_burtc_set_interrupts(flags)
62 #define sleeptimer_hal_burtc_enable_interrupts(interrupts) sl_hal_burtc_enable_interrupts(interrupts)
63 #define sleeptimer_hal_burtc_disable_interrupts(interrupts) sl_hal_burtc_disable_interrupts(interrupts)
64 #define sleeptimer_hal_burtc_clear_interrupts(flags) sl_hal_burtc_clear_interrupts(flags)
65 
66 #endif
67 
68 #include "sl_clock_manager.h"
69 #include "sl_device_peripheral.h"
70 #include "sl_interrupt_manager.h"
71 
72 #if defined(SL_CATALOG_POWER_MANAGER_PRESENT)
73 #include "sl_power_manager.h"
74 #endif
75 
76 #if SL_SLEEPTIMER_PERIPHERAL == SL_SLEEPTIMER_PERIPHERAL_BURTC
77 
78 #if defined(_SILICON_LABS_32B_SERIES_0)
79 #error BURTC implementation of the sleeptimer not available on Series 0 chips
80 #endif
81 
82 // Minimum difference between current count value and what the comparator of the timer can be set to.
83 // 1 tick is added to the minimum diff for the algorithm of compensation for the IRQ handler that
84 // triggers when CNT == compare_value + 1. For more details refer to sleeptimer_hal_set_compare() function's header.
85 #if defined(_SILICON_LABS_32B_SERIES_2_CONFIG_8)
86 #define SLEEPTIMER_COMPARE_MIN_DIFF  (5 + 1)
87 #else
88 #define SLEEPTIMER_COMPARE_MIN_DIFF  (4 + 1)
89 #endif
90 
91 #define SLEEPTIMER_TMR_WIDTH (_BURTC_CNT_MASK)
92 
93 static uint32_t get_time_diff(uint32_t a, uint32_t b);
94 
95 /******************************************************************************
96  * Convert HAL interrupt flag BURTC-interrupt-enable bitmask
97  *****************************************************************************/
irqien_hal2burtc(uint8_t hal_flag)98 static uint32_t irqien_hal2burtc(uint8_t hal_flag)
99 {
100   uint32_t burtc_if = 0u;
101 
102   if (hal_flag & SLEEPTIMER_EVENT_OF) {
103     burtc_if |= BURTC_IEN_OF;
104   }
105 
106   if (hal_flag & SLEEPTIMER_EVENT_COMP) {
107     burtc_if |= BURTC_IEN_COMP;
108   }
109 
110   return burtc_if;
111 }
112 
113 /******************************************************************************
114  * Convert BURTC interrupt flags to HAL events
115  *****************************************************************************/
irqflags_burtc2hal(uint32_t burtc_flag)116 static uint8_t irqflags_burtc2hal(uint32_t burtc_flag)
117 {
118   uint8_t hal_if = 0u;
119 
120   if (burtc_flag & BURTC_IF_OF) {
121     hal_if |= SLEEPTIMER_EVENT_OF;
122   }
123 
124   if (burtc_flag & BURTC_IF_COMP) {
125     hal_if |= SLEEPTIMER_EVENT_COMP;
126   }
127 
128   return hal_if;
129 }
130 
131 /******************************************************************************
132  * Initializes BURTC sleep timer.
133  *****************************************************************************/
sleeptimer_hal_init_timer()134 void sleeptimer_hal_init_timer()
135 {
136   sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_BURTC);
137 
138 #if defined(_SILICON_LABS_32B_SERIES_2)
139   BURTC_Init_TypeDef burtc_init = BURTC_INIT_DEFAULT;
140 
141   burtc_init.start  = false;
142   burtc_init.clkDiv = SL_SLEEPTIMER_FREQ_DIVIDER;
143 #if (SL_SLEEPTIMER_DEBUGRUN == 1)
144   burtc_init.debugRun = true;
145 #endif
146 
147   BURTC_Init(&burtc_init);
148   BURTC_IntDisable(_BURTC_IEN_MASK);
149   BURTC_IntClear(_BURTC_IF_MASK);
150   BURTC_CounterReset();
151 
152   BURTC_Start();
153   BURTC_SyncWait();
154 #elif defined(_SILICON_LABS_32B_SERIES_3)
155   sl_hal_burtc_init_config_t burtc_init = SL_HAL_BURTC_INIT_DEFAULT;
156 
157   burtc_init.clock_divider = SL_SLEEPTIMER_FREQ_DIVIDER;
158 #if (SL_SLEEPTIMER_DEBUGRUN == 1)
159   burtc_init.debug_run = true;
160 #endif
161   sl_hal_burtc_init(&burtc_init);
162   sl_hal_burtc_enable();
163   sl_hal_burtc_disable_interrupts(_BURTC_IEN_MASK);
164   sl_hal_burtc_clear_interrupts(_BURTC_IF_MASK);
165   sl_hal_burtc_reset_counter();
166 
167   sl_hal_burtc_start();
168   sl_hal_burtc_wait_sync();
169 #endif
170 
171   // Setup BURTC interrupt
172   sl_interrupt_manager_clear_irq_pending(BURTC_IRQn);
173   sl_interrupt_manager_enable_irq(BURTC_IRQn);
174 }
175 
176 /******************************************************************************
177  * Gets BURTC counter.
178  *****************************************************************************/
sleeptimer_hal_get_counter(void)179 uint32_t sleeptimer_hal_get_counter(void)
180 {
181   return sleeptimer_hal_burtc_get_counter();
182 }
183 
184 /******************************************************************************
185  * Gets BURTC compare value
186  *****************************************************************************/
sleeptimer_hal_get_compare(void)187 uint32_t sleeptimer_hal_get_compare(void)
188 {
189   return sleeptimer_hal_burtc_get_compare();
190 }
191 
192 /******************************************************************************
193  * Sets BURTC compare value
194  *
195  * @note Compare match value is set to the requested value - 1. This is done
196  * to compensate for the fact that the BURTC compare match interrupt always
197  * triggers at the end of the requested ticks and that the IRQ handler is
198  * executed when current tick count == compare_value + 1.
199  *****************************************************************************/
sleeptimer_hal_set_compare(uint32_t value)200 void sleeptimer_hal_set_compare(uint32_t value)
201 {
202   CORE_DECLARE_IRQ_STATE;
203   uint32_t counter;
204   uint32_t compare_current;
205   uint32_t compare_new = value;
206 
207   CORE_ENTER_CRITICAL();
208   counter = sleeptimer_hal_get_counter();
209   compare_current = sleeptimer_hal_get_compare();
210 
211   if ((((sleeptimer_hal_burtc_get_interrupts()) & _BURTC_IF_COMP_MASK) != 0)
212       || get_time_diff(compare_current, counter) > SLEEPTIMER_COMPARE_MIN_DIFF
213       || compare_current == counter) {
214     // Add margin if necessary
215     if (get_time_diff(compare_new, counter) < SLEEPTIMER_COMPARE_MIN_DIFF) {
216       compare_new = counter + SLEEPTIMER_COMPARE_MIN_DIFF;
217     }
218 
219     // wrap around if necessary
220     compare_new %= SLEEPTIMER_TMR_WIDTH;
221     sleeptimer_hal_burtc_set_compare(compare_new - 1);
222     sleeptimer_hal_enable_int(SLEEPTIMER_EVENT_COMP);
223   }
224   CORE_EXIT_CRITICAL();
225 }
226 
227 /******************************************************************************
228  * Enables BURTC interrupts.
229  *****************************************************************************/
sleeptimer_hal_enable_int(uint8_t local_flag)230 void sleeptimer_hal_enable_int(uint8_t local_flag)
231 {
232   sleeptimer_hal_burtc_enable_interrupts(irqien_hal2burtc(local_flag));
233 }
234 
235 /******************************************************************************
236  * Disables BURTC interrupts.
237  *****************************************************************************/
sleeptimer_hal_disable_int(uint8_t local_flag)238 void sleeptimer_hal_disable_int(uint8_t local_flag)
239 {
240   sleeptimer_hal_burtc_disable_interrupts(irqien_hal2burtc(local_flag));
241 }
242 
243 /*******************************************************************************
244  * Hardware Abstraction Layer to set timer interrupts.
245  ******************************************************************************/
sleeptimer_hal_set_int(uint8_t local_flag)246 void sleeptimer_hal_set_int(uint8_t local_flag)
247 {
248   sleeptimer_hal_burtc_set_interrupts(irqien_hal2burtc(local_flag));
249 }
250 
251 /******************************************************************************
252  * Gets status of specified interrupt.
253  *
254  * Note: This function must be called with interrupts disabled.
255  *****************************************************************************/
sli_sleeptimer_hal_is_int_status_set(uint8_t local_flag)256 bool sli_sleeptimer_hal_is_int_status_set(uint8_t local_flag)
257 {
258   bool int_is_set = false;
259 
260   uint32_t irq_flag = sleeptimer_hal_burtc_get_interrupts();
261 
262   switch (local_flag) {
263     case SLEEPTIMER_EVENT_COMP:
264       int_is_set = (irq_flag & BURTC_IF_COMP);
265       break;
266 
267     case SLEEPTIMER_EVENT_OF:
268       int_is_set = (irq_flag & BURTC_IF_OF);
269       break;
270 
271     default:
272       break;
273   }
274 
275   return int_is_set;
276 }
277 
278 /*******************************************************************************
279  * Gets BURTC timer frequency.
280  ******************************************************************************/
sleeptimer_hal_get_timer_frequency(void)281 uint32_t sleeptimer_hal_get_timer_frequency(void)
282 {
283   uint32_t frequency;
284   sl_clock_branch_t clock_branch;
285 
286   clock_branch = sl_device_peripheral_get_clock_branch(SL_PERIPHERAL_BURTC);
287   sl_clock_manager_get_clock_branch_frequency(clock_branch, &frequency);
288   return (frequency >> (sleeptimer_hal_presc_to_log2(SL_SLEEPTIMER_FREQ_DIVIDER - 1)));
289 }
290 
291 /*******************************************************************************
292  * BURTC interrupt handler.
293  ******************************************************************************/
BURTC_IRQHandler(void)294 void BURTC_IRQHandler(void)
295 {
296   CORE_DECLARE_IRQ_STATE;
297   uint8_t local_flag = 0;
298   uint32_t irq_flag;
299 
300   CORE_ENTER_ATOMIC();
301 
302   irq_flag = sleeptimer_hal_burtc_get_interrupts();
303 
304   local_flag = irqflags_burtc2hal(irq_flag);
305 
306   sleeptimer_hal_burtc_clear_interrupts(irq_flag & (BURTC_IF_OF | BURTC_IF_COMP));
307   process_timer_irq(local_flag);
308 
309   CORE_EXIT_ATOMIC();
310 }
311 
312 /*******************************************************************************
313  * Computes difference between two times taking into account timer wrap-around.
314  *
315  * @param a Time.
316  * @param b Time to substract from a.
317  *
318  * @return Time difference.
319  ******************************************************************************/
get_time_diff(uint32_t a,uint32_t b)320 static uint32_t get_time_diff(uint32_t a, uint32_t b)
321 {
322   return (a - b);
323 }
324 
325 /*******************************************************************************
326  * @brief
327  *   Gets the precision (in PPM) of the sleeptimer's clock.
328  *
329  * @return
330  *   Clock accuracy, in PPM.
331  ******************************************************************************/
sleeptimer_hal_get_clock_accuracy(void)332 uint16_t sleeptimer_hal_get_clock_accuracy(void)
333 {
334   uint16_t precision;
335   sl_clock_manager_get_clock_branch_precision(SL_CLOCK_BRANCH_EM4GRPACLK, &precision);
336   return precision;
337 }
338 
339 /*******************************************************************************
340  * Hardware Abstraction Layer to get the capture channel value.
341  ******************************************************************************/
sleeptimer_hal_get_capture(void)342 uint32_t sleeptimer_hal_get_capture(void)
343 {
344   // Invalid for BURTC peripheral
345   EFM_ASSERT(0);
346   return 0;
347 }
348 
349 /*******************************************************************************
350  * Hardware Abstraction Layer to reset PRS signal triggered by the associated
351  * peripheral.
352  ******************************************************************************/
sleeptimer_hal_reset_prs_signal(void)353 void sleeptimer_hal_reset_prs_signal(void)
354 {
355   // Invalid for BURTC peripheral
356   EFM_ASSERT(0);
357 }
358 
359 /***************************************************************************//**
360  * Set lowest energy mode based on a project's configurations and clock source
361  *
362  * @note If power_manager_no_deepsleep component is included in a project, the
363  *       lowest possible energy mode is EM1, else lowest energy mode is
364  *       determined by clock source.
365  ******************************************************************************/
366 #if defined(SL_CATALOG_POWER_MANAGER_PRESENT)
sli_sleeptimer_set_pm_em_requirement(void)367 void sli_sleeptimer_set_pm_em_requirement(void)
368 {
369   switch (CMU->EM4GRPACLKCTRL & _CMU_EM4GRPACLKCTRL_CLKSEL_MASK) {
370     case CMU_EM4GRPACLKCTRL_CLKSEL_LFRCO:
371     case CMU_EM4GRPACLKCTRL_CLKSEL_LFXO:
372       sl_power_manager_add_em_requirement(SL_POWER_MANAGER_EM2);
373       break;
374     default:
375       break;
376   }
377 }
378 #endif
379 #endif
380 
381 #endif
382