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