824 lines
20 KiB
C
824 lines
20 KiB
C
/*!
|
|
* \file xc_drv_dma.c
|
|
*
|
|
* \brief Target xinchip dma driver implementation
|
|
*
|
|
* \copyright Revised BSD License, see section \ref LICENSE.
|
|
*
|
|
* \code
|
|
*
|
|
* _ __ _ ________ _
|
|
* | |/ /(_)___ / ____/ /_ (_)___
|
|
* | // / __ \/ / / __ \/ / __ \
|
|
* / |/ / / / / /___/ / / / / /_/ /
|
|
* /_/|_/_/_/ /_/\____/_/ /_/_/ .___/
|
|
* /_/
|
|
* (C) 2022-2025 XinChip
|
|
*
|
|
* \endcode
|
|
*
|
|
* \author ( XinChip ) Alex-J
|
|
*
|
|
* \author ( XinChip )
|
|
*/
|
|
|
|
/*-----------------------------------------------------------------------------------
|
|
INCLUDE HEADE FILES
|
|
------------------------------------------------------------------------------------*/
|
|
#include "xc_drv_dma.h"
|
|
#if XC_CHECK(XC_DMA_ENABLED)
|
|
/*------------------------------------------------------------------------------------
|
|
Global Variables
|
|
-------------------------------------------------------------------------------------*/
|
|
DMA_Instance_t DMA_Instance;
|
|
|
|
/*------------------------------------------------------------------------------------
|
|
Functions
|
|
-------------------------------------------------------------------------------------*/
|
|
/**
|
|
* @brief xc_dma_set_test_mode
|
|
* @details DMAC test mode set
|
|
*
|
|
* @param uint8_t - mode
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE void xc_dma_set_test_mode(uint8_t mode)
|
|
{
|
|
dma_test_reg_set(mode);
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_set_channel_priority_order
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE void xc_dma_set_channel_priority_order(DMA_Instance_t *inst)
|
|
{
|
|
uint8_t priority[DMA_MAX_CHANNELS];
|
|
uint8_t i, ch_priority, order;
|
|
|
|
for (i = 0; i < DMA_MAX_CHANNELS; i++) {
|
|
priority[i] = 0;
|
|
inst->Ch_Order[i] = i;
|
|
}
|
|
|
|
for (uint8_t ch_num = 0; ch_num < DMA_MAX_CHANNELS; ch_num++) {
|
|
// read the priority of the current channel
|
|
ch_priority = dma_cfg_l__ch_prior__getf(ch_num);
|
|
|
|
// Check the channel order array to see what position
|
|
// this channel comes in the priority list.
|
|
for (i = 0; i <= DMA_MAX_CHANNELS; i++) {
|
|
if (A_MAXEQ_B(ch_priority, priority[i]) || i == DMA_MAX_CHANNELS) {
|
|
order = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Now we know the order for this channel insert in the correct
|
|
// array position and shift current priority ordering to suite.
|
|
for (i = ch_num; i > order; i--) {
|
|
priority[i] = priority[i - 1];
|
|
inst->Ch_Order[i] = inst->Ch_Order[i - 1];
|
|
}
|
|
priority[order] = ch_priority;
|
|
inst->Ch_Order[order] = ch_num;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_reset_instance
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE void xc_dma_reset_instance(DMA_Instance_t *inst)
|
|
{
|
|
for (uint8_t i = 0; i < DMA_MAX_CHANNELS; i++) {
|
|
switch (i) {
|
|
case 0:
|
|
inst->Ch[i].ch_num = DMA_CHANNEL0;
|
|
break;
|
|
case 1:
|
|
inst->Ch[i].ch_num = DMA_CHANNEL1;
|
|
break;
|
|
case 2:
|
|
inst->Ch[i].ch_num = DMA_CHANNEL2;
|
|
break;
|
|
case 3:
|
|
inst->Ch[i].ch_num = DMA_CHANNEL3;
|
|
break;
|
|
}
|
|
|
|
inst->Ch[i].userCallback = NULL;
|
|
inst->Ch[i].userListener = NULL;
|
|
}
|
|
|
|
// Set the channel priority order
|
|
xc_dma_set_channel_priority_order(inst);
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_check_channel_range
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE uint8_t xc_dma_check_channel_range(eDMA_Ch_Num ch_num)
|
|
{
|
|
uint8_t errorCode = DMA_OK;
|
|
|
|
if (ch_num == DMA_NO_CHANNEL || ch_num > DMA_ALL_CHANNELS) {
|
|
errorCode = DMA_ECHRNG;
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_check_channel_busy
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE uint8_t xc_dma_check_channel_busy(eDMA_Ch_Num ch_num)
|
|
{
|
|
uint8_t errorCode = DMA_OK;
|
|
|
|
if (ch_num == DMA_NO_CHANNEL || ch_num & (~DMA_ALL_CHANNELS)) {
|
|
errorCode = DMA_ECHRNG;
|
|
} else {
|
|
if (((uint16_t)dma_ch_en_reg_get( )) & ch_num) {
|
|
errorCode = DMA_EBUSY;
|
|
}
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_get_channel_index
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE uint8_t xc_dma_get_channel_index(eDMA_Ch_Num ch_num)
|
|
{
|
|
uint8_t ch_enum = 1;
|
|
uint8_t ch_index = 0;
|
|
|
|
ch_num &= (DMA_ALL_CHANNELS >> 8);
|
|
while (ch_index < DMA_MAX_CHANNELS) {
|
|
if (ch_enum == ch_num) {
|
|
break;
|
|
}
|
|
ch_enum *= 2;
|
|
ch_index++;
|
|
}
|
|
|
|
return ch_index;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_enable
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE void xc_dma_enable(void) { dma_cfg_reg_set(ENABLE); }
|
|
|
|
/**
|
|
* @brief xc_dma_disable
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE uint8_t xc_dma_disable(void)
|
|
{
|
|
uint8_t errorCode = 0;
|
|
uint8_t dma_en = dma_cfg_reg__dma_en__getf( );
|
|
|
|
if (dma_en != 0) {
|
|
dma_cfg_reg_set(DISABLE);
|
|
|
|
// Ensure that the DMA was disabled
|
|
// May not disable due to split response on one
|
|
// of the DMA channels
|
|
if (dma_cfg_reg_get( )) {
|
|
errorCode = DMA_EBUSY;
|
|
}
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_is_enable
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
bool xc_dma_is_enable(void)
|
|
{
|
|
bool ret;
|
|
|
|
ret = (bool)dma_cfg_reg__dma_en__getf( );
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_enable_channel
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE uint8_t xc_dma_enable_channel(eDMA_Ch_Num ch_num)
|
|
{
|
|
uint8_t errorCode = 0;
|
|
|
|
// Check for valid channel number and not busy
|
|
errorCode = xc_dma_check_channel_busy(ch_num);
|
|
if (errorCode == 0) {
|
|
// The dw_dmac_channel_number enum is declared such that
|
|
// the enumerated value maps exactly to the value that
|
|
// needs to be written into the ChEnReg for enabling.
|
|
dma_ch_en_reg_set(ch_num);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_disable_channel
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE uint8_t xc_dma_disable_channel(eDMA_Ch_Num ch_num)
|
|
{
|
|
uint8_t errorCode = 0;
|
|
uint32_t enabled_ch;
|
|
|
|
// Check for valid channel number
|
|
errorCode = xc_dma_check_channel_range(ch_num);
|
|
|
|
if (errorCode == 0) {
|
|
enabled_ch = (dma_ch_en_reg_get( ) & ch_num);
|
|
if (enabled_ch != 0) {
|
|
dma_ch_en_reg_set((ch_num & (DMA_ALL_CHANNELS << 8)));
|
|
|
|
// Ensure that the channel(s) was disabled.
|
|
// Channel may not disable due to a split response.
|
|
if (dma_ch_en_reg_get( ) & ch_num) {
|
|
errorCode = DMA_EBUSY;
|
|
}
|
|
}
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_is_channel_enabled
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
bool xc_dma_is_channel_enabled(eDMA_Ch_Num ch_num)
|
|
{
|
|
uint8_t ch_index;
|
|
bool ret;
|
|
|
|
ch_index = xc_dma_get_channel_index(ch_num);
|
|
|
|
// Allow only ONE channel to be specified
|
|
if (ch_index == DMA_MAX_CHANNELS)
|
|
return false;
|
|
|
|
// Check that the specified channel is in range
|
|
if (ch_num & (~DMA_ALL_CHANNELS))
|
|
return false;
|
|
|
|
ret = (bool)REG_BIT_VAL_GET(dma_ch_en_reg__ch_en__getf( ), 1 << ch_index, ch_index);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_enable_channel_irq
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE uint8_t xc_dma_enable_channel_irq(eDMA_Ch_Num ch_num)
|
|
{
|
|
uint8_t errorCode = DMA_OK;
|
|
|
|
// Check for valid channel number and not busy
|
|
errorCode = xc_dma_check_channel_busy(ch_num);
|
|
|
|
if (errorCode == 0) {
|
|
// Loop through each channel in turn and disable
|
|
// the channel Irq for the selected channels.
|
|
for (uint8_t x = 0; x < DMA_MAX_CHANNELS; x++) {
|
|
if (ch_num & (1 << x)) {
|
|
if (dma_ctl_l__int_en__getf(x) != 0x1) {
|
|
dma_ctl_l__int_en__setf(x, ENABLE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_disable_channel_irq
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE uint8_t xc_dma_disable_channel_irq(eDMA_Ch_Num ch_num)
|
|
{
|
|
uint8_t errorCode = DMA_OK;
|
|
|
|
// Check for valid channel number and not busy
|
|
errorCode = xc_dma_check_channel_busy(ch_num);
|
|
|
|
if (errorCode == 0) {
|
|
// Loop through each channel in turn and disable
|
|
// the channel Irq for the selected channels.
|
|
for (uint8_t x = 0; x < DMA_MAX_CHANNELS; x++) {
|
|
if (ch_num & (1 << x)) {
|
|
if (dma_ctl_l__int_en__getf(x) != 0x0) {
|
|
dma_ctl_l__int_en__setf(x, DISABLE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_mask_irq
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE uint8_t xc_dma_mask_irq(eDMA_Ch_Num ch_num, eDMA_Irq ch_irq)
|
|
{
|
|
uint8_t errorCode = DMA_OK;
|
|
uint16_t reg;
|
|
|
|
// Check for valid channel number
|
|
errorCode = xc_dma_check_channel_range(ch_num);
|
|
if (errorCode == 0) {
|
|
// Loop through and clear the selected channel Irq
|
|
// for the targeted channels.
|
|
reg = (ch_num & (DMA_ALL_CHANNELS << 8));
|
|
for (uint8_t x = 0; x < DMA_MAX_INTERRUPTS; x++) {
|
|
if (ch_irq & (1 << x)) {
|
|
if(x == 0){
|
|
dma_mask_tfr_set(reg);
|
|
}else if(x == 1){
|
|
dma_mask_block_set(reg);
|
|
}else if(x == 2){
|
|
dma_mask_src_tran_set(reg);
|
|
}else if(x == 3){
|
|
dma_mask_dst_tran_set(reg);
|
|
}else if(x == 4){
|
|
dma_mask_err_set(reg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_unmask_irq
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE uint8_t xc_dma_unmask_irq(eDMA_Ch_Num ch_num, eDMA_Irq ch_irq)
|
|
{
|
|
uint8_t errorCode = DMA_OK;
|
|
uint16_t reg;
|
|
|
|
// Check for valid channel number
|
|
errorCode = xc_dma_check_channel_range(ch_num);
|
|
if (errorCode == 0) {
|
|
// Loop through and clear the selected channel Irq
|
|
// for the targeted channels.
|
|
reg = ch_num;
|
|
for (uint8_t x = 0; x < DMA_MAX_INTERRUPTS; x++) {
|
|
if (ch_irq & (1 << x)) {
|
|
if(x == 0){
|
|
dma_mask_tfr_set(reg);
|
|
}else if(x == 1){
|
|
dma_mask_block_set(reg);
|
|
}else if(x == 2){
|
|
dma_mask_src_tran_set(reg);
|
|
}else if(x == 3){
|
|
dma_mask_dst_tran_set(reg);
|
|
}else if(x == 4){
|
|
dma_mask_err_set(reg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_clear_irq
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE uint8_t xc_dma_clear_irq(eDMA_Ch_Num ch_num, eDMA_Irq ch_irq)
|
|
{
|
|
uint8_t errorCode = DMA_OK;
|
|
uint16_t reg;
|
|
|
|
// Check for valid channel number
|
|
errorCode = xc_dma_check_channel_range(ch_num);
|
|
if (errorCode == 0) {
|
|
// Loop through and clear the selected channel Irq
|
|
// for the targeted channels.
|
|
reg = (ch_num & (DMA_ALL_CHANNELS >> 8));
|
|
for (uint8_t x = 0; x < DMA_MAX_INTERRUPTS; x++) {
|
|
if (ch_irq & (1 << x)) {
|
|
if(x == 0){
|
|
dma_clear_tfr_set(reg);
|
|
}else if(x == 1){
|
|
dma_clear_block_set(reg);
|
|
}else if(x == 2){
|
|
dma_clear_src_tran_set(reg);
|
|
}else if(x == 3){
|
|
dma_clear_dst_tran_set(reg);
|
|
}else if(x == 4){
|
|
dma_clear_err_set(reg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_init
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE uint8_t xc_dma_init(void)
|
|
{
|
|
// Remove from test mode
|
|
xc_dma_set_test_mode(DISABLE);
|
|
// Reset the DMA instance structure
|
|
xc_dma_reset_instance(&DMA_Instance);
|
|
// Disable the DMA controller
|
|
uint8_t errorCode = xc_dma_disable( );
|
|
if (errorCode == 0) {
|
|
// Disable all DMA channels
|
|
errorCode = xc_dma_disable_channel(DMA_ALL_CHANNELS);
|
|
}
|
|
|
|
if (errorCode == 0) {
|
|
// Disable all channel interrupts
|
|
errorCode = xc_dma_disable_channel_irq(DMA_ALL_CHANNELS);
|
|
}
|
|
|
|
if (errorCode == 0) {
|
|
// Mask all channel interrupts
|
|
errorCode = xc_dma_mask_irq(DMA_ALL_CHANNELS, DMA_IRQ_ALL);
|
|
}
|
|
|
|
if (errorCode == 0) {
|
|
// Clear any pending interrupts
|
|
errorCode = xc_dma_clear_irq(DMA_ALL_CHANNELS, DMA_IRQ_ALL);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_set_channel_config
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE uint8_t xc_dma_set_channel_config(eDMA_Ch_Num ch_num,
|
|
DMA_Chx_Cfg_t *ch)
|
|
{
|
|
uint8_t errorCode;
|
|
uint8_t ch_index;
|
|
|
|
ch_index = xc_dma_get_channel_index(ch_num);
|
|
|
|
// Check for valid channel number and not busy
|
|
errorCode = xc_dma_check_channel_busy(ch_num);
|
|
if (errorCode == 0) {
|
|
// Set the control register
|
|
dma_ctl_l_pack(ch_index, ch->ctl_tt_fc, ch->ctl_src_msize, ch->ctl_dst_msize,
|
|
ch->ctl_sinc, ch->ctl_dinc, ch->ctl_src_tr_width, ch->ctl_dst_tr_width,
|
|
0);
|
|
|
|
dma_ctl_h__block_ts__setf(ch_index, ch->ctl_block_ts);
|
|
|
|
// Set the config register
|
|
dma_cfg_l__hs_sel_src__setf(ch_index, ch->cfg_hs_sel_src);
|
|
dma_cfg_l__hs_sel_dst__setf(ch_index, ch->cfg_hs_sel_dst);
|
|
|
|
dma_cfg_h_pack(ch_index, (ch->cfg_dst_per & 0xf), (ch->cfg_src_per & 0xf), (ch->cfg_fifo_mode & 0x1));
|
|
|
|
// set the SAR/DAR registers
|
|
dma_sar_set(ch_index, ch->sar);
|
|
dma_dar_set(ch_index, ch->dar);
|
|
};
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_set_listener
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE bool xc_dma_set_listener(eDMA_Ch_Num ch_num,
|
|
DMA_Callback userFunction)
|
|
{
|
|
uint8_t ch_index;
|
|
DMA_Instance_t *inst = &DMA_Instance;
|
|
|
|
ch_index = xc_dma_get_channel_index(ch_num);
|
|
|
|
// Allow only ONE channel to be specified
|
|
if (ch_index == DMA_MAX_CHANNELS)
|
|
return false;
|
|
|
|
// Check that the specified channel is in range
|
|
if (ch_num & (~DMA_ALL_CHANNELS))
|
|
return false;
|
|
|
|
inst->Ch[ch_index].userListener = userFunction;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief xc_dma_start_transfer
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE uint8_t xc_dma_start_transfer(eDMA_Ch_Num ch_num, DMA_Callback cb_func)
|
|
{
|
|
uint8_t errorCode;
|
|
uint8_t ch_index;
|
|
|
|
DMA_Instance_t *inst = &DMA_Instance;
|
|
// Update the channel instance
|
|
ch_index = xc_dma_get_channel_index(ch_num);
|
|
|
|
// Allow only ONE channel to be specified
|
|
if (ch_index == DMA_MAX_CHANNELS) {
|
|
errorCode = DMA_ECHRNG;
|
|
} else {
|
|
// Check for valid channel number and not busy
|
|
errorCode = xc_dma_check_channel_busy(ch_num);
|
|
}
|
|
|
|
if (errorCode == 0) {
|
|
// Disable the channels interrupts
|
|
errorCode = xc_dma_disable_channel_irq(ch_num);
|
|
}
|
|
|
|
if (errorCode == 0) {
|
|
// set the call back function, the number of blocks
|
|
// in the transfer and the source and destination states.
|
|
inst->Ch[ch_index].userCallback = cb_func;
|
|
|
|
// always want to unmask the tfr, block and err interrupts
|
|
xc_dma_unmask_irq(ch_num, DMA_IRQ_TRF);
|
|
xc_dma_unmask_irq(ch_num, DMA_IRQ_ERR);
|
|
|
|
// Enable the channel interrupts for the type of transfer
|
|
errorCode = xc_dma_enable_channel_irq(ch_num);
|
|
|
|
if (errorCode == 0) {
|
|
// Enable the DMA channel
|
|
errorCode = xc_dma_enable_channel(ch_num);
|
|
}
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
/**
|
|
* @brief dma_irq_handler
|
|
* @details
|
|
*
|
|
* @param
|
|
* @param
|
|
* @return void
|
|
* @retval void
|
|
* @retval void
|
|
* @note
|
|
*/
|
|
__RAM_CODE bool dma_irq_handler(DMA_Instance_t *inst)
|
|
{
|
|
bool retval;
|
|
uint8_t i, ch_index, callbackArg;
|
|
uint32_t reg, mask;
|
|
DMA_Callback userCallback;
|
|
|
|
// Assume an interrupt will be processed. The return value will be
|
|
// set to false if an active interrupt is not found.
|
|
retval = true;
|
|
|
|
// ERR INTERRUPT
|
|
if (dma_status_int__err__getf( )) {
|
|
reg = dma_status_err_get( );
|
|
|
|
// Loop through the channels until we find
|
|
// active interrupt. We start at the highest priority
|
|
// channel and work down to the lowest priority.
|
|
for (i = 0; i < DMA_MAX_CHANNELS; i++) {
|
|
mask = 1 << inst->Ch_Order[i];
|
|
if (reg & mask) {
|
|
ch_index = inst->Ch_Order[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// run the listener function
|
|
if (inst->Ch[ch_index].userListener != NULL) {
|
|
userCallback = inst->Ch[ch_index].userListener;
|
|
callbackArg = DMA_IRQ_ERR;
|
|
userCallback(callbackArg);
|
|
}
|
|
|
|
// clear the interrupt
|
|
dma_clear_err_set(mask);
|
|
}
|
|
// TFR INTERRUPT
|
|
else if (dma_status_int__tfr__getf( )) {
|
|
reg = dma_status_tfr_get( );
|
|
|
|
// Loop through the channels until we find
|
|
// active interrupt. We start at the highest priority
|
|
// channel and work down to the lowest priority.
|
|
for (i = 0; i < DMA_MAX_CHANNELS; i++) {
|
|
mask = 1 << inst->Ch_Order[i];
|
|
if (reg & mask) {
|
|
ch_index = inst->Ch_Order[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Disable all channel interrupts
|
|
xc_dma_disable_channel_irq(inst->Ch[ch_index].ch_num);
|
|
|
|
// clear any pending block/srcTran/dstTran interrupts
|
|
dma_clear_block_set(mask);
|
|
dma_clear_src_tran_set(mask);
|
|
dma_clear_dst_tran_set(mask);
|
|
xc_dma_clear_irq(inst->Ch[ch_index].ch_num, DMA_IRQ_TRF);
|
|
|
|
// Mask all channel interrupts
|
|
xc_dma_mask_irq(inst->Ch[ch_index].ch_num, DMA_IRQ_ALL);
|
|
|
|
// run the callback function
|
|
if (inst->Ch[ch_index].userCallback != NULL) {
|
|
userCallback = inst->Ch[ch_index].userCallback;
|
|
callbackArg = 0;
|
|
userCallback(callbackArg);
|
|
}
|
|
|
|
} else {
|
|
// If we've reached this point, either the enabling and
|
|
// disabling of DW_ahb_dmac interrupts is not being handled
|
|
// properly or this function is being called unnecessarily.
|
|
retval = false;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
__RAM_CODE void DMA_Handler(void) { dma_irq_handler(&DMA_Instance); }
|
|
|
|
#endif // XC_CHECK(XC_DMA_ENABLED)
|