From 15447fa1d86487f3164cc89447d2cde1fce3d3c0 Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Thu, 3 May 2018 19:24:21 +0530 Subject: [PATCH] drivers: dma: Add dma driver for Nios-II MSGDMA core Add dma driver for Nios-II Modular Scatter-Gather DMA soft IP. This driver relies upon the Altera HAL msgdma driver for all the dma core register programming and interrupt handling. Signed-off-by: Ramakrishna Pallala --- .../nios2/soc/nios2f-zephyr/Kconfig.defconfig | 7 + drivers/dma/CMakeLists.txt | 1 + drivers/dma/Kconfig | 2 + drivers/dma/Kconfig.nios2_msgdma | 13 + drivers/dma/dma_nios2_msgdma.c | 233 ++++++++++++++++++ 5 files changed, 256 insertions(+) create mode 100644 drivers/dma/Kconfig.nios2_msgdma create mode 100644 drivers/dma/dma_nios2_msgdma.c diff --git a/arch/nios2/soc/nios2f-zephyr/Kconfig.defconfig b/arch/nios2/soc/nios2f-zephyr/Kconfig.defconfig index 46ad66f7f67..e9284b1559c 100644 --- a/arch/nios2/soc/nios2f-zephyr/Kconfig.defconfig +++ b/arch/nios2/soc/nios2f-zephyr/Kconfig.defconfig @@ -32,6 +32,13 @@ config ALTERA_AVALON_I2C endif # I2C_NIOS2 +if DMA_NIOS2_MSGDMA + +config ALTERA_AVALON_MSGDMA + def_bool y + +endif # DMA_NIOS2_MSGDMA + if UART_NS16550 config UART_NS16550_PCI diff --git a/drivers/dma/CMakeLists.txt b/drivers/dma/CMakeLists.txt index 5006e3d071e..d47ee4b49f2 100644 --- a/drivers/dma/CMakeLists.txt +++ b/drivers/dma/CMakeLists.txt @@ -2,4 +2,5 @@ zephyr_sources_ifdef(CONFIG_DMA_QMSI dma_qmsi.c) zephyr_sources_ifdef(CONFIG_DMA_SAM_XDMAC dma_sam_xdmac.c) zephyr_sources_ifdef(CONFIG_DMA_STM32F4X dma_stm32f4x.c) zephyr_sources_ifdef(CONFIG_DMA_CAVS dma_cavs.c) +zephyr_sources_ifdef(CONFIG_DMA_NIOS2_MSGDMA dma_nios2_msgdma.c) zephyr_sources_ifdef(CONFIG_USERSPACE dma_handlers.c) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 8711ebf0685..596e988ff11 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -81,4 +81,6 @@ source "drivers/dma/Kconfig.sam_xdmac" source "drivers/dma/Kconfig.cavs" +source "drivers/dma/Kconfig.nios2_msgdma" + endif # DMA diff --git a/drivers/dma/Kconfig.nios2_msgdma b/drivers/dma/Kconfig.nios2_msgdma new file mode 100644 index 00000000000..4338c298ef9 --- /dev/null +++ b/drivers/dma/Kconfig.nios2_msgdma @@ -0,0 +1,13 @@ +# Kconfig - Nios-II mSGDMA driver configuration options +# +# Copyright (c) 2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +config DMA_NIOS2_MSGDMA + bool "Nios-II Modular Scatter-Gather DMA(MSGDMA) driver" + depends on HAS_ALTERA_HAL + default n + help + Enable Nios-II Modular Scatter-Gather DMA(MSGDMA) driver. diff --git a/drivers/dma/dma_nios2_msgdma.c b/drivers/dma/dma_nios2_msgdma.c new file mode 100644 index 00000000000..455a2749fd4 --- /dev/null +++ b/drivers/dma/dma_nios2_msgdma.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2018 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "altera_msgdma_csr_regs.h" +#include "altera_msgdma_descriptor_regs.h" +#include "altera_msgdma.h" + +#define SYS_LOG_LEVEL CONFIG_SYS_LOG_DMA_LEVEL +#include + +/* Device configuration parameters */ +struct nios2_msgdma_dev_cfg { + alt_msgdma_dev *msgdma_dev; + alt_msgdma_standard_descriptor desc; + u32_t direction; + struct k_sem sem_lock; + void (*dma_callback)(struct device *dev, u32_t id, + int error_code); +}; + +#define DEV_NAME(dev) ((dev)->config->name) +#define DEV_CFG(dev) \ + ((struct nios2_msgdma_dev_cfg *)(dev)->config->config_info) + +static void nios2_msgdma_isr(void *arg) +{ + struct device *dev = (struct device *)arg; + struct nios2_msgdma_dev_cfg *cfg = DEV_CFG(dev); + + /* Call Altera HAL driver ISR */ + alt_handle_irq(cfg->msgdma_dev, MSGDMA_0_CSR_IRQ); +} + +static void nios2_msgdma_callback(void *context) +{ + struct nios2_msgdma_dev_cfg *dev_cfg = + DEV_CFG((struct device *)context); + int err_code; + u32_t status; + + status = IORD_ALTERA_MSGDMA_CSR_STATUS(dev_cfg->msgdma_dev->csr_base); + + if (status & ALTERA_MSGDMA_CSR_STOPPED_ON_ERROR_MASK) { + err_code = -EIO; + } else if (status & ALTERA_MSGDMA_CSR_BUSY_MASK) { + err_code = -EBUSY; + } else { + err_code = 0; + } + + SYS_LOG_DBG("msgdma csr status Reg: 0x%x", status); + + dev_cfg->dma_callback((struct device *)context, 0, err_code); +} + +static int nios2_msgdma_config(struct device *dev, u32_t channel, + struct dma_config *cfg) +{ + struct nios2_msgdma_dev_cfg *dev_cfg = DEV_CFG(dev); + struct dma_block_config *dma_block; + int status; + u32_t control; + + /* Nios-II MSGDMA supports only one channel per DMA core */ + if (channel != 0) { + SYS_LOG_ERR("invalid channel number"); + return -EINVAL; + } + +#if MSGDMA_0_CSR_PREFETCHER_ENABLE + if (cfg->block_count > 1) { + SYS_LOG_ERR("driver yet add support multiple descriptors"); + return -EINVAL; + } +#else + if (cfg->block_count != 1) { + SYS_LOG_ERR("invalid block count!!"); + return -EINVAL; + } +#endif + + if (cfg->head_block == NULL) { + SYS_LOG_ERR("head_block ptr NULL!!"); + return -EINVAL; + } + + if (cfg->head_block->block_size > MSGDMA_0_DESCRIPTOR_SLAVE_MAX_BYTE) { + SYS_LOG_ERR("DMA error: Data size too big: %d", + cfg->head_block->block_size); + return -EINVAL; + } + + k_sem_take(&dev_cfg->sem_lock, K_FOREVER); + dev_cfg->dma_callback = cfg->dma_callback; + dev_cfg->direction = cfg->channel_direction; + dma_block = cfg->head_block; + control = ALTERA_MSGDMA_DESCRIPTOR_CONTROL_TRANSFER_COMPLETE_IRQ_MASK | + ALTERA_MSGDMA_DESCRIPTOR_CONTROL_EARLY_TERMINATION_IRQ_MASK; + + if (dev_cfg->direction == MEMORY_TO_MEMORY) { + status = alt_msgdma_construct_standard_mm_to_mm_descriptor( + dev_cfg->msgdma_dev, &dev_cfg->desc, + (alt_u32 *)dma_block->source_address, + (alt_u32 *)dma_block->dest_address, + dma_block->block_size, + control); + } else if (dev_cfg->direction == MEMORY_TO_PERIPHERAL) { + status = alt_msgdma_construct_standard_mm_to_st_descriptor( + dev_cfg->msgdma_dev, &dev_cfg->desc, + (alt_u32 *)dma_block->source_address, + dma_block->block_size, + control); + } else if (dev_cfg->direction == PERIPHERAL_TO_MEMORY) { + status = alt_msgdma_construct_standard_st_to_mm_descriptor( + dev_cfg->msgdma_dev, &dev_cfg->desc, + (alt_u32 *)dma_block->dest_address, + dma_block->block_size, + control); + } else { + SYS_LOG_ERR("invalid channel direction"); + status = -EINVAL; + } + + /* Register msgdma callback */ + alt_msgdma_register_callback(dev_cfg->msgdma_dev, + nios2_msgdma_callback, + ALTERA_MSGDMA_CSR_GLOBAL_INTERRUPT_MASK | + ALTERA_MSGDMA_CSR_STOP_ON_ERROR_MASK | + ALTERA_MSGDMA_CSR_STOP_ON_EARLY_TERMINATION_MASK, + dev); + + /* Clear the IRQ status */ + IOWR_ALTERA_MSGDMA_CSR_STATUS(dev_cfg->msgdma_dev->csr_base, + ALTERA_MSGDMA_CSR_IRQ_SET_MASK); + k_sem_give(&dev_cfg->sem_lock); + + return status; +} + +static int nios2_msgdma_transfer_start(struct device *dev, u32_t channel) +{ + struct nios2_msgdma_dev_cfg *cfg = DEV_CFG(dev); + int status; + + /* Nios-II mSGDMA supports only one channel per DMA core */ + if (channel != 0) { + SYS_LOG_ERR("Invalid channel number"); + return -EINVAL; + } + + k_sem_take(&cfg->sem_lock, K_FOREVER); + status = alt_msgdma_standard_descriptor_async_transfer(cfg->msgdma_dev, + &cfg->desc); + k_sem_give(&cfg->sem_lock); + + if (status < 0) { + SYS_LOG_ERR("DMA transfer error (%d)", status); + } + + return status; +} + +static int nios2_msgdma_transfer_stop(struct device *dev, u32_t channel) +{ + struct nios2_msgdma_dev_cfg *cfg = DEV_CFG(dev); + int ret = -EIO; + u32_t status; + + k_sem_take(&cfg->sem_lock, K_FOREVER); + /* Stop the DMA Dispatcher */ + IOWR_ALTERA_MSGDMA_CSR_CONTROL(cfg->msgdma_dev->csr_base, + ALTERA_MSGDMA_CSR_STOP_MASK); + + status = IORD_ALTERA_MSGDMA_CSR_STATUS(cfg->msgdma_dev->csr_base); + k_sem_give(&cfg->sem_lock); + + if (status & ALTERA_MSGDMA_CSR_STOP_STATE_MASK) { + SYS_LOG_DBG("DMA Dispatcher stopped"); + ret = 0; + } + + SYS_LOG_DBG("msgdma csr status Reg: 0x%x", status); + + return status; +} + +static const struct dma_driver_api nios2_msgdma_driver_api = { + .config = nios2_msgdma_config, + .start = nios2_msgdma_transfer_start, + .stop = nios2_msgdma_transfer_stop, +}; + +/* DMA0 */ +static struct device DEVICE_NAME_GET(dma0_nios2); + +static int nios2_msgdma0_initialize(struct device *dev) +{ + struct nios2_msgdma_dev_cfg *dev_cfg = DEV_CFG(dev); + + /* Initialize semaphore */ + k_sem_init(&dev_cfg->sem_lock, 1, 1); + + alt_msgdma_init(dev_cfg->msgdma_dev, 0, MSGDMA_0_CSR_IRQ); + + IRQ_CONNECT(MSGDMA_0_CSR_IRQ, CONFIG_DMA_0_IRQ_PRI, + nios2_msgdma_isr, DEVICE_GET(dma0_nios2), 0); + + irq_enable(MSGDMA_0_CSR_IRQ); + + return 0; +} + +ALTERA_MSGDMA_CSR_DESCRIPTOR_SLAVE_INSTANCE(MSGDMA_0, MSGDMA_0_CSR, + MSGDMA_0_DESCRIPTOR_SLAVE, msgdma_dev0) + +static struct nios2_msgdma_dev_cfg dma0_nios2_config = { + .msgdma_dev = &msgdma_dev0, +}; + +DEVICE_AND_API_INIT(dma0_nios2, CONFIG_DMA_0_NAME, &nios2_msgdma0_initialize, + NULL, &dma0_nios2_config, POST_KERNEL, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &nios2_msgdma_driver_api);