/* * Copyright (c) 2023 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT intel_sedi_dma #include #include #include #include #include #include #include #include #include #include #include "sedi_driver_dma.h" #include "sedi_driver_core.h" #include LOG_MODULE_REGISTER(sedi_dma, CONFIG_DMA_LOG_LEVEL); extern void dma_isr(sedi_dma_t dma_device); struct dma_sedi_config_info { sedi_dma_t peripheral_id; /* Controller instance. */ uint8_t chn_num; void (*irq_config)(void); }; struct dma_sedi_driver_data { struct dma_config dma_configs[DMA_CHANNEL_NUM]; }; #define DEV_DATA(dev) ((struct dma_sedi_driver_data *const)(dev)->data) #define DEV_CFG(dev) \ ((const struct dma_sedi_config_info *const)(dev)->config) /* * this function will be called when dma transferring is completed * or error happened */ static void dma_handler(sedi_dma_t dma_device, int channel, int event_id, void *args) { ARG_UNUSED(args); const struct device *dev = (const struct device *)args; struct dma_sedi_driver_data *const data = DEV_DATA(dev); struct dma_config *config = &(data->dma_configs[channel]); /* run user-defined callback */ if (config->dma_callback) { if ((event_id == SEDI_DMA_EVENT_TRANSFER_DONE) && (config->complete_callback_en)) { config->dma_callback(dev, config->user_data, channel, 0); } else if (!config->error_callback_dis) { config->dma_callback(dev, config->user_data, channel, event_id); } } } /* map width to certain macros*/ static int width_index(uint32_t num_bytes, uint32_t *index) { switch (num_bytes) { case 1: *index = DMA_TRANS_WIDTH_8; break; case 2: *index = DMA_TRANS_WIDTH_16; break; case 4: *index = DMA_TRANS_WIDTH_32; break; case 8: *index = DMA_TRANS_WIDTH_64; break; case 16: *index = DMA_TRANS_WIDTH_128; break; case 32: *index = DMA_TRANS_WIDTH_256; break; default: return -ENOTSUP; } return 0; } /* map burst size to certain macros*/ static int burst_index(uint32_t num_units, uint32_t *index) { switch (num_units) { case 1: *index = DMA_BURST_TRANS_LENGTH_1; break; case 4: *index = DMA_BURST_TRANS_LENGTH_4; break; case 8: *index = DMA_BURST_TRANS_LENGTH_8; break; case 16: *index = DMA_BURST_TRANS_LENGTH_16; break; case 32: *index = DMA_BURST_TRANS_LENGTH_32; break; case 64: *index = DMA_BURST_TRANS_LENGTH_64; break; case 128: *index = DMA_BURST_TRANS_LENGTH_128; break; case 256: *index = DMA_BURST_TRANS_LENGTH_256; break; default: return -ENOTSUP; } return 0; } static void dma_config_convert(struct dma_config *config, dma_memory_type_t *src_mem, dma_memory_type_t *dst_mem, uint8_t *sedi_dma_dir) { *src_mem = DMA_SRAM_MEM; *dst_mem = DMA_SRAM_MEM; *sedi_dma_dir = MEMORY_TO_MEMORY; switch (config->channel_direction) { case MEMORY_TO_MEMORY: case MEMORY_TO_PERIPHERAL: case PERIPHERAL_TO_MEMORY: case PERIPHERAL_TO_PERIPHERAL: *sedi_dma_dir = config->channel_direction; break; case MEMORY_TO_HOST: *dst_mem = DMA_DRAM_MEM; break; case HOST_TO_MEMORY: *src_mem = DMA_DRAM_MEM; break; #ifdef MEMORY_TO_IMR case MEMORY_TO_IMR: *dst_mem = DMA_UMA_MEM; break; #endif #ifdef IMR_TO_MEMORY case IMR_TO_MEMORY: *src_mem = DMA_UMA_MEM; break; #endif } } /* config basic dma */ static int dma_sedi_apply_common_config(sedi_dma_t dev, uint32_t channel, struct dma_config *config, uint8_t *dir) { uint8_t direction = MEMORY_TO_MEMORY; dma_memory_type_t src_mem = DMA_SRAM_MEM, dst_mem = DMA_SRAM_MEM; dma_config_convert(config, &src_mem, &dst_mem, &direction); if (dir) { *dir = direction; } /* configure dma transferring direction*/ sedi_dma_control(dev, channel, SEDI_CONFIG_DMA_DIRECTION, direction); if (direction == MEMORY_TO_MEMORY) { sedi_dma_control(dev, channel, SEDI_CONFIG_DMA_SR_MEM_TYPE, src_mem); sedi_dma_control(dev, channel, SEDI_CONFIG_DMA_DT_MEM_TYPE, dst_mem); } else if (direction == MEMORY_TO_PERIPHERAL) { sedi_dma_control(dev, channel, SEDI_CONFIG_DMA_HS_DEVICE_ID, config->dma_slot); sedi_dma_control(dev, channel, SEDI_CONFIG_DMA_HS_POLARITY, DMA_HS_POLARITY_HIGH); sedi_dma_control(dev, channel, SEDI_CONFIG_DMA_HS_DEVICE_ID_PER_DIR, DMA_HS_PER_TX); } else if (direction == PERIPHERAL_TO_MEMORY) { sedi_dma_control(dev, channel, SEDI_CONFIG_DMA_HS_DEVICE_ID, config->dma_slot); sedi_dma_control(dev, channel, SEDI_CONFIG_DMA_HS_POLARITY, DMA_HS_POLARITY_HIGH); sedi_dma_control(dev, channel, SEDI_CONFIG_DMA_HS_DEVICE_ID_PER_DIR, DMA_HS_PER_RX); } else { return -1; } return 0; } static int dma_sedi_apply_single_config(sedi_dma_t dev, uint32_t channel, struct dma_config *config) { int ret = 0; uint32_t temp = 0; ret = dma_sedi_apply_common_config(dev, channel, config, NULL); if (ret != 0) { goto INVALID_ARGS; } /* configurate dma width of source data*/ ret = width_index(config->source_data_size, &temp); if (ret != 0) { goto INVALID_ARGS; } sedi_dma_control(dev, channel, SEDI_CONFIG_DMA_SR_TRANS_WIDTH, temp); /* configurate dma width of destination data*/ ret = width_index(config->dest_data_size, &temp); if (ret != 0) { goto INVALID_ARGS; } sedi_dma_control(dev, channel, SEDI_CONFIG_DMA_DT_TRANS_WIDTH, temp); /* configurate dma burst size*/ ret = burst_index(config->source_burst_length, &temp); if (ret != 0) { goto INVALID_ARGS; } sedi_dma_control(dev, channel, SEDI_CONFIG_DMA_BURST_LENGTH, temp); return 0; INVALID_ARGS: return ret; } static int dma_sedi_chan_config(const struct device *dev, uint32_t channel, struct dma_config *config) { if ((dev == NULL) || (channel >= DEV_CFG(dev)->chn_num) || (config == NULL) || (config->block_count != 1)) { goto INVALID_ARGS; } const struct dma_sedi_config_info *const info = DEV_CFG(dev); struct dma_sedi_driver_data *const data = DEV_DATA(dev); memcpy(&(data->dma_configs[channel]), config, sizeof(struct dma_config)); /* initialize the dma controller, following the sedi api*/ sedi_dma_event_cb_t cb = dma_handler; sedi_dma_init(info->peripheral_id, (int)channel, cb, (void *)dev); return 0; INVALID_ARGS: return -1; } static int dma_sedi_reload(const struct device *dev, uint32_t channel, uint64_t src, uint64_t dst, size_t size) { if ((dev == NULL) || (channel >= DEV_CFG(dev)->chn_num)) { LOG_ERR("dma reload failed for invalid args"); return -ENOTSUP; } int ret = 0; struct dma_sedi_driver_data *const data = DEV_DATA(dev); struct dma_config *config = &(data->dma_configs[channel]); struct dma_block_config *block_config; if ((config == NULL) || (config->head_block == NULL)) { LOG_ERR("dma reload failed, no config found"); return -ENOTSUP; } block_config = config->head_block; if ((config->block_count == 1) || (block_config->next_block == NULL)) { block_config->source_address = src; block_config->dest_address = dst; block_config->block_size = size; } else { LOG_ERR("no reload support for multi-linkedlist mode"); return -ENOTSUP; } return ret; } static int dma_sedi_start(const struct device *dev, uint32_t channel) { if ((dev == NULL) || (channel >= DEV_CFG(dev)->chn_num)) { LOG_ERR("dma transferring failed for invalid args"); return -ENOTSUP; } int ret = -1; const struct dma_sedi_config_info *const info = DEV_CFG(dev); struct dma_sedi_driver_data *const data = DEV_DATA(dev); struct dma_config *config = &(data->dma_configs[channel]); struct dma_block_config *block_config = config->head_block; uint64_t src_addr, dst_addr; if (config->block_count == 1) { /* call sedi start function */ ret = dma_sedi_apply_single_config(info->peripheral_id, channel, config); if (ret) { goto ERR; } src_addr = block_config->source_address; dst_addr = block_config->dest_address; ret = sedi_dma_start_transfer(info->peripheral_id, channel, src_addr, dst_addr, block_config->block_size); } else { LOG_ERR("MULTIPLE_BLOCK CONFIG is not set"); goto ERR; } if (ret != SEDI_DRIVER_OK) { goto ERR; } return ret; ERR: LOG_ERR("dma transfer failed"); return ret; } static int dma_sedi_stop(const struct device *dev, uint32_t channel) { const struct dma_sedi_config_info *const info = DEV_CFG(dev); LOG_DBG("stopping dma: %p, %d", dev, channel); sedi_dma_abort_transfer(info->peripheral_id, channel); return 0; } static const struct dma_driver_api dma_funcs = { .config = dma_sedi_chan_config, .start = dma_sedi_start, .stop = dma_sedi_stop, .reload = dma_sedi_reload, .get_status = NULL }; static int dma_sedi_init(const struct device *dev) { const struct dma_sedi_config_info *const config = DEV_CFG(dev); config->irq_config(); return 0; } #define DMA_DEVICE_INIT_SEDI(inst) \ static void dma_sedi_##inst##_irq_config(void); \ \ static struct dma_sedi_driver_data dma_sedi_dev_data_##inst; \ static const struct dma_sedi_config_info dma_sedi_config_data_##inst = { \ .peripheral_id = DT_INST_PROP(inst, peripheral_id), \ .chn_num = DT_INST_PROP(inst, dma_channels), \ .irq_config = dma_sedi_##inst##_irq_config \ }; \ DEVICE_DT_DEFINE(DT_INST(inst, DT_DRV_COMPAT), &dma_sedi_init, \ NULL, &dma_sedi_dev_data_##inst, &dma_sedi_config_data_##inst, PRE_KERNEL_2, \ CONFIG_KERNEL_INIT_PRIORITY_DEVICE, (void *)&dma_funcs); \ \ static void dma_sedi_##inst##_irq_config(void) \ { \ IRQ_CONNECT(DT_INST_IRQN(inst), \ DT_INST_IRQ(inst, priority), dma_isr, \ (void *)DT_INST_PROP(inst, peripheral_id), \ DT_INST_IRQ(inst, sense)); \ irq_enable(DT_INST_IRQN(inst)); \ } DT_INST_FOREACH_STATUS_OKAY(DMA_DEVICE_INIT_SEDI)