drivers: flash: octo spi for stm32 with DMA
Introducing the dma transfer (also through dmamux) to transfer data to/from the NOR octo-flash With a DMAMUX, the DMA channel is given by the DTS. Note that STM32U5X does not support DMA here. Signed-off-by: Francois Ramu <francois.ramu@st.com>
This commit is contained in:
parent
0fe776c5a2
commit
b6ee1dfe6e
2 changed files with 165 additions and 1 deletions
|
@ -3,6 +3,9 @@
|
|||
# Copyright (c) 2022 STMicroelectronics
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
DT_STM32_OCTOSPI_1_HAS_DMA := $(dt_nodelabel_has_prop,octospi1,dmas)
|
||||
DT_STM32_OCTOSPI_2_HAS_DMA := $(dt_nodelabel_has_prop,octospi2,dmas)
|
||||
|
||||
config FLASH_STM32_OSPI
|
||||
bool "STM32 Octo SPI Flash driver"
|
||||
default y
|
||||
|
@ -14,5 +17,8 @@ config FLASH_STM32_OSPI
|
|||
select FLASH_JESD216
|
||||
select FLASH_PAGE_LAYOUT
|
||||
select FLASH_HAS_PAGE_LAYOUT
|
||||
select DMA if $(DT_STM32_OCTOSPI_1_HAS_DMA) || $(DT_STM32_OCTOSPI_2_HAS_DMA)
|
||||
select USE_STM32_HAL_DMA if $(DT_STM32_OCTOSPI_1_HAS_DMA) || \
|
||||
$(DT_STM32_OCTOSPI_2_HAS_DMA)
|
||||
help
|
||||
Enable OSPI-NOR support on the STM32 family of processors.
|
||||
|
|
|
@ -28,6 +28,13 @@ LOG_MODULE_REGISTER(flash_stm32_ospi, CONFIG_FLASH_LOG_LEVEL);
|
|||
|
||||
#define STM32_OSPI_RESET_GPIO DT_INST_NODE_HAS_PROP(0, reset_gpios)
|
||||
|
||||
#define STM32_OSPI_USE_DMA DT_NODE_HAS_PROP(DT_PARENT(DT_DRV_INST(0)), dmas)
|
||||
#if STM32_OSPI_USE_DMA
|
||||
#include <zephyr/drivers/dma/dma_stm32.h>
|
||||
#include <zephyr/drivers/dma.h>
|
||||
#include <stm32_ll_dma.h>
|
||||
#endif /* STM32_OSPI_USE_DMA */
|
||||
|
||||
#define STM32_OSPI_FIFO_THRESHOLD 4
|
||||
#define STM32_OSPI_CLOCK_PRESCALER_MAX 255
|
||||
|
||||
|
@ -41,6 +48,35 @@ LOG_MODULE_REGISTER(flash_stm32_ospi, CONFIG_FLASH_LOG_LEVEL);
|
|||
/* used as default value for DTS writeoc */
|
||||
#define SPI_NOR_WRITEOC_NONE 0xFF
|
||||
|
||||
#if STM32_OSPI_USE_DMA
|
||||
uint32_t table_m_size[] = {
|
||||
LL_DMA_MDATAALIGN_BYTE,
|
||||
LL_DMA_MDATAALIGN_HALFWORD,
|
||||
LL_DMA_MDATAALIGN_WORD,
|
||||
};
|
||||
|
||||
uint32_t table_p_size[] = {
|
||||
LL_DMA_PDATAALIGN_BYTE,
|
||||
LL_DMA_PDATAALIGN_HALFWORD,
|
||||
LL_DMA_PDATAALIGN_WORD,
|
||||
};
|
||||
|
||||
/* Lookup table to set dma priority from the DTS */
|
||||
uint32_t table_priority[] = {
|
||||
DMA_PRIORITY_LOW,
|
||||
DMA_PRIORITY_MEDIUM,
|
||||
DMA_PRIORITY_HIGH,
|
||||
DMA_PRIORITY_VERY_HIGH,
|
||||
};
|
||||
|
||||
struct stream {
|
||||
DMA_TypeDef *reg;
|
||||
const struct device *dev;
|
||||
uint32_t channel;
|
||||
struct dma_config cfg;
|
||||
};
|
||||
#endif /* STM32_OSPI_USE_DMA */
|
||||
|
||||
typedef void (*irq_config_func_t)(const struct device *dev);
|
||||
|
||||
struct flash_stm32_ospi_config {
|
||||
|
@ -80,6 +116,9 @@ struct flash_stm32_ospi_data {
|
|||
enum jesd216_mode_type read_mode;
|
||||
enum jesd216_dw15_qer_type qer_type;
|
||||
int cmd_status;
|
||||
#if STM32_OSPI_USE_DMA
|
||||
struct stream dma;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline void ospi_lock_thread(const struct device *dev)
|
||||
|
@ -132,8 +171,11 @@ static int ospi_read_access(const struct device *dev, OSPI_RegularCmdTypeDef *cm
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
#if STM32_OSPI_USE_DMA
|
||||
hal_ret = HAL_OSPI_Receive_DMA(&dev_data->hospi, data);
|
||||
#else
|
||||
hal_ret = HAL_OSPI_Receive_IT(&dev_data->hospi, data);
|
||||
|
||||
#endif
|
||||
if (hal_ret != HAL_OK) {
|
||||
LOG_ERR("%d: Failed to read data", hal_ret);
|
||||
return -EIO;
|
||||
|
@ -170,7 +212,11 @@ static int ospi_write_access(const struct device *dev, OSPI_RegularCmdTypeDef *c
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
#if STM32_OSPI_USE_DMA
|
||||
hal_ret = HAL_OSPI_Transmit_DMA(&dev_data->hospi, (uint8_t *)data);
|
||||
#else
|
||||
hal_ret = HAL_OSPI_Transmit_IT(&dev_data->hospi, (uint8_t *)data);
|
||||
#endif
|
||||
|
||||
if (hal_ret != HAL_OK) {
|
||||
LOG_ERR("%d: Failed to read data", hal_ret);
|
||||
|
@ -1081,6 +1127,24 @@ __weak HAL_StatusTypeDef HAL_DMA_Abort(DMA_HandleTypeDef *hdma)
|
|||
return HAL_OK;
|
||||
}
|
||||
#endif /* !CONFIG_SOC_SERIES_STM32H7X */
|
||||
|
||||
/* This function is executed in the interrupt context */
|
||||
#if STM32_OSPI_USE_DMA
|
||||
static void ospi_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.
|
||||
*/
|
||||
|
@ -1591,6 +1655,68 @@ static int flash_stm32_ospi_init(const struct device *dev)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
#if STM32_OSPI_USE_DMA
|
||||
/*
|
||||
* DMA configuration
|
||||
* Due to use of OSPI 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.
|
||||
*/
|
||||
struct dma_config dma_cfg = dev_data->dma.cfg;
|
||||
static DMA_HandleTypeDef hdma;
|
||||
|
||||
if (!device_is_ready(dev_data->dma.dev)) {
|
||||
LOG_ERR("%s device not ready", dev_data->dma.dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Proceed to the minimum Zephyr DMA driver init */
|
||||
dma_cfg.user_data = &hdma;
|
||||
/* HACK: This field is used to inform driver that it is overridden */
|
||||
dma_cfg.linked_channel = STM32_DMA_HAL_OVERRIDE;
|
||||
/* Because of the STREAM OFFSET, the DMA channel given here is from 1 - 8 */
|
||||
ret = dma_config(dev_data->dma.dev, dev_data->dma.channel + 1, &dma_cfg);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Proceed to the HAL DMA driver init */
|
||||
if (dma_cfg.source_data_size != dma_cfg.dest_data_size) {
|
||||
LOG_ERR("Source and destination data sizes not aligned");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int index = find_lsb_set(dma_cfg.source_data_size) - 1;
|
||||
|
||||
hdma.Init.PeriphDataAlignment = table_p_size[index];
|
||||
hdma.Init.MemDataAlignment = table_m_size[index];
|
||||
hdma.Init.PeriphInc = DMA_PINC_DISABLE;
|
||||
hdma.Init.MemInc = DMA_MINC_ENABLE;
|
||||
hdma.Init.Mode = DMA_NORMAL;
|
||||
hdma.Init.Priority = table_priority[dma_cfg.channel_priority];
|
||||
hdma.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
||||
hdma.Init.Request = dma_cfg.dma_slot;
|
||||
#ifdef CONFIG_DMAMUX_STM32
|
||||
/*
|
||||
* HAL expects a valid DMA channel (not DMAMUX).
|
||||
* The channel is from 0 to 7 because of the STREAM_OFFSET in the dma_stm32 driver
|
||||
*/
|
||||
hdma.Instance = __LL_DMA_GET_CHANNEL_INSTANCE(dev_data->dma.reg,
|
||||
dev_data->dma.channel);
|
||||
#else
|
||||
hdma.Instance = __LL_DMA_GET_CHANNEL_INSTANCE(dev_data->dma.reg,
|
||||
dev_data->dma.channel-1);
|
||||
#endif /* CONFIG_DMAMUX_STM32 */
|
||||
|
||||
/* Initialize DMA HAL */
|
||||
__HAL_LINKDMA(&dev_data->hospi, hdma, hdma);
|
||||
HAL_DMA_Init(&hdma);
|
||||
LOG_INF("OSPI with DMA transfer");
|
||||
|
||||
#endif /* STM32_OSPI_USE_DMA */
|
||||
|
||||
/* Clock configuration */
|
||||
if (clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE),
|
||||
(clock_control_subsys_t) &dev_cfg->pclken[0]) != 0) {
|
||||
|
@ -1788,6 +1914,37 @@ static int flash_stm32_ospi_init(const struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if STM32_OSPI_USE_DMA
|
||||
#define DMA_CHANNEL_CONFIG(node, dir) \
|
||||
DT_DMAS_CELL_BY_NAME(node, dir, channel_config)
|
||||
|
||||
#define OSPI_DMA_CHANNEL_INIT(node, dir) \
|
||||
.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), \
|
||||
.source_data_size = STM32_DMA_CONFIG_PERIPHERAL_DATA_SIZE( \
|
||||
DMA_CHANNEL_CONFIG(node, dir)), \
|
||||
.dest_data_size = STM32_DMA_CONFIG_MEMORY_DATA_SIZE( \
|
||||
DMA_CHANNEL_CONFIG(node, dir)), \
|
||||
.channel_priority = STM32_DMA_CONFIG_PRIORITY( \
|
||||
DMA_CHANNEL_CONFIG(node, dir)), \
|
||||
.dma_callback = ospi_dma_callback, \
|
||||
}, \
|
||||
|
||||
#define OSPI_DMA_CHANNEL(node, dir) \
|
||||
.dma = { \
|
||||
COND_CODE_1(DT_DMAS_HAS_NAME(node, dir), \
|
||||
(OSPI_DMA_CHANNEL_INIT(node, dir)), \
|
||||
(NULL)) \
|
||||
},
|
||||
|
||||
#else
|
||||
#define OSPI_DMA_CHANNEL(node, dir)
|
||||
#endif /* CONFIG_USE_STM32_HAL_DMA */
|
||||
|
||||
#define OSPI_FLASH_MODULE(drv_id, flash_id) \
|
||||
(DT_DRV_INST(drv_id), ospi_nor_flash_##flash_id)
|
||||
|
||||
|
@ -1840,6 +1997,7 @@ static struct flash_stm32_ospi_data flash_stm32_ospi_dev_data = {
|
|||
},
|
||||
.qer_type = DT_QER_PROP_OR(0, JESD216_DW15_QER_VAL_S1B6),
|
||||
.write_opcode = DT_WRITEOC_PROP_OR(0, SPI_NOR_WRITEOC_NONE),
|
||||
OSPI_DMA_CHANNEL(STM32_OSPI_NODE, tx_rx)
|
||||
};
|
||||
|
||||
DEVICE_DT_INST_DEFINE(0, &flash_stm32_ospi_init, NULL,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue