diff --git a/drivers/dma/CMakeLists.txt b/drivers/dma/CMakeLists.txt index 48ab9fc771a25..0c04dfb22c4fa 100644 --- a/drivers/dma/CMakeLists.txt +++ b/drivers/dma/CMakeLists.txt @@ -22,3 +22,4 @@ zephyr_library_sources_ifdef(CONFIG_DMA_INTEL_ADSP_HDA_HOST_OUT dma_intel_adsp_h zephyr_library_sources_ifdef(CONFIG_DMA_INTEL_ADSP_HDA_LINK_IN dma_intel_adsp_hda_link_in.c) zephyr_library_sources_ifdef(CONFIG_DMA_INTEL_ADSP_HDA_LINK_OUT dma_intel_adsp_hda_link_out.c) zephyr_library_sources_ifdef(CONFIG_DMA_INTEL_ADSP_GPDMA dma_intel_adsp_gpdma.c dma_dw_common.c) +zephyr_library_sources_ifdef(CONFIG_DMA_GD32 dma_gd32.c) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 0b5534205f522..cf3b60c24cb1b 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -48,4 +48,6 @@ source "drivers/dma/Kconfig.intel_adsp_gpdma" source "drivers/dma/Kconfig.intel_adsp_hda" +source "drivers/dma/Kconfig.gd32" + endif # DMA diff --git a/drivers/dma/Kconfig.gd32 b/drivers/dma/Kconfig.gd32 new file mode 100644 index 0000000000000..a095e1a33f57e --- /dev/null +++ b/drivers/dma/Kconfig.gd32 @@ -0,0 +1,10 @@ +# Copyright (c) 2022 Tokita, Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +config DMA_GD32 + bool "Gigadevice GD32 DMA driver" + default y + depends on DT_HAS_GD_GD32_DMA_ENABLED + select USE_GD32_DMA + help + DMA driver for GigaDevice GD32 series MCUs. diff --git a/drivers/dma/dma_gd32.c b/drivers/dma/dma_gd32.c new file mode 100644 index 0000000000000..7ee4291f7be29 --- /dev/null +++ b/drivers/dma/dma_gd32.c @@ -0,0 +1,679 @@ +/* + * Copyright (c) 2022 TOKITA Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT gd_gd32_dma + +#include +#include +#include + +#include + +#ifdef CONFIG_SOC_SERIES_GD32F4XX +#define CHXCTL_PERIEN_OFFSET ((uint32_t)25U) +#define GD32_DMA_CHXCTL_DIR BIT(6) +#define GD32_DMA_CHXCTL_M2M BIT(7) +#define GD32_DMA_INTERRUPT_ERRORS (DMA_CHXCTL_SDEIE | DMA_CHXCTL_TAEIE) +#define GD32_DMA_FLAG_ERRORS (DMA_FLAG_SDE | DMA_FLAG_TAE) +#else +#define GD32_DMA_CHXCTL_DIR BIT(4) +#define GD32_DMA_CHXCTL_M2M BIT(14) +#define GD32_DMA_INTERRUPT_ERRORS DMA_CHXCTL_ERRIE +#define GD32_DMA_FLAG_ERRORS DMA_FLAG_ERR +#endif + +#ifdef CONFIG_SOC_SERIES_GD32F3X0 +#undef DMA_INTF +#undef DMA_INTC +#undef DMA_CHCTL +#undef DMA_CHCNT +#undef DMA_CHPADDR +#undef DMA_CHMADDR + +#define DMA_INTF(dma) REG32(dma + 0x00UL) +#define DMA_INTC(dma) REG32(dma + 0x04UL) +#define DMA_CHCTL(dma, ch) REG32((dma + 0x08UL) + 0x14UL * (uint32_t)(ch)) +#define DMA_CHCNT(dma, ch) REG32((dma + 0x0CUL) + 0x14UL * (uint32_t)(ch)) +#define DMA_CHPADDR(dma, ch) REG32((dma + 0x10UL) + 0x14UL * (uint32_t)(ch)) +#define DMA_CHMADDR(dma, ch) REG32((dma + 0x14UL) + 0x14UL * (uint32_t)(ch)) +#endif + +#define GD32_DMA_INTF(dma) DMA_INTF(dma) +#define GD32_DMA_INTC(dma) DMA_INTC(dma) +#define GD32_DMA_CHCTL(dma, ch) DMA_CHCTL((dma), (ch)) +#define GD32_DMA_CHCNT(dma, ch) DMA_CHCNT((dma), (ch)) +#define GD32_DMA_CHPADDR(dma, ch) DMA_CHPADDR((dma), (ch)) +#define GD32_DMA_CHMADDR(dma, ch) DMA_CHMADDR((dma), (ch)) + +LOG_MODULE_REGISTER(dma_gd32, CONFIG_DMA_LOG_LEVEL); + +struct dma_gd32_config { + uint32_t reg; + uint32_t channels; + uint32_t rcu_periph_clock; + void (*irq_configure)(void); +}; + +struct dma_gd32_channel { + dma_callback_t callback; + void *user_data; + uint32_t direction; + bool busy; +}; + +struct dma_gd32_data { + struct dma_context ctx; + struct dma_gd32_channel *channels; +}; + +struct dma_gd32_srcdst_config { + uint32_t addr; + uint32_t adj; + uint32_t width; +}; + +/* + * Register access functions + */ + +static inline void +gd32_dma_periph_increase_enable(uint32_t reg, dma_channel_enum ch) +{ + GD32_DMA_CHCTL(reg, ch) |= DMA_CHXCTL_PNAGA; +} + +static inline void +gd32_dma_periph_increase_disable(uint32_t reg, dma_channel_enum ch) +{ + GD32_DMA_CHCTL(reg, ch) &= ~DMA_CHXCTL_PNAGA; +} + +static inline void +gd32_dma_transfer_set_memory_to_memory(uint32_t reg, dma_channel_enum ch) +{ + GD32_DMA_CHCTL(reg, ch) |= GD32_DMA_CHXCTL_M2M; + GD32_DMA_CHCTL(reg, ch) &= ~GD32_DMA_CHXCTL_DIR; +} + +static inline void +gd32_dma_transfer_set_memory_to_periph(uint32_t reg, dma_channel_enum ch) +{ + GD32_DMA_CHCTL(reg, ch) &= ~GD32_DMA_CHXCTL_M2M; + GD32_DMA_CHCTL(reg, ch) |= GD32_DMA_CHXCTL_DIR; +} + +static inline void +gd32_dma_transfer_set_periph_to_memory(uint32_t reg, dma_channel_enum ch) +{ + GD32_DMA_CHCTL(reg, ch) &= ~GD32_DMA_CHXCTL_M2M; + GD32_DMA_CHCTL(reg, ch) &= ~GD32_DMA_CHXCTL_DIR; +} + +static inline void +gd32_dma_memory_increase_enable(uint32_t reg, dma_channel_enum ch) +{ + GD32_DMA_CHCTL(reg, ch) |= DMA_CHXCTL_MNAGA; +} + +static inline void +gd32_dma_memory_increase_disable(uint32_t reg, dma_channel_enum ch) +{ + GD32_DMA_CHCTL(reg, ch) &= ~DMA_CHXCTL_MNAGA; +} + +static inline void +gd32_dma_circulation_enable(uint32_t reg, dma_channel_enum ch) +{ + GD32_DMA_CHCTL(reg, ch) |= DMA_CHXCTL_CMEN; +} + +static inline void +gd32_dma_circulation_disable(uint32_t reg, dma_channel_enum ch) +{ + GD32_DMA_CHCTL(reg, ch) &= ~DMA_CHXCTL_CMEN; +} + +static inline void gd32_dma_channel_enable(uint32_t reg, dma_channel_enum ch) +{ + GD32_DMA_CHCTL(reg, ch) |= DMA_CHXCTL_CHEN; +} + +static inline void gd32_dma_channel_disable(uint32_t reg, dma_channel_enum ch) +{ + GD32_DMA_CHCTL(reg, ch) &= ~DMA_CHXCTL_CHEN; +} + +static inline void +gd32_dma_interrupt_enable(uint32_t reg, dma_channel_enum ch, uint32_t source) +{ + GD32_DMA_CHCTL(reg, ch) |= source; +} + +static inline void +gd32_dma_interrupt_disable(uint32_t reg, dma_channel_enum ch, uint32_t source) +{ + GD32_DMA_CHCTL(reg, ch) &= ~source; +} + +static inline void +gd32_dma_priority_config(uint32_t reg, dma_channel_enum ch, uint32_t priority) +{ + uint32_t ctl = GD32_DMA_CHCTL(reg, ch); + + GD32_DMA_CHCTL(reg, ch) = (ctl & (~DMA_CHXCTL_PRIO)) | priority; +} + +static inline void +gd32_dma_memory_width_config(uint32_t reg, dma_channel_enum ch, uint32_t mwidth) +{ + uint32_t ctl = GD32_DMA_CHCTL(reg, ch); + + GD32_DMA_CHCTL(reg, ch) = (ctl & (~DMA_CHXCTL_MWIDTH)) | mwidth; +} + +static inline void +gd32_dma_periph_width_config(uint32_t reg, dma_channel_enum ch, uint32_t pwidth) +{ + uint32_t ctl = GD32_DMA_CHCTL(reg, ch); + + GD32_DMA_CHCTL(reg, ch) = (ctl & (~DMA_CHXCTL_PWIDTH)) | pwidth; +} + +#ifdef CONFIG_SOC_SERIES_GD32F4XX +static inline void +gd32_dma_channel_subperipheral_select(uint32_t reg, dma_channel_enum ch, + dma_subperipheral_enum sub_periph) +{ + uint32_t ctl = GD32_DMA_CHCTL(reg, ch); + + GD32_DMA_CHCTL(reg, ch) = + (ctl & (~DMA_CHXCTL_PERIEN)) | + ((uint32_t)sub_periph << CHXCTL_PERIEN_OFFSET); +} +#endif + +static inline void +gd32_dma_periph_address_config(uint32_t reg, dma_channel_enum ch, uint32_t addr) +{ + GD32_DMA_CHPADDR(reg, ch) = addr; +} + +static inline void +gd32_dma_memory_address_config(uint32_t reg, dma_channel_enum ch, uint32_t addr) +{ +#ifdef CONFIG_SOC_SERIES_GD32F4XX + DMA_CHM0ADDR(reg, ch) = addr; +#else + GD32_DMA_CHMADDR(reg, ch) = addr; +#endif +} + +static inline void +gd32_dma_transfer_number_config(uint32_t reg, dma_channel_enum ch, uint32_t num) +{ + GD32_DMA_CHCNT(reg, ch) = (num & DMA_CHXCNT_CNT); +} + +static inline uint32_t +gd32_dma_transfer_number_get(uint32_t reg, dma_channel_enum ch) +{ + return GD32_DMA_CHCNT(reg, ch); +} + +static inline void +gd32_dma_interrupt_flag_clear(uint32_t reg, dma_channel_enum ch, uint32_t flag) +{ +#ifdef CONFIG_SOC_SERIES_GD32F4XX + if (ch < DMA_CH4) { + DMA_INTC0(reg) |= DMA_FLAG_ADD(flag, ch); + } else { + DMA_INTC1(reg) |= DMA_FLAG_ADD(flag, ch - DMA_CH4); + } +#else + GD32_DMA_INTC(reg) |= DMA_FLAG_ADD(flag, ch); +#endif +} + +static inline void +gd32_dma_flag_clear(uint32_t reg, dma_channel_enum ch, uint32_t flag) +{ +#ifdef CONFIG_SOC_SERIES_GD32F4XX + if (ch < DMA_CH4) { + DMA_INTC0(reg) |= DMA_FLAG_ADD(flag, ch); + } else { + DMA_INTC1(reg) |= DMA_FLAG_ADD(flag, ch - DMA_CH4); + } +#else + GD32_DMA_INTC(reg) |= DMA_FLAG_ADD(flag, ch); +#endif +} + +static inline uint32_t +gd32_dma_interrupt_flag_get(uint32_t reg, dma_channel_enum ch, uint32_t flag) +{ +#ifdef CONFIG_SOC_SERIES_GD32F4XX + if (ch < DMA_CH4) { + return (DMA_INTF0(reg) & DMA_FLAG_ADD(flag, ch)); + } else { + return (DMA_INTF1(reg) & DMA_FLAG_ADD(flag, ch - DMA_CH4)); + } +#else + return (GD32_DMA_INTF(reg) & DMA_FLAG_ADD(flag, ch)); +#endif +} + +static inline void gd32_dma_deinit(uint32_t reg, dma_channel_enum ch) +{ + GD32_DMA_CHCTL(reg, ch) &= ~DMA_CHXCTL_CHEN; + + GD32_DMA_CHCTL(reg, ch) = DMA_CHCTL_RESET_VALUE; + GD32_DMA_CHCNT(reg, ch) = DMA_CHCNT_RESET_VALUE; + GD32_DMA_CHPADDR(reg, ch) = DMA_CHPADDR_RESET_VALUE; +#ifdef CONFIG_SOC_SERIES_GD32F4XX + DMA_CHM0ADDR(reg, ch) = DMA_CHMADDR_RESET_VALUE; + DMA_CHFCTL(reg, ch) = DMA_CHFCTL_RESET_VALUE; + if (ch < DMA_CH4) { + DMA_INTC0(reg) |= DMA_FLAG_ADD(DMA_CHINTF_RESET_VALUE, ch); + } else { + DMA_INTC1(reg) |= + DMA_FLAG_ADD(DMA_CHINTF_RESET_VALUE, ch - DMA_CH4); + } +#else + GD32_DMA_CHMADDR(reg, ch) = DMA_CHMADDR_RESET_VALUE; + GD32_DMA_INTC(reg) |= DMA_FLAG_ADD(DMA_CHINTF_RESET_VALUE, ch); +#endif +} + +/* + * Utility functions + */ + +static inline uint32_t dma_gd32_priority(uint32_t prio) +{ + return CHCTL_PRIO(prio); +} + +static inline uint32_t dma_gd32_memory_width(uint32_t width) +{ + switch (width) { + case 4: + return CHCTL_MWIDTH(2); + case 2: + return CHCTL_MWIDTH(1); + default: + return CHCTL_MWIDTH(0); + } +} + +static inline uint32_t dma_gd32_periph_width(uint32_t width) +{ + switch (width) { + case 4: + return CHCTL_PWIDTH(2); + case 2: + return CHCTL_PWIDTH(1); + default: + return CHCTL_PWIDTH(0); + } +} + +/* + * API functions + */ + +static int dma_gd32_config(const struct device *dev, uint32_t channel, + struct dma_config *dma_cfg) +{ + const struct dma_gd32_config *cfg = dev->config; + struct dma_gd32_data *data = dev->data; + struct dma_gd32_srcdst_config src_cfg; + struct dma_gd32_srcdst_config dst_cfg; + struct dma_gd32_srcdst_config *memory_cfg = NULL; + struct dma_gd32_srcdst_config *periph_cfg = NULL; + + if (channel >= cfg->channels) { + LOG_ERR("channel must be < %" PRIu32 " (%" PRIu32 ")", + cfg->channels, channel); + return -EINVAL; + } + + if (dma_cfg->block_count != 1) { + LOG_ERR("chained block transfer not supported."); + return -ENOTSUP; + } + + if (dma_cfg->channel_priority > 3) { + LOG_ERR("channel_priority must be < 4 (%" PRIu32 ")", + dma_cfg->channel_priority); + return -EINVAL; + } + + if (dma_cfg->head_block->source_addr_adj == DMA_ADDR_ADJ_DECREMENT) { + LOG_ERR("source_addr_adj not supported DMA_ADDR_ADJ_DECREMENT"); + return -ENOTSUP; + } + + if (dma_cfg->head_block->dest_addr_adj == DMA_ADDR_ADJ_DECREMENT) { + LOG_ERR("dest_addr_adj not supported DMA_ADDR_ADJ_DECREMENT"); + return -ENOTSUP; + } + + if (dma_cfg->head_block->source_addr_adj != DMA_ADDR_ADJ_INCREMENT && + dma_cfg->head_block->source_addr_adj != DMA_ADDR_ADJ_NO_CHANGE) { + LOG_ERR("invalid source_addr_adj %" PRIu16, + dma_cfg->head_block->source_addr_adj); + return -ENOTSUP; + } + if (dma_cfg->head_block->dest_addr_adj != DMA_ADDR_ADJ_INCREMENT && + dma_cfg->head_block->dest_addr_adj != DMA_ADDR_ADJ_NO_CHANGE) { + LOG_ERR("invalid dest_addr_adj %" PRIu16, + dma_cfg->head_block->dest_addr_adj); + return -ENOTSUP; + } + + if (dma_cfg->source_data_size != 1 && dma_cfg->source_data_size != 2 && + dma_cfg->source_data_size != 4) { + LOG_ERR("source_data_size must be 1, 2, or 4 (%" PRIu32 ")", + dma_cfg->source_data_size); + return -EINVAL; + } + + if (dma_cfg->dest_data_size != 1 && dma_cfg->dest_data_size != 2 && + dma_cfg->dest_data_size != 4) { + LOG_ERR("dest_data_size must be 1, 2, or 4 (%" PRIu32 ")", + dma_cfg->dest_data_size); + return -EINVAL; + } + + if (dma_cfg->channel_direction > PERIPHERAL_TO_MEMORY) { + LOG_ERR("channel_direction must be MEMORY_TO_MEMORY, " + "MEMORY_TO_PERIPHERAL or PERIPHERAL_TO_MEMORY (%" PRIu32 + ")", + dma_cfg->channel_direction); + return -ENOTSUP; + } + +#ifdef CONFIG_SOC_SERIES_GD32F4XX + if (dma_cfg->linked_channel > 0xF) { + LOG_ERR("linked_channel must be <7 (%" PRIu32 ")", + dma_cfg->linked_channel); + return -EINVAL; + } +#endif + + gd32_dma_deinit(cfg->reg, channel); + + src_cfg.addr = dma_cfg->head_block->source_address; + src_cfg.adj = dma_cfg->head_block->source_addr_adj; + src_cfg.width = dma_cfg->source_data_size; + + dst_cfg.addr = dma_cfg->head_block->dest_address; + dst_cfg.adj = dma_cfg->head_block->dest_addr_adj; + dst_cfg.width = dma_cfg->dest_data_size; + + switch (dma_cfg->channel_direction) { + case MEMORY_TO_MEMORY: + gd32_dma_transfer_set_memory_to_memory(cfg->reg, channel); + memory_cfg = &dst_cfg; + periph_cfg = &src_cfg; + break; + case PERIPHERAL_TO_MEMORY: + gd32_dma_transfer_set_periph_to_memory(cfg->reg, channel); + memory_cfg = &dst_cfg; + periph_cfg = &src_cfg; + break; + case MEMORY_TO_PERIPHERAL: + gd32_dma_transfer_set_memory_to_periph(cfg->reg, channel); + memory_cfg = &src_cfg; + periph_cfg = &dst_cfg; + break; + } + + gd32_dma_memory_address_config(cfg->reg, channel, memory_cfg->addr); + if (memory_cfg->adj == DMA_ADDR_ADJ_INCREMENT) { + gd32_dma_memory_increase_enable(cfg->reg, channel); + } else { + gd32_dma_memory_increase_disable(cfg->reg, channel); + } + + gd32_dma_periph_address_config(cfg->reg, channel, periph_cfg->addr); + if (periph_cfg->adj == DMA_ADDR_ADJ_INCREMENT) { + gd32_dma_periph_increase_enable(cfg->reg, channel); + } else { + gd32_dma_periph_increase_disable(cfg->reg, channel); + } + + gd32_dma_transfer_number_config(cfg->reg, channel, + dma_cfg->head_block->block_size); + gd32_dma_priority_config(cfg->reg, channel, + dma_gd32_priority(dma_cfg->channel_priority)); + gd32_dma_memory_width_config(cfg->reg, channel, + dma_gd32_memory_width(memory_cfg->width)); + gd32_dma_periph_width_config(cfg->reg, channel, + dma_gd32_periph_width(periph_cfg->width)); + gd32_dma_circulation_disable(cfg->reg, channel); +#ifdef CONFIG_SOC_SERIES_GD32F4XX + if (dma_cfg->channel_direction != MEMORY_TO_MEMORY) { + gd32_dma_channel_subperipheral_select(cfg->reg, channel, + dma_cfg->linked_channel); + } +#endif + + data->channels[channel].callback = dma_cfg->dma_callback; + data->channels[channel].user_data = dma_cfg->user_data; + data->channels[channel].direction = dma_cfg->channel_direction; + + return 0; +} + +static int dma_gd32_reload(const struct device *dev, uint32_t ch, uint32_t src, + uint32_t dst, size_t size) +{ + const struct dma_gd32_config *cfg = dev->config; + struct dma_gd32_data *data = dev->data; + + if (ch >= cfg->channels) { + LOG_ERR("reload channel must be < %" PRIu32 " (%" PRIu32 ")", + cfg->channels, ch); + return -EINVAL; + } + + if (data->channels[ch].busy) { + return -EBUSY; + } + + gd32_dma_channel_disable(cfg->reg, ch); + + gd32_dma_transfer_number_config(cfg->reg, ch, size); + + switch (data->channels[ch].direction) { + case MEMORY_TO_MEMORY: + case PERIPHERAL_TO_MEMORY: + gd32_dma_memory_address_config(cfg->reg, ch, dst); + gd32_dma_periph_address_config(cfg->reg, ch, src); + break; + case MEMORY_TO_PERIPHERAL: + gd32_dma_memory_address_config(cfg->reg, ch, src); + gd32_dma_periph_address_config(cfg->reg, ch, dst); + break; + } + + gd32_dma_channel_enable(cfg->reg, ch); + + return 0; +} + +static int dma_gd32_start(const struct device *dev, uint32_t ch) +{ + const struct dma_gd32_config *cfg = dev->config; + struct dma_gd32_data *data = dev->data; + + if (ch >= cfg->channels) { + LOG_ERR("start channel must be < %" PRIu32 " (%" PRIu32 ")", + cfg->channels, ch); + return -EINVAL; + } + + gd32_dma_interrupt_enable(cfg->reg, ch, + DMA_CHXCTL_FTFIE | GD32_DMA_INTERRUPT_ERRORS); + gd32_dma_channel_enable(cfg->reg, ch); + data->channels[ch].busy = true; + + return 0; +} + +static int dma_gd32_stop(const struct device *dev, uint32_t ch) +{ + const struct dma_gd32_config *cfg = dev->config; + struct dma_gd32_data *data = dev->data; + + if (ch >= cfg->channels) { + LOG_ERR("stop channel must be < %" PRIu32 " (%" PRIu32 ")", + cfg->channels, ch); + return -EINVAL; + } + + gd32_dma_interrupt_disable( + cfg->reg, ch, DMA_CHXCTL_FTFIE | GD32_DMA_INTERRUPT_ERRORS); + gd32_dma_interrupt_flag_clear(cfg->reg, ch, + DMA_FLAG_FTF | GD32_DMA_FLAG_ERRORS); + gd32_dma_channel_disable(cfg->reg, ch); + data->channels[ch].busy = false; + + return 0; +} + +static int dma_gd32_get_status(const struct device *dev, uint32_t ch, + struct dma_status *stat) +{ + const struct dma_gd32_config *cfg = dev->config; + struct dma_gd32_data *data = dev->data; + + if (ch >= cfg->channels) { + LOG_ERR("channel must be < %" PRIu32 " (%" PRIu32 ")", + cfg->channels, ch); + return -EINVAL; + } + + stat->pending_length = gd32_dma_transfer_number_get(cfg->reg, ch); + stat->dir = data->channels[ch].direction; + stat->busy = data->channels[ch].busy; + + return 0; +} + +static bool dma_gd32_api_chan_filter(const struct device *dev, int ch, + void *filter_param) +{ + uint32_t filter; + + if (!filter_param) { + LOG_ERR("filter_param must not be NULL"); + return false; + } + + filter = *((uint32_t *)filter_param); + + return (filter & BIT(ch)); +} + +static int dma_gd32_init(const struct device *dev) +{ + const struct dma_gd32_config *cfg = dev->config; + + rcu_periph_clock_enable(cfg->rcu_periph_clock); + + for (uint32_t i = 0; i < cfg->channels; i++) { + gd32_dma_interrupt_disable(cfg->reg, i, + DMA_CHXCTL_FTFIE | GD32_DMA_INTERRUPT_ERRORS); + gd32_dma_deinit(cfg->reg, i); + } + + cfg->irq_configure(); + + return 0; +} + +static void dma_gd32_isr(const struct device *dev) +{ + const struct dma_gd32_config *cfg = dev->config; + struct dma_gd32_data *data = dev->data; + uint32_t errflag, ftfflag; + int err = 0; + + for (uint32_t i = 0; i < cfg->channels; i++) { + errflag = gd32_dma_interrupt_flag_get(cfg->reg, i, + GD32_DMA_FLAG_ERRORS); + ftfflag = + gd32_dma_interrupt_flag_get(cfg->reg, i, DMA_FLAG_FTF); + + if (errflag == 0 && ftfflag == 0) { + continue; + } + + if (errflag) { + err = -EIO; + } + + gd32_dma_interrupt_flag_clear( + cfg->reg, i, DMA_FLAG_FTF | GD32_DMA_FLAG_ERRORS); + data->channels[i].busy = false; + + if (data->channels[i].callback) { + data->channels[i].callback( + dev, data->channels[i].user_data, i, err); + } + } +} + +static const struct dma_driver_api dma_gd32_driver_api = { + .config = dma_gd32_config, + .reload = dma_gd32_reload, + .start = dma_gd32_start, + .stop = dma_gd32_stop, + .get_status = dma_gd32_get_status, + .chan_filter = dma_gd32_api_chan_filter, +}; + +#define IRQ_CONFIGURE(n, inst) \ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(inst, n, irq), \ + DT_INST_IRQ_BY_IDX(inst, n, priority), dma_gd32_isr, \ + DEVICE_DT_INST_GET(inst), 0); \ + irq_enable(DT_INST_IRQ_BY_IDX(inst, n, irq)); + +#define CONFIGURE_ALL_IRQS(inst, n) LISTIFY(n, IRQ_CONFIGURE, (), inst) + +#define GD32_DMA_INIT(inst) \ + static void dma_gd32##inst##_irq_configure(void) \ + { \ + CONFIGURE_ALL_IRQS(inst, DT_NUM_IRQS(DT_DRV_INST(inst))); \ + } \ + static const struct dma_gd32_config dma_gd32##inst##_config = { \ + .reg = DT_INST_REG_ADDR(inst), \ + .rcu_periph_clock = DT_INST_PROP(inst, rcu_periph_clock), \ + .channels = DT_INST_PROP(inst, dma_channels), \ + .irq_configure = dma_gd32##inst##_irq_configure, \ + }; \ + \ + static struct dma_gd32_channel \ + dma_gd32##inst##_channels[DT_INST_PROP(inst, dma_channels)]; \ + ATOMIC_DEFINE(dma_gd32_atomic##inst, \ + DT_INST_PROP(inst, dma_channels)); \ + static struct dma_gd32_data dma_gd32##inst##_data = { \ + .ctx = { \ + .magic = DMA_MAGIC, \ + .atomic = dma_gd32_atomic##inst, \ + .dma_channels = DT_INST_PROP(inst, dma_channels), \ + }, \ + .channels = dma_gd32##inst##_channels, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, &dma_gd32_init, NULL, \ + &dma_gd32##inst##_data, \ + &dma_gd32##inst##_config, POST_KERNEL, \ + CONFIG_DMA_INIT_PRIORITY, &dma_gd32_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(GD32_DMA_INIT) diff --git a/dts/arm/gigadevice/gd32e10x/gd32e10x.dtsi b/dts/arm/gigadevice/gd32e10x/gd32e10x.dtsi index b297bf3f77bcc..0845f25ecee64 100644 --- a/dts/arm/gigadevice/gd32e10x/gd32e10x.dtsi +++ b/dts/arm/gigadevice/gd32e10x/gd32e10x.dtsi @@ -460,6 +460,28 @@ }; }; + dma0: dma@40020000 { + compatible = "gd,gd32-dma"; + reg = <0x40020000 0x400>; + interrupts = <11 0>, <12 0>, <13 0>, <14 0>, + <15 0>, <16 0>, <17 0>; + rcu-periph-clock = <0x500>; + dma-channels = <7>; + #dma-cells = <1>; + status = "disabled"; + }; + + dma1: dma@40020400 { + compatible = "gd,gd32-dma"; + reg = <0x40020400 0x400>; + interrupts = <56 0>, <57 0>, <58 0>, <59 0>, + <60 0>; + rcu-periph-clock = <0x501>; + dma-channels = <5>; + #dma-cells = <1>; + status = "disabled"; + }; + }; }; diff --git a/dts/arm/gigadevice/gd32f3x0/gd32f3x0.dtsi b/dts/arm/gigadevice/gd32f3x0/gd32f3x0.dtsi index 307f9ec3d1a21..6f0c6c4d23610 100644 --- a/dts/arm/gigadevice/gd32f3x0/gd32f3x0.dtsi +++ b/dts/arm/gigadevice/gd32f3x0/gd32f3x0.dtsi @@ -70,6 +70,16 @@ #io-channel-cells = <1>; }; + dma0: dma@40020000 { + compatible = "gd,gd32-dma"; + reg = <0x40020000 0x400>; + interrupts = <9 0>, <10 0>, <11 0>, <48 0>; + rcu-periph-clock = <0x500>; + dma-channels = <7>; + #dma-cells = <1>; + status = "disabled"; + }; + pinctrl: pin-controller@48000000 { compatible = "gd,gd32-pinctrl-af"; reg = <0x48000000 0x1800>; diff --git a/dts/arm/gigadevice/gd32f403/gd32f403.dtsi b/dts/arm/gigadevice/gd32f403/gd32f403.dtsi index 360e59e401df0..1c3466e523e4e 100644 --- a/dts/arm/gigadevice/gd32f403/gd32f403.dtsi +++ b/dts/arm/gigadevice/gd32f403/gd32f403.dtsi @@ -478,6 +478,28 @@ #pwm-cells = <3>; }; }; + + dma0: dma@40020000 { + compatible = "gd,gd32-dma"; + reg = <0x40026000 0x400>; + interrupts = <11 0>, <12 0>, <13 0>, <14 0>, + <15 0>, <16 0>, <17 0>; + rcu-periph-clock = <0x500>; + dma-channels = <7>; + #dma-cells = <1>; + status = "disabled"; + }; + + dma1: dma@40020400 { + compatible = "gd,gd32-dma"; + reg = <0x40026400 0x400>; + interrupts = <56 0>, <57 0>, <58 0>, <59 0>, + <60 0>; + rcu-periph-clock = <0x501>; + dma-channels = <5>; + #dma-cells = <1>; + status = "disabled"; + }; }; }; diff --git a/dts/arm/gigadevice/gd32f4xx/gd32f4xx.dtsi b/dts/arm/gigadevice/gd32f4xx/gd32f4xx.dtsi index 5762babf42cfd..51de6908faca9 100644 --- a/dts/arm/gigadevice/gd32f4xx/gd32f4xx.dtsi +++ b/dts/arm/gigadevice/gd32f4xx/gd32f4xx.dtsi @@ -612,6 +612,28 @@ #pwm-cells = <3>; }; }; + + dma0: dma@40026000 { + compatible = "gd,gd32-dma"; + reg = <0x40026000 0x400>; + interrupts = <11 0>, <12 0>, <13 0>, <14 0>, + <15 0>, <16 0>, <17 0>, <47 0>; + rcu-periph-clock = <0xc15>; + dma-channels = <8>; + #dma-cells = <1>; + status = "disabled"; + }; + + dma1: dma@40026400 { + compatible = "gd,gd32-dma"; + reg = <0x40026400 0x400>; + interrupts = <56 0>, <57 0>, <58 0>, <59 0>, + <60 0>, <68 0>, <69 0>, <70 0>; + rcu-periph-clock = <0xc16>; + dma-channels = <8>; + #dma-cells = <1>; + status = "disabled"; + }; }; }; diff --git a/dts/bindings/dma/gd,gd32-dma.yaml b/dts/bindings/dma/gd,gd32-dma.yaml new file mode 100644 index 0000000000000..b26231fd309ed --- /dev/null +++ b/dts/bindings/dma/gd,gd32-dma.yaml @@ -0,0 +1,30 @@ +# Copyright (c) 2022, TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +description: | + GD32 DMA controller + +compatible: "gd,gd32-dma" + +include: dma-controller.yaml + +properties: + reg: + required: true + + interrupts: + required: true + + dma-channels: + required: true + + rcu-periph-clock: + type: int + description: Peripheral RCU(Reset Clock Unit) Clock ID + required: true + + "#dma-cells": + const: 1 + +dma-cells: + - channel diff --git a/dts/riscv/gigadevice/gd32vf103.dtsi b/dts/riscv/gigadevice/gd32vf103.dtsi index 8581b745f3038..daa4d1bbe9b09 100644 --- a/dts/riscv/gigadevice/gd32vf103.dtsi +++ b/dts/riscv/gigadevice/gd32vf103.dtsi @@ -349,5 +349,29 @@ channels = <0>; status = "disabled"; }; + + dma0: dma@40020000 { + compatible = "gd,gd32-dma"; + reg = <0x40020000 0x400>; + interrupt-parent = <&eclic>; + interrupts = <30 0>, <31 0>, <32 0>, <33 0>, + <34 0>, <35 0>, <36 0>; + rcu-periph-clock = <0x500>; + dma-channels = <7>; + #dma-cells = <1>; + status = "disabled"; + }; + + dma1: dma@40020400 { + compatible = "gd,gd32-dma"; + reg = <0x40020400 0x400>; + interrupt-parent = <&eclic>; + interrupts = <75 0>, <76 0>, <77 0>, <78 0>, + <79 0>; + rcu-periph-clock = <0x501>; + dma-channels = <5>; + #dma-cells = <1>; + status = "disabled"; + }; }; }; diff --git a/tests/drivers/dma/loop_transfer/boards/gd32e103v_eval.overlay b/tests/drivers/dma/loop_transfer/boards/gd32e103v_eval.overlay new file mode 100644 index 0000000000000..f9de4d577ad6a --- /dev/null +++ b/tests/drivers/dma/loop_transfer/boards/gd32e103v_eval.overlay @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2022 Tokita, Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +test_dma: &dma0 { + status = "okay"; +}; diff --git a/tests/drivers/dma/loop_transfer/boards/gd32f350r_eval.overlay b/tests/drivers/dma/loop_transfer/boards/gd32f350r_eval.overlay new file mode 100644 index 0000000000000..f9de4d577ad6a --- /dev/null +++ b/tests/drivers/dma/loop_transfer/boards/gd32f350r_eval.overlay @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2022 Tokita, Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +test_dma: &dma0 { + status = "okay"; +}; diff --git a/tests/drivers/dma/loop_transfer/boards/gd32f403z_eval.overlay b/tests/drivers/dma/loop_transfer/boards/gd32f403z_eval.overlay new file mode 100644 index 0000000000000..f9de4d577ad6a --- /dev/null +++ b/tests/drivers/dma/loop_transfer/boards/gd32f403z_eval.overlay @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2022 Tokita, Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +test_dma: &dma0 { + status = "okay"; +}; diff --git a/tests/drivers/dma/loop_transfer/boards/gd32f450i_eval.overlay b/tests/drivers/dma/loop_transfer/boards/gd32f450i_eval.overlay new file mode 100644 index 0000000000000..013d737700297 --- /dev/null +++ b/tests/drivers/dma/loop_transfer/boards/gd32f450i_eval.overlay @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2022 Tokita, Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +test_dma: &dma1 { + status = "okay"; +}; diff --git a/tests/drivers/dma/loop_transfer/boards/gd32f450z_eval.overlay b/tests/drivers/dma/loop_transfer/boards/gd32f450z_eval.overlay new file mode 100644 index 0000000000000..013d737700297 --- /dev/null +++ b/tests/drivers/dma/loop_transfer/boards/gd32f450z_eval.overlay @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2022 Tokita, Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +test_dma: &dma1 { + status = "okay"; +}; diff --git a/tests/drivers/dma/loop_transfer/boards/gd32f470i_eval.overlay b/tests/drivers/dma/loop_transfer/boards/gd32f470i_eval.overlay new file mode 100644 index 0000000000000..013d737700297 --- /dev/null +++ b/tests/drivers/dma/loop_transfer/boards/gd32f470i_eval.overlay @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2022 Tokita, Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +test_dma: &dma1 { + status = "okay"; +}; diff --git a/tests/drivers/dma/loop_transfer/boards/gd32vf103v_eval.overlay b/tests/drivers/dma/loop_transfer/boards/gd32vf103v_eval.overlay new file mode 100644 index 0000000000000..f9de4d577ad6a --- /dev/null +++ b/tests/drivers/dma/loop_transfer/boards/gd32vf103v_eval.overlay @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2022 Tokita, Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +test_dma: &dma0 { + status = "okay"; +}; diff --git a/tests/drivers/dma/loop_transfer/boards/longan_nano.overlay b/tests/drivers/dma/loop_transfer/boards/longan_nano.overlay new file mode 100644 index 0000000000000..f9de4d577ad6a --- /dev/null +++ b/tests/drivers/dma/loop_transfer/boards/longan_nano.overlay @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2022 Tokita, Hiroshi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +test_dma: &dma0 { + status = "okay"; +};