832 lines
24 KiB
C
832 lines
24 KiB
C
/****************************************************************************
|
|
* arch/arm/src/efm32/efm32_dma.c
|
|
*
|
|
* Copyright (C) 2014 Gregory Nutt. All rights reserved.
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* 3. Neither the name NuttX nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <semaphore.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
|
|
#include <nuttx/arch.h>
|
|
|
|
#include "up_arch.h"
|
|
#include "chip/efm32_cmu.h"
|
|
#include "chip/efm32_dma.h"
|
|
#include "efm32_dma.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define ALIGN_MASK(s) ((1 << s) - 1)
|
|
#define ALIGN_DOWN(v,m) ((v) & ~m)
|
|
#define ALIGN_UP(v,m) (((v) + (m)) & ~m)
|
|
|
|
/****************************************************************************
|
|
* Public Types
|
|
****************************************************************************/
|
|
/* This structure describes one DMA channel */
|
|
|
|
struct dma_channel_s
|
|
{
|
|
uint8_t chan; /* DMA channel number (0-EFM32_DMA_NCHANNELS) */
|
|
bool inuse; /* TRUE: The DMA channel is in use */
|
|
dma_config_t config; /* Current configuration */
|
|
dma_callback_t callback; /* Callback invoked when the DMA completes */
|
|
void *arg; /* Argument passed to callback function */
|
|
};
|
|
|
|
/* This structure describes the state of the DMA controller */
|
|
|
|
struct dma_controller_s
|
|
{
|
|
sem_t exclsem; /* Protects channel table */
|
|
sem_t chansem; /* Count of free channels */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
/* This is the overall state of the DMA controller */
|
|
|
|
static struct dma_controller_s g_dmac;
|
|
|
|
/* This is the array of all DMA channels */
|
|
|
|
static struct dma_channel_s g_dmach[EFM32_DMA_NCHANNELS];
|
|
|
|
/* This array describes the available channel control data structures. Each
|
|
* structure must be aligned to a 256 address boundary. The last 8 or 9
|
|
* bits of address are provided by the DMA controller:
|
|
*
|
|
* 8-Channels:
|
|
* Bit 7: Selects the alternate descriptor list
|
|
* Bits 4-6: The DMA channel (0-7)
|
|
* Bits 2-3: Selects the descriptor field
|
|
* Bits 0-1: Always zero
|
|
*
|
|
* 12-Channels:
|
|
* Bit 8: Selects the alternate descriptor list
|
|
* Bits 4-7: The DMA channel (0-11)
|
|
* Bits 2-3: Selects the descriptor field
|
|
* Bits 0-1: Always zero
|
|
*/
|
|
|
|
#if EFM32_DMA_NCHANNELS <= 8
|
|
# define DESC_TABLE_SIZE 8
|
|
# define DESC_TABLE_ALIGN 256 /* 2*8*16 */
|
|
#elif EFM32_DMA_NCHANNELS <= 16
|
|
# define DESC_TABLE_SIZE 16
|
|
# define DESC_TABLE_ALIGN 512 /* 2*16*16 */
|
|
#else
|
|
# error Unknown descriptor table size
|
|
#endif
|
|
|
|
#ifdef CONFIG_EFM32_DMA_ALTDSEC
|
|
static struct dma_descriptor_s g_descriptors[DESC_TABLE_SIZE + EFM32_DMA_NCHANNELS]
|
|
__attribute__((aligned(DESC_TABLE_ALIGN)));
|
|
#else
|
|
static struct dma_descriptor_s g_descriptors[EFM32_DMA_NCHANNELS]
|
|
__attribute__((aligned(DESC_TABLE_ALIGN)));
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: efm32_set_chctrl
|
|
*
|
|
* Description:
|
|
* Set the channel control register
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void efm32_set_chctrl(struct dma_channel_s *dmach, dma_config_t config)
|
|
{
|
|
uintptr_t regaddr;
|
|
uint32_t decoded;
|
|
uint32_t regval;
|
|
|
|
decoded = (uint32_t)(config & EFM32_DMA_SIGSEL_MASK) >> EFM32_DMA_SIGSEL_SHIFT;
|
|
regval = (decoded << _DMA_CH_CTRL_SIGSEL_SHIFT);
|
|
decoded = (uint32_t)(config & EFM32_DMA_SOURCSEL_MASK) >> EFM32_DMA_SOURCSEL_SHIFT;
|
|
regval |= (decoded << _DMA_CH_CTRL_SOURCESEL_SHIFT);
|
|
|
|
regaddr = EFM32_DMA_CHn_CTRL(dmach->chan);
|
|
putreg32(regval, regaddr);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: efm32_align_shift
|
|
*
|
|
* Description:
|
|
* Set the channel control register
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline unsigned int efm32_align_shift(dma_config_t config)
|
|
{
|
|
unsigned int shift;
|
|
|
|
shift = (config & EFM32_DMA_XFERSIZE_MASK) >> EFM32_DMA_XFERSIZE_SHIFT;
|
|
DEBUGASSERT(shift != 3);
|
|
return shift;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: efm32_get_descriptor
|
|
*
|
|
* Description:
|
|
* Get the address of the primary or alternate descriptor assigned to the
|
|
* DMA channel
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline struct dma_descriptor_s *
|
|
efm32_get_descriptor(struct dma_channel_s *dmach, bool alt)
|
|
{
|
|
uintptr_t base = alt ? getreg32(EFM32_DMA_ALTCTRLBASE) :
|
|
getreg32(EFM32_DMA_CTRLBASE);
|
|
return ((struct dma_descriptor_s *)base) + dmach->chan;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: efm32_dmac_interrupt
|
|
*
|
|
* Description:
|
|
* DMA interrupt handler
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int efm32_dmac_interrupt(int irq, void *context)
|
|
{
|
|
struct dma_channel_s *dmach;
|
|
unsigned int chndx;
|
|
uint32_t pending;
|
|
uint32_t bit;
|
|
|
|
/* Get the set of pending, unmasked global XDMAC interrupts */
|
|
|
|
pending = getreg32(EFM32_DMA_IF) & getreg32(EFM32_DMA_IEN);
|
|
putreg32(pending, EFM32_DMA_IFC);
|
|
|
|
/* Check each bit to see which channel(s) have interrupted */
|
|
|
|
for (chndx = 0; chndx < EFM32_DMA_NCHANNELS && pending != 0; chndx++)
|
|
{
|
|
/* Are any interrupts pending for this channel? */
|
|
|
|
bit = 1 << chndx;
|
|
if ((pending & bit) != 0)
|
|
{
|
|
dmach = &g_dmach[chndx];
|
|
|
|
/* Put the DMA channel in the stopped state */
|
|
|
|
efm32_dmastop((DMA_HANDLE)dmach);
|
|
|
|
/* Call the DMA completion callback */
|
|
|
|
if (dmach->callback)
|
|
{
|
|
dmach->callback((DMA_HANDLE)dmach, OK, dmach->arg);
|
|
dmach->callback = NULL;
|
|
}
|
|
|
|
dmach->arg = NULL;
|
|
|
|
/* Clear the bit in the sampled set of pending interrupts */
|
|
|
|
pending &= !bit;
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: up_dmainitialize
|
|
*
|
|
* Description:
|
|
* Initialize the DMA subsystem
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
void weak_function up_dmainitialize(void)
|
|
{
|
|
uint32_t regval;
|
|
int i;
|
|
|
|
dmallvdbg("Initialize XDMAC0\n");
|
|
|
|
/* Initialize the channel list */
|
|
|
|
sem_init(&g_dmac.exclsem, 0, 1);
|
|
sem_init(&g_dmac.chansem, 0, EFM32_DMA_NCHANNELS);
|
|
|
|
for (i = 0; i < EFM32_DMA_NCHANNELS; i++)
|
|
{
|
|
g_dmach[i].chan = i;
|
|
}
|
|
|
|
/* Enable clocking to the DMA module. DMA is clocked by HFCORECLK. */
|
|
|
|
regval = getreg32(EFM32_CMU_HFCORECLKEN0);
|
|
regval |= CMU_HFCORECLKEN0_DMA;
|
|
putreg32(regval, EFM32_CMU_HFCORECLKEN0);
|
|
|
|
/* Set the control base addresses. Note: EFM32_DMA_ALTCTRLBASE
|
|
* is a read-only register that provides the location where hardware
|
|
* will obtain the alternative descriptors.
|
|
*/
|
|
|
|
putreg32((uint32_t)g_descriptors, EFM32_DMA_CTRLBASE);
|
|
|
|
/* Attach DMA interrupt vector */
|
|
|
|
(void)irq_attach(EFM32_IRQ_DMA, efm32_dmac_interrupt);
|
|
|
|
/* Enable the DMA controller */
|
|
|
|
putreg32(DMA_CONFIG_EN, EFM32_DMA_CONFIG);
|
|
|
|
/* Enable the IRQ at the AIC (still disabled at the DMA controller) */
|
|
|
|
up_enable_irq(EFM32_IRQ_DMA);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: efm32_dmachannel
|
|
*
|
|
* Description:
|
|
* Allocate a DMA channel. This function gives the caller mutually
|
|
* exclusive access to a DMA channel.
|
|
*
|
|
* If no DMA channel is available, then efm32_dmachannel() will wait
|
|
* until the holder of a channel relinquishes the channel by calling
|
|
* efm32_dmafree().
|
|
*
|
|
* Input parameters:
|
|
* None
|
|
*
|
|
* Returned Value:
|
|
* This function ALWAYS returns a non-NULL, void* DMA channel handle.
|
|
*
|
|
* Assumptions:
|
|
* - The caller can wait for a DMA channel to be freed if it is not
|
|
* available.
|
|
*
|
|
****************************************************************************/
|
|
|
|
DMA_HANDLE efm32_dmachannel(void)
|
|
{
|
|
struct dma_channel_s *dmach;
|
|
unsigned int chndx;
|
|
uint32_t bit;
|
|
|
|
/* Take a count from from the channel counting semaphore. We may block
|
|
* if there are no free channels. When we get the count, then we can
|
|
* be assured that a channel is available in the channel list and is
|
|
* reserved for us.
|
|
*/
|
|
|
|
while (sem_wait(&g_dmac.chansem) < 0)
|
|
{
|
|
/* sem_wait should fail only if it is awakened by a a signal */
|
|
|
|
DEBUGASSERT(errno == EINTR);
|
|
}
|
|
|
|
/* Get exclusive access to the DMA channel list */
|
|
|
|
while (sem_wait(&g_dmac.exclsem) < 0)
|
|
{
|
|
/* sem_wait should fail only if it is awakened by a a signal */
|
|
|
|
DEBUGASSERT(errno == EINTR);
|
|
}
|
|
|
|
/* Search for an available DMA channel */
|
|
|
|
for (chndx = 0, dmach = NULL; chndx < EFM32_DMA_NCHANNELS; chndx++)
|
|
{
|
|
struct dma_channel_s *candidate = &g_dmach[chndx];
|
|
if (!candidate->inuse)
|
|
{
|
|
dmach = candidate;
|
|
dmach->inuse = true;
|
|
|
|
/* Clear any pending channel interrupts */
|
|
|
|
bit = 1 << chndx;
|
|
putreg32(bit, EFM32_DMA_IFC);
|
|
|
|
/* Disable the channel */
|
|
|
|
putreg32(bit, EFM32_DMA_CHENC);
|
|
break;
|
|
}
|
|
}
|
|
|
|
sem_post(&g_dmac.exclsem);
|
|
|
|
/* Since we have reserved a DMA descriptor by taking a count from chansem,
|
|
* it would be a serious logic failure if we could not find a free channel
|
|
* for our use.
|
|
*/
|
|
|
|
DEBUGASSERT(dmach);
|
|
return (DMA_HANDLE)dmach;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: efm32_dmafree
|
|
*
|
|
* Description:
|
|
* Release a DMA channel. If another thread is waiting for this DMA channel
|
|
* in a call to efm32_dmachannel, then this function will re-assign the
|
|
* DMA channel to that thread and wake it up. NOTE: The 'handle' used
|
|
* in this argument must NEVER be used again until efm32_dmachannel() is
|
|
* called again to re-gain access to the channel.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
* - The caller holds the DMA channel.
|
|
* - There is no DMA in progress
|
|
*
|
|
****************************************************************************/
|
|
|
|
void efm32_dmafree(DMA_HANDLE handle)
|
|
{
|
|
struct dma_channel_s *dmach = (struct dma_channel_s *)handle;
|
|
|
|
DEBUGASSERT(dmach != NULL && dmach->inuse);
|
|
dmavdbg("DMA channel %d\n", dmach->chan);
|
|
|
|
/* Disable the channel */
|
|
|
|
putreg32(1 << dmach->chan, EFM32_DMA_CHENC);
|
|
|
|
/* Mark the channel no longer in use. Clearing the in-use flag is an atomic
|
|
* operation and so should be safe.
|
|
*/
|
|
|
|
dmach->inuse = false;
|
|
|
|
/* And increment the count of free channels... possibly waking up a
|
|
* thread that may be waiting for a channel.
|
|
*/
|
|
|
|
sem_post(&g_dmac.chansem);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: efm32_rxdmasetup
|
|
*
|
|
* Description:
|
|
* Configure an RX (peripheral-to-memory) DMA before starting the transfer.
|
|
*
|
|
* Input Parameters:
|
|
* paddr - Peripheral address (source)
|
|
* maddr - Memory address (destination)
|
|
* nbytes - Number of bytes to transfer. Must be an even multiple of the
|
|
* configured transfer size.
|
|
* config - Channel configuration selections
|
|
*
|
|
****************************************************************************/
|
|
|
|
void efm32_rxdmasetup(DMA_HANDLE handle, uintptr_t paddr, uintptr_t maddr,
|
|
size_t nbytes, dma_config_t config)
|
|
{
|
|
struct dma_channel_s *dmach = (struct dma_channel_s *)handle;
|
|
struct dma_descriptor_s *desc;
|
|
unsigned int xfersize;
|
|
unsigned int shift;
|
|
uint32_t regval;
|
|
uint32_t incr;
|
|
uint32_t mask;
|
|
|
|
DEBUGASSERT(dmach != NULL && dmach->inuse);
|
|
|
|
/* Get the properly alignment shift and mask */
|
|
|
|
shift = efm32_align_shift(config);
|
|
mask = ALIGN_MASK(shift);
|
|
|
|
/* Make sure that the number of bytes we are asked to transfer is a multiple
|
|
* of the transfer size.
|
|
*/
|
|
|
|
xfersize = (1 << shift);
|
|
nbytes = ALIGN_DOWN(nbytes, mask);
|
|
DEBUGASSERT(nbytes > 0);
|
|
|
|
/* Save the configuration (for efm32_dmastart()). */
|
|
|
|
dmach->config = config;
|
|
|
|
/* Configure for the selected peripheral */
|
|
|
|
efm32_set_chctrl(dmach, config);
|
|
|
|
/* Configure the primary channel descriptor */
|
|
|
|
desc = efm32_get_descriptor(dmach, false);
|
|
desc->srcend = (uint32_t *)paddr;
|
|
desc->dstend = (uint32_t *)(maddr + nbytes - xfersize);
|
|
|
|
/* No source increment, destination increments according to transfer size.
|
|
* No privileges. Arbitrate after each transfer. Default priority.
|
|
*/
|
|
|
|
regval = DMA_CTRL_SRC_INC_NONE | DMA_CTRL_DST_PROT_NON_PRIVILEGED |
|
|
DMA_CTRL_SRC_PROT_NON_PRIVILEGED | DMA_CTRL_R_POWER_1 |
|
|
(0 << _DMA_CTRL_NEXT_USEBURST_SHIFT) | _DMA_CTRL_CYCLE_CTRL_BASIC;
|
|
|
|
switch (shift)
|
|
{
|
|
default:
|
|
case 0: /* Byte transfer */
|
|
regval |= DMA_CTRL_DST_SIZE_BYTE | DMA_CTRL_SRC_SIZE_BYTE;
|
|
incr = DMA_CTRL_DST_INC_BYTE;
|
|
break;
|
|
|
|
case 1: /* Half word transfer */
|
|
regval |= DMA_CTRL_DST_SIZE_HALFWORD | DMA_CTRL_SRC_SIZE_HALFWORD;
|
|
incr = DMA_CTRL_DST_INC_HALFWORD;
|
|
break;
|
|
|
|
case 2: /* Word transfer */
|
|
regval |= DMA_CTRL_DST_SIZE_WORD | DMA_CTRL_SRC_SIZE_WORD;
|
|
incr = DMA_CTRL_DST_INC_WORD;
|
|
break;
|
|
}
|
|
|
|
/* Do we need to increment the memory address? */
|
|
|
|
if ((config & EFM32_DMA_MEMINCR_MASK) == EFM32_DMA_MEMINCR)
|
|
{
|
|
regval |= incr;
|
|
}
|
|
|
|
/* Set the number of transfers (minus 1) */
|
|
|
|
DEBUGASSERT((nbytes >> shift) < 1024);
|
|
regval |= ((nbytes >> shift) - 1) << _DMA_CTRL_N_MINUS_1_SHIFT;
|
|
desc->ctrl = regval;
|
|
desc->user = 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: efm32_txdmasetup
|
|
*
|
|
* Description:
|
|
* Configure an TX (memory-to-memory) DMA before starting the transfer.
|
|
*
|
|
* Input Parameters:
|
|
* paddr - Peripheral address (destination)
|
|
* maddr - Memory address (source)
|
|
* nbytes - Number of bytes to transfer. Must be an even multiple of the
|
|
* configured transfer size.
|
|
* config - Channel configuration selections
|
|
*
|
|
****************************************************************************/
|
|
|
|
void efm32_txdmasetup(DMA_HANDLE handle, uintptr_t paddr, uintptr_t maddr,
|
|
size_t nbytes, dma_config_t config)
|
|
{
|
|
struct dma_channel_s *dmach = (struct dma_channel_s *)handle;
|
|
struct dma_descriptor_s *desc;
|
|
unsigned int xfersize;
|
|
unsigned int shift;
|
|
uint32_t regval;
|
|
uint32_t incr;
|
|
uint32_t mask;
|
|
|
|
DEBUGASSERT(dmach != NULL && dmach->inuse);
|
|
|
|
/* Get the properly alignment shift and mask */
|
|
|
|
shift = efm32_align_shift(config);
|
|
mask = ALIGN_MASK(shift);
|
|
|
|
/* Make sure that the number of bytes we are asked to transfer is a multiple
|
|
* of the transfer size.
|
|
*/
|
|
|
|
xfersize = (1 << shift);
|
|
nbytes = ALIGN_DOWN(nbytes, mask);
|
|
DEBUGASSERT(nbytes > 0);
|
|
|
|
/* Save the configuration (for efm32_dmastart()). */
|
|
|
|
dmach->config = config;
|
|
|
|
/* Configure for the selected peripheral */
|
|
|
|
efm32_set_chctrl(dmach, config);
|
|
|
|
/* Configure the primary channel descriptor */
|
|
|
|
desc = efm32_get_descriptor(dmach, false);
|
|
desc->srcend = (uint32_t *)(maddr + nbytes - xfersize);
|
|
desc->dstend = (uint32_t *)paddr;
|
|
|
|
/* No destination increment, source increments according to transfer size.
|
|
* No privileges. Arbitrate after each transfer. Default priority.
|
|
*/
|
|
|
|
regval = DMA_CTRL_DST_INC_NONE | DMA_CTRL_DST_PROT_NON_PRIVILEGED |
|
|
DMA_CTRL_SRC_PROT_NON_PRIVILEGED | DMA_CTRL_R_POWER_1 |
|
|
(0 << _DMA_CTRL_NEXT_USEBURST_SHIFT) | _DMA_CTRL_CYCLE_CTRL_BASIC;
|
|
|
|
switch (shift)
|
|
{
|
|
default:
|
|
case 0: /* Byte transfer */
|
|
regval |= DMA_CTRL_DST_SIZE_BYTE | DMA_CTRL_SRC_SIZE_BYTE;
|
|
incr = DMA_CTRL_SRC_INC_BYTE;
|
|
break;
|
|
|
|
case 1: /* Half word transfer */
|
|
regval |= DMA_CTRL_DST_SIZE_HALFWORD | DMA_CTRL_SRC_SIZE_HALFWORD;
|
|
incr = DMA_CTRL_SRC_INC_HALFWORD;
|
|
break;
|
|
|
|
case 2: /* Word transfer */
|
|
regval |= DMA_CTRL_DST_SIZE_WORD | DMA_CTRL_SRC_SIZE_WORD;
|
|
incr = DMA_CTRL_SRC_INC_WORD;
|
|
break;
|
|
}
|
|
|
|
/* Do we need to increment the memory address? */
|
|
|
|
if ((config & EFM32_DMA_MEMINCR_MASK) == EFM32_DMA_MEMINCR)
|
|
{
|
|
regval |= incr;
|
|
}
|
|
|
|
/* Set the number of transfers (minus 1) */
|
|
|
|
DEBUGASSERT((nbytes >> shift) < 1024);
|
|
regval |= ((nbytes >> shift) - 1) << _DMA_CTRL_N_MINUS_1_SHIFT;
|
|
desc->ctrl = regval;
|
|
desc->user = 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: efm32_dmastart
|
|
*
|
|
* Description:
|
|
* Start the DMA transfer
|
|
*
|
|
* Assumptions:
|
|
* - DMA handle allocated by efm32_dmachannel()
|
|
* - No DMA in progress
|
|
*
|
|
****************************************************************************/
|
|
|
|
void efm32_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg)
|
|
{
|
|
struct dma_channel_s *dmach = (struct dma_channel_s *)handle;
|
|
irqstate_t flags;
|
|
uint32_t regval;
|
|
uint32_t bit;
|
|
|
|
DEBUGASSERT(dmach && dmach->inuse && dmach->desc);
|
|
|
|
/* Save the DMA complete callback info */
|
|
|
|
dmach->callback = callback;
|
|
dmach->arg = arg;
|
|
|
|
/* Finish configuring the channel */
|
|
|
|
bit = 1 << dmach->chan;
|
|
if ((dmach->config & EFM32_DMA_SINGLE_MASK) == EFM32_DMA_BUFFER_FULL)
|
|
{
|
|
/* Disable the single requests for the channel (i.e. do not react to data
|
|
* available, wait for buffer full)
|
|
*/
|
|
|
|
putreg32(bit, EFM32_DMA_CHUSEBURSTS);
|
|
|
|
/* Enable buffer-full requests for the channel */
|
|
|
|
putreg32(bit, EFM32_DMA_CHREQMASKC);
|
|
}
|
|
else
|
|
{
|
|
/* Enable the single requests for the channel */
|
|
|
|
putreg32(bit, EFM32_DMA_CHUSEBURSTC);
|
|
|
|
/* Disable buffer-full requests for the channel */
|
|
|
|
putreg32(bit, EFM32_DMA_CHREQMASKS);
|
|
}
|
|
|
|
/* Use the primary data structure for the channel */
|
|
|
|
putreg32(bit, EFM32_DMA_CHALTC);
|
|
|
|
/* Enable DMA completion interrupts */
|
|
|
|
flags = irqsave();
|
|
regval = getreg32(EFM32_DMA_IEN);
|
|
regval |= bit;
|
|
putreg32(regval, EFM32_DMA_IEN);
|
|
|
|
/* Enable the channel */
|
|
|
|
putreg32(bit, EFM32_DMA_CHENS);
|
|
irqrestore(flags);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: efm32_dmastop
|
|
*
|
|
* Description:
|
|
* Cancel the DMA. After efm32_dmastop() is called, the DMA channel is
|
|
* reset and efm32_dmasetup() must be called before efm32_dmastart() can be
|
|
* called again
|
|
*
|
|
* Assumptions:
|
|
* - DMA handle allocated by efm32_dmachannel()
|
|
*
|
|
****************************************************************************/
|
|
|
|
void efm32_dmastop(DMA_HANDLE handle)
|
|
{
|
|
struct dma_channel_s *dmach = (struct dma_channel_s *)handle;
|
|
irqstate_t flags;
|
|
uint32_t regval;
|
|
uint32_t bit;
|
|
|
|
DEBUGASSERT(dmach);
|
|
bit = 1 << dmach->chan;
|
|
|
|
/* Disable the channel */
|
|
|
|
flags = irqsave();
|
|
putreg32(bit, EFM32_DMA_CHENC);
|
|
|
|
/* Disable Channel interrupts */
|
|
|
|
regval = getreg32(EFM32_DMA_IEN);
|
|
regval |= bit;
|
|
putreg32(regval, EFM32_DMA_IEN);
|
|
irqrestore(flags);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: efm32_dmasample
|
|
*
|
|
* Description:
|
|
* Sample DMA register contents
|
|
*
|
|
* Assumptions:
|
|
* - DMA handle allocated by efm32_dmachannel()
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_DMA
|
|
void efm32_dmasample(DMA_HANDLE handle, struct efm32_dmaregs_s *regs)
|
|
{
|
|
struct dma_channel_s *dmach = (struct dma_channel_s *)handle;
|
|
uintptr_t regaddr;
|
|
irqstate_t flags;
|
|
|
|
/* Sample DMA registers. */
|
|
|
|
flags = irqsave();
|
|
|
|
regs->status = getreg32(EFM32_DMA_STATUS);
|
|
regs->ctrlbase = getreg32(EFM32_DMA_CTRLBASE);
|
|
regs->altctrlbase = getreg32(EFM32_DMA_ALTCTRLBASE);
|
|
regs->chwaitstatus = getreg32(EFM32_DMA_CHWAITSTATUS);
|
|
regs->chusebursts = getreg32(EFM32_DMA_CHUSEBURSTS);
|
|
regs->chreqmasks = getreg32(EFM32_DMA_CHREQMASKS);
|
|
regs->chens = getreg32(EFM32_DMA_CHENS);
|
|
regs->chalts = getreg32(EFM32_DMA_CHALTS);
|
|
regs->chpris = getreg32(EFM32_DMA_CHPRIS);
|
|
regs->errorc = getreg32(EFM32_DMA_ERRORC);
|
|
regs->chreqstatus = getreg32(EFM32_DMA_CHREQSTATUS);
|
|
regs->chsreqstatus = getreg32(EFM32_DMA_CHSREQSTATUS);
|
|
regs->ifr = getreg32(EFM32_DMA_IF);
|
|
regs->ien = getreg32(EFM32_DMA_IEN);
|
|
#if defined(CONFIG_EFM32_EFM32GG)
|
|
regs->ctrl = getreg32(EFM32_DMA_CTRL);
|
|
regs->rds = getreg32(EFM32_DMA_RDS);
|
|
regs->loop0 = getreg32(EFM32_DMA_LOOP0);
|
|
regs->loop1 = getreg32(EFM32_DMA_LOOP1);
|
|
regs->rect0 = getreg32(EFM32_DMA_RECT0);
|
|
#endif
|
|
|
|
/* Sample channel control register */
|
|
|
|
regaddr = EFM32_DMA_CHn_CTRL(dmach->chan)
|
|
regs->chnctrl = getreg32(regaddr);
|
|
|
|
irqrestore(flags);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: efm32_dmadump
|
|
*
|
|
* Description:
|
|
* Dump previously sampled DMA register contents
|
|
*
|
|
* Assumptions:
|
|
* - DMA handle allocated by efm32_dmachannel()
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_DMA
|
|
void efm32_dmadump(DMA_HANDLE handle, const struct efm32_dmaregs_s *regs,
|
|
const char *msg)
|
|
{
|
|
struct dma_channel_s *dmach = (struct dma_channel_s *)handle;
|
|
|
|
dmadbg("%s\n", msg);
|
|
dmadbg(" DMA Registers:\n");
|
|
dmadbg(" STATUS: %08x\n", regs->status);
|
|
dmadbg(" CTRLBASE: %08x\n", regs->ctrlbase);
|
|
dmadbg(" ALTCTRLBASE: %08x\n", regs->altctrlbase);
|
|
dmadbg(" CHWAITSTATUS: %08x\n", regs->chwaitstatus);
|
|
dmadbg(" CHUSEBURSTS: %08x\n", regs->chusebursts);
|
|
dmadbg(" CHREQMASKS: %08x\n", regs->chreqmasks);
|
|
dmadbg(" CHENS: %08x\n", regs->chens);
|
|
dmadbg(" CHALTS: %08x\n", regs->chalts);
|
|
dmadbg(" CHPRIS: %08x\n", regs->chpris);
|
|
dmadbg(" ERRORC: %08x\n", regs->errorc);
|
|
dmadbg(" CHREQSTATUS: %08x\n", regs->chreqstatus);
|
|
dmadbg(" CHSREQSTATUS: %08x\n", regs->chsreqstatus);
|
|
dmadbg(" IEN: %08x\n", regs->ien);
|
|
#if defined(CONFIG_EFM32_EFM32GG)
|
|
dmadbg(" CTRL: %08x\n", regs->ctrl);
|
|
dmadbg(" RDS: %08x\n", regs->rds);
|
|
dmadbg(" LOOP0: %08x\n", regs->loop0);
|
|
dmadbg(" LOOP1: %08x\n", regs->loop1);
|
|
dmadbg(" RECT0: %08x\n", regs->rect0);
|
|
#endif
|
|
dmadbg(" DMA Channel %d Registers:\n", dmach->chan);
|
|
dmadbg(" CHCTRL: %08x\n", regs->chnctrl);
|
|
}
|
|
#endif
|