From 75aa78fbd473c12bc8760f1308af70db06e2018c 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 | 3 + drivers/dma/Kconfig.andes_atcdmac300 | 12 + drivers/dma/dma_andes_atcdmac300.c | 427 ++++++++++++++++++ drivers/dma/dma_andes_atcdmac300.h | 159 +++++++ 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 + 13 files changed, 721 insertions(+) create mode 100644 drivers/dma/Kconfig.andes_atcdmac300 create mode 100644 drivers/dma/dma_andes_atcdmac300.c create mode 100644 drivers/dma/dma_andes_atcdmac300.h 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 bc39000962b4115..58c916130571abd 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 b7e792cac8acb12..4e70da4d0ef7db7 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 3cb622ae9e831a8..b9cd66e71139431 100644 --- a/drivers/dma/CMakeLists.txt +++ b/drivers/dma/CMakeLists.txt @@ -32,3 +32,4 @@ zephyr_library_sources_ifdef(CONFIG_DMA_MCHP_XEC dma_mchp_xec.c) 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_ANDES_ATCDMAC300 dma_andes_atcdmac300.c) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 5023d9af9dd3a92..38ce22b30bfd94e 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -61,4 +61,7 @@ source "drivers/dma/Kconfig.rpi_pico" source "drivers/dma/Kconfig.intel_lpss" source "drivers/dma/Kconfig.mcux_pxp" + +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 000000000000000..7de49bbf3b3a644 --- /dev/null +++ b/drivers/dma/Kconfig.andes_atcdmac300 @@ -0,0 +1,12 @@ +# Andestech ATCDMAC300 configuration options +# Copyright (c) 2021 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 000000000000000..92b471731b1fdaf --- /dev/null +++ b/drivers/dma/dma_andes_atcdmac300.c @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2023 Andes Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include "dma_andes_atcdmac300.h" + +#define DT_DRV_COMPAT andestech_atcdmac300 + +#define LOG_LEVEL CONFIG_DMA_LOG_LEVEL +#include +LOG_MODULE_REGISTER(dma_andes_atcdmac300); + +static struct 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; + + int_status = sys_read32(DMA_INT_STATUS(dev)); + + /* Clear interrupt*/ + sys_write32(int_status, DMA_INT_STATUS(dev)); + + /* 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; + } + + key = k_spin_lock(&data->chan[channel].lock); + + 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)); + + /* Clear DMA interrupts status */ + sys_write32(DMA_INT_STATUS_CH_MSK(channel), DMA_INT_STATUS(dev)); + + /* 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 &= ~(0x3 << DMA_CH_CTRL_SRCADDRCTRL_POS | + 0x3 << DMA_CH_CTRL_DSTADDRCTRL_POS); + + 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: + k_spin_unlock(&data->chan[channel].lock, key); + return ret; +} + +static int dma_atcdmac300_reload(const struct device *dev, uint32_t channel, + uint32_t src, uint32_t dst, size_t size) +{ + struct dma_atcdmac300_data *const data = dev->data; + uint32_t src_width; + k_spinlock_key_t key; + + if (channel >= ATCDMAC100_MAX_CHAN) { + return -EINVAL; + } + + key = k_spin_lock(&data->chan[channel].lock); + + /* 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 = sys_read32(DMA_CH_CTRL(dev, channel)) & DMA_CH_CTRL_SWIDTH_MASK; + src_width = (BIT(src_width >> DMA_CH_CTRL_SWIDTH_POS)); + + /* Set transfer size */ + sys_write32(size/src_width, DMA_CH_TRANSIZE(dev, channel)); + + k_spin_unlock(&data->chan[channel].lock, key); + + return 0; +} + +static int dma_atcdmac300_transfer_start(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->chan[channel].lock); + + sys_write32(sys_read32(DMA_CH_CTRL(dev, channel)) | DMA_CH_CTRL_ENABLE, + DMA_CH_CTRL(dev, channel)); + + data->chan[channel].status.busy = true; + + k_spin_unlock(&data->chan[channel].lock, key); + + 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->chan[channel].lock); + + sys_write32(BIT(channel), DMA_ABORT(dev)); + sys_write32(0, DMA_CH_CTRL(dev, channel)); + sys_write32((BIT(channel) << DMA_INT_STATUS_ABORT_POS), + DMA_INT_STATUS(dev)); + + data->chan[channel].status.busy = false; + + k_spin_unlock(&data->chan[channel].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; + k_spinlock_key_t key; + + key = k_spin_lock(&data->chan[channel].lock); + + stat->busy = data->chan[channel].status.busy; + stat->dir = data->chan[channel].status.dir; + stat->pending_length = data->chan[channel].status.pending_length; + + k_spin_unlock(&data->chan[channel].lock, key); + + 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/drivers/dma/dma_andes_atcdmac300.h b/drivers/dma/dma_andes_atcdmac300.h new file mode 100644 index 000000000000000..e68ed4dd63e8396 --- /dev/null +++ b/drivers/dma/dma_andes_atcdmac300.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2023 Andes Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_DMA_ANDES_ATCDMAC300_H_ +#define ZEPHYR_DRIVERS_DMA_ANDES_ATCDMAC300_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ATCDMAC100_MAX_CHAN 8 + +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; + 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; + struct k_spinlock lock; +}; + +/* Device run time data */ +struct dma_atcdmac300_data { + struct dma_chan_data chan[ATCDMAC100_MAX_CHAN]; +}; + +/* Device constant configuration parameters */ +struct dma_atcdmac300_cfg { + atcdmac300_cfg_func_t irq_config; + uint32_t base; + uint32_t irq_num; +}; + +#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_SBINFIDX_POS (31) +#define DMA_CH_CTRL_SBINF(n) ((n) << DMA_CH_CTRL_SBINFIDX_POS) +#define DMA_CH_CTRL_SBINF_MASK (1 << DMA_CH_CTRL_SBINFIDX_POS) +#define DMA_CH_CTRL_DBINFIDX_POS (30) +#define DMA_CH_CTRL_DBINF(n) ((n) << DMA_CH_CTRL_DBINFIDX_POS) +#define DMA_CH_CTRL_DBINF_MASK (1 << DMA_CH_CTRL_DBINFIDX_POS) +#define DMA_CH_CTRL_PRIORITY_HIGH (1 << 29) +#define DMA_CH_CTRL_SBSIZE_POS (24) +#define DMA_CH_CTRL_SBSIZE_MASK (0x0f << DMA_CH_CTRL_SBSIZE_POS) +#define DMA_CH_CTRL_SBSIZE(n) \ + (((n) << DMA_CH_CTRL_SBSIZE_POS) & DMA_CH_CTRL_SBSIZE_MASK) +#define DMA_CH_CTRL_SWIDTH_POS (21) +#define DMA_CH_CTRL_SWIDTH_MASK (0x07 << DMA_CH_CTRL_SWIDTH_POS) +#define DMA_CH_CTRL_SWIDTH(n) \ + (((n) << DMA_CH_CTRL_SWIDTH_POS) & DMA_CH_CTRL_SWIDTH_MASK) +#define DMA_CH_CTRL_DWIDTH_POS (18) +#define DMA_CH_CTRL_DWIDTH_MASK (0x07 << DMA_CH_CTRL_DWIDTH_POS) +#define DMA_CH_CTRL_DWIDTH(n) \ + (((n) << DMA_CH_CTRL_DWIDTH_POS) & DMA_CH_CTRL_DWIDTH_MASK) +#define DMA_CH_CTRL_SMODE_HANDSHAKE (1 << 17) +#define DMA_CH_CTRL_DMODE_HANDSHAKE (1 << 16) +#define DMA_CH_CTRL_SRCADDRCTRL_POS (14) +#define DMA_CH_CTRL_SRCADDRCTRL_MASK (0x03 << DMA_CH_CTRL_SRCADDRCTRL_POS) +#define DMA_CH_CTRL_SRCADDR_INC (0 << DMA_CH_CTRL_SRCADDRCTRL_POS) +#define DMA_CH_CTRL_SRCADDR_DEC (1 << DMA_CH_CTRL_SRCADDRCTRL_POS) +#define DMA_CH_CTRL_SRCADDR_FIX (2 << DMA_CH_CTRL_SRCADDRCTRL_POS) +#define DMA_CH_CTRL_DSTADDRCTRL_POS (12) +#define DMA_CH_CTRL_DSTADDRCTRL_MASK \ + (0x03 << DMA_CH_CTRL_DSTADDRCTRL_POS) +#define DMA_CH_CTRL_DSTADDR_INC (0 << DMA_CH_CTRL_DSTADDRCTRL_POS) +#define DMA_CH_CTRL_DSTADDR_DEC (1 << DMA_CH_CTRL_DSTADDRCTRL_POS) +#define DMA_CH_CTRL_DSTADDR_FIX (2 << DMA_CH_CTRL_DSTADDRCTRL_POS) +#define DMA_CH_CTRL_SRCREQ_POS (8) +#define DMA_CH_CTRL_SRCREQ_MASK (0x0F << DMA_CH_CTRL_SRCREQ_POS) +#define DMA_CH_CTRL_SRCREQ(n) \ + (((n) << DMA_CH_CTRL_SRCREQ_POS) & DMA_CH_CTRL_SRCREQ_MASK) +#define DMA_CH_CTRL_DSTREQ_POS (4) +#define DMA_CH_CTRL_DSTREQ_MASK (0x0F << DMA_CH_CTRL_DSTREQ_POS) +#define DMA_CH_CTRL_DSTREQ(n) \ + (((n) << DMA_CH_CTRL_DSTREQ_POS) & DMA_CH_CTRL_DSTREQ_MASK) +#define DMA_CH_CTRL_INTABT (1 << 3) +#define DMA_CH_CTRL_INTERR (1 << 2) +#define DMA_CH_CTRL_INTTC (1 << 1) +#define DMA_CH_CTRL_ENABLE (1 << 0) + +/* DMA Interrupt Status Register Definition */ +#define DMA_INT_STATUS_TC_POS (16) +#define DMA_INT_STATUS_ABORT_POS (8) +#define DMA_INT_STATUS_ERROR_POS (0) +#define DMA_INT_STATUS_TC_VAL(x) \ + ((x >> DMA_INT_STATUS_TC_POS) & 0xff) +#define DMA_INT_STATUS_ABORT_VAL(x) \ + ((x >> DMA_INT_STATUS_ABORT_POS) & 0xff) +#define DMA_INT_STATUS_ERROR_VAL(x) (x & 0xff) +#define DMA_INT_STATUS_CH_MSK(ch) (0x111 << ch) + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_DRIVERS_DMA_ANDES_ATCDMAC300_H_ */ diff --git a/dts/bindings/dma/andestech,atcdmac300.yaml b/dts/bindings/dma/andestech,atcdmac300.yaml new file mode 100644 index 000000000000000..931cc1ffaebb52c --- /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 4a9456520a18968..4ded84ce63a9c6b 100644 --- a/dts/riscv/andes/andes_v5_ae350.dtsi +++ b/dts/riscv/andes/andes_v5_ae350.dtsi @@ -278,6 +278,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>; @@ -290,6 +293,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>; @@ -302,6 +308,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 000000000000000..c448e14811f7bb2 --- /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 000000000000000..c58ffdfcc20dbe6 --- /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 000000000000000..c448e14811f7bb2 --- /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 000000000000000..c58ffdfcc20dbe6 --- /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"; +};