From 16754378fb8c16c0242f129f54deff5fb918e574 Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Wed, 27 Oct 2021 13:20:55 +0200 Subject: [PATCH] drivers: flash : introduce a new driver for the OCTOSPI This is the stm32 octospi driver based on the exisitng quadspi for stm32 devices and source code from the STM32Cube. This drivers initialized the peripheral and the NOR memory in SPI or OctoSPI mode with STR or DTR data Transfer rates. The NOR-flash can provide the SDFP table directly (if supported) or through the DeviceTree. Limitation: no DMA transfer. Signed-off-by: Francois Ramu --- drivers/flash/CMakeLists.txt | 1 + drivers/flash/Kconfig | 2 + drivers/flash/Kconfig.stm32_ospi | 19 + drivers/flash/flash_stm32_ospi.c | 1599 ++++++++++++++++++++++++++++++ 4 files changed, 1621 insertions(+) create mode 100644 drivers/flash/Kconfig.stm32_ospi create mode 100644 drivers/flash/flash_stm32_ospi.c diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index ab79078c5b2..b7b03746342 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -20,6 +20,7 @@ zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NIOS2_QSPI soc_flash_nios2_qspi.c) zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_GECKO flash_gecko.c) zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_RV32M1 soc_flash_rv32m1.c) zephyr_library_sources_ifdef(CONFIG_FLASH_STM32_QSPI flash_stm32_qspi.c) +zephyr_library_sources_ifdef(CONFIG_FLASH_STM32_OSPI flash_stm32_ospi.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_FLEXSPI_MX25UM51345G flash_mcux_flexspi_mx25um51345g.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_FLEXSPI_NOR flash_mcux_flexspi_nor.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_FLEXSPI_HYPERFLASH flash_mcux_flexspi_hyperflash.c) diff --git a/drivers/flash/Kconfig b/drivers/flash/Kconfig index caf73bbf330..e53144c76b2 100644 --- a/drivers/flash/Kconfig +++ b/drivers/flash/Kconfig @@ -90,6 +90,8 @@ source "drivers/flash/Kconfig.stm32" source "drivers/flash/Kconfig.stm32_qspi" +source "drivers/flash/Kconfig.stm32_ospi" + source "drivers/flash/Kconfig.sam0" source "drivers/flash/Kconfig.sam" diff --git a/drivers/flash/Kconfig.stm32_ospi b/drivers/flash/Kconfig.stm32_ospi new file mode 100644 index 00000000000..f1abcbacff1 --- /dev/null +++ b/drivers/flash/Kconfig.stm32_ospi @@ -0,0 +1,19 @@ +# STM32 Octo SPI flash driver configuration options + +# Copyright (c) 2022 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +DT_COMPAT_ST_STM32_OSPI_NOR := st,stm32-ospi-nor + +config FLASH_STM32_OSPI + bool "STM32 Octo SPI Flash driver" + depends on SOC_FAMILY_STM32 + default $(dt_compat_enabled,$(DT_COMPAT_ST_STM32_OSPI_NOR)) + select USE_STM32_HAL_OSPI + select USE_STM32_LL_DLYB if SOC_SERIES_STM32U5X + select FLASH_HAS_DRIVER_ENABLED + select FLASH_JESD216 + select FLASH_PAGE_LAYOUT + select FLASH_HAS_PAGE_LAYOUT + help + Enable OSPI-NOR support on the STM32 family of processors. diff --git a/drivers/flash/flash_stm32_ospi.c b/drivers/flash/flash_stm32_ospi.c new file mode 100644 index 00000000000..9778d817d39 --- /dev/null +++ b/drivers/flash/flash_stm32_ospi.c @@ -0,0 +1,1599 @@ +/* + * Copyright (c) 2022 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT st_stm32_ospi_nor + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi_nor.h" +#include "jesd216.h" + +#include +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_FIFO_THRESHOLD 4 +#define STM32_OSPI_CLOCK_PRESCALER_MAX 255 + +/* Max Time value during reset or erase operation */ +#define STM32_OSPI_RESET_MAX_TIME 100U +#define STM32_OSPI_BULK_ERASE_MAX_TIME 460000U +#define STM32_OSPI_SECTOR_ERASE_MAX_TIME 1000U +#define STM32_OSPI_SUBSECTOR_4K_ERASE_MAX_TIME 400U +#define STM32_OSPI_WRITE_REG_MAX_TIME 40U + +typedef void (*irq_config_func_t)(const struct device *dev); + +struct flash_stm32_ospi_config { + OCTOSPI_TypeDef *regs; + struct stm32_pclken pclken; + irq_config_func_t irq_config; + size_t flash_size; + uint32_t max_frequency; + int data_mode; /* SPI or QSPI or OSPI */ + int data_rate; /* DTR or STR */ + const struct pinctrl_dev_config *pcfg; +#if STM32_OSPI_RESET_GPIO + const struct gpio_dt_spec reset; +#endif /* STM32_OSPI_RESET_GPIO */ +#if DT_NODE_HAS_PROP(DT_INST(0, st_stm32_ospi_nor), sfdp_bfp) + uint8_t sfdp_bfp[DT_INST_PROP_LEN(0, sfdp_bfp)]; +#endif /* sfdp_bfp */ +}; + +struct flash_stm32_ospi_data { + OSPI_HandleTypeDef hospi; + struct k_sem sem; + struct k_sem sync; +#if defined(CONFIG_FLASH_PAGE_LAYOUT) + struct flash_pages_layout layout; +#endif + struct jesd216_erase_type erase_types[JESD216_NUM_ERASE_TYPES]; + /* Number of bytes per page */ + uint16_t page_size; + int cmd_status; +}; + +static inline void ospi_lock_thread(const struct device *dev) +{ + struct flash_stm32_ospi_data *dev_data = dev->data; + + k_sem_take(&dev_data->sem, K_FOREVER); +} + +static inline void ospi_unlock_thread(const struct device *dev) +{ + struct flash_stm32_ospi_data *dev_data = dev->data; + + k_sem_give(&dev_data->sem); +} + +static int ospi_send_cmd(const struct device *dev, OSPI_RegularCmdTypeDef *cmd) +{ + const struct flash_stm32_ospi_config *dev_cfg = dev->config; + struct flash_stm32_ospi_data *dev_data = dev->data; + HAL_StatusTypeDef hal_ret; + + LOG_DBG("Instruction 0x%x", cmd->Instruction); + + dev_data->cmd_status = 0; + + hal_ret = HAL_OSPI_Command(&dev_data->hospi, cmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE); + if (hal_ret != HAL_OK) { + LOG_ERR("%d: Failed to send OSPI instruction", hal_ret); + return -EIO; + } + LOG_DBG("CCR 0x%x", dev_cfg->regs->CCR); + + return dev_data->cmd_status; +} + +static int ospi_read_access(const struct device *dev, OSPI_RegularCmdTypeDef *cmd, + uint8_t *data, size_t size) +{ + struct flash_stm32_ospi_data *dev_data = dev->data; + HAL_StatusTypeDef hal_ret; + + cmd->NbData = size; + + dev_data->cmd_status = 0; + + hal_ret = HAL_OSPI_Command(&dev_data->hospi, cmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE); + if (hal_ret != HAL_OK) { + LOG_ERR("%d: Failed to send OSPI instruction", hal_ret); + return -EIO; + } + + hal_ret = HAL_OSPI_Receive_IT(&dev_data->hospi, data); + + if (hal_ret != HAL_OK) { + LOG_ERR("%d: Failed to read data", hal_ret); + return -EIO; + } + + k_sem_take(&dev_data->sync, K_FOREVER); + + return dev_data->cmd_status; +} + +static int ospi_write_access(const struct device *dev, OSPI_RegularCmdTypeDef *cmd, + const uint8_t *data, size_t size) +{ + const struct flash_stm32_ospi_config *dev_cfg = dev->config; + struct flash_stm32_ospi_data *dev_data = dev->data; + HAL_StatusTypeDef hal_ret; + + LOG_DBG("Instruction 0x%x", cmd->Instruction); + + cmd->NbData = size; + + dev_data->cmd_status = 0; + + /* in OPI/STR the 3-byte AddressSize is not supported by the NOR flash */ + if ((dev_cfg->data_mode == OSPI_OPI_MODE) && + (cmd->AddressSize != HAL_OSPI_ADDRESS_32_BITS)) { + LOG_ERR("OSPI wr in OPI/STR mode is for 32bit address only"); + return -EIO; + } + + hal_ret = HAL_OSPI_Command(&dev_data->hospi, cmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE); + if (hal_ret != HAL_OK) { + LOG_ERR("%d: Failed to send OSPI instruction", hal_ret); + return -EIO; + } + + hal_ret = HAL_OSPI_Transmit_IT(&dev_data->hospi, (uint8_t *)data); + + if (hal_ret != HAL_OK) { + LOG_ERR("%d: Failed to read data", hal_ret); + return -EIO; + } + + k_sem_take(&dev_data->sync, K_FOREVER); + + return dev_data->cmd_status; +} + +/* + * Read Serial Flash Discovery Parameter : + * perform a read access over SPI bus for SDFP (DataMode is already set) + * or get it from the sdfp table (in the DTS) + */ +static int ospi_read_sfdp(const struct device *dev, off_t addr, uint8_t *data, + size_t size) +{ + const struct flash_stm32_ospi_config *dev_cfg = dev->config; + struct flash_stm32_ospi_data *dev_data = dev->data; + +#if DT_NODE_HAS_PROP(DT_INST(0, st_stm32_ospi_nor), sfdp_bfp) + /* simulate the SDFP */ + ARG_UNUSED(addr); /* addr is 0 */ + + for (uint8_t i_ind = 0; i_ind < ARRAY_SIZE(dev_cfg->sfdp_bfp); i_ind++) { + *(data + i_ind) = dev_cfg->sfdp_bfp[i_ind]; + } +#else /* sfdp_bfp */ + + OSPI_RegularCmdTypeDef cmd = { + .OperationType = HAL_OSPI_OPTYPE_COMMON_CFG, + .FlashId = HAL_OSPI_FLASH_ID_1, + .InstructionMode = ((dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_1_LINE + : HAL_OSPI_INSTRUCTION_8_LINES), + .InstructionDtrMode = ((dev_cfg->data_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_INSTRUCTION_DTR_ENABLE + : HAL_OSPI_INSTRUCTION_DTR_DISABLE), + .InstructionSize = ((dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_8_BITS + : HAL_OSPI_INSTRUCTION_16_BITS), + .Instruction = ((dev_cfg->data_mode == OSPI_SPI_MODE) + ? JESD216_CMD_READ_SFDP + : JESD216_OCMD_READ_SFDP), + .Address = addr, + .AddressDtrMode = ((dev_cfg->data_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_ADDRESS_DTR_ENABLE + : HAL_OSPI_ADDRESS_DTR_DISABLE), + .AddressSize = ((dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_ADDRESS_24_BITS + : HAL_OSPI_ADDRESS_32_BITS), + .AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE, + .DataMode = ((dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_DATA_1_LINE + : HAL_OSPI_DATA_8_LINES), + .DataDtrMode = ((dev_cfg->data_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_DATA_DTR_ENABLE + : HAL_OSPI_DATA_DTR_DISABLE), + .DummyCycles = ((dev_cfg->data_mode == OSPI_SPI_MODE) ? 8U : 20U), + .DQSMode = ((dev_cfg->data_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_DQS_ENABLE + : HAL_OSPI_DQS_DISABLE), + .SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD, + }; + + cmd.NbData = size; + + HAL_StatusTypeDef hal_ret; + + hal_ret = HAL_OSPI_Command(&dev_data->hospi, &cmd, + HAL_OSPI_TIMEOUT_DEFAULT_VALUE); + if (hal_ret != HAL_OK) { + LOG_ERR("%d: Failed to send OSPI instruction", hal_ret); + return -EIO; + } + + hal_ret = HAL_OSPI_Receive(&dev_data->hospi, data, HAL_OSPI_TIMEOUT_DEFAULT_VALUE); + if (hal_ret != HAL_OK) { + LOG_ERR("%d: Failed to read data", hal_ret); + return -EIO; + } + +#endif /* sfdp_bfp */ + dev_data->cmd_status = 0; + + return 0; +} + +static bool ospi_address_is_valid(const struct device *dev, off_t addr, + size_t size) +{ + const struct flash_stm32_ospi_config *dev_cfg = dev->config; + size_t flash_size = dev_cfg->flash_size; + + return (addr >= 0) && ((uint64_t)addr + (uint64_t)size <= flash_size); +} + +/* + * This function Polls the WIP(Write In Progress) bit to become to 0 + * in nor_mode SPI/OPI OSPI_SPI_MODE or OSPI_OPI_MODE + * and nor_rate transfer STR/DTR OSPI_STR_TRANSFER or OSPI_DTR_TRANSFER + */transmit_data +static int stm32_ospi_mem_ready(OSPI_HandleTypeDef *hospi, uint8_t nor_mode, uint8_t nor_rate) +{ + OSPI_RegularCmdTypeDef s_command = {0}; + OSPI_AutoPollingTypeDef s_config = {0}; + + /* NOR flash memory SPI/DTR config is not a valid mode */ + if ((nor_mode == OSPI_SPI_MODE) && (nor_rate == OSPI_DTR_TRANSFER)) { + LOG_ERR("OSPI SPI/DTR mode is not supported"); + return -ENOTSUP; + } + + /* Configure automatic polling mode command to wait for memory ready */ + s_command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; + s_command.FlashId = HAL_OSPI_FLASH_ID_1; + s_command.InstructionMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_1_LINE + : HAL_OSPI_INSTRUCTION_8_LINES; + s_command.InstructionDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_INSTRUCTION_DTR_ENABLE + : HAL_OSPI_INSTRUCTION_DTR_DISABLE; + s_command.InstructionSize = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_8_BITS + : HAL_OSPI_INSTRUCTION_16_BITS; + s_command.Instruction = (nor_mode == OSPI_SPI_MODE) + ? SPI_NOR_CMD_RDSR + : SPI_NOR_OCMD_RDSR; + s_command.AddressMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_ADDRESS_NONE + : HAL_OSPI_ADDRESS_8_LINES; + s_command.AddressDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_ADDRESS_DTR_ENABLE + : HAL_OSPI_ADDRESS_DTR_DISABLE; + s_command.AddressSize = HAL_OSPI_ADDRESS_32_BITS; + s_command.Address = 0; + s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + s_command.DataMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_DATA_1_LINE + : HAL_OSPI_DATA_8_LINES; + s_command.DataDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_DATA_DTR_ENABLE + : HAL_OSPI_DATA_DTR_DISABLE; + s_command.DummyCycles = (nor_mode == OSPI_SPI_MODE) + ? 0U + : ((nor_rate == OSPI_DTR_TRANSFER) + ? SPI_NOR_DUMMY_REG_OCTAL_DTR + : SPI_NOR_DUMMY_REG_OCTAL); + s_command.NbData = (nor_rate == OSPI_DTR_TRANSFER) ? 2U : 1U; + s_command.DQSMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_DQS_ENABLE + : HAL_OSPI_DQS_DISABLE; + s_command.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD; + + /* Set the mask to 0x01 to mask all Status REG bits except WIP */ + /* Set the match to 0x00 to check if the WIP bit is Reset */ + s_config.Match = SPI_NOR_MEM_RDY_MATCH; + s_config.Mask = SPI_NOR_MEM_RDY_MASK; /* Write in progress */ + s_config.MatchMode = HAL_OSPI_MATCH_MODE_AND; + s_config.Interval = SPI_NOR_AUTO_POLLING_INTERVAL; + s_config.AutomaticStop = HAL_OSPI_AUTOMATIC_STOP_ENABLE; + + if (HAL_OSPI_Command(hospi, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("OSPI AutoPoll command failed"); + return -EIO; + } + + /* Start Automatic-Polling mode to wait until the memory is ready WIP=0 */ + if (HAL_OSPI_AutoPolling(hospi, &s_config, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("OSPI AutoPoll failed"); + return -EIO; + } + + return 0; +} + +/* Enables writing to the memory sending a Write Enable and wait it is effective */ +static int stm32_ospi_write_enable(OSPI_HandleTypeDef *hospi, uint8_t nor_mode, uint8_t nor_rate) +{ + OSPI_RegularCmdTypeDef s_command = {0}; + OSPI_AutoPollingTypeDef s_config = {0}; + + /* Initialize the write enable command */ + s_command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; + s_command.FlashId = HAL_OSPI_FLASH_ID_1; + s_command.InstructionMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_1_LINE + : HAL_OSPI_INSTRUCTION_8_LINES; + s_command.InstructionDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_INSTRUCTION_DTR_ENABLE + : HAL_OSPI_INSTRUCTION_DTR_DISABLE; + s_command.InstructionSize = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_8_BITS + : HAL_OSPI_INSTRUCTION_16_BITS; + s_command.Instruction = (nor_mode == OSPI_SPI_MODE) + ? SPI_NOR_CMD_WREN + : SPI_NOR_OCMD_WREN; + s_command.AddressMode = HAL_OSPI_ADDRESS_NONE; + s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + s_command.DataMode = HAL_OSPI_DATA_NONE; + s_command.DummyCycles = 0U; + s_command.DQSMode = HAL_OSPI_DQS_DISABLE; + s_command.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD; + + if (HAL_OSPI_Command(hospi, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("OSPI flash write enable cmd failed"); + return -EIO; + } + + /* Configure automatic polling mode to wait for write enabling */ + s_command.Instruction = (nor_mode == OSPI_SPI_MODE) + ? SPI_NOR_CMD_RDSR + : SPI_NOR_OCMD_RDSR; + s_command.AddressMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_ADDRESS_1_LINE + : HAL_OSPI_ADDRESS_8_LINES; + s_command.AddressDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_ADDRESS_DTR_ENABLE + : HAL_OSPI_ADDRESS_DTR_DISABLE; + s_command.AddressSize = HAL_OSPI_ADDRESS_32_BITS; + s_command.Address = 0U; + s_command.DataMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_DATA_1_LINE + : HAL_OSPI_DATA_8_LINES; + s_command.DataDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_DATA_DTR_ENABLE + : HAL_OSPI_DATA_DTR_DISABLE; + s_command.DummyCycles = (nor_mode == OSPI_SPI_MODE) + ? 0U + : ((nor_rate == OSPI_DTR_TRANSFER) + ? SPI_NOR_DUMMY_REG_OCTAL_DTR + : SPI_NOR_DUMMY_REG_OCTAL); + s_command.NbData = (nor_rate == OSPI_DTR_TRANSFER) ? 2U : 1U; + s_command.DQSMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_DQS_ENABLE + : HAL_OSPI_DQS_DISABLE; + + if (HAL_OSPI_Command(hospi, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("OSPI config auto polling cmd failed"); + return -EIO; + } + + s_config.Match = SPI_NOR_WREN_MATCH; + s_config.Mask = SPI_NOR_WREN_MASK; + s_config.MatchMode = HAL_OSPI_MATCH_MODE_AND; + s_config.Interval = SPI_NOR_AUTO_POLLING_INTERVAL; + s_config.AutomaticStop = HAL_OSPI_AUTOMATIC_STOP_ENABLE; + + if (HAL_OSPI_AutoPolling(hospi, &s_config, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("OSPI config auto polling failed"); + return -EIO; + } + + return 0; +} + +/* Write Flash configuration register 2 with new dummy cycles */ +static int stm32_ospi_write_cfg2reg_dummy(OSPI_HandleTypeDef *hospi, + uint8_t nor_mode, uint8_t nor_rate) +{ + OSPI_RegularCmdTypeDef s_command; + + /* Initialize the writing of configuration register 2 */ + s_command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; + s_command.FlashId = HAL_OSPI_FLASH_ID_1; + s_command.InstructionMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_1_LINE + : HAL_OSPI_INSTRUCTION_8_LINES; + s_command.InstructionDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_INSTRUCTION_DTR_ENABLE + : HAL_OSPI_INSTRUCTION_DTR_DISABLE; + s_command.InstructionSize = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_8_BITS + : HAL_OSPI_INSTRUCTION_16_BITS; + s_command.Instruction = (nor_mode == OSPI_SPI_MODE) + ? SPI_NOR_CMD_WR_CFGREG2 + : SPI_NOR_OCMD_WR_CFGREG2; + s_command.AddressMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_ADDRESS_1_LINE + : HAL_OSPI_ADDRESS_8_LINES; + s_command.AddressDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_ADDRESS_DTR_ENABLE + : HAL_OSPI_ADDRESS_DTR_DISABLE; + s_command.AddressSize = HAL_OSPI_ADDRESS_32_BITS; + s_command.Address = SPI_NOR_REG2_ADDR3; + s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + s_command.DataMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_DATA_1_LINE + : HAL_OSPI_DATA_8_LINES; + s_command.DataDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_DATA_DTR_ENABLE + : HAL_OSPI_DATA_DTR_DISABLE; + s_command.DummyCycles = 0U; + s_command.NbData = (nor_mode == OSPI_SPI_MODE) ? 1U + : ((nor_rate == OSPI_DTR_TRANSFER) ? 2U : 1U); + s_command.DQSMode = HAL_OSPI_DQS_DISABLE; + s_command.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD; + + if (HAL_OSPI_Command(hospi, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("OSPI transmit "); + return -EIO; + } + uint8_t tmp = SPI_NOR_CR2_DUMMY_CYCLES_66MHZ; + + if (HAL_OSPI_Transmit(hospi, &tmp, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("OSPI transmit "); + return -EIO; + } + + return 0; +} + +/* Write Flash configuration register 2 with new single or octal SPI protocol */ +static int stm32_ospi_write_cfg2reg_io(OSPI_HandleTypeDef *hospi, + uint8_t nor_mode, uint8_t nor_rate, uint8_t op_enable) +{ + OSPI_RegularCmdTypeDef s_command; + uint8_t transmit_data; + + if ((op_enable != SPI_NOR_CR2_STR_OPI_EN) && (op_enable != SPI_NOR_CR2_DTR_OPI_EN)) { + LOG_ERR("OSPI wrong OSPI protocol"); + return -EIO; + } + + /* Initialize the writing of configuration register 2 */ + s_command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; + s_command.FlashId = HAL_OSPI_FLASH_ID_1; + s_command.InstructionMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_1_LINE + : HAL_OSPI_INSTRUCTION_8_LINES; + s_command.InstructionDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_INSTRUCTION_DTR_ENABLE + : HAL_OSPI_INSTRUCTION_DTR_DISABLE; + s_command.InstructionSize = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_8_BITS + : HAL_OSPI_INSTRUCTION_16_BITS; + s_command.Instruction = (nor_mode == OSPI_SPI_MODE) + ? SPI_NOR_CMD_WR_CFGREG2 + : SPI_NOR_OCMD_WR_CFGREG2; + s_command.AddressMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_ADDRESS_1_LINE + : HAL_OSPI_ADDRESS_8_LINES; + s_command.AddressDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_ADDRESS_DTR_ENABLE + : HAL_OSPI_ADDRESS_DTR_DISABLE; + s_command.AddressSize = HAL_OSPI_ADDRESS_32_BITS; + s_command.Address = SPI_NOR_REG2_ADDR1; + s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + s_command.DataMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_DATA_1_LINE + : HAL_OSPI_DATA_8_LINES; + s_command.DataDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_DATA_DTR_ENABLE + : HAL_OSPI_DATA_DTR_DISABLE; + s_command.DummyCycles = 0U; + s_command.NbData = (nor_mode == OSPI_SPI_MODE) ? 1U + : ((nor_rate == OSPI_DTR_TRANSFER) ? 2U : 1U); + s_command.DQSMode = HAL_OSPI_DQS_DISABLE; + s_command.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD; + + if (HAL_OSPI_Command(hospi, &s_command, + HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("Write Flash configuration reg2 failed"); + return -EIO; + } + + transmit_data = op_enable; + + if (HAL_OSPI_Transmit(hospi, &transmit_data, + HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("Write Flash configuration reg2 failed"); + return -EIO; + } + + return 0; +} + +/* Read Flash configuration register 2 with new single or octal SPI protocol */ +static int stm32_ospi_read_cfg2reg(OSPI_HandleTypeDef *hospi, + uint8_t nor_mode, uint8_t nor_rate, uint8_t *value) +{ + OSPI_RegularCmdTypeDef s_command; + + /* Initialize the writing of configuration register 2 */ + s_command.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; + s_command.FlashId = HAL_OSPI_FLASH_ID_1; + s_command.InstructionMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_1_LINE + : HAL_OSPI_INSTRUCTION_8_LINES; + s_command.InstructionDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_INSTRUCTION_DTR_ENABLE + : HAL_OSPI_INSTRUCTION_DTR_DISABLE; + s_command.InstructionSize = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_8_BITS + : HAL_OSPI_INSTRUCTION_16_BITS; + s_command.Instruction = (nor_mode == OSPI_SPI_MODE) + ? SPI_NOR_CMD_RD_CFGREG2 + : SPI_NOR_OCMD_RD_CFGREG2; + s_command.AddressMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_ADDRESS_1_LINE + : HAL_OSPI_ADDRESS_8_LINES; + s_command.AddressDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_ADDRESS_DTR_ENABLE + : HAL_OSPI_ADDRESS_DTR_DISABLE; + s_command.InstructionSize = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_8_BITS + : HAL_OSPI_INSTRUCTION_16_BITS; + s_command.Instruction = (nor_mode == OSPI_SPI_MODE) + ? SPI_NOR_CMD_RD_CFGREG2 + : SPI_NOR_OCMD_RD_CFGREG2; + s_command.AddressMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_ADDRESS_1_LINE + : HAL_OSPI_ADDRESS_8_LINES; + s_command.AddressDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_ADDRESS_DTR_ENABLE + : HAL_OSPI_ADDRESS_DTR_DISABLE; + s_command.AddressSize = HAL_OSPI_ADDRESS_32_BITS; + s_command.Address = SPI_NOR_REG2_ADDR1; + s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + s_command.DataMode = (nor_mode == OSPI_SPI_MODE) + ? HAL_OSPI_DATA_1_LINE + : HAL_OSPI_DATA_8_LINES; + s_command.DataDtrMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_DATA_DTR_ENABLE + : HAL_OSPI_DATA_DTR_DISABLE; + s_command.DummyCycles = (nor_mode == OSPI_SPI_MODE) + ? 0U + : ((nor_rate == OSPI_DTR_TRANSFER) + ? SPI_NOR_DUMMY_REG_OCTAL_DTR + : SPI_NOR_DUMMY_REG_OCTAL); + s_command.NbData = (nor_rate == OSPI_DTR_TRANSFER) ? 2U : 1U; + s_command.DQSMode = (nor_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_DQS_ENABLE + : HAL_OSPI_DQS_DISABLE; + s_command.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD; + + if (HAL_OSPI_Command(hospi, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("Write Flash configuration reg2 failed"); + return -EIO; + } + + if (HAL_OSPI_Receive(hospi, value, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("Write Flash configuration reg2 failed"); + return -EIO; + } + + return 0; +} + +/* Set the NOR Flash to desired Interface mode : SPI/OSPI and STR/DTR according to the DTS */ +static int stm32_ospi_config_mem(const struct device *dev) +{ + const struct flash_stm32_ospi_config *dev_cfg = dev->config; + struct flash_stm32_ospi_data *dev_data = dev->data; + uint8_t reg[2]; + + /* Going to set the SPI mode and STR transfer rate : done */ + if ((dev_cfg->data_mode == OSPI_SPI_MODE) + && (dev_cfg->data_rate == OSPI_STR_TRANSFER)) { + LOG_INF("OSPI flash config is SPI / STR"); + return 0; + } + + /* Going to set the OPI mode (STR or DTR transfer rate) */ + LOG_DBG("OSPI configuring OctoSPI mode"); + + if (stm32_ospi_write_enable(&dev_data->hospi, + OSPI_SPI_MODE, OSPI_STR_TRANSFER) != 0) { + LOG_ERR("OSPI write Enable failed"); + return -EIO; + } + + /* Write Configuration register 2 (with new dummy cycles) */ + if (stm32_ospi_write_cfg2reg_dummy(&dev_data->hospi, + OSPI_SPI_MODE, OSPI_STR_TRANSFER) != 0) { + LOG_ERR("OSPI write CFGR2 failed"); + return -EIO; + } + if (stm32_ospi_mem_ready(&dev_data->hospi, + OSPI_SPI_MODE, OSPI_STR_TRANSFER) != 0) { + LOG_ERR("OSPI autopolling failed"); + return -EIO; + } + if (stm32_ospi_write_enable(&dev_data->hospi, + OSPI_SPI_MODE, OSPI_STR_TRANSFER) != 0) { + LOG_ERR("OSPI write Enable 2 failed"); + return -EIO; + } + + /* Write Configuration register 2 (with Octal I/O SPI protocol : choose STR or DTR) */ + uint8_t mode_enable = ((dev_cfg->data_rate == OSPI_DTR_TRANSFER) + ? SPI_NOR_CR2_DTR_OPI_EN + : SPI_NOR_CR2_STR_OPI_EN); + if (stm32_ospi_write_cfg2reg_io(&dev_data->hospi, + OSPI_SPI_MODE, OSPI_STR_TRANSFER, mode_enable) != 0) { + LOG_ERR("OSPI write CFGR2 failed"); + return -EIO; + } + + /* Wait that the configuration is effective and check that memory is ready */ + k_msleep(STM32_OSPI_WRITE_REG_MAX_TIME); + + /* Reconfigure the memory type of the peripheral */ + dev_data->hospi.Init.MemoryType = HAL_OSPI_MEMTYPE_MACRONIX; + dev_data->hospi.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE; + if (HAL_OSPI_Init(&dev_data->hospi) != HAL_OK) { + LOG_ERR("OSPI mem type MACRONIX failed"); + return -EIO; + } + + if (dev_cfg->data_rate == OSPI_STR_TRANSFER) { + if (stm32_ospi_mem_ready(&dev_data->hospi, + OSPI_OPI_MODE, OSPI_STR_TRANSFER) != 0) { + /* Check Flash busy ? */ + LOG_ERR("OSPI flash busy failed"); + return -EIO; + } + + if (stm32_ospi_read_cfg2reg(&dev_data->hospi, + OSPI_OPI_MODE, OSPI_STR_TRANSFER, reg) != 0) { + /* Check the configuration has been correctly done on SPI_NOR_REG2_ADDR1 */ + LOG_ERR("OSPI flash config read failed"); + return -EIO; + } + + LOG_INF("OSPI flash config is OPI / STR"); + } + + if (dev_cfg->data_rate == OSPI_DTR_TRANSFER) { + if (stm32_ospi_mem_ready(&dev_data->hospi, + OSPI_OPI_MODE, OSPI_DTR_TRANSFER) != 0) { + /* Check Flash busy ? */ + LOG_ERR("OSPI flash busy failed"); + return -EIO; + } + + LOG_INF("OSPI flash config is OPI / DTR"); + } + + return 0; +} + +/* gpio or send the different reset command to the NOR flash in SPI/OSPI and STR/DTR */ +static int stm32_ospi_mem_reset(const struct device *dev) +{ + struct flash_stm32_ospi_data *dev_data = dev->data; + +#if STM32_OSPI_RESET_GPIO + /* Generate RESETn pulse for the flash memory */ + gpio_pin_configure_dt(&dev_cfg->reset, GPIO_OUTPUT_ACTIVE); + k_msleep(DT_INST_PROP(0, reset_gpios_duration)); + gpio_pin_set_dt(&dev_cfg->reset, 0); +#else + + /* Reset command sent sucessively for each mode SPI/OPS & STR/DTR */ + OSPI_RegularCmdTypeDef s_command = { + .OperationType = HAL_OSPI_OPTYPE_COMMON_CFG, + .FlashId = HAL_OSPI_FLASH_ID_1, + .AddressMode = HAL_OSPI_ADDRESS_NONE, + .InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE, + .InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE, + .Instruction = SPI_NOR_CMD_RESET_EN, + .InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS, + .AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE, + .DataMode = HAL_OSPI_DATA_NONE, + .DummyCycles = 0U, + .DQSMode = HAL_OSPI_DQS_DISABLE, + .SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD, + }; + + /* Reset enable in SPI mode and STR transfer mode */ + if (HAL_OSPI_Command(&dev_data->hospi, + &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("OSPI reset enable (SPI/STR) failed"); + return -EIO; + } + + /* Reset memory in SPI mode and STR transfer mode */ + s_command.Instruction = SPI_NOR_CMD_RESET_MEM; + if (HAL_OSPI_Command(&dev_data->hospi, + &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("OSPI reset memory (SPI/STR) failed"); + return -EIO; + } + + /* Reset enable in OPI mode and STR transfer mode */ + s_command.InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES; + s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE; + s_command.Instruction = SPI_NOR_OCMD_RESET_EN; + s_command.InstructionSize = HAL_OSPI_INSTRUCTION_16_BITS; + if (HAL_OSPI_Command(&dev_data->hospi, + &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("OSPI reset enable (OPI/STR) failed"); + return -EIO; + } + + /* Reset memory in OPI mode and STR transfer mode */ + s_command.Instruction = SPI_NOR_OCMD_RESET_MEM; + if (HAL_OSPI_Command(&dev_data->hospi, + &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("OSPI reset memory (OPI/STR) failed"); + return -EIO; + } + + /* Reset enable in OPI mode and DTR transfer mode */ + s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_ENABLE; + s_command.Instruction = SPI_NOR_OCMD_RESET_EN; + if (HAL_OSPI_Command(&dev_data->hospi, + &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("OSPI reset enable (OPI/DTR) failed"); + return -EIO; + } + + /* Reset memory in OPI mode and DTR transfer mode */ + s_command.Instruction = SPI_NOR_OCMD_RESET_MEM; + if (HAL_OSPI_Command(&dev_data->hospi, + &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("OSPI reset memory (OPI/DTR) failed"); + return -EIO; + } + +#endif + /* After SWreset CMD, wait in case SWReset occurred during erase operation */ + k_msleep(STM32_OSPI_RESET_MAX_TIME); + + return 0; +} + +/* + * Function to erase the flash : chip or sector with possible OSPI/SPI and STR/DTR + * to erase the complete chip (using dedicated command) : + * set size >= flash size + * set addr = 0 + */ +static int flash_stm32_ospi_erase(const struct device *dev, off_t addr, + size_t size) +{ + const struct flash_stm32_ospi_config *dev_cfg = dev->config; + struct flash_stm32_ospi_data *dev_data = dev->data; + int ret = 0; + + /* Ignore zero size erase */ + if (size == 0) { + return 0; + } + + /* Maximise erase size : means the complete chip */ + if (size > dev_cfg->flash_size) { + /* Reset addr in that case */ + addr = 0; + size = dev_cfg->flash_size; + } + + if (!ospi_address_is_valid(dev, addr, size)) { + LOG_ERR("Error: address or size exceeds expected values: " + "addr 0x%lx, size %zu", (long)addr, size); + return -EINVAL; + } + + if ((size != SPI_NOR_SECTOR_SIZE) && (size < dev_cfg->flash_size)) { + LOG_ERR("Error: wrong sector size 0x%x", size); + return -ENOTSUP; + } + + OSPI_RegularCmdTypeDef cmd_erase = { + .OperationType = HAL_OSPI_OPTYPE_COMMON_CFG, + .FlashId = HAL_OSPI_FLASH_ID_1, + .AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE, + .DataMode = HAL_OSPI_DATA_NONE, + .DummyCycles = 0, + .DQSMode = HAL_OSPI_DQS_DISABLE, + .SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD, + }; + + ospi_lock_thread(dev); + + if (stm32_ospi_mem_ready(&dev_data->hospi, + dev_cfg->data_mode, dev_cfg->data_rate) != 0) { + LOG_ERR("Erase failed : flash busy"); + return -EBUSY; + } + + if (stm32_ospi_write_enable(&dev_data->hospi, + dev_cfg->data_mode, dev_cfg->data_rate) != 0) { + LOG_ERR("Erase failed : write enable"); + return -EIO; + } + + cmd_erase.InstructionMode = (dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_1_LINE + : HAL_OSPI_INSTRUCTION_8_LINES; + cmd_erase.InstructionDtrMode = (dev_cfg->data_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_INSTRUCTION_DTR_ENABLE + : HAL_OSPI_INSTRUCTION_DTR_DISABLE; + cmd_erase.InstructionSize = (dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_8_BITS + : HAL_OSPI_INSTRUCTION_16_BITS; + + while ((size > 0) && (ret == 0)) { + if (size == dev_cfg->flash_size) { + /* Chip erase */ + LOG_INF("Chip Erase"); + cmd_erase.Instruction = (dev_cfg->data_mode == OSPI_SPI_MODE) + ? SPI_NOR_CMD_BULKE + : SPI_NOR_OCMD_BULKE; + cmd_erase.AddressMode = HAL_OSPI_ADDRESS_NONE; + /* Full chip erase command */ + ospi_send_cmd(dev, &cmd_erase); + + size -= dev_cfg->flash_size; + } else { + /* Sector erase */ + LOG_INF("Sector Erase"); + cmd_erase.Address = addr; + const struct jesd216_erase_type *erase_types = + dev_data->erase_types; + const struct jesd216_erase_type *bet = NULL; + + for (uint8_t ei = 0; + ei < JESD216_NUM_ERASE_TYPES; ++ei) { + const struct jesd216_erase_type *etp = + &erase_types[ei]; + + if ((etp->exp != 0) + && SPI_NOR_IS_ALIGNED(addr, etp->exp) + && SPI_NOR_IS_ALIGNED(size, etp->exp) + && ((bet == NULL) + || (etp->exp > bet->exp))) { + bet = etp; + cmd_erase.Instruction = bet->cmd; + } else { + /* Use the default sector erase cmd */ + cmd_erase.Instruction = + (dev_cfg->data_mode == OSPI_SPI_MODE) + ? SPI_NOR_CMD_SE /* Erase sector size 3 bytes */ + : SPI_NOR_OCMD_SE; + cmd_erase.AddressMode = + (dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_ADDRESS_1_LINE + : HAL_OSPI_ADDRESS_8_LINES; + cmd_erase.AddressDtrMode = + (dev_cfg->data_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_ADDRESS_DTR_ENABLE + : HAL_OSPI_ADDRESS_DTR_DISABLE; + cmd_erase.AddressSize = HAL_OSPI_ADDRESS_32_BITS; + cmd_erase.Address = addr; + } + } + + ospi_send_cmd(dev, &cmd_erase); + + if (bet != NULL) { + addr += BIT(bet->exp); + size -= BIT(bet->exp); + } else { + addr += SPI_NOR_SECTOR_SIZE; + size -= SPI_NOR_SECTOR_SIZE; + } + + ret = stm32_ospi_mem_ready(&dev_data->hospi, + dev_cfg->data_mode, dev_cfg->data_rate); + } + } + + ospi_unlock_thread(dev); + + return ret; +} + +/* Function to read the flash with possible OSPI/SPI and STR/DTR */ +static int flash_stm32_ospi_read(const struct device *dev, off_t addr, + void *data, size_t size) +{ + const struct flash_stm32_ospi_config *dev_cfg = dev->config; + int ret; + + if (!ospi_address_is_valid(dev, addr, size)) { + LOG_ERR("Error: address or size exceeds expected values: " + "addr 0x%lx, size %zu", (long)addr, size); + return -EINVAL; + } + + /* Ignore zero size read */ + if (size == 0) { + return 0; + } + + OSPI_RegularCmdTypeDef cmd = { + .OperationType = HAL_OSPI_OPTYPE_COMMON_CFG, + .FlashId = HAL_OSPI_FLASH_ID_1, + .InstructionMode = ((dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_1_LINE + : HAL_OSPI_INSTRUCTION_8_LINES), + .InstructionDtrMode = ((dev_cfg->data_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_INSTRUCTION_DTR_ENABLE + : HAL_OSPI_INSTRUCTION_DTR_DISABLE), + .InstructionSize = ((dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_8_BITS + : HAL_OSPI_INSTRUCTION_16_BITS), + .AddressMode = ((dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_ADDRESS_1_LINE + : HAL_OSPI_ADDRESS_8_LINES), + .AddressDtrMode = ((dev_cfg->data_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_ADDRESS_DTR_ENABLE + : HAL_OSPI_ADDRESS_DTR_DISABLE), + .Address = addr, + .AddressSize = HAL_OSPI_ADDRESS_32_BITS, + .AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE, + .DataMode = ((dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_DATA_1_LINE + : HAL_OSPI_DATA_8_LINES), + .DataDtrMode = ((dev_cfg->data_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_DATA_DTR_ENABLE + : HAL_OSPI_DATA_DTR_DISABLE), + /* dataSize is set by the read cmd */ + .DQSMode = ((dev_cfg->data_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_DQS_ENABLE + : HAL_OSPI_DQS_DISABLE), + .SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD, + /* other parameters are set below */ + }; + + LOG_DBG("OSPI: read %u data", size); + ospi_lock_thread(dev); + + /* Configure other parameters */ + if (dev_cfg->data_rate == OSPI_DTR_TRANSFER) { + /* DTR transfer rate (==> Octal mode) */ + cmd.Instruction = SPI_NOR_OCMD_DTR_RD; + cmd.DummyCycles = SPI_NOR_DUMMY_RD_OCTAL_DTR; + } else { + /* STR transfer rate */ + if (dev_cfg->data_mode == OSPI_SPI_MODE) { + /* SPI and STR : use fast read with addr on 4 bytes */ + cmd.Instruction = SPI_NOR_CMD_READ_FAST_4B; + cmd.DummyCycles = SPI_NOR_DUMMY_RD; + } else { + /* OPI and STR */ + cmd.Instruction = SPI_NOR_OCMD_RD; + cmd.DummyCycles = SPI_NOR_DUMMY_RD_OCTAL; + } + } + + ret = ospi_read_access(dev, &cmd, data, size); + + ospi_unlock_thread(dev); + + return ret; +} + +/* Function to write the flash (page program) : with possible OSPI/SPI and STR/DTR */ +static int flash_stm32_ospi_write(const struct device *dev, off_t addr, + const void *data, size_t size) +{ + const struct flash_stm32_ospi_config *dev_cfg = dev->config; + struct flash_stm32_ospi_data *dev_data = dev->data; + int ret = 0; + + if (!ospi_address_is_valid(dev, addr, size)) { + LOG_ERR("Error: address or size exceeds expected values: " + "addr 0x%lx, size %zu", (long)addr, size); + return -EINVAL; + } + + /* Ignore zero size write */ + if (size == 0) { + return 0; + } + + /* Page Program for STR or DTR mode */ + OSPI_RegularCmdTypeDef cmd_pp = { + .OperationType = HAL_OSPI_OPTYPE_COMMON_CFG, + .FlashId = HAL_OSPI_FLASH_ID_1, + .InstructionMode = ((dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_1_LINE + : HAL_OSPI_INSTRUCTION_8_LINES), + .InstructionSize = ((dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_INSTRUCTION_8_BITS + : HAL_OSPI_INSTRUCTION_16_BITS), + .InstructionDtrMode = ((dev_cfg->data_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_INSTRUCTION_DTR_ENABLE + : HAL_OSPI_INSTRUCTION_DTR_DISABLE), + .AddressMode = ((dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_ADDRESS_1_LINE + : HAL_OSPI_ADDRESS_8_LINES), + .AddressDtrMode = ((dev_cfg->data_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_ADDRESS_DTR_ENABLE + : HAL_OSPI_ADDRESS_DTR_DISABLE), + .AddressSize = HAL_OSPI_ADDRESS_32_BITS, + .AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE, + .DataMode = ((dev_cfg->data_mode == OSPI_SPI_MODE) + ? HAL_OSPI_DATA_1_LINE + : HAL_OSPI_DATA_8_LINES), + .DataDtrMode = ((dev_cfg->data_rate == OSPI_DTR_TRANSFER) + ? HAL_OSPI_DATA_DTR_ENABLE + : HAL_OSPI_DATA_DTR_DISABLE), + .DummyCycles = 0, + .DQSMode = HAL_OSPI_DQS_DISABLE, + .SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD, + }; + + LOG_INF("OSPI: write %u data", size); + ospi_lock_thread(dev); + + while ((size > 0) && (ret == 0)) { + size_t to_write = size; + + ret = stm32_ospi_mem_ready(&dev_data->hospi, + dev_cfg->data_mode, dev_cfg->data_rate); + if (ret != 0) { + break; + } + + ret = stm32_ospi_write_enable(&dev_data->hospi, + dev_cfg->data_mode, dev_cfg->data_rate); + if (ret != 0) { + break; + } + /* Don't write more than a page. */ + if (to_write >= SPI_NOR_PAGE_SIZE) { + to_write = SPI_NOR_PAGE_SIZE; + } + + /* Don't write across a page boundary */ + if (((addr + to_write - 1U) / SPI_NOR_PAGE_SIZE) + != (addr / SPI_NOR_PAGE_SIZE)) { + to_write = SPI_NOR_PAGE_SIZE - + (addr % SPI_NOR_PAGE_SIZE); + } + + cmd_pp.Instruction = (dev_cfg->data_mode == OSPI_SPI_MODE) + ? SPI_NOR_CMD_PP_4B + : SPI_NOR_OCMD_PAGE_PRG; + cmd_pp.Address = addr; + ret = ospi_write_access(dev, &cmd_pp, data, to_write); + if (ret != 0) { + break; + } + + size -= to_write; + data = (const uint8_t *)data + to_write; + addr += to_write; + + /* Configure automatic polling mode to wait for end of program */ + ret = stm32_ospi_mem_ready(&dev_data->hospi, + dev_cfg->data_mode, dev_cfg->data_rate); + if (ret != 0) { + break; + } + } + + ospi_unlock_thread(dev); + + return ret; +} + +static const struct flash_parameters flash_stm32_ospi_parameters = { + .write_block_size = 1, + .erase_value = 0xff +}; + +static const struct flash_parameters * +flash_stm32_ospi_get_parameters(const struct device *dev) +{ + ARG_UNUSED(dev); + + return &flash_stm32_ospi_parameters; +} + +static void flash_stm32_ospi_isr(const struct device *dev) +{ + struct flash_stm32_ospi_data *dev_data = dev->data; + + HAL_OSPI_IRQHandler(&dev_data->hospi); +} + +/* weak function required for HAL compilation */ +__weak HAL_StatusTypeDef HAL_DMA_Abort_IT(DMA_HandleTypeDef *hdma) +{ + return HAL_OK; +} + +/* weak function required for HAL compilation */ +__weak HAL_StatusTypeDef HAL_DMA_Abort(DMA_HandleTypeDef *hdma) +{ + return HAL_OK; +} + +/* + * Transfer Error callback. + */ +void HAL_OSPI_ErrorCallback(OSPI_HandleTypeDef *hospi) +{ + struct flash_stm32_ospi_data *dev_data = + CONTAINER_OF(hospi, struct flash_stm32_ospi_data, hospi); + + LOG_DBG("Error cb"); + + dev_data->cmd_status = -EIO; + + k_sem_give(&dev_data->sync); +} + +/* + * Command completed callback. + */ +void HAL_OSPI_CmdCpltCallback(OSPI_HandleTypeDef *hospi) +{ + struct flash_stm32_ospi_data *dev_data = + CONTAINER_OF(hospi, struct flash_stm32_ospi_data, hospi); + + LOG_DBG("Cmd Cplt cb"); + + k_sem_give(&dev_data->sync); +} + +/* + * Rx Transfer completed callback. + */ +void HAL_OSPI_RxCpltCallback(OSPI_HandleTypeDef *hospi) +{ + struct flash_stm32_ospi_data *dev_data = + CONTAINER_OF(hospi, struct flash_stm32_ospi_data, hospi); + + LOG_DBG("Rx Cplt cb"); + + k_sem_give(&dev_data->sync); +} + +/* + * Tx Transfer completed callback. + */ +void HAL_OSPI_TxCpltCallback(OSPI_HandleTypeDef *hospi) +{ + struct flash_stm32_ospi_data *dev_data = + CONTAINER_OF(hospi, struct flash_stm32_ospi_data, hospi); + + LOG_DBG("Tx Cplt cb"); + + k_sem_give(&dev_data->sync); +} + +/* + * Status Match callback. + */ +void HAL_OSPI_StatusMatchCallback(OSPI_HandleTypeDef *hospi) +{ + struct flash_stm32_ospi_data *dev_data = + CONTAINER_OF(hospi, struct flash_stm32_ospi_data, hospi); + + LOG_DBG("Status Match cb"); + + k_sem_give(&dev_data->sync); +} + +/* + * Timeout callback. + */ +void HAL_OSPI_TimeOutCallback(OSPI_HandleTypeDef *hospi) +{ + struct flash_stm32_ospi_data *dev_data = + CONTAINER_OF(hospi, struct flash_stm32_ospi_data, hospi); + + LOG_DBG("Timeout cb"); + + dev_data->cmd_status = -EIO; + + k_sem_give(&dev_data->sync); +} + +#if defined(CONFIG_FLASH_PAGE_LAYOUT) +static void flash_stm32_ospi_pages_layout(const struct device *dev, + const struct flash_pages_layout **layout, + size_t *layout_size) +{ + struct flash_stm32_ospi_data *dev_data = dev->data; + + *layout = &dev_data->layout; + *layout_size = 1; +} +#endif + +static const struct flash_driver_api flash_stm32_ospi_driver_api = { + .read = flash_stm32_ospi_read, + .write = flash_stm32_ospi_write, + .erase = flash_stm32_ospi_erase, + .get_parameters = flash_stm32_ospi_get_parameters, +#if defined(CONFIG_FLASH_PAGE_LAYOUT) + .page_layout = flash_stm32_ospi_pages_layout, +#endif +}; + +#if defined(CONFIG_FLASH_PAGE_LAYOUT) +static int setup_pages_layout(const struct device *dev) +{ + const struct flash_stm32_ospi_config *dev_cfg = dev->config; + struct flash_stm32_ospi_data *data = dev->data; + const size_t flash_size = dev_cfg->flash_size; + uint32_t layout_page_size = data->page_size; + uint8_t value = 0; + int rv = 0; + + /* Find the smallest erase size. */ + for (size_t i = 0; i < ARRAY_SIZE(data->erase_types); ++i) { + const struct jesd216_erase_type *etp = &data->erase_types[i]; + + if ((etp->cmd != 0) + && ((value == 0) || (etp->exp < value))) { + value = etp->exp; + } + } + + uint32_t erase_size = BIT(value); + + if (erase_size == 0) { + erase_size = SPI_NOR_SECTOR_SIZE; + } + + /* We need layout page size to be compatible with erase size */ + if ((layout_page_size % erase_size) != 0) { + LOG_DBG("layout page %u not compatible with erase size %u", + layout_page_size, erase_size); + LOG_DBG("erase size will be used as layout page size"); + layout_page_size = erase_size; + } + + /* Warn but accept layout page sizes that leave inaccessible + * space. + */ + if ((flash_size % layout_page_size) != 0) { + LOG_DBG("layout page %u wastes space with device size %zu", + layout_page_size, flash_size); + } + + data->layout.pages_size = layout_page_size; + data->layout.pages_count = flash_size / layout_page_size; + LOG_DBG("layout %u x %u By pages", data->layout.pages_count, + data->layout.pages_size); + + return rv; +} +#endif /* CONFIG_FLASH_PAGE_LAYOUT */ + +static int spi_nor_process_bfp(const struct device *dev, + const struct jesd216_param_header *php, + const struct jesd216_bfp *bfp) +{ + const struct flash_stm32_ospi_config *dev_cfg = dev->config; + struct flash_stm32_ospi_data *data = dev->data; + struct jesd216_erase_type *etp = data->erase_types; + const size_t flash_size = jesd216_bfp_density(bfp) / 8U; + + if (flash_size != dev_cfg->flash_size) { + LOG_DBG("Unexpected flash size: %u", flash_size); + } + + LOG_DBG("%s: %u MiBy flash", dev->name, (uint32_t)(flash_size >> 20)); + + /* Copy over the erase types, preserving their order. (The + * Sector Map Parameter table references them by index.) + */ + memset(data->erase_types, 0, sizeof(data->erase_types)); + for (uint8_t ti = 1; ti <= ARRAY_SIZE(data->erase_types); ++ti) { + if (jesd216_bfp_erase(bfp, ti, etp) == 0) { + LOG_DBG("Erase %u with %02x", + (uint32_t)BIT(etp->exp), etp->cmd); + } + ++etp; + } + + data->page_size = jesd216_bfp_page_size(php, bfp); + + LOG_DBG("Page size %u bytes", data->page_size); + LOG_DBG("Flash size %u bytes", flash_size); + return 0; +} + +static int flash_stm32_ospi_init(const struct device *dev) +{ + const struct flash_stm32_ospi_config *dev_cfg = dev->config; + struct flash_stm32_ospi_data *dev_data = dev->data; + uint32_t ahb_clock_freq; + uint32_t prescaler = 0; + int ret; + + /* The SPI/DTR is not a valid config of data_mode/data_rate according to the DTS */ + if ((dev_cfg->data_mode == OSPI_SPI_MODE) + && (dev_cfg->data_rate == OSPI_DTR_TRANSFER)) { + /* already the right config, continue */ + LOG_ERR("OSPI mode SPI/DTR is not valid"); + return -ENOTSUP; + } + + /* Signals configuration */ + ret = pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("OSPI pinctrl setup failed (%d)", ret); + return ret; + } + + /* Initializes the independent peripherals clock */ + __HAL_RCC_OSPI_CONFIG(RCC_OSPICLKSOURCE_SYSCLK); /* */ + + /* Clock configuration */ + if (clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), + (clock_control_subsys_t) &dev_cfg->pclken) != 0) { + LOG_ERR("Could not enable OSPI clock"); + return -EIO; + } + + if (clock_control_get_rate(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), + (clock_control_subsys_t) &dev_cfg->pclken, + &ahb_clock_freq) < 0) { + LOG_ERR("Failed to get AHB clock frequency"); + return -EIO; + } + + for (; prescaler <= STM32_OSPI_CLOCK_PRESCALER_MAX; prescaler++) { + uint32_t clk = ahb_clock_freq / (prescaler + 1); + + if (clk <= dev_cfg->max_frequency) { + break; + } + } + __ASSERT_NO_MSG(prescaler <= STM32_OSPI_CLOCK_PRESCALER_MAX); + + /* Initialize OSPI HAL structure completely */ + dev_data->hospi.Init.FifoThreshold = 4; + dev_data->hospi.Init.ClockPrescaler = prescaler; + dev_data->hospi.Init.DeviceSize = find_lsb_set(dev_cfg->flash_size); + dev_data->hospi.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE; + dev_data->hospi.Init.ChipSelectHighTime = 2; + dev_data->hospi.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE; + dev_data->hospi.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0; + dev_data->hospi.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED; + dev_data->hospi.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE; + /* STR mode else Macronix for DTR mode */ + if (dev_cfg->data_rate == OSPI_DTR_TRANSFER) { + dev_data->hospi.Init.MemoryType = HAL_OSPI_MEMTYPE_MACRONIX; + dev_data->hospi.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE; + } else { + dev_data->hospi.Init.MemoryType = HAL_OSPI_MEMTYPE_MICRON; + dev_data->hospi.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_DISABLE; + } + dev_data->hospi.Init.ChipSelectBoundary = 0; + dev_data->hospi.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_USED; + dev_data->hospi.Init.Refresh = 0; + + if (HAL_OSPI_Init(&dev_data->hospi) != HAL_OK) { + LOG_ERR("OSPI Init failed"); + return -EIO; + } + +#if defined(CONFIG_SOC_SERIES_STM32U5X) + /* OCTOSPI I/O manager init Function */ + OSPIM_CfgTypeDef ospi_mgr_cfg = {0}; + + __HAL_RCC_OSPIM_CLK_ENABLE(); + if (dev_data->hospi.Instance == OCTOSPI1) { + ospi_mgr_cfg.ClkPort = 1; + ospi_mgr_cfg.DQSPort = 1; + ospi_mgr_cfg.NCSPort = 1; + ospi_mgr_cfg.IOLowPort = HAL_OSPIM_IOPORT_1_LOW; + ospi_mgr_cfg.IOHighPort = HAL_OSPIM_IOPORT_1_HIGH; + } else if (dev_data->hospi.Instance == OCTOSPI2) { + ospi_mgr_cfg.ClkPort = 2; + ospi_mgr_cfg.DQSPort = 2; + ospi_mgr_cfg.NCSPort = 2; + ospi_mgr_cfg.IOLowPort = HAL_OSPIM_IOPORT_2_LOW; + ospi_mgr_cfg.IOHighPort = HAL_OSPIM_IOPORT_2_HIGH; + } + + ospi_mgr_cfg.Req2AckTime = 1; + + if (HAL_OSPIM_Config(&dev_data->hospi, &ospi_mgr_cfg, + HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + LOG_ERR("OSPI M config failed"); + return -EIO; + } + + /* OCTOSPI2 delay block init Function */ + HAL_OSPI_DLYB_CfgTypeDef ospi_delay_block_cfg = {0}; + + ospi_delay_block_cfg.Units = 56; + ospi_delay_block_cfg.PhaseSel = 2; + if (HAL_OSPI_DLYB_SetConfig(&dev_data->hospi, &ospi_delay_block_cfg) != HAL_OK) { + LOG_ERR("OSPI DelayBlock failed"); + return -EIO; + } +#endif /* CONFIG_SOC_SERIES_STM32U5X */ + + /* Reset NOR flash memory : still with the SPI/STR config for the NOR */ + if (stm32_ospi_mem_reset(dev) != 0) { + LOG_ERR("OSPI reset failed"); + return -EIO; + } + + /* Check if memory is ready in the SPI/STR mode */ + if (stm32_ospi_mem_ready(&dev_data->hospi, + OSPI_SPI_MODE, OSPI_STR_TRANSFER) != 0) { + LOG_ERR("OSPI memory not ready"); + return -EIO; + } + + if (stm32_ospi_config_mem(dev) != 0) { + LOG_ERR("OSPI mode not config'd (%u rate %u)", + dev_cfg->data_mode, dev_cfg->data_rate); + return -EIO; + } + + /* Send the instruction to read the SFDP */ + const uint8_t decl_nph = 2; + union { + /* We only process BFP so use one parameter block */ + uint8_t raw[JESD216_SFDP_SIZE(decl_nph)]; + struct jesd216_sfdp_header sfdp; + } u; + const struct jesd216_sfdp_header *hp = &u.sfdp; + + ret = ospi_read_sfdp(dev, 0, u.raw, sizeof(u.raw)); + if (ret != 0) { + LOG_ERR("SFDP read failed: %d", ret); + return ret; + } + + uint32_t magic = jesd216_sfdp_magic(hp); + + if (magic != JESD216_SFDP_MAGIC) { + LOG_ERR("SFDP magic %08x invalid", magic); + return -EINVAL; + } + + LOG_DBG("%s: SFDP v %u.%u AP %x with %u PH", dev->name, + hp->rev_major, hp->rev_minor, hp->access, 1 + hp->nph); + + const struct jesd216_param_header *php = hp->phdr; + const struct jesd216_param_header *phpe = php + + MIN(decl_nph, 1 + hp->nph); + + while (php != phpe) { + uint16_t id = jesd216_param_id(php); + + LOG_DBG("PH%u: %04x rev %u.%u: %u DW @ %x", + (php - hp->phdr), id, php->rev_major, php->rev_minor, + php->len_dw, jesd216_param_addr(php)); + + if (id == JESD216_SFDP_PARAM_ID_BFP) { + union { + uint32_t dw[MIN(php->len_dw, 20)]; + struct jesd216_bfp bfp; + } u; + const struct jesd216_bfp *bfp = &u.bfp; + + ret = ospi_read_sfdp(dev, jesd216_param_addr(php), + (uint8_t *)u.dw, sizeof(u.dw)); + if (ret == 0) { + ret = spi_nor_process_bfp(dev, php, bfp); + } + + if (ret != 0) { + LOG_ERR("SFDP BFP failed: %d", ret); + break; + } + } + ++php; + } + +#if defined(CONFIG_FLASH_PAGE_LAYOUT) + ret = setup_pages_layout(dev); + if (ret != 0) { + LOG_ERR("layout setup failed: %d", ret); + return -ENODEV; + } +#endif /* CONFIG_FLASH_PAGE_LAYOUT */ + + /* Initialize semaphores */ + k_sem_init(&dev_data->sem, 1, 1); + k_sem_init(&dev_data->sync, 0, 1); + + /* Run IRQ init */ + dev_cfg->irq_config(dev); + + return 0; +} + +#define OSPI_FLASH_MODULE(drv_id, flash_id) \ + (DT_DRV_INST(drv_id), ospi_nor_flash_##flash_id) + +static void flash_stm32_ospi_irq_config_func(const struct device *dev); + +#define STM32_OSPI_NODE DT_INST_PARENT(0) + +PINCTRL_DT_DEFINE(STM32_OSPI_NODE); + +static const struct flash_stm32_ospi_config flash_stm32_ospi_cfg = { + .regs = (OCTOSPI_TypeDef *)DT_REG_ADDR(STM32_OSPI_NODE), + .pclken = { + .enr = DT_CLOCKS_CELL(STM32_OSPI_NODE, bits), + .bus = DT_CLOCKS_CELL(STM32_OSPI_NODE, bus) + }, + .irq_config = flash_stm32_ospi_irq_config_func, + .flash_size = DT_INST_PROP(0, size) / 8U, + .max_frequency = DT_INST_PROP(0, ospi_max_frequency), + .data_mode = DT_INST_PROP(0, spi_bus_width), /* SPI or OPI */ + .data_rate = DT_INST_PROP(0, data_rate), /* DTR or STR */ + .pcfg = PINCTRL_DT_DEV_CONFIG_GET(STM32_OSPI_NODE), +#if STM32_OSPI_RESET_GPIO + .reset = GPIO_DT_SPEC_INST_GET(0, reset_gpios), +#endif /* STM32_OSPI_RESET_GPIO */ +#if DT_NODE_HAS_PROP(DT_INST(0, st_stm32_ospi_nor), sfdp_bfp) + .sfdp_bfp = DT_INST_PROP(0, sfdp_bfp), +#endif /* sfdp_bfp */ +}; + +static struct flash_stm32_ospi_data flash_stm32_ospi_dev_data = { + .hospi = { + .Instance = (OCTOSPI_TypeDef *)DT_REG_ADDR(STM32_OSPI_NODE), + .Init = { + .FifoThreshold = STM32_OSPI_FIFO_THRESHOLD, + .SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE, + .ChipSelectHighTime = 1, + .ClockMode = HAL_OSPI_CLOCK_MODE_0, + }, + }, +}; + +DEVICE_DT_INST_DEFINE(0, &flash_stm32_ospi_init, NULL, + &flash_stm32_ospi_dev_data, &flash_stm32_ospi_cfg, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, + &flash_stm32_ospi_driver_api); + +static void flash_stm32_ospi_irq_config_func(const struct device *dev) +{ + IRQ_CONNECT(DT_IRQN(STM32_OSPI_NODE), DT_IRQ(STM32_OSPI_NODE, priority), + flash_stm32_ospi_isr, DEVICE_DT_INST_GET(0), 0); + irq_enable(DT_IRQN(STM32_OSPI_NODE)); +}