Files
E3_boot/drivers/source/dma/sdrv_dma.c
2025-11-07 10:05:24 +08:00

1023 lines
35 KiB
C

/**
* @file sdrv_dma.c
* @brief semidriver dma source file.
*
* @copyright Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*/
#include <debug.h>
#include <stdio.h>
#include <string.h>
#include <udelay/udelay.h>
#include "sdrv_dma.h"
#include "armv7-r/cache.h"
#include "dma_mux.h"
#include "irq.h"
#include "irq_num.h"
#include "param.h"
#define SDRV_DMA_LOG_INFO SSDK_INFO
#define SDRV_DMA_LOG_ERROR SSDK_ERR
#define SDRV_DMA_DEFAULT_TIMEOUT_US (5)
/* core sf0 interrupt table */
static uint32_t sdrv_dma_sf0_irq_id_tab[SDRV_DMA_SF_MAX_CHANNEL] = {
DMA_SF_DMA0_CH_0_INTR_NUM, DMA_SF_DMA0_CH_1_INTR_NUM,
DMA_SF_DMA0_CH_2_INTR_NUM, DMA_SF_DMA0_CH_3_INTR_NUM,
DMA_SF_DMA0_CH_4_INTR_NUM, DMA_SF_DMA0_CH_5_INTR_NUM,
DMA_SF_DMA0_CH_6_INTR_NUM, DMA_SF_DMA0_CH_7_INTR_NUM,
DMA_SF_DMA0_CH_8_INTR_NUM, DMA_SF_DMA0_CH_9_INTR_NUM,
DMA_SF_DMA0_CH_10_INTR_NUM, DMA_SF_DMA0_CH_11_INTR_NUM,
DMA_SF_DMA0_CH_12_INTR_NUM, DMA_SF_DMA0_CH_13_INTR_NUM,
DMA_SF_DMA0_CH_14_INTR_NUM, DMA_SF_DMA0_CH_15_INTR_NUM,
#ifdef DMA_SF_DMA0_CH_16_INTR_NUM
DMA_SF_DMA0_CH_16_INTR_NUM, DMA_SF_DMA0_CH_17_INTR_NUM,
DMA_SF_DMA0_CH_18_INTR_NUM, DMA_SF_DMA0_CH_19_INTR_NUM,
DMA_SF_DMA0_CH_20_INTR_NUM, DMA_SF_DMA0_CH_21_INTR_NUM,
DMA_SF_DMA0_CH_22_INTR_NUM, DMA_SF_DMA0_CH_23_INTR_NUM,
#endif
};
/* core sf1 interrupt table */
static uint32_t sdrv_dma_sf1_irq_id_tab[SDRV_DMA_SF_MAX_CHANNEL] = {
DMA_SF_DMA1_CH_0_INTR_NUM, DMA_SF_DMA1_CH_1_INTR_NUM,
DMA_SF_DMA1_CH_2_INTR_NUM, DMA_SF_DMA1_CH_3_INTR_NUM,
DMA_SF_DMA1_CH_4_INTR_NUM, DMA_SF_DMA1_CH_5_INTR_NUM,
DMA_SF_DMA1_CH_6_INTR_NUM, DMA_SF_DMA1_CH_7_INTR_NUM,
DMA_SF_DMA1_CH_8_INTR_NUM, DMA_SF_DMA1_CH_9_INTR_NUM,
DMA_SF_DMA1_CH_10_INTR_NUM, DMA_SF_DMA1_CH_11_INTR_NUM,
DMA_SF_DMA1_CH_12_INTR_NUM, DMA_SF_DMA1_CH_13_INTR_NUM,
DMA_SF_DMA1_CH_14_INTR_NUM, DMA_SF_DMA1_CH_15_INTR_NUM,
#ifdef DMA_SF_DMA1_CH_16_INTR_NUM
DMA_SF_DMA1_CH_16_INTR_NUM, DMA_SF_DMA1_CH_17_INTR_NUM,
DMA_SF_DMA1_CH_18_INTR_NUM, DMA_SF_DMA1_CH_19_INTR_NUM,
DMA_SF_DMA1_CH_20_INTR_NUM, DMA_SF_DMA1_CH_21_INTR_NUM,
DMA_SF_DMA1_CH_22_INTR_NUM, DMA_SF_DMA1_CH_23_INTR_NUM,
#endif
};
#ifdef APB_DMA_AP_BASE
/* core ap interrupt table */
static uint32_t sdrv_dma_ap0_irq_id_tab[SDRV_DMA_AP_MAX_CHANNEL] = {
DMA_AP_DMA_CH_0_INTR_NUM, DMA_AP_DMA_CH_1_INTR_NUM,
DMA_AP_DMA_CH_2_INTR_NUM, DMA_AP_DMA_CH_3_INTR_NUM,
DMA_AP_DMA_CH_4_INTR_NUM, DMA_AP_DMA_CH_5_INTR_NUM,
DMA_AP_DMA_CH_6_INTR_NUM, DMA_AP_DMA_CH_7_INTR_NUM,
};
#endif
static sdrv_dma_mux_param_t sdrv_dma_sf_mux_tbl[] = {
DMA_SF_PERIPHERALS_MUX_MAP};
#ifdef APB_DMA_AP_BASE
static sdrv_dma_mux_param_t sdrv_dma_ap_mux_tbl[] = {
DMA_AP_PERIPHERALS_MUX_MAP};
#endif
#define SDRV_DMA_ERROR_MASK \
(SDRV_DMA_HANDSHAKE_E2E_COR_ERR | SDRV_DMA_HANDSHAKE_E2E_UNCOR_ERR | \
SDRV_DMA_CHANNEL_FIFO_ECC_COR_ERR | \
SDRV_DMA_CHANNEL_FIFO_ECC_UNCOR_ERR | SDRV_DMA_AHB_READ_ERR | \
SDRV_DMA_AHB_WRITE_ERR | SDRV_DMA_AXI_READ_ERR | \
SDRV_DMA_AXI_WRITE_ERR | SDRV_DMA_CHANNEL_LINK_ERR | \
SDRV_DMA_MAD_CRC_ERR | SDRV_DMA_FIREWALL_RD_ERR | \
SDRV_DMA_FIREWALL_WR_ERR | SDRV_DMA_DATA_CRC_ERR | \
SDRV_DMA_AXI_OUTSTANDING_UTID_ERR | SDRV_DMA_PROGRAMING_SEQUENCE_ERR)
/**
* @brief 32-bit CRC calculation on the
* polynomial:x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
* The final result of the reverse calculation is polynomial
* 0x04c11db7L,generate a crc for every 8-bit reverse value get the table.
*
*/
static const uint32_t sdrv_dma_crc32_table[256] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4};
/**
* @brief sdrv dma crc32 function
*
* @param[in] crc initial value.
* @param[in] buf input data address.
* @param[in] len input data length.
* @return crc-32 value, if input buffer address is NULL will return 0.
*/
static uint32_t sdrv_dma_crc32(uint32_t crc, const int8_t *buf,
uint32_t len)
{
if (buf == NULL)
return 0;
do {
crc = sdrv_dma_crc32_table[((int)(crc >> 24) ^ (*buf++)) & 0xff] ^
(crc << 8);
} while (--len);
return crc;
}
/**
* @brief get periph mux id by periph address.
*
* @param[in] tbl mux id table.
* @param[in] tbl_len table length.
* @param[in] direct mux direction.
* @param[in] periph_addr periph address.
* @retval mux id.
*/
static int32_t sdrv_dma_get_channel_muxid(sdrv_dma_mux_param_t *tbl,
uint32_t tbl_len,
sdrv_dma_mux_direction_e direct,
paddr_t periph_addr)
{
int32_t mux_id = -1;
ASSERT(NULL != tbl);
for (int i = 0; i < tbl_len; i++) {
if (periph_addr == tbl[i].start_addr + tbl[i].len) {
if ((direct & tbl[i].direct) > 0) {
mux_id = tbl[i].port;
break;
}
}
}
return mux_id;
}
/**
* @brief get dma channel buffer size by loop_mode.
*
* @param[in] loop_mode loop mode.
* @param[in] xfer_len transfer length.
* @param[in] burst_len burst length.
* @retval buffer size.
*/
static uint32_t sdrv_dma_get_channel_buf_size(sdrv_dma_loop_mode_e loop_mode,
uint32_t xfer_len,
uint32_t burst_len)
{
uint32_t buf_size = 0;
uint32_t lp2_size = 0;
uint32_t lp1_cnt = 0;
uint32_t lp1_size = 0;
switch (loop_mode) {
case SDRV_DMA_LOOP_MODE_2:
lp2_size = 1u << (31 - __builtin_clz(burst_len));
buf_size = SDRV_DMA_LP2_TT(xfer_len) | SDRV_DMA_LP2_LPSIZE(lp2_size);
break;
case SDRV_DMA_LOOP_MODE_1:
if (xfer_len < burst_len) {
lp1_cnt = 0;
lp1_size = xfer_len - 1;
} else {
lp1_cnt = xfer_len / burst_len;
lp1_cnt -= 1;
lp1_size = burst_len - 1;
}
buf_size = lp1_cnt << 12 | lp1_size;
break;
case SDRV_DMA_LOOP_MODE_0:
buf_size = xfer_len - 1;
break;
default:
break;
}
return buf_size;
}
/**
* @brief get dma channel burst length.
*
* @param[in] config dma channel config.
* @retval burst length.
*/
static uint32_t
sdrv_dma_get_channel_loop_size(const sdrv_dma_channel_config_t *config)
{
ASSERT(NULL != config);
uint32_t loop_size = 0;
if (config->xfer_type == SDRV_DMA_DIR_MEM2DEV) {
loop_size = config->dst_burst_len * config->dst_width;
} else if (config->xfer_type == SDRV_DMA_DIR_DEV2MEM) {
loop_size = config->src_burst_len * config->src_width;
} else if (config->xfer_type == SDRV_DMA_DIR_MEM2MEM) {
loop_size = config->xfer_bytes;
}
return loop_size;
}
/**
* @brief set dma channel config.
*
* @param[in] desc dma channel config descriptor.
* @param[in] config dma channel config.
*/
static void
sdrv_dma_set_channel_xfer_config(sdrv_dma_linklist_descriptor_t *desc,
const sdrv_dma_channel_config_t *config)
{
ASSERT(NULL != desc);
ASSERT(NULL != config);
uint32_t burst_len = sdrv_dma_get_channel_loop_size(config);
desc->src.v = config->src_addr;
desc->dst.v = config->dst_addr;
desc->xfer_ctrl.v = 0;
desc->xfer_ctrl.src_bst_len = SDRV_DMA_BURST_LEN_16;
desc->xfer_ctrl.src_beat_size = __builtin_ctz(config->src_width);
desc->xfer_ctrl.src_port_sel = config->src_port_sel;
desc->xfer_ctrl.src_bst_type = config->src_inc;
desc->xfer_ctrl.src_stride_pol = 0;
desc->xfer_ctrl.src_protect = 0;
desc->xfer_ctrl.src_cache = config->src_cache;
desc->xfer_ctrl.dst_bst_len = SDRV_DMA_BURST_LEN_16;
desc->xfer_ctrl.dst_beat_size = __builtin_ctz(config->dst_width);
desc->xfer_ctrl.dst_port_sel = config->dst_port_sel;
desc->xfer_ctrl.dst_bst_type = config->dst_inc;
desc->xfer_ctrl.dst_stride_pol = 0;
desc->xfer_ctrl.dst_protect = 0;
desc->xfer_ctrl.dst_cache = config->dst_cache;
desc->blk_cfg.v = 0;
desc->blk_cfg.buf_size = sdrv_dma_get_channel_buf_size(
config->loop_mode, config->xfer_bytes, burst_len);
desc->blk_cfg.lop_mode = config->loop_mode;
desc->blk_cfg.buf_mode = config->buffer_mode;
desc->blk_cfg.swt_evt_ctl = config->switch_event_ctrl;
desc->blk_cfg.src_stride_en = 0;
desc->blk_cfg.dst_stride_en = 0;
desc->op_mod.v = 0;
desc->op_mod.flow_ctrl = config->xfer_type;
desc->op_mod.transfer_mode = config->xfer_mode;
desc->op_mod.mad_crc_mode = config->mad_crc_mode;
if (config->interrupt_type & SDRV_DMA_CH_HALT) {
desc->op_mod.halt_en = 1;
}
if (config->linklist_mad_type == SDRV_DMA_LINKLIST_FIRST_MAD) {
desc->op_mod.first_mad = 1;
desc->op_mod.last_mad = 0;
} else if (config->linklist_mad_type == SDRV_DMA_LINKLIST_LAST_MAD) {
desc->op_mod.first_mad = 0;
desc->op_mod.last_mad = 1;
} else {
desc->op_mod.first_mad = 0;
desc->op_mod.last_mad = 0;
}
desc->op_mod.mad_intr = 1;
desc->op_mod.trigger_mode = config->trig_mode;
desc->op_data.v = 0;
desc->mad_crc.v = 0;
desc->link_addr.v = config->linklist_addr;
}
/**
* @brief dma interrupt status handle.
*
* @param[in] status dma interrupt status.
* @retval sdrv_dma_status_e.
*/
static sdrv_dma_status_e sdrv_dma_irq_status_handle(uint32_t status)
{
sdrv_dma_status_e dma_channel_status = SDRV_DMA_PENDING;
if (status & SDRV_DMA_ERROR_MASK) {
return SDRV_DMA_ERR;
}
if (status & SDRV_DMA_CH_HALT) {
return SDRV_DMA_PAUSED;
}
if (status & SDRV_DMA_CH_ABORT) {
return SDRV_DMA_ABORT;
}
if (status & SDRV_DMA_LAST_MAD_DONE) {
return SDRV_DMA_COMPLETED;
}
if (status & SDRV_DMA_EVERY_MAD_DONE) {
return SDRV_DMA_BLOCK_DONE;
}
return dma_channel_status;
}
/**
* @brief dma interrupt callback.
*
* @param[in] irq interrupt number.
* @param[in] arg sdrv_dma_channel_t pointer.
* @retval 0.
*/
static int sdrv_dma_channel_irq_handler(uint32_t irq, void *arg)
{
ASSERT(NULL != arg);
sdrv_dma_channel_t *channel = (sdrv_dma_channel_t *)arg;
uint32_t status = sdrv_dma_get_channel_xfer_status(channel);
sdrv_dma_clear_channel_xfer_status(channel, status);
if (channel->irq_callback) {
channel->irq_callback(sdrv_dma_irq_status_handle(status), status,
channel->irq_context);
}
if ((status & SDRV_DMA_CH_HALT) != 0 && channel->channel != NULL) {
sdrv_dma_set_channel_status(channel->channel,
SDRV_DMA_CH_STATUS_RESUME);
}
return 0;
}
/**
* @brief check dma channel config.
*
* @param[in] config dma channel config pointer.
* @retval true or false.
*/
static bool
sdrv_dma_check_channel_config(const sdrv_dma_channel_config_t *config)
{
ASSERT(NULL != config);
uint32_t loop_size = sdrv_dma_get_channel_loop_size(config);
if (0 == loop_size) {
return false;
}
// check loop count and loop size
switch (config->loop_mode) {
case SDRV_DMA_LOOP_MODE_0:
if (config->xfer_bytes > SDRV_DMA_LP0_LPSIZE_MAX + 1 ||
config->xfer_bytes == 0)
return false;
break;
case SDRV_DMA_LOOP_MODE_1:
if (config->xfer_bytes > (SDRV_DMA_LP1_LPSIZE_MAX + 1) *
(SDRV_DMA_LP1_LP_COUNT_MAX + 1) ||
config->xfer_bytes == 0)
return false;
if ((config->xfer_bytes >= loop_size) &&
(config->xfer_bytes % loop_size == 0)) {
return true;
} else {
ssdk_printf(SDRV_DMA_LOG_ERROR, "xfer_bytes: %d, loop_size: %d\r\n",
config->xfer_bytes, loop_size);
return false;
}
break;
case SDRV_DMA_LOOP_MODE_2:
if (config->xfer_bytes > (SDRV_DMA_LP2_LPSIZE_MAX + 1) ||
config->xfer_bytes == 0)
return false;
if (1u << (31 - __builtin_clz(loop_size)) != loop_size) {
ssdk_printf(SDRV_DMA_LOG_ERROR, "xfer_bytes: %d, loop_size: %d\r\n",
config->xfer_bytes, loop_size);
return false;
}
break;
default:
return false;
}
return true;
}
/**
* @brief Initialize the DMA controller.It only needs to be called once after
* power-on, and does not need to be called when power is off.
*
* @param[in] base DMA controller base address.
*/
void sdrv_dma_init_dmac(paddr_t base)
{
sdrv_dma_reset_fifo((sdrv_dma_ctrl_t *)base);
sdrv_dma_reset_core_int_status((sdrv_dma_ctrl_t *)base);
}
/**
* @brief Create DMA controller instance.
*
* @param[in] dma_instance Pointer to DMA instance structure.
* @param[in] base DMA controller base address.
* @return SDRV_STATUS_OK create instance successfully.
*/
status_t sdrv_dma_create_instance(sdrv_dma_t *dma_instance, paddr_t base)
{
ASSERT(NULL != dma_instance);
dma_instance->dma = (sdrv_dma_ctrl_t *)base;
return SDRV_STATUS_OK;
}
/**
* @brief Initialize default configuration for DMA channel.
*
* @param[in] config DMA channel config to initialize.
* @param[in] dma_instance DMA controller instance.
* @return SDRV_STATUS_OK successfully.
*/
status_t sdrv_dma_init_channel_config(sdrv_dma_channel_config_t *config,
sdrv_dma_t *instance)
{
ASSERT(config != NULL);
ASSERT(instance != NULL);
const sdrv_dma_channel_config_t default_config = {
.instance = NULL, /* dma controller instance */
.channel_id = SDRV_DMA_CHANNEL_0, /* select channel */
.xfer_mode =
SDRV_DMA_TRANSFER_MODE_SINGLE, /* transfer mode
(single/circular/linklist) */
.xfer_type =
SDRV_DMA_DIR_MEM2MEM, /* transfer type (mem2mem/mem2dev/dev2mem) */
.src_addr = 0, /* source address */
.src_width = SDRV_DMA_BUSWIDTH_8_BYTES, /* source address width */
.src_inc = SDRV_DMA_ADDR_NO_INC, /* source address increase or not */
.src_burst_len = 1, /* source address burst length */
.dst_addr = 0, /* destination address */
.dst_width = SDRV_DMA_BUSWIDTH_8_BYTES, /* destination address width */
.dst_inc =
SDRV_DMA_ADDR_NO_INC, /* destination address increase or not */
.dst_burst_len = 1, /* destination address burst length */
.xfer_bytes = 0, /* transfer bytes */
.mux_id = -1, /* periph mux id */
.src_port_sel = SDRV_DMA_PORT_AXI64, /* source port select
(memory->axi64,periph->ahb32) */
.dst_port_sel = SDRV_DMA_PORT_AXI64, /* destination port select
(memory->axi64,periph->ahb32) */
.src_cache = 0, /* source cache enable */
.dst_cache = 0, /* destination cache enable */
.interrupt_type =
0, /* interrupt type
(SDRV_DMA_LAST_MAD_DONE,SDRV_DMA_EVERY_MAD_DONE...) */
.buffer_mode = SDRV_DMA_SINGLE_BUFFER, /* buffer mode */
.loop_mode = SDRV_DMA_LOOP_MODE_0, /* loop mode */
.switch_event_ctrl =
SDRV_DMA_SWT_EVT_CTL_CONTINUE_WTH_INT, /* switch event control */
.trig_mode = SDRV_DMA_TRIGGER_BY_SOFTWARE, /* xfer trigger mode */
.mad_crc_mode = SDRV_DMA_NO_MAD_CRC, /* default no mad crc */
.linklist_addr = 0, /* next linklist address */
.linklist_mad_type =
SDRV_DMA_LINKLIST_NORMAL_MAD, /* linklist type (first or last) */
};
*config = default_config;
config->instance = instance;
return SDRV_STATUS_OK;
}
/**
* @brief get dma channel shadow mad.
*
* @param[channel] dma channel.
* @param[mad] output mad infomation.
* @return SDRV_STATUS_OK successfully.
*/
status_t sdrv_dma_get_channel_mad_shadow(sdrv_dma_channel_t *channel,
sdrv_dma_linklist_descriptor_t *mad)
{
ASSERT(channel != NULL);
ASSERT(channel->channel != NULL);
ASSERT(mad != NULL);
mad->src.v = channel->channel->shadow_src.v;
mad->dst.v = channel->channel->shadow_dst.v;
mad->xfer_ctrl.v = channel->channel->shadow_xfer_ctrl.v;
mad->blk_cfg.v = channel->channel->shadow_blk_cfg.v;
mad->op_mod.v = channel->channel->shadow_op_mod.v;
mad->op_data.v = channel->channel->shadow_op_data.v;
mad->mad_crc.v = channel->channel->shadow_mad_crc.v;
mad->link_addr.v = channel->channel->shadow_link_addr.v;
return SDRV_STATUS_OK;
}
/**
* @brief set dma mad register(0x20~0x3C).
*
* @param[channel] dma channel.
* @param[mad] input mad infomation.
* @return SDRV_STATUS_OK successfully.
*/
status_t sdrv_dma_set_channel_mad(sdrv_dma_channel_t *channel,
sdrv_dma_linklist_descriptor_t *mad)
{
ASSERT(mad != NULL);
ASSERT(channel != NULL);
ASSERT(channel->channel != NULL);
channel->channel->src.v = mad->src.v;
channel->channel->dst.v = mad->dst.v;
channel->channel->xfer_ctrl.v = mad->xfer_ctrl.v;
channel->channel->blk_cfg.v = mad->blk_cfg.v;
channel->channel->op_mod.v = mad->op_mod.v;
channel->channel->op_data.v = mad->op_data.v;
channel->channel->mad_crc.v = mad->mad_crc.v;
channel->channel->link_addr.v = mad->link_addr.v;
return SDRV_STATUS_OK;
}
/**
* @brief Prepare MAD crc, It needs to be called after the linklist organization
* is completed and before DMA is enabled.
*
* @param[channel] dma channel.
* @param[init_value] crc initial value.
* @param[head] Linklist head.
* @return SDRV_STATUS_OK successfully.
*/
status_t sdrv_dma_prepare_mad_crc(sdrv_dma_channel_t* channel, uint32_t init_value,
sdrv_dma_linklist_descriptor_t *head)
{
ASSERT(head != NULL);
ASSERT(channel != NULL);
ASSERT(channel->channel != NULL);
sdrv_dma_linklist_descriptor_t *current = head;
uint32_t crc_res[SDRV_DMA_MAD_CRC_REGISTERS];
channel->channel->mad_crc_int.v = init_value;
while (1) {
// format data
if (current->op_mod.mad_crc_mode == SDRV_DMA_CHECK_CURR_MAD) {
crc_res[0] = current->link_addr.v;
crc_res[1] = current->op_data.v;
crc_res[2] = current->op_mod.v;
crc_res[3] = current->blk_cfg.v;
crc_res[4] = current->xfer_ctrl.v;
crc_res[5] = current->dst.v;
crc_res[6] = current->src.v;
} else {
return SDRV_DMA_UNSUPPORT_CONFIG;
}
init_value = sdrv_dma_crc32(init_value, (const int8_t *)crc_res,
(unsigned int)SDRV_DMA_MAD_CRC_REGISTERS *
sizeof(int32_t));
current->mad_crc.v = init_value;
arch_clean_cache_range((uint32_t)current,
sizeof(sdrv_dma_linklist_descriptor_t));
current = (sdrv_dma_linklist_descriptor_t *)(current->link_addr.v);
if (current == NULL || current == head)
break;
}
return SDRV_STATUS_OK;
}
/**
* @brief Initialize DMA channel.
*
* @param[in] channel The channel to initialize.
* @param[in] config DMA channel configuration.
* @return SDRV_DMA_INVALID_CHANNEL_ID means channel id invalid,
* SDRV_DMA_UNSUPPORT_CONFIG input config error,SDRV_STATUS_OK initialized
* successfully.
*/
status_t sdrv_dma_init_channel(sdrv_dma_channel_t *channel,
const sdrv_dma_channel_config_t *config)
{
ASSERT(channel != NULL);
ASSERT(config != NULL);
paddr_t base_addr = (paddr_t)config->instance->dma;
int32_t mux_id = -1;
sdrv_dma_mux_param_t *mux_table = NULL;
uint32_t mux_table_len = 0;
uint32_t *irq_table_ptr = NULL;
if ((base_addr == APB_DMA_SF0_BASE) || (base_addr == APB_DMA_SF1_BASE)) {
if (config->channel_id >= SDRV_DMA_SF_MAX_CHANNEL ||
config->channel_id < SDRV_DMA_CHANNEL_0) {
return SDRV_DMA_INVALID_CHANNEL_ID;
}
mux_table = &sdrv_dma_sf_mux_tbl[0];
mux_table_len = ARRAY_SIZE(sdrv_dma_sf_mux_tbl);
if (base_addr == APB_DMA_SF0_BASE) {
irq_table_ptr = &sdrv_dma_sf0_irq_id_tab[0];
} else {
irq_table_ptr = &sdrv_dma_sf1_irq_id_tab[0];
}
#ifdef APB_DMA_AP_BASE
} else if (base_addr == APB_DMA_AP_BASE) {
if (config->channel_id >= SDRV_DMA_AP_MAX_CHANNEL ||
config->channel_id < SDRV_DMA_CHANNEL_0) {
return SDRV_DMA_INVALID_CHANNEL_ID;
}
mux_table = &sdrv_dma_ap_mux_tbl[0];
mux_table_len = ARRAY_SIZE(sdrv_dma_ap_mux_tbl);
irq_table_ptr = &sdrv_dma_ap0_irq_id_tab[0];
#endif
} else if (base_addr != 0x0) {
// Some special scenarios, such as xspi slave.
mux_table = NULL;
mux_table_len = 0;
} else {
return SDRV_STATUS_INVALID_PARAM;
}
if (true != sdrv_dma_check_channel_config(config)) {
return SDRV_DMA_UNSUPPORT_CONFIG;
}
memset((void *)channel, 0, sizeof(sdrv_dma_channel_t));
channel->dma = config->instance->dma;
channel->channel_id = config->channel_id;
channel->channel =
sdrv_dma_get_channel_ctrl_base(channel->dma, channel->channel_id);
ASSERT(channel->channel != NULL);
if (config->xfer_type == SDRV_DMA_DIR_MEM2MEM) {
sdrv_dma_set_channel_rd_outstanding(channel->dma, channel->channel_id,
0x0F);
sdrv_dma_set_channel_wr_outstanding(channel->dma, channel->channel_id,
0x0F);
}
if (mux_table) {
if (config->xfer_type == SDRV_DMA_DIR_MEM2DEV) {
mux_id = sdrv_dma_get_channel_muxid(
mux_table, mux_table_len, SDRV_DMA_MUX_WR, config->dst_addr);
if (mux_id >= 0) {
sdrv_dma_set_channel_muxid(channel->dma, channel->channel_id,
mux_id);
}
} else if (config->xfer_type == SDRV_DMA_DIR_DEV2MEM) {
mux_id = sdrv_dma_get_channel_muxid(
mux_table, mux_table_len, SDRV_DMA_MUX_RD, config->src_addr);
if (mux_id >= 0) {
sdrv_dma_set_channel_muxid(channel->dma, channel->channel_id,
mux_id);
}
}
}
if (config->mux_id >= 0) {
sdrv_dma_set_channel_muxid(channel->dma, channel->channel_id,
config->mux_id);
}
sdrv_dma_linklist_descriptor_t *desc =
(sdrv_dma_linklist_descriptor_t *)&channel->channel->src.v;
sdrv_dma_set_channel_xfer_config(desc, config);
sdrv_dma_set_channel_interrupt(channel->dma, channel->channel_id,
config->interrupt_type);
if ((config->interrupt_type > 0) && (NULL != irq_table_ptr)) {
/* irq_attach and irq_enable may not take effect,
depending on whether irq_attach and irq_enable is enabled */
irq_attach(irq_table_ptr[channel->channel_id],
sdrv_dma_channel_irq_handler, channel);
irq_enable(irq_table_ptr[channel->channel_id]);
}
return SDRV_STATUS_OK;
}
/**
* @brief Start DMA transaction.
*
* @param[in] channel The DMA channel to start.
* @return SDRV_STATUS_OK start DMA channel successfully.
*/
status_t sdrv_dma_start_channel_xfer(sdrv_dma_channel_t *channel)
{
ASSERT(channel != NULL);
ASSERT(channel->channel != NULL);
channel->channel->ps_ctrl.ps_cnt = channel->channel->ps_ctrl.ps_cnt + 1;
channel->channel->ch_cfg.en = 1;
if (channel->channel->op_mod.flow_ctrl == SDRV_DMA_DIR_MEM2MEM) {
sdrv_dma_set_channel_sw_handshake(channel->channel);
}
return SDRV_STATUS_OK;
}
/**
* @brief Stop DMA transaction.
*
* @param[in] channel The DMA channel to stop.
* @return SDRV_STATUS_OK stop DMA channel successfully.
*/
status_t sdrv_dma_stop_channel_xfer(sdrv_dma_channel_t *channel)
{
ASSERT(channel != NULL);
ASSERT(channel->channel != NULL);
int32_t timeout = SDRV_DMA_DEFAULT_TIMEOUT_US;
if (channel->channel->ch_cfg.en) {
sdrv_dma_set_channel_status(channel->channel, SDRV_DMA_CH_STATUS_ABORT);
while (timeout--) {
if (!channel->channel->ch_cfg.abort)
return SDRV_STATUS_OK;
udelay(1);
}
}
return SDRV_STATUS_TIMEOUT;
}
/**
* @brief De-initialize the DMA channel.
*
* @param[in] channel The DMA channel to de-initialize.
* @return SDRV_STATUS_OK deinit DMA channel successfully.
*/
status_t sdrv_dma_deinit_channel(sdrv_dma_channel_t *channel)
{
ASSERT(channel != NULL);
ASSERT(channel->channel != NULL);
channel->channel->int_en.v = 0x0;
channel->channel->int_clr.v = 0xDFFFFFF;
channel->dma = NULL;
channel->channel = NULL;
channel->channel_id = SDRV_DMA_CHANNEL_INVALID;
channel->irq_callback = NULL;
channel->irq_context = NULL;
return SDRV_STATUS_OK;
}
/**
* @brief Initialize DMA link list descriptor.
*
* This function initializes a DMA link list descriptor, using specified
* channel configuration.
*
* @param[in] desc Link list descriptor.
* @param[in] config DMA channel config.
* @return SDRV_STATUS_OK init linklist entry successfully,
* SDRV_DMA_UNSUPPORT_CONFIG channel config unsupport.
*/
status_t sdrv_dma_init_linklist_entry(sdrv_dma_linklist_descriptor_t *desc,
const sdrv_dma_channel_config_t *config)
{
ASSERT(desc != NULL);
ASSERT(config != NULL);
if (true != sdrv_dma_check_channel_config(config)) {
return SDRV_DMA_UNSUPPORT_CONFIG;
}
sdrv_dma_set_channel_xfer_config(desc, config);
return SDRV_STATUS_OK;
}
/**
* @brief Set DMA channel source address.
*
* @param[in] channel DMA channel.
* @param[in] addr source address.
* @return SDRV_STATUS_OK update channel source address successfully.
*/
status_t sdrv_dma_set_channel_source_address(sdrv_dma_channel_t *channel,
paddr_t addr)
{
ASSERT(channel != NULL);
ASSERT(channel->channel != NULL);
channel->channel->src.v = addr;
return SDRV_STATUS_OK;
}
/**
* @brief Set DMA channel target address.
*
* @param[in] channel DMA channel.
* @param[in] addr target address.
* @return SDRV_STATUS_OK update channel detination address successfully.
*/
status_t sdrv_dma_set_channel_destination_address(sdrv_dma_channel_t *channel,
paddr_t addr)
{
ASSERT(channel != NULL);
ASSERT(channel->channel != NULL);
channel->channel->dst.v = addr;
return SDRV_STATUS_OK;
}
/**
* @brief set dma channel single transmit buffer size
*
* This is an unsafe interface. The parameters filled in need to be calculated
* by yourself, and there is no verification inside the function.
*
* @param channel dma channel ptr
* @param loop_mode loop mode
* @param total_size total bytes
* @param loop_size every handshake transmic bytes(The loop mode 0 does not take
* effect)
*
* @return
*/
void sdrv_dma_set_channel_buffer_size(sdrv_dma_channel_t *channel,
sdrv_dma_loop_mode_e loop_mode,
uint32_t total_size,
uint32_t loop_size)
{
ASSERT(channel != NULL);
ASSERT(channel->channel != NULL);
switch (loop_mode)
{
case SDRV_DMA_LOOP_MODE_2:
channel->channel->blk_cfg.lop_mode = loop_mode;
channel->channel->blk_cfg.lp2_total_len = total_size - 1;
channel->channel->blk_cfg.lp2_each_len = SDRV_DMA_LP2_LPSIZE(loop_size);
break;
case SDRV_DMA_LOOP_MODE_1:
channel->channel->blk_cfg.lop_mode = loop_mode;
channel->channel->blk_cfg.lp1_each_len = loop_size - 1;
channel->channel->blk_cfg.lp1_count = (total_size / loop_size) - 1;
break;
case SDRV_DMA_LOOP_MODE_0:
channel->channel->blk_cfg.lop_mode = loop_mode;
channel->channel->blk_cfg.buf_size = total_size - 1;
break;
}
}
/**
* @brief Set transfer length in bytes for the channel.
*
* @param[in] channel DMA channel.
* @param[in] xfer_bytes transfer bytes.
* @return SDRV_STATUS_OK update channel transfer bytes successfully.
*/
status_t sdrv_dma_set_channel_xfer_bytes(sdrv_dma_channel_t *channel,
uint32_t xfer_bytes)
{
ASSERT(channel != NULL);
ASSERT(channel->channel != NULL);
uint32_t burst_len = 0;
uint32_t buf_size = channel->channel->blk_cfg.buf_size;
// get once dma requst transfer bytes
switch (channel->channel->blk_cfg.lop_mode) {
case SDRV_DMA_LOOP_MODE_2:
if (xfer_bytes > (SDRV_DMA_LP2_LPSIZE_MAX + 1) || xfer_bytes == 0)
return SDRV_DMA_UNSUPPORT_CONFIG;
burst_len = 1 << (buf_size & 0xf);
break;
case SDRV_DMA_LOOP_MODE_1:
if (xfer_bytes > (SDRV_DMA_LP1_LPSIZE_MAX + 1) *
(SDRV_DMA_LP1_LP_COUNT_MAX + 1) ||
xfer_bytes == 0)
return SDRV_DMA_UNSUPPORT_CONFIG;
burst_len = (buf_size & 0xfff) + 1;
if (xfer_bytes < burst_len || xfer_bytes % burst_len != 0)
return SDRV_DMA_UNSUPPORT_CONFIG;
break;
case SDRV_DMA_LOOP_MODE_0:
if (xfer_bytes > (SDRV_DMA_LP0_LPSIZE_MAX + 1) || xfer_bytes == 0)
return SDRV_DMA_UNSUPPORT_CONFIG;
burst_len = buf_size + 1;
break;
default:
return SDRV_STATUS_FAIL;
}
channel->channel->blk_cfg.buf_size = sdrv_dma_get_channel_buf_size(
(sdrv_dma_loop_mode_e)channel->channel->blk_cfg.lop_mode, xfer_bytes,
burst_len);
return SDRV_STATUS_OK;
}
/**
* @brief Get number of transfered bytes for the channel
*
* @param[in] channel DMA channel.
* @return Number of transfered bytes,if channel is NULL will return 0.
*/
uint32_t sdrv_dma_get_channel_xfer_bytes(sdrv_dma_channel_t *channel)
{
if (channel && channel->channel != NULL)
return channel->channel->xferred_num.v;
else
return 0;
}
/**
* @brief Clear DMA channel transfered bytes counter.
*
* @param[in] channel DMA channel.
* @return SDRV_STATUS_BUSY dma must disable,if not clear failed,
* SDRV_STATUS_OK clear successfully.
*/
status_t sdrv_dma_clear_channel_xfer_bytes(sdrv_dma_channel_t *channel)
{
ASSERT(channel != NULL);
ASSERT(channel->channel != NULL);
if (!channel->channel->ch_cfg.en)
channel->channel->xferred_num.v = 0;
else
return SDRV_STATUS_BUSY;
return SDRV_STATUS_OK;
}
/**
* @brief Get DMA channel status.
*
* @param[in] channel DMA channel.
* @return channel interrupt status.
*/
uint32_t sdrv_dma_get_channel_xfer_status(sdrv_dma_channel_t *channel)
{
ASSERT(channel != NULL);
ASSERT(channel->channel != NULL);
if (channel && channel->channel)
return channel->channel->int_status.v;
else
return 0;
}
/**
* @brief Clear DMA channel status.
*
* @param[in] channel DMA channel.
* @param[in] status Channel status bitmask.
* @return SDRV_STATUS_OK clear successfully,
* SDRV_STATUS_INVALID_PARAM clear failed.
*/
status_t sdrv_dma_clear_channel_xfer_status(sdrv_dma_channel_t *channel,
uint32_t status)
{
ASSERT(channel != NULL);
ASSERT(channel->channel != NULL);
if (channel && channel->channel)
channel->channel->int_clr.v = status;
else
return SDRV_STATUS_INVALID_PARAM;
return SDRV_STATUS_OK;
}