/* * Copyright (c) 2016 Linaro Limited. * Copyright (c) 2019 Song Qiang * Copyright (c) 2022 STMicroelectronics * * SPDX-License-Identifier: Apache-2.0 */ /** * @brief Common part of DMA drivers for stm32U5. * @note Functions named with stm32_dma_* are SoCs related functions * */ #include "dma_stm32.h" #include #include #include #include #include LOG_MODULE_REGISTER(dma_stm32, CONFIG_DMA_LOG_LEVEL); #define DT_DRV_COMPAT st_stm32u5_dma static const uint32_t table_src_size[] = { LL_DMA_SRC_DATAWIDTH_BYTE, LL_DMA_SRC_DATAWIDTH_HALFWORD, LL_DMA_SRC_DATAWIDTH_WORD, }; static const uint32_t table_dst_size[] = { LL_DMA_DEST_DATAWIDTH_BYTE, LL_DMA_DEST_DATAWIDTH_HALFWORD, LL_DMA_DEST_DATAWIDTH_WORD, }; static const uint32_t table_priority[4] = { LL_DMA_LOW_PRIORITY_LOW_WEIGHT, LL_DMA_LOW_PRIORITY_MID_WEIGHT, LL_DMA_LOW_PRIORITY_HIGH_WEIGHT, LL_DMA_HIGH_PRIORITY, }; static void dma_stm32_dump_stream_irq(const struct device *dev, uint32_t id) { const struct dma_stm32_config *config = dev->config; DMA_TypeDef *dma = (DMA_TypeDef *)(config->base); stm32_dma_dump_stream_irq(dma, id); } static void dma_stm32_clear_stream_irq(const struct device *dev, uint32_t id) { const struct dma_stm32_config *config = dev->config; DMA_TypeDef *dma = (DMA_TypeDef *)(config->base); dma_stm32_clear_tc(dma, id); dma_stm32_clear_ht(dma, id); stm32_dma_clear_stream_irq(dma, id); } uint32_t dma_stm32_id_to_stream(uint32_t id) { static const uint32_t stream_nr[] = { LL_DMA_CHANNEL_0, LL_DMA_CHANNEL_1, LL_DMA_CHANNEL_2, LL_DMA_CHANNEL_3, LL_DMA_CHANNEL_4, LL_DMA_CHANNEL_5, LL_DMA_CHANNEL_6, LL_DMA_CHANNEL_7, LL_DMA_CHANNEL_8, LL_DMA_CHANNEL_9, LL_DMA_CHANNEL_10, LL_DMA_CHANNEL_11, LL_DMA_CHANNEL_12, LL_DMA_CHANNEL_13, LL_DMA_CHANNEL_14, LL_DMA_CHANNEL_15, }; __ASSERT_NO_MSG(id < ARRAY_SIZE(stream_nr)); return stream_nr[id]; } bool dma_stm32_is_tc_active(DMA_TypeDef *DMAx, uint32_t id) { return LL_DMA_IsActiveFlag_TC(DMAx, dma_stm32_id_to_stream(id)); } void dma_stm32_clear_tc(DMA_TypeDef *DMAx, uint32_t id) { LL_DMA_ClearFlag_TC(DMAx, dma_stm32_id_to_stream(id)); } /* data transfer error */ static inline bool dma_stm32_is_dte_active(DMA_TypeDef *dma, uint32_t id) { return LL_DMA_IsActiveFlag_DTE(dma, dma_stm32_id_to_stream(id)); } /* link transfer error */ static inline bool dma_stm32_is_ule_active(DMA_TypeDef *dma, uint32_t id) { return LL_DMA_IsActiveFlag_ULE(dma, dma_stm32_id_to_stream(id)); } /* user setting error */ static inline bool dma_stm32_is_use_active(DMA_TypeDef *dma, uint32_t id) { return LL_DMA_IsActiveFlag_USE(dma, dma_stm32_id_to_stream(id)); } /* transfer error either a data or user or link error */ bool dma_stm32_is_te_active(DMA_TypeDef *DMAx, uint32_t id) { return ( LL_DMA_IsActiveFlag_DTE(DMAx, dma_stm32_id_to_stream(id)) || LL_DMA_IsActiveFlag_ULE(DMAx, dma_stm32_id_to_stream(id)) || LL_DMA_IsActiveFlag_USE(DMAx, dma_stm32_id_to_stream(id)) ); } /* clear transfer error either a data or user or link error */ void dma_stm32_clear_te(DMA_TypeDef *DMAx, uint32_t id) { LL_DMA_ClearFlag_DTE(DMAx, dma_stm32_id_to_stream(id)); LL_DMA_ClearFlag_ULE(DMAx, dma_stm32_id_to_stream(id)); LL_DMA_ClearFlag_USE(DMAx, dma_stm32_id_to_stream(id)); } bool dma_stm32_is_ht_active(DMA_TypeDef *DMAx, uint32_t id) { return LL_DMA_IsActiveFlag_HT(DMAx, dma_stm32_id_to_stream(id)); } void dma_stm32_clear_ht(DMA_TypeDef *DMAx, uint32_t id) { LL_DMA_ClearFlag_HT(DMAx, dma_stm32_id_to_stream(id)); } void stm32_dma_dump_stream_irq(DMA_TypeDef *dma, uint32_t id) { LOG_INF("tc: %d, ht: %d, dte: %d, ule: %d, use: %d", dma_stm32_is_tc_active(dma, id), dma_stm32_is_ht_active(dma, id), dma_stm32_is_dte_active(dma, id), dma_stm32_is_ule_active(dma, id), dma_stm32_is_use_active(dma, id) ); } /* Check if nsecure masked interrupt is active on channel */ bool stm32_dma_is_tc_irq_active(DMA_TypeDef *dma, uint32_t id) { return (LL_DMA_IsEnabledIT_TC(dma, dma_stm32_id_to_stream(id)) && LL_DMA_IsActiveFlag_TC(dma, dma_stm32_id_to_stream(id))); } bool stm32_dma_is_ht_irq_active(DMA_TypeDef *dma, uint32_t id) { return (LL_DMA_IsEnabledIT_HT(dma, dma_stm32_id_to_stream(id)) && LL_DMA_IsActiveFlag_HT(dma, dma_stm32_id_to_stream(id))); } static inline bool stm32_dma_is_te_irq_active(DMA_TypeDef *dma, uint32_t id) { return ( (LL_DMA_IsEnabledIT_DTE(dma, dma_stm32_id_to_stream(id)) && LL_DMA_IsActiveFlag_DTE(dma, dma_stm32_id_to_stream(id))) || (LL_DMA_IsEnabledIT_ULE(dma, dma_stm32_id_to_stream(id)) && LL_DMA_IsActiveFlag_ULE(dma, dma_stm32_id_to_stream(id))) || (LL_DMA_IsEnabledIT_USE(dma, dma_stm32_id_to_stream(id)) && LL_DMA_IsActiveFlag_USE(dma, dma_stm32_id_to_stream(id))) ); } /* check if and irq of any type occurred on the channel */ #define stm32_dma_is_irq_active LL_DMA_IsActiveFlag_MIS void stm32_dma_clear_stream_irq(DMA_TypeDef *dma, uint32_t id) { dma_stm32_clear_te(dma, id); LL_DMA_ClearFlag_TO(dma, dma_stm32_id_to_stream(id)); LL_DMA_ClearFlag_SUSP(dma, dma_stm32_id_to_stream(id)); } bool stm32_dma_is_irq_happened(DMA_TypeDef *dma, uint32_t id) { if (dma_stm32_is_te_active(dma, id)) { return true; } return false; } void stm32_dma_enable_stream(DMA_TypeDef *dma, uint32_t id) { LL_DMA_EnableChannel(dma, dma_stm32_id_to_stream(id)); } bool stm32_dma_is_enabled_stream(DMA_TypeDef *dma, uint32_t id) { if (LL_DMA_IsEnabledChannel(dma, dma_stm32_id_to_stream(id)) == 1) { return true; } return false; } int stm32_dma_disable_stream(DMA_TypeDef *dma, uint32_t id) { /* GPDMA channel abort sequence */ LL_DMA_SuspendChannel(dma, dma_stm32_id_to_stream(id)); /* reset the channel will disable it */ LL_DMA_ResetChannel(dma, dma_stm32_id_to_stream(id)); if (!stm32_dma_is_enabled_stream(dma, id)) { return 0; } return -EAGAIN; } void stm32_dma_set_mem_periph_address(DMA_TypeDef *dma, uint32_t channel, uint32_t src_addr, uint32_t dest_addr) { LL_DMA_ConfigAddresses(dma, channel, src_addr, dest_addr); } /* same function to set periph/mem addresses */ void stm32_dma_set_periph_mem_address(DMA_TypeDef *dma, uint32_t channel, uint32_t src_addr, uint32_t dest_addr) { LL_DMA_ConfigAddresses(dma, channel, src_addr, dest_addr); } static void dma_stm32_irq_handler(const struct device *dev, uint32_t id) { const struct dma_stm32_config *config = dev->config; DMA_TypeDef *dma = (DMA_TypeDef *)(config->base); struct dma_stm32_stream *stream; uint32_t callback_arg; __ASSERT_NO_MSG(id < config->max_streams); stream = &config->streams[id]; /* The busy channel is pertinent if not overridden by the HAL */ if ((stream->hal_override != true) && (stream->busy == false)) { /* * When DMA channel is not overridden by HAL, * ignore irq if the channel is not busy anymore */ dma_stm32_clear_stream_irq(dev, id); return; } callback_arg = id + STM32_DMA_STREAM_OFFSET; stream->busy = false; /* The dma stream id is in range from STM32_DMA_STREAM_OFFSET.. */ if (stm32_dma_is_ht_irq_active(dma, id)) { /* Let HAL DMA handle flags on its own */ if (!stream->hal_override) { dma_stm32_clear_ht(dma, id); } stream->dma_callback(dev, stream->user_data, callback_arg, DMA_STATUS_BLOCK); } else if (stm32_dma_is_tc_irq_active(dma, id)) { /* Let HAL DMA handle flags on its own */ if (!stream->hal_override) { dma_stm32_clear_tc(dma, id); } stream->dma_callback(dev, stream->user_data, callback_arg, DMA_STATUS_COMPLETE); } else { LOG_ERR("Transfer Error."); dma_stm32_dump_stream_irq(dev, id); dma_stm32_clear_stream_irq(dev, id); stream->dma_callback(dev, stream->user_data, callback_arg, -EIO); } } static int dma_stm32_get_priority(uint8_t priority, uint32_t *ll_priority) { if (priority > ARRAY_SIZE(table_priority)) { LOG_ERR("Priority error. %d", priority); return -EINVAL; } *ll_priority = table_priority[priority]; return 0; } static int dma_stm32_get_direction(enum dma_channel_direction direction, uint32_t *ll_direction) { switch (direction) { case MEMORY_TO_MEMORY: *ll_direction = LL_DMA_DIRECTION_MEMORY_TO_MEMORY; break; case MEMORY_TO_PERIPHERAL: *ll_direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; break; case PERIPHERAL_TO_MEMORY: *ll_direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; break; default: LOG_ERR("Direction error. %d", direction); return -EINVAL; } return 0; } static int dma_stm32_disable_stream(DMA_TypeDef *dma, uint32_t id) { int count = 0; for (;;) { if (stm32_dma_disable_stream(dma, id) == 0) { return 0; } /* After trying for 5 seconds, give up */ if (count++ > (5 * 1000)) { return -EBUSY; } k_sleep(K_MSEC(1)); } return 0; } static int dma_stm32_configure(const struct device *dev, uint32_t id, struct dma_config *config) { const struct dma_stm32_config *dev_config = dev->config; struct dma_stm32_stream *stream = &dev_config->streams[id - STM32_DMA_STREAM_OFFSET]; DMA_TypeDef *dma = (DMA_TypeDef *)dev_config->base; LL_DMA_InitTypeDef DMA_InitStruct; int ret; LL_DMA_StructInit(&DMA_InitStruct); /* Give channel from index 0 */ id = id - STM32_DMA_STREAM_OFFSET; if (id >= dev_config->max_streams) { LOG_ERR("cannot configure the dma stream %d.", id); return -EINVAL; } if (stream->busy) { LOG_ERR("dma stream %d is busy.", id); return -EBUSY; } if (dma_stm32_disable_stream(dma, id) != 0) { LOG_ERR("could not disable dma stream %d.", id); return -EBUSY; } dma_stm32_clear_stream_irq(dev, id); /* Check potential DMA override (if id parameters and stream are valid) */ if (config->linked_channel == STM32_DMA_HAL_OVERRIDE) { /* DMA channel is overridden by HAL DMA * Retain that the channel is busy and proceed to the minimal * configuration to properly route the IRQ */ stream->busy = true; stream->hal_override = true; stream->dma_callback = config->dma_callback; stream->user_data = config->user_data; return 0; } if (config->head_block->block_size > DMA_STM32_MAX_DATA_ITEMS) { LOG_ERR("Data size too big: %d\n", config->head_block->block_size); return -EINVAL; } /* Support only the same data width for source and dest */ if (config->dest_data_size != config->source_data_size) { LOG_ERR("source and dest data size differ."); return -EINVAL; } if (config->source_data_size != 4U && config->source_data_size != 2U && config->source_data_size != 1U) { LOG_ERR("source and dest unit size error, %d", config->source_data_size); return -EINVAL; } /* * STM32's circular mode will auto reset both source address * counter and destination address counter. */ if (config->head_block->source_reload_en != config->head_block->dest_reload_en) { LOG_ERR("source_reload_en and dest_reload_en must " "be the same."); return -EINVAL; } stream->busy = true; stream->dma_callback = config->dma_callback; stream->direction = config->channel_direction; stream->user_data = config->user_data; stream->src_size = config->source_data_size; stream->dst_size = config->dest_data_size; /* Check dest or source memory address, warn if 0 */ if (config->head_block->source_address == 0) { LOG_WRN("source_buffer address is null."); } if (config->head_block->dest_address == 0) { LOG_WRN("dest_buffer address is null."); } DMA_InitStruct.SrcAddress = config->head_block->source_address; DMA_InitStruct.DestAddress = config->head_block->dest_address; DMA_InitStruct.BlkHWRequest = LL_DMA_HWREQUEST_SINGLEBURST; DMA_InitStruct.DataAlignment = LL_DMA_DATA_ALIGN_ZEROPADD; ret = dma_stm32_get_priority(config->channel_priority, &DMA_InitStruct.Priority); if (ret < 0) { return ret; } ret = dma_stm32_get_direction(config->channel_direction, &DMA_InitStruct.Direction); if (ret < 0) { return ret; } /* This part is for source */ switch (config->head_block->source_addr_adj) { case DMA_ADDR_ADJ_INCREMENT: DMA_InitStruct.SrcIncMode = LL_DMA_SRC_INCREMENT; break; case DMA_ADDR_ADJ_NO_CHANGE: DMA_InitStruct.SrcIncMode = LL_DMA_SRC_FIXED; break; case DMA_ADDR_ADJ_DECREMENT: return -ENOTSUP; default: LOG_ERR("Memory increment error. %d", config->head_block->source_addr_adj); return -EINVAL; } LOG_DBG("Channel (%d) src inc (%x).", id, DMA_InitStruct.SrcIncMode); /* This part is for dest */ switch (config->head_block->dest_addr_adj) { case DMA_ADDR_ADJ_INCREMENT: DMA_InitStruct.DestIncMode = LL_DMA_DEST_INCREMENT; break; case DMA_ADDR_ADJ_NO_CHANGE: DMA_InitStruct.DestIncMode = LL_DMA_DEST_FIXED; break; case DMA_ADDR_ADJ_DECREMENT: return -ENOTSUP; default: LOG_ERR("Periph increment error. %d", config->head_block->dest_addr_adj); return -EINVAL; } LOG_DBG("Channel (%d) dest inc (%x).", id, DMA_InitStruct.DestIncMode); stream->source_periph = (stream->direction == PERIPHERAL_TO_MEMORY); /* Set the data width, when source_data_size equals dest_data_size */ int index = find_lsb_set(config->source_data_size) - 1; DMA_InitStruct.SrcDataWidth = table_src_size[index]; index = find_lsb_set(config->dest_data_size) - 1; DMA_InitStruct.DestDataWidth = table_dst_size[index]; DMA_InitStruct.BlkDataLength = config->head_block->block_size; /* The request ID is stored in the dma_slot */ DMA_InitStruct.Request = config->dma_slot; LL_DMA_Init(dma, dma_stm32_id_to_stream(id), &DMA_InitStruct); LL_DMA_EnableIT_TC(dma, dma_stm32_id_to_stream(id)); LL_DMA_EnableIT_USE(dma, dma_stm32_id_to_stream(id)); LL_DMA_EnableIT_ULE(dma, dma_stm32_id_to_stream(id)); LL_DMA_EnableIT_DTE(dma, dma_stm32_id_to_stream(id)); /* Enable Half-Transfer irq if circular mode is enabled */ if (config->head_block->source_reload_en) { LL_DMA_EnableIT_HT(dma, dma_stm32_id_to_stream(id)); } return ret; } static int dma_stm32_reload(const struct device *dev, uint32_t id, uint32_t src, uint32_t dst, size_t size) { const struct dma_stm32_config *config = dev->config; DMA_TypeDef *dma = (DMA_TypeDef *)(config->base); struct dma_stm32_stream *stream; /* Give channel from index 0 */ id = id - STM32_DMA_STREAM_OFFSET; if (id >= config->max_streams) { return -EINVAL; } stream = &config->streams[id]; if (dma_stm32_disable_stream(dma, id) != 0) { return -EBUSY; } if (stream->direction > PERIPHERAL_TO_MEMORY) { return -EINVAL; } LL_DMA_ConfigAddresses(dma, dma_stm32_id_to_stream(id), src, dst); if (stream->source_periph) { LL_DMA_SetBlkDataLength(dma, dma_stm32_id_to_stream(id), size / stream->src_size); } else { LL_DMA_SetBlkDataLength(dma, dma_stm32_id_to_stream(id), size / stream->dst_size); } /* When reloading the dma, the stream is busy again before enabling */ stream->busy = true; stm32_dma_enable_stream(dma, id); return 0; } static int dma_stm32_start(const struct device *dev, uint32_t id) { const struct dma_stm32_config *config = dev->config; DMA_TypeDef *dma = (DMA_TypeDef *)(config->base); struct dma_stm32_stream *stream; /* Give channel from index 0 */ id = id - STM32_DMA_STREAM_OFFSET; /* Only M2P or M2M mode can be started manually. */ if (id >= config->max_streams) { return -EINVAL; } /* Repeated start : return now if channel is already started */ if (stm32_dma_is_enabled_stream(dma, id)) { return 0; } /* When starting the dma, the stream is busy before enabling */ stream = &config->streams[id]; stream->busy = true; dma_stm32_clear_stream_irq(dev, id); stm32_dma_enable_stream(dma, id); return 0; } static int dma_stm32_suspend(const struct device *dev, uint32_t id) { const struct dma_stm32_config *config = dev->config; DMA_TypeDef *dma = (DMA_TypeDef *)(config->base); /* Give channel from index 0 */ id = id - STM32_DMA_STREAM_OFFSET; if (id >= config->max_streams) { return -EINVAL; } /* Suspend the channel and wait for suspend Flag set */ LL_DMA_SuspendChannel(dma, dma_stm32_id_to_stream(id)); /* It's not enough to wait for the SUSPF bit with LL_DMA_IsActiveFlag_SUSP */ do { k_msleep(1); /* A delay is needed (1ms is valid) */ } while (LL_DMA_IsActiveFlag_SUSP(dma, dma_stm32_id_to_stream(id)) != 1); /* Do not Reset the channel to allow resuming later */ return 0; } static int dma_stm32_resume(const struct device *dev, uint32_t id) { const struct dma_stm32_config *config = dev->config; DMA_TypeDef *dma = (DMA_TypeDef *)(config->base); /* Give channel from index 0 */ id = id - STM32_DMA_STREAM_OFFSET; if (id >= config->max_streams) { return -EINVAL; } /* Resume the channel : it's enough after suspend */ LL_DMA_ResumeChannel(dma, dma_stm32_id_to_stream(id)); return 0; } static int dma_stm32_stop(const struct device *dev, uint32_t id) { const struct dma_stm32_config *config = dev->config; struct dma_stm32_stream *stream = &config->streams[id - STM32_DMA_STREAM_OFFSET]; DMA_TypeDef *dma = (DMA_TypeDef *)(config->base); /* Give channel from index 0 */ id = id - STM32_DMA_STREAM_OFFSET; if (id >= config->max_streams) { return -EINVAL; } /* Repeated stop : return now if channel is already stopped */ if (!stm32_dma_is_enabled_stream(dma, id)) { return 0; } LL_DMA_DisableIT_TC(dma, dma_stm32_id_to_stream(id)); LL_DMA_DisableIT_USE(dma, dma_stm32_id_to_stream(id)); LL_DMA_DisableIT_ULE(dma, dma_stm32_id_to_stream(id)); LL_DMA_DisableIT_DTE(dma, dma_stm32_id_to_stream(id)); dma_stm32_clear_stream_irq(dev, id); dma_stm32_disable_stream(dma, id); /* Finally, flag stream as free */ stream->busy = false; return 0; } static int dma_stm32_init(const struct device *dev) { const struct dma_stm32_config *config = dev->config; const struct device *clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); if (clock_control_on(clk, (clock_control_subsys_t) &config->pclken) != 0) { LOG_ERR("clock op failed\n"); return -EIO; } config->config_irq(dev); for (uint32_t i = 0; i < config->max_streams; i++) { config->streams[i].busy = false; } ((struct dma_stm32_data *)dev->data)->dma_ctx.magic = 0; ((struct dma_stm32_data *)dev->data)->dma_ctx.dma_channels = 0; ((struct dma_stm32_data *)dev->data)->dma_ctx.atomic = 0; return 0; } static int dma_stm32_get_status(const struct device *dev, uint32_t id, struct dma_status *stat) { const struct dma_stm32_config *config = dev->config; DMA_TypeDef *dma = (DMA_TypeDef *)(config->base); struct dma_stm32_stream *stream; /* Give channel from index 0 */ id = id - STM32_DMA_STREAM_OFFSET; if (id >= config->max_streams) { return -EINVAL; } stream = &config->streams[id]; stat->pending_length = LL_DMA_GetBlkDataLength(dma, dma_stm32_id_to_stream(id)); stat->dir = stream->direction; stat->busy = stream->busy; return 0; } static const struct dma_driver_api dma_funcs = { .reload = dma_stm32_reload, .config = dma_stm32_configure, .start = dma_stm32_start, .stop = dma_stm32_stop, .get_status = dma_stm32_get_status, .suspend = dma_stm32_suspend, .resume = dma_stm32_resume, }; /* * Macro to CONNECT and enable each irq (order is given by the 'listify') * chan: channel of the DMA instance (assuming one irq per channel) * stm32U5x has 16 channels * dma : dma instance (one GPDMA instance on stm32U5x) */ #define DMA_STM32_IRQ_CONNECT_CHANNEL(chan, dma) \ do { \ IRQ_CONNECT(DT_INST_IRQ_BY_IDX(dma, chan, irq), \ DT_INST_IRQ_BY_IDX(dma, chan, priority), \ dma_stm32_irq_##dma##_##chan, \ DEVICE_DT_INST_GET(dma), 0); \ irq_enable(DT_INST_IRQ_BY_IDX(dma, chan, irq)); \ } while (0) /* * Macro to configure the irq for each dma instance (index) * Loop to CONNECT and enable each irq for each channel * Expecting as many irq as property */ #define DMA_STM32_IRQ_CONNECT(index) \ static void dma_stm32_config_irq_##index(const struct device *dev) \ { \ ARG_UNUSED(dev); \ \ LISTIFY(DT_INST_PROP(index, dma_channels), \ DMA_STM32_IRQ_CONNECT_CHANNEL, (;), index); \ } /* * Macro to instanciate the irq handler (order is given by the 'listify') * chan: channel of the DMA instance (assuming one irq per channel) * stm32U5x has 16 channels * dma : dma instance (one GPDMA instance on stm32U5x) */ #define DMA_STM32_DEFINE_IRQ_HANDLER(chan, dma) \ static void dma_stm32_irq_##dma##_##chan(const struct device *dev) \ { \ dma_stm32_irq_handler(dev, chan); \ } #define DMA_STM32_INIT_DEV(index) \ BUILD_ASSERT(DT_INST_PROP(index, dma_channels) \ == DT_NUM_IRQS(DT_DRV_INST(index)), \ "Nb of Channels and IRQ mismatch"); \ \ LISTIFY(DT_INST_PROP(index, dma_channels), \ DMA_STM32_DEFINE_IRQ_HANDLER, (;), index); \ \ DMA_STM32_IRQ_CONNECT(index); \ \ static struct dma_stm32_stream \ dma_stm32_streams_##index[DT_INST_PROP_OR(index, dma_channels, \ DT_NUM_IRQS(DT_DRV_INST(index)))]; \ \ const struct dma_stm32_config dma_stm32_config_##index = { \ .pclken = { .bus = DT_INST_CLOCKS_CELL(index, bus), \ .enr = DT_INST_CLOCKS_CELL(index, bits) }, \ .config_irq = dma_stm32_config_irq_##index, \ .base = DT_INST_REG_ADDR(index), \ .max_streams = DT_INST_PROP_OR(index, dma_channels, \ DT_NUM_IRQS(DT_DRV_INST(index)) \ ), \ .streams = dma_stm32_streams_##index, \ }; \ \ static struct dma_stm32_data dma_stm32_data_##index = { \ }; \ \ DEVICE_DT_INST_DEFINE(index, \ &dma_stm32_init, \ NULL, \ &dma_stm32_data_##index, &dma_stm32_config_##index, \ PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY, \ &dma_funcs); DT_INST_FOREACH_STATUS_OKAY(DMA_STM32_INIT_DEV)