diff --git a/boards/riscv/adp_xc7k_ae350/adp_xc7k_ae350.dts b/boards/riscv/adp_xc7k_ae350/adp_xc7k_ae350.dts index 8ff809f022c962f..f2a46c1e6b88009 100644 --- a/boards/riscv/adp_xc7k_ae350/adp_xc7k_ae350.dts +++ b/boards/riscv/adp_xc7k_ae350/adp_xc7k_ae350.dts @@ -180,3 +180,7 @@ status = "okay"; cs-gpios = <&gpio0 7 GPIO_ACTIVE_LOW>; }; + +&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 8a2a1031993ff3d..91cadd72f6ca37c 100644 --- a/boards/riscv/adp_xc7k_ae350/adp_xc7k_ae350.yaml +++ b/boards/riscv/adp_xc7k_ae350/adp_xc7k_ae350.yaml @@ -12,6 +12,7 @@ supported: - i2c - spi - eeprom + - dma testing: ignore_tags: - bluetooth diff --git a/drivers/dma/CMakeLists.txt b/drivers/dma/CMakeLists.txt index 8b91184cc2bbbd5..9828c302d6bdc3b 100644 --- a/drivers/dma/CMakeLists.txt +++ b/drivers/dma/CMakeLists.txt @@ -29,3 +29,4 @@ zephyr_library_sources_ifdef(CONFIG_DMA_ESP32 dma_esp32_gdma.c) 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_DMA_ANDES_ATCDMAC300 dma_andes_atcdmac300.c) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index d88d2bbd832a892..bc5a122a352ba47 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -59,4 +59,6 @@ source "drivers/dma/Kconfig.xmc4xxx" source "drivers/dma/Kconfig.rpi_pico" source "drivers/dma/Kconfig.intel_lpss" + +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..8cfe55e4c6a213d --- /dev/null +++ b/drivers/dma/Kconfig.andes_atcdmac300 @@ -0,0 +1,13 @@ +# 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 + depends on !CACHE_ENABLE || NOCACHE_MEMORY + 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..c32d6d937571707 --- /dev/null +++ b/drivers/dma/dma_andes_atcdmac300.c @@ -0,0 +1,435 @@ +/* + * 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); + +K_HEAP_DEFINE(dma_chain, 1024); +static struct chain_block *head_block[8] = {NULL}; + +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(dev); + struct dma_chan_data *ch_data; + + int_status = INWORD(DMA_INT_STATUS); + + /* Clear interrupt*/ + OUTWORD(DMA_INT_STATUS, int_status); + + /* 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 &= ~(1 << 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; + + if (head_block[channel] != NULL) { + struct chain_block *current_block = head_block[channel]->next_block; + + k_heap_free(&dma_chain, head_block[channel]); + head_block[channel] = current_block; + } + } + + /* 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 &= ~(1 << 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(dev); + 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 */ + OUTWORD(DMA_CH_CTRL(channel), 0); + + /* Clear DMA interrupts status */ + OUTWORD(DMA_INT_STATUS, DMA_INT_STATUS_CH_MSK(channel)); + + /* Set transfer size */ + OUTWORD(DMA_CH_TRANSIZE(channel), tfr_size); + + /* 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; + + OUTWORD(DMA_CH_CTRL(channel), ch_ctrl); + + /* Set source and destination address */ + OUTWORD(DMA_CH_SRC_ADDR_L(channel), cfg_blocks->source_address); + OUTWORD(DMA_CH_SRC_ADDR_H(channel), 0); + OUTWORD(DMA_CH_DST_ADDR_L(channel), cfg_blocks->dest_address); + OUTWORD(DMA_CH_DST_ADDR_H(channel), 0); + + if (cfg->dest_chaining_en == 1 && cfg_blocks->next_block) { + + head_block[channel] = k_heap_alloc(&dma_chain, + sizeof(struct chain_block), K_NO_WAIT); + struct chain_block *current_block = head_block[channel]; + + OUTWORD(DMA_CH_LL_PTR_L(channel), (uint32_t)((long)current_block)); + OUTWORD(DMA_CH_LL_PTR_H(channel), 0); + + 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; + } + current_block->ctrl = ch_ctrl; + current_block->transize = cfg_blocks->block_size/cfg->source_data_size; + current_block->srcaddrl = (uint32_t)cfg_blocks->source_address; + current_block->srcaddrh = 0x0; + current_block->dstaddrl = (uint32_t)((long)cfg_blocks->dest_address); + current_block->dstaddrh = 0x0; + + if (cfg_blocks->next_block) { + current_block->next_block = k_heap_alloc(&dma_chain, + sizeof(struct chain_block), K_NO_WAIT); + current_block->llpointerl = + (uint32_t)((long)current_block->next_block); + current_block->llpointerh = 0x0; + current_block = current_block->next_block; + } else { + current_block->llpointerl = 0x0; + current_block->llpointerh = 0x0; + current_block->next_block = NULL; + } + } + } else { + /* Single transfer is supported, but Chain transfer is still + * not supported. Therefore, set LLPointer to zero + */ + OUTWORD(DMA_CH_LL_PTR_L(channel), 0); + OUTWORD(DMA_CH_LL_PTR_H(channel), 0); + } + +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(dev); + 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 */ + OUTWORD(DMA_CH_SRC_ADDR_L(channel), src); + OUTWORD(DMA_CH_SRC_ADDR_H(channel), 0); + OUTWORD(DMA_CH_DST_ADDR_L(channel), dst); + OUTWORD(DMA_CH_DST_ADDR_H(channel), 0); + + src_width = INWORD(DMA_CH_CTRL(channel)) & DMA_CH_CTRL_SWIDTH_MASK; + src_width = (1 << (src_width >> DMA_CH_CTRL_SWIDTH_POS)); + + /* Set transfer size */ + OUTWORD(DMA_CH_TRANSIZE(channel), size/src_width); + + 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(dev); + k_spinlock_key_t key; + + if (channel >= ATCDMAC100_MAX_CHAN) { + return -EINVAL; + } + + key = k_spin_lock(&data->chan[channel].lock); + + SET_MASK(DMA_CH_CTRL(channel), DMA_CH_CTRL_ENABLE); + + 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(dev); + k_spinlock_key_t key; + + if (channel >= ATCDMAC100_MAX_CHAN) { + return -EINVAL; + } + + key = k_spin_lock(&data->chan[channel].lock); + + OUTWORD(DMA_ABORT, (1 << channel)); + OUTWORD(DMA_CH_CTRL(channel), 0); + OUTWORD(DMA_INT_STATUS, ((1 << channel) << DMA_INT_STATUS_ABORT_POS)); + + data->chan[channel].status.busy = false; + + struct chain_block *current_block = head_block[channel]->next_block; + + while (head_block[channel] != NULL) { + k_heap_free(&dma_chain, head_block[channel]); + head_block[channel] = current_block; + current_block = head_block[channel]->next_block; + } + + 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 = DEV_CFG(dev); + uint32_t ch_num; + + /* Disable all channels and Channel interrupts */ + for (ch_num = 0; ch_num < ATCDMAC100_MAX_CHAN; ch_num++) { + OUTWORD(DMA_CH_CTRL(ch_num), 0); + } + + OUTWORD(DMA_INT_STATUS, 0xFFFFFF); + + /* 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(dev); + 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..e0df98dad7a5d4d --- /dev/null +++ b/drivers/dma/dma_andes_atcdmac300.h @@ -0,0 +1,165 @@ +/* + * 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 DEV_CFG(dev) ((const struct dma_atcdmac300_cfg * const) (dev)->config) +#define DEV_DATA(dev) ((struct dma_atcdmac300_data *) (dev)->data) + +#define DMA_IDR (DEV_CFG(dev)->base + 0x00) +#define DMA_CFG (DEV_CFG(dev)->base + 0x10) +#define DMA_CTRL (DEV_CFG(dev)->base + 0x20) +#define DMA_ABORT (DEV_CFG(dev)->base + 0x24) +#define DMA_INT_STATUS (DEV_CFG(dev)->base + 0x30) +#define DMA_CH_ENABLE (DEV_CFG(dev)->base + 0x34) + +#define DMA_CH_OFFSET(ch) (ch * 0x20) +#define DMA_CH_CTRL(ch) (DEV_CFG(dev)->base + 0x40 + DMA_CH_OFFSET(ch)) +#define DMA_CH_TRANSIZE(ch) (DEV_CFG(dev)->base + 0x44 + DMA_CH_OFFSET(ch)) +#define DMA_CH_SRC_ADDR_L(ch) (DEV_CFG(dev)->base + 0x48 + DMA_CH_OFFSET(ch)) +#define DMA_CH_SRC_ADDR_H(ch) (DEV_CFG(dev)->base + 0x4C + DMA_CH_OFFSET(ch)) +#define DMA_CH_DST_ADDR_L(ch) (DEV_CFG(dev)->base + 0x50 + DMA_CH_OFFSET(ch)) +#define DMA_CH_DST_ADDR_H(ch) (DEV_CFG(dev)->base + 0x54 + DMA_CH_OFFSET(ch)) +#define DMA_CH_LL_PTR_L(ch) (DEV_CFG(dev)->base + 0x58 + DMA_CH_OFFSET(ch)) +#define DMA_CH_LL_PTR_H(ch) (DEV_CFG(dev)->base + 0x5C + DMA_CH_OFFSET(ch)) + +/* GPDMA Events */ +#define DMA_EVENT_TERMINAL_COUNT_REQUEST (1) +#define DMA_EVENT_ERROR (2) +#define DMA_EVENT_ABORT (3) + +/* 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) + +#define INWORD(x) sys_read32(x) +#define OUTWORD(x, d) sys_write32(d, x) + +#define SET_MASK(x, msk) OUTWORD(x, INWORD(x) | msk) +#define CLR_MASK(x, msk) OUTWORD(x, INWORD(x) & ~msk) + +#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 fc4dab3cfffd507..f4ea3e86967ed46 100644 --- a/dts/riscv/andes/andes_v5_ae350.dtsi +++ b/dts/riscv/andes/andes_v5_ae350.dtsi @@ -282,6 +282,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>; @@ -294,6 +297,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>; @@ -306,6 +312,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..2542e89e1e8f5e2 --- /dev/null +++ b/tests/drivers/dma/chan_blen_transfer/boards/adp_xc7k_ae350.conf @@ -0,0 +1 @@ +CONFIG_CACHE_ENABLE=n 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..2542e89e1e8f5e2 --- /dev/null +++ b/tests/drivers/dma/loop_transfer/boards/adp_xc7k_ae350.conf @@ -0,0 +1 @@ +CONFIG_CACHE_ENABLE=n 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"; +};