diff --git a/drivers/dma/CMakeLists.txt b/drivers/dma/CMakeLists.txt index 51347993eb1..4a55b9a3e51 100644 --- a/drivers/dma/CMakeLists.txt +++ b/drivers/dma/CMakeLists.txt @@ -6,6 +6,7 @@ zephyr_library_sources_ifdef(CONFIG_DMA_SAM_XDMAC dma_sam_xdmac.c) zephyr_library_sources_ifdef(CONFIG_DMA_STM32 dma_stm32.c) zephyr_library_sources_ifdef(CONFIG_DMA_STM32_V1 dma_stm32_v1.c) zephyr_library_sources_ifdef(CONFIG_DMA_STM32_V2 dma_stm32_v2.c) +zephyr_library_sources_ifdef(CONFIG_DMAMUX_STM32 dmamux_stm32.c) zephyr_library_sources_ifdef(CONFIG_DMA_DW dma_dw.c) zephyr_library_sources_ifdef(CONFIG_DMA_NIOS2_MSGDMA dma_nios2_msgdma.c) zephyr_library_sources_ifdef(CONFIG_DMA_SAM0 dma_sam0.c) diff --git a/drivers/dma/Kconfig.stm32 b/drivers/dma/Kconfig.stm32 index 6e25fc9a3b7..f2199bd29e0 100644 --- a/drivers/dma/Kconfig.stm32 +++ b/drivers/dma/Kconfig.stm32 @@ -22,9 +22,16 @@ config DMA_STM32_V1 config DMA_STM32_V2 bool - depends on SOC_SERIES_STM32F0X || SOC_SERIES_STM32F1X || SOC_SERIES_STM32F3X || SOC_SERIES_STM32L0X || SOC_SERIES_STM32L4X + depends on SOC_SERIES_STM32F0X || SOC_SERIES_STM32F1X || SOC_SERIES_STM32F3X || SOC_SERIES_STM32L0X || SOC_SERIES_STM32L4X || SOC_SERIES_STM32WBX select USE_STM32_LL_DMA help - Enable DMA support on F0/F1/F3/L0/L4 series SoCs. + Enable DMA support on F0/F1/F3/L0/L4/WB series SoCs. + +config DMAMUX_STM32 + bool + depends on DMA_STM32_V2 + depends on SOC_STM32L4R5XX || SOC_SERIES_STM32WBX + help + Enable DMAMUX support on L4R/WB series SoCs. endif # DMA_STM32 diff --git a/drivers/dma/dma_stm32.c b/drivers/dma/dma_stm32.c index 5c7e1d97388..928d117d074 100644 --- a/drivers/dma/dma_stm32.c +++ b/drivers/dma/dma_stm32.c @@ -78,24 +78,46 @@ static void dma_stm32_irq_handler(void *arg) } stream = &data->streams[id]; - stream->busy = false; + + if (!IS_ENABLED(CONFIG_DMAMUX_STM32)) { + stream->busy = false; + } /* the dma stream id is in range from STREAM_OFFSET.. */ if (func_ll_is_active_tc[id](dma)) { func_ll_clear_tc[id](dma); + +#ifdef CONFIG_DMAMUX_STM32 + stream->busy = false; + /* the callback function expects the dmamux channel nb */ + stream->dma_callback(stream->callback_arg, + stream->mux_channel, 0); +#else stream->dma_callback(stream->callback_arg, id + STREAM_OFFSET, 0); +#endif /* CONFIG_DMAMUX_STM32 */ } else if (stm32_dma_is_unexpected_irq_happened(dma, id)) { LOG_ERR("Unexpected irq happened."); + +#ifdef CONFIG_DMAMUX_STM32 + stream->dma_callback(stream->callback_arg, + stream->mux_channel, -EIO); +#else stream->dma_callback(stream->callback_arg, id + STREAM_OFFSET, -EIO); +#endif /* CONFIG_DMAMUX_STM32 */ } else { LOG_ERR("Transfer Error."); dma_stm32_dump_stream_irq(dev, id); dma_stm32_clear_stream_irq(dev, id); +#ifdef CONFIG_DMAMUX_STM32 + stream->dma_callback(stream->callback_arg, + stream->mux_channel, -EIO); +#else stream->dma_callback(stream->callback_arg, id + STREAM_OFFSET, -EIO); +#endif /* CONFIG_DMAMUX_STM32 */ } } @@ -210,8 +232,13 @@ static int dma_stm32_get_periph_increment(enum dma_addr_adj increment, return 0; } +#ifdef CONFIG_DMAMUX_STM32 +int dma_stm32_configure(struct device *dev, u32_t id, + struct dma_config *config) +#else static int dma_stm32_configure(struct device *dev, u32_t id, struct dma_config *config) +#endif /* CONFIG_DMAMUX_STM32 */ { struct dma_stm32_data *data = dev->driver_data; struct dma_stm32_stream *stream = &data->streams[id - STREAM_OFFSET]; @@ -399,9 +426,10 @@ static int dma_stm32_configure(struct device *dev, u32_t id, DMA_InitStruct.NbData = config->head_block->block_size / config->dest_data_size; } -#if defined(CONFIG_DMA_STM32_V2) + +#if defined(CONFIG_DMA_STM32_V2) || defined(CONFIG_DMAMUX_STM32) /* - * the with dma V2, + * the with dma V2 and dma mux, * the request ID is stored in the dma_slot */ DMA_InitStruct.PeriphRequest = config->dma_slot; @@ -441,8 +469,13 @@ static int dma_stm32_disable_stream(DMA_TypeDef *dma, u32_t id) return 0; } +#ifdef CONFIG_DMAMUX_STM32 +int dma_stm32_reload(struct device *dev, u32_t id, + u32_t src, u32_t dst, size_t size) +#else static int dma_stm32_reload(struct device *dev, u32_t id, u32_t src, u32_t dst, size_t size) +#endif /* CONFIG_DMAMUX_STM32 */ { const struct dma_stm32_config *config = dev->config->config_info; DMA_TypeDef *dma = (DMA_TypeDef *)(config->base); @@ -485,7 +518,11 @@ static int dma_stm32_reload(struct device *dev, u32_t id, return 0; } +#ifdef CONFIG_DMAMUX_STM32 +int dma_stm32_start(struct device *dev, u32_t id) +#else static int dma_stm32_start(struct device *dev, u32_t id) +#endif /* CONFIG_DMAMUX_STM32 */ { const struct dma_stm32_config *config = dev->config->config_info; DMA_TypeDef *dma = (DMA_TypeDef *)(config->base); @@ -506,7 +543,11 @@ static int dma_stm32_start(struct device *dev, u32_t id) return 0; } +#ifdef CONFIG_DMAMUX_STM32 +int dma_stm32_stop(struct device *dev, u32_t id) +#else static int dma_stm32_stop(struct device *dev, u32_t id) +#endif /* CONFIG_DMAMUX_STM32 */ { struct dma_stm32_data *data = dev->driver_data; struct dma_stm32_stream *stream = &data->streams[id - STREAM_OFFSET]; @@ -521,7 +562,10 @@ static int dma_stm32_stop(struct device *dev, u32_t id) return -EINVAL; } +#ifndef CONFIG_DMAMUX_STM32 LL_DMA_DisableIT_TC(dma, table_ll_stream[id]); +#endif /* CONFIG_DMAMUX_STM32 */ + #if defined(CONFIG_DMA_STM32_V1) stm32_dma_disable_fifo_irq(dma, id); #endif @@ -560,8 +604,17 @@ static int dma_stm32_init(struct device *dev) } memset(data->streams, 0, size_stream); +#ifdef CONFIG_DMAMUX_STM32 + int offset = ((dev == device_get_binding((const char *)"DMA_1")) + ? 0 : data->max_streams); +#endif /* CONFIG_DMAMUX_STM32 */ + for (int i = 0; i < data->max_streams; i++) { data->streams[i].busy = false; +#ifdef CONFIG_DMAMUX_STM32 + /* each further stream->mux_channel is fixed here */ + data->streams[i].mux_channel = i + offset; +#endif /* CONFIG_DMAMUX_STM32 */ } return 0; diff --git a/drivers/dma/dma_stm32.h b/drivers/dma/dma_stm32.h index b8df05dcab0..5b10ffc0bfa 100644 --- a/drivers/dma/dma_stm32.h +++ b/drivers/dma/dma_stm32.h @@ -12,6 +12,9 @@ struct dma_stm32_stream { u32_t direction; +#ifdef CONFIG_DMAMUX_STM32 + int mux_channel; /* stores the dmamux channel */ +#endif /* CONFIG_DMAMUX_STM32 */ bool source_periph; bool busy; u32_t src_size; @@ -71,4 +74,14 @@ u32_t stm32_dma_get_mburst(struct dma_config *config, bool source_periph); u32_t stm32_dma_get_pburst(struct dma_config *config, bool source_periph); #endif +#ifdef CONFIG_DMAMUX_STM32 +/* dma_stm32_ api functions are exported to the dmamux_stm32 */ +int dma_stm32_configure(struct device *dev, u32_t id, + struct dma_config *config); +int dma_stm32_reload(struct device *dev, u32_t id, + u32_t src, u32_t dst, size_t size); +int dma_stm32_start(struct device *dev, u32_t id); +int dma_stm32_stop(struct device *dev, u32_t id); +#endif /* CONFIG_DMAMUX_STM32 */ + #endif /* DMA_STM32_H_*/ diff --git a/drivers/dma/dmamux_stm32.c b/drivers/dma/dmamux_stm32.c new file mode 100644 index 00000000000..b01c1b2f7be --- /dev/null +++ b/drivers/dma/dmamux_stm32.c @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2020 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Common part of DMAMUX drivers for stm32. + * @note api functions named dmamux_stm32_ + * are calling the dma_stm32 corresponding function + * implemented in dma_stm32.c + */ + +#include +#include +#include +#include +#include + +#include "dmamux_stm32.h" +#include "dma_stm32.h" + +#include +LOG_MODULE_REGISTER(dmamux_stm32, CONFIG_DMA_LOG_LEVEL); + +#define DT_DRV_COMPAT st_stm32_dmamux + +int dmamux_stm32_configure(struct device *dev, u32_t id, + struct dma_config *config) +{ + /* device is the dmamux, id is the dmamux channel from 0 */ + struct dmamux_stm32_data *data = dev->driver_data; + const struct dmamux_stm32_config *dev_config = + dev->config->config_info; + + /* + * request line ID for this mux channel is stored + * in the dma_slot parameter + */ + int request_id = config->dma_slot; + + if (request_id >= dev_config->req_nb + dev_config->gen_nb) { + LOG_ERR("request ID %d is too big.", request_id); + return -EINVAL; + } + + /* check if this channel is valid */ + if (id >= dev_config->channel_nb) { + LOG_ERR("channel ID %d is too big.", id); + return -EINVAL; + } + + /* + * Also configures the corresponding dma channel + * instance is given by the dev_dma + * stream is given by the index i + * config is directly this dma_config + */ + + /* + * This dmamux channel 'id' is now used for this peripheral request + * It gives this mux request ID to the dma through the config.dma_slot + */ + if (dma_stm32_configure(data->mux_channels[id].dev_dma, + data->mux_channels[id].dma_id, config) != 0) { + LOG_ERR("cannot configure the dmamux."); + return -EINVAL; + } + + /* set the Request Line ID to this dmamux channel i */ + DMAMUX_Channel_TypeDef *dmamux = + (DMAMUX_Channel_TypeDef *)dev_config->base; + + LL_DMAMUX_SetRequestID(dmamux, id, request_id); + + return 0; +} + +int dmamux_stm32_start(struct device *dev, u32_t id) +{ + const struct dmamux_stm32_config *dev_config = + dev->config->config_info; + struct dmamux_stm32_data *data = dev->driver_data; + + /* check if this channel is valid */ + if (id >= dev_config->channel_nb) { + LOG_ERR("channel ID %d is too big.", id); + return -EINVAL; + } + + if (dma_stm32_start(data->mux_channels[id].dev_dma, + data->mux_channels[id].dma_id) != 0) { + LOG_ERR("cannot start the dmamux channel %d.", id); + return -EINVAL; + } + + return 0; +} + +int dmamux_stm32_stop(struct device *dev, u32_t id) +{ + const struct dmamux_stm32_config *dev_config = + dev->config->config_info; + struct dmamux_stm32_data *data = dev->driver_data; + + /* check if this channel is valid */ + if (id >= dev_config->channel_nb) { + LOG_ERR("channel ID %d is too big.", id); + return -EINVAL; + } + + if (dma_stm32_stop(data->mux_channels[id].dev_dma, + data->mux_channels[id].dma_id) != 0) { + LOG_ERR("cannot stop the dmamux channel %d.", id); + return -EINVAL; + } + + return 0; +} + +int dmamux_stm32_reload(struct device *dev, u32_t id, + u32_t src, u32_t dst, size_t size) +{ + const struct dmamux_stm32_config *dev_config = + dev->config->config_info; + struct dmamux_stm32_data *data = dev->driver_data; + + /* check if this channel is valid */ + if (id >= dev_config->channel_nb) { + LOG_ERR("channel ID %d is too big.", id); + return -EINVAL; + } + + if (dma_stm32_reload(data->mux_channels[id].dev_dma, + data->mux_channels[id].dma_id, + src, dst, size) != 0) { + LOG_ERR("cannot reload the dmamux channel %d.", id); + return -EINVAL; + } + + return 0; +} + +static int dmamux_stm32_init(struct device *dev) +{ + struct dmamux_stm32_data *data = dev->driver_data; + const struct dmamux_stm32_config *config = + dev->config->config_info; + struct device *clk = + device_get_binding(STM32_CLOCK_CONTROL_NAME); + + if (clock_control_on(clk, + (clock_control_subsys_t *) &config->pclken) != 0) { + LOG_ERR("clock op failed\n"); + return -EIO; + } + + int size_stream = + sizeof(struct dmamux_stm32_channel) * config->channel_nb; + data->mux_channels = k_malloc(size_stream); + if (!data->mux_channels) { + LOG_ERR("HEAP_MEM_POOL_SIZE is too small"); + return -ENOMEM; + } + for (int i = 0; i < config->channel_nb; i++) { + /* + * associates the dmamux channel + * to the corresponding dma stream + */ + if (i < config->channel_nb / 2) { + data->mux_channels[i].dev_dma = + device_get_binding((const char *)"DMA_1"); + /* dma 1 channels from 1 to N */ + data->mux_channels[i].dma_id = i + 1; + } else { + data->mux_channels[i].dev_dma = + device_get_binding((const char *)"DMA_2"); + data->mux_channels[i].dma_id = + /* dma 2 channels from 1 to N */ + i - config->channel_nb / 2 + 1; + } + } + + return 0; +} + +static const struct dma_driver_api dma_funcs = { + .reload = dmamux_stm32_reload, + .config = dmamux_stm32_configure, + .start = dmamux_stm32_start, + .stop = dmamux_stm32_stop, +}; + +#define DMAMUX_INIT(index) \ + \ +const struct dmamux_stm32_config dmamux_stm32_config_##index = {\ + .pclken = { .bus = DT_INST_CLOCKS_CELL(index, bus), \ + .enr = DT_INST_CLOCKS_CELL(index, bits) }, \ + .base = DT_INST_REG_ADDR(index), \ + .channel_nb = DT_INST_PROP(index, dma_channels), \ + .gen_nb = DT_INST_PROP(index, dma_generators), \ + .req_nb = DT_INST_PROP(index, dma_requests), \ +}; \ + \ +static struct dmamux_stm32_data dmamux_stm32_data_##index = { \ + .mux_channels = NULL, \ +}; \ + \ +DEVICE_AND_API_INIT(dmamux_##index, DT_INST_LABEL(index), \ + &dmamux_stm32_init, \ + &dmamux_stm32_data_##index, &dmamux_stm32_config_##index,\ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,\ + &dma_funcs) + +#if DT_HAS_DRV_INST(0) +DMAMUX_INIT(0); +#endif /* DT_HAS_DRV_INST(0) */ diff --git a/drivers/dma/dmamux_stm32.h b/drivers/dma/dmamux_stm32.h new file mode 100644 index 00000000000..238b2d181dc --- /dev/null +++ b/drivers/dma/dmamux_stm32.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2020 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef DMAMUX_STM32_H_ +#define DMAMUX_STM32_H_ + +/* this is the configuration of one dmamux channel */ +struct dmamux_stm32_channel { + struct device *dev_dma; /* pointer to the associated dma instance */ + u8_t dma_id; /* ref of the associated dma stream for this instance */ +}; + +/* the table of all the dmamux channel */ +struct dmamux_stm32_data { + struct dmamux_stm32_channel *mux_channels; + void *callback_arg; + void (*dmamux_callback)(void *arg, u32_t id, + int error_code); +}; + +/* this is the configuration of the dmamux IP */ +struct dmamux_stm32_config { + struct stm32_pclken pclken; + u32_t base; + u8_t channel_nb; /* total nb of channels */ + u8_t gen_nb; /* total nb of Request generator */ + u8_t req_nb; /* total nb of Peripheral Request inputs */ +}; + +u32_t table_ll_channel[] = { + LL_DMAMUX_CHANNEL_0, + LL_DMAMUX_CHANNEL_1, + LL_DMAMUX_CHANNEL_2, + LL_DMAMUX_CHANNEL_3, + LL_DMAMUX_CHANNEL_4, +#if defined(LL_DMAMUX_CHANNEL_5) + LL_DMAMUX_CHANNEL_5, +#endif /* LL_DMAMUX_CHANNEL_5 */ +#if defined(LL_DMAMUX_CHANNEL_6) + LL_DMAMUX_CHANNEL_6, +#endif /* LL_DMAMUX_CHANNEL_6 */ +#if defined(LL_DMAMUX_CHANNEL_7) + LL_DMAMUX_CHANNEL_7, +#endif /* LL_DMAMUX_CHANNEL_7 */ +#if defined(LL_DMAMUX_CHANNEL_8) + LL_DMAMUX_CHANNEL_8, +#endif /* LL_DMAMUX_CHANNEL_8 */ +#if defined(LL_DMAMUX_CHANNEL_9) + LL_DMAMUX_CHANNEL_9, +#endif /* LL_DMAMUX_CHANNEL_9 */ +#if defined(LL_DMAMUX_CHANNEL_10) + LL_DMAMUX_CHANNEL_10, +#endif /* LL_DMAMUX_CHANNEL_10 */ +#if defined(LL_DMAMUX_CHANNEL_11) + LL_DMAMUX_CHANNEL_11, +#endif /* LL_DMAMUX_CHANNEL_11 */ +#if defined(LL_DMAMUX_CHANNEL_12) + LL_DMAMUX_CHANNEL_12, +#endif /* LL_DMAMUX_CHANNEL_12 */ +#if defined(LL_DMAMUX_CHANNEL_13) + LL_DMAMUX_CHANNEL_13, +#endif /* LL_DMAMUX_CHANNEL_13 */ +#if defined(LL_DMAMUX_CHANNEL_14) + LL_DMAMUX_CHANNEL_14, +#endif /* LL_DMAMUX_CHANNEL_14 */ +#if defined(LL_DMAMUX_CHANNEL_15) + LL_DMAMUX_CHANNEL_15, +#endif /* LL_DMAMUX_CHANNEL_15 */ +}; + +u32_t (*func_ll_is_active_so[])(DMAMUX_Channel_TypeDef *DMAMUXx) = { + LL_DMAMUX_IsActiveFlag_SO0, + LL_DMAMUX_IsActiveFlag_SO1, + LL_DMAMUX_IsActiveFlag_SO2, + LL_DMAMUX_IsActiveFlag_SO3, + LL_DMAMUX_IsActiveFlag_SO4, + LL_DMAMUX_IsActiveFlag_SO5, +#if defined(DMAMUX_CSR_SOF6) + LL_DMAMUX_IsActiveFlag_SO6, +#endif /* DMAMUX_CSR_SOF6 */ +#if defined(DMAMUX_CSR_SOF7) + LL_DMAMUX_IsActiveFlag_SO7, +#endif /* DMAMUX_CSR_SOF7 */ +#if defined(DMAMUX_CSR_SOF8) + LL_DMAMUX_IsActiveFlag_SO8, +#endif /* DMAMUX_CSR_SOF8 */ +#if defined(DMAMUX_CSR_SOF9) + LL_DMAMUX_IsActiveFlag_SO9, +#endif /* DMAMUX_CSR_SOF9 */ +#if defined(DMAMUX_CSR_SOF10) + LL_DMAMUX_IsActiveFlag_SO10, +#endif /* DMAMUX_CSR_SOF10 */ +#if defined(DMAMUX_CSR_SOF11) + LL_DMAMUX_IsActiveFlag_SO11, +#endif /* DMAMUX_CSR_SOF11 */ +#if defined(DMAMUX_CSR_SOF12) + LL_DMAMUX_IsActiveFlag_SO12, +#endif /* DMAMUX_CSR_SOF12 */ +#if defined(DMAMUX_CSR_SOF130) + LL_DMAMUX_IsActiveFlag_SO13, +#endif /* DMAMUX_CSR_SOF130 */ +#if defined(DMAMUX_CSR_SOF14) + LL_DMAMUX_IsActiveFlag_SO14, +#endif /* DMAMUX_CSR_SOF14 */ +#if defined(DMAMUX_CSR_SOF15) + LL_DMAMUX_IsActiveFlag_SO15, +#endif /* DMAMUX_CSR_SOF15 */ +}; + +void (*func_ll_clear_so[])(DMAMUX_Channel_TypeDef *DMAMUXx) = { + LL_DMAMUX_ClearFlag_SO0, + LL_DMAMUX_ClearFlag_SO1, + LL_DMAMUX_ClearFlag_SO2, + LL_DMAMUX_ClearFlag_SO3, + LL_DMAMUX_ClearFlag_SO4, + LL_DMAMUX_ClearFlag_SO5, +#if defined(DMAMUX_CSR_SOF6) + LL_DMAMUX_ClearFlag_SO6, +#endif /* DMAMUX_CSR_SOF6 */ +#if defined(DMAMUX_CSR_SOF7) + LL_DMAMUX_ClearFlag_SO7, +#endif /* DMAMUX_CSR_SOF7 */ +#if defined(DMAMUX_CSR_SOF8) + LL_DMAMUX_ClearFlag_SO8, +#endif /* DMAMUX_CSR_SOF8 */ +#if defined(DMAMUX_CSR_SOF9) + LL_DMAMUX_ClearFlag_SO9, +#endif /* DMAMUX_CSR_SOF9 */ +#if defined(DMAMUX_CSR_SOF10) + LL_DMAMUX_ClearFlag_SO10, +#endif /* DMAMUX_CSR_SOF10 */ +#if defined(DMAMUX_CSR_SOF11) + LL_DMAMUX_ClearFlag_SO11, +#endif /* DMAMUX_CSR_SOF11 */ +#if defined(DMAMUX_CSR_SOF12) + LL_DMAMUX_ClearFlag_SO12, +#endif /* DMAMUX_CSR_SOF12 */ +#if defined(DMAMUX_CSR_SOF13) + LL_DMAMUX_ClearFlag_SO13, +#endif /* DMAMUX_CSR_SOF13 */ +#if defined(DMAMUX_CSR_SOF14) + LL_DMAMUX_ClearFlag_SO14, +#endif /* DMAMUX_CSR_SOF145 */ +#if defined(DMAMUX_CSR_SOF15) + LL_DMAMUX_ClearFlag_SO15, +#endif /* DMAMUX_CSR_SOF15 */ +}; + +u32_t (*func_ll_is_active_rgo[])(DMAMUX_Channel_TypeDef *DMAMUXx) = { + LL_DMAMUX_IsActiveFlag_RGO0, + LL_DMAMUX_IsActiveFlag_RGO1, + LL_DMAMUX_IsActiveFlag_RGO2, + LL_DMAMUX_IsActiveFlag_RGO3, +}; + +void (*func_ll_clear_rgo[])(DMAMUX_Channel_TypeDef *DMAMUXx) = { + LL_DMAMUX_ClearFlag_RGO0, + LL_DMAMUX_ClearFlag_RGO1, + LL_DMAMUX_ClearFlag_RGO2, + LL_DMAMUX_ClearFlag_RGO3, +}; + +#endif /* DMAMUX_STM32_H_*/