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