/** * @file sdrv_dma.c * @brief semidriver dma source file. * * @copyright Copyright (c) 2022 Semidrive Semiconductor. * All rights reserved. */ #include #include #include #include #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; }