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_dma.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_dma.c	Thu Oct 01 15:25:22 2015 +0300
@@ -0,0 +1,1230 @@
+/***************************************************************************//**
+ * @file em_dma.c
+ * @brief Direct memory access (DMA) module 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_dma.h"
+#if defined( DMA_PRESENT )
+
+#include "em_cmu.h"
+#include "em_assert.h"
+#include "em_bitband.h"
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup DMA
+ * @brief Direct Memory Access (DMA) Peripheral API
+ * @details
+ *  These DMA access functions provide basic support for the following
+ *  types of DMA cycles:
+ *
+ *  @li @b Basic, used for transferring data between memory and peripherals.
+ *  @li @b Auto-request, used for transferring data between memory locations.
+ *  @li @b Ping-pong, used for for continuous transfer of data between memory
+ *    and peripherals, automatically toggling between primary and alternate
+ *    descriptors.
+ *  @li @b Memory @b scatter-gather, used for transferring a number of buffers
+ *    between memory locations.
+ *  @li @b Peripheral @b scatter-gather, used for transferring a number of
+ *    buffers between memory and peripherals.
+ *
+ *  A basic understanding of the DMA controller is assumed. Please refer to
+ *  the EFM32 reference manual for further details.
+ *
+ *  The term 'descriptor' is used as a synonym to the 'channel control data
+ *  structure' term.
+ *
+ *  In order to use the DMA controller, the initialization function must have
+ *  been executed once (normally during system init):
+ * @verbatim
+ * DMA_Init();
+ * @endverbatim
+ *
+ *  Then, normally a user of a DMA channel configures the channel:
+ * @verbatim
+ * DMA_CfgChannel();
+ * @endverbatim
+ *
+ *  The channel configuration only has to be done once, if reusing the channel
+ *  for the same purpose later.
+ *
+ *  In order to set up a DMA cycle, the primary and/or alternate descriptor
+ *  has to be set up as indicated below.
+ *
+ *  For basic or auto-request cycles, use once on either primary or alternate
+ *  descriptor:
+ * @verbatim
+ * DMA_CfgDescr();
+ * @endverbatim
+ *
+ * For ping-pong cycles, configure both primary or alternate descriptors:
+ * @verbatim
+ * DMA_CfgDescr(); // Primary descriptor config
+ * DMA_CfgDescr(); // Alternate descriptor config
+ * @endverbatim
+ *
+ * For scatter-gather cycles, the alternate descriptor array must be programmed:
+ * @verbatim
+ * // 'n' is the number of scattered buffers
+ * // 'descr' points to the start of the alternate descriptor array
+ *
+ * // Fill in 'cfg'
+ * DMA_CfgDescrScatterGather(descr, 0, cfg);
+ * // Fill in 'cfg'
+ * DMA_CfgDescrScatterGather(descr, 1, cfg);
+ * :
+ * // Fill in 'cfg'
+ * DMA_CfgDescrScatterGather(descr, n - 1, cfg);
+ * @endverbatim
+ *
+ * In many cases, the descriptor configuration only has to be done once, if
+ * re-using the channel for the same type of DMA cycles later.
+ *
+ * In order to activate the DMA cycle, use the respective DMA_Activate...()
+ * function.
+ *
+ * For ping-pong DMA cycles, use DMA_RefreshPingPong() from the callback to
+ * prepare the completed descriptor for reuse. Notice that the refresh must
+ * be done prior to the other active descriptor completes, otherwise the
+ * ping-pong DMA cycle will halt.
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ **************************   LOCAL FUNCTIONS   ********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/***************************************************************************//**
+ * @brief
+ *   Prepare descriptor for DMA cycle.
+ *
+ * @details
+ *   This function prepares the last pieces of configuration required to start a
+ *   DMA cycle. Since the DMA controller itself modifies some parts of the
+ *   descriptor during use, those parts need to be refreshed if reusing a
+ *   descriptor configuration.
+ *
+ * @note
+ *   If using this function on a descriptor already activated and in use by the
+ *   DMA controller, the behaviour is undefined.
+ *
+ * @param[in] channel
+ *   DMA channel to prepare for DMA cycle.
+ *
+ * @param[in] cycleCtrl
+ *   DMA cycle type to prepare for.
+ *
+ * @param[in] primary
+ *   @li true - prepare primary descriptor
+ *   @li false - prepare alternate descriptor
+ *
+ * @param[in] useBurst
+ *   The burst feature is only used on peripherals supporting DMA bursts.
+ *   Bursts must not be used if the total length (as given by nMinus1) is
+ *   less than the arbitration rate configured for the descriptor. Please
+ *   refer to the reference manual for further details on burst usage.
+ *
+ * @param[in] dst
+ *   Address to start location to transfer data to. If NULL, leave setting in
+ *   descriptor as is.
+ *
+ * @param[in] src
+ *   Address to start location to transfer data from. If NULL, leave setting in
+ *   descriptor as is.
+ *
+ * @param[in] nMinus1
+ *   Number of elements (minus 1) to transfer (<= 1023).
+ ******************************************************************************/
+static void DMA_Prepare(unsigned int channel,
+                        DMA_CycleCtrl_TypeDef cycleCtrl,
+                        bool primary,
+                        bool useBurst,
+                        void *dst,
+                        void *src,
+                        unsigned int nMinus1)
+{
+  DMA_DESCRIPTOR_TypeDef *descr;
+  DMA_DESCRIPTOR_TypeDef *primDescr;
+  DMA_CB_TypeDef         *cb;
+  uint32_t               inc;
+  uint32_t               chBit;
+  uint32_t               tmp;
+
+  primDescr = ((DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE)) + channel;
+
+  /* Find descriptor to configure */
+  if (primary)
+  {
+    descr = primDescr;
+  }
+  else
+  {
+    descr = ((DMA_DESCRIPTOR_TypeDef *)(DMA->ALTCTRLBASE)) + channel;
+  }
+
+  /* If callback defined, update info on whether callback is issued */
+  /* for primary or alternate descriptor. Mainly needed for ping-pong */
+  /* cycles. */
+  cb = (DMA_CB_TypeDef *)(primDescr->USER);
+  if (cb)
+  {
+    cb->primary = (uint8_t)primary;
+  }
+
+  if (src)
+  {
+    inc = (descr->CTRL & _DMA_CTRL_SRC_INC_MASK) >> _DMA_CTRL_SRC_INC_SHIFT;
+    if (inc == _DMA_CTRL_SRC_INC_NONE)
+    {
+      descr->SRCEND = src;
+    }
+    else
+    {
+      descr->SRCEND = (void *)((uint32_t)src + (nMinus1 << inc));
+    }
+  }
+
+  if (dst)
+  {
+    inc = (descr->CTRL & _DMA_CTRL_DST_INC_MASK) >> _DMA_CTRL_DST_INC_SHIFT;
+    if (inc == _DMA_CTRL_DST_INC_NONE)
+    {
+      descr->DSTEND = dst;
+    }
+    else
+    {
+      descr->DSTEND = (void *)((uint32_t)dst + (nMinus1 << inc));
+    }
+  }
+
+  chBit = 1 << channel;
+  if (useBurst)
+  {
+    DMA->CHUSEBURSTS = chBit;
+  }
+  else
+  {
+    DMA->CHUSEBURSTC = chBit;
+  }
+
+  if (primary)
+  {
+    DMA->CHALTC = chBit;
+  }
+  else
+  {
+    DMA->CHALTS = chBit;
+  }
+
+  /* Set cycle control */
+  tmp         = descr->CTRL & ~(_DMA_CTRL_CYCLE_CTRL_MASK | _DMA_CTRL_N_MINUS_1_MASK);
+  tmp        |= nMinus1 << _DMA_CTRL_N_MINUS_1_SHIFT;
+  tmp        |= (uint32_t)cycleCtrl << _DMA_CTRL_CYCLE_CTRL_SHIFT;
+  descr->CTRL = tmp;
+}
+
+/** @endcond */
+
+/*******************************************************************************
+ ************************   INTERRUPT FUNCTIONS   ******************************
+ ******************************************************************************/
+
+#ifndef EXCLUDE_DEFAULT_DMA_IRQ_HANDLER
+
+/***************************************************************************//**
+ * @brief
+ *   Interrupt handler for DMA cycle completion handling.
+ *
+ * @details
+ *   Clears any pending flags and calls registered callback (if any).
+ *
+ *   If using the default interrupt vector table setup provided, this function
+ *   is automatically placed in the IRQ table due to weak linking. If taking
+ *   control over the interrupt vector table in some other way, this interrupt
+ *   handler must be installed in order to be able to support callback actions.
+ *
+ *   In order for the user to implement a custom IRQ handler or run without
+ *   a DMA IRQ handler, the user can define EXCLUDE_DEFAULT_DMA_IRQ_HANDLER
+ *   with a \#define statement or with the compiler option -D.
+ *
+ ******************************************************************************/
+void DMA_IRQHandler(void)
+{
+  int                    channel;
+  DMA_CB_TypeDef         *cb;
+  uint32_t               pending;
+  uint32_t               pendingPrio;
+  uint32_t               prio;
+  uint32_t               primaryCpy;
+  int                    i;
+
+  /* Get all pending and enabled interrupts */
+  pending  = DMA->IF;
+  pending &= DMA->IEN;
+
+  /* Check for bus error */
+  if (pending & DMA_IF_ERR)
+  {
+    /* Loop here to enable the debugger to see what has happened */
+    while (1)
+      ;
+  }
+
+  /* Process all pending channel interrupts. First process channels */
+  /* defined with high priority, then those with default priority. */
+  prio        = DMA->CHPRIS;
+  pendingPrio = pending & prio;
+  for (i = 0; i < 2; i++)
+  {
+    channel = 0;
+    /* Process pending interrupts within high/default priority group */
+    /* honouring priority within group. */
+    while (pendingPrio)
+    {
+      if (pendingPrio & 1)
+      {
+        DMA_DESCRIPTOR_TypeDef *descr = (DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE);
+        uint32_t chmask = 1 << channel;
+
+        /* Clear pending interrupt prior to invoking callback, in case it */
+        /* sets up another DMA cycle. */
+        DMA->IFC = chmask;
+
+        /* Normally, no point in enabling interrupt without callback, but */
+        /* check if callback is defined anyway. Callback info is always */
+        /* located in primary descriptor. */
+        cb = (DMA_CB_TypeDef *)(descr[channel].USER);
+        if (cb)
+        {
+          /* Toggle next-descriptor indicator always prior to invoking */
+          /* callback (in case callback reconfigurs something) */
+          primaryCpy   = cb->primary;
+          cb->primary ^= 1;
+          if (cb->cbFunc)
+          {
+            cb->cbFunc(channel, (bool)primaryCpy, cb->userPtr);
+          }
+        }
+      }
+
+      pendingPrio >>= 1;
+      channel++;
+    }
+
+    /* On second iteration, process default priority channels */
+    pendingPrio = pending & ~prio;
+  }
+}
+
+#endif /* EXCLUDE_DEFAULT_DMA_IRQ_HANDLER */
+
+
+/*******************************************************************************
+ **************************   GLOBAL FUNCTIONS   *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ *   Activate DMA auto-request cycle (used for memory-memory transfers).
+ *
+ * @details
+ *   Prior to activating the DMA cycle, the channel and descriptor to be used
+ *   must have been properly configured.
+ *
+ * @note
+ *   If using this function on a channel already activated and in use by the
+ *   DMA controller, the behaviour is undefined.
+ *
+ * @param[in] channel
+ *   DMA channel to activate DMA cycle for.
+ *
+ * @param[in] primary
+ *   @li true - activate using primary descriptor
+ *   @li false - activate using alternate descriptor
+ *
+ * @param[in] dst
+ *   Address to start location to transfer data to. If NULL, leave setting in
+ *   descriptor as is from a previous activation.
+ *
+ * @param[in] src
+ *   Address to start location to transfer data from. If NULL, leave setting in
+ *   descriptor as is from a previous activation.
+ *
+ * @param[in] nMinus1
+ *   Number of DMA transfer elements (minus 1) to transfer (<= 1023). The
+ *   size of the DMA transfer element (1, 2 or 4 bytes) is configured with
+ *   DMA_CfgDescr().
+ ******************************************************************************/
+void DMA_ActivateAuto(unsigned int channel,
+                      bool primary,
+                      void *dst,
+                      void *src,
+                      unsigned int nMinus1)
+{
+  uint32_t chBit;
+
+  EFM_ASSERT(channel < DMA_CHAN_COUNT);
+  EFM_ASSERT(nMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT));
+
+  DMA_Prepare(channel,
+              dmaCycleCtrlAuto,
+              primary,
+              false,
+              dst,
+              src,
+              nMinus1);
+
+  chBit        = 1 << channel;
+  DMA->CHENS   = chBit; /* Enable channel */
+  DMA->CHSWREQ = chBit; /* Activate with SW request */
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Activate DMA basic cycle (used for memory-peripheral transfers).
+ *
+ * @details
+ *   Prior to activating the DMA cycle, the channel and descriptor to be used
+ *   must have been properly configured.
+ *
+ * @note
+ *   If using this function on a channel already activated and in use by the
+ *   DMA controller, the behaviour is undefined.
+ *
+ * @param[in] channel
+ *   DMA channel to activate DMA cycle for.
+ *
+ * @param[in] primary
+ *   @li true - activate using primary descriptor
+ *   @li false - activate using alternate descriptor
+ *
+ * @param[in] useBurst
+ *   The burst feature is only used on peripherals supporting DMA bursts.
+ *   Bursts must not be used if the total length (as given by nMinus1) is
+ *   less than the arbitration rate configured for the descriptor. Please
+ *   refer to the reference manual for further details on burst usage.
+ *
+ * @param[in] dst
+ *   Address to start location to transfer data to. If NULL, leave setting in
+ *   descriptor as is from a previous activation.
+ *
+ * @param[in] src
+ *   Address to start location to transfer data from. If NULL, leave setting in
+ *   descriptor as is from a previous activation.
+ *
+ * @param[in] nMinus1
+ *   Number of DMA transfer elements (minus 1) to transfer (<= 1023). The
+ *   size of the DMA transfer element (1, 2 or 4 bytes) is configured with
+ *   DMA_CfgDescr().
+ ******************************************************************************/
+void DMA_ActivateBasic(unsigned int channel,
+                       bool primary,
+                       bool useBurst,
+                       void *dst,
+                       void *src,
+                       unsigned int nMinus1)
+{
+  EFM_ASSERT(channel < DMA_CHAN_COUNT);
+  EFM_ASSERT(nMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT));
+
+  DMA_Prepare(channel,
+              dmaCycleCtrlBasic,
+              primary,
+              useBurst,
+              dst,
+              src,
+              nMinus1);
+
+  /* Enable channel, request signal is provided by peripheral device */
+  DMA->CHENS = 1 << channel;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Activate DMA ping-pong cycle (used for memory-peripheral transfers).
+ *
+ * @details
+ *   Prior to activating the DMA cycle, the channel and both descriptors must
+ *   have been properly configured. The primary descriptor is always the first
+ *   descriptor to be used by the DMA controller.
+ *
+ * @note
+ *   If using this function on a channel already activated and in use by the
+ *   DMA controller, the behaviour is undefined.
+ *
+ * @param[in] channel
+ *   DMA channel to activate DMA cycle for.
+ *
+ * @param[in] useBurst
+ *   The burst feature is only used on peripherals supporting DMA bursts.
+ *   Bursts must not be used if the total length (as given by nMinus1) is
+ *   less than the arbitration rate configured for the descriptors. Please
+ *   refer to the reference manual for further details on burst usage. Notice
+ *   that this setting is used for both the primary and alternate descriptors.
+ *
+ * @param[in] primDst
+ *   Address to start location to transfer data to, for primary descriptor.
+ *   If NULL, leave setting in descriptor as is from a previous activation.
+ *
+ * @param[in] primSrc
+ *   Address to start location to transfer data from, for primary descriptor.
+ *   If NULL, leave setting in descriptor as is from a previous activation.
+ *
+ * @param[in] primNMinus1
+ *   Number of DMA transfer elements (minus 1) to transfer (<= 1023), for
+ *   primary descriptor. The size of the DMA transfer element (1, 2 or 4 bytes)
+ *   is configured with DMA_CfgDescr().
+ *
+ * @param[in] altDst
+ *   Address to start location to transfer data to, for alternate descriptor.
+ *   If NULL, leave setting in descriptor as is from a previous activation.
+ *
+ * @param[in] altSrc
+ *   Address to start location to transfer data from, for alternate descriptor.
+ *   If NULL, leave setting in descriptor as is from a previous activation.
+ *
+ * @param[in] altNMinus1
+ *   Number of DMA transfer elements (minus 1) to transfer (<= 1023), for
+ *   alternate descriptor. The size of the DMA transfer element (1, 2 or 4 bytes)
+ *   is configured with DMA_CfgDescr().
+ ******************************************************************************/
+void DMA_ActivatePingPong(unsigned int channel,
+                          bool useBurst,
+                          void *primDst,
+                          void *primSrc,
+                          unsigned int primNMinus1,
+                          void *altDst,
+                          void *altSrc,
+                          unsigned int altNMinus1)
+{
+  EFM_ASSERT(channel < DMA_CHAN_COUNT);
+  EFM_ASSERT(primNMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT));
+  EFM_ASSERT(altNMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT));
+
+  /* Prepare alternate descriptor first */
+  DMA_Prepare(channel,
+              dmaCycleCtrlPingPong,
+              false,
+              useBurst,
+              altDst,
+              altSrc,
+              altNMinus1);
+
+  /* Prepare primary descriptor last in order to start cycle using it */
+  DMA_Prepare(channel,
+              dmaCycleCtrlPingPong,
+              true,
+              useBurst,
+              primDst,
+              primSrc,
+              primNMinus1);
+
+  /* Enable channel, request signal is provided by peripheral device */
+  DMA->CHENS = 1 << channel;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Activate DMA scatter-gather cycle (used for either memory-peripheral
+ *   or memory-memory transfers).
+ *
+ * @details
+ *   Prior to activating the DMA cycle, the array with alternate descriptors
+ *   must have been properly configured. This function can be reused without
+ *   reconfiguring the alternate descriptors, as long as @p count is the same.
+ *
+ * @note
+ *   If using this function on a channel already activated and in use by the
+ *   DMA controller, the behaviour is undefined.
+ *
+ * @param[in] channel
+ *   DMA channel to activate DMA cycle for.
+ *
+ * @param[in] useBurst
+ *   The burst feature is only used on peripherals supporting DMA bursts
+ *   (and thus this parameter is ignored for memory scatter-gather cycles).
+ *   This parameter determines if bursts should be enabled during DMA transfers
+ *   using the alternate descriptors. Bursts must not be used if the total
+ *   length (as given by nMinus1 for the alternate descriptor) is
+ *   less than the arbitration rate configured for the descriptor. Please
+ *   refer to the reference manual for further details on burst usage.
+ *
+ * @param[in,out] altDescr
+ *   Pointer to start of array with prepared alternate descriptors. The last
+ *   descriptor will have its cycle control type reprogrammed to basic type.
+ *
+ * @param[in] count
+ *   Number of alternate descriptors in @p altDescr array. Maximum number of
+ *   alternate descriptors is 256.
+ ******************************************************************************/
+void DMA_ActivateScatterGather(unsigned int channel,
+                               bool useBurst,
+                               DMA_DESCRIPTOR_TypeDef *altDescr,
+                               unsigned int count)
+{
+  DMA_DESCRIPTOR_TypeDef *descr;
+  DMA_CB_TypeDef         *cb;
+  uint32_t               cycleCtrl;
+  uint32_t               chBit;
+
+  EFM_ASSERT(channel < DMA_CHAN_COUNT);
+  EFM_ASSERT(altDescr);
+  EFM_ASSERT(count && (count <= 256));
+
+  /* We have to configure the primary descriptor properly in order to */
+  /* transfer one complete alternate descriptor from the alternate */
+  /* descriptor table into the actual alternate descriptor. */
+  descr = (DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE) + channel;
+
+  /* Set source end address to point to alternate descriptor array */
+  descr->SRCEND = (uint32_t *)altDescr + (count * 4) - 1;
+
+  /* The destination end address in the primary descriptor MUST point */
+  /* to the corresponding alternate descriptor in scatter-gather mode. */
+  descr->DSTEND = (uint32_t *)((DMA_DESCRIPTOR_TypeDef *)(DMA->ALTCTRLBASE) +
+                               channel + 1) - 1;
+
+  /* The user field of the descriptor is used for callback configuration, */
+  /* and already configured when channel is configured. Do not modify it. */
+
+  /* Determine from alternate configuration whether this is a memory or */
+  /* peripheral scatter-gather, by looking at the first alternate descriptor. */
+  cycleCtrl  = altDescr->CTRL & _DMA_CTRL_CYCLE_CTRL_MASK;
+  cycleCtrl &= ~(1 << _DMA_CTRL_CYCLE_CTRL_SHIFT);
+
+  EFM_ASSERT((cycleCtrl == dmaCycleCtrlMemScatterGather) ||
+             (cycleCtrl == dmaCycleCtrlPerScatterGather));
+
+  /* Set last alternate descriptor to basic or auto-request cycle type in */
+  /* order to have dma_done signal asserted when complete. Otherwise interrupt */
+  /* will not be triggered when done. */
+  altDescr[count - 1].CTRL &= ~_DMA_CTRL_CYCLE_CTRL_MASK;
+  if (cycleCtrl == dmaCycleCtrlMemScatterGather)
+  {
+    altDescr[count - 1].CTRL |= (uint32_t)dmaCycleCtrlAuto << _DMA_CTRL_CYCLE_CTRL_SHIFT;
+  }
+  else
+  {
+    altDescr[count - 1].CTRL |= (uint32_t)dmaCycleCtrlBasic << _DMA_CTRL_CYCLE_CTRL_SHIFT;
+  }
+
+  /* If callback defined, update info on whether callback is issued for */
+  /* primary or alternate descriptor. Not really useful for scatter-gather, */
+  /* but do for consistency. Always set to alternate, since that is the last */
+  /* descriptor actually used. */
+  cb = (DMA_CB_TypeDef *)(descr->USER);
+  if (cb)
+  {
+    cb->primary = false;
+  }
+
+  /* Configure primary descriptor control word */
+  descr->CTRL =
+    ((uint32_t)dmaDataInc4 << _DMA_CTRL_DST_INC_SHIFT) |
+    ((uint32_t)dmaDataSize4 << _DMA_CTRL_DST_SIZE_SHIFT) |
+    ((uint32_t)dmaDataInc4 << _DMA_CTRL_SRC_INC_SHIFT) |
+    ((uint32_t)dmaDataSize4 << _DMA_CTRL_SRC_SIZE_SHIFT) |
+    /* Use same protection scheme as for alternate descriptors */
+    (altDescr->CTRL & _DMA_CTRL_SRC_PROT_CTRL_MASK) |
+    ((uint32_t)dmaArbitrate4 << _DMA_CTRL_R_POWER_SHIFT) |
+    (((count * 4) - 1) << _DMA_CTRL_N_MINUS_1_SHIFT) |
+    (((uint32_t)useBurst & 1) << _DMA_CTRL_NEXT_USEBURST_SHIFT) |
+    cycleCtrl;
+
+  chBit = 1 << channel;
+
+  /* Start with primary descriptor */
+  DMA->CHALTC = chBit;
+
+  /* Enable channel */
+  DMA->CHENS = chBit;
+
+  /* Send request if memory scatter-gather, otherwise request signal is */
+  /* provided by peripheral. */
+  if (cycleCtrl == dmaCycleCtrlMemScatterGather)
+  {
+    DMA->CHSWREQ = chBit;
+  }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Configure a DMA channel.
+ *
+ * @details
+ *   Configure miscellaneous issues for a DMA channel. This function is typically
+ *   used once to setup a channel for a certain type of use.
+ *
+ * @note
+ *   If using this function on a channel already in use by the DMA controller,
+ *   the behaviour is undefined.
+ *
+ * @param[in] channel
+ *   DMA channel to configure.
+ *
+ * @param[in] cfg
+ *   Configuration to use.
+ ******************************************************************************/
+void DMA_CfgChannel(unsigned int channel, DMA_CfgChannel_TypeDef *cfg)
+{
+  DMA_DESCRIPTOR_TypeDef *descr;
+
+  EFM_ASSERT(channel < DMA_CHAN_COUNT);
+  EFM_ASSERT(cfg);
+
+  /* Always keep callback configuration reference in primary descriptor */
+  descr               = (DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE);
+  descr[channel].USER = (uint32_t)(cfg->cb);
+
+  /* Set to specified priority for channel */
+  if (cfg->highPri)
+  {
+    DMA->CHPRIS = 1 << channel;
+  }
+  else
+  {
+    DMA->CHPRIC = 1 << channel;
+  }
+
+  /* Set DMA signal source select */
+  DMA->CH[channel].CTRL = cfg->select;
+
+  /* Enable/disable interrupt as specified */
+  if (cfg->enableInt)
+  {
+    DMA->IFC = (1 << channel);
+    BITBAND_Peripheral(&(DMA->IEN), channel, 1);
+  }
+  else
+  {
+    BITBAND_Peripheral(&(DMA->IEN), channel, 0);
+  }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Configure DMA descriptor for auto-request, basic or ping-pong DMA cycles.
+ *
+ * @details
+ *   This function is used for configuration of a descriptor for the following
+ *   DMA cycle types:
+ *
+ *   @li auto-request - used for memory/memory transfer
+ *   @li basic - used for a peripheral/memory transfer
+ *   @li ping-pong - used for a ping-pong based peripheral/memory transfer
+ *     style providing time to refresh one descriptor while the other is
+ *     in use.
+ *
+ *   The DMA cycle is not activated, please see DMA_ActivateAuto(),
+ *   DMA_ActivateBasic() or DMA_ActivatePingPong() to activate the DMA cycle.
+ *   In many cases, the configuration only has to be done once, and all
+ *   subsequent cycles may be activated with the activate function.
+ *
+ *   For ping-pong DMA cycles, this function must be used both on the primary
+ *   and the alternate descriptor prior to activating the DMA cycle.
+ *
+ *   Notice that the DMA channel must also be configured, see DMA_CfgChannel().
+ *
+ * @note
+ *   If using this function on a descriptor already activated and in use by
+ *   the DMA controller, the behaviour is undefined.
+ *
+ * @param[in] channel
+ *   DMA channel to configure for.
+ *
+ * @param[in] primary
+ *   @li true - configure primary descriptor
+ *   @li false - configure alternate descriptor
+ *
+ * @param[in] cfg
+ *   Configuration to use.
+ ******************************************************************************/
+void DMA_CfgDescr(unsigned int channel,
+                  bool primary,
+                  DMA_CfgDescr_TypeDef *cfg)
+{
+  DMA_DESCRIPTOR_TypeDef *descr;
+
+  EFM_ASSERT(channel < DMA_CHAN_COUNT);
+  EFM_ASSERT(cfg);
+
+  /* Find descriptor to configure */
+  if (primary)
+  {
+    descr = (DMA_DESCRIPTOR_TypeDef *)DMA->CTRLBASE;
+  }
+  else
+  {
+    descr = (DMA_DESCRIPTOR_TypeDef *)DMA->ALTCTRLBASE;
+  }
+  descr += channel;
+
+  /* Prepare the descriptor */
+  /* Source/destination end addresses set when started */
+  descr->CTRL =
+    (cfg->dstInc << _DMA_CTRL_DST_INC_SHIFT) |
+    (cfg->size << _DMA_CTRL_DST_SIZE_SHIFT) |
+    (cfg->srcInc << _DMA_CTRL_SRC_INC_SHIFT) |
+    (cfg->size << _DMA_CTRL_SRC_SIZE_SHIFT) |
+    ((uint32_t)(cfg->hprot) << _DMA_CTRL_SRC_PROT_CTRL_SHIFT) |
+    (cfg->arbRate << _DMA_CTRL_R_POWER_SHIFT) |
+    (0 << _DMA_CTRL_N_MINUS_1_SHIFT) |         /* Set when activated */
+    (0 << _DMA_CTRL_NEXT_USEBURST_SHIFT) |     /* Set when activated */
+    DMA_CTRL_CYCLE_CTRL_INVALID;               /* Set when activated */
+}
+
+
+#if defined( _DMA_LOOP0_MASK ) && defined( _DMA_LOOP1_MASK )
+/***************************************************************************//**
+ * @brief Configure DMA channel for Loop mode or 2D transfer.
+ *
+ * @details
+ *   For 2D transfer, set cfg->enable to "false", and only configure nMinus1
+ *   to same width as channel descriptor.
+ *
+ * @param[in] channel
+ *   DMA channel to configure for.
+ *
+ * @param[in] cfg
+ *   Configuration to use.
+ ******************************************************************************/
+void DMA_CfgLoop(unsigned int channel, DMA_CfgLoop_TypeDef *cfg)
+{
+  EFM_ASSERT(channel <= 1);
+  EFM_ASSERT(cfg->nMinus1 <= 1023);
+
+  /* Configure LOOP setting */
+  switch( channel )
+  {
+  case 0:
+    DMA->LOOP0 = (cfg->enable << _DMA_LOOP0_EN_SHIFT|
+                  cfg->nMinus1 << _DMA_LOOP0_WIDTH_SHIFT);
+    break;
+  case 1:
+    DMA->LOOP1 = (cfg->enable << _DMA_LOOP1_EN_SHIFT|
+                  cfg->nMinus1 << _DMA_LOOP1_WIDTH_SHIFT);
+    break;
+  }
+}
+#endif
+
+
+#if defined( _DMA_RECT0_MASK )
+/***************************************************************************//**
+ * @brief Configure DMA channel 2D transfer properties.
+ *
+ * @param[in] channel
+ *   DMA channel to configure for.
+ *
+ * @param[in] cfg
+ *   Configuration to use.
+ ******************************************************************************/
+void DMA_CfgRect(unsigned int channel, DMA_CfgRect_TypeDef *cfg)
+{
+  (void)channel;                            /* Unused parameter */
+
+  EFM_ASSERT(channel == 0);
+  EFM_ASSERT(cfg->dstStride <= 2047);
+  EFM_ASSERT(cfg->srcStride <= 2047);
+  EFM_ASSERT(cfg->height <= 1023);
+
+  /* Configure rectangular/2D copy */
+  DMA->RECT0 = (cfg->dstStride << _DMA_RECT0_DSTSTRIDE_SHIFT|
+                cfg->srcStride << _DMA_RECT0_SRCSTRIDE_SHIFT|
+                cfg->height << _DMA_RECT0_HEIGHT_SHIFT);
+}
+#endif
+
+
+/***************************************************************************//**
+ * @brief
+ *   Configure an alternate DMA descriptor for use with scatter-gather DMA
+ *   cycles.
+ *
+ * @details
+ *   In scatter-gather mode, the alternate descriptors are located in one
+ *   contiguous memory area. Each of the alternate descriptor must be fully
+ *   configured prior to starting the scatter-gather DMA cycle.
+ *
+ *   The DMA cycle is not activated by this function, please see
+ *   DMA_ActivateScatterGather() to activate the DMA cycle. In some cases, the
+ *   alternate configuration only has to be done once, and all subsequent
+ *   transfers may be activated with the activate function.
+ *
+ *   Notice that the DMA channel must also be configured, see DMA_CfgChannel().
+ *
+ * @param[in] descr
+ *   Points to start of memory area holding the alternate descriptors.
+ *
+ * @param[in] indx
+ *   Alternate descriptor index number to configure (numbered from 0).
+ *
+ * @param[in] cfg
+ *   Configuration to use.
+ ******************************************************************************/
+void DMA_CfgDescrScatterGather(DMA_DESCRIPTOR_TypeDef *descr,
+                               unsigned int indx,
+                               DMA_CfgDescrSGAlt_TypeDef *cfg)
+{
+  uint32_t cycleCtrl;
+
+  EFM_ASSERT(descr);
+  EFM_ASSERT(cfg);
+
+  /* Point to selected entry in alternate descriptor table */
+  descr += indx;
+
+  if (cfg->srcInc == dmaDataIncNone)
+  {
+    descr->SRCEND = cfg->src;
+  }
+  else
+  {
+    descr->SRCEND = (void *)((uint32_t)(cfg->src) + ((uint32_t)(cfg->nMinus1) << cfg->srcInc));
+  }
+
+  if (cfg->dstInc == dmaDataIncNone)
+  {
+    descr->DSTEND = cfg->dst;
+  }
+  else
+  {
+    descr->DSTEND = (void *)((uint32_t)(cfg->dst) + ((uint32_t)(cfg->nMinus1) << cfg->dstInc));
+  }
+
+  /* User definable part not used */
+  descr->USER = 0;
+
+  if (cfg->peripheral)
+  {
+    cycleCtrl = (uint32_t)dmaCycleCtrlPerScatterGather + 1;
+  }
+  else
+  {
+    cycleCtrl = (uint32_t)dmaCycleCtrlMemScatterGather + 1;
+  }
+
+  descr->CTRL =
+    (cfg->dstInc << _DMA_CTRL_DST_INC_SHIFT) |
+    (cfg->size << _DMA_CTRL_DST_SIZE_SHIFT) |
+    (cfg->srcInc << _DMA_CTRL_SRC_INC_SHIFT) |
+    (cfg->size << _DMA_CTRL_SRC_SIZE_SHIFT) |
+    ((uint32_t)(cfg->hprot) << _DMA_CTRL_SRC_PROT_CTRL_SHIFT) |
+    (cfg->arbRate << _DMA_CTRL_R_POWER_SHIFT) |
+    ((uint32_t)(cfg->nMinus1) << _DMA_CTRL_N_MINUS_1_SHIFT) |
+    /* Never set next useburst bit, since the descriptor used after the */
+    /* alternate descriptor is the primary descriptor which operates on */
+    /* memory. If the alternate descriptors need to have useBurst set, this */
+    /* done when setting up the primary descriptor, ie when activating. */
+    (0 << _DMA_CTRL_NEXT_USEBURST_SHIFT) |
+    (cycleCtrl << _DMA_CTRL_CYCLE_CTRL_SHIFT);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Enable or disable a DMA channel.
+ *
+ * @details
+ *   Use this function to explicitly enable or disable a DMA channel. A DMA
+ *   channel is automatically disabled when the DMA controller has finished a
+ *   transaction.
+ *
+ * @param[in] channel
+ *   DMA channel to enable or disable.
+ *
+ * @param[in] enable
+ *   If 'true' the channel will be enabled. If 'false' the channel will be
+ *   disabled.
+ ******************************************************************************/
+void DMA_ChannelEnable(unsigned int channel, bool enable)
+{
+  EFM_ASSERT(channel < DMA_CHAN_COUNT);
+
+  if (enable)
+  {
+    DMA->CHENS = 1<<channel;
+  }
+  else
+  {
+    DMA->CHENC = 1<<channel;
+  }
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Check if DMA channel is enabled.
+ *
+ * @details
+ *   The DMA channel is disabled when the DMA controller has finished a DMA
+ *   cycle.
+ *
+ * @param[in] channel
+ *   DMA channel to check.
+ *
+ * @return
+ *   true if channel is enabled, false if not.
+ ******************************************************************************/
+bool DMA_ChannelEnabled(unsigned int channel)
+{
+  EFM_ASSERT(channel < DMA_CHAN_COUNT);
+
+  return (bool)((DMA->CHENS >> channel) & 1);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Initializes DMA controller.
+ *
+ * @details
+ *   This function will reset and prepare the DMA controller for use. Although
+ *   it may be used several times, it is normally only used during system
+ *   init. If reused during normal operation, notice that any ongoing DMA
+ *   transfers will be aborted. When completed, the DMA controller is in
+ *   an enabled state.
+ *
+ * @note
+ *   Must be invoked before using the DMA controller.
+ *
+ * @param[in] init
+ *   Pointer to a structure containing DMA init information.
+ ******************************************************************************/
+void DMA_Init(DMA_Init_TypeDef *init)
+{
+  EFM_ASSERT(init);
+
+  /* Make sure control block is properly aligned */
+#if (DMA_CHAN_COUNT <= 4)
+  EFM_ASSERT(!((uint32_t)(init->controlBlock) & (128 - 1)));
+#elif (DMA_CHAN_COUNT <= 8) || (DMA_CHAN_COUNT <= 12)
+  EFM_ASSERT(!((uint32_t)(init->controlBlock) & (256 - 1)));
+#else
+#error "Unsupported DMA channel count (em_dma.c)."
+#endif
+
+  /* Make sure DMA clock is enabled prior to accessing DMA module */
+  CMU_ClockEnable(cmuClock_DMA, true);
+
+  /* Make sure DMA controller is set to a known reset state */
+  DMA_Reset();
+
+  /* Clear/enable DMA interrupts */
+  NVIC_ClearPendingIRQ(DMA_IRQn);
+  NVIC_EnableIRQ(DMA_IRQn);
+
+  /* Enable bus error interrupt */
+  DMA->IEN = DMA_IEN_ERR;
+
+  /* Set pointer to control block, notice that this ptr must have been */
+  /* properly aligned, according to requirements defined in the reference */
+  /* manual. */
+  DMA->CTRLBASE = (uint32_t)(init->controlBlock);
+
+  /* Configure and enable the DMA controller */
+  DMA->CONFIG = ((uint32_t)(init->hprot) << _DMA_CONFIG_CHPROT_SHIFT) | DMA_CONFIG_EN;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Refresh a descriptor used in a DMA ping-pong cycle.
+ *
+ * @details
+ *   During a ping-pong DMA cycle, the DMA controller automatically alternates
+ *   between primary and alternate descriptors, when completing use of a
+ *   descriptor. While the other descriptor is in use by the DMA controller,
+ *   the SW should refresh the completed descriptor. This is typically done from
+ *   the callback defined for the ping-pong cycle.
+ *
+ * @param[in] channel
+ *   DMA channel to refresh ping-pong descriptor for.
+ *
+ * @param[in] primary
+ *   @li true - refresh primary descriptor
+ *   @li false - refresh alternate descriptor
+ *
+ * @param[in] useBurst
+ *   The burst feature is only used on peripherals supporting DMA bursts.
+ *   Bursts must not be used if the total length (as given by nMinus1) is
+ *   less than the arbitration rate configured for the descriptor. Please
+ *   refer to the reference manual for further details on burst usage.
+ *
+ * @param[in] dst
+ *   Address to start location to transfer data to. If NULL, leave setting in
+ *   descriptor as is.
+ *
+ * @param[in] src
+ *   Address to start location to transfer data from. If NULL, leave setting in
+ *   descriptor as is.
+ *
+ * @param[in] nMinus1
+ *   Number of DMA transfer elements (minus 1) to transfer (<= 1023). The
+ *   size of the DMA transfer element (1, 2 or 4 bytes) is configured with
+ *   DMA_CfgDescr().
+ *
+ * @param[in] stop
+ *   Indicate that the DMA ping-pong cycle shall stop @b after completing use
+ *   of this descriptor.
+ ******************************************************************************/
+void DMA_RefreshPingPong(unsigned int channel,
+                         bool primary,
+                         bool useBurst,
+                         void *dst,
+                         void *src,
+                         unsigned int nMinus1,
+                         bool stop)
+{
+  DMA_CycleCtrl_TypeDef  cycleCtrl;
+  DMA_DESCRIPTOR_TypeDef *descr;
+  uint32_t               inc;
+  uint32_t               chBit;
+  uint32_t               tmp;
+
+  EFM_ASSERT(channel < DMA_CHAN_COUNT);
+  EFM_ASSERT(nMinus1 <= (_DMA_CTRL_N_MINUS_1_MASK >> _DMA_CTRL_N_MINUS_1_SHIFT));
+
+  /* The ping-pong DMA cycle may be stopped by issuing a basic cycle type */
+  if (stop)
+  {
+    cycleCtrl = dmaCycleCtrlBasic;
+  }
+  else
+  {
+    cycleCtrl = dmaCycleCtrlPingPong;
+  }
+
+  /* Find descriptor to configure */
+  if (primary)
+  {
+    descr = ((DMA_DESCRIPTOR_TypeDef *)(DMA->CTRLBASE)) + channel;
+  }
+  else
+  {
+    descr = ((DMA_DESCRIPTOR_TypeDef *)(DMA->ALTCTRLBASE)) + channel;
+  }
+
+  if (src)
+  {
+    inc = (descr->CTRL & _DMA_CTRL_SRC_INC_MASK) >> _DMA_CTRL_SRC_INC_SHIFT;
+    if (inc == _DMA_CTRL_SRC_INC_NONE)
+    {
+      descr->SRCEND = src;
+    }
+    else
+    {
+      descr->SRCEND = (void *)((uint32_t)src + (nMinus1 << inc));
+    }
+  }
+
+  if (dst)
+  {
+    inc = (descr->CTRL & _DMA_CTRL_DST_INC_MASK) >> _DMA_CTRL_DST_INC_SHIFT;
+    if (inc == _DMA_CTRL_DST_INC_NONE)
+    {
+      descr->DSTEND = dst;
+    }
+    else
+    {
+      descr->DSTEND = (void *)((uint32_t)dst + (nMinus1 << inc));
+    }
+  }
+
+  chBit = 1 << channel;
+  if (useBurst)
+  {
+    DMA->CHUSEBURSTS = chBit;
+  }
+  else
+  {
+    DMA->CHUSEBURSTC = chBit;
+  }
+
+  /* Set cycle control */
+  tmp         = descr->CTRL & ~(_DMA_CTRL_CYCLE_CTRL_MASK | _DMA_CTRL_N_MINUS_1_MASK);
+  tmp        |= nMinus1 << _DMA_CTRL_N_MINUS_1_SHIFT;
+  tmp        |= cycleCtrl << _DMA_CTRL_CYCLE_CTRL_SHIFT;
+  descr->CTRL = tmp;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Reset the DMA controller.
+ *
+ * @details
+ *   This functions will disable the DMA controller and set it to a reset
+ *   state.
+ *
+ * @note
+ *   Notice that any ongoing transfers will be aborted.
+ ******************************************************************************/
+void DMA_Reset(void)
+{
+  int i;
+
+  /* Disable DMA interrupts */
+  NVIC_DisableIRQ(DMA_IRQn);
+
+  /* Put the DMA controller into a known state, first disabling it. */
+  DMA->CONFIG      = _DMA_CONFIG_RESETVALUE;
+  DMA->CHUSEBURSTC = _DMA_CHUSEBURSTC_MASK;
+  DMA->CHREQMASKC  = _DMA_CHREQMASKC_MASK;
+  DMA->CHENC       = _DMA_CHENC_MASK;
+  DMA->CHALTC      = _DMA_CHALTC_MASK;
+  DMA->CHPRIC      = _DMA_CHPRIC_MASK;
+  DMA->ERRORC      = DMA_ERRORC_ERRORC;
+  DMA->IEN         = _DMA_IEN_RESETVALUE;
+  DMA->IFC         = _DMA_IFC_MASK;
+
+  /* Clear channel control flags */
+  for (i = 0; i < DMA_CHAN_COUNT; i++)
+  {
+    DMA->CH[i].CTRL = _DMA_CH_CTRL_RESETVALUE;
+  }
+}
+
+
+/** @} (end addtogroup DMA) */
+/** @} (end addtogroup EM_Library) */
+#endif /* defined( DMA_PRESENT ) */