1 /***************************************************************************//**
2  * @file
3  * @brief Backup Real Time Counter (BURTC) Peripheral API
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 #include "em_burtc.h"
32 #if defined(BURTC_PRESENT)
33 
34 /***************************************************************************//**
35  * @addtogroup burtc BURTC - Backup RTC
36  * @brief Backup Real Time Counter (BURTC) Peripheral API
37  * @details
38  *  This module contains functions to control the BURTC peripheral of Silicon
39  *  Labs 32-bit MCUs. The Backup Real Time Counter allows timekeeping in all
40  *  energy modes. The Backup RTC is also available when the system is in backup
41  *  mode.
42  * @{
43  ******************************************************************************/
44 
45 /*******************************************************************************
46  *******************************   DEFINES   ***********************************
47  ******************************************************************************/
48 
49 /*******************************************************************************
50  **************************   LOCAL FUNCTIONS   ********************************
51  ******************************************************************************/
52 
53 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
54 /***************************************************************************//**
55  * @brief Convert dividend to a prescaler logarithmic value. Only works for even
56  *        numbers equal to 2^n.
57  * @param[in] div Unscaled dividend,
58  * @return Base 2 logarithm of input, as used by fixed prescalers.
59  ******************************************************************************/
divToLog2(uint32_t div)60 __STATIC_INLINE uint32_t divToLog2(uint32_t div)
61 {
62   uint32_t log2;
63 
64   /* Prescaler accepts an argument of 128 or less, valid values being 2^n. */
65   EFM_ASSERT((div > 0) && (div <= 32768));
66 
67   /* Count leading zeroes and "reverse" result, Cortex-M3 intrinsic. */
68   log2 = (31 - __CLZ(div));
69 
70   return log2;
71 }
72 
73 /***************************************************************************//**
74  * @brief
75  *   Wait for an ongoing sync of register(s) to low frequency domain to complete.
76  *
77  * @param[in] mask
78  *   A bitmask corresponding to SYNCBUSY register defined bits, indicating
79  *   registers that must complete any ongoing synchronization.
80  ******************************************************************************/
regSync(uint32_t mask)81 __STATIC_INLINE void regSync(uint32_t mask)
82 {
83 #if defined(_BURTC_FREEZE_MASK)
84   /* Avoid deadlock if modifying the same register twice when freeze mode is
85      activated or when a clock is not selected for the BURTC. If a clock is
86      not selected, then the sync is done once the clock source is set. */
87   if ((BURTC->FREEZE & BURTC_FREEZE_REGFREEZE)
88       || ((BURTC->CTRL & _BURTC_CTRL_CLKSEL_MASK) == BURTC_CTRL_CLKSEL_NONE)
89       || ((BURTC->CTRL & _BURTC_CTRL_RSTEN_MASK) == BURTC_CTRL_RSTEN)) {
90     return;
91   }
92 #endif
93 
94   /* Wait for any pending previous write operation to complete */
95   /* in low frequency domain. This is only required for the Gecko Family. */
96   while ((BURTC->SYNCBUSY & mask) != 0U)
97     ;
98 }
99 /** @endcond */
100 
101 /*******************************************************************************
102  **************************   GLOBAL FUNCTIONS   *******************************
103  ******************************************************************************/
104 
105 /***************************************************************************//**
106  * @brief Initialize BURTC.
107  *
108  * @details
109  *    Configures the BURTC peripheral.
110  *
111  * @note
112  *   Before initialization, BURTC module must first be enabled by clearing the
113  *   reset bit in the RMU, i.e.,
114  * @verbatim
115  *   RMU_ResetControl(rmuResetBU, rmuResetModeClear);
116  * @endverbatim
117  *   Compare channel 0 must be configured outside this function, before
118  *   initialization if enable is set to true. The counter will always be reset.
119  *
120  * @param[in] burtcInit
121  *   A pointer to the BURTC initialization structure.
122  ******************************************************************************/
BURTC_Init(const BURTC_Init_TypeDef * burtcInit)123 void BURTC_Init(const BURTC_Init_TypeDef *burtcInit)
124 {
125 #if defined(_SILICON_LABS_32B_SERIES_0)
126   uint32_t ctrl;
127   uint32_t presc;
128 
129   /* Check initializer structure integrity. */
130   EFM_ASSERT(burtcInit != (BURTC_Init_TypeDef *) 0);
131   /* Clock divider must be between 1 and 128, really on the form 2^n. */
132   EFM_ASSERT((burtcInit->clkDiv >= 1) && (burtcInit->clkDiv <= 128));
133 
134   /* Ignored compare bits during low power operation must be less than 7. */
135   /* Note! Giant Gecko revision C errata, do NOT use LPCOMP=7. */
136   EFM_ASSERT(burtcInit->lowPowerComp <= 6);
137   /* You cannot enable the BURTC if mode is set to disabled. */
138   EFM_ASSERT((burtcInit->enable == false)
139              || ((burtcInit->enable == true)
140                  && (burtcInit->mode != burtcModeDisable)));
141   /* Low power mode is only available with LFRCO or LFXO as clock source. */
142   EFM_ASSERT((burtcInit->clkSel != burtcClkSelULFRCO)
143              || ((burtcInit->clkSel == burtcClkSelULFRCO)
144                  && (burtcInit->lowPowerMode == burtcLPDisable)));
145 
146   /* Calculate a prescaler value from the clock divider input. */
147   /* Note! If clock select (clkSel) is ULFRCO, a clock divisor (clkDiv) of
148      value 1 will select a 2 kHz ULFRCO clock, while any other value will
149      select a 1 kHz ULFRCO clock source. */
150   presc = divToLog2(burtcInit->clkDiv);
151 
152   /* Make sure all registers are updated simultaneously. */
153   if (burtcInit->enable) {
154     BURTC_FreezeEnable(true);
155   }
156 
157   /* Modification of LPMODE register requires sync with potential ongoing
158    * register updates in LF domain. */
159   regSync(BURTC_SYNCBUSY_LPMODE);
160 
161   /* Configure low power mode. */
162   BURTC->LPMODE = (uint32_t) (burtcInit->lowPowerMode);
163 
164   /* New configuration. */
165   ctrl = (BURTC_CTRL_RSTEN
166           | (burtcInit->mode)
167           | (burtcInit->debugRun << _BURTC_CTRL_DEBUGRUN_SHIFT)
168           | (burtcInit->compare0Top << _BURTC_CTRL_COMP0TOP_SHIFT)
169           | (burtcInit->lowPowerComp << _BURTC_CTRL_LPCOMP_SHIFT)
170           | (presc << _BURTC_CTRL_PRESC_SHIFT)
171           | (burtcInit->clkSel)
172           | (burtcInit->timeStamp << _BURTC_CTRL_BUMODETSEN_SHIFT));
173 
174   /* Clear interrupts. */
175   BURTC_IntClear(0xFFFFFFFF);
176 
177   /* Set the new configuration. */
178   BURTC->CTRL = ctrl;
179 
180   /* Enable BURTC and counter. */
181   if (burtcInit->enable) {
182     /* To enable BURTC counter, disable reset. */
183     BURTC_Enable(true);
184 
185     /* Clear freeze. */
186     BURTC_FreezeEnable(false);
187   }
188 #elif defined(_SILICON_LABS_32B_SERIES_2)
189   uint32_t presc;
190 
191   presc = divToLog2(burtcInit->clkDiv);
192 
193   if (BURTC->EN != 0U) {
194     BURTC_SyncWait();
195   }
196   BURTC->EN_CLR = BURTC_EN_EN;
197 #if defined(_BURTC_SYNCBUSY_EN_MASK)
198   regSync(BURTC_SYNCBUSY_EN);
199 #elif defined(_BURTC_EN_DISABLING_MASK)
200   while (BURTC->EN & _BURTC_EN_DISABLING_MASK) {
201     /* Wait for disabling to finish */
202   }
203 #endif
204 
205   BURTC->CFG = (presc << _BURTC_CFG_CNTPRESC_SHIFT)
206                | ((burtcInit->compare0Top ? 1U : 0U) << _BURTC_CFG_COMPTOP_SHIFT)
207                | ((burtcInit->debugRun ? 1U : 0U) << _BURTC_CFG_DEBUGRUN_SHIFT);
208   BURTC->EM4WUEN = ((burtcInit->em4comp ? 1U : 0U) << _BURTC_EM4WUEN_COMPEM4WUEN_SHIFT)
209                    | ((burtcInit->em4overflow ? 1U : 0U) << _BURTC_EM4WUEN_OFEM4WUEN_SHIFT);
210   BURTC->EN_SET = BURTC_EN_EN;
211   if (burtcInit->start) {
212     BURTC_Start();
213   }
214 #endif
215 }
216 
217 #if defined(_SILICON_LABS_32B_SERIES_2)
218 /***************************************************************************//**
219  * @brief
220  *   Enable or Disable BURTC peripheral.
221  *
222  * @param[in] enable
223  *   true to enable, false to disable.
224  ******************************************************************************/
BURTC_Enable(bool enable)225 void BURTC_Enable(bool enable)
226 {
227 #if defined(_BURTC_SYNCBUSY_EN_MASK)
228   regSync(BURTC_SYNCBUSY_EN);
229 #endif
230 
231   if ((BURTC->EN == 0U) && !enable) {
232     /* Trying to disable BURTC when it's already disabled */
233     return;
234   }
235 
236   if (BURTC->EN != 0U) {
237     /* Modifying the enable bit while synchronization is active will BusFault */
238     BURTC_SyncWait();
239   }
240 
241   if (enable) {
242     BURTC->EN_SET = BURTC_EN_EN;
243   } else {
244     BURTC_Stop();
245     BURTC_SyncWait(); /* Wait for the stop to synchronize */
246     BURTC->EN_CLR = BURTC_EN_EN;
247 #if defined(_BURTC_EN_DISABLING_MASK)
248     while (BURTC->EN & _BURTC_EN_DISABLING_MASK) {
249       /* Wait for disabling to finish */
250     }
251 #endif
252   }
253 }
254 #elif defined(_SILICON_LABS_32B_SERIES_0)
255 /***************************************************************************//**
256  * @brief
257  *   Enable or Disable BURTC peripheral reset and start counter
258  * @param[in] enable
259  *   If true; asserts reset to BURTC, halts counter, if false; deassert reset
260  ******************************************************************************/
BURTC_Enable(bool enable)261 void BURTC_Enable(bool enable)
262 {
263   /* Note! If mode is disabled, BURTC counter will not start */
264   EFM_ASSERT(((enable == true)
265               && ((BURTC->CTRL & _BURTC_CTRL_MODE_MASK)
266                   != BURTC_CTRL_MODE_DISABLE))
267              || (enable == false));
268   BUS_RegBitWrite(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT, (uint32_t) !enable);
269 }
270 #endif
271 
272 /***************************************************************************//**
273  * @brief Set BURTC compare channel.
274  *
275  * @param[in] comp Compare the channel index, must be 0 for current devices.
276  *
277  * @param[in] value New compare value.
278  ******************************************************************************/
BURTC_CompareSet(unsigned int comp,uint32_t value)279 void BURTC_CompareSet(unsigned int comp, uint32_t value)
280 {
281   (void) comp;  /* Unused parameter when EFM_ASSERT is undefined. */
282 
283   EFM_ASSERT(comp == 0U);
284 
285 #if defined(_BURTC_COMP0_MASK)
286   /* Modification of COMP0 register requires sync with potential ongoing
287    * register updates in LF domain. */
288   regSync(BURTC_SYNCBUSY_COMP0);
289 
290   /* Configure compare channel 0/. */
291   BURTC->COMP0 = value;
292 #else
293   /* Wait for last potential write to complete. */
294   regSync(BURTC_SYNCBUSY_COMP);
295 
296   /* Configure compare channel 0 */
297   BURTC->COMP = value;
298   regSync(BURTC_SYNCBUSY_COMP);
299 #endif
300 }
301 
302 /***************************************************************************//**
303  * @brief Get the BURTC compare value.
304  *
305  * @param[in] comp Compare the channel index value, must be 0 for Giant/Leopard Gecko.
306  *
307  * @return The currently configured value for this compare channel.
308  ******************************************************************************/
BURTC_CompareGet(unsigned int comp)309 uint32_t BURTC_CompareGet(unsigned int comp)
310 {
311   (void) comp;  /* Unused parameter when EFM_ASSERT is undefined. */
312 
313   EFM_ASSERT(comp == 0U);
314 #if defined(_BURTC_COMP0_MASK)
315   return BURTC->COMP0;
316 #else
317   return BURTC->COMP;
318 #endif
319 }
320 
321 /***************************************************************************//**
322  * @brief Reset counter
323  ******************************************************************************/
BURTC_CounterReset(void)324 void BURTC_CounterReset(void)
325 {
326 #if defined(_BURTC_CTRL_MASK)
327   /* Set and clear reset bit */
328   BUS_RegBitWrite(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT, 1U);
329   BUS_RegBitWrite(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT, 0U);
330 #else
331   BURTC_Stop();
332   BURTC->CNT = 0U;
333   BURTC_Start();
334 #endif
335 }
336 
337 /***************************************************************************//**
338  * @brief
339  *   Restore BURTC to reset state.
340  * @note
341  *   Before accessing the BURTC, BURSTEN in RMU->CTRL must be cleared.
342  *   LOCK will not be reset to default value, as this will disable access
343  *   to core BURTC registers.
344  ******************************************************************************/
BURTC_Reset(void)345 void BURTC_Reset(void)
346 {
347 #if defined(_SILICON_LABS_32B_SERIES_0)
348   bool buResetState;
349 
350   /* Read reset state, set reset, and restore state. */
351   buResetState = BUS_RegBitRead(&RMU->CTRL, _RMU_CTRL_BURSTEN_SHIFT);
352   BUS_RegBitWrite(&RMU->CTRL, _RMU_CTRL_BURSTEN_SHIFT, 1);
353   BUS_RegBitWrite(&RMU->CTRL, _RMU_CTRL_BURSTEN_SHIFT, buResetState);
354 #elif defined(_SILICON_LABS_32B_SERIES_2)
355   if (BURTC->EN != 0U) {
356     BURTC_SyncWait();
357   }
358   BURTC->EN_SET = BURTC_EN_EN;
359   BURTC_Stop();
360   BURTC->CNT     = 0x0;
361   BURTC->PRECNT  = 0x0;
362   BURTC->COMP    = 0x0;
363   BURTC->EM4WUEN = _BURTC_EM4WUEN_RESETVALUE;
364   BURTC->IEN     = _BURTC_IEN_RESETVALUE;
365   BURTC->IF_CLR  = _BURTC_IF_MASK;
366   /* Wait for all values to synchronize. BusFaults can happen if we don't
367    * do this before the enable bit is cleared. */
368   BURTC_SyncWait();
369   BURTC->EN_CLR  = BURTC_EN_EN;
370 #if defined(_BURTC_SYNCBUSY_EN_MASK)
371   while (BURTC->SYNCBUSY != 0U) {
372     // Wait for the EN=0 to synchronize
373   }
374 #elif defined(_BURTC_EN_DISABLING_MASK)
375   while (BURTC->EN & _BURTC_EN_DISABLING_MASK) {
376     /* Wait for disabling to finish */
377   }
378 #endif
379   BURTC->CFG = _BURTC_CFG_RESETVALUE;
380 #endif
381 }
382 
383 #if defined(_BURTC_CTRL_MASK)
384 /***************************************************************************//**
385  * @brief
386  *   Get the clock frequency of the BURTC.
387  *
388  * @return
389  *   The current frequency in Hz.
390  ******************************************************************************/
BURTC_ClockFreqGet(void)391 uint32_t BURTC_ClockFreqGet(void)
392 {
393   uint32_t clkSel;
394   uint32_t clkDiv;
395   uint32_t frequency;
396 
397   clkSel = BURTC->CTRL & _BURTC_CTRL_CLKSEL_MASK;
398   clkDiv = (BURTC->CTRL & _BURTC_CTRL_PRESC_MASK) >> _BURTC_CTRL_PRESC_SHIFT;
399 
400   switch (clkSel) {
401     /** Ultra-low frequency (1 kHz) clock. */
402     case BURTC_CTRL_CLKSEL_ULFRCO:
403       if (_BURTC_CTRL_PRESC_DIV1 == clkDiv) {
404         frequency = 2000;     /* 2 kHz when clock divisor is 1. */
405       } else {
406         frequency = SystemULFRCOClockGet();  /* 1 kHz when divisor is different
407                                                 from 1. */
408       }
409       break;
410 
411     /** Low-frequency RC oscillator. */
412     case BURTC_CTRL_CLKSEL_LFRCO:
413       frequency = SystemLFRCOClockGet() / (1 << clkDiv); /* freq=32768/2^clkDiv */
414       break;
415 
416     /** Low-frequency crystal oscillator. */
417     case BURTC_CTRL_CLKSEL_LFXO:
418       frequency = SystemLFXOClockGet() / (1 << clkDiv); /* freq=32768/2^clkDiv */
419       break;
420 
421     default:
422       /* No clock selected for BURTC. */
423       frequency = 0;
424   }
425   return frequency;
426 }
427 #endif
428 
429 /** @} (end addtogroup burtc) */
430 
431 #endif /* BURTC_PRESENT */
432