drivers: dma: Add Andestech atcdmac300 driver.
Support the Andes atcdmac300 dma driver. Signed-off-by: Kevin Wang <kevinwang821020@google.com>
This commit is contained in:
parent
84c01bb527
commit
d3a73cdb0e
12 changed files with 670 additions and 0 deletions
|
@ -211,3 +211,7 @@
|
||||||
&wdt {
|
&wdt {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
&dma0 {
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
|
@ -15,6 +15,7 @@ supported:
|
||||||
- watchdog
|
- watchdog
|
||||||
- mbox
|
- mbox
|
||||||
- flash
|
- flash
|
||||||
|
- dma
|
||||||
testing:
|
testing:
|
||||||
ignore_tags:
|
ignore_tags:
|
||||||
- bluetooth
|
- bluetooth
|
||||||
|
|
|
@ -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_DMA_RPI_PICO dma_rpi_pico.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_MCUX_PXP dma_mcux_pxp.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_MCUX_SMARTDMA dma_mcux_smartdma.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_DMA_ANDES_ATCDMAC300 dma_andes_atcdmac300.c)
|
||||||
|
|
|
@ -64,4 +64,6 @@ source "drivers/dma/Kconfig.mcux_pxp"
|
||||||
|
|
||||||
source "drivers/dma/Kconfig.mcux_smartdma"
|
source "drivers/dma/Kconfig.mcux_smartdma"
|
||||||
|
|
||||||
|
source "drivers/dma/Kconfig.andes_atcdmac300"
|
||||||
|
|
||||||
endif # DMA
|
endif # DMA
|
||||||
|
|
12
drivers/dma/Kconfig.andes_atcdmac300
Normal file
12
drivers/dma/Kconfig.andes_atcdmac300
Normal file
|
@ -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.
|
536
drivers/dma/dma_andes_atcdmac300.c
Normal file
536
drivers/dma/dma_andes_atcdmac300.c
Normal file
|
@ -0,0 +1,536 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Andes Technology Corporation.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/init.h>
|
||||||
|
#include <zephyr/drivers/dma.h>
|
||||||
|
#include <soc.h>
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT andestech_atcdmac300
|
||||||
|
|
||||||
|
#define LOG_LEVEL CONFIG_DMA_LOG_LEVEL
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
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)
|
84
dts/bindings/dma/andestech,atcdmac300.yaml
Normal file
84
dts/bindings/dma/andestech,atcdmac300.yaml
Normal file
|
@ -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";
|
||||||
|
};
|
|
@ -275,6 +275,9 @@
|
||||||
reg-names = "control", "mem";
|
reg-names = "control", "mem";
|
||||||
interrupts = <4 1>;
|
interrupts = <4 1>;
|
||||||
interrupt-parent = <&plic0>;
|
interrupt-parent = <&plic0>;
|
||||||
|
dmas = <&dma0 0 0 0x009>,
|
||||||
|
<&dma0 1 1 0x00A>;
|
||||||
|
dma-names = "tx", "rx";
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
clock-frequency = <66000000>;
|
clock-frequency = <66000000>;
|
||||||
|
@ -287,6 +290,9 @@
|
||||||
reg-names = "control";
|
reg-names = "control";
|
||||||
interrupts = <5 1>;
|
interrupts = <5 1>;
|
||||||
interrupt-parent = <&plic0>;
|
interrupt-parent = <&plic0>;
|
||||||
|
dmas = <&dma0 2 2 0x009>,
|
||||||
|
<&dma0 3 3 0x00A>;
|
||||||
|
dma-names = "tx", "rx";
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
clock-frequency = <66000000>;
|
clock-frequency = <66000000>;
|
||||||
|
@ -299,6 +305,10 @@
|
||||||
interrupts = <10 1>;
|
interrupts = <10 1>;
|
||||||
interrupt-parent = <&plic0>;
|
interrupt-parent = <&plic0>;
|
||||||
dma-channels = <8>;
|
dma-channels = <8>;
|
||||||
|
dma-requests = <16>;
|
||||||
|
chain-transfer = <1>;
|
||||||
|
#dma-cells = <3>;
|
||||||
|
status = "disabled";
|
||||||
};
|
};
|
||||||
|
|
||||||
eth0: eth@e0100000 {
|
eth0: eth@e0100000 {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
CONFIG_NOCACHE_MEMORY=y
|
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Andes Technology Corporation.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
test_dma0: &dma0 {
|
||||||
|
status = "okay";
|
||||||
|
};
|
|
@ -0,0 +1 @@
|
||||||
|
CONFIG_NOCACHE_MEMORY=y
|
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Andes Technology Corporation.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
test_dma0: &dma0 {
|
||||||
|
status = "okay";
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue