blob: 9c51d0dcd4f04247ccd4322734d11e13c8a0358f [file] [log] [blame]
/****************************************************************************
* arch/arm/src/gd32f4/gd32f4xx_dma.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/semaphore.h>
#include "sched/sched.h"
#include "chip.h"
#include "gd32f4xx_dma.h"
#include "gd32f4xx.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#if GD32_NDMA > 1
#define DMA0_NPERIPHS (8)
#define DMA0_NCHANNELS (8)
#define DMA1_NCHANNELS (8)
#define DMA_NCHANNELS (DMA0_NCHANNELS + DMA1_NCHANNELS)
/****************************************************************************
* Private Types
****************************************************************************/
/* This structure describes one DMA channel */
struct gd32_dma_channel_s
{
uint8_t chan_num; /* DMA channel number (0-7) */
uint8_t irq; /* DMA channel IRQ number */
uint8_t periph; /* DMA peripheral number (0-7) */
sem_t chsem; /* Used to wait for DMA channel to become available */
uint32_t dmabase; /* DMA base address */
dma_callback_t callback; /* Callback invoked when the DMA completes */
void *arg; /* Argument passed to callback function */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static uint8_t gd32_dma_interrupt_flag_get(uint32_t dma_periph,
uint8_t channelx);
static void gd32_dma_interrupt_flag_clear(uint32_t dma_periph,
uint8_t channelx,
uint8_t flag);
/****************************************************************************
* Private Data
****************************************************************************/
/* This array describes the state of each DMA */
static struct gd32_dma_channel_s g_dmachan[DMA_NCHANNELS] =
{
{
.chan_num = GD32_DMA_CH0,
.irq = GD32_IRQ_DMA0_CHANNEL0,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA0,
},
{
.chan_num = GD32_DMA_CH1,
.irq = GD32_IRQ_DMA0_CHANNEL1,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA0,
},
{
.chan_num = GD32_DMA_CH2,
.irq = GD32_IRQ_DMA0_CHANNEL2,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA0,
},
{
.chan_num = GD32_DMA_CH3,
.irq = GD32_IRQ_DMA0_CHANNEL3,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA0,
},
{
.chan_num = GD32_DMA_CH4,
.irq = GD32_IRQ_DMA0_CHANNEL4,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA0,
},
{
.chan_num = GD32_DMA_CH5,
.irq = GD32_IRQ_DMA0_CHANNEL5,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA0,
},
{
.chan_num = GD32_DMA_CH6,
.irq = GD32_IRQ_DMA0_CHANNEL6,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA0,
},
{
.chan_num = GD32_DMA_CH7,
.irq = GD32_IRQ_DMA0_CHANNEL7,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA0,
},
{
.chan_num = GD32_DMA_CH0,
.irq = GD32_IRQ_DMA1_CHANNEL0,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA1,
},
{
.chan_num = GD32_DMA_CH1,
.irq = GD32_IRQ_DMA1_CHANNEL1,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA1,
},
{
.chan_num = GD32_DMA_CH2,
.irq = GD32_IRQ_DMA1_CHANNEL2,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA1,
},
{
.chan_num = GD32_DMA_CH3,
.irq = GD32_IRQ_DMA1_CHANNEL3,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA1,
},
{
.chan_num = GD32_DMA_CH4,
.irq = GD32_IRQ_DMA1_CHANNEL4,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA1,
},
{
.chan_num = GD32_DMA_CH5,
.irq = GD32_IRQ_DMA1_CHANNEL5,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA1,
},
{
.chan_num = GD32_DMA_CH6,
.irq = GD32_IRQ_DMA1_CHANNEL6,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA1,
},
{
.chan_num = GD32_DMA_CH7,
.irq = GD32_IRQ_DMA1_CHANNEL7,
.chsem = SEM_INITIALIZER(1),
.dmabase = GD32_DMA1,
},
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: gd32_dma_clock_enable
*
* Description:
* Enable DMA clock
****************************************************************************/
static void gd32_dma_clock_enable(uint32_t dmabase)
{
uint32_t rcu_en;
uint32_t regaddr;
/* Determine which DMA to configure */
switch (dmabase)
{
default:
return;
#ifdef CONFIG_GD32F4_DMA0
case GD32_DMA0:
rcu_en = RCU_AHB1EN_DMA0EN;
regaddr = GD32_RCU_AHB1EN;
break;
#endif
#ifdef CONFIG_GD32F4_DMA1
case GD32_DMA1:
rcu_en = RCU_AHB1EN_DMA1EN;
regaddr = GD32_RCU_AHB1EN;
break;
#endif
}
/* Enable AHB1 clock for DMA */
modifyreg32(regaddr, 0, rcu_en);
}
/****************************************************************************
* Name: gd32_channel_enable
*
* Description:
* Enable the DMA channelx
*
****************************************************************************/
static void gd32_channel_enable(uint32_t dma_periph, uint8_t channelx)
{
uint32_t regaddr;
uint32_t regval;
DEBUGASSERT(channelx < DMA0_NCHANNELS);
DEBUGASSERT(dma_periph == GD32_DMA0_BASE && dma_periph == GD32_DMA1_BASE);
/* Get DMA channel control register address */
regaddr = GD32_DMA_CHCTL(dma_periph, channelx);
/* Enable DMA channelx */
regval = getreg32(regaddr);
regval |= DMA_CHXCTL_CHEN;
putreg32(regval, regaddr);
}
/****************************************************************************
* Name: gd32_channel_disable
*
* Description:
* Disable the DMA channelx
*
****************************************************************************/
static void gd32_channel_disable(uint32_t dma_periph, uint8_t channelx)
{
uint32_t regaddr;
uint32_t regval;
DEBUGASSERT(channelx < DMA0_NCHANNELS);
DEBUGASSERT(dma_periph == GD32_DMA0_BASE && dma_periph == GD32_DMA1_BASE);
/* Get DMA channel control register address */
regaddr = GD32_DMA_CHCTL(dma_periph, channelx);
/* Disable DMA channelx */
regval = getreg32(regaddr);
regval &= ~DMA_CHXCTL_CHEN;
putreg32(regval, regaddr);
}
/****************************************************************************
* Name: gd32_channel_interrupt_enable
*
* Description:
* Enable the DMA channelx interrupt
*
****************************************************************************/
static void gd32_channel_interrupt_enable(uint32_t dma_periph,
uint8_t channelx,
uint32_t interrupt)
{
uint32_t regaddr;
uint32_t regval;
DEBUGASSERT(channelx < DMA0_NCHANNELS);
DEBUGASSERT(dma_periph == GD32_DMA0_BASE && dma_periph == GD32_DMA1_BASE);
/* Get DMA channel control register address */
regaddr = GD32_DMA_CHCTL(dma_periph, channelx);
/* Disable all interrupts at the DMA controller */
regval = getreg32(regaddr);
regval &= ~(DMA_CHXCTL_SDEIE | DMA_CHXCTL_TAEIE |
DMA_CHXCTL_HTFIE | DMA_CHXCTL_FTFIE);
/* Set DMA channelx interrupt */
regval |= interrupt;
putreg32(regval, regaddr);
}
/****************************************************************************
* Name: gd32_channel_interrupt_disable
*
* Description:
* Disable the DMA channelx interrupt
*
****************************************************************************/
static void gd32_channel_interrupt_disable(uint32_t dma_periph,
uint8_t channelx)
{
uint32_t regaddr;
uint32_t regval;
DEBUGASSERT(channelx < DMA0_NCHANNELS);
DEBUGASSERT(dma_periph == GD32_DMA0_BASE && dma_periph == GD32_DMA1_BASE);
/* Get DMA channel control register address */
regaddr = GD32_DMA_CHCTL(dma_periph, channelx);
/* Disable all interrupts at the DMA controller */
regval = getreg32(regaddr);
regval &= ~(DMA_CHXCTL_SDEIE | DMA_CHXCTL_TAEIE |
DMA_CHXCTL_HTFIE | DMA_CHXCTL_FTFIE);
putreg32(regval, regaddr);
/* Clear channelx interrupt flag */
regval = (DMA_INTF_FEEIF | DMA_INTF_SDEIF | DMA_INTF_TAEIF |
DMA_INTF_HTFIF | DMA_INTF_FTFIF);
gd32_dma_interrupt_flag_clear(dma_periph, channelx, (uint8_t)regval);
}
/****************************************************************************
* Name: gd32_dma_interrupt_flag_get
*
* Description:
* Get DMA interrupt flag
*
****************************************************************************/
static uint8_t gd32_dma_interrupt_flag_get(uint32_t dma_periph,
uint8_t channelx)
{
uint32_t regval;
uint8_t chan_intf_shift = 0;
uint8_t status = 0;
DEBUGASSERT(channelx < DMA0_NCHANNELS);
DEBUGASSERT(dma_periph == GD32_DMA0_BASE && dma_periph == GD32_DMA1_BASE);
/* Get the interrupt status for this channel */
if (channelx < 4)
{
/* Calculate channelx interrupt flag shift */
chan_intf_shift = channelx * 8 - (channelx % 2) * 2;
regval = getreg32(dma_periph + GD32_DMA_INTF0_OFFSET);
status = (regval >> chan_intf_shift) & DMA_INTF_MASK;
}
else
{
/* Calculate channelx interrupt flag shift */
channelx = channelx - 4;
chan_intf_shift = channelx * 8 - (channelx % 2) * 2;
regval = getreg32(dma_periph + GD32_DMA_INTF1_OFFSET);
status = (regval >> chan_intf_shift) & DMA_INTF_MASK;
}
return status;
}
/****************************************************************************
* Name: gd32_dma_interrupt_flag_clear
*
* Description:
* Clear DMA interrupt flag
*
****************************************************************************/
static void gd32_dma_interrupt_flag_clear(uint32_t dma_periph,
uint8_t channelx, uint8_t flag)
{
uint32_t regval;
uint8_t chan_intf_shift = 0;
DEBUGASSERT(channelx < DMA0_NCHANNELS);
DEBUGASSERT(dma_periph == GD32_DMA0_BASE && dma_periph == GD32_DMA1_BASE);
/* Clear the interrupt status for this channel */
if (channelx < 4)
{
/* Calculate channelx interrupt flag shift */
chan_intf_shift = channelx * 8 - (channelx % 2) * 2;
if (flag)
{
regval = flag << chan_intf_shift;
/* Clear channelx interrupt flag */
putreg32(regval, (dma_periph + GD32_DMA_INTC0_OFFSET));
}
}
else
{
/* Calculate channelx interrupt flag shift */
channelx = channelx - 4;
chan_intf_shift = channelx * 8 - (channelx % 2) * 2;
if (flag)
{
regval = flag << chan_intf_shift;
/* Clear channelx interrupt flag */
putreg32(regval, (dma_periph + GD32_DMA_INTC1_OFFSET));
}
}
}
/****************************************************************************
* Name: gd32_dma_interrupt
*
* Description:
* DMA interrupt handler
*
****************************************************************************/
static int gd32_dma_interrupt(int irq, void *context, void *arg)
{
struct gd32_dma_channel_s *dmachan;
uint8_t status;
/* 'arg' should the DMA channel instance. */
dmachan = (struct gd32_dma_channel_s *)arg;
DEBUGASSERT(dmachan != NULL);
if (dmachan->dmabase == GD32_DMA0_BASE)
{
DEBUGASSERT(dmachan == &g_dmachan[dmachan->chan_num]);
}
else
{
DEBUGASSERT(dmachan == &g_dmachan[dmachan->chan_num + DMA0_NCHANNELS]);
}
/* Get the interrupt status for this channel */
status = gd32_dma_interrupt_flag_get(dmachan->dmabase, dmachan->chan_num);
if (status)
{
gd32_dma_interrupt_flag_clear(dmachan->dmabase, dmachan->chan_num,
status);
/* Call the DMA callback */
if (dmachan->callback)
{
dmachan->callback(dmachan, status, dmachan->arg);
}
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: arm_dma_initialize
*
* Description:
* Initialize the DMA subsystem
*
* Returned Value:
* None
*
****************************************************************************/
void weak_function arm_dma_initialize(void)
{
struct gd32_dma_channel_s *dmachan;
uint8_t channelx;
dmainfo("Initialize DMA\n");
/* Initialize each DMA channel */
for (channelx = 0; channelx < DMA_NCHANNELS; channelx++)
{
dmachan = &g_dmachan[channelx];
DEBUGASSERT(dmachan != NULL);
/* Attach DMA interrupt vectors */
irq_attach(dmachan->irq, gd32_dma_interrupt, dmachan);
/* Disable the DMA channel and channel interrupt */
gd32_channel_interrupt_disable(dmachan->dmabase, dmachan->chan_num);
gd32_channel_disable(dmachan->dmabase, dmachan->chan_num);
/* Enable the channel interrupts at the NVIC (still disabled
* at the DMA controller)
*/
up_enable_irq(dmachan->irq);
}
}
/****************************************************************************
* Name: gd32_dma_channel_alloc
*
* Description:
* Allocate a DMA channel. This function gives the caller mutually
* exclusive access to the DMA channel specified by the 'dmamap' argument.
* Should note that a DMA channel only can be used by on peripheral at the
* time.
*
* If the DMA channel is not available, then gd32_dma_channel_alloc() will
* wait until the holder of the channel relinquishes the channel by calling
* gd32_dma_channel_free().
*
* Input Parameters:
* periph_req - Identifies the DMA channle is request by which peripheral
*
* Returned Value:
* If periph_req is valid, this function ALWAYS returns a non-NULL
* void* DMA channel handle.
*
****************************************************************************/
DMA_HANDLE gd32_dma_channel_alloc(uint8_t periph_req)
{
struct gd32_dma_channel_s *dmachan;
int ret;
uint8_t index = 0;
uint8_t subperiph = 0;
/* Get the peripheral numbers from the peripheral request */
subperiph = (periph_req >> PERIPH_SHIFT) & PERIPH_MASK;
DEBUGASSERT(subperiph < DMA0_NPERIPHS);
/* Get the channel index from the peripheral request */
index = (periph_req >> CHANNEL_SHIFT) & CHANNEL_MASK;
DEBUGASSERT(index < DMA_NCHANNELS);
/* Get the chan_num index from the bit-encoded channel value */
dmachan = &g_dmachan[index];
DEBUGASSERT(dmachan != NULL);
/* Get exclusive access to the DMA channel */
ret = nxsem_wait_uninterruptible(&dmachan->chsem);
if (ret < 0)
{
return NULL;
}
dmachan->periph = subperiph;
return (DMA_HANDLE)dmachan;
}
/****************************************************************************
* Name: gd32_dma_channel_free
*
* Description:
* Release a DMA channel. If another thread is waiting for this DMA
* channel in a call to gd32_dma_channel_alloc, 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 gd32_dma_channel_alloc() 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 gd32_dma_channel_free(DMA_HANDLE handle)
{
struct gd32_dma_channel_s *dmachan = (struct gd32_dma_channel_s *)handle;
DEBUGASSERT(dmachan != NULL);
/* Disable the DMA channel and channel interrupt */
gd32_channel_interrupt_disable(dmachan->dmabase, dmachan->chan_num);
gd32_channel_disable(dmachan->dmabase, dmachan->chan_num);
/* Release the channel */
nxsem_post(&dmachan->chsem);
}
/****************************************************************************
* Name: gd32_dma_singlemode_setup
*
* Description:
* Configure DMA single data mode before using
*
****************************************************************************/
void gd32_dma_singlemode_setup(struct gd32_dma_channel_s *dmachan,
dma_single_data_parameter_struct *init_struct)
{
uint32_t regaddr;
uint32_t regval;
dmainfo("peripheral address: %08" PRIx32 "memory address: %08" PRIx32
"transfer numbers: %08" PRIx16 "\n",
init_struct->periph_addr, init_struct->memory0_addr,
init_struct->number);
/* Clear CHEN bit in channel x control register */
regaddr = GD32_DMA_CHCTL(dmachan->dmabase, dmachan->chan_num);
regval = getreg32(regaddr);
regval &= ~DMA_CHXCTL_CHEN;
putreg32(regval, regaddr);
/* Clear channelx interrupt flag */
regval = (DMA_INTF_FEEIF | DMA_INTF_SDEIF | DMA_INTF_TAEIF |
DMA_INTF_HTFIF | DMA_INTF_FTFIF);
gd32_dma_interrupt_flag_clear(dmachan->dmabase, dmachan->chan_num,
(uint8_t)regval);
/* Select single data mode */
regaddr = GD32_DMA_CHFCTL(dmachan->dmabase, dmachan->chan_num);
regval = getreg32(regaddr);
regval &= ~DMA_CHXFCTL_MDMEN;
putreg32(regval, regaddr);
/* Configure peripheral base address */
regaddr = GD32_DMA_CHPADDR(dmachan->dmabase, dmachan->chan_num);
putreg32(init_struct->periph_addr, regaddr);
/* Configure memory base address */
regaddr = GD32_DMA_CHM0ADDR(dmachan->dmabase, dmachan->chan_num);
putreg32(init_struct->memory0_addr, regaddr);
/* Configure the number of remaining data to be transferred */
regaddr = GD32_DMA_CHCNT(dmachan->dmabase, dmachan->chan_num);
putreg32(init_struct->number, regaddr);
/* Configure peripheral and memory transfer width, channel priotity,
* transfer mode
*/
regaddr = GD32_DMA_CHCTL(dmachan->dmabase, dmachan->chan_num);
regval = getreg32(regaddr);
regval &= ~(DMA_CHXCTL_PWIDTH_MASK | DMA_CHXCTL_MWIDTH_MASK |
DMA_CHXCTL_PRIO_MASK | DMA_CHXCTL_TM_MASK);
regval |= init_struct->direction;
if (DMA_WIDTH_32BITS_SELECT == init_struct->periph_memory_width)
{
regval |= DMA_MEMORY_WIDTH_32BIT | DMA_PERIPH_WIDTH_32BIT;
}
else if (DMA_WIDTH_16BITS_SELECT == init_struct->periph_memory_width)
{
regval |= DMA_MEMORY_WIDTH_16BIT | DMA_PERIPH_WIDTH_16BIT;
}
else
{
regval |= DMA_MEMORY_WIDTH_8BIT | DMA_PERIPH_WIDTH_8BIT;
}
if (DMA_PRIO_LOW_SELECT == init_struct->priority)
{
regval |= DMA_PRIORITY_LOW;
}
else if (DMA_PRIO_MEDIUM_SELECT == init_struct->priority)
{
regval |= DMA_PRIORITY_MEDIUM;
}
else if (DMA_PRIO_HIGH_SELECT == init_struct->priority)
{
regval |= DMA_PRIORITY_HIGH;
}
else if (DMA_PRIO_ULTRA_HIGHSELECT == init_struct->priority)
{
regval |= DMA_PRIORITY_ULTRA_HIGH;
}
else
{
regval |= DMA_PRIORITY_MEDIUM;
}
/* Configure peripheral, memory increasing mode and DMA circular mode */
if (DMA_PERIPH_INCREASE_ENABLE == init_struct->periph_inc)
{
regval |= DMA_CHXCTL_PNAGA;
}
else if (DMA_PERIPH_INCREASE_DISABLE == init_struct->periph_inc)
{
regval &= ~DMA_CHXCTL_PNAGA;
}
else
{
regval |= DMA_CHXCTL_PAIF;
}
if (DMA_MEMORY_INCREASE_ENABLE == init_struct->memory_inc)
{
regval |= DMA_CHXCTL_MNAGA;
}
else
{
regval &= ~DMA_CHXCTL_MNAGA;
}
if (DMA_CIRCULAR_MODE_ENABLE == init_struct->circular_mode)
{
regval |= DMA_CHXCTL_CMEN;
}
else
{
regval &= ~DMA_CHXCTL_CMEN;
}
/* DMA channel peripheral select */
regval &= ~DMA_CHXCTL_PERIEN_MASK;
regval |= (dmachan->periph << DMA_CHXCTL_PERIEN_SHIFT);
putreg32(regval, regaddr);
}
/****************************************************************************
* Name: gd32_dma_setup
*
* Description:
* Configure DMA before using
*
****************************************************************************/
void gd32_dma_setup(DMA_HANDLE handle, void *arg, uint8_t data_mode)
{
struct gd32_dma_channel_s *dmachan = (struct gd32_dma_channel_s *)handle;
gd32_dma_clock_enable(dmachan->dmabase);
data_mode = 1;
if (data_mode)
{
dma_single_data_parameter_struct *init_struct =
(dma_single_data_parameter_struct *)arg;
gd32_dma_singlemode_setup(dmachan, init_struct);
}
}
/****************************************************************************
* Name: gd32_dma_start
*
* Description:
* Start the DMA transfer
*
* Assumptions:
* - DMA handle allocated by gd32_dma_channel_alloc()
* - No DMA in progress
*
****************************************************************************/
void gd32_dma_start(DMA_HANDLE handle, dma_callback_t callback, void *arg,
uint32_t interrupt)
{
struct gd32_dma_channel_s *dmachan = (struct gd32_dma_channel_s *)handle;
uint32_t regval;
DEBUGASSERT(dmachan != NULL);
DEBUGASSERT(dmachan->chan_num < DMA0_NCHANNELS);
DEBUGASSERT(dmachan->dmabase == GD32_DMA0_BASE &&
dmachan->dmabase == GD32_DMA1_BASE);
/* Save the callback info. This will be invoked when the DMA completes */
dmachan->callback = callback;
dmachan->arg = arg;
if (interrupt & DMA_INT_MASK)
{
regval = (interrupt & DMA_INT_MASK);
}
else
{
/* If interrupt flag is not set, then set full transfer finish
* interrupt as default
*/
regval = DMA_CHXCTL_FTFIE;
}
/* Enable DMA channel and channel interrupt */
gd32_channel_interrupt_enable(dmachan->dmabase, dmachan->chan_num, regval);
gd32_channel_enable(dmachan->dmabase, dmachan->chan_num);
}
/****************************************************************************
* Name: gd32_dma_stop
*
* Description:
* Cancel the DMA. After gd32_dma_stop() is called, the DMA channel is
* reset and gd32_dma_setup() must be called before gd32_dma_start()
* can be called again
*
* Assumptions:
* - DMA handle allocated by gd32_dma_channel_alloc()
*
****************************************************************************/
void gd32_dma_stop(DMA_HANDLE handle)
{
struct gd32_dma_channel_s *dmachan = (struct gd32_dma_channel_s *)handle;
DEBUGASSERT(dmachan != NULL);
DEBUGASSERT(dmachan->chan_num < DMA0_NCHANNELS);
DEBUGASSERT(dmachan->dmabase == GD32_DMA0_BASE &&
dmachan->dmabase == GD32_DMA1_BASE);
gd32_channel_interrupt_disable(dmachan->dmabase, dmachan->chan_num);
gd32_channel_disable(dmachan->dmabase, dmachan->chan_num);
}
/****************************************************************************
* Name: gd32_dma_tansnum_get
*
* Description:
* Get the number of remaining data to be transferred by the DMA
*
* Assumptions:
* - DMA handle allocated by gd32_dma_channel_alloc()
*
****************************************************************************/
size_t gd32_dma_tansnum_get(DMA_HANDLE handle)
{
struct gd32_dma_channel_s *dmachan = (struct gd32_dma_channel_s *)handle;
uint32_t remain_num;
DEBUGASSERT(dmachan != NULL);
DEBUGASSERT(dmachan->chan_num < DMA0_NCHANNELS);
DEBUGASSERT(dmachan->dmabase == GD32_DMA0_BASE &&
dmachan->dmabase == GD32_DMA1_BASE);
remain_num = getreg32(GD32_DMA_CHCNT(dmachan->dmabase, dmachan->chan_num));
return (size_t)remain_num;
}
/****************************************************************************
* Name: gd32_dma_sample
*
* Description:
* Sample DMA register contents
*
* Assumptions:
* - DMA handle allocated by gd32_dma_channel_alloc()
*
****************************************************************************/
#ifdef CONFIG_DEBUG_DMA_INFO
void gd32_dma_sample(DMA_HANDLE handle, struct gd32_dmaregs_s *regs)
{
struct gd32_dma_channel_s *dmachan = (struct gd32_dma_channel_s *)handle;
irqstate_t flags;
DEBUGASSERT(dmachan != NULL);
DEBUGASSERT(dmachan->chan_num < DMA0_NCHANNELS);
DEBUGASSERT(dmachan->dmabase == GD32_DMA0_BASE &&
dmachan->dmabase == GD32_DMA1_BASE);
flags = enter_critical_section();
regs->intf0 = getreg32(dmachan->dmabase + GD32_DMA_INTF0_OFFSET);
regs->intf1 = getreg32(dmachan->dmabase + GD32_DMA_INTF1_OFFSET);
regs->chctl = getreg32(GD32_DMA_CHCTL(dmachan->dmabase,
dmachan->chan_num));
regs->chcnt = getreg32(GD32_DMA_CHCNT(dmachan->dmabase,
dmachan->chan_num));
regs->chpaddr = getreg32(GD32_DMA_CHPADDR(dmachan->dmabase,
dmachan->chan_num));
regs->chm0addr = getreg32(GD32_DMA_CHM0ADDR(dmachan->dmabase,
dmachan->chan_num));
regs->chm1addr = getreg32(GD32_DMA_CHM1ADDR(dmachan->dmabase,
dmachan->chan_num));
regs->chfctl = getreg32(GD32_DMA_CHFCTL(dmachan->dmabase,
dmachan->chan_num));
leave_critical_section(flags);
}
#endif
/****************************************************************************
* Name: gd32_dma_dump
*
* Description:
* Dump previously sampled DMA register contents
*
* Assumptions:
* - DMA handle allocated by gd32_dma_channel_alloc()
*
****************************************************************************/
#ifdef CONFIG_DEBUG_DMA_INFO
void gd32_dma_dump(DMA_HANDLE handle, const struct gd32_dmaregs_s *regs,
const char *msg)
{
struct gd32_dma_channel_s *dmachan = (struct gd32_dma_channel_s *)handle;
DEBUGASSERT(dmachan != NULL);
dmainfo("DMA Registers: %s\n", msg);
dmainfo(" INTF0: %08x\n", regs->intf0);
dmainfo(" INTF1: %08x\n", regs->intf1);
dmainfo(" CHCTL: %08x\n", regs->chctl);
dmainfo(" CHCNT: %08x\n", regs->chcnt);
dmainfo(" CHPADDR: %08x\n", regs->chpaddr);
dmainfo(" CHM0ADDR: %08x\n", regs->chm0addr);
dmainfo(" CHM1ADDR: %08x\n", regs->chm1addr);
dmainfo(" CHFCTL: %08x\n", regs->chfctl);
}
#endif
#endif /* GD32_NDMA */