added prescaler for 16 bit pwm in LPC1347 target

Fork of mbed-dev by mbed official

Revision:
144:ef7eb2e8f9f7
Parent:
50:a417edff4437
--- a/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_dma.c	Tue Aug 02 14:07:36 2016 +0000
+++ b/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_dma.c	Fri Sep 02 15:07:44 2016 +0100
@@ -1,1231 +1,1231 @@
-/***************************************************************************//**
- * @file em_dma.c
- * @brief Direct memory access (DMA) module peripheral API
- * @version 4.2.1
- *******************************************************************************
- * @section License
- * <b>(C) Copyright 2015 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_bus.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 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);
-    BUS_RegBitWrite(&(DMA->IEN), channel, 1);
-  }
-  else
-  {
-    BUS_RegBitWrite(&(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 ) */
+/***************************************************************************//**
+ * @file em_dma.c
+ * @brief Direct memory access (DMA) module peripheral API
+ * @version 4.2.1
+ *******************************************************************************
+ * @section License
+ * <b>(C) Copyright 2015 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_bus.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 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);
+    BUS_RegBitWrite(&(DMA->IEN), channel, 1);
+  }
+  else
+  {
+    BUS_RegBitWrite(&(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 ) */