diff --git a/drivers/flash/Kconfig.stm32_xspi b/drivers/flash/Kconfig.stm32_xspi index 3bea03801f3..56241e2ebd3 100644 --- a/drivers/flash/Kconfig.stm32_xspi +++ b/drivers/flash/Kconfig.stm32_xspi @@ -3,6 +3,9 @@ # Copyright (c) 2024 STMicroelectronics # SPDX-License-Identifier: Apache-2.0 +DT_STM32_XSPI_1_HAS_DMA := $(dt_nodelabel_has_prop,xspi1,dmas) +DT_STM32_XSPI_2_HAS_DMA := $(dt_nodelabel_has_prop,xspi2,dmas) + config FLASH_STM32_XSPI bool "STM32 XSPI Flash driver" default y @@ -13,6 +16,11 @@ config FLASH_STM32_XSPI select FLASH_JESD216 select FLASH_PAGE_LAYOUT select FLASH_HAS_PAGE_LAYOUT - + select DMA if $(DT_STM32_XSPI_1_HAS_DMA) || $(DT_STM32_XSPI_2_HAS_DMA) + select USE_STM32_HAL_DMA if $(DT_STM32_XSPI_1_HAS_DMA) || \ + $(DT_STM32_XSPI_2_HAS_DMA) + select USE_STM32_HAL_DMA_EX if SOC_SERIES_STM32H5X && \ + ($(DT_STM32_XSPI_1_HAS_DMA) || \ + $(DT_STM32_XSPI_2_HAS_DMA)) help Enable XSPI-NOR support on the STM32 family of processors. diff --git a/drivers/flash/flash_stm32_xspi.c b/drivers/flash/flash_stm32_xspi.c index f75165a90a6..ae7cbb6862b 100644 --- a/drivers/flash/flash_stm32_xspi.c +++ b/drivers/flash/flash_stm32_xspi.c @@ -44,6 +44,14 @@ LOG_MODULE_REGISTER(flash_stm32_xspi, CONFIG_FLASH_LOG_LEVEL); #define STM32_XSPI_DLYB_BYPASSED DT_PROP(STM32_XSPI_NODE, dlyb_bypass) +#define STM32_XSPI_USE_DMA DT_NODE_HAS_PROP(STM32_XSPI_NODE, dmas) + +#if STM32_XSPI_USE_DMA +#include +#include +#include +#endif /* STM32_XSPI_USE_DMA */ + #include "flash_stm32_xspi.h" static inline void xspi_lock_thread(const struct device *dev) @@ -97,7 +105,11 @@ static int xspi_read_access(const struct device *dev, XSPI_RegularCmdTypeDef *cm return -EIO; } +#if STM32_XSPI_USE_DMA + hal_ret = HAL_XSPI_Receive_DMA(&dev_data->hxspi, data); +#else hal_ret = HAL_XSPI_Receive_IT(&dev_data->hxspi, data); +#endif if (hal_ret != HAL_OK) { LOG_ERR("%d: Failed to read data", hal_ret); @@ -135,7 +147,11 @@ static int xspi_write_access(const struct device *dev, XSPI_RegularCmdTypeDef *c return -EIO; } +#if STM32_XSPI_USE_DMA + hal_ret = HAL_XSPI_Transmit_DMA(&dev_data->hxspi, (uint8_t *)data); +#else hal_ret = HAL_XSPI_Transmit_IT(&dev_data->hxspi, (uint8_t *)data); +#endif if (hal_ret != HAL_OK) { LOG_ERR("%d: Failed to write data", hal_ret); @@ -1220,6 +1236,24 @@ __weak HAL_StatusTypeDef HAL_DMA_Abort(DMA_HandleTypeDef *hdma) } #endif /* !CONFIG_SOC_SERIES_STM32H7X */ +/* This function is executed in the interrupt context */ +#if STM32_XSPI_USE_DMA +static void xspi_dma_callback(const struct device *dev, void *arg, + uint32_t channel, int status) +{ + DMA_HandleTypeDef *hdma = arg; + + ARG_UNUSED(dev); + + if (status < 0) { + LOG_ERR("DMA callback error with channel %d.", channel); + } + + HAL_DMA_IRQHandler(hdma); +} +#endif + + /* * Transfer Error callback. */ @@ -1707,6 +1741,85 @@ static int spi_nor_process_bfp(const struct device *dev, return 0; } +#if STM32_XSPI_USE_DMA +static int flash_stm32_xspi_dma_init(DMA_HandleTypeDef *hdma, struct stream *dma_stream) +{ + int ret; + /* + * DMA configuration + * Due to use of XSPI HAL API in current driver, + * both HAL and Zephyr DMA drivers should be configured. + * The required configuration for Zephyr DMA driver should only provide + * the minimum information to inform the DMA slot will be in used and + * how to route callbacks. + */ + + if (!device_is_ready(dma_stream->dev)) { + LOG_ERR("DMA %s device not ready", dma_stream->dev->name); + return -ENODEV; + } + /* Proceed to the minimum Zephyr DMA driver init of the channel */ + dma_stream->cfg.user_data = hdma; + /* HACK: This field is used to inform driver that it is overridden */ + dma_stream->cfg.linked_channel = STM32_DMA_HAL_OVERRIDE; + /* Because of the STREAM OFFSET, the DMA channel given here is from 1 - 8 */ + ret = dma_config(dma_stream->dev, + (dma_stream->channel + STM32_DMA_STREAM_OFFSET), &dma_stream->cfg); + if (ret != 0) { + LOG_ERR("Failed to configure DMA channel %d", + dma_stream->channel + STM32_DMA_STREAM_OFFSET); + return ret; + } + + /* Proceed to the HAL DMA driver init */ + if (dma_stream->cfg.source_data_size != dma_stream->cfg.dest_data_size) { + LOG_ERR("DMA Source and destination data sizes not aligned"); + return -EINVAL; + } + + hdma->Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD; /* Fixed value */ + hdma->Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD; /* Fixed value */ + hdma->Init.SrcInc = (dma_stream->src_addr_increment) + ? DMA_SINC_INCREMENTED + : DMA_SINC_FIXED; + hdma->Init.DestInc = (dma_stream->dst_addr_increment) + ? DMA_DINC_INCREMENTED + : DMA_DINC_FIXED; + hdma->Init.SrcBurstLength = 4; + hdma->Init.DestBurstLength = 4; + hdma->Init.Priority = table_priority[dma_stream->cfg.channel_priority]; + hdma->Init.Direction = table_direction[dma_stream->cfg.channel_direction]; + hdma->Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_SRC_ALLOCATED_PORT1; + hdma->Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER; + hdma->Init.Mode = DMA_NORMAL; + hdma->Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST; + hdma->Init.Request = dma_stream->cfg.dma_slot; + + /* + * HAL expects a valid DMA channel (not DMAMUX). + * The channel is from 0 to 7 because of the STM32_DMA_STREAM_OFFSET + * in the dma_stm32 driver + */ + hdma->Instance = LL_DMA_GET_CHANNEL_INSTANCE(dma_stream->reg, + dma_stream->channel); + + /* Initialize DMA HAL */ + if (HAL_DMA_Init(hdma) != HAL_OK) { + LOG_ERR("XSPI DMA Init failed"); + return -EIO; + } + + if (HAL_DMA_ConfigChannelAttributes(hdma, DMA_CHANNEL_NPRIV) != HAL_OK) { + LOG_ERR("XSPI DMA Init failed"); + return -EIO; + } + + LOG_DBG("XSPI with DMA transfer"); + return 0; +} +#endif /* STM32_XSPI_USE_DMA */ + + static int flash_stm32_xspi_init(const struct device *dev) { const struct flash_stm32_xspi_config *dev_cfg = dev->config; @@ -1859,6 +1972,28 @@ static int flash_stm32_xspi_init(const struct device *dev) #endif /* DLYB_ */ +#if STM32_XSPI_USE_DMA + /* Configure and enable the DMA channels after XSPI config */ + static DMA_HandleTypeDef hdma_tx; + static DMA_HandleTypeDef hdma_rx; + + if (flash_stm32_xspi_dma_init(&hdma_tx, &dev_data->dma_tx) != 0) { + LOG_ERR("XSPI DMA Tx init failed"); + return -EIO; + } + + /* The dma_tx handle is hold by the dma_stream.cfg.user_data */ + __HAL_LINKDMA(&dev_data->hxspi, hdmatx, hdma_tx); + + if (flash_stm32_xspi_dma_init(&hdma_rx, &dev_data->dma_rx) != 0) { + LOG_ERR("XSPI DMA Rx init failed"); + return -EIO; + } + + /* The dma_rx handle is hold by the dma_stream.cfg.user_data */ + __HAL_LINKDMA(&dev_data->hxspi, hdmarx, hdma_rx); + +#endif /* CONFIG_USE_STM32_HAL_DMA */ /* Initialize semaphores */ k_sem_init(&dev_data->sem, 1, 1); k_sem_init(&dev_data->sync, 0, 1); @@ -2004,6 +2139,39 @@ static int flash_stm32_xspi_init(const struct device *dev) return 0; } + +#if STM32_XSPI_USE_DMA +#define DMA_CHANNEL_CONFIG(node, dir) \ + DT_DMAS_CELL_BY_NAME(node, dir, channel_config) + +#define XSPI_DMA_CHANNEL_INIT(node, dir, dir_cap, src_dev, dest_dev) \ + .dev = DEVICE_DT_GET(DT_DMAS_CTLR(node)), \ + .channel = DT_DMAS_CELL_BY_NAME(node, dir, channel), \ + .reg = (DMA_TypeDef *)DT_REG_ADDR( \ + DT_PHANDLE_BY_NAME(node, dmas, dir)), \ + .cfg = { \ + .dma_slot = DT_DMAS_CELL_BY_NAME(node, dir, slot), \ + .channel_direction = STM32_DMA_CONFIG_DIRECTION( \ + DMA_CHANNEL_CONFIG(node, dir)), \ + .channel_priority = STM32_DMA_CONFIG_PRIORITY( \ + DMA_CHANNEL_CONFIG(node, dir)), \ + .dma_callback = xspi_dma_callback, \ + }, \ + .src_addr_increment = STM32_DMA_CONFIG_##src_dev##_ADDR_INC( \ + DMA_CHANNEL_CONFIG(node, dir)), \ + .dst_addr_increment = STM32_DMA_CONFIG_##dest_dev##_ADDR_INC( \ + DMA_CHANNEL_CONFIG(node, dir)), + +#define XSPI_DMA_CHANNEL(node, dir, DIR, src, dest) \ + .dma_##dir = { \ + COND_CODE_1(DT_DMAS_HAS_NAME(node, dir), \ + (XSPI_DMA_CHANNEL_INIT(node, dir, DIR, src, dest)), \ + (NULL)) \ + }, +#else +#define XSPI_DMA_CHANNEL(node, dir, DIR, src, dest) +#endif /* CONFIG_USE_STM32_HAL_DMA */ + #define XSPI_FLASH_MODULE(drv_id, flash_id) \ (DT_DRV_INST(drv_id), xspi_nor_flash_##flash_id) @@ -2065,6 +2233,8 @@ static struct flash_stm32_xspi_data flash_stm32_xspi_dev_data = { #if DT_NODE_HAS_PROP(DT_INST(0, st_stm32_ospi_nor), jedec_id) .jedec_id = DT_INST_PROP(0, jedec_id), #endif /* jedec_id */ + XSPI_DMA_CHANNEL(STM32_XSPI_NODE, tx, TX, MEMORY, PERIPHERAL) + XSPI_DMA_CHANNEL(STM32_XSPI_NODE, rx, RX, PERIPHERAL, MEMORY) }; static void flash_stm32_xspi_irq_config_func(const struct device *dev) diff --git a/drivers/flash/flash_stm32_xspi.h b/drivers/flash/flash_stm32_xspi.h index 364c89b948c..316efdbcd87 100644 --- a/drivers/flash/flash_stm32_xspi.h +++ b/drivers/flash/flash_stm32_xspi.h @@ -37,6 +37,33 @@ /* used as default value for DTS writeoc */ #define SPI_NOR_WRITEOC_NONE 0xFF +#if STM32_XSPI_USE_DMA +/* Lookup table to set dma priority from the DTS */ +static const uint32_t table_priority[] = { + DMA_LOW_PRIORITY_LOW_WEIGHT, + DMA_LOW_PRIORITY_MID_WEIGHT, + DMA_LOW_PRIORITY_HIGH_WEIGHT, + DMA_HIGH_PRIORITY, +}; + +/* Lookup table to set dma channel direction from the DTS */ +static const uint32_t table_direction[] = { + DMA_MEMORY_TO_MEMORY, + DMA_MEMORY_TO_PERIPH, + DMA_PERIPH_TO_MEMORY, +}; + +struct stream { + DMA_TypeDef *reg; + const struct device *dev; + uint32_t channel; + struct dma_config cfg; + uint8_t priority; + bool src_addr_increment; + bool dst_addr_increment; +}; +#endif /* STM32_XSPI_USE_DMA */ + typedef void (*irq_config_func_t)(const struct device *dev); struct flash_stm32_xspi_config { @@ -77,6 +104,10 @@ struct flash_stm32_xspi_data { uint8_t jedec_id[JESD216_READ_ID_LEN]; #endif /* CONFIG_FLASH_JESD216_API */ int cmd_status; +#if STM32_XSPI_USE_DMA + struct stream dma_tx; + struct stream dma_rx; +#endif /* STM32_XSPI_USE_DMA */ }; #endif /* ZEPHYR_DRIVERS_FLASH_XSPI_STM32_H_ */