From d3a73cdb0eda205414f181835ec8be795ead81e1 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 7 Jun 2023 17:43:15 +0800 Subject: [PATCH] drivers: dma: Add Andestech atcdmac300 driver. Support the Andes atcdmac300 dma driver. Signed-off-by: Kevin Wang --- .../riscv/adp_xc7k_ae350/adp_xc7k_ae350.dts | 4 + .../riscv/adp_xc7k_ae350/adp_xc7k_ae350.yaml | 1 + drivers/dma/CMakeLists.txt | 1 + drivers/dma/Kconfig | 2 + drivers/dma/Kconfig.andes_atcdmac300 | 12 + drivers/dma/dma_andes_atcdmac300.c | 536 ++++++++++++++++++ dts/bindings/dma/andestech,atcdmac300.yaml | 84 +++ dts/riscv/andes/andes_v5_ae350.dtsi | 10 + .../boards/adp_xc7k_ae350.conf | 1 + .../boards/adp_xc7k_ae350.overlay | 9 + .../loop_transfer/boards/adp_xc7k_ae350.conf | 1 + .../boards/adp_xc7k_ae350.overlay | 9 + 12 files changed, 670 insertions(+) create mode 100644 drivers/dma/Kconfig.andes_atcdmac300 create mode 100644 drivers/dma/dma_andes_atcdmac300.c create mode 100644 dts/bindings/dma/andestech,atcdmac300.yaml create mode 100644 tests/drivers/dma/chan_blen_transfer/boards/adp_xc7k_ae350.conf create mode 100644 tests/drivers/dma/chan_blen_transfer/boards/adp_xc7k_ae350.overlay create mode 100644 tests/drivers/dma/loop_transfer/boards/adp_xc7k_ae350.conf create mode 100644 tests/drivers/dma/loop_transfer/boards/adp_xc7k_ae350.overlay diff --git a/boards/riscv/adp_xc7k_ae350/adp_xc7k_ae350.dts b/boards/riscv/adp_xc7k_ae350/adp_xc7k_ae350.dts index bc39000962b..58c91613057 100644 --- a/boards/riscv/adp_xc7k_ae350/adp_xc7k_ae350.dts +++ b/boards/riscv/adp_xc7k_ae350/adp_xc7k_ae350.dts @@ -211,3 +211,7 @@ &wdt { status = "okay"; }; + +&dma0 { + status = "okay"; +}; diff --git a/boards/riscv/adp_xc7k_ae350/adp_xc7k_ae350.yaml b/boards/riscv/adp_xc7k_ae350/adp_xc7k_ae350.yaml index 6d5233e3cd8..5716d7e6e13 100644 --- a/boards/riscv/adp_xc7k_ae350/adp_xc7k_ae350.yaml +++ b/boards/riscv/adp_xc7k_ae350/adp_xc7k_ae350.yaml @@ -15,6 +15,7 @@ supported: - watchdog - mbox - flash + - dma testing: ignore_tags: - bluetooth diff --git a/drivers/dma/CMakeLists.txt b/drivers/dma/CMakeLists.txt index 5a11cd7f324..98e5642c904 100644 --- a/drivers/dma/CMakeLists.txt +++ b/drivers/dma/CMakeLists.txt @@ -34,3 +34,4 @@ zephyr_library_sources_ifdef(CONFIG_DMA_XMC4XXX dma_xmc4xxx.c) zephyr_library_sources_ifdef(CONFIG_DMA_RPI_PICO dma_rpi_pico.c) zephyr_library_sources_ifdef(CONFIG_MCUX_PXP dma_mcux_pxp.c) zephyr_library_sources_ifdef(CONFIG_DMA_MCUX_SMARTDMA dma_mcux_smartdma.c) +zephyr_library_sources_ifdef(CONFIG_DMA_ANDES_ATCDMAC300 dma_andes_atcdmac300.c) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 37cd83383d8..71059a0e27f 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -64,4 +64,6 @@ source "drivers/dma/Kconfig.mcux_pxp" source "drivers/dma/Kconfig.mcux_smartdma" +source "drivers/dma/Kconfig.andes_atcdmac300" + endif # DMA diff --git a/drivers/dma/Kconfig.andes_atcdmac300 b/drivers/dma/Kconfig.andes_atcdmac300 new file mode 100644 index 00000000000..cb1133cdeb5 --- /dev/null +++ b/drivers/dma/Kconfig.andes_atcdmac300 @@ -0,0 +1,12 @@ +# Andestech ATCDMAC300 configuration options +# Copyright (c) 2023 Andes Technology Corporation. + +# SPDX-License-Identifier: Apache-2.0 + + +config DMA_ANDES_ATCDMAC300 + bool "Using Andes ATCDMAC300 DMA driver" + default y + depends on DT_HAS_ANDESTECH_ATCDMAC300_ENABLED + help + Andes ATCDMAC300 DMA driver. diff --git a/drivers/dma/dma_andes_atcdmac300.c b/drivers/dma/dma_andes_atcdmac300.c new file mode 100644 index 00000000000..ef90868f943 --- /dev/null +++ b/drivers/dma/dma_andes_atcdmac300.c @@ -0,0 +1,536 @@ +/* + * Copyright (c) 2023 Andes Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DT_DRV_COMPAT andestech_atcdmac300 + +#define LOG_LEVEL CONFIG_DMA_LOG_LEVEL +#include +LOG_MODULE_REGISTER(dma_andes_atcdmac300); + +#define ATCDMAC100_MAX_CHAN 8 + +#define DMA_ABORT(dev) (((struct dma_atcdmac300_cfg *)dev->config)->base + 0x24) +#define DMA_INT_STATUS(dev) \ + (((struct dma_atcdmac300_cfg *)dev->config)->base + 0x30) + +#define DMA_CH_OFFSET(ch) (ch * 0x20) +#define DMA_CH_CTRL(dev, ch) \ + (((struct dma_atcdmac300_cfg *)dev->config)->base + 0x40 + DMA_CH_OFFSET(ch)) +#define DMA_CH_TRANSIZE(dev, ch) \ + (((struct dma_atcdmac300_cfg *)dev->config)->base + 0x44 + DMA_CH_OFFSET(ch)) +#define DMA_CH_SRC_ADDR_L(dev, ch) \ + (((struct dma_atcdmac300_cfg *)dev->config)->base + 0x48 + DMA_CH_OFFSET(ch)) +#define DMA_CH_SRC_ADDR_H(dev, ch) \ + (((struct dma_atcdmac300_cfg *)dev->config)->base + 0x4C + DMA_CH_OFFSET(ch)) +#define DMA_CH_DST_ADDR_L(dev, ch) \ + (((struct dma_atcdmac300_cfg *)dev->config)->base + 0x50 + DMA_CH_OFFSET(ch)) +#define DMA_CH_DST_ADDR_H(dev, ch) \ + (((struct dma_atcdmac300_cfg *)dev->config)->base + 0x54 + DMA_CH_OFFSET(ch)) +#define DMA_CH_LL_PTR_L(dev, ch) \ + (((struct dma_atcdmac300_cfg *)dev->config)->base + 0x58 + DMA_CH_OFFSET(ch)) +#define DMA_CH_LL_PTR_H(dev, ch) \ + (((struct dma_atcdmac300_cfg *)dev->config)->base + 0x5C + DMA_CH_OFFSET(ch)) + +/* Source burst size options */ +#define DMA_BSIZE_1 (0) +#define DMA_BSIZE_2 (1) +#define DMA_BSIZE_4 (2) +#define DMA_BSIZE_8 (3) +#define DMA_BSIZE_16 (4) +#define DMA_BSIZE_32 (5) +#define DMA_BSIZE_64 (6) +#define DMA_BSIZE_128 (7) +#define DMA_BSIZE_256 (8) +#define DMA_BSIZE_512 (9) +#define DMA_BSIZE_1024 (10) + +/* Source/Destination transfer width options */ +#define DMA_WIDTH_BYTE (0) +#define DMA_WIDTH_HALFWORD (1) +#define DMA_WIDTH_WORD (2) +#define DMA_WIDTH_DWORD (3) +#define DMA_WIDTH_QWORD (4) +#define DMA_WIDTH_EWORD (5) + +/* Bus interface index */ +#define DMA_INF_IDX0 (0) +#define DMA_INF_IDX1 (1) + +/* DMA Channel Control Register Definition */ +#define DMA_CH_CTRL_SBINF_MASK BIT(31) +#define DMA_CH_CTRL_DBINF_MASK BIT(30) +#define DMA_CH_CTRL_PRIORITY_HIGH BIT(29) +#define DMA_CH_CTRL_SBSIZE_MASK BIT(24) +#define DMA_CH_CTRL_SBSIZE(n) FIELD_PREP(DMA_CH_CTRL_SBSIZE_MASK, (n)) +#define DMA_CH_CTRL_SWIDTH_MASK GENMASK(23, 21) +#define DMA_CH_CTRL_SWIDTH(n) FIELD_PREP(DMA_CH_CTRL_SWIDTH_MASK, (n)) +#define DMA_CH_CTRL_DWIDTH_MASK GENMASK(20, 18) +#define DMA_CH_CTRL_DWIDTH(n) FIELD_PREP(DMA_CH_CTRL_DWIDTH_MASK, (n)) +#define DMA_CH_CTRL_SMODE_HANDSHAKE BIT(17) +#define DMA_CH_CTRL_DMODE_HANDSHAKE BIT(17) +#define DMA_CH_CTRL_SRCADDRCTRL_MASK GENMASK(15, 14) +#define DMA_CH_CTRL_SRCADDR_INC FIELD_PREP(DMA_CH_CTRL_DWIDTH_MASK, (0)) +#define DMA_CH_CTRL_SRCADDR_DEC FIELD_PREP(DMA_CH_CTRL_DWIDTH_MASK, (1)) +#define DMA_CH_CTRL_SRCADDR_FIX FIELD_PREP(DMA_CH_CTRL_DWIDTH_MASK, (2)) +#define DMA_CH_CTRL_DSTADDRCTRL_MASK GENMASK(13, 12) +#define DMA_CH_CTRL_DSTADDR_INC FIELD_PREP(DMA_CH_CTRL_DSTADDRCTRL_MASK, (0)) +#define DMA_CH_CTRL_DSTADDR_DEC FIELD_PREP(DMA_CH_CTRL_DSTADDRCTRL_MASK, (1)) +#define DMA_CH_CTRL_DSTADDR_FIX FIELD_PREP(DMA_CH_CTRL_DSTADDRCTRL_MASK, (2)) +#define DMA_CH_CTRL_SRCREQ_MASK GENMASK(11, 8) +#define DMA_CH_CTRL_SRCREQ(n) FIELD_PREP(DMA_CH_CTRL_SRCREQ_MASK, (n)) +#define DMA_CH_CTRL_DSTREQ_MASK GENMASK(7, 4) +#define DMA_CH_CTRL_DSTREQ(n) FIELD_PREP(DMA_CH_CTRL_DSTREQ_MASK, (n)) +#define DMA_CH_CTRL_INTABT BIT(3) +#define DMA_CH_CTRL_INTERR BIT(2) +#define DMA_CH_CTRL_INTTC BIT(1) +#define DMA_CH_CTRL_ENABLE BIT(0) + +/* DMA Interrupt Status Register Definition */ +#define DMA_INT_STATUS_TC_MASK GENMASK(23, 16) +#define DMA_INT_STATUS_ABORT_MASK GENMASK(15, 8) +#define DMA_INT_STATUS_ERROR_MASK GENMASK(7, 0) +#define DMA_INT_STATUS_TC_VAL(x) FIELD_GET(DMA_INT_STATUS_TC_MASK, (x)) +#define DMA_INT_STATUS_ABORT_VAL(x) FIELD_GET(DMA_INT_STATUS_ABORT_MASK, (x)) +#define DMA_INT_STATUS_ERROR_VAL(x) FIELD_GET(DMA_INT_STATUS_ERROR_MASK, (x)) +#define DMA_INT_STATUS_CH_MSK(ch) (0x111 << ch) + +typedef void (*atcdmac300_cfg_func_t)(void); + +struct chain_block { + uint32_t ctrl; + uint32_t transize; + uint32_t srcaddrl; + uint32_t srcaddrh; + uint32_t dstaddrl; + uint32_t dstaddrh; + uint32_t llpointerl; + uint32_t llpointerh; +#if __riscv_xlen == 32 + uint32_t reserved; +#endif + struct chain_block *next_block; +}; + +/* data for each DMA channel */ +struct dma_chan_data { + void *blkuser_data; + dma_callback_t blkcallback; + struct chain_block *head_block; + struct dma_status status; +}; + +/* Device run time data */ +struct dma_atcdmac300_data { + struct dma_chan_data chan[ATCDMAC100_MAX_CHAN]; + struct k_spinlock lock; +}; + +/* Device constant configuration parameters */ +struct dma_atcdmac300_cfg { + atcdmac300_cfg_func_t irq_config; + uint32_t base; + uint32_t irq_num; +}; + +static struct __aligned(64) + chain_block dma_chain[ATCDMAC100_MAX_CHAN][sizeof(struct chain_block) * 16]; + +static void dma_atcdmac300_isr(const struct device *dev) +{ + uint32_t int_status, int_ch_status, channel; + struct dma_atcdmac300_data *const data = dev->data; + struct dma_chan_data *ch_data; + k_spinlock_key_t key; + + key = k_spin_lock(&data->lock); + int_status = sys_read32(DMA_INT_STATUS(dev)); + /* Clear interrupt*/ + sys_write32(int_status, DMA_INT_STATUS(dev)); + + k_spin_unlock(&data->lock, key); + + /* Handle terminal count status */ + int_ch_status = DMA_INT_STATUS_TC_VAL(int_status); + while (int_ch_status) { + channel = find_msb_set(int_ch_status) - 1; + int_ch_status &= ~(BIT(channel)); + + ch_data = &data->chan[channel]; + if (ch_data->blkcallback) { + ch_data->blkcallback(dev, ch_data->blkuser_data, channel, 0); + } + data->chan[channel].status.busy = false; + } + + /* Handle error status */ + int_ch_status = DMA_INT_STATUS_ERROR_VAL(int_status); + while (int_ch_status) { + channel = find_msb_set(int_ch_status) - 1; + int_ch_status &= ~(BIT(channel)); + + ch_data = &data->chan[channel]; + if (ch_data->blkcallback) { + ch_data->blkcallback(dev, ch_data->blkuser_data, channel, -EIO); + } + } +} + +static int dma_atcdmac300_config(const struct device *dev, uint32_t channel, + struct dma_config *cfg) +{ + struct dma_atcdmac300_data *const data = dev->data; + uint32_t src_width, dst_width, src_burst_size, ch_ctrl, tfr_size; + int32_t ret = 0; + struct dma_block_config *cfg_blocks; + k_spinlock_key_t key; + + if (channel >= ATCDMAC100_MAX_CHAN) { + return -EINVAL; + } + + __ASSERT_NO_MSG(cfg->source_data_size == cfg->dest_data_size); + __ASSERT_NO_MSG(cfg->source_burst_length == cfg->dest_burst_length); + + if (cfg->source_data_size != 1 && cfg->source_data_size != 2 && + cfg->source_data_size != 4) { + LOG_ERR("Invalid 'source_data_size' value"); + ret = -EINVAL; + goto end; + } + + cfg_blocks = cfg->head_block; + if (cfg_blocks == NULL) { + ret = -EINVAL; + goto end; + } + + tfr_size = cfg_blocks->block_size/cfg->source_data_size; + if (tfr_size == 0) { + ret = -EINVAL; + goto end; + } + + ch_ctrl = 0; + + switch (cfg->channel_direction) { + case MEMORY_TO_MEMORY: + break; + case MEMORY_TO_PERIPHERAL: + ch_ctrl |= DMA_CH_CTRL_DSTREQ(cfg->dma_slot); + ch_ctrl |= DMA_CH_CTRL_DMODE_HANDSHAKE; + break; + case PERIPHERAL_TO_MEMORY: + ch_ctrl |= DMA_CH_CTRL_SRCREQ(cfg->dma_slot); + ch_ctrl |= DMA_CH_CTRL_SMODE_HANDSHAKE; + break; + default: + ret = -EINVAL; + goto end; + } + + + switch (cfg_blocks->source_addr_adj) { + case DMA_ADDR_ADJ_INCREMENT: + ch_ctrl |= DMA_CH_CTRL_SRCADDR_INC; + break; + case DMA_ADDR_ADJ_DECREMENT: + ch_ctrl |= DMA_CH_CTRL_SRCADDR_DEC; + break; + case DMA_ADDR_ADJ_NO_CHANGE: + ch_ctrl |= DMA_CH_CTRL_SRCADDR_FIX; + break; + default: + ret = -EINVAL; + goto end; + } + + switch (cfg_blocks->dest_addr_adj) { + case DMA_ADDR_ADJ_INCREMENT: + ch_ctrl |= DMA_CH_CTRL_DSTADDR_INC; + break; + case DMA_ADDR_ADJ_DECREMENT: + ch_ctrl |= DMA_CH_CTRL_DSTADDR_DEC; + break; + case DMA_ADDR_ADJ_NO_CHANGE: + ch_ctrl |= DMA_CH_CTRL_DSTADDR_FIX; + break; + default: + ret = -EINVAL; + goto end; + } + + ch_ctrl |= DMA_CH_CTRL_INTABT; + + /* Disable the error callback */ + if (!cfg->error_callback_en) { + ch_ctrl |= DMA_CH_CTRL_INTERR; + } + + src_width = find_msb_set(cfg->source_data_size) - 1; + dst_width = find_msb_set(cfg->dest_data_size) - 1; + src_burst_size = find_msb_set(cfg->source_burst_length) - 1; + + ch_ctrl |= DMA_CH_CTRL_SWIDTH(src_width) | + DMA_CH_CTRL_DWIDTH(dst_width) | + DMA_CH_CTRL_SBSIZE(src_burst_size); + + + /* Reset DMA channel configuration */ + sys_write32(0, DMA_CH_CTRL(dev, channel)); + + key = k_spin_lock(&data->lock); + /* Clear DMA interrupts status */ + sys_write32(DMA_INT_STATUS_CH_MSK(channel), DMA_INT_STATUS(dev)); + k_spin_unlock(&data->lock, key); + + /* Set transfer size */ + sys_write32(tfr_size, DMA_CH_TRANSIZE(dev, channel)); + + /* Update the status of channel */ + data->chan[channel].status.dir = cfg->channel_direction; + data->chan[channel].status.pending_length = cfg->source_data_size; + + /* Configure a callback appropriately depending on whether the + * interrupt is requested at the end of transaction completion or + * at the end of each block. + */ + data->chan[channel].blkcallback = cfg->dma_callback; + data->chan[channel].blkuser_data = cfg->user_data; + + sys_write32(ch_ctrl, DMA_CH_CTRL(dev, channel)); + + /* Set source and destination address */ + sys_write32(cfg_blocks->source_address, + DMA_CH_SRC_ADDR_L(dev, channel)); + sys_write32(0, DMA_CH_SRC_ADDR_H(dev, channel)); + sys_write32(cfg_blocks->dest_address, + DMA_CH_DST_ADDR_L(dev, channel)); + sys_write32(0, DMA_CH_DST_ADDR_H(dev, channel)); + + if (cfg->dest_chaining_en == 1 && cfg_blocks->next_block) { + uint32_t current_block_idx = 0; + + sys_write32((uint32_t)((long)&dma_chain[channel][current_block_idx]), + DMA_CH_LL_PTR_L(dev, channel)); + sys_write32(0, DMA_CH_LL_PTR_H(dev, channel)); + + for (cfg_blocks = cfg_blocks->next_block; cfg_blocks != NULL; + cfg_blocks = cfg_blocks->next_block) { + + ch_ctrl &= ~(DMA_CH_CTRL_SRCADDRCTRL_MASK | + DMA_CH_CTRL_DSTADDRCTRL_MASK); + + switch (cfg_blocks->source_addr_adj) { + case DMA_ADDR_ADJ_INCREMENT: + ch_ctrl |= DMA_CH_CTRL_SRCADDR_INC; + break; + case DMA_ADDR_ADJ_DECREMENT: + ch_ctrl |= DMA_CH_CTRL_SRCADDR_DEC; + break; + case DMA_ADDR_ADJ_NO_CHANGE: + ch_ctrl |= DMA_CH_CTRL_SRCADDR_FIX; + break; + default: + ret = -EINVAL; + goto end; + } + + switch (cfg_blocks->dest_addr_adj) { + case DMA_ADDR_ADJ_INCREMENT: + ch_ctrl |= DMA_CH_CTRL_DSTADDR_INC; + break; + case DMA_ADDR_ADJ_DECREMENT: + ch_ctrl |= DMA_CH_CTRL_DSTADDR_DEC; + break; + case DMA_ADDR_ADJ_NO_CHANGE: + ch_ctrl |= DMA_CH_CTRL_DSTADDR_FIX; + break; + default: + ret = -EINVAL; + goto end; + } + dma_chain[channel][current_block_idx].ctrl = ch_ctrl; + dma_chain[channel][current_block_idx].transize = + cfg_blocks->block_size/cfg->source_data_size; + + dma_chain[channel][current_block_idx].srcaddrl = + (uint32_t)cfg_blocks->source_address; + dma_chain[channel][current_block_idx].srcaddrh = 0x0; + + dma_chain[channel][current_block_idx].dstaddrl = + (uint32_t)((long)cfg_blocks->dest_address); + dma_chain[channel][current_block_idx].dstaddrh = 0x0; + + if (cfg_blocks->next_block) { + dma_chain[channel][current_block_idx].llpointerl = + (uint32_t)&dma_chain[channel][current_block_idx + 1]; + dma_chain[channel][current_block_idx].llpointerh = 0x0; + + current_block_idx = current_block_idx + 1; + + } else { + dma_chain[channel][current_block_idx].llpointerl = 0x0; + dma_chain[channel][current_block_idx].llpointerh = 0x0; + dma_chain[channel][current_block_idx].next_block = NULL; + } + } + } else { + /* Single transfer is supported, but Chain transfer is still + * not supported. Therefore, set LLPointer to zero + */ + sys_write32(0, DMA_CH_LL_PTR_L(dev, channel)); + sys_write32(0, DMA_CH_LL_PTR_H(dev, channel)); + } + +end: + return ret; +} + +static int dma_atcdmac300_reload(const struct device *dev, uint32_t channel, + uint32_t src, uint32_t dst, size_t size) +{ + uint32_t src_width; + + if (channel >= ATCDMAC100_MAX_CHAN) { + return -EINVAL; + } + + /* Set source and destination address */ + sys_write32(src, DMA_CH_SRC_ADDR_L(dev, channel)); + sys_write32(0, DMA_CH_SRC_ADDR_H(dev, channel)); + sys_write32(dst, DMA_CH_DST_ADDR_L(dev, channel)); + sys_write32(0, DMA_CH_DST_ADDR_H(dev, channel)); + + src_width = FIELD_GET(DMA_CH_CTRL_SWIDTH_MASK, sys_read32(DMA_CH_CTRL(dev, channel))); + src_width = BIT(src_width); + + /* Set transfer size */ + sys_write32(size/src_width, DMA_CH_TRANSIZE(dev, channel)); + + return 0; +} + +static int dma_atcdmac300_transfer_start(const struct device *dev, + uint32_t channel) +{ + struct dma_atcdmac300_data *const data = dev->data; + + if (channel >= ATCDMAC100_MAX_CHAN) { + return -EINVAL; + } + + sys_write32(sys_read32(DMA_CH_CTRL(dev, channel)) | DMA_CH_CTRL_ENABLE, + DMA_CH_CTRL(dev, channel)); + + data->chan[channel].status.busy = true; + + return 0; +} + +static int dma_atcdmac300_transfer_stop(const struct device *dev, + uint32_t channel) +{ + struct dma_atcdmac300_data *const data = dev->data; + k_spinlock_key_t key; + + if (channel >= ATCDMAC100_MAX_CHAN) { + return -EINVAL; + } + + key = k_spin_lock(&data->lock); + + sys_write32(BIT(channel), DMA_ABORT(dev)); + sys_write32(0, DMA_CH_CTRL(dev, channel)); + sys_write32(FIELD_GET(DMA_INT_STATUS_ABORT_MASK, (channel)), DMA_INT_STATUS(dev)); + data->chan[channel].status.busy = false; + + k_spin_unlock(&data->lock, key); + + return 0; +} + +static int dma_atcdmac300_init(const struct device *dev) +{ + const struct dma_atcdmac300_cfg *const config = (struct dma_atcdmac300_cfg *)dev->config; + uint32_t ch_num; + + /* Disable all channels and Channel interrupts */ + for (ch_num = 0; ch_num < ATCDMAC100_MAX_CHAN; ch_num++) { + sys_write32(0, DMA_CH_CTRL(dev, ch_num)); + } + + sys_write32(0xFFFFFF, DMA_INT_STATUS(dev)); + + /* Configure interrupts */ + config->irq_config(); + + irq_enable(config->irq_num); + + return 0; +} + +static int dma_atcdmac300_get_status(const struct device *dev, + uint32_t channel, + struct dma_status *stat) +{ + struct dma_atcdmac300_data *const data = dev->data; + + stat->busy = data->chan[channel].status.busy; + stat->dir = data->chan[channel].status.dir; + stat->pending_length = data->chan[channel].status.pending_length; + + return 0; +} + +static const struct dma_driver_api dma_atcdmac300_api = { + .config = dma_atcdmac300_config, + .reload = dma_atcdmac300_reload, + .start = dma_atcdmac300_transfer_start, + .stop = dma_atcdmac300_transfer_stop, + .get_status = dma_atcdmac300_get_status +}; + +#define ATCDMAC300_INIT(n) \ + \ + static void dma_atcdmac300_irq_config_##n(void); \ + \ + static const struct dma_atcdmac300_cfg dma_config_##n = { \ + .irq_config = dma_atcdmac300_irq_config_##n, \ + .base = DT_INST_REG_ADDR(n), \ + .irq_num = DT_INST_IRQN(n), \ + }; \ + \ + static struct dma_atcdmac300_data dma_data_##n; \ + \ + DEVICE_DT_INST_DEFINE(0, \ + dma_atcdmac300_init, \ + NULL, \ + &dma_data_##n, \ + &dma_config_##n, \ + POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &dma_atcdmac300_api); \ + \ + static void dma_atcdmac300_irq_config_##n(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), \ + 1, \ + dma_atcdmac300_isr, \ + DEVICE_DT_INST_GET(n), \ + 0); \ + } + + +DT_INST_FOREACH_STATUS_OKAY(ATCDMAC300_INIT) diff --git a/dts/bindings/dma/andestech,atcdmac300.yaml b/dts/bindings/dma/andestech,atcdmac300.yaml new file mode 100644 index 00000000000..931cc1ffaeb --- /dev/null +++ b/dts/bindings/dma/andestech,atcdmac300.yaml @@ -0,0 +1,84 @@ +# +# Copyright (c) 2023 Andes Technology Corporation. +# +# SPDX-License-Identifier: Apache-2.0 + +compatible: "andestech,atcdmac300" + +include: dma-controller.yaml + +properties: + reg: + required: true + + interrupts: + required: true + + chain-transfer: + type: int + + "#dma-cells": + const: 3 + +dma-cells: + - channel + - slot + - channel-config + +description: | + Andes DMA controller + channel: a phandle to the DMA controller plus the following four integer cells: + 1. channel: the dma channel + 2. slot: DMA peripherial request ID + 3. channel-config: A 32bit mask specifying the DMA channel configuration + which is device dependent: + -bit 0-1 : Direction (see dma.h) + 0x0: MEM to MEM + 0x1: MEM to PERIPH + 0x2: PERIPH to MEM + 0x3: reserved for PERIPH to PERIPH + -bit 2 : Peripheral Increment Address + 0x0: no address increment between transfers + 0x1: increment address between transfers + -bit 3 : Memory Increment Address + 0x0: no address increment between transfers + 0x1: increment address between transfers + -bit 4-6 : Peripheral data size + 0x0: Byte (8 bits) + 0x1: Half-word (16 bits) + 0x2: Word (32 bits) + 0x3: Double word (64 bits) + 0x4: Quad word (128 bits) + 0x5: Eight word (256 bits) + 0x6-0x7: reserved + -bit 7-9 : Memory data size + 0x0: Byte (8 bits) + 0x1: Half-word (16 bits) + 0x2: Word (32 bits) + 0x3: Double word (64 bits) + 0x4: Quad word (128 bits) + 0x5: Eight word (256 bits) + 0x6-0x7: reserved + -bit 10 : Priority level + 0x0: lower priority + 0x1: higher priority + + examples for andes_v5_ae350 DMA instance + dma0: dma0@f0c00000 { + compatible = "andestech,atcdmac300"; + ... + dma-channels = <8>; + dma-requests = <16>; + status = "disabled"; + label = "DMA_0"; + }; + + For the client part, example for andes_ae350 DMA instance + Tx using channel 2, slot 0 + Rx using channel 3, slot 1 + spi1: spi@f0f00000 { + compatible = "andestech,atcspi200" + dmas = <&dma0 2 0 0x0129>, + <&dma0 3 1 0x012A>; + dma-names = "tx", "rx"; + }; diff --git a/dts/riscv/andes/andes_v5_ae350.dtsi b/dts/riscv/andes/andes_v5_ae350.dtsi index 1b5abf0ee8c..7af3e25ef62 100644 --- a/dts/riscv/andes/andes_v5_ae350.dtsi +++ b/dts/riscv/andes/andes_v5_ae350.dtsi @@ -275,6 +275,9 @@ reg-names = "control", "mem"; interrupts = <4 1>; interrupt-parent = <&plic0>; + dmas = <&dma0 0 0 0x009>, + <&dma0 1 1 0x00A>; + dma-names = "tx", "rx"; #address-cells = <1>; #size-cells = <0>; clock-frequency = <66000000>; @@ -287,6 +290,9 @@ reg-names = "control"; interrupts = <5 1>; interrupt-parent = <&plic0>; + dmas = <&dma0 2 2 0x009>, + <&dma0 3 3 0x00A>; + dma-names = "tx", "rx"; #address-cells = <1>; #size-cells = <0>; clock-frequency = <66000000>; @@ -299,6 +305,10 @@ interrupts = <10 1>; interrupt-parent = <&plic0>; dma-channels = <8>; + dma-requests = <16>; + chain-transfer = <1>; + #dma-cells = <3>; + status = "disabled"; }; eth0: eth@e0100000 { diff --git a/tests/drivers/dma/chan_blen_transfer/boards/adp_xc7k_ae350.conf b/tests/drivers/dma/chan_blen_transfer/boards/adp_xc7k_ae350.conf new file mode 100644 index 00000000000..c448e14811f --- /dev/null +++ b/tests/drivers/dma/chan_blen_transfer/boards/adp_xc7k_ae350.conf @@ -0,0 +1 @@ +CONFIG_NOCACHE_MEMORY=y diff --git a/tests/drivers/dma/chan_blen_transfer/boards/adp_xc7k_ae350.overlay b/tests/drivers/dma/chan_blen_transfer/boards/adp_xc7k_ae350.overlay new file mode 100644 index 00000000000..c58ffdfcc20 --- /dev/null +++ b/tests/drivers/dma/chan_blen_transfer/boards/adp_xc7k_ae350.overlay @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2023 Andes Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +test_dma0: &dma0 { + status = "okay"; +}; diff --git a/tests/drivers/dma/loop_transfer/boards/adp_xc7k_ae350.conf b/tests/drivers/dma/loop_transfer/boards/adp_xc7k_ae350.conf new file mode 100644 index 00000000000..c448e14811f --- /dev/null +++ b/tests/drivers/dma/loop_transfer/boards/adp_xc7k_ae350.conf @@ -0,0 +1 @@ +CONFIG_NOCACHE_MEMORY=y diff --git a/tests/drivers/dma/loop_transfer/boards/adp_xc7k_ae350.overlay b/tests/drivers/dma/loop_transfer/boards/adp_xc7k_ae350.overlay new file mode 100644 index 00000000000..c58ffdfcc20 --- /dev/null +++ b/tests/drivers/dma/loop_transfer/boards/adp_xc7k_ae350.overlay @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2023 Andes Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +test_dma0: &dma0 { + status = "okay"; +};