1 /***************************************************************************//**
2 * @file
3 * @brief SLEEPTIMER hardware abstraction implementation for PRORTC.
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 // Define module name for Power Manager debug feature
32 #define CURRENT_MODULE_NAME "SLEEPTIMER_PRORTC"
33
34 #include "sl_sleeptimer.h"
35 #include "sli_sleeptimer_hal.h"
36 #include "sl_core.h"
37 #include "sl_clock_manager.h"
38 #include "sl_interrupt_manager.h"
39 #include "em_bus.h"
40
41 #if defined(SL_COMPONENT_CATALOG_PRESENT)
42 #include <sl_component_catalog.h>
43 #endif
44
45 #if defined(SL_CATALOG_POWER_MANAGER_PRESENT)
46 #include "sl_power_manager.h"
47 #endif
48
49 #if defined(SL_CATALOG_POWER_MANAGER_PRESENT) && defined(_SILICON_LABS_32B_SERIES_2_CONFIG) \
50 && (_SILICON_LABS_32B_SERIES_2_CONFIG == 1)
51 #include "sli_power_manager.h"
52 #endif
53
54 #if SL_SLEEPTIMER_PERIPHERAL == SL_SLEEPTIMER_PERIPHERAL_PRORTC
55
56 // Minimum difference between current count value and what the comparator of the timer can be set to.
57 // 1 tick is added to the minimum diff for the algorithm of compensation for the IRQ handler that
58 // triggers when CNT == compare_value + 1. For more details refer to sleeptimer_hal_set_compare() function's header.
59 #define SLEEPTIMER_COMPARE_MIN_DIFF (2 + 1)
60 #define PRORTC_CNT_MASK 0xFFFFFFFFUL
61
62 #define SLEEPTIMER_TMR_WIDTH (PRORTC_CNT_MASK)
63
64 #define TIMER_COMP_REQ 0U
65
66 #define _PRORTC_IF_COMP_SHIFT 1 /**< Shift value for RTC_COMP */
67 #define PRORTC_IF_OF (0x1UL << 0) /**< Overflow Interrupt Flag */
68
69 #if (TIMER_COMP_REQ == 0)
70 #define PRORTC_IF_COMP_BIT RTCC_IF_CC0
71 #elif (TIMER_COMP_REQ == 1)
72 #define PRORTC_IF_COMP_BIT RTCC_IF_CC1
73 #endif
74
75 #ifndef SL_SLEEPTIMER_PRORTC_HAL_OWNS_IRQ_HANDLER
76 #define SL_SLEEPTIMER_PRORTC_HAL_OWNS_IRQ_HANDLER 0
77 #endif
78
79 #if SL_SLEEPTIMER_FREQ_DIVIDER != 1
80 #warning A value other than 1 for SL_SLEEPTIMER_FREQ_DIVIDER is not supported on Radio Internal RTC (PRORTC)
81 #endif
82
83 static uint32_t get_time_diff(uint32_t a, uint32_t b);
84
85 static bool cc_disabled = true;
86
87 /******************************************************************************
88 * Initializes PRORTC sleep timer.
89 *****************************************************************************/
sleeptimer_hal_init_timer(void)90 void sleeptimer_hal_init_timer(void)
91 {
92 #if defined(_SILICON_LABS_32B_SERIES_1)
93 uint32_t cmu_status = CMU->STATUS;
94 uint32_t lfr_clk_sel = CMU_LFRCLKSEL_LFR_DEFAULT;
95
96 if ((cmu_status & _CMU_STATUS_LFXORDY_MASK) == CMU_STATUS_LFXORDY) {
97 lfr_clk_sel = CMU_LFRCLKSEL_LFR_LFXO;
98 #ifdef (PLFRCO_PRESENT)
99 } else if ((cmu_status & _CMU_STATUS_PLFRCORDY_MASK) == CMU_STATUS_PLFRCORDY) {
100 lfr_clk_sel = CMU_LFRCLKSEL_LFR_PLFRCO;
101 #endif
102 } else {
103 lfr_clk_sel = CMU_LFRCLKSEL_LFR_LFRCO;
104 }
105
106 // Set the Low Frequency R Clock Select Register
107 CMU->LFRCLKSEL = (CMU->LFRCLKSEL & ~_CMU_LFRCLKSEL_LFR_MASK)
108 | (lfr_clk_sel << _CMU_LFRCLKSEL_LFR_SHIFT);
109
110 // Enable the PRORTC module
111 CMU->LFRCLKEN0 |= 1 << _CMU_LFRCLKEN0_PRORTC_SHIFT;
112
113 PRORTC->IFC = _PRORTC_IF_MASK;
114
115 #if (SL_SLEEPTIMER_DEBUGRUN == 1)
116 PRORTC->CTRL = _PRORTC_CTRL_EN_MASK | _PRORTC_CTRL_DEBUGRUN_MASK;
117 #else
118 PRORTC->CTRL = _PRORTC_CTRL_EN_MASK;
119 #endif
120 #else
121 bool use_clk_lfxo = true;
122
123 // Must enable radio clock branch to clock the radio bus as PRORTC is on this bus.
124 CMU->RADIOCLKCTRL = CMU_RADIOCLKCTRL_EN;
125
126 #if defined(CMU_CLKEN1_PRORTC)
127 CMU->CLKEN1_SET = CMU_CLKEN1_PRORTC;
128 #endif
129
130 #if _SILICON_LABS_32B_SERIES_2_CONFIG > 1
131 uint32_t enabled_clocks = CMU->CLKEN0;
132 use_clk_lfxo = ((enabled_clocks & _CMU_CLKEN0_LFXO_MASK) == _CMU_CLKEN0_LFXO_MASK);
133 #endif //_SILICON_LABS_32B_SERIES_2_CONFIG == 2
134
135 // On demand clocking for series 2 chips, make sure that the clock is
136 // not force disabled i.e. FORCEEN = 0, DISONDEMAND = 1
137 // Cannot rely on ENS bit of STATUS register since that is only set
138 // when a module uses the LF clock otherwise the ENS bit will be set to 0.
139 use_clk_lfxo = use_clk_lfxo
140 && ((LFXO->CTRL
141 & (_LFXO_CTRL_FORCEEN_MASK | _LFXO_CTRL_DISONDEMAND_MASK))
142 != _LFXO_CTRL_DISONDEMAND_MASK);
143
144 if (use_clk_lfxo) {
145 CMU->PRORTCCLKCTRL = CMU_PRORTCCLKCTRL_CLKSEL_LFXO;
146 } else {
147 CMU->PRORTCCLKCTRL = CMU_PRORTCCLKCTRL_CLKSEL_LFRCO;
148 }
149
150 #if (SL_SLEEPTIMER_DEBUGRUN == 1)
151 PRORTC->CFG |= _RTCC_CFG_DEBUGRUN_MASK;
152 #endif
153
154 PRORTC->EN_SET = RTCC_EN_EN;
155
156 PRORTC->CC[TIMER_COMP_REQ].CTRL = (_RTCC_CC_CTRL_MODE_OFF << _RTCC_CC_CTRL_MODE_SHIFT)
157 | (_RTCC_CC_CTRL_CMOA_PULSE << _RTCC_CC_CTRL_CMOA_SHIFT)
158 | (_RTCC_CC_CTRL_ICEDGE_NONE << _RTCC_CC_CTRL_ICEDGE_SHIFT)
159 | (_RTCC_CC_CTRL_COMPBASE_CNT << _RTCC_CC_CTRL_COMPBASE_SHIFT);
160
161 // Write the start bit until it syncs to the low frequency domain
162 do {
163 PRORTC->CMD = RTCC_CMD_START;
164 while ((PRORTC->SYNCBUSY & _RTCC_SYNCBUSY_MASK) != 0U) ;
165 } while ((PRORTC->STATUS & _RTCC_STATUS_RUNNING_MASK) != RTCC_STATUS_RUNNING);
166
167 // Disable ALL PRORTC interrupts
168 PRORTC->IEN &= ~_RTCC_IEN_MASK;
169
170 // Clear any pending interrupts
171 #if defined (RTCC_HAS_SET_CLEAR)
172 PRORTC->IF_CLR = _RTCC_IF_MASK;
173 #else
174 PRORTC->IFC = _RTCC_IF_MASK;
175 #endif
176 #endif
177
178 sl_interrupt_manager_clear_irq_pending(PRORTC_IRQn);
179 sl_interrupt_manager_enable_irq(PRORTC_IRQn);
180 }
181
182 /******************************************************************************
183 * Gets PRORTC counter value.
184 *****************************************************************************/
sleeptimer_hal_get_counter(void)185 uint32_t sleeptimer_hal_get_counter(void)
186 {
187 return PRORTC->CNT;
188 }
189
190 /******************************************************************************
191 * Gets PRORTC compare value.
192 *****************************************************************************/
sleeptimer_hal_get_compare(void)193 uint32_t sleeptimer_hal_get_compare(void)
194 {
195 #if defined(_SILICON_LABS_32B_SERIES_1)
196 return PRORTC->COMP[TIMER_COMP_REQ].COMP;
197 #else
198 return PRORTC->CC[TIMER_COMP_REQ].OCVALUE;
199 #endif
200 }
201
202 /******************************************************************************
203 * Sets PRORTC compare value.
204 *
205 * @note Compare match value is set to the requested value - 1. This is done
206 * to compensate for the fact that the PRORTC compare match interrupt always
207 * triggers at the end of the requested ticks and that the IRQ handler is
208 * executed when current tick count == compare_value + 1.
209 *****************************************************************************/
sleeptimer_hal_set_compare(uint32_t value)210 void sleeptimer_hal_set_compare(uint32_t value)
211 {
212 CORE_DECLARE_IRQ_STATE;
213 uint32_t counter;
214 uint32_t compare;
215 uint32_t compare_value = value;
216
217 CORE_ENTER_CRITICAL();
218 counter = sleeptimer_hal_get_counter();
219 compare = sleeptimer_hal_get_compare();
220
221 if (((PRORTC->IF & PRORTC_IF_COMP_BIT) != 0)
222 || get_time_diff(compare, counter) > SLEEPTIMER_COMPARE_MIN_DIFF
223 || compare == counter) {
224 // Add margin if necessary
225 if (get_time_diff(compare_value, counter) < SLEEPTIMER_COMPARE_MIN_DIFF) {
226 compare_value = counter + SLEEPTIMER_COMPARE_MIN_DIFF;
227 }
228 compare_value %= SLEEPTIMER_TMR_WIDTH;
229
230 #if defined(_SILICON_LABS_32B_SERIES_1)
231 PRORTC->COMP[TIMER_COMP_REQ].COMP = compare_value - 1;
232 #else
233 PRORTC->CC[TIMER_COMP_REQ].OCVALUE = compare_value - 1;
234 #endif
235
236 sleeptimer_hal_enable_int(SLEEPTIMER_EVENT_COMP);
237 }
238 CORE_EXIT_CRITICAL();
239
240 #if defined(_SILICON_LABS_32B_SERIES_2)
241 if (cc_disabled) {
242 PRORTC->CC[TIMER_COMP_REQ].CTRL |= RTCC_CC_CTRL_MODE_OUTPUTCOMPARE;
243 cc_disabled = false;
244 }
245 #endif
246 }
247
248 /******************************************************************************
249 * Enables PRORTC interrupts.
250 *****************************************************************************/
sleeptimer_hal_enable_int(uint8_t local_flag)251 void sleeptimer_hal_enable_int(uint8_t local_flag)
252 {
253 uint32_t prortc_ien = 0u;
254
255 if (local_flag & SLEEPTIMER_EVENT_OF) {
256 prortc_ien |= PRORTC_IF_OF;
257 }
258
259 if (local_flag & SLEEPTIMER_EVENT_COMP) {
260 #if defined(_SILICON_LABS_32B_SERIES_1)
261 if (cc_disabled == true) {
262 #if defined (RTCC_HAS_SET_CLEAR)
263 PRORTC->IF_CLR = PRORTC_IF_COMP_BIT;
264 #else
265 PRORTC->IFC = PRORTC_IF_COMP_BIT;
266 #endif
267
268 cc_disabled = false;
269 }
270 #endif
271
272 prortc_ien |= PRORTC_IF_COMP_BIT;
273 }
274
275 BUS_RegMaskedSet(&PRORTC->IEN, prortc_ien);
276 }
277
278 /******************************************************************************
279 * Disables PRORTC interrupts.
280 *****************************************************************************/
sleeptimer_hal_disable_int(uint8_t local_flag)281 void sleeptimer_hal_disable_int(uint8_t local_flag)
282 {
283 uint32_t prortc_int_dis = 0u;
284
285 if (local_flag & SLEEPTIMER_EVENT_OF) {
286 prortc_int_dis |= PRORTC_IF_OF;
287 }
288
289 if (local_flag & SLEEPTIMER_EVENT_COMP) {
290 prortc_int_dis |= PRORTC_IF_COMP_BIT;
291 cc_disabled = true;
292
293 #if defined(_SILICON_LABS_32B_SERIES_2)
294 PRORTC->CC[TIMER_COMP_REQ].CTRL &= ~_RTCC_CC_CTRL_MODE_MASK;
295 #endif
296 }
297
298 BUS_RegMaskedClear(&PRORTC->IEN, prortc_int_dis);
299 }
300
301 /*******************************************************************************
302 * Hardware Abstraction Layer to set timer interrupts.
303 ******************************************************************************/
sleeptimer_hal_set_int(uint8_t local_flag)304 void sleeptimer_hal_set_int(uint8_t local_flag)
305 {
306 if (local_flag & SLEEPTIMER_EVENT_COMP) {
307 #if defined (RTCC_HAS_SET_CLEAR)
308 PRORTC->IF_SET = PRORTC_IF_COMP_BIT;
309 #else
310 PRORTC->IFS = PRORTC_IF_COMP_BIT;
311 #endif
312 }
313 }
314
315 /******************************************************************************
316 * Gets status of specified interrupt.
317 *
318 * Note: This function must be called with interrupts disabled.
319 *****************************************************************************/
sli_sleeptimer_hal_is_int_status_set(uint8_t local_flag)320 bool sli_sleeptimer_hal_is_int_status_set(uint8_t local_flag)
321 {
322 bool int_is_set = false;
323 uint32_t irq_flag = PRORTC->IF;;
324
325 switch (local_flag) {
326 case SLEEPTIMER_EVENT_COMP:
327 int_is_set = ((irq_flag & PRORTC_IF_COMP_BIT) == PRORTC_IF_COMP_BIT);
328 break;
329
330 case SLEEPTIMER_EVENT_OF:
331 int_is_set = ((irq_flag & PRORTC_IF_OF) == PRORTC_IF_OF);
332 break;
333
334 default:
335 break;
336 }
337
338 return int_is_set;
339 }
340
341 /*******************************************************************************
342 * PRORTC interrupt handler.
343 ******************************************************************************/
344 #if (SL_SLEEPTIMER_PRORTC_HAL_OWNS_IRQ_HANDLER == 1)
PRORTC_IRQHandler(void)345 void PRORTC_IRQHandler(void)
346 #else
347 void PRORTC_IRQHandlerOverride(void)
348 #endif
349 {
350 CORE_DECLARE_IRQ_STATE;
351 uint8_t local_flag = 0;
352 uint32_t irq_flag;
353
354 CORE_ENTER_ATOMIC();
355 irq_flag = PRORTC->IF;
356
357 if (irq_flag & PRORTC_IF_OF) {
358 local_flag |= SLEEPTIMER_EVENT_OF;
359 }
360 if (irq_flag & PRORTC_IF_COMP_BIT) {
361 local_flag |= SLEEPTIMER_EVENT_COMP;
362 }
363
364 /* Clear interrupt source. */
365 #if defined (RTCC_HAS_SET_CLEAR)
366 PRORTC->IF_CLR = irq_flag;
367 #else
368 PRORTC->IFC = irq_flag;
369 #endif
370
371 process_timer_irq(local_flag);
372 CORE_EXIT_ATOMIC();
373 }
374
375 /*******************************************************************************
376 * Gets PRORTC timer frequency.
377 ******************************************************************************/
sleeptimer_hal_get_timer_frequency(void)378 uint32_t sleeptimer_hal_get_timer_frequency(void)
379 {
380 #if defined(_SILICON_LABS_32B_SERIES_1)
381 uint32_t lfr_clk_sel;
382 #endif
383 uint32_t freq;
384
385 #if defined(_SILICON_LABS_32B_SERIES_1)
386 lfr_clk_sel = (CMU->LFRCLKSEL & _CMU_LFRCLKSEL_LFR_MASK) << _CMU_LFRCLKSEL_LFR_SHIFT;
387
388 switch (lfr_clk_sel) {
389 case _CMU_LFRCLKSEL_LFR_LFXO:
390 freq = SystemLFXOClockGet();
391 break;
392
393 #if defined(PLFRCO_PRESENT)
394 case _CMU_LFRCLKSEL_LFR_PLFRCO:
395 freq = SystemLFRCOClockGet();
396 break;
397 #endif
398
399 case _CMU_LFRCLKSEL_LFR_LFRCO:
400 default:
401 freq = SystemLFRCOClockGet();
402 break;
403 }
404
405 freq >>= (CMU->LFRPRESC0 & _CMU_LFRPRESC0_PRORTC_MASK)
406 >> _CMU_LFRPRESC0_PRORTC_SHIFT;
407 #elif defined(_SILICON_LABS_32B_SERIES_2)
408 if (CMU->PRORTCCLKCTRL == CMU_PRORTCCLKCTRL_CLKSEL_LFXO) {
409 freq = SystemLFXOClockGet();
410 } else {
411 freq = SystemLFRCOClockGet();
412 }
413 #endif
414
415 return freq;
416 }
417
418 /*******************************************************************************
419 * Computes difference between two times taking into account timer wrap-around.
420 *
421 * @param a Time.
422 * @param b Time to substract from a.
423 *
424 * @return Time difference.
425 ******************************************************************************/
get_time_diff(uint32_t a,uint32_t b)426 static uint32_t get_time_diff(uint32_t a, uint32_t b)
427 {
428 return (a - b);
429 }
430
431 /*******************************************************************************
432 * @brief
433 * Gets the precision (in PPM) of the sleeptimer's clock.
434 *
435 * @return
436 * Clock accuracy, in PPM.
437 *
438 * @note
439 * The CMU_LF_ClockPrecisionGet function usally used to retrieve
440 * the clock accuracy can't be used for PRORTC as it isn't public,
441 * so this function works in the same way.
442 ******************************************************************************/
sleeptimer_hal_get_clock_accuracy(void)443 uint16_t sleeptimer_hal_get_clock_accuracy(void)
444 {
445 uint16_t precision = 0xFFFF;
446
447 if (CMU->PRORTCCLKCTRL == CMU_PRORTCCLKCTRL_CLKSEL_LFXO) {
448 sl_clock_manager_get_oscillator_precision(SL_OSCILLATOR_LFXO, &precision);
449
450 #if defined(LFRCO_CFG_HIGHPRECEN) && defined(LFRCO_PRECISION_MODE) && (LFRCO_PRECISION_MODE == 1)
451 } else {
452 CMU->CLKEN0_SET = CMU_CLKEN0_LFRCO;
453
454 if (LFRCO->CFG & _LFRCO_CFG_HIGHPRECEN_MASK) {
455 precision = 500;
456 } else {
457 precision = 0xFFFF;
458 }
459 #endif
460 }
461
462 return precision;
463 }
464
465 /*******************************************************************************
466 * Hardware Abstraction Layer to get the capture channel value.
467 ******************************************************************************/
sleeptimer_hal_get_capture(void)468 uint32_t sleeptimer_hal_get_capture(void)
469 {
470 // Invalid for PRORTC peripheral
471 EFM_ASSERT(0);
472 return 0;
473 }
474
475 /*******************************************************************************
476 * Hardware Abstraction Layer to reset PRS signal triggered by the associated
477 * peripheral.
478 ******************************************************************************/
sleeptimer_hal_reset_prs_signal(void)479 void sleeptimer_hal_reset_prs_signal(void)
480 {
481 // Invalid for PRORTC peripheral
482 EFM_ASSERT(0);
483 }
484
485 /***************************************************************************//**
486 * Set lowest energy mode based on a project's configurations and clock source
487 *
488 * @note If power_manager_no_deepsleep component is included in a project, the
489 * lowest possible energy mode is EM1, else lowest energy mode is
490 * determined by clock source.
491 ******************************************************************************/
492 #if defined(SL_CATALOG_POWER_MANAGER_PRESENT)
sli_sleeptimer_set_pm_em_requirement(void)493 void sli_sleeptimer_set_pm_em_requirement(void)
494 {
495 #if defined(_SILICON_LABS_32B_SERIES_1)
496 switch (CMU->LFRCLKSEL & _CMU_LFRCLKSEL_LFR_MASK) {
497 case CMU_LFRCLKSEL_LFR_LFRCO:
498 case CMU_LFRCLKSEL_LFR_LFXO:
499 sl_power_manager_add_em_requirement(SL_POWER_MANAGER_EM2);
500 break;
501 default:
502 break;
503 }
504 #else
505 switch (CMU->PRORTCCLKCTRL & _CMU_PRORTCCLKCTRL_CLKSEL_MASK) {
506 case CMU_PRORTCCLKCTRL_CLKSEL_LFRCO:
507 case CMU_PRORTCCLKCTRL_CLKSEL_LFXO:
508 sl_power_manager_add_em_requirement(SL_POWER_MANAGER_EM2);
509 break;
510 default:
511 break;
512 }
513 #endif
514 }
515 #endif
516 #endif
517