1 /***************************************************************************//**
2  * @file
3  * @brief Low Energy Timer (LETIMER) Peripheral API
4  * @author Energy Micro AS
5  * @version 3.0.0
6  *******************************************************************************
7  * @section License
8  * <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
9  *******************************************************************************
10  *
11  * Permission is granted to anyone to use this software for any purpose,
12  * including commercial applications, and to alter it and redistribute it
13  * freely, subject to the following restrictions:
14  *
15  * 1. The origin of this software must not be misrepresented; you must not
16  *    claim that you wrote the original software.
17  * 2. Altered source versions must be plainly marked as such, and must not be
18  *    misrepresented as being the original software.
19  * 3. This notice may not be removed or altered from any source distribution.
20  *
21  * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
22  * obligation to support this Software. Energy Micro AS is providing the
23  * Software "AS IS", with no express or implied warranties of any kind,
24  * including, but not limited to, any implied warranties of merchantability
25  * or fitness for any particular purpose or warranties against infringement
26  * of any proprietary rights of a third party.
27  *
28  * Energy Micro AS will not be liable for any consequential, incidental, or
29  * special damages, or any other relief, or for any claim by any third party,
30  * arising from your use of this Software.
31  *
32  ******************************************************************************/
33 #include "em_letimer.h"
34 #include "em_cmu.h"
35 #include "em_assert.h"
36 
37 /***************************************************************************//**
38  * @addtogroup EM_Library
39  * @{
40  ******************************************************************************/
41 
42 /***************************************************************************//**
43  * @addtogroup LETIMER
44  * @brief Low Energy Timer (LETIMER) Peripheral API
45  * @{
46  ******************************************************************************/
47 
48 /*******************************************************************************
49  *******************************   DEFINES   ***********************************
50  ******************************************************************************/
51 
52 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
53 
54 /** Validation of valid comparator register for assert statements. */
55 #define LETIMER_COMP_REG_VALID(reg)    (((reg) <= 1))
56 
57 /** Validation of LETIMER register block pointer reference for assert statements. */
58 #define LETIMER_REF_VALID(ref)         ((ref) == LETIMER0)
59 
60 /** Validation of valid repeat counter register for assert statements. */
61 #define LETIMER_REP_REG_VALID(reg)     (((reg) <= 1))
62 
63 /** @endcond */
64 
65 
66 /*******************************************************************************
67  **************************   LOCAL FUNCTIONS   ********************************
68  ******************************************************************************/
69 
70 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
71 
72 #if defined(_EFM32_GECKO_FAMILY)
73 /***************************************************************************//**
74  * @brief
75  *   Wait for ongoing sync of register(s) to low frequency domain to complete.
76  *
77  * @note
78  *   This only applies to the Gecko Family, see the reference manual
79  *   chapter about Access to Low Energy Peripherals (Asynchronos Registers)
80  *   for details.
81  *
82  * @param[in] letimer
83  *   Pointer to LETIMER peripheral register block
84  *
85  * @param[in] mask
86  *   Bitmask corresponding to SYNCBUSY register defined bits, indicating
87  *   registers that must complete any ongoing synchronization.
88  ******************************************************************************/
LETIMER_Sync(LETIMER_TypeDef * letimer,uint32_t mask)89 __STATIC_INLINE void LETIMER_Sync(LETIMER_TypeDef *letimer, uint32_t mask)
90 {
91   /* Avoid deadlock if modifying the same register twice when freeze mode is */
92   /* activated. */
93   if (letimer->FREEZE & LETIMER_FREEZE_REGFREEZE)
94     return;
95 
96   /* Wait for any pending previous write operation to have been completed */
97   /* in low frequency domain, only required for Gecko Family of devices  */
98   while (letimer->SYNCBUSY & mask)
99     ;
100 }
101 #endif
102 
103 /** @endcond */
104 
105 /*******************************************************************************
106  **************************   GLOBAL FUNCTIONS   *******************************
107  ******************************************************************************/
108 
109 /***************************************************************************//**
110  * @brief
111  *   Get LETIMER compare register value.
112  *
113  * @param[in] letimer
114  *   Pointer to LETIMER peripheral register block
115  *
116  * @param[in] comp
117  *   Compare register to get, either 0 or 1
118  *
119  * @return
120  *   Compare register value, 0 if invalid register selected.
121  ******************************************************************************/
LETIMER_CompareGet(LETIMER_TypeDef * letimer,unsigned int comp)122 uint32_t LETIMER_CompareGet(LETIMER_TypeDef *letimer, unsigned int comp)
123 {
124   uint32_t ret;
125 
126   EFM_ASSERT(LETIMER_REF_VALID(letimer) && LETIMER_COMP_REG_VALID(comp));
127 
128   /* Initialize selected compare value */
129   switch (comp)
130   {
131   case 0:
132     ret = letimer->COMP0;
133     break;
134 
135   case 1:
136     ret = letimer->COMP1;
137     break;
138 
139   default:
140     /* Unknown compare register selected */
141     ret = 0;
142     break;
143   }
144 
145   return(ret);
146 }
147 
148 
149 /***************************************************************************//**
150  * @brief
151  *   Set LETIMER compare register value.
152  *
153  * @note
154  *   The setting of a compare register requires synchronization into the
155  *   low frequency domain. If the same register is modified before a previous
156  *   update has completed, this function will stall until the previous
157  *   synchronization has completed. This only applies to the Gecko Family, see
158  *   comment in the LETIMER_Sync() internal function call.
159  *
160  * @param[in] letimer
161  *   Pointer to LETIMER peripheral register block
162  *
163  * @param[in] comp
164  *   Compare register to set, either 0 or 1
165  *
166  * @param[in] value
167  *   Initialization value (<= 0x0000ffff)
168  ******************************************************************************/
LETIMER_CompareSet(LETIMER_TypeDef * letimer,unsigned int comp,uint32_t value)169 void LETIMER_CompareSet(LETIMER_TypeDef *letimer,
170                         unsigned int comp,
171                         uint32_t value)
172 {
173   volatile uint32_t *compReg;
174   uint32_t          syncbusy;
175 
176   EFM_ASSERT(LETIMER_REF_VALID(letimer) &&
177              LETIMER_COMP_REG_VALID(comp) &&
178              ((value & ~(_LETIMER_COMP0_COMP0_MASK >> _LETIMER_COMP0_COMP0_SHIFT)) == 0));
179 
180   /* Initialize selected compare value */
181   switch (comp)
182   {
183   case 0:
184     compReg  = &(letimer->COMP0);
185     syncbusy = LETIMER_SYNCBUSY_COMP0;
186     break;
187 
188   case 1:
189     compReg  = &(letimer->COMP1);
190     syncbusy = LETIMER_SYNCBUSY_COMP1;
191     break;
192 
193   default:
194     /* Unknown compare register selected, abort */
195     return;
196   }
197 
198 #if defined(_EFM32_GECKO_FAMILY)
199   /* LF register about to be modified require sync. busy check */
200   LETIMER_Sync(letimer, syncbusy);
201 #endif
202 
203   *compReg = value;
204 }
205 
206 
207 /***************************************************************************//**
208  * @brief
209  *   Start/stop LETIMER.
210  *
211  * @note
212  *   The enabling/disabling of the LETIMER modifies the LETIMER CMD register
213  *   which requires synchronization into the low frequency domain. If this
214  *   register is modified before a previous update to the same register has
215  *   completed, this function will stall until the previous synchronization has
216  *   completed. This only applies to the Gecko Family, see comment in the
217  *   LETIMER_Sync() internal function call.
218  *
219  * @param[in] letimer
220  *   Pointer to LETIMER peripheral register block.
221  *
222  * @param[in] enable
223  *   true to enable counting, false to disable.
224  ******************************************************************************/
LETIMER_Enable(LETIMER_TypeDef * letimer,bool enable)225 void LETIMER_Enable(LETIMER_TypeDef *letimer, bool enable)
226 {
227   EFM_ASSERT(LETIMER_REF_VALID(letimer));
228 
229 #if defined(_EFM32_GECKO_FAMILY)
230   /* LF register about to be modified require sync. busy check */
231   LETIMER_Sync(letimer, LETIMER_SYNCBUSY_CMD);
232 #endif
233 
234   if (enable)
235   {
236     letimer->CMD = LETIMER_CMD_START;
237   }
238   else
239   {
240     letimer->CMD = LETIMER_CMD_STOP;
241   }
242 }
243 
244 
245 /***************************************************************************//**
246  * @brief
247  *   LETIMER register synchronization freeze control.
248  *
249  * @details
250  *   Some LETIMER registers require synchronization into the low frequency (LF)
251  *   domain. The freeze feature allows for several such registers to be
252  *   modified before passing them to the LF domain simultaneously (which
253  *   takes place when the freeze mode is disabled).
254  *
255  * @note
256  *   When enabling freeze mode, this function will wait for all current
257  *   ongoing LETIMER synchronization to LF domain to complete (Normally
258  *   synchronization will not be in progress.) However for this reason, when
259  *   using freeze mode, modifications of registers requiring LF synchronization
260  *   should be done within one freeze enable/disable block to avoid unecessary
261  *   stalling.
262  *
263  * @param[in] letimer
264  *   Pointer to LETIMER peripheral register block.
265  *
266  * @param[in] enable
267  *   @li true - enable freeze, modified registers are not propagated to the
268  *       LF domain
269  *   @li false - disables freeze, modified registers are propagated to LF
270  *       domain
271  ******************************************************************************/
LETIMER_FreezeEnable(LETIMER_TypeDef * letimer,bool enable)272 void LETIMER_FreezeEnable(LETIMER_TypeDef *letimer, bool enable)
273 {
274   if (enable)
275   {
276     /*
277      * Wait for any ongoing LF synchronization to complete. This is just to
278      * protect against the rare case when a user
279      * - modifies a register requiring LF sync
280      * - then enables freeze before LF sync completed
281      * - then modifies the same register again
282      * since modifying a register while it is in sync progress should be
283      * avoided.
284      */
285     while (letimer->SYNCBUSY)
286       ;
287 
288     letimer->FREEZE = LETIMER_FREEZE_REGFREEZE;
289   }
290   else
291   {
292     letimer->FREEZE = 0;
293   }
294 }
295 
296 
297 /***************************************************************************//**
298  * @brief
299  *   Initialize LETIMER.
300  *
301  * @details
302  *   Note that the compare/repeat values must be set separately with
303  *   LETIMER_CompareSet() and LETIMER_RepeatSet(). That should probably be done
304  *   prior to the use of this function if configuring the LETIMER to start when
305  *   initialization is completed.
306  *
307  * @note
308  *   The initialization of the LETIMER modifies the LETIMER CTRL/CMD registers
309  *   which require synchronization into the low frequency domain. If any of those
310  *   registers are modified before a previous update to the same register has
311  *   completed, this function will stall until the previous synchronization has
312  *   completed. This only applies to the Gecko Family, see comment in the
313  *   LETIMER_Sync() internal function call.
314  *
315  * @param[in] letimer
316  *   Pointer to LETIMER peripheral register block.
317  *
318  * @param[in] init
319  *   Pointer to LETIMER initialization structure.
320  ******************************************************************************/
LETIMER_Init(LETIMER_TypeDef * letimer,const LETIMER_Init_TypeDef * init)321 void LETIMER_Init(LETIMER_TypeDef *letimer, const LETIMER_Init_TypeDef *init)
322 {
323   uint32_t tmp = 0;
324 
325   EFM_ASSERT(LETIMER_REF_VALID(letimer));
326 
327   /* Stop timer if specified to be disabled and running */
328   if (!(init->enable) && (letimer->STATUS & LETIMER_STATUS_RUNNING))
329   {
330 #if defined(_EFM32_GECKO_FAMILY)
331     /* LF register about to be modified require sync. busy check */
332     LETIMER_Sync(letimer, LETIMER_SYNCBUSY_CMD);
333 #endif
334     letimer->CMD = LETIMER_CMD_STOP;
335   }
336 
337   /* Configure DEBUGRUN flag, sets whether or not counter should be
338    * updated when debugger is active */
339   if (init->debugRun)
340   {
341     tmp |= LETIMER_CTRL_DEBUGRUN;
342   }
343 
344   if (init->rtcComp0Enable)
345   {
346     tmp |= LETIMER_CTRL_RTCC0TEN;
347   }
348 
349   if (init->rtcComp1Enable)
350   {
351     tmp |= LETIMER_CTRL_RTCC1TEN;
352   }
353 
354   if (init->comp0Top)
355   {
356     tmp |= LETIMER_CTRL_COMP0TOP;
357   }
358 
359   if (init->bufTop)
360   {
361     tmp |= LETIMER_CTRL_BUFTOP;
362   }
363 
364   if (init->out0Pol)
365   {
366     tmp |= LETIMER_CTRL_OPOL0;
367   }
368 
369   if (init->out1Pol)
370   {
371     tmp |= LETIMER_CTRL_OPOL1;
372   }
373 
374   tmp |= init->ufoa0 << _LETIMER_CTRL_UFOA0_SHIFT;
375   tmp |= init->ufoa1 << _LETIMER_CTRL_UFOA1_SHIFT;
376   tmp |= init->repMode << _LETIMER_CTRL_REPMODE_SHIFT;
377 
378 #if defined(_EFM32_GECKO_FAMILY)
379   /* LF register about to be modified require sync. busy check */
380   LETIMER_Sync(letimer, LETIMER_SYNCBUSY_CTRL);
381 #endif
382   letimer->CTRL = tmp;
383 
384   /* Start timer if specified to be enabled and not already running */
385   if (init->enable && !(letimer->STATUS & LETIMER_STATUS_RUNNING))
386   {
387 #if defined(_EFM32_GECKO_FAMILY)
388     /* LF register about to be modified require sync. busy check */
389     LETIMER_Sync(letimer, LETIMER_SYNCBUSY_CMD);
390 #endif
391     letimer->CMD = LETIMER_CMD_START;
392   }
393 }
394 
395 
396 /***************************************************************************//**
397  * @brief
398  *   Get LETIMER repeat register value.
399  *
400  * @param[in] letimer
401  *   Pointer to LETIMER peripheral register block
402  *
403  * @param[in] rep
404  *   Repeat register to get, either 0 or 1
405  *
406  * @return
407  *   Repeat register value, 0 if invalid register selected.
408  ******************************************************************************/
LETIMER_RepeatGet(LETIMER_TypeDef * letimer,unsigned int rep)409 uint32_t LETIMER_RepeatGet(LETIMER_TypeDef *letimer, unsigned int rep)
410 {
411   uint32_t ret;
412 
413   EFM_ASSERT(LETIMER_REF_VALID(letimer) && LETIMER_REP_REG_VALID(rep));
414 
415   /* Initialize selected compare value */
416   switch (rep)
417   {
418   case 0:
419     ret = letimer->REP0;
420     break;
421 
422   case 1:
423     ret = letimer->REP1;
424     break;
425 
426   default:
427     /* Unknown compare register selected */
428     ret = 0;
429     break;
430   }
431 
432   return(ret);
433 }
434 
435 
436 /***************************************************************************//**
437  * @brief
438  *   Set LETIMER repeat counter register value.
439  *
440  * @note
441  *   The setting of a repeat counter register requires synchronization into the
442  *   low frequency domain. If the same register is modified before a previous
443  *   update has completed, this function will stall until the previous
444  *   synchronization has completed. This only applies to the Gecko Family, see
445  *   comment in the LETIMER_Sync() internal function call.
446  *
447  * @param[in] letimer
448  *   Pointer to LETIMER peripheral register block
449  *
450  * @param[in] rep
451  *   Repeat counter register to set, either 0 or 1
452  *
453  * @param[in] value
454  *   Initialization value (<= 0x0000ffff)
455  ******************************************************************************/
LETIMER_RepeatSet(LETIMER_TypeDef * letimer,unsigned int rep,uint32_t value)456 void LETIMER_RepeatSet(LETIMER_TypeDef *letimer,
457                        unsigned int rep,
458                        uint32_t value)
459 {
460   volatile uint32_t *repReg;
461 #if defined(_EFM32_GECKO_FAMILY)
462   uint32_t          syncbusy;
463 #endif
464   EFM_ASSERT(LETIMER_REF_VALID(letimer) &&
465              LETIMER_REP_REG_VALID(rep) &&
466              ((value & ~(_LETIMER_REP0_REP0_MASK >> _LETIMER_REP0_REP0_SHIFT)) == 0));
467 
468   /* Initialize selected compare value */
469   switch (rep)
470   {
471   case 0:
472     repReg = &(letimer->REP0);
473 #if defined(_EFM32_GECKO_FAMILY)
474     syncbusy = LETIMER_SYNCBUSY_REP0;
475 #endif
476     break;
477 
478   case 1:
479     repReg = &(letimer->REP1);
480 #if defined(_EFM32_GECKO_FAMILY)
481     syncbusy = LETIMER_SYNCBUSY_REP1;
482 #endif
483     break;
484 
485   default:
486     /* Unknown compare register selected, abort */
487     return;
488   }
489 
490 #if defined(_EFM32_GECKO_FAMILY)
491   /* LF register about to be modified require sync. busy check */
492   LETIMER_Sync(letimer, syncbusy);
493 #endif
494 
495   *repReg = value;
496 }
497 
498 
499 /***************************************************************************//**
500  * @brief
501  *   Reset LETIMER to same state as after a HW reset.
502  *
503  * @note
504  *   The ROUTE register is NOT reset by this function, in order to allow for
505  *   centralized setup of this feature.
506  *
507  * @param[in] letimer
508  *   Pointer to LETIMER peripheral register block.
509  ******************************************************************************/
LETIMER_Reset(LETIMER_TypeDef * letimer)510 void LETIMER_Reset(LETIMER_TypeDef *letimer)
511 {
512   /* Freeze registers to avoid stalling for LF synchronization */
513   LETIMER_FreezeEnable(letimer, true);
514 
515   /* Make sure disabled first, before resetting other registers */
516   letimer->CMD = LETIMER_CMD_STOP | LETIMER_CMD_CLEAR |
517                  LETIMER_CMD_CTO0 | LETIMER_CMD_CTO1;
518   letimer->CTRL  = _LETIMER_CTRL_RESETVALUE;
519   letimer->COMP0 = _LETIMER_COMP0_RESETVALUE;
520   letimer->COMP1 = _LETIMER_COMP1_RESETVALUE;
521   letimer->REP0  = _LETIMER_REP0_RESETVALUE;
522   letimer->REP1  = _LETIMER_REP1_RESETVALUE;
523   letimer->IEN   = _LETIMER_IEN_RESETVALUE;
524   letimer->IFC   = _LETIMER_IFC_MASK;
525   /* Do not reset route register, setting should be done independently */
526 
527   /* Unfreeze registers, pass new settings on to LETIMER */
528   LETIMER_FreezeEnable(letimer, false);
529 }
530 
531 
532 /** @} (end addtogroup LETIMER) */
533 /** @} (end addtogroup EM_Library) */
534