1 /***************************************************************************//**
2  * @file
3  * @brief Real Time Counter (RTC) 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_rtc.h"
32 #if defined(RTC_COUNT) && (RTC_COUNT > 0)
33 
34 #include "em_assert.h"
35 #include "em_bus.h"
36 
37 /***************************************************************************//**
38  * @addtogroup rtc RTC - Real Time Counter
39  * @brief Real Time Counter (RTC) Peripheral API
40  * @details
41  *  This module contains functions to control the RTC peripheral of Silicon
42  *  Labs 32-bit MCUs and SoCs. The RTC ensures timekeeping in low energy modes.
43  * @{
44  ******************************************************************************/
45 
46 /*******************************************************************************
47  *******************************   DEFINES   ***********************************
48  ******************************************************************************/
49 
50 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
51 
52 /** Validation of valid comparator register for assert statements. */
53 #define RTC_COMP_REG_VALID(reg)    (((reg) < NUM_RTC_CHANNELS))
54 
55 /** @endcond */
56 
57 /*******************************************************************************
58  **************************   LOCAL FUNCTIONS   ********************************
59  ******************************************************************************/
60 
61 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
62 
63 #if defined(_EFM32_GECKO_FAMILY)
64 /***************************************************************************//**
65  * @brief
66  *   Wait for ongoing sync of register(s) to low-frequency domain to complete.
67  *
68  * @note
69  *   This only applies to the Gecko Family, see the reference manual
70  *   chapter about Access to Low Energy Peripherals (Asynchronos Registers)
71  *   for details. For Tiny Gecko and Giant Gecko, the RTC supports immediate
72  *   updates of registers and will automatically hold the bus until the
73  *   register has been updated.
74  *
75  * @param[in] mask
76  *   A bitmask corresponding to SYNCBUSY register defined bits, indicating
77  *   registers that must complete any ongoing synchronization.
78  ******************************************************************************/
regSync(uint32_t mask)79 __STATIC_INLINE void regSync(uint32_t mask)
80 {
81   /* Avoid deadlock if modifying the same register twice when freeze mode is */
82   /* activated. */
83   if (RTC->FREEZE & RTC_FREEZE_REGFREEZE) {
84     return;
85   }
86 
87   /* Wait for any pending previous write operations to have been completed */
88   /* in low-frequency domain. This is only required for the Gecko Family. */
89   while (RTC->SYNCBUSY & mask)
90     ;
91 }
92 #endif
93 
94 /** @endcond */
95 
96 /*******************************************************************************
97  **************************   GLOBAL FUNCTIONS   *******************************
98  ******************************************************************************/
99 
100 /***************************************************************************//**
101  * @brief
102  *   Get the RTC compare register value.
103  *
104  * @param[in] comp
105  *   A compare register to get. This value must be less than
106  *   @ref NUM_RTC_CHANNELS.
107  *
108  * @return
109  *   A compare register value, 0 if invalid register selected.
110  ******************************************************************************/
RTC_CompareGet(unsigned int comp)111 uint32_t RTC_CompareGet(unsigned int comp)
112 {
113   uint32_t ret;
114 
115   EFM_ASSERT(RTC_COMP_REG_VALID(comp));
116 
117 #if defined(_RTC_COMP_COMP_MASK)
118   ret = RTC->COMP[comp].COMP;
119 #elif defined(_RTC_COMP0_MASK)
120   /* Initialize selected compare value */
121   switch (comp) {
122     case 0:
123       ret = RTC->COMP0;
124       break;
125 
126     case 1:
127       ret = RTC->COMP1;
128       break;
129 
130     default:
131       /* An unknown compare register selected. */
132       ret = 0;
133       break;
134   }
135 #endif
136   return ret;
137 }
138 
139 /***************************************************************************//**
140  * @brief
141  *   Set the RTC compare register value.
142  *
143  * @note
144  *   The setting of a compare register requires synchronization into the
145  *   low-frequency domain. If the same register is modified before a previous
146  *   update has completed, this function will stall until the previous
147  *   synchronization has completed. This only applies to the Gecko Family. See
148  *   comments in the regSync() internal function call.
149  *
150  * @param[in] comp
151  *   A compare register to set. This value must be less than
152  *   @ref NUM_RTC_CHANNELS.
153  *
154  * @param[in] value
155  *   An initialization value (<= 0x00ffffff).
156  ******************************************************************************/
RTC_CompareSet(unsigned int comp,uint32_t value)157 void RTC_CompareSet(unsigned int comp, uint32_t value)
158 {
159   volatile uint32_t *compReg;
160 #if defined(_EFM32_GECKO_FAMILY)
161   uint32_t          syncbusy;
162 #endif
163 
164   EFM_ASSERT(RTC_COMP_REG_VALID(comp));
165 
166 #if defined(_RTC_COMP_COMP_COMP_MASK)
167   EFM_ASSERT((value & ~(_RTC_COMP_COMP_COMP_MASK >> _RTC_COMP_COMP_COMP_SHIFT)) == 0);
168 #elif defined(_RTC_COMP0_COMP0_MASK)
169   EFM_ASSERT((value & ~(_RTC_COMP0_COMP0_MASK >> _RTC_COMP0_COMP0_SHIFT)) == 0);
170 #endif
171 
172 #if defined(_RTC_COMP_COMP_MASK)
173   compReg = &(RTC->COMP[comp].COMP);
174 #elif defined(_RTC_COMP0_MASK)
175   /* Initialize the selected compare value. */
176   switch (comp) {
177     case 0:
178       compReg = &(RTC->COMP0);
179 #if defined(_EFM32_GECKO_FAMILY)
180       syncbusy = RTC_SYNCBUSY_COMP0;
181 #endif
182       break;
183 
184     case 1:
185       compReg = &(RTC->COMP1);
186 #if defined(_EFM32_GECKO_FAMILY)
187       syncbusy = RTC_SYNCBUSY_COMP1;
188 #endif
189       break;
190 
191     default:
192       /* An unknown compare register selected. Abort. */
193       return;
194   }
195 #endif
196 
197 #if defined(_EFM32_GECKO_FAMILY)
198   /* LF register about to be modified requires sync. busy check. */
199   regSync(syncbusy);
200 #endif
201 
202   *compReg = value;
203 }
204 
205 /***************************************************************************//**
206  * @brief
207  *   Enable/disable RTC.
208  *
209  * @note
210  *   The enabling/disabling of RTC modifies the RTC CTRL register which
211  *   requires synchronization into the low-frequency domain. If this register is
212  *   modified before a previous update to the same register has completed, this
213  *   function will stall until the previous synchronization has completed. This
214  *   only applies to the Gecko Family. See comments in the regSync() internal
215  *   function call.
216  *
217  * @param[in] enable
218  *   True to enable counting, false to disable.
219  ******************************************************************************/
RTC_Enable(bool enable)220 void RTC_Enable(bool enable)
221 {
222 #if defined(_EFM32_GECKO_FAMILY)
223   /* LF register about to be modified requires sync. busy check. */
224   regSync(RTC_SYNCBUSY_CTRL);
225 #endif
226 
227   BUS_RegBitWrite(&(RTC->CTRL), _RTC_CTRL_EN_SHIFT, enable);
228 
229 #if defined(_EFM32_GECKO_FAMILY)
230   /* Wait for CTRL to be updated before returning because a calling code may
231      depend on the CTRL register being updated after this function has
232      returned. */
233   regSync(RTC_SYNCBUSY_CTRL);
234 #endif
235 }
236 
237 #if defined(_RTC_FREEZE_MASK)
238 /***************************************************************************//**
239  * @brief
240  *   RTC register synchronization freeze control.
241  *
242  * @details
243  *   Some RTC registers require synchronization into the low-frequency (LF)
244  *   domain. The freeze feature allows for several registers to be
245  *   modified before passing them to the LF domain simultaneously (which
246  *   takes place when the freeze mode is disabled).
247  *
248  * @note
249  *   When enabling freeze mode, this function will wait for all current
250  *   ongoing RTC synchronization to LF domain to complete (normally
251  *   synchronization will not be in progress.) However, for this reason, when
252  *   using freeze mode, modifications of registers requiring LF synchronization
253  *   should be done within one freeze enable/disable block to avoid unnecessary
254  *   stalling. This only applies to the Gecko Family. See the reference manual
255  *   chapter about Access to Low Energy Peripherals (Asynchronos Registers)
256  *   for details.
257  *
258  * @param[in] enable
259  *   @li True - enable freeze, modified registers are not propagated to the
260  *       LF domain
261  *   @li False - disables freeze, modified registers are propagated to LF
262  *       domain
263  ******************************************************************************/
RTC_FreezeEnable(bool enable)264 void RTC_FreezeEnable(bool enable)
265 {
266   if (enable) {
267 #if defined(_EFM32_GECKO_FAMILY)
268     /* Wait for any ongoing LF synchronization to complete to */
269     /* protect against the rare case when a user                            */
270     /* - modifies a register requiring LF sync                              */
271     /* - then enables freeze before LF sync completed                       */
272     /* - then modifies the same register again                              */
273     /* since modifying a register while it is in sync progress should be    */
274     /* avoided.                                                             */
275     while (RTC->SYNCBUSY)
276       ;
277 #endif
278     RTC->FREEZE = RTC_FREEZE_REGFREEZE;
279   } else {
280     RTC->FREEZE = 0;
281   }
282 }
283 #endif
284 
285 /***************************************************************************//**
286  * @brief
287  *   Initialize RTC.
288  *
289  * @details
290  *   Note that the compare values must be set separately with RTC_CompareSet()
291  *   prior to the use of this function if
292  *   configuring the RTC to start when initialization is completed.
293  *
294  * @note
295  *   The initialization of the RTC modifies the RTC CTRL register which requires
296  *   synchronization into the low-frequency domain. If this register is
297  *   modified before a previous update to the same register has completed, this
298  *   function will stall until the previous synchronization has completed. This
299  *   only applies to the Gecko Family. See comments in the regSync() internal
300  *   function call.
301  *
302  * @param[in] init
303  *   A pointer to the RTC initialization structure.
304  ******************************************************************************/
RTC_Init(const RTC_Init_TypeDef * init)305 void RTC_Init(const RTC_Init_TypeDef *init)
306 {
307   uint32_t tmp;
308 
309   if (init->enable) {
310     tmp = RTC_CTRL_EN;
311   } else {
312     tmp = 0;
313   }
314 
315   /* Configure the DEBUGRUN flag, which sets whether or not counter should be
316    * updated when debugger is active. */
317   if (init->debugRun) {
318     tmp |= RTC_CTRL_DEBUGRUN;
319   }
320 
321   /* Configure COMP0TOP, which will use the COMP0 compare value as an
322    * overflow value, instead of default 24-bit 0x00ffffff. */
323   if (init->comp0Top) {
324     tmp |= RTC_CTRL_COMP0TOP;
325   }
326 
327 #if defined(_EFM32_GECKO_FAMILY)
328   /* LF register about to be modified requires sync. busy check. */
329   regSync(RTC_SYNCBUSY_CTRL);
330 #endif
331 
332   RTC->CTRL = tmp;
333 }
334 
335 /***************************************************************************//**
336  * @brief
337  *   Restore RTC to reset state.
338  ******************************************************************************/
RTC_Reset(void)339 void RTC_Reset(void)
340 {
341   /* Restore all essential RTC register to default configurations. */
342 #if defined(_RTC_FREEZE_MASK)
343   RTC->FREEZE = _RTC_FREEZE_RESETVALUE;
344 #endif
345   RTC->CTRL   = _RTC_CTRL_RESETVALUE;
346 #if defined(_RTC_COMP_COMP_MASK)
347   for (unsigned int ch = 0; ch < NUM_RTC_CHANNELS; ch++) {
348     RTC->COMP[ch].COMP = _RTC_COMP_COMP_RESETVALUE;
349   }
350 #elif defined(_RTC_COMP0_MASK)
351   RTC->COMP0  = _RTC_COMP0_RESETVALUE;
352   RTC->COMP1  = _RTC_COMP1_RESETVALUE;
353 #endif
354   RTC->IEN    = _RTC_IEN_RESETVALUE;
355   RTC->IFC    = _RTC_IFC_RESETVALUE;
356 
357 #if defined(_EFM32_GECKO_FAMILY)
358   /* Wait for CTRL, COMP0, and COMP1 to be updated before returning because the
359      calling code may depend on the register values being updated after
360      this function has returned. */
361   regSync(RTC_SYNCBUSY_CTRL | RTC_SYNCBUSY_COMP0 | RTC_SYNCBUSY_COMP1);
362 #endif
363 }
364 
365 /***************************************************************************//**
366  * @brief
367  *   Restart the RTC counter from zero.
368  ******************************************************************************/
RTC_CounterReset(void)369 void RTC_CounterReset(void)
370 {
371   /* A disable/enable sequence will start the counter at zero. */
372   RTC_Enable(false);
373   RTC_Enable(true);
374 }
375 
376 /** @} (end addtogroup rtc) */
377 #endif /* defined(RTC_COUNT) && (RTC_COUNT > 0) */
378