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
|
# Copyright (c) 2022 STMicroelectronics
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# 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
|
config FLASH_STM32_OSPI
|
||||||
bool "STM32 Octo SPI Flash driver"
|
bool "STM32 Octo SPI Flash driver"
|
||||||
default y
|
default y
|
||||||
|
@ -14,5 +17,8 @@ config FLASH_STM32_OSPI
|
||||||
select FLASH_JESD216
|
select FLASH_JESD216
|
||||||
select FLASH_PAGE_LAYOUT
|
select FLASH_PAGE_LAYOUT
|
||||||
select FLASH_HAS_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
|
help
|
||||||
Enable OSPI-NOR support on the STM32 family of processors.
|
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_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_FIFO_THRESHOLD 4
|
||||||
#define STM32_OSPI_CLOCK_PRESCALER_MAX 255
|
#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 */
|
/* used as default value for DTS writeoc */
|
||||||
#define SPI_NOR_WRITEOC_NONE 0xFF
|
#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);
|
typedef void (*irq_config_func_t)(const struct device *dev);
|
||||||
|
|
||||||
struct flash_stm32_ospi_config {
|
struct flash_stm32_ospi_config {
|
||||||
|
@ -80,6 +116,9 @@ struct flash_stm32_ospi_data {
|
||||||
enum jesd216_mode_type read_mode;
|
enum jesd216_mode_type read_mode;
|
||||||
enum jesd216_dw15_qer_type qer_type;
|
enum jesd216_dw15_qer_type qer_type;
|
||||||
int cmd_status;
|
int cmd_status;
|
||||||
|
#if STM32_OSPI_USE_DMA
|
||||||
|
struct stream dma;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void ospi_lock_thread(const struct device *dev)
|
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;
|
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);
|
hal_ret = HAL_OSPI_Receive_IT(&dev_data->hospi, data);
|
||||||
|
#endif
|
||||||
if (hal_ret != HAL_OK) {
|
if (hal_ret != HAL_OK) {
|
||||||
LOG_ERR("%d: Failed to read data", hal_ret);
|
LOG_ERR("%d: Failed to read data", hal_ret);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
@ -170,7 +212,11 @@ static int ospi_write_access(const struct device *dev, OSPI_RegularCmdTypeDef *c
|
||||||
return -EIO;
|
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);
|
hal_ret = HAL_OSPI_Transmit_IT(&dev_data->hospi, (uint8_t *)data);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (hal_ret != HAL_OK) {
|
if (hal_ret != HAL_OK) {
|
||||||
LOG_ERR("%d: Failed to read data", hal_ret);
|
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;
|
return HAL_OK;
|
||||||
}
|
}
|
||||||
#endif /* !CONFIG_SOC_SERIES_STM32H7X */
|
#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.
|
* Transfer Error callback.
|
||||||
*/
|
*/
|
||||||
|
@ -1591,6 +1655,68 @@ static int flash_stm32_ospi_init(const struct device *dev)
|
||||||
return -ENODEV;
|
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 */
|
/* Clock configuration */
|
||||||
if (clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE),
|
if (clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE),
|
||||||
(clock_control_subsys_t) &dev_cfg->pclken[0]) != 0) {
|
(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;
|
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) \
|
#define OSPI_FLASH_MODULE(drv_id, flash_id) \
|
||||||
(DT_DRV_INST(drv_id), ospi_nor_flash_##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),
|
.qer_type = DT_QER_PROP_OR(0, JESD216_DW15_QER_VAL_S1B6),
|
||||||
.write_opcode = DT_WRITEOC_PROP_OR(0, SPI_NOR_WRITEOC_NONE),
|
.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,
|
DEVICE_DT_INST_DEFINE(0, &flash_stm32_ospi_init, NULL,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue