added prescaler for 16 bit pwm in LPC1347 target

Fork of mbed-dev by mbed official

Revision:
0:9b334a45a8ff
Child:
50:a417edff4437
diff -r 000000000000 -r 9b334a45a8ff targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_pcnt.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_pcnt.c	Thu Oct 01 15:25:22 2015 +0300
@@ -0,0 +1,763 @@
+/***************************************************************************//**
+ * @file em_pcnt.c
+ * @brief Pulse Counter (PCNT) peripheral API
+ * @version 3.20.12
+ *******************************************************************************
+ * @section License
+ * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b>
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
+ * obligation to support this Software. Silicon Labs is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Silicon Labs will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+
+
+#include "em_pcnt.h"
+#if defined(PCNT_COUNT) && (PCNT_COUNT > 0)
+
+#include "em_cmu.h"
+#include "em_assert.h"
+#include "em_bitband.h"
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup PCNT
+ * @brief Pulse Counter (PCNT) Peripheral API
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ *******************************   DEFINES   ***********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+
+/** Validation of PCNT register block pointer reference for assert statements. */
+#if (PCNT_COUNT == 1)
+#define PCNT_REF_VALID(ref)    ((ref) == PCNT0)
+#elif (PCNT_COUNT == 2)
+#define PCNT_REF_VALID(ref)    (((ref) == PCNT0) || ((ref) == PCNT1))
+#elif (PCNT_COUNT == 3)
+#define PCNT_REF_VALID(ref)    (((ref) == PCNT0) || ((ref) == PCNT1) || \
+                                ((ref) == PCNT2))
+#else
+#error Undefined number of pulse counters (PCNT).
+#endif
+
+/** @endcond */
+
+
+/*******************************************************************************
+ **************************   LOCAL FUNCTIONS   ********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/***************************************************************************//**
+ * @brief
+ *   Map PCNT structure into instance number.
+ *
+ * @param[in] pcnt
+ *   Pointer to PCNT peripheral register block
+ *
+ * @return
+ *   Instance number.
+ ******************************************************************************/
+__STATIC_INLINE unsigned int PCNT_Map(PCNT_TypeDef *pcnt)
+{
+  return(((uint32_t)pcnt - PCNT0_BASE) / 0x400);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Wait for ongoing sync of register(s) to low frequency domain to complete.
+ *
+ * @param[in] pcnt
+ *   Pointer to PCNT peripheral register block
+ *
+ * @param[in] mask
+ *   Bitmask corresponding to SYNCBUSY register defined bits, indicating
+ *   registers that must complete any ongoing synchronization.
+ ******************************************************************************/
+__STATIC_INLINE void PCNT_Sync(PCNT_TypeDef *pcnt, uint32_t mask)
+{
+  /* Avoid deadlock if modifying the same register twice when freeze mode is
+   * activated. */
+  if (pcnt->FREEZE & PCNT_FREEZE_REGFREEZE)
+  {
+    return;
+  }
+
+  /* Wait for any pending previous write operation to have been completed in low
+   * frequency domain. */
+  while (pcnt->SYNCBUSY & mask)
+    ;
+}
+
+/** @endcond */
+
+/*******************************************************************************
+ **************************   GLOBAL FUNCTIONS   *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ *   Reset PCNT counters and TOP register.
+ *
+ * @note
+ *   Notice that special SYNCBUSY handling is not applicable for the RSTEN
+ *   bit of the control register, so we don't need to wait for it when only
+ *   modifying RSTEN. (It would mean undefined wait time if clocked by external
+ *   clock.) The SYNCBUSY bit will however be set, leading to a synchronization
+ *   in the LF domain, with in reality no changes.
+ *
+ * @param[in] pcnt
+ *   Pointer to PCNT peripheral register block.
+ ******************************************************************************/
+void PCNT_CounterReset(PCNT_TypeDef *pcnt)
+{
+  EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+  /* Enable reset of CNT and TOP register */
+  BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
+
+  /* Disable reset of CNT and TOP register */
+  BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Set counter and top values.
+ *
+ * @details
+ *   The pulse counter is disabled while changing these values, and reenabled
+ *   (if originally enabled) when values have been set.
+ *
+ * @note
+ *   This function will stall until synchronization to low frequency domain is
+ *   completed. For that reason, it should normally not be used when using
+ *   an external clock to clock the PCNT module, since stall time may be
+ *   undefined in that case. The counter should normally only be set when
+ *   operating in (or about to enable) #pcntModeOvsSingle mode.
+ *
+ * @param[in] pcnt
+ *   Pointer to PCNT peripheral register block.
+ *
+ * @param[in] count
+ *   Value to set in counter register.
+ *
+ * @param[in] top
+ *   Value to set in top register.
+ ******************************************************************************/
+void PCNT_CounterTopSet(PCNT_TypeDef *pcnt, uint32_t count, uint32_t top)
+{
+  uint32_t ctrl;
+
+  EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+#ifdef PCNT0
+  if (PCNT0 == pcnt)
+  {
+    EFM_ASSERT((1<<PCNT0_CNT_SIZE) > count);
+    EFM_ASSERT((1<<PCNT0_CNT_SIZE) > top);
+  }
+#endif
+
+#ifdef PCNT1
+  if (PCNT1 == pcnt)
+  {
+    EFM_ASSERT((1<<PCNT1_CNT_SIZE) > count);
+    EFM_ASSERT((1<<PCNT1_CNT_SIZE) > top);
+  }
+#endif
+
+#ifdef PCNT2
+  if (PCNT2 == pcnt)
+  {
+    EFM_ASSERT((1<<PCNT2_CNT_SIZE) > count);
+    EFM_ASSERT((1<<PCNT2_CNT_SIZE) > top);
+  }
+#endif
+
+  /* Keep current control setting, must be restored */
+  ctrl = pcnt->CTRL;
+
+  /* If enabled, disable pulse counter before changing values */
+  if ((ctrl & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
+  {
+    PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
+    pcnt->CTRL = (ctrl & ~_PCNT_CTRL_MODE_MASK) | PCNT_CTRL_MODE_DISABLE;
+  }
+
+  /* Load into TOPB */
+  PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
+  pcnt->TOPB = count;
+
+  /* Load TOPB value into TOP */
+  PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
+
+  /* This bit has no effect on rev. C and onwards parts - for compatibility */
+  pcnt->CMD = PCNT_CMD_LTOPBIM;
+  PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
+
+  /* Load TOP into CNT */
+  pcnt->CMD = PCNT_CMD_LCNTIM;
+
+  /* Restore TOP? ('count' setting has been loaded into pcnt->TOP, better
+   * to use 'top' than pcnt->TOP in compare, since latter may in theory not
+   * be visible yet.) */
+  if (top != count)
+  {
+    /* Wait for command to sync LCNTIM before setting TOPB */
+    PCNT_Sync(pcnt, PCNT_SYNCBUSY_CMD);
+
+    /* Load into TOPB, we don't need to check for TOPB sync complete here,
+     * it has been ensured above. */
+    pcnt->TOPB = top;
+
+    /* Load TOPB value into TOP */
+    PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
+    pcnt->CMD = PCNT_CMD_LTOPBIM;
+  }
+
+  /* Reenable if it was enabled */
+  if ((ctrl & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
+  {
+    PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL | PCNT_SYNCBUSY_CMD);
+    pcnt->CTRL = ctrl;
+  }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Set PCNT operational mode.
+ *
+ * @details
+ *   Notice that this function does not do any configuration. Setting operational
+ *   mode is normally only required after initialization is done, and if not
+ *   done as part of initialization. Or if requiring to disable/reenable pulse
+ *   counter.
+ *
+ * @note
+ *   This function may stall until synchronization to low frequency domain is
+ *   completed. For that reason, it should normally not be used when using
+ *   an external clock to clock the PCNT module, since stall time may be
+ *   undefined in that case.
+ *
+ * @param[in] pcnt
+ *   Pointer to PCNT peripheral register block.
+ *
+ * @param[in] mode
+ *   Operational mode to use for PCNT.
+ ******************************************************************************/
+void PCNT_Enable(PCNT_TypeDef *pcnt, PCNT_Mode_TypeDef mode)
+{
+  uint32_t tmp;
+
+  EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+  /* Set as specified */
+  tmp  = pcnt->CTRL & ~_PCNT_CTRL_MODE_MASK;
+  tmp |= (uint32_t)mode << _PCNT_CTRL_MODE_SHIFT;
+
+  /* LF register about to be modified require sync. busy check */
+  PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
+  pcnt->CTRL = tmp;
+}
+
+#if defined( _PCNT_INPUT_MASK )
+/***************************************************************************//**
+ * @brief
+ *   Enable/disable the selected PRS input of PCNT.
+ *
+ * @details
+ *   Notice that this function does not do any configuration.
+ *
+ * @param[in] pcnt
+ *   Pointer to PCNT peripheral register block.
+ *
+ * @param[in] prsInput
+ *   PRS input (S0 or S1) of the selected PCNT module.
+ *
+ * @param[in] enable
+ *   Set to true to enable, false to disable the selected PRS input.
+ ******************************************************************************/
+void PCNT_PRSInputEnable(PCNT_TypeDef *pcnt,
+                         PCNT_PRSInput_TypeDef prsInput,
+                         bool enable)
+{
+  EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+  /* Enable/disable the selected PRS input on the selected PCNT module. */
+  switch (prsInput)
+  {
+  /* Enable/disable PRS input S0. */
+  case pcntPRSInputS0:
+  {
+    BITBAND_Peripheral(&(pcnt->INPUT), _PCNT_INPUT_S0PRSEN_SHIFT, (uint32_t)enable);
+  }
+  break;
+
+  /* Enable/disable PRS input S1. */
+  case pcntPRSInputS1:
+  {
+    BITBAND_Peripheral(&(pcnt->INPUT), _PCNT_INPUT_S1PRSEN_SHIFT, (uint32_t)enable);
+  }
+  break;
+
+  /* Invalid parameter, asserted. */
+  default:
+  {
+    EFM_ASSERT(0);
+  }
+  break;
+  }
+}
+#endif
+
+
+/***************************************************************************//**
+ * @brief
+ *   PCNT register synchronization freeze control.
+ *
+ * @details
+ *   Some PCNT registers require synchronization into the low frequency (LF)
+ *   domain. The freeze feature allows for several such registers to be
+ *   modified before passing them to the LF domain simultaneously (which
+ *   takes place when the freeze mode is disabled).
+ *
+ * @note
+ *   When enabling freeze mode, this function will wait for all current
+ *   ongoing PCNT synchronization to LF domain to complete (Normally
+ *   synchronization will not be in progress.) However for this reason, when
+ *   using freeze mode, modifications of registers requiring LF synchronization
+ *   should be done within one freeze enable/disable block to avoid unecessary
+ *   stalling.
+ *
+ * @param[in] pcnt
+ *   Pointer to PCNT peripheral register block.
+ *
+ * @param[in] enable
+ *   @li true - enable freeze, modified registers are not propagated to the
+ *       LF domain
+ *   @li false - disables freeze, modified registers are propagated to LF
+ *       domain
+ ******************************************************************************/
+void PCNT_FreezeEnable(PCNT_TypeDef *pcnt, bool enable)
+{
+  EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+  if (enable)
+  {
+    /* Wait for any ongoing LF synchronization to complete. This is just to
+     * protect against the rare case when a user:
+     * - modifies a register requiring LF sync
+     * - then enables freeze before LF sync completed
+     * - then modifies the same register again
+     * since modifying a register while it is in sync progress should be
+     * avoided. */
+    while (pcnt->SYNCBUSY)
+      ;
+
+    pcnt->FREEZE = PCNT_FREEZE_REGFREEZE;
+  }
+  else
+  {
+    pcnt->FREEZE = 0;
+  }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Init pulse counter.
+ *
+ * @details
+ *   This function will configure the pulse counter. The clock selection is
+ *   configured as follows, depending on operational mode:
+ *
+ *   @li #pcntModeOvsSingle - Use LFACLK.
+ *   @li #pcntModeExtSingle - Use external PCNTn_S0 pin.
+ *   @li #pcntModeExtQuad - Use external PCNTn_S0 pin.
+ *
+ *   Notice that the LFACLK must be enabled in all modes, since some basic setup
+ *   is done with this clock even if external pin clock usage mode is chosen.
+ *   The pulse counter clock for the selected instance must also be enabled
+ *   prior to init.
+ *
+ *   Notice that pins used by the PCNT module must be properly configured
+ *   by the user explicitly through setting the ROUTE register, in order for
+ *   the PCNT to work as intended.
+ *
+ *   Writing to CNT will not occur in external clock modes (EXTCLKQUAD and
+ *   EXTCLKSINGLE) because the external clock rate is unknown. The user should
+ *   handle it manually depending on the application
+ *
+ *   TOPB is written for all modes but in external clock mode it will take
+ *   3 external clock cycles to sync to TOP
+ *
+ *
+ * @note
+ *   Initializing requires synchronization into the low frequency domain. This
+ *   may cause some delay.
+ *
+ * @param[in] pcnt
+ *   Pointer to PCNT peripheral register block.
+ *
+ * @param[in] init
+ *   Pointer to initialization structure used to initialize.
+ ******************************************************************************/
+void PCNT_Init(PCNT_TypeDef *pcnt, const PCNT_Init_TypeDef *init)
+{
+  unsigned int inst;
+  uint32_t     tmp;
+
+  EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+#ifdef PCNT0
+  if (PCNT0 == pcnt)
+  {
+    EFM_ASSERT((1<<PCNT0_CNT_SIZE) > init->counter);
+    EFM_ASSERT((1<<PCNT0_CNT_SIZE) > init->top);
+  }
+#endif
+  
+#ifdef PCNT1
+  if (PCNT1 == pcnt)
+  {
+    EFM_ASSERT((1<<PCNT1_CNT_SIZE) > init->counter);
+    EFM_ASSERT((1<<PCNT1_CNT_SIZE) > init->top);
+  }
+#endif
+  
+#ifdef PCNT2
+  if (PCNT2 == pcnt)
+  {
+    EFM_ASSERT((1<<PCNT2_CNT_SIZE) > init->counter);
+    EFM_ASSERT((1<<PCNT2_CNT_SIZE) > init->top);
+  }
+#endif
+  
+  /* Map pointer to instance */
+  inst = PCNT_Map(pcnt);
+
+#if defined( _PCNT_INPUT_MASK )
+  /* Selecting the PRS channels for the PRS input sources of the PCNT. These are
+   * written with a Read-Modify-Write sequence in order to keep the value of the
+   * input enable bits which can be modified using PCNT_PRSInputEnable(). */
+  tmp = pcnt->INPUT & ~(_PCNT_INPUT_S0PRSSEL_MASK | _PCNT_INPUT_S1PRSSEL_MASK);
+  tmp |= ((uint32_t)init->s0PRS << _PCNT_INPUT_S0PRSSEL_SHIFT) |
+         ((uint32_t)init->s1PRS << _PCNT_INPUT_S1PRSSEL_SHIFT);
+  pcnt->INPUT = tmp;
+#endif
+
+  /* Build CTRL setting, except for mode */
+  tmp = 0;
+  if (init->negEdge)
+  {
+    tmp |= PCNT_CTRL_EDGE_NEG;
+  }
+
+  if (init->countDown)
+  {
+    tmp |= PCNT_CTRL_CNTDIR_DOWN;
+  }
+
+  if (init->filter)
+  {
+    tmp |= PCNT_CTRL_FILT;
+  }
+
+#if defined( PCNT_CTRL_HYST )
+  if (init->hyst)
+  {
+    tmp |= PCNT_CTRL_HYST;
+  }
+#endif
+
+#if defined( PCNT_CTRL_S1CDIR )
+  if (init->s1CntDir)
+  {
+    tmp |= PCNT_CTRL_S1CDIR;
+  }
+#endif
+
+  /* Configure counter events for regular and auxiliary counter. */
+#if defined( _PCNT_CTRL_CNTEV_SHIFT )
+  tmp |= init->cntEvent << _PCNT_CTRL_CNTEV_SHIFT;
+#endif
+
+#if defined( _PCNT_CTRL_AUXCNTEV_SHIFT )
+  {
+    /* Modify the auxCntEvent value before writing to the AUXCNTEV field in
+       the CTRL register because the AUXCNTEV field values are different from
+       the CNTEV field values, and cntEvent and auxCntEvent are of the same type
+       PCNT_CntEvent_TypeDef.
+    */
+    uint32_t auxCntEventField = 0; /* Get rid of compiler warning. */
+    switch (init->auxCntEvent)
+    {
+    case pcntCntEventBoth:
+      auxCntEventField = pcntCntEventNone;
+      break;
+    case pcntCntEventNone:
+      auxCntEventField = pcntCntEventBoth;
+      break;
+    case pcntCntEventUp:
+    case pcntCntEventDown:
+      auxCntEventField = init->auxCntEvent;
+      break;
+    default:
+      /* Invalid parameter, asserted. */
+      EFM_ASSERT(0);
+    }
+    tmp |= auxCntEventField << _PCNT_CTRL_AUXCNTEV_SHIFT;
+  }
+#endif
+
+  /* Reset pulse counter while changing clock source. The reset bit */
+  /* is asynchronous, we don't have to check for SYNCBUSY. */
+  BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
+
+  /* Select LFACLK to clock in control setting */
+  CMU_PCNTClockExternalSet(inst, false);
+
+  /* Handling depends on whether using external clock or not. */
+  switch (init->mode)
+  {
+  case pcntModeExtSingle:
+  case pcntModeExtQuad:
+    tmp |= init->mode << _PCNT_CTRL_MODE_SHIFT;
+
+    /* In most cases, the SYNCBUSY bit is set due to reset bit set, and waiting
+     * for asynchronous reset bit is strictly not necessary.
+     * But in theory, other operations on CTRL register may have been done
+     * outside this function, so wait. */
+    PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
+
+    /* Enable PCNT Clock Domain Reset. The PCNT must be in reset before changing
+     * the clock source to an external clock */
+    pcnt->CTRL = PCNT_CTRL_RSTEN;
+
+    /* Wait until CTRL write synchronized into LF domain. */
+    PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
+
+    /* Change to external clock BEFORE disabling reset */
+    CMU_PCNTClockExternalSet(inst, true);
+
+    /* Write to TOPB. If using external clock TOPB will sync to TOP at the same
+     * time as the mode. This will insure that if the user chooses to count
+     * down, the first "countable" pulse will make CNT go to TOP and not 0xFF
+     * (default TOP value). */
+    pcnt->TOPB = init->top;
+
+    /* This bit has no effect on rev. C and onwards parts - for compatibility */
+    pcnt->CMD = PCNT_CMD_LTOPBIM;
+
+    /* Write the CTRL register with the configurations.
+     * This should be written after TOPB in the eventuality of a pulse between
+     * these two writes that would cause the CTRL register to be synced one
+     * clock cycle earlier than the TOPB. */
+    pcnt->CTRL = tmp;
+
+    /* There are no syncs for TOP, CMD or CTRL because the clock rate is unknown
+     * and the program could stall
+     * These will be synced within 3 clock cycles of the external clock  /
+     * For the same reason CNT cannot be written here. */
+    break;
+
+  /* pcntModeDisable */
+  /* pcntModeOvsSingle */
+  default:
+    /* No need to set disabled mode if already disabled. */
+    if ((pcnt->CTRL & _PCNT_CTRL_MODE_MASK) != PCNT_CTRL_MODE_DISABLE)
+    {
+      /* Set control to disabled mode, leave reset on until ensured disabled.
+       * We don't need to wait for CTRL SYNCBUSY completion here, it was
+       * triggered by reset bit above, which is asynchronous. */
+      pcnt->CTRL = tmp | PCNT_CTRL_MODE_DISABLE | PCNT_CTRL_RSTEN;
+
+      /* Wait until CTRL write synchronized into LF domain before proceeding
+       * to disable reset. */
+      PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
+    }
+
+    /* Disable reset bit, counter should now be in disabled mode. */
+    BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
+
+    /* Set counter and top values as specified. */
+    PCNT_CounterTopSet(pcnt, init->counter, init->top);
+
+    /* Enter oversampling mode if selected. */
+    if (init->mode == pcntModeOvsSingle)
+    {
+      PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
+      pcnt->CTRL = tmp | (init->mode << _PCNT_CTRL_MODE_SHIFT);
+    }
+    break;
+  }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Reset PCNT to same state as after a HW reset.
+ *
+ * @details
+ *   Notice the LFACLK must be enabled, since some basic reset is done with
+ *   this clock. The pulse counter clock for the selected instance must also
+ *   be enabled prior to init.
+ *
+ * @note
+ *   The ROUTE register is NOT reset by this function, in order to allow for
+ *   centralized setup of this feature.
+ *
+ * @param[in] pcnt
+ *   Pointer to PCNT peripheral register block.
+ ******************************************************************************/
+void PCNT_Reset(PCNT_TypeDef *pcnt)
+{
+  unsigned int inst;
+
+  EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+  /* Map pointer to instance and clock info */
+  inst = PCNT_Map(pcnt);
+
+  pcnt->IEN = _PCNT_IEN_RESETVALUE;
+
+  /* Notice that special SYNCBUSY handling is not applicable for the RSTEN
+   * bit of the control register, so we don't need to wait for it when only
+   * modifying RSTEN. The SYNCBUSY bit will be set, leading to a
+   * synchronization in the LF domain, with in reality no changes to LF domain.
+   * Enable reset of CNT and TOP register. */
+  BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 1);
+
+  /* Select LFACLK as default */
+  CMU_PCNTClockExternalSet(inst, false);
+
+  PCNT_TopBufferSet(pcnt, _PCNT_TOPB_RESETVALUE);
+
+  /* Reset CTRL leaving RSTEN set */
+  pcnt->CTRL = _PCNT_CTRL_RESETVALUE | PCNT_CTRL_RSTEN;
+
+  /* Disable reset after CTRL reg has been synchronized */
+  PCNT_Sync(pcnt, PCNT_SYNCBUSY_CTRL);
+  BITBAND_Peripheral(&(pcnt->CTRL), _PCNT_CTRL_RSTEN_SHIFT, 0);
+
+  /* Clear pending interrupts */
+  pcnt->IFC = _PCNT_IFC_MASK;
+
+  /* Do not reset route register, setting should be done independently */
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Set top buffer value.
+ *
+ * @note
+ *   This function may stall until synchronization to low frequency domain is
+ *   completed. For that reason, it should normally not be used when using
+ *   an external clock to clock the PCNT module, since stall time may be
+ *   undefined in that case.
+ *
+ * @param[in] pcnt
+ *   Pointer to PCNT peripheral register block.
+ *
+ * @param[in] val
+ *   Value to set in top buffer register.
+ ******************************************************************************/
+void PCNT_TopBufferSet(PCNT_TypeDef *pcnt, uint32_t val)
+{
+  EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+  /* LF register about to be modified require sync. busy check */
+  PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
+  pcnt->TOPB = val;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Set top value.
+ *
+ * @note
+ *   This function will stall until synchronization to low frequency domain is
+ *   completed. For that reason, it should normally not be used when using
+ *   an external clock to clock the PCNT module, since stall time may be
+ *   undefined in that case.
+ *
+ * @param[in] pcnt
+ *   Pointer to PCNT peripheral register block.
+ *
+ * @param[in] val
+ *   Value to set in top register.
+ ******************************************************************************/
+void PCNT_TopSet(PCNT_TypeDef *pcnt, uint32_t val)
+{
+  EFM_ASSERT(PCNT_REF_VALID(pcnt));
+
+#ifdef PCNT0
+  if (PCNT0 == pcnt)
+  {
+    EFM_ASSERT((1<<PCNT0_CNT_SIZE) > val);
+  }
+#endif
+
+#ifdef PCNT1
+  if (PCNT1 == pcnt)
+  {
+    EFM_ASSERT((1<<PCNT1_CNT_SIZE) > val);
+  }
+#endif
+
+#ifdef PCNT2
+  if (PCNT2 == pcnt)
+  {
+    EFM_ASSERT((1<<PCNT2_CNT_SIZE) > val);
+  }
+#endif
+
+  /* LF register about to be modified require sync. busy check */
+
+  /* Load into TOPB */
+  PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB);
+  pcnt->TOPB = val;
+
+  /* Load TOPB value into TOP */
+  PCNT_Sync(pcnt, PCNT_SYNCBUSY_TOPB | PCNT_SYNCBUSY_CMD);
+  pcnt->CMD = PCNT_CMD_LTOPBIM;
+}
+
+
+/** @} (end addtogroup PCNT) */
+/** @} (end addtogroup EM_Library) */
+#endif /* defined(PCNT_COUNT) && (PCNT_COUNT > 0) */