added prescaler for 16 bit pwm in LPC1347 target
Fork of mbed-dev by
Diff: targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_dma.c
- 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 ) */