drivers: flash: stm32 xspi driver with DMA support
Add the commands to read and write to/from the xSPI with gpDMA On the stm32h5 device, the one request for xspi instance Signed-off-by: Francois Ramu <francois.ramu@st.com>
This commit is contained in:
parent
be55b2c263
commit
ac48369731
3 changed files with 210 additions and 1 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 <zephyr/drivers/dma/dma_stm32.h>
|
||||
#include <zephyr/drivers/dma.h>
|
||||
#include <stm32_ll_dma.h>
|
||||
#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)
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
Loading…
Reference in a new issue