|  | /* | 
|  | * Copyright (c) 2018 Intel Corporation | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include <device.h> | 
|  | #include <errno.h> | 
|  | #include <init.h> | 
|  | #include <string.h> | 
|  | #include <soc.h> | 
|  | #include <drivers/dma.h> | 
|  | #include <altera_common.h> | 
|  | #include "altera_msgdma_csr_regs.h" | 
|  | #include "altera_msgdma_descriptor_regs.h" | 
|  | #include "altera_msgdma.h" | 
|  |  | 
|  | #define LOG_LEVEL CONFIG_DMA_LOG_LEVEL | 
|  | #include <logging/log.h> | 
|  | LOG_MODULE_REGISTER(dma_nios2); | 
|  |  | 
|  | /* 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 *callback_arg; | 
|  | void (*dma_callback)(void *arg, 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; | 
|  | } | 
|  |  | 
|  | LOG_DBG("msgdma csr status Reg: 0x%x", status); | 
|  |  | 
|  | dev_cfg->dma_callback(dev_cfg->callback_arg, 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 != 0U) { | 
|  | LOG_ERR("invalid channel number"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | #if MSGDMA_0_CSR_PREFETCHER_ENABLE | 
|  | if (cfg->block_count > 1) { | 
|  | LOG_ERR("driver yet add support multiple descriptors"); | 
|  | return -EINVAL; | 
|  | } | 
|  | #else | 
|  | if (cfg->block_count != 1U) { | 
|  | LOG_ERR("invalid block count!!"); | 
|  | return -EINVAL; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (cfg->head_block == NULL) { | 
|  | LOG_ERR("head_block ptr NULL!!"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (cfg->head_block->block_size > MSGDMA_0_DESCRIPTOR_SLAVE_MAX_BYTE) { | 
|  | 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->callback_arg = cfg->callback_arg; | 
|  | 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 { | 
|  | 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 != 0U) { | 
|  | 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) { | 
|  | 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) { | 
|  | LOG_DBG("DMA Dispatcher stopped"); | 
|  | ret = 0; | 
|  | } | 
|  |  | 
|  | 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); |