--- /dev/null
+/***************************************************************************//**\r
+ * @file em_letimer.c\r
+ * @brief Low Energy Timer (LETIMER) Peripheral API\r
+ * @version 4.2.1\r
+ *******************************************************************************\r
+ * @section License\r
+ * <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b>\r
+ *******************************************************************************\r
+ *\r
+ * Permission is granted to anyone to use this software for any purpose,\r
+ * including commercial applications, and to alter it and redistribute it\r
+ * freely, subject to the following restrictions:\r
+ *\r
+ * 1. The origin of this software must not be misrepresented; you must not\r
+ * claim that you wrote the original software.\r
+ * 2. Altered source versions must be plainly marked as such, and must not be\r
+ * misrepresented as being the original software.\r
+ * 3. This notice may not be removed or altered from any source distribution.\r
+ *\r
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no\r
+ * obligation to support this Software. Silicon Labs is providing the\r
+ * Software "AS IS", with no express or implied warranties of any kind,\r
+ * including, but not limited to, any implied warranties of merchantability\r
+ * or fitness for any particular purpose or warranties against infringement\r
+ * of any proprietary rights of a third party.\r
+ *\r
+ * Silicon Labs will not be liable for any consequential, incidental, or\r
+ * special damages, or any other relief, or for any claim by any third party,\r
+ * arising from your use of this Software.\r
+ *\r
+ ******************************************************************************/\r
+\r
+#include "em_letimer.h"\r
+#if defined(LETIMER_COUNT) && (LETIMER_COUNT > 0)\r
+#include "em_cmu.h"\r
+#include "em_assert.h"\r
+\r
+/***************************************************************************//**\r
+ * @addtogroup EM_Library\r
+ * @{\r
+ ******************************************************************************/\r
+\r
+/***************************************************************************//**\r
+ * @addtogroup LETIMER\r
+ * @brief Low Energy Timer (LETIMER) Peripheral API\r
+ * @{\r
+ ******************************************************************************/\r
+\r
+/*******************************************************************************\r
+ ******************************* DEFINES ***********************************\r
+ ******************************************************************************/\r
+\r
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */\r
+\r
+/** Validation of valid comparator register for assert statements. */\r
+#define LETIMER_COMP_REG_VALID(reg) (((reg) <= 1))\r
+\r
+/** Validation of LETIMER register block pointer reference for assert statements. */\r
+#define LETIMER_REF_VALID(ref) ((ref) == LETIMER0)\r
+\r
+/** Validation of valid repeat counter register for assert statements. */\r
+#define LETIMER_REP_REG_VALID(reg) (((reg) <= 1))\r
+\r
+/** @endcond */\r
+\r
+\r
+/*******************************************************************************\r
+ ************************** LOCAL FUNCTIONS ********************************\r
+ ******************************************************************************/\r
+\r
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */\r
+\r
+#if defined(_EFM32_GECKO_FAMILY)\r
+/***************************************************************************//**\r
+ * @brief\r
+ * Wait for ongoing sync of register(s) to low frequency domain to complete.\r
+ *\r
+ * @note\r
+ * This only applies to the Gecko Family, see the reference manual\r
+ * chapter about Access to Low Energy Peripherals (Asynchronos Registers)\r
+ * for details.\r
+ *\r
+ * @param[in] letimer\r
+ * Pointer to LETIMER peripheral register block\r
+ *\r
+ * @param[in] mask\r
+ * Bitmask corresponding to SYNCBUSY register defined bits, indicating\r
+ * registers that must complete any ongoing synchronization.\r
+ ******************************************************************************/\r
+__STATIC_INLINE void regSync(LETIMER_TypeDef *letimer, uint32_t mask)\r
+{\r
+#if defined(_LETIMER_FREEZE_MASK)\r
+ /* Avoid deadlock if modifying the same register twice when freeze mode is */\r
+ /* activated. */\r
+ if (letimer->FREEZE & LETIMER_FREEZE_REGFREEZE)\r
+ return;\r
+#endif\r
+\r
+ /* Wait for any pending previous write operation to have been completed */\r
+ /* in low frequency domain, only required for Gecko Family of devices */\r
+ while (letimer->SYNCBUSY & mask)\r
+ ;\r
+}\r
+#endif\r
+\r
+/** @endcond */\r
+\r
+/*******************************************************************************\r
+ ************************** GLOBAL FUNCTIONS *******************************\r
+ ******************************************************************************/\r
+\r
+/***************************************************************************//**\r
+ * @brief\r
+ * Get LETIMER compare register value.\r
+ *\r
+ * @param[in] letimer\r
+ * Pointer to LETIMER peripheral register block\r
+ *\r
+ * @param[in] comp\r
+ * Compare register to get, either 0 or 1\r
+ *\r
+ * @return\r
+ * Compare register value, 0 if invalid register selected.\r
+ ******************************************************************************/\r
+uint32_t LETIMER_CompareGet(LETIMER_TypeDef *letimer, unsigned int comp)\r
+{\r
+ uint32_t ret;\r
+\r
+ EFM_ASSERT(LETIMER_REF_VALID(letimer) && LETIMER_COMP_REG_VALID(comp));\r
+\r
+ /* Initialize selected compare value */\r
+ switch (comp)\r
+ {\r
+ case 0:\r
+ ret = letimer->COMP0;\r
+ break;\r
+\r
+ case 1:\r
+ ret = letimer->COMP1;\r
+ break;\r
+\r
+ default:\r
+ /* Unknown compare register selected */\r
+ ret = 0;\r
+ break;\r
+ }\r
+\r
+ return(ret);\r
+}\r
+\r
+\r
+/***************************************************************************//**\r
+ * @brief\r
+ * Set LETIMER compare register value.\r
+ *\r
+ * @note\r
+ * The setting of a compare register requires synchronization into the\r
+ * low frequency domain. If the same register is modified before a previous\r
+ * update has completed, this function will stall until the previous\r
+ * synchronization has completed. This only applies to the Gecko Family, see\r
+ * comment in the LETIMER_Sync() internal function call.\r
+ *\r
+ * @param[in] letimer\r
+ * Pointer to LETIMER peripheral register block\r
+ *\r
+ * @param[in] comp\r
+ * Compare register to set, either 0 or 1\r
+ *\r
+ * @param[in] value\r
+ * Initialization value (<= 0x0000ffff)\r
+ ******************************************************************************/\r
+void LETIMER_CompareSet(LETIMER_TypeDef *letimer,\r
+ unsigned int comp,\r
+ uint32_t value)\r
+{\r
+ volatile uint32_t *compReg;\r
+\r
+ EFM_ASSERT(LETIMER_REF_VALID(letimer)\r
+ && LETIMER_COMP_REG_VALID(comp)\r
+ && ((value & ~(_LETIMER_COMP0_COMP0_MASK\r
+ >> _LETIMER_COMP0_COMP0_SHIFT))\r
+ == 0));\r
+\r
+ /* Initialize selected compare value */\r
+ switch (comp)\r
+ {\r
+ case 0:\r
+ compReg = &(letimer->COMP0);\r
+ break;\r
+\r
+ case 1:\r
+ compReg = &(letimer->COMP1);\r
+ break;\r
+\r
+ default:\r
+ /* Unknown compare register selected, abort */\r
+ return;\r
+ }\r
+\r
+#if defined(_EFM32_GECKO_FAMILY)\r
+ /* LF register about to be modified require sync. busy check */\r
+ regSync(letimer, comp ? LETIMER_SYNCBUSY_COMP1 : LETIMER_SYNCBUSY_COMP0);\r
+#endif\r
+\r
+ *compReg = value;\r
+}\r
+\r
+\r
+/***************************************************************************//**\r
+ * @brief\r
+ * Start/stop LETIMER.\r
+ *\r
+ * @note\r
+ * The enabling/disabling of the LETIMER modifies the LETIMER CMD register\r
+ * which requires synchronization into the low frequency domain. If this\r
+ * register is modified before a previous update to the same register has\r
+ * completed, this function will stall until the previous synchronization has\r
+ * completed. This only applies to the Gecko Family, see comment in the\r
+ * LETIMER_Sync() internal function call.\r
+ *\r
+ * @param[in] letimer\r
+ * Pointer to LETIMER peripheral register block.\r
+ *\r
+ * @param[in] enable\r
+ * true to enable counting, false to disable.\r
+ ******************************************************************************/\r
+void LETIMER_Enable(LETIMER_TypeDef *letimer, bool enable)\r
+{\r
+ EFM_ASSERT(LETIMER_REF_VALID(letimer));\r
+\r
+#if defined(_EFM32_GECKO_FAMILY)\r
+ /* LF register about to be modified require sync. busy check */\r
+ regSync(letimer, LETIMER_SYNCBUSY_CMD);\r
+#endif\r
+\r
+ if (enable)\r
+ {\r
+ letimer->CMD = LETIMER_CMD_START;\r
+ }\r
+ else\r
+ {\r
+ letimer->CMD = LETIMER_CMD_STOP;\r
+ }\r
+}\r
+\r
+#if defined(_LETIMER_FREEZE_MASK)\r
+/***************************************************************************//**\r
+ * @brief\r
+ * LETIMER register synchronization freeze control.\r
+ *\r
+ * @details\r
+ * Some LETIMER registers require synchronization into the low frequency (LF)\r
+ * domain. The freeze feature allows for several such registers to be\r
+ * modified before passing them to the LF domain simultaneously (which\r
+ * takes place when the freeze mode is disabled).\r
+ *\r
+ * @note\r
+ * When enabling freeze mode, this function will wait for all current\r
+ * ongoing LETIMER synchronization to LF domain to complete (Normally\r
+ * synchronization will not be in progress.) However for this reason, when\r
+ * using freeze mode, modifications of registers requiring LF synchronization\r
+ * should be done within one freeze enable/disable block to avoid unecessary\r
+ * stalling.\r
+ *\r
+ * @param[in] letimer\r
+ * Pointer to LETIMER peripheral register block.\r
+ *\r
+ * @param[in] enable\r
+ * @li true - enable freeze, modified registers are not propagated to the\r
+ * LF domain\r
+ * @li false - disables freeze, modified registers are propagated to LF\r
+ * domain\r
+ ******************************************************************************/\r
+void LETIMER_FreezeEnable(LETIMER_TypeDef *letimer, bool enable)\r
+{\r
+ if (enable)\r
+ {\r
+ /*\r
+ * Wait for any ongoing LF synchronization to complete. This is just to\r
+ * protect against the rare case when a user\r
+ * - modifies a register requiring LF sync\r
+ * - then enables freeze before LF sync completed\r
+ * - then modifies the same register again\r
+ * since modifying a register while it is in sync progress should be\r
+ * avoided.\r
+ */\r
+ while (letimer->SYNCBUSY)\r
+ ;\r
+\r
+ letimer->FREEZE = LETIMER_FREEZE_REGFREEZE;\r
+ }\r
+ else\r
+ {\r
+ letimer->FREEZE = 0;\r
+ }\r
+}\r
+#endif /* defined(_LETIMER_FREEZE_MASK) */\r
+\r
+/***************************************************************************//**\r
+ * @brief\r
+ * Initialize LETIMER.\r
+ *\r
+ * @details\r
+ * Note that the compare/repeat values must be set separately with\r
+ * LETIMER_CompareSet() and LETIMER_RepeatSet(). That should probably be done\r
+ * prior to the use of this function if configuring the LETIMER to start when\r
+ * initialization is completed.\r
+ *\r
+ * @note\r
+ * The initialization of the LETIMER modifies the LETIMER CTRL/CMD registers\r
+ * which require synchronization into the low frequency domain. If any of those\r
+ * registers are modified before a previous update to the same register has\r
+ * completed, this function will stall until the previous synchronization has\r
+ * completed. This only applies to the Gecko Family, see comment in the\r
+ * LETIMER_Sync() internal function call.\r
+ *\r
+ * @param[in] letimer\r
+ * Pointer to LETIMER peripheral register block.\r
+ *\r
+ * @param[in] init\r
+ * Pointer to LETIMER initialization structure.\r
+ ******************************************************************************/\r
+void LETIMER_Init(LETIMER_TypeDef *letimer, const LETIMER_Init_TypeDef *init)\r
+{\r
+ uint32_t tmp = 0;\r
+\r
+ EFM_ASSERT(LETIMER_REF_VALID(letimer));\r
+\r
+ /* Stop timer if specified to be disabled and running */\r
+ if (!(init->enable) && (letimer->STATUS & LETIMER_STATUS_RUNNING))\r
+ {\r
+#if defined(_EFM32_GECKO_FAMILY)\r
+ /* LF register about to be modified require sync. busy check */\r
+ regSync(letimer, LETIMER_SYNCBUSY_CMD);\r
+#endif\r
+ letimer->CMD = LETIMER_CMD_STOP;\r
+ }\r
+\r
+ /* Configure DEBUGRUN flag, sets whether or not counter should be\r
+ * updated when debugger is active */\r
+ if (init->debugRun)\r
+ {\r
+ tmp |= LETIMER_CTRL_DEBUGRUN;\r
+ }\r
+\r
+#if defined(LETIMER_CTRL_RTCC0TEN)\r
+ if (init->rtcComp0Enable)\r
+ {\r
+ tmp |= LETIMER_CTRL_RTCC0TEN;\r
+ }\r
+\r
+ if (init->rtcComp1Enable)\r
+ {\r
+ tmp |= LETIMER_CTRL_RTCC1TEN;\r
+ }\r
+#endif\r
+\r
+ if (init->comp0Top)\r
+ {\r
+ tmp |= LETIMER_CTRL_COMP0TOP;\r
+ }\r
+\r
+ if (init->bufTop)\r
+ {\r
+ tmp |= LETIMER_CTRL_BUFTOP;\r
+ }\r
+\r
+ if (init->out0Pol)\r
+ {\r
+ tmp |= LETIMER_CTRL_OPOL0;\r
+ }\r
+\r
+ if (init->out1Pol)\r
+ {\r
+ tmp |= LETIMER_CTRL_OPOL1;\r
+ }\r
+\r
+ tmp |= init->ufoa0 << _LETIMER_CTRL_UFOA0_SHIFT;\r
+ tmp |= init->ufoa1 << _LETIMER_CTRL_UFOA1_SHIFT;\r
+ tmp |= init->repMode << _LETIMER_CTRL_REPMODE_SHIFT;\r
+\r
+#if defined(_EFM32_GECKO_FAMILY)\r
+ /* LF register about to be modified require sync. busy check */\r
+ regSync(letimer, LETIMER_SYNCBUSY_CTRL);\r
+#endif\r
+ letimer->CTRL = tmp;\r
+\r
+ /* Start timer if specified to be enabled and not already running */\r
+ if (init->enable && !(letimer->STATUS & LETIMER_STATUS_RUNNING))\r
+ {\r
+#if defined(_EFM32_GECKO_FAMILY)\r
+ /* LF register about to be modified require sync. busy check */\r
+ regSync(letimer, LETIMER_SYNCBUSY_CMD);\r
+#endif\r
+ letimer->CMD = LETIMER_CMD_START;\r
+ }\r
+}\r
+\r
+\r
+/***************************************************************************//**\r
+ * @brief\r
+ * Get LETIMER repeat register value.\r
+ *\r
+ * @param[in] letimer\r
+ * Pointer to LETIMER peripheral register block\r
+ *\r
+ * @param[in] rep\r
+ * Repeat register to get, either 0 or 1\r
+ *\r
+ * @return\r
+ * Repeat register value, 0 if invalid register selected.\r
+ ******************************************************************************/\r
+uint32_t LETIMER_RepeatGet(LETIMER_TypeDef *letimer, unsigned int rep)\r
+{\r
+ uint32_t ret;\r
+\r
+ EFM_ASSERT(LETIMER_REF_VALID(letimer) && LETIMER_REP_REG_VALID(rep));\r
+\r
+ /* Initialize selected compare value */\r
+ switch (rep)\r
+ {\r
+ case 0:\r
+ ret = letimer->REP0;\r
+ break;\r
+\r
+ case 1:\r
+ ret = letimer->REP1;\r
+ break;\r
+\r
+ default:\r
+ /* Unknown compare register selected */\r
+ ret = 0;\r
+ break;\r
+ }\r
+\r
+ return(ret);\r
+}\r
+\r
+\r
+/***************************************************************************//**\r
+ * @brief\r
+ * Set LETIMER repeat counter register value.\r
+ *\r
+ * @note\r
+ * The setting of a repeat counter register requires synchronization into the\r
+ * low frequency domain. If the same register is modified before a previous\r
+ * update has completed, this function will stall until the previous\r
+ * synchronization has completed. This only applies to the Gecko Family, see\r
+ * comment in the LETIMER_Sync() internal function call.\r
+ *\r
+ * @param[in] letimer\r
+ * Pointer to LETIMER peripheral register block\r
+ *\r
+ * @param[in] rep\r
+ * Repeat counter register to set, either 0 or 1\r
+ *\r
+ * @param[in] value\r
+ * Initialization value (<= 0x0000ffff)\r
+ ******************************************************************************/\r
+void LETIMER_RepeatSet(LETIMER_TypeDef *letimer,\r
+ unsigned int rep,\r
+ uint32_t value)\r
+{\r
+ volatile uint32_t *repReg;\r
+#if defined(_EFM32_GECKO_FAMILY)\r
+ uint32_t syncbusy;\r
+#endif\r
+ EFM_ASSERT(LETIMER_REF_VALID(letimer)\r
+ && LETIMER_REP_REG_VALID(rep)\r
+ && ((value & ~(_LETIMER_REP0_REP0_MASK\r
+ >> _LETIMER_REP0_REP0_SHIFT))\r
+ == 0));\r
+\r
+ /* Initialize selected compare value */\r
+ switch (rep)\r
+ {\r
+ case 0:\r
+ repReg = &(letimer->REP0);\r
+#if defined(_EFM32_GECKO_FAMILY)\r
+ syncbusy = LETIMER_SYNCBUSY_REP0;\r
+#endif\r
+ break;\r
+\r
+ case 1:\r
+ repReg = &(letimer->REP1);\r
+#if defined(_EFM32_GECKO_FAMILY)\r
+ syncbusy = LETIMER_SYNCBUSY_REP1;\r
+#endif\r
+ break;\r
+\r
+ default:\r
+ /* Unknown compare register selected, abort */\r
+ return;\r
+ }\r
+\r
+#if defined(_EFM32_GECKO_FAMILY)\r
+ /* LF register about to be modified require sync. busy check */\r
+ regSync(letimer, syncbusy);\r
+#endif\r
+\r
+ *repReg = value;\r
+}\r
+\r
+\r
+/***************************************************************************//**\r
+ * @brief\r
+ * Reset LETIMER to same state as after a HW reset.\r
+ *\r
+ * @note\r
+ * The ROUTE register is NOT reset by this function, in order to allow for\r
+ * centralized setup of this feature.\r
+ *\r
+ * @param[in] letimer\r
+ * Pointer to LETIMER peripheral register block.\r
+ ******************************************************************************/\r
+void LETIMER_Reset(LETIMER_TypeDef *letimer)\r
+{\r
+#if defined(_LETIMER_FREEZE_MASK)\r
+ /* Freeze registers to avoid stalling for LF synchronization */\r
+ LETIMER_FreezeEnable(letimer, true);\r
+#endif\r
+\r
+ /* Make sure disabled first, before resetting other registers */\r
+ letimer->CMD = LETIMER_CMD_STOP | LETIMER_CMD_CLEAR\r
+ | LETIMER_CMD_CTO0 | LETIMER_CMD_CTO1;\r
+ letimer->CTRL = _LETIMER_CTRL_RESETVALUE;\r
+ letimer->COMP0 = _LETIMER_COMP0_RESETVALUE;\r
+ letimer->COMP1 = _LETIMER_COMP1_RESETVALUE;\r
+ letimer->REP0 = _LETIMER_REP0_RESETVALUE;\r
+ letimer->REP1 = _LETIMER_REP1_RESETVALUE;\r
+ letimer->IEN = _LETIMER_IEN_RESETVALUE;\r
+ letimer->IFC = _LETIMER_IFC_MASK;\r
+ /* Do not reset route register, setting should be done independently */\r
+\r
+#if defined(_LETIMER_FREEZE_MASK)\r
+ /* Unfreeze registers, pass new settings on to LETIMER */\r
+ LETIMER_FreezeEnable(letimer, false);\r
+#endif\r
+}\r
+\r
+\r
+/** @} (end addtogroup LETIMER) */\r
+/** @} (end addtogroup EM_Library) */\r
+#endif /* defined(LETIMER_COUNT) && (LETIMER_COUNT > 0) */\r