/*
 * Copyright (c) 2024 STMicroelectronics
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/*
 * **************************************************************************
 * xSPI flash controller driver for stm32 series with xSPI periherals
 * This driver is based on the stm32Cube HAL XSPI driver
 * with one xspi DTS NODE
 * **************************************************************************
 */
#define DT_DRV_COMPAT st_stm32_xspi_nor

#include <errno.h>
#include <zephyr/kernel.h>
#include <soc.h>
#include <stm32_bitops.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/dt-bindings/flash_controller/xspi.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/irq.h>

#include "spi_nor.h"
#include "jesd216.h"

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(flash_stm32_xspi, CONFIG_FLASH_LOG_LEVEL);

#define STM32_XSPI_NODE(inst) DT_INST_PARENT(inst)

#define STM32_XSPI_RESET_GPIO DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)

#ifdef CONFIG_FLASH_STM32_XSPI_DMA
#include <zephyr/drivers/dma/dma_stm32.h>
#include <zephyr/drivers/dma.h>
#include <stm32_ll_dma.h>
#endif /* CONFIG_FLASH_STM32_XSPI_DMA */

#if defined(CONFIG_SOC_SERIES_STM32H7RSX)
#include <stm32_ll_pwr.h>
#include <stm32_ll_system.h>
#endif /* CONFIG_SOC_SERIES_STM32H7RSX */

#include "flash_stm32_xspi.h"

#if defined(CONFIG_STM32_HAL2)
#define STM32_XSPI_TIMEOUT_DEFAULT_VALUE 5000U

#define XSPI_HandleTypeDef hal_xspi_handle_t
#define XSPI_RegularCmdTypeDef hal_xspi_regular_cmd_t
#define XSPI_AutoPollingTypeDef hal_xspi_auto_polling_config_t
#define XSPI_MemoryMappedTypeDef hal_xspi_memory_mapped_config_t
#define HAL_StatusTypeDef hal_status_t

#define HAL_XSPI_TIMEOUT_DEFAULT_VALUE STM32_XSPI_TIMEOUT_DEFAULT_VALUE
#define HAL_XSPI_Command HAL_XSPI_SendRegularCmd
#define HAL_XSPI_AutoPolling_IT HAL_XSPI_ExecRegularAutoPoll_IT
#define HAL_XSPI_MemoryMapped HAL_XSPI_StartMemoryMappedMode
#define HAL_XSPI_Abort(hxspi) HAL_XSPI_Abort((hxspi), STM32_XSPI_TIMEOUT_DEFAULT_VALUE)

#define HAL_XSPI_OPTYPE_COMMON_CFG HAL_XSPI_OPERATION_COMMON_CFG
#define HAL_XSPI_OPTYPE_READ_CFG HAL_XSPI_OPERATION_READ_CFG
#define HAL_XSPI_OPTYPE_WRITE_CFG HAL_XSPI_OPERATION_WRITE_CFG
#define HAL_XSPI_INSTRUCTION_1_LINE HAL_XSPI_INSTRUCTION_1LINE
#define HAL_XSPI_INSTRUCTION_2_LINES HAL_XSPI_INSTRUCTION_2LINES
#define HAL_XSPI_INSTRUCTION_4_LINES HAL_XSPI_INSTRUCTION_4LINES
#define HAL_XSPI_INSTRUCTION_8_LINES HAL_XSPI_INSTRUCTION_8LINES
#define HAL_XSPI_INSTRUCTION_8_BITS HAL_XSPI_INSTRUCTION_8BIT
#define HAL_XSPI_INSTRUCTION_16_BITS HAL_XSPI_INSTRUCTION_16BIT
#define HAL_XSPI_INSTRUCTION_DTR_DISABLE HAL_XSPI_INSTRUCTION_DTR_DISABLED
#define HAL_XSPI_INSTRUCTION_DTR_ENABLE HAL_XSPI_INSTRUCTION_DTR_ENABLED
#define HAL_XSPI_ADDRESS_NONE HAL_XSPI_ADDR_NONE
#define HAL_XSPI_ADDRESS_1_LINE HAL_XSPI_ADDR_1LINE
#define HAL_XSPI_ADDRESS_2_LINES HAL_XSPI_ADDR_2LINES
#define HAL_XSPI_ADDRESS_4_LINES HAL_XSPI_ADDR_4LINES
#define HAL_XSPI_ADDRESS_8_LINES HAL_XSPI_ADDR_8LINES
#define HAL_XSPI_ADDRESS_24_BITS HAL_XSPI_ADDR_24BIT
#define HAL_XSPI_ADDRESS_32_BITS HAL_XSPI_ADDR_32BIT
#define HAL_XSPI_ADDRESS_DTR_DISABLE HAL_XSPI_ADDR_DTR_DISABLED
#define HAL_XSPI_ADDRESS_DTR_ENABLE HAL_XSPI_ADDR_DTR_ENABLED
#define HAL_XSPI_ALT_BYTES_NONE HAL_XSPI_ALTERNATE_BYTES_NONE
#define HAL_XSPI_DATA_NONE HAL_XSPI_REGULAR_DATA_NONE
#define HAL_XSPI_DATA_1_LINE HAL_XSPI_REGULAR_DATA_1LINE
#define HAL_XSPI_DATA_2_LINES HAL_XSPI_REGULAR_DATA_2LINES
#define HAL_XSPI_DATA_4_LINES HAL_XSPI_REGULAR_DATA_4LINES
#define HAL_XSPI_DATA_8_LINES HAL_XSPI_REGULAR_DATA_8LINES
#define HAL_XSPI_DATA_DTR_DISABLE HAL_XSPI_DATA_DTR_DISABLED
#define HAL_XSPI_DATA_DTR_ENABLE HAL_XSPI_DATA_DTR_ENABLED
#define HAL_XSPI_DQS_DISABLE HAL_XSPI_DQS_DISABLED
#define HAL_XSPI_DQS_ENABLE HAL_XSPI_DQS_ENABLED
#define HAL_XSPI_AUTOMATIC_STOP_ENABLE HAL_XSPI_AUTOMATIC_STOP_ENABLED
#define HAL_XSPI_TIMEOUT_COUNTER_DISABLE HAL_XSPI_TIMEOUT_DISABLE
#define HAL_XSPI_MEMTYPE_MACRONIX HAL_XSPI_MEMORY_TYPE_MACRONIX
#define HAL_XSPI_DHQC_ENABLE HAL_XSPI_DELAY_HOLD_QUARTCYCLE
#define OperationType operation_type
#define Instruction instruction
#define InstructionMode instruction_mode
#define InstructionWidth instruction_width
#define InstructionDTRMode instruction_dtr_mode_status
#define Address addr
#define AddressMode addr_mode
#define AddressWidth addr_width
#define AddressDTRMode addr_dtr_mode_status
#define AlternateBytes alternate_bytes
#define AlternateBytesMode alternate_bytes_mode
#define AlternateBytesWidth alternate_bytes_width
#define AlternateBytesDTRMode alternate_bytes_dtr_mode_status
#define DataMode data_mode
#define DataDTRMode data_dtr_mode_status
#define DQSMode dqs_mode_status
#define DummyCycles dummy_cycle
#define DataLength size_byte
#define MatchValue match_value
#define MatchMask match_mask
#define MatchMode match_mode
#define IntervalTime interval_cycle
#define AutomaticStop automatic_stop_status
#define TimeOutActivation timeout_activation
#define TimeOutPeriod timeout_period_cycle

static inline XSPI_TypeDef *stm32_xspi_hal2_instance(hal_xspi_handle_t *hxspi)
{
	return (XSPI_TypeDef *)((uint32_t)hxspi->instance);
}

static int stm32_xspi_hal2_init(const struct device *dev, uint32_t prescaler,
				uint32_t hold)
{
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;
	struct flash_stm32_xspi_data *dev_data = dev->data;
	hal_xspi_config_t xspi_config = {
		.memory = {
			.mode = HAL_XSPI_MEMORY_SINGLE,
			.type = (hold == HAL_XSPI_DELAY_HOLD_QUARTCYCLE)
				? HAL_XSPI_MEMORY_TYPE_MACRONIX
				: HAL_XSPI_MEMORY_TYPE_MICRON,
			.size_bit = (find_lsb_set(dev_cfg->flash_size) - 2U)
				<< XSPI_DCR1_DEVSIZE_Pos,
			.wrap_size_byte = HAL_XSPI_WRAP_NOT_SUPPORTED,
			.cs_boundary = HAL_XSPI_CS_BOUNDARY_NONE,
		},
		.timing = {
			.clk_prescaler = prescaler,
			.shift = dev_cfg->sample_shift,
			.hold = hold,
			.cs_high_time_cycle = 2U,
			.cs_refresh_time_cycle = 0U,
			.dlyb_state = dev_cfg->dlyb_state,
		},
	};

	if (HAL_XSPI_Init(&dev_data->hxspi, dev_data->hxspi.instance) != HAL_OK) {
		LOG_ERR("XSPI HAL2 init failed");
		return -EIO;
	}

	if (HAL_XSPI_SetConfig(&dev_data->hxspi, &xspi_config) != HAL_OK) {
		LOG_ERR("XSPI HAL2 config failed");
		return -EIO;
	}

	return 0;
}
#endif /* CONFIG_STM32_HAL2 */

static inline void xspi_lock_thread(const struct device *dev)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;

	k_sem_take(&dev_data->sem, K_FOREVER);
}

static inline void xspi_unlock_thread(const struct device *dev)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;

	k_sem_give(&dev_data->sem);
}

static int xspi_send_cmd(const struct device *dev, XSPI_RegularCmdTypeDef *cmd)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;
	HAL_StatusTypeDef hal_ret;

	LOG_DBG("Instruction 0x%x", cmd->Instruction);

	dev_data->cmd_status = 0;

	hal_ret = HAL_XSPI_Command(&dev_data->hxspi, cmd, HAL_XSPI_TIMEOUT_DEFAULT_VALUE);
	if (hal_ret != HAL_OK) {
		LOG_ERR("%d: Failed to send XSPI instruction", hal_ret);
		return -EIO;
	}
#if defined(CONFIG_STM32_HAL2)
	LOG_DBG("CCR 0x%x", stm32_xspi_hal2_instance(&dev_data->hxspi)->CCR);
#else
	LOG_DBG("CCR 0x%x", dev_data->hxspi.Instance->CCR);
#endif /* CONFIG_STM32_HAL2 */

	return dev_data->cmd_status;
}

static int xspi_read_access(const struct device *dev, XSPI_RegularCmdTypeDef *cmd,
			    uint8_t *data, const size_t size)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;
	HAL_StatusTypeDef hal_ret;

	LOG_DBG("Instruction 0x%x", cmd->Instruction);

	cmd->DataLength = size;

	dev_data->cmd_status = 0;

	hal_ret = HAL_XSPI_Command(&dev_data->hxspi, cmd, HAL_XSPI_TIMEOUT_DEFAULT_VALUE);
	if (hal_ret != HAL_OK) {
		LOG_ERR("%d: Failed to send XSPI instruction", hal_ret);
		return -EIO;
	}

#ifdef CONFIG_FLASH_STM32_XSPI_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);
		return -EIO;
	}

	k_sem_take(&dev_data->sync, K_FOREVER);

	return dev_data->cmd_status;
}

static int xspi_write_access(const struct device *dev, XSPI_RegularCmdTypeDef *cmd,
			     const uint8_t *data, const size_t size)
{
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;
	struct flash_stm32_xspi_data *dev_data = dev->data;
	HAL_StatusTypeDef hal_ret;

	LOG_DBG("Instruction 0x%x", cmd->Instruction);

	cmd->DataLength = size;

	dev_data->cmd_status = 0;

	/* in OPI/STR the 3-byte AddressWidth is not supported by the NOR flash */
	if ((dev_cfg->data_mode == XSPI_OCTO_MODE) &&
		(cmd->AddressWidth != HAL_XSPI_ADDRESS_32_BITS)) {
		LOG_ERR("XSPI wr in OPI/STR mode is for 32bit address only");
		return -EIO;
	}

	hal_ret = HAL_XSPI_Command(&dev_data->hxspi, cmd, HAL_XSPI_TIMEOUT_DEFAULT_VALUE);
	if (hal_ret != HAL_OK) {
		LOG_ERR("%d: Failed to send XSPI instruction", hal_ret);
		return -EIO;
	}

#ifdef CONFIG_FLASH_STM32_XSPI_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);
		return -EIO;
	}

	k_sem_take(&dev_data->sync, K_FOREVER);

	return dev_data->cmd_status;
}

/*
 * Gives a XSPI_RegularCmdTypeDef with all parameters set
 * except Instruction, Address, DummyCycles, NbData
 */
static XSPI_RegularCmdTypeDef xspi_prepare_cmd(const uint8_t transfer_mode,
					       const uint8_t transfer_rate)
{
	XSPI_RegularCmdTypeDef cmd_tmp = {
		.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG,
		.InstructionWidth = ((transfer_mode == XSPI_OCTO_MODE)
				? HAL_XSPI_INSTRUCTION_16_BITS
				: HAL_XSPI_INSTRUCTION_8_BITS),
		.InstructionDTRMode = ((transfer_rate == XSPI_DTR_TRANSFER)
				? HAL_XSPI_INSTRUCTION_DTR_ENABLE
				: HAL_XSPI_INSTRUCTION_DTR_DISABLE),
		.AddressDTRMode = ((transfer_rate == XSPI_DTR_TRANSFER)
				? HAL_XSPI_ADDRESS_DTR_ENABLE
				: HAL_XSPI_ADDRESS_DTR_DISABLE),
		/* AddressWidth must be set to 32bits for init and mem config phase */
		.AddressWidth = HAL_XSPI_ADDRESS_32_BITS,
		.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE,
		.DataDTRMode = ((transfer_rate == XSPI_DTR_TRANSFER)
				? HAL_XSPI_DATA_DTR_ENABLE
				: HAL_XSPI_DATA_DTR_DISABLE),
		.DQSMode = (transfer_rate == XSPI_DTR_TRANSFER)
				? HAL_XSPI_DQS_ENABLE
				: HAL_XSPI_DQS_DISABLE,
#ifdef XSPI_CCR_SIOO
		.SIOOMode = HAL_XSPI_SIOO_INST_EVERY_CMD,
#endif /* XSPI_CCR_SIOO */
	};

	switch (transfer_mode) {
	case XSPI_OCTO_MODE: {
		cmd_tmp.InstructionMode = HAL_XSPI_INSTRUCTION_8_LINES;
		cmd_tmp.AddressMode = HAL_XSPI_ADDRESS_8_LINES;
		cmd_tmp.DataMode = HAL_XSPI_DATA_8_LINES;
		break;
	}
	case XSPI_QUAD_MODE: {
		cmd_tmp.InstructionMode = HAL_XSPI_INSTRUCTION_4_LINES;
		cmd_tmp.AddressMode = HAL_XSPI_ADDRESS_4_LINES;
		cmd_tmp.DataMode = HAL_XSPI_DATA_4_LINES;
		break;
	}
	case XSPI_DUAL_MODE: {
		cmd_tmp.InstructionMode = HAL_XSPI_INSTRUCTION_2_LINES;
		cmd_tmp.AddressMode = HAL_XSPI_ADDRESS_2_LINES;
		cmd_tmp.DataMode = HAL_XSPI_DATA_2_LINES;
		break;
	}
	default: {
		cmd_tmp.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
		cmd_tmp.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
		cmd_tmp.DataMode = HAL_XSPI_DATA_1_LINE;
		break;
	}
	}

	return cmd_tmp;
}

static uint32_t stm32_xspi_hal_address_size(const struct device *dev)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;

	if (dev_data->address_width == 4U) {
		return HAL_XSPI_ADDRESS_32_BITS;
	}

	return HAL_XSPI_ADDRESS_24_BITS;
}

#if defined(CONFIG_FLASH_JESD216_API)
/*
 * Read the JEDEC ID data from the external Flash at init
 * and store in the jedec_id Table of the flash_stm32_xspi_data
 * The JEDEC ID is not given by a DTS property
 */
static int stm32_xspi_read_jedec_id(const struct device *dev)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;

	/* This is a SPI/STR command to issue to the external Flash device */
	XSPI_RegularCmdTypeDef cmd = xspi_prepare_cmd(XSPI_SPI_MODE, XSPI_STR_TRANSFER);

	cmd.Instruction = JESD216_CMD_READ_ID;
	cmd.AddressWidth = stm32_xspi_hal_address_size(dev);
	cmd.AddressMode = HAL_XSPI_ADDRESS_NONE;
	cmd.DataLength = JESD216_READ_ID_LEN; /* 3 bytes in the READ ID */

	HAL_StatusTypeDef hal_ret;

	hal_ret = HAL_XSPI_Command(&dev_data->hxspi, &cmd,
				   HAL_XSPI_TIMEOUT_DEFAULT_VALUE);

	if (hal_ret != HAL_OK) {
		LOG_ERR("%d: Failed to send XSPI instruction", hal_ret);
		return -EIO;
	}

	/* Place the received data directly into the jedec Table */
	hal_ret = HAL_XSPI_Receive(&dev_data->hxspi, dev_data->jedec_id,
				   HAL_XSPI_TIMEOUT_DEFAULT_VALUE);
	if (hal_ret != HAL_OK) {
		LOG_ERR("%d: Failed to read data", hal_ret);
		return -EIO;
	}

	LOG_DBG("Jedec ID = [%02x %02x %02x]",
		dev_data->jedec_id[0], dev_data->jedec_id[1], dev_data->jedec_id[2]);

	dev_data->cmd_status = 0;

	return 0;
}

/*
 * Read Serial Flash ID :
 * just gives the values received by the external Flash
 */
static int xspi_read_jedec_id(const struct device *dev,  uint8_t *id)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;

	/* Take jedec Id values from the table (issued from the octoFlash) */
	memcpy(id, dev_data->jedec_id, JESD216_READ_ID_LEN);

	LOG_INF("Manuf ID = %02x   Memory Type = %02x   Memory Density = %02x",
		id[0], id[1], id[2]);

	return 0;
}
#endif /* CONFIG_FLASH_JESD216_API */

/*
 * Read Serial Flash Discovery Parameter from the external Flash at init :
 * perform a read access over SPI bus for SDFP (DataMode is already set)
 * The SFDP table is not given by a DTS property
 */
static int stm32_xspi_read_sfdp(const struct device *dev, off_t addr,
				void *data,
				size_t size)
{
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;
	struct flash_stm32_xspi_data *dev_data = dev->data;

	XSPI_RegularCmdTypeDef cmd = xspi_prepare_cmd(dev_cfg->data_mode,
						      dev_cfg->data_rate);
	if (dev_cfg->data_mode == XSPI_OCTO_MODE) {
		cmd.Instruction = JESD216_OCMD_READ_SFDP;
		cmd.DummyCycles = 20U;
		cmd.AddressWidth = HAL_XSPI_ADDRESS_32_BITS;
	} else {
		cmd.Instruction = JESD216_CMD_READ_SFDP;
		cmd.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
		cmd.DataMode = HAL_XSPI_DATA_1_LINE;
		cmd.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
		cmd.DummyCycles = 8U;
		cmd.AddressWidth = HAL_XSPI_ADDRESS_24_BITS;
	}
	cmd.Address = addr;
	cmd.DataLength = size;

	HAL_StatusTypeDef hal_ret;

	hal_ret = HAL_XSPI_Command(&dev_data->hxspi, &cmd, HAL_XSPI_TIMEOUT_DEFAULT_VALUE);
	if (hal_ret != HAL_OK) {
		LOG_ERR("%d: Failed to send XSPI instruction", hal_ret);
		return -EIO;
	}

	hal_ret = HAL_XSPI_Receive(&dev_data->hxspi, (uint8_t *)data,
				   HAL_XSPI_TIMEOUT_DEFAULT_VALUE);
	if (hal_ret != HAL_OK) {
		LOG_ERR("%d: Failed to read data", hal_ret);
		return -EIO;
	}

	dev_data->cmd_status = 0;

	return 0;
}

/*
 * Read Serial Flash Discovery Parameter :
 * perform a read access over SPI bus for SDFP (DataMode is already set)
 */
static int xspi_read_sfdp(const struct device *dev, off_t addr, void *data,
			  size_t size)
{
	LOG_INF("Read SFDP from externalFlash");
	/* Get the SFDP from the external Flash (no sfdp-bfp table in the DeviceTree) */
	if (stm32_xspi_read_sfdp(dev, addr, data, size) == 0) {
		/* If valid, then ignore any table from the DTS */
		return 0;
	}
	LOG_INF("Error reading SFDP from external Flash and none in the DTS");
	return -EINVAL;
}

static bool xspi_address_is_valid(const struct device *dev, off_t addr,
				  size_t size)
{
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;
	size_t flash_size = dev_cfg->flash_size;

	return (addr >= 0) && ((uint64_t)addr + (uint64_t)size <= flash_size);
}

static int stm32_xspi_wait_auto_polling(const struct device *dev,
		XSPI_AutoPollingTypeDef *s_config, uint32_t timeout_ms)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;

	dev_data->cmd_status = 0;

	if (HAL_XSPI_AutoPolling_IT(&dev_data->hxspi, s_config) != HAL_OK) {
		LOG_ERR("XSPI AutoPoll failed");
		return -EIO;
	}

	if (k_sem_take(&dev_data->sync, K_MSEC(timeout_ms)) != 0) {
		LOG_ERR("XSPI AutoPoll wait failed");
		if (HAL_XSPI_Abort(&dev_data->hxspi) != HAL_OK) {
			LOG_ERR("XSPI abort failed");
		}
		k_sem_reset(&dev_data->sync);
		return -EIO;
	}

	/* HAL_XSPI_AutoPolling_IT enables transfer error interrupt which sets
	 * cmd_status.
	 */
	return dev_data->cmd_status;
}

/*
 * This function Polls the WEL (write enable latch) bit to become to 0
 * When the Chip Erase Cycle is completed, the Write Enable Latch (WEL) bit is cleared.
 * in nor_mode SPI/OPI XSPI_SPI_MODE or XSPI_OCTO_MODE
 * and nor_rate transfer STR/DTR XSPI_STR_TRANSFER or XSPI_DTR_TRANSFER
 */
static int stm32_xspi_mem_erased(const struct device *dev)
{
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;
	struct flash_stm32_xspi_data *dev_data = dev->data;
	uint8_t nor_mode = dev_cfg->data_mode;
	uint8_t nor_rate = dev_cfg->data_rate;

	XSPI_AutoPollingTypeDef s_config = {0};
	XSPI_RegularCmdTypeDef s_command = xspi_prepare_cmd(nor_mode, nor_rate);

	/* Configure automatic polling mode command to wait for memory ready */
	if (nor_mode == XSPI_OCTO_MODE) {
		s_command.Instruction = SPI_NOR_OCMD_RDSR;
		s_command.DummyCycles = (nor_rate == XSPI_DTR_TRANSFER)
					? SPI_NOR_DUMMY_REG_OCTAL_DTR
					: SPI_NOR_DUMMY_REG_OCTAL;
	} else {
		s_command.Instruction = SPI_NOR_CMD_RDSR;
		/* force 1-line InstructionMode for any non-OSPI transfer */
		s_command.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
		s_command.AddressMode = HAL_XSPI_ADDRESS_NONE;
		/* force 1-line DataMode for any non-OSPI transfer */
		s_command.DataMode = HAL_XSPI_DATA_1_LINE;
		s_command.DummyCycles = 0;
	}
	s_command.DataLength = ((nor_rate == XSPI_DTR_TRANSFER) ? 2U : 1U);
	s_command.Address = 0U;

	/* Set the mask to  0x02 to mask all Status REG bits except WEL */
	/* Set the match to 0x00 to check if the WEL bit is Reset */
	s_config.MatchValue         = SPI_NOR_WEL_MATCH;
	s_config.MatchMask          = SPI_NOR_WEL_MASK; /* Write Enable Latch */

	s_config.MatchMode          = HAL_XSPI_MATCH_MODE_AND;
	s_config.IntervalTime       = SPI_NOR_AUTO_POLLING_INTERVAL;
	s_config.AutomaticStop      = HAL_XSPI_AUTOMATIC_STOP_ENABLE;

	if (HAL_XSPI_Command(&dev_data->hxspi, &s_command,
		HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("XSPI AutoPoll command (WEL) failed");
		return -EIO;
	}

	/* Start Automatic-Polling mode to wait until the memory is totally erased */
	return stm32_xspi_wait_auto_polling(dev,
			&s_config, STM32_XSPI_BULK_ERASE_MAX_TIME);
}

/*
 * This function Polls the WIP(Write In Progress) bit to become to 0
 * in nor_mode SPI/OPI XSPI_SPI_MODE or XSPI_OCTO_MODE
 * and nor_rate transfer STR/DTR XSPI_STR_TRANSFER or XSPI_DTR_TRANSFER
 */
static int stm32_xspi_mem_ready(const struct device *dev, uint8_t nor_mode,
		uint8_t nor_rate)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;

	XSPI_AutoPollingTypeDef s_config = {0};
	XSPI_RegularCmdTypeDef s_command = xspi_prepare_cmd(nor_mode, nor_rate);

	/* Configure automatic polling mode command to wait for memory ready */
	if (nor_mode == XSPI_OCTO_MODE) {
		s_command.Instruction = SPI_NOR_OCMD_RDSR;
		s_command.DummyCycles = (nor_rate == XSPI_DTR_TRANSFER)
					? SPI_NOR_DUMMY_REG_OCTAL_DTR
					: SPI_NOR_DUMMY_REG_OCTAL;
	} else {
		s_command.Instruction = SPI_NOR_CMD_RDSR;
		/* force 1-line InstructionMode for any non-OSPI transfer */
		s_command.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
		s_command.AddressMode = HAL_XSPI_ADDRESS_NONE;
		/* force 1-line DataMode for any non-OSPI transfer */
		s_command.DataMode = HAL_XSPI_DATA_1_LINE;
		s_command.DummyCycles = 0;
	}
	s_command.DataLength = ((nor_rate == XSPI_DTR_TRANSFER) ? 2U : 1U);
	s_command.Address = 0U;

	/* 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.MatchValue         = SPI_NOR_MEM_RDY_MATCH;
	s_config.MatchMask          = SPI_NOR_MEM_RDY_MASK; /* Write in progress */
	s_config.MatchMode          = HAL_XSPI_MATCH_MODE_AND;
	s_config.IntervalTime       = SPI_NOR_AUTO_POLLING_INTERVAL;
	s_config.AutomaticStop      = HAL_XSPI_AUTOMATIC_STOP_ENABLE;

	if (HAL_XSPI_Command(&dev_data->hxspi, &s_command,
		HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("XSPI AutoPoll command failed");
		return -EIO;
	}

	/* Start Automatic-Polling mode to wait until the memory is ready WIP=0 */
	return stm32_xspi_wait_auto_polling(dev, &s_config, HAL_XSPI_TIMEOUT_DEFAULT_VALUE);
}

/* Enables writing to the memory sending a Write Enable and wait it is effective */
static int stm32_xspi_write_enable(const struct device *dev,
		uint8_t nor_mode, uint8_t nor_rate)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;

	XSPI_AutoPollingTypeDef s_config = {0};
	XSPI_RegularCmdTypeDef s_command = xspi_prepare_cmd(nor_mode, nor_rate);

	/* Initialize the write enable command */
	if (nor_mode == XSPI_OCTO_MODE) {
		s_command.Instruction = SPI_NOR_OCMD_WREN;
	} else {
		s_command.Instruction = SPI_NOR_CMD_WREN;
		/* force 1-line InstructionMode for any non-OSPI transfer */
		s_command.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
	}
	s_command.AddressMode = HAL_XSPI_ADDRESS_NONE;
	s_command.DataMode    = HAL_XSPI_DATA_NONE;
	s_command.DummyCycles = 0U;

	if (HAL_XSPI_Command(&dev_data->hxspi, &s_command,
		HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("XSPI flash write enable cmd failed");
		return -EIO;
	}

	/* New command to Configure automatic polling mode to wait for write enabling */
	if (nor_mode == XSPI_OCTO_MODE) {
		s_command.Instruction = SPI_NOR_OCMD_RDSR;
		s_command.AddressMode = HAL_XSPI_ADDRESS_8_LINES;
		s_command.DataMode = HAL_XSPI_DATA_8_LINES;
		s_command.DummyCycles = (nor_rate == XSPI_DTR_TRANSFER)
				? SPI_NOR_DUMMY_REG_OCTAL_DTR
						: SPI_NOR_DUMMY_REG_OCTAL;
	} else {
		s_command.Instruction = SPI_NOR_CMD_RDSR;
		/* force 1-line DataMode for any non-OSPI transfer */
		s_command.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
		s_command.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
		s_command.DataMode = HAL_XSPI_DATA_1_LINE;
		s_command.DummyCycles = 0;

		/* DummyCycles remains 0 */
	}
	s_command.DataLength = (nor_rate == XSPI_DTR_TRANSFER) ? 2U : 1U;
	s_command.Address = 0U;

	if (HAL_XSPI_Command(&dev_data->hxspi, &s_command,
		HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("XSPI config auto polling cmd failed");
		return -EIO;
	}

	s_config.MatchValue      = SPI_NOR_WREN_MATCH;
	s_config.MatchMask       = SPI_NOR_WREN_MASK;
	s_config.MatchMode       = HAL_XSPI_MATCH_MODE_AND;
	s_config.IntervalTime    = SPI_NOR_AUTO_POLLING_INTERVAL;
	s_config.AutomaticStop   = HAL_XSPI_AUTOMATIC_STOP_ENABLE;

	return stm32_xspi_wait_auto_polling(dev, &s_config, HAL_XSPI_TIMEOUT_DEFAULT_VALUE);
}

static int xspi_write_unprotect(const struct device *dev)
{
	int ret = 0;

	/* This is a SPI/STR command to issue to the external Flash device */
	XSPI_RegularCmdTypeDef cmd_unprotect = xspi_prepare_cmd(XSPI_SPI_MODE, XSPI_STR_TRANSFER);

	cmd_unprotect.Instruction = SPI_NOR_CMD_ULBPR;
	cmd_unprotect.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
	cmd_unprotect.AddressMode = HAL_XSPI_ADDRESS_NONE;
	cmd_unprotect.DataMode    = HAL_XSPI_DATA_NONE;

	ret = stm32_xspi_write_enable(dev, XSPI_SPI_MODE, XSPI_STR_TRANSFER);
	if (ret != 0) {
		return ret;
	}

	return xspi_send_cmd(dev, &cmd_unprotect);
}

/* Write Flash configuration register 2 with new dummy cycles */
static int stm32_xspi_write_cfg2reg_dummy(const struct device *dev,
					uint8_t nor_mode, uint8_t nor_rate)
{
	XSPI_RegularCmdTypeDef s_command = xspi_prepare_cmd(nor_mode, nor_rate);
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;
	struct flash_stm32_xspi_data *dev_data = dev->data;
	uint8_t transmit_data;

	if (dev_cfg->max_frequency == MHZ(200)) {
		/* Use memory default value */
		return 0;
	}

	transmit_data = SPI_NOR_CR2_DUMMY_CYCLES_66MHZ;

	/* Initialize the writing of configuration register 2 */
	s_command.Instruction = (nor_mode == XSPI_SPI_MODE)
				? SPI_NOR_CMD_WR_CFGREG2
				: SPI_NOR_OCMD_WR_CFGREG2;
	s_command.Address = SPI_NOR_REG2_ADDR3;
	s_command.DummyCycles = 0U;
	s_command.DataLength = (nor_mode == XSPI_SPI_MODE) ? 1U
			: ((nor_rate == XSPI_DTR_TRANSFER) ? 2U : 1U);

	if (HAL_XSPI_Command(&dev_data->hxspi, &s_command,
		HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("XSPI transmit cmd");
		return -EIO;
	}

	if (HAL_XSPI_Transmit(&dev_data->hxspi, &transmit_data,
		HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("XSPI transmit ");
		return -EIO;
	}

	return 0;
}

/* Write Flash configuration register 2 with new single or octal SPI protocol */
static int stm32_xspi_write_cfg2reg_io(XSPI_HandleTypeDef *hxspi,
				       uint8_t nor_mode, uint8_t nor_rate, uint8_t op_enable)
{
	XSPI_RegularCmdTypeDef s_command = xspi_prepare_cmd(nor_mode, nor_rate);

	/* Initialize the writing of configuration register 2 */
	s_command.Instruction = (nor_mode == XSPI_SPI_MODE)
				? SPI_NOR_CMD_WR_CFGREG2
				: SPI_NOR_OCMD_WR_CFGREG2;
	s_command.Address = SPI_NOR_REG2_ADDR1;
	s_command.DummyCycles = 0U;
	s_command.DataLength = (nor_mode == XSPI_SPI_MODE) ? 1U
		: ((nor_rate == XSPI_DTR_TRANSFER) ? 2U : 1U);

	if (HAL_XSPI_Command(hxspi, &s_command,
		HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("Write Flash configuration reg2 failed");
		return -EIO;
	}

	if (HAL_XSPI_Transmit(hxspi, &op_enable,
		HAL_XSPI_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_xspi_read_cfg2reg(XSPI_HandleTypeDef *hxspi,
				   uint8_t nor_mode, uint8_t nor_rate, uint8_t *value)
{
	XSPI_RegularCmdTypeDef s_command = xspi_prepare_cmd(nor_mode, nor_rate);

	/* Initialize the writing of configuration register 2 */
	s_command.Instruction = (nor_mode == XSPI_SPI_MODE)
				? SPI_NOR_CMD_RD_CFGREG2
				: SPI_NOR_OCMD_RD_CFGREG2;
	s_command.Address = SPI_NOR_REG2_ADDR1;
	s_command.DummyCycles = (nor_mode == XSPI_SPI_MODE)
				? 0U
				: ((nor_rate == XSPI_DTR_TRANSFER)
					? SPI_NOR_DUMMY_REG_OCTAL_DTR
					: SPI_NOR_DUMMY_REG_OCTAL);
	s_command.DataLength = (nor_rate == XSPI_DTR_TRANSFER) ? 2U : 1U;

	if (HAL_XSPI_Command(hxspi, &s_command, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("Write Flash configuration reg2 failed");
		return -EIO;
	}

	if (HAL_XSPI_Receive(hxspi, value, HAL_XSPI_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_xspi_config_mem(const struct device *dev)
{
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;
	struct flash_stm32_xspi_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 != XSPI_OCTO_MODE)
		&& (dev_cfg->data_rate == XSPI_STR_TRANSFER)) {
		LOG_INF("OSPI flash config is SPI|DUAL|QUAD / STR");
		return 0;
	}

	/* Going to set the XPI mode (STR or DTR transfer rate) */
	LOG_DBG("XSPI configuring Octo SPI mode");

	if (stm32_xspi_write_enable(dev,
		XSPI_SPI_MODE, XSPI_STR_TRANSFER) != 0) {
		LOG_ERR("OSPI write Enable failed");
		return -EIO;
	}

	/* Write Configuration register 2 (with new dummy cycles) */
	if (stm32_xspi_write_cfg2reg_dummy(dev,
		XSPI_SPI_MODE, XSPI_STR_TRANSFER) != 0) {
		LOG_ERR("XSPI write CFGR2 failed");
		return -EIO;
	}
	if (stm32_xspi_mem_ready(dev,
		XSPI_SPI_MODE, XSPI_STR_TRANSFER) != 0) {
		LOG_ERR("XSPI autopolling failed");
		return -EIO;
	}
	if (stm32_xspi_write_enable(dev,
		XSPI_SPI_MODE, XSPI_STR_TRANSFER) != 0) {
		LOG_ERR("XSPI 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 == XSPI_DTR_TRANSFER)
				? SPI_NOR_CR2_DTR_OPI_EN
				: SPI_NOR_CR2_STR_OPI_EN);
	if (stm32_xspi_write_cfg2reg_io(&dev_data->hxspi,
		XSPI_SPI_MODE, XSPI_STR_TRANSFER, mode_enable) != 0) {
		LOG_ERR("XSPI write CFGR2 failed");
		return -EIO;
	}

	/* Wait that the configuration is effective and check that memory is ready */
	k_busy_wait(STM32_XSPI_WRITE_REG_MAX_TIME * USEC_PER_MSEC);

	/* Reconfigure the memory type of the peripheral */
#if defined(CONFIG_STM32_HAL2)
	if (stm32_xspi_hal2_init(dev, HAL_XSPI_GetPrescaler(&dev_data->hxspi),
				 HAL_XSPI_DELAY_HOLD_QUARTCYCLE) != 0) {
#else
	dev_data->hxspi.Init.MemoryType            = HAL_XSPI_MEMTYPE_MACRONIX;
	dev_data->hxspi.Init.DelayHoldQuarterCycle = HAL_XSPI_DHQC_ENABLE;
	if (HAL_XSPI_Init(&dev_data->hxspi) != HAL_OK) {
#endif /* CONFIG_STM32_HAL2 */
		LOG_ERR("XSPI mem type MACRONIX failed");
		return -EIO;
	}

	if (dev_cfg->data_rate == XSPI_STR_TRANSFER) {
		if (stm32_xspi_mem_ready(dev,
			XSPI_OCTO_MODE, XSPI_STR_TRANSFER) != 0) {
			/* Check Flash busy ? */
			LOG_ERR("XSPI flash busy failed");
			return -EIO;
		}

		if (stm32_xspi_read_cfg2reg(&dev_data->hxspi,
			XSPI_OCTO_MODE, XSPI_STR_TRANSFER, reg) != 0) {
			/* Check the configuration has been correctly done on SPI_NOR_REG2_ADDR1 */
			LOG_ERR("XSPI flash config read failed");
			return -EIO;
		}

		LOG_INF("XSPI flash config is OCTO / STR");
	}

	if (dev_cfg->data_rate == XSPI_DTR_TRANSFER) {
		if (stm32_xspi_mem_ready(dev,
			XSPI_OCTO_MODE, XSPI_DTR_TRANSFER) != 0) {
			/* Check Flash busy ? */
			LOG_ERR("XSPI flash busy failed");
			return -EIO;
		}

		if (stm32_xspi_read_cfg2reg(&dev_data->hxspi,
			XSPI_OCTO_MODE, XSPI_DTR_TRANSFER, reg) != 0) {
			/* Check the configuration has been correctly done on SPI_NOR_REG2_ADDR1 */
			LOG_ERR("XSPI flash config read failed");
			return -EIO;
		}

		LOG_INF("XSPI flash config is OCTO / DTR");
	}

	return 0;
}

/* gpio or send the different reset command to the NOR flash in SPI/OSPI and STR/DTR */
static int stm32_xspi_mem_reset(const struct device *dev)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;

#if STM32_XSPI_RESET_GPIO
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;

	/* Generate RESETn pulse for the flash memory */
	gpio_pin_configure_dt(&dev_cfg->reset, GPIO_OUTPUT_ACTIVE);
	k_msleep(dev_cfg->reset_gpios_duration);
	gpio_pin_set_dt(&dev_cfg->reset, 0);
#else

	/* Reset command sent successively for each mode SPI/OPS & STR/DTR */
	XSPI_RegularCmdTypeDef s_command = {
		.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG,
		.AddressMode = HAL_XSPI_ADDRESS_NONE,
		.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE,
		.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE,
		.Instruction = SPI_NOR_CMD_RESET_EN,
		.InstructionWidth = HAL_XSPI_INSTRUCTION_8_BITS,
		.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE,
		.DataLength = HAL_XSPI_DATA_NONE,
		.DummyCycles = 0U,
		.DQSMode = HAL_XSPI_DQS_DISABLE,
#ifdef XSPI_CCR_SIOO
		.SIOOMode = HAL_XSPI_SIOO_INST_EVERY_CMD,
#endif /* XSPI_CCR_SIOO */
	};

	/* Reset enable in SPI mode and STR transfer mode */
	if (HAL_XSPI_Command(&dev_data->hxspi,
		&s_command, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("XSPI 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_XSPI_Command(&dev_data->hxspi,
		&s_command, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("XSPI reset memory (SPI/STR) failed");
		return -EIO;
	}

	/* Reset enable in OPI mode and STR transfer mode */
	s_command.InstructionMode    = HAL_XSPI_INSTRUCTION_8_LINES;
	s_command.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_DISABLE;
	s_command.Instruction = SPI_NOR_OCMD_RESET_EN;
	s_command.InstructionWidth = HAL_XSPI_INSTRUCTION_16_BITS;
	if (HAL_XSPI_Command(&dev_data->hxspi,
		&s_command, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("XSPI reset enable (OCTO/STR) failed");
		return -EIO;
	}

	/* Reset memory in OPI mode and STR transfer mode */
	s_command.Instruction = SPI_NOR_OCMD_RESET_MEM;
	if (HAL_XSPI_Command(&dev_data->hxspi,
		&s_command, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("XSPI reset memory (OCTO/STR) failed");
		return -EIO;
	}

	/* Reset enable in OPI mode and DTR transfer mode */
	s_command.InstructionDTRMode = HAL_XSPI_INSTRUCTION_DTR_ENABLE;
	s_command.Instruction = SPI_NOR_OCMD_RESET_EN;
	if (HAL_XSPI_Command(&dev_data->hxspi,
		&s_command, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("XSPI reset enable (OCTO/DTR) failed");
		return -EIO;
	}

	/* Reset memory in OPI mode and DTR transfer mode */
	s_command.Instruction = SPI_NOR_OCMD_RESET_MEM;
	if (HAL_XSPI_Command(&dev_data->hxspi,
		&s_command, HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("XSPI reset memory (OCTO/DTR) failed");
		return -EIO;
	}

#endif /* STM32_XSPI_RESET_GPIO */
	/* Wait after SWreset CMD, in case SWReset occurred during erase operation */
	k_busy_wait(STM32_XSPI_RESET_MAX_TIME * USEC_PER_MSEC);

	return 0;
}

#ifdef CONFIG_STM32_MEMMAP
/* Function to configure the octoflash in MemoryMapped mode */
static int stm32_xspi_set_memorymap(const struct device *dev)
{
	HAL_StatusTypeDef ret;
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;
	struct flash_stm32_xspi_data *dev_data = dev->data;
	XSPI_RegularCmdTypeDef s_command = {0}; /* Non-zero values disturb the command */
	XSPI_MemoryMappedTypeDef s_MemMappedCfg = {0};

	/* Configure octoflash in MemoryMapped mode */
	if ((dev_cfg->data_mode == XSPI_SPI_MODE) &&
		(stm32_xspi_hal_address_size(dev) == HAL_XSPI_ADDRESS_24_BITS)) {
		/* OPI mode and 3-bytes address size not supported by memory */
		LOG_ERR("XSPI_SPI_MODE in 3Bytes addressing is not supported");
		return -EIO;
	}

	/* Initialize the read command */
	s_command.OperationType = HAL_XSPI_OPTYPE_READ_CFG;
	s_command.InstructionMode = (dev_cfg->data_rate == XSPI_STR_TRANSFER)
				? ((dev_cfg->data_mode == XSPI_SPI_MODE)
					? HAL_XSPI_INSTRUCTION_1_LINE
					: HAL_XSPI_INSTRUCTION_8_LINES)
				: HAL_XSPI_INSTRUCTION_8_LINES;
	s_command.InstructionDTRMode = (dev_cfg->data_rate == XSPI_STR_TRANSFER)
				? HAL_XSPI_INSTRUCTION_DTR_DISABLE
				: HAL_XSPI_INSTRUCTION_DTR_ENABLE;
	s_command.InstructionWidth = (dev_cfg->data_rate == XSPI_STR_TRANSFER)
				? ((dev_cfg->data_mode == XSPI_SPI_MODE)
					? HAL_XSPI_INSTRUCTION_8_BITS
					: HAL_XSPI_INSTRUCTION_16_BITS)
				: HAL_XSPI_INSTRUCTION_16_BITS;
	s_command.Instruction = (dev_cfg->data_rate == XSPI_STR_TRANSFER)
				? ((dev_cfg->data_mode == XSPI_SPI_MODE)
					? ((stm32_xspi_hal_address_size(dev) ==
					HAL_XSPI_ADDRESS_24_BITS)
						? SPI_NOR_CMD_READ_FAST
						: SPI_NOR_CMD_READ_FAST_4B)
					: dev_data->read_opcode)
				: SPI_NOR_OCMD_DTR_RD;
	s_command.AddressMode = (dev_cfg->data_rate == XSPI_STR_TRANSFER)
				? ((dev_cfg->data_mode == XSPI_SPI_MODE)
					? HAL_XSPI_ADDRESS_1_LINE
					: HAL_XSPI_ADDRESS_8_LINES)
				: HAL_XSPI_ADDRESS_8_LINES;
	s_command.AddressDTRMode = (dev_cfg->data_rate == XSPI_STR_TRANSFER)
				? HAL_XSPI_ADDRESS_DTR_DISABLE
				: HAL_XSPI_ADDRESS_DTR_ENABLE;
	s_command.AddressWidth = (dev_cfg->data_rate == XSPI_STR_TRANSFER)
				? stm32_xspi_hal_address_size(dev)
				: HAL_XSPI_ADDRESS_32_BITS;
	s_command.DataMode = (dev_cfg->data_rate == XSPI_STR_TRANSFER)
				? ((dev_cfg->data_mode == XSPI_SPI_MODE)
					? HAL_XSPI_DATA_1_LINE
					: HAL_XSPI_DATA_8_LINES)
				: HAL_XSPI_DATA_8_LINES;
	s_command.DataDTRMode = (dev_cfg->data_rate == XSPI_STR_TRANSFER)
				? HAL_XSPI_DATA_DTR_DISABLE
				: HAL_XSPI_DATA_DTR_ENABLE;
	s_command.DummyCycles = (dev_cfg->data_rate == XSPI_STR_TRANSFER)
				? ((dev_cfg->data_mode == XSPI_SPI_MODE)
					? SPI_NOR_DUMMY_RD
					: SPI_NOR_DUMMY_RD_OCTAL)
				: SPI_NOR_DUMMY_RD_OCTAL_DTR;
	s_command.DQSMode = (dev_cfg->data_rate == XSPI_STR_TRANSFER)
				? HAL_XSPI_DQS_DISABLE
				: HAL_XSPI_DQS_ENABLE;
#ifdef XSPI_CCR_SIOO
	s_command.SIOOMode = HAL_XSPI_SIOO_INST_EVERY_CMD;
#endif /* XSPI_CCR_SIOO */

	ret = HAL_XSPI_Command(&dev_data->hxspi, &s_command, HAL_XSPI_TIMEOUT_DEFAULT_VALUE);
	if (ret != HAL_OK) {
		LOG_ERR("%d: Failed to set memory map", ret);
		return -EIO;
	}

	/* Initialize the program command */
	s_command.OperationType = HAL_XSPI_OPTYPE_WRITE_CFG;
	if (dev_cfg->data_rate == XSPI_STR_TRANSFER) {
		s_command.Instruction = (dev_cfg->data_mode == XSPI_SPI_MODE)
					? ((stm32_xspi_hal_address_size(dev) ==
					HAL_XSPI_ADDRESS_24_BITS)
						? SPI_NOR_CMD_PP
						: SPI_NOR_CMD_PP_4B)
					: SPI_NOR_OCMD_PAGE_PRG;
	} else {
		s_command.Instruction = SPI_NOR_OCMD_PAGE_PRG;
	}
	s_command.DQSMode = HAL_XSPI_DQS_DISABLE;

	ret = HAL_XSPI_Command(&dev_data->hxspi, &s_command, HAL_XSPI_TIMEOUT_DEFAULT_VALUE);
	if (ret != HAL_OK) {
		LOG_ERR("%d: Failed to set memory mapped", ret);
		return -EIO;
	}

	/* Enable the memory-mapping */
	s_MemMappedCfg.TimeOutActivation = HAL_XSPI_TIMEOUT_COUNTER_DISABLE;

#ifdef XSPI_CR_NOPREF
	s_MemMappedCfg.NoPrefetchData = HAL_XSPI_AUTOMATIC_PREFETCH_ENABLE;
#ifdef XSPI_CR_NOPREF_AXI
	s_MemMappedCfg.NoPrefetchAXI = HAL_XSPI_AXI_PREFETCH_DISABLE;
#endif /* XSPI_CR_NOPREF_AXI */
#endif /* XSPI_CR_NOPREF */

	ret = HAL_XSPI_MemoryMapped(&dev_data->hxspi, &s_MemMappedCfg);
	if (ret != HAL_OK) {
		LOG_ERR("%d: Failed to enable memory mapped", ret);
		return -EIO;
	}

	LOG_DBG("MemoryMap mode enabled");
	return 0;
}
#endif /* CONFIG_STM32_MEMMAP */

static int stm32_xspi_abort(const struct device *dev)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;

	if (HAL_XSPI_Abort(&dev_data->hxspi) != HAL_OK) {
		LOG_ERR("XSPI abort failed");
		return -EIO;
	}

	return 0;
}

/* Function to return true if the octoflash is in MemoryMapped else false */
static bool stm32_xspi_is_memorymap(const struct device *dev)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;

#if defined(CONFIG_STM32_HAL2)
	return stm32_reg_read_bits(&stm32_xspi_hal2_instance(&dev_data->hxspi)->CR,
				   XSPI_CR_FMODE) == XSPI_CR_FMODE;
#else
	return stm32_reg_read_bits(&dev_data->hxspi.Instance->CR, XSPI_CR_FMODE) == XSPI_CR_FMODE;
#endif /* CONFIG_STM32_HAL2 */
}

/*
 * Function to erase the flash : chip or sector with possible OCTO/SPI and STR/DTR
 * to erase the complete chip (using dedicated command) :
 *   set size >= flash size
 *   set addr = 0
 */
static int flash_stm32_xspi_erase(const struct device *dev, off_t addr,
				  size_t size)
{
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;
	struct flash_stm32_xspi_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) {
		size = dev_cfg->flash_size;
	}

	if (!xspi_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) != 0) && (size < dev_cfg->flash_size)) {
		LOG_ERR("Error: wrong sector size 0x%x", size);
		return -ENOTSUP;
	}

	xspi_lock_thread(dev);

#ifdef CONFIG_STM32_MEMMAP
	if (stm32_xspi_is_memorymap(dev)) {
		/* Abort ongoing transfer to force CS high/BUSY deasserted */
		ret = stm32_xspi_abort(dev);
		if (ret != 0) {
			LOG_ERR("Failed to abort memory-mapped access before erase");
			goto erase_end;
		}
	}
#endif /* CONFIG_STM32_MEMMAP */

	XSPI_RegularCmdTypeDef cmd_erase = {
		.OperationType = HAL_XSPI_OPTYPE_COMMON_CFG,
		.AlternateBytesMode = HAL_XSPI_ALT_BYTES_NONE,
		.DataMode = HAL_XSPI_DATA_NONE,
		.DummyCycles = 0U,
		.DQSMode = HAL_XSPI_DQS_DISABLE,
#ifdef XSPI_CCR_SIOO
		.SIOOMode = HAL_XSPI_SIOO_INST_EVERY_CMD,
#endif /* XSPI_CCR_SIOO */
	};

	if (stm32_xspi_mem_ready(dev,
		dev_cfg->data_mode, dev_cfg->data_rate) != 0) {
		LOG_ERR("Erase failed : flash busy");
		goto erase_end;
	}

	cmd_erase.InstructionMode    = (dev_cfg->data_mode == XSPI_OCTO_MODE)
					? HAL_XSPI_INSTRUCTION_8_LINES
					: HAL_XSPI_INSTRUCTION_1_LINE;
	cmd_erase.InstructionDTRMode = (dev_cfg->data_rate == XSPI_DTR_TRANSFER)
					? HAL_XSPI_INSTRUCTION_DTR_ENABLE
					: HAL_XSPI_INSTRUCTION_DTR_DISABLE;
	cmd_erase.InstructionWidth    = (dev_cfg->data_mode == XSPI_OCTO_MODE)
					? HAL_XSPI_INSTRUCTION_16_BITS
					: HAL_XSPI_INSTRUCTION_8_BITS;

	while ((size > 0) && (ret == 0)) {

		ret = stm32_xspi_write_enable(dev,
			dev_cfg->data_mode, dev_cfg->data_rate);
		if (ret != 0) {
			LOG_ERR("Erase failed : write enable");
			break;
		}

		if (size == dev_cfg->flash_size) {
			/* Chip erase */
			LOG_DBG("Chip Erase");

			cmd_erase.Address = 0;
			cmd_erase.Instruction = (dev_cfg->data_mode == XSPI_OCTO_MODE)
					? SPI_NOR_OCMD_BULKE
					: SPI_NOR_CMD_BULKE;
			cmd_erase.AddressMode = HAL_XSPI_ADDRESS_NONE;
			/* Full chip erase (Bulk) command */
			xspi_send_cmd(dev, &cmd_erase);

			size -= dev_cfg->flash_size;
			/* Chip (Bulk) erase started, wait until WEL becomes 0 */
			ret = stm32_xspi_mem_erased(dev);
			if (ret != 0) {
				LOG_ERR("Chip Erase failed");
				break;
			}
		} else {
			/* Sector or Block erase depending on the size */
			LOG_DBG("Sector/Block Erase");

			cmd_erase.AddressMode =
				(dev_cfg->data_mode == XSPI_OCTO_MODE)
				? HAL_XSPI_ADDRESS_8_LINES
				: HAL_XSPI_ADDRESS_1_LINE;
			cmd_erase.AddressDTRMode =
				(dev_cfg->data_rate == XSPI_DTR_TRANSFER)
				? HAL_XSPI_ADDRESS_DTR_ENABLE
				: HAL_XSPI_ADDRESS_DTR_DISABLE;
			cmd_erase.AddressWidth = stm32_xspi_hal_address_size(dev);
			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)
				    && (size >= BIT(etp->exp))
				    && ((bet == NULL)
					|| (etp->exp > bet->exp))) {
					bet = etp;
					cmd_erase.Instruction = bet->cmd;
				} else if (bet == NULL) {
					/* Use the default sector erase cmd */
					if (dev_cfg->data_mode == XSPI_OCTO_MODE) {
						cmd_erase.Instruction = SPI_NOR_OCMD_SE;
					} else {
						cmd_erase.Instruction =
							(stm32_xspi_hal_address_size(dev) ==
							HAL_XSPI_ADDRESS_32_BITS)
							? SPI_NOR_CMD_SE_4B
							: SPI_NOR_CMD_SE;
					}
				}
				/* Avoid using wrong erase type,
				 * if zero entries are found in erase_types
				 */
				bet = NULL;
			}
			LOG_DBG("Sector/Block Erase addr 0x%x, asize 0x%x amode 0x%x  instr 0x%x",
				cmd_erase.Address, cmd_erase.AddressWidth,
				cmd_erase.AddressMode, cmd_erase.Instruction);

			xspi_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_xspi_mem_ready(dev, dev_cfg->data_mode,
						dev_cfg->data_rate);
		}

	}
	/* Ends the erase operation */

erase_end:
	xspi_unlock_thread(dev);

	return ret;
}

/* Function to read the flash with possible OCTO/SPI and STR/DTR */
static int flash_stm32_xspi_read(const struct device *dev, off_t addr,
				 void *data, size_t size)
{
	__maybe_unused const struct flash_stm32_xspi_config *dev_cfg = dev->config;
	__maybe_unused struct flash_stm32_xspi_data *dev_data = dev->data;
	int ret = 0;

	if (!xspi_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;
	}

#if defined(CONFIG_STM32_MEMMAP) || (defined(CONFIG_STM32_APP_IN_EXT_FLASH) && defined(CONFIG_XIP))
	/*
	 * When the call is made by an app executing in external flash,
	 * skip the memory-mapped mode check
	 */
#ifdef CONFIG_STM32_MEMMAP

	/* Do reads through memory-mapping instead of indirect */
	if (!stm32_xspi_is_memorymap(dev)) {
		xspi_lock_thread(dev);
		ret = stm32_xspi_set_memorymap(dev);
		xspi_unlock_thread(dev);

		if (ret != 0) {
			LOG_ERR("READ: failed to set memory mapped");
			return ret;
		}
	}

	__ASSERT_NO_MSG(stm32_xspi_is_memorymap(dev));
#endif /* CONFIG_STM32_MEMMAP */
	uintptr_t mmap_addr = dev_cfg->mem_map_based_address + addr;

	LOG_DBG("Memory-mapped read from 0x%08lx, len %zu", mmap_addr, size);
	memcpy(data, (void *)mmap_addr, size);
	return ret;

#else /* CONFIG_STM32_MEMMAP || (CONFIG_STM32_APP_IN_EXT_FLASH && CONFIG_XIP) */

	XSPI_RegularCmdTypeDef cmd = xspi_prepare_cmd(dev_cfg->data_mode, dev_cfg->data_rate);

	if (dev_cfg->data_mode != XSPI_OCTO_MODE) {
		switch (dev_data->read_mode) {
		case JESD216_MODE_112: {
			cmd.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
			cmd.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
			cmd.DataMode = HAL_XSPI_DATA_2_LINES;
			break;
		}
		case JESD216_MODE_122: {
			cmd.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
			cmd.AddressMode = HAL_XSPI_ADDRESS_2_LINES;
			cmd.DataMode = HAL_XSPI_DATA_2_LINES;
			break;
		}
		case JESD216_MODE_114: {
			cmd.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
			cmd.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
			cmd.DataMode = HAL_XSPI_DATA_4_LINES;
			break;
		}
		case JESD216_MODE_144: {
			cmd.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
			cmd.AddressMode = HAL_XSPI_ADDRESS_4_LINES;
			cmd.DataMode = HAL_XSPI_DATA_4_LINES;
			break;
		}
		default:
			/* use the mode from ospi_prepare_cmd */
			break;
		}
	}

	/* Instruction and DummyCycles are set below */
	cmd.Address = addr; /* AddressSize is 32bits in OPSI mode */
	cmd.AddressWidth = stm32_xspi_hal_address_size(dev);
	/* DataSize is set by the read cmd */

	/* Configure other parameters */
	if (dev_cfg->data_rate == XSPI_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 == XSPI_OCTO_MODE) {
			/* OPI and STR */
			cmd.Instruction = SPI_NOR_OCMD_RD;
			cmd.DummyCycles = SPI_NOR_DUMMY_RD_OCTAL;
		} else {
			/* use SFDP:BFP read instruction */
			cmd.Instruction = dev_data->read_opcode;
			cmd.DummyCycles = dev_data->read_dummy;
			/* in SPI and STR : expecting SPI_NOR_CMD_READ_FAST_4B */
		}
	}

	LOG_DBG("XSPI: read %zu data at 0x%lx",
		size,
		(long)(dev_cfg->mem_map_based_address + addr));
	xspi_lock_thread(dev);

	ret = xspi_read_access(dev, &cmd, data, size);
	xspi_unlock_thread(dev);

	return ret;
#endif /* CONFIG_STM32_MEMMAP || (CONFIG_STM32_APP_IN_EXT_FLASH && CONFIG_XIP) */
}

/* Function to write the flash (page program) : with possible OCTO/SPI and STR/DTR */
static int flash_stm32_xspi_write(const struct device *dev, off_t addr,
				  const void *data, size_t size)
{
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;
	struct flash_stm32_xspi_data *dev_data = dev->data;
	size_t to_write;
	int ret = 0;

	if (!xspi_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;
	}

	xspi_lock_thread(dev);

#ifdef CONFIG_STM32_MEMMAP
	if (stm32_xspi_is_memorymap(dev)) {
		/* Abort ongoing transfer to force CS high/BUSY deasserted */
		ret = stm32_xspi_abort(dev);
		if (ret != 0) {
			LOG_ERR("Failed to abort memory-mapped access before write");
			goto write_end;
		}
	}
#endif
	/* page program for STR or DTR mode */
	XSPI_RegularCmdTypeDef cmd_pp = xspi_prepare_cmd(dev_cfg->data_mode, dev_cfg->data_rate);

	/* using 32bits address also in SPI/STR mode */
	cmd_pp.Instruction = dev_data->write_opcode;

	if (dev_cfg->data_mode != XSPI_OCTO_MODE) {
		switch (cmd_pp.Instruction) {
		case SPI_NOR_CMD_PP_4B:
			__fallthrough;
		case SPI_NOR_CMD_PP: {
			cmd_pp.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
			cmd_pp.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
			cmd_pp.DataMode = HAL_XSPI_DATA_1_LINE;
			break;
		}
		case SPI_NOR_CMD_PP_1_1_4_4B:
			__fallthrough;
		case SPI_NOR_CMD_PP_1_1_4: {
			cmd_pp.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
			cmd_pp.AddressMode = HAL_XSPI_ADDRESS_1_LINE;
			cmd_pp.DataMode = HAL_XSPI_DATA_4_LINES;
			break;
		}
		case SPI_NOR_CMD_PP_1_4_4_4B:
			__fallthrough;
		case SPI_NOR_CMD_PP_1_4_4: {
#if defined(CONFIG_USE_MICROCHIP_QSPI_FLASH_WITH_STM32)
			/* Microchip QSPI flash uses PP_1_1_4 opcode for the PP_1_4_4 operation */
			cmd_pp.Instruction = SPI_NOR_CMD_PP_1_1_4;
#endif /* CONFIG_USE_MICROCHIP_QSPI_FLASH_WITH_STM32 */
			cmd_pp.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE;
			cmd_pp.AddressMode = HAL_XSPI_ADDRESS_4_LINES;
			cmd_pp.DataMode = HAL_XSPI_DATA_4_LINES;
			break;
		}
		default:
			/* use the mode from ospi_prepare_cmd */
			break;
		}
	}

	cmd_pp.Address = addr;
	cmd_pp.AddressWidth = stm32_xspi_hal_address_size(dev);
	cmd_pp.DummyCycles = 0U;

	LOG_DBG("XSPI: write %zu data at 0x%lx",
		size,
		(long)(dev_cfg->mem_map_based_address + addr));

	ret = stm32_xspi_mem_ready(dev,
				   dev_cfg->data_mode, dev_cfg->data_rate);
	if (ret != 0) {
		LOG_ERR("XSPI: write not ready");
		goto write_end;
	}

	while ((size > 0) && (ret == 0)) {
		to_write = size;
		ret = stm32_xspi_write_enable(dev,
					      dev_cfg->data_mode, dev_cfg->data_rate);
		if (ret != 0) {
			LOG_ERR("XSPI: write not enabled");
			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.Address = addr;

		ret = xspi_write_access(dev, &cmd_pp, data, to_write);
		if (ret != 0) {
			LOG_ERR("XSPI: write not access");
			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_xspi_mem_ready(dev,
						 dev_cfg->data_mode, dev_cfg->data_rate);
		if (ret != 0) {
			LOG_ERR("XSPI: write PP not ready");
			break;
		}
	}
	/* Ends the write operation */

write_end:
	xspi_unlock_thread(dev);

	return ret;
}

static const struct flash_parameters flash_stm32_xspi_parameters = {
	.write_block_size = 1,
	.erase_value = 0xff
};

static const struct flash_parameters *
flash_stm32_xspi_get_parameters(const struct device *dev)
{
	ARG_UNUSED(dev);

	return &flash_stm32_xspi_parameters;
}

static void flash_stm32_xspi_isr(const struct device *dev)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;

	HAL_XSPI_IRQHandler(&dev_data->hxspi);
}

#if !defined(CONFIG_SOC_SERIES_STM32H7X) && !defined(CONFIG_STM32_HAL2)
/* 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;
}
#endif /* !CONFIG_SOC_SERIES_STM32H7X */

#if defined(CONFIG_STM32_HAL2)
/* weak functions required by the HAL2 XSPI module when DMA support is compiled in */
__weak hal_status_t HAL_DMA_Abort_IT(hal_dma_handle_t *hdma)
{
	ARG_UNUSED(hdma);

	return HAL_OK;
}

__weak hal_status_t HAL_DMA_Abort(hal_dma_handle_t *hdma)
{
	ARG_UNUSED(hdma);

	return HAL_OK;
}
#endif /* CONFIG_STM32_HAL2 */

/* This function is executed in the interrupt context */
#ifdef CONFIG_FLASH_STM32_XSPI_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.
 */
void HAL_XSPI_ErrorCallback(XSPI_HandleTypeDef *hxspi)
{
	struct flash_stm32_xspi_data *dev_data =
		CONTAINER_OF(hxspi, struct flash_stm32_xspi_data, hxspi);

	LOG_DBG("Error cb");

	dev_data->cmd_status = -EIO;

	k_sem_give(&dev_data->sync);
}

/*
 * Command completed callback.
 */
void HAL_XSPI_CmdCpltCallback(XSPI_HandleTypeDef *hxspi)
{
	struct flash_stm32_xspi_data *dev_data =
		CONTAINER_OF(hxspi, struct flash_stm32_xspi_data, hxspi);

	LOG_DBG("Cmd Cplt cb");

	k_sem_give(&dev_data->sync);
}

/*
 * Rx Transfer completed callback.
 */
void HAL_XSPI_RxCpltCallback(XSPI_HandleTypeDef *hxspi)
{
	struct flash_stm32_xspi_data *dev_data =
		CONTAINER_OF(hxspi, struct flash_stm32_xspi_data, hxspi);

	LOG_DBG("Rx Cplt cb");

	k_sem_give(&dev_data->sync);
}

/*
 * Tx Transfer completed callback.
 */
void HAL_XSPI_TxCpltCallback(XSPI_HandleTypeDef *hxspi)
{
	struct flash_stm32_xspi_data *dev_data =
		CONTAINER_OF(hxspi, struct flash_stm32_xspi_data, hxspi);

	LOG_DBG("Tx Cplt cb");

	k_sem_give(&dev_data->sync);
}

/*
 * Status Match callback.
 */
void HAL_XSPI_StatusMatchCallback(XSPI_HandleTypeDef *hxspi)
{
	struct flash_stm32_xspi_data *dev_data =
		CONTAINER_OF(hxspi, struct flash_stm32_xspi_data, hxspi);

	LOG_DBG("Status Match cb");

	k_sem_give(&dev_data->sync);
}

/*
 * Timeout callback.
 */
void HAL_XSPI_TimeOutCallback(XSPI_HandleTypeDef *hxspi)
{
	struct flash_stm32_xspi_data *dev_data =
		CONTAINER_OF(hxspi, struct flash_stm32_xspi_data, hxspi);

	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_xspi_pages_layout(const struct device *dev,
				const struct flash_pages_layout **layout,
				size_t *layout_size)
{
	struct flash_stm32_xspi_data *dev_data = dev->data;

	*layout = &dev_data->layout;
	*layout_size = 1;
}
#endif

static int flash_stm32_xspi_get_size(const struct device *dev, uint64_t *size)
{
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;

	*size = (uint64_t)dev_cfg->flash_size;

	return 0;
}

static DEVICE_API(flash, flash_stm32_xspi_driver_api) = {
	.read = flash_stm32_xspi_read,
	.write = flash_stm32_xspi_write,
	.erase = flash_stm32_xspi_erase,
	.get_parameters = flash_stm32_xspi_get_parameters,
	.get_size = flash_stm32_xspi_get_size,
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
	.page_layout = flash_stm32_xspi_pages_layout,
#endif
#if defined(CONFIG_FLASH_JESD216_API)
	.sfdp_read = xspi_read_sfdp,
	.read_jedec_id = xspi_read_jedec_id,
#endif /* CONFIG_FLASH_JESD216_API */
};

#if defined(CONFIG_FLASH_PAGE_LAYOUT)
static int setup_pages_layout(const struct device *dev)
{
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;
	struct flash_stm32_xspi_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 stm32_xspi_read_status_register(const struct device *dev, uint8_t reg_num, uint8_t *reg)
{
	XSPI_RegularCmdTypeDef s_command = {
		.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE,
		.DataMode = HAL_XSPI_DATA_1_LINE,
	};

	switch (reg_num) {
	case 1U:
		s_command.Instruction = SPI_NOR_CMD_RDSR;
		break;
	case 2U:
		s_command.Instruction = SPI_NOR_CMD_RDSR2;
		break;
	case 3U:
		s_command.Instruction = SPI_NOR_CMD_RDSR3;
		break;
	default:
		return -EINVAL;
	}

	return xspi_read_access(dev, &s_command, reg, sizeof(*reg));
}

static int stm32_xspi_write_status_register(const struct device *dev, uint8_t reg_num, uint8_t reg)
{
	struct flash_stm32_xspi_data *data = dev->data;
	XSPI_RegularCmdTypeDef s_command = {
		.Instruction = SPI_NOR_CMD_WRSR,
		.InstructionMode = HAL_XSPI_INSTRUCTION_1_LINE,
		.DataMode = HAL_XSPI_DATA_1_LINE
	};
	size_t size;
	uint8_t regs[4] = { 0 };
	uint8_t *regs_p;
	int ret;

	if (reg_num == 1U) {
		size = 1U;
		regs[0] = reg;
		regs_p = &regs[0];
		/* 1 byte write clears SR2, write SR2 as well */
		if (data->qer_type == JESD216_DW15_QER_S2B1v1) {
			ret = stm32_xspi_read_status_register(dev, 2, &regs[1]);
			if (ret < 0) {
				return ret;
			}
			size = 2U;
		}
	} else if (reg_num == 2U) {
		s_command.Instruction = SPI_NOR_CMD_WRSR2;
		size = 1U;
		regs[1] = reg;
		regs_p = &regs[1];
		/* if SR2 write needs SR1 */
		if ((data->qer_type == JESD216_DW15_QER_VAL_S2B1v1) ||
		    (data->qer_type == JESD216_DW15_QER_VAL_S2B1v4) ||
		    (data->qer_type == JESD216_DW15_QER_VAL_S2B1v5)) {
			ret = stm32_xspi_read_status_register(dev, 1, &regs[0]);
			if (ret < 0) {
				return ret;
			}
			s_command.Instruction = SPI_NOR_CMD_WRSR;
			size = 2U;
			regs_p = &regs[0];
		}
	} else if (reg_num == 3U) {
		s_command.Instruction = SPI_NOR_CMD_WRSR3;
		size = 1U;
		regs[2] = reg;
		regs_p = &regs[2];
	} else {
		return -EINVAL;
	}

	return xspi_write_access(dev, &s_command, regs_p, size);
}

static int stm32_xspi_enable_qe(const struct device *dev)
{
	struct flash_stm32_xspi_data *data = dev->data;
	uint8_t qe_reg_num;
	uint8_t qe_bit;
	uint8_t reg;
	int ret;

	switch (data->qer_type) {
	case JESD216_DW15_QER_NONE:
		/* no QE bit, device detects reads based on opcode */
		return 0;
	case JESD216_DW15_QER_S1B6:
		qe_reg_num = 1U;
		qe_bit = BIT(6U);
		break;
	case JESD216_DW15_QER_S2B7:
		qe_reg_num = 2U;
		qe_bit = BIT(7U);
		break;
	case JESD216_DW15_QER_S2B1v1:
		__fallthrough;
	case JESD216_DW15_QER_S2B1v4:
		__fallthrough;
	case JESD216_DW15_QER_S2B1v5:
		__fallthrough;
	case JESD216_DW15_QER_S2B1v6:
		qe_reg_num = 2U;
		qe_bit = BIT(1U);
		break;
	default:
		return -ENOTSUP;
	}

	ret = stm32_xspi_read_status_register(dev, qe_reg_num, &reg);
	if (ret < 0) {
		return ret;
	}

	/* exit early if QE bit is already set */
	if ((reg & qe_bit) != 0U) {
		return 0;
	}

	ret = stm32_xspi_write_enable(dev, XSPI_SPI_MODE, XSPI_STR_TRANSFER);
	if (ret < 0) {
		return ret;
	}

	reg |= qe_bit;

	ret = stm32_xspi_write_status_register(dev, qe_reg_num, reg);
	if (ret < 0) {
		return ret;
	}

	ret = stm32_xspi_mem_ready(dev, XSPI_SPI_MODE, XSPI_STR_TRANSFER);
	if (ret < 0) {
		return ret;
	}

	/* validate that QE bit is set */
	ret = stm32_xspi_read_status_register(dev, qe_reg_num, &reg);
	if (ret < 0) {
		return ret;
	}

	if ((reg & qe_bit) == 0U) {
		LOG_ERR("Status Register %u [0x%02x] not set", qe_reg_num, reg);
		ret = -EIO;
	}

	return ret;
}

static void spi_nor_process_bfp_addrbytes(const struct device *dev,
					  const uint8_t jesd216_bfp_addrbytes)
{
	struct flash_stm32_xspi_data *data = dev->data;

	if ((jesd216_bfp_addrbytes == JESD216_SFDP_BFP_DW1_ADDRBYTES_VAL_4B) ||
	    (jesd216_bfp_addrbytes == JESD216_SFDP_BFP_DW1_ADDRBYTES_VAL_3B4B)) {
		data->address_width = 4U;
	} else {
		data->address_width = 3U;
	}
}

static inline uint8_t spi_nor_convert_read_to_4b(const uint8_t opcode)
{
	switch (opcode) {
	case SPI_NOR_CMD_READ:
		return SPI_NOR_CMD_READ_4B;
	case SPI_NOR_CMD_DREAD:
		return SPI_NOR_CMD_DREAD_4B;
	case SPI_NOR_CMD_2READ:
		return SPI_NOR_CMD_2READ_4B;
	case SPI_NOR_CMD_QREAD:
		return SPI_NOR_CMD_QREAD_4B;
	case SPI_NOR_CMD_4READ:
		return SPI_NOR_CMD_4READ_4B;
	default:
		/* use provided */
		return opcode;
	}
}

static inline uint8_t spi_nor_convert_write_to_4b(const uint8_t opcode)
{
	switch (opcode) {
	case SPI_NOR_CMD_PP:
		return SPI_NOR_CMD_PP_4B;
	case SPI_NOR_CMD_PP_1_1_4:
		return SPI_NOR_CMD_PP_1_1_4_4B;
	case SPI_NOR_CMD_PP_1_4_4:
		return SPI_NOR_CMD_PP_1_4_4_4B;
	default:
		/* use provided */
		return opcode;
	}
}

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_xspi_config *dev_cfg = dev->config;
	struct flash_stm32_xspi_data *data = dev->data;
	/* must be kept in data mode order, ignore 1-1-1 (always supported) */
	const enum jesd216_mode_type supported_read_modes[] = { JESD216_MODE_112, JESD216_MODE_122,
								JESD216_MODE_114,
								JESD216_MODE_144 };
	size_t supported_read_modes_max_idx;
	struct jesd216_erase_type *etp = data->erase_types;
	size_t idx;
	const size_t flash_size = jesd216_bfp_density(bfp) / 8U;
	struct jesd216_instr read_instr = { 0 };
	struct jesd216_bfp_dw15 dw15;

	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 (idx = 1U; idx <= ARRAY_SIZE(data->erase_types); ++idx) {
		if (jesd216_bfp_erase(bfp, idx, etp) == 0) {
			LOG_DBG("Erase %u with %02x",
					(uint32_t)BIT(etp->exp), etp->cmd);
		}
		++etp;
	}

	spi_nor_process_bfp_addrbytes(dev, jesd216_bfp_addrbytes(bfp));
	LOG_DBG("Address width: %u Bytes", data->address_width);

	/* use PP opcode based on configured data mode if nothing is set in DTS */
	if (data->write_opcode == SPI_NOR_WRITEOC_NONE) {
		switch (dev_cfg->data_mode) {
		case XSPI_OCTO_MODE:
			data->write_opcode = SPI_NOR_OCMD_PAGE_PRG;
			break;
		case XSPI_QUAD_MODE:
			data->write_opcode = SPI_NOR_CMD_PP_1_4_4;
			break;
		case XSPI_DUAL_MODE:
			data->write_opcode = SPI_NOR_CMD_PP_1_1_2;
			break;
		default:
			data->write_opcode = SPI_NOR_CMD_PP;
			break;
		}
	}

	if (dev_cfg->data_mode != XSPI_OCTO_MODE) {
		/* determine supported read modes, begin from the slowest */
		data->read_mode = JESD216_MODE_111;
		data->read_opcode = SPI_NOR_CMD_READ;
		data->read_dummy = 0U;

		if (dev_cfg->data_mode != XSPI_SPI_MODE) {
			if (dev_cfg->data_mode == XSPI_DUAL_MODE) {
				/* the index of JESD216_MODE_114 in supported_read_modes */
				supported_read_modes_max_idx = 2U;
			} else {
				supported_read_modes_max_idx = ARRAY_SIZE(supported_read_modes);
			}

			for (idx = 0U; idx < supported_read_modes_max_idx; ++idx) {
				if (jesd216_bfp_read_support(php, bfp, supported_read_modes[idx],
							     &read_instr) < 0) {
					/* not supported */
					continue;
				}

				LOG_DBG("Supports read mode: %d, instr: 0x%X",
					supported_read_modes[idx], read_instr.instr);
				data->read_mode = supported_read_modes[idx];
				data->read_opcode = read_instr.instr;
				data->read_dummy =
					(read_instr.wait_states + read_instr.mode_clocks);
			}
		}

		/* convert 3-Byte opcodes to 4-Byte (if required) */
		if (dev_cfg->four_byte_opcodes) {
			if (data->address_width != 4U) {
				LOG_DBG("4-Byte opcodes require 4-Byte address width");
				return -ENOTSUP;
			}
			data->read_opcode = spi_nor_convert_read_to_4b(data->read_opcode);
			data->write_opcode = spi_nor_convert_write_to_4b(data->write_opcode);
		}

		/* enable quad mode (if required) */
		if (dev_cfg->data_mode == XSPI_QUAD_MODE) {
			if (jesd216_bfp_decode_dw15(php, bfp, &dw15) < 0) {
				/* will use QER from DTS or default (refer to device data) */
				LOG_WRN("Unable to decode QE requirement [DW15]");
			} else {
				/* bypass DTS QER value */
				data->qer_type = dw15.qer;
			}

			LOG_DBG("QE requirement mode: %x", data->qer_type);

			if (stm32_xspi_enable_qe(dev) < 0) {
				LOG_ERR("Failed to enable QUAD mode");
				return -EIO;
			}

			LOG_DBG("QUAD mode enabled");
		}
	}

	data->page_size = jesd216_bfp_page_size(php, bfp);

	LOG_DBG("Page size %u bytes", data->page_size);
	LOG_DBG("Flash size %zu bytes", flash_size);
	LOG_DBG("Using read mode: %d, instr: 0x%X, dummy cycles: %u",
		data->read_mode, data->read_opcode, data->read_dummy);
	LOG_DBG("Using write instr: 0x%X", data->write_opcode);

	return 0;
}

#ifdef CONFIG_FLASH_STM32_XSPI_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;
	ret = dma_config(dma_stream->dev, dma_stream->channel, &dma_stream->cfg);
	if (ret != 0) {
		LOG_ERR("Failed to configure DMA channel %d", dma_stream->channel);
		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;
	}

#if defined(CONFIG_SOC_SERIES_STM32H7RSX)
	/*
	 * Assume the DMA is HPDMA because GPDMA does not have request line from XSPI.
	 * Allocate source/destination port based on transfer direction:
	 *  - XSPI is only accessible by HPDMA port 1
	 *  - SRAM is only accessible by HPDMA port 0
	 */
	if (table_direction[dma_stream->cfg.channel_direction] == DMA_PERIPH_TO_MEMORY) {
		hdma->Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT1 |
			DMA_DEST_ALLOCATED_PORT0;
	} else if (table_direction[dma_stream->cfg.channel_direction] == DMA_MEMORY_TO_PERIPH) {
		hdma->Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 |
			DMA_DEST_ALLOCATED_PORT1;
	} else {
		LOG_ERR("DMA direction %d is not valid",
			table_direction[dma_stream->cfg.channel_direction]);
		return -EINVAL;
	}
#else /* CONFIG_SOC_SERIES_STM32H7RSX */
	hdma->Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 |
		DMA_DEST_ALLOCATED_PORT0;
#endif /* CONFIG_SOC_SERIES_STM32H7RSX */
	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.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 /* CONFIG_FLASH_STM32_XSPI_DMA */


static int flash_stm32_xspi_init(const struct device *dev)
{
	const struct flash_stm32_xspi_config *dev_cfg = dev->config;
	struct flash_stm32_xspi_data *dev_data = dev->data;
	uint32_t ahb_clock_freq;
	uint32_t prescaler = STM32_XSPI_CLOCK_PRESCALER_MIN;
	int ret;

#if defined(CONFIG_STM32_APP_IN_EXT_FLASH) && defined(CONFIG_XIP)
	/* If MemoryMapped then configure skip init
	 * Check clock status first as reading CR register without bus clock doesn't work on N6
	 * If clock is off, then MemoryMapped is off too and we do init
	 */
	if (clock_control_get_status(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE),
				     (clock_control_subsys_t) &dev_cfg->pclken)
				     == CLOCK_CONTROL_STATUS_ON) {
		if (stm32_xspi_is_memorymap(dev)) {
			LOG_DBG("NOR init'd in MemMapped mode");
			/* Force HAL instance in correct state */
#if defined(CONFIG_STM32_HAL2)
			dev_data->hxspi.global_state = HAL_XSPI_STATE_MEMORY_MAPPED_ACTIVE;
#else
			dev_data->hxspi.State = HAL_XSPI_STATE_BUSY_MEM_MAPPED;
#endif /* CONFIG_STM32_HAL2 */
			return 0;
		}
	}
#endif /* CONFIG_STM32_APP_IN_EXT_FLASH && CONFIG_XIP */

	/* The SPI/DTR is not a valid config of data_mode/data_rate according to the DTS */
	if ((dev_cfg->data_mode != XSPI_OCTO_MODE)
		&& (dev_cfg->data_rate == XSPI_DTR_TRANSFER)) {
		/* already the right config, continue */
		LOG_ERR("XSPI mode SPI|DUAL|QUAD/DTR is not valid");
		return -ENOTSUP;
	}

	/* Signals configuration */
	ret = pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT);
	if (ret < 0) {
		LOG_ERR("XSPI pinctrl setup failed (%d)", ret);
		return ret;
	}

	/* 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 XSPI 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 call clock_control_get_rate(pclken)");
		return -EIO;
	}

	if (dev_cfg->has_pclken_ker) {
		/* Kernel clock config for peripheral if any */
		if (clock_control_configure(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE),
			(clock_control_subsys_t) &dev_cfg->pclken_ker, NULL) != 0) {
			LOG_ERR("Could not select XSPI domain clock");
			return -EIO;
		}

		if (clock_control_get_rate(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE),
			(clock_control_subsys_t) &dev_cfg->pclken_ker,
								&ahb_clock_freq) < 0) {
			LOG_ERR("Failed call clock_control_get_rate(pclken_ker)");
			return -EIO;
		}
	}

	if (dev_cfg->has_pclken_mgr) {
		/* Clock domain corresponding to the IO-Mgr (XSPIM) */
		if (clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE),
				(clock_control_subsys_t) &dev_cfg->pclken_mgr) != 0) {
			LOG_ERR("Could not enable XSPI Manager clock");
			return -EIO;
		}
	}

	for (; prescaler <= STM32_XSPI_CLOCK_PRESCALER_MAX; prescaler++) {
		uint32_t clk = STM32_XSPI_CLOCK_COMPUTE(ahb_clock_freq, prescaler);

		if (clk <= dev_cfg->max_frequency) {
			break;
		}
	}

	if (prescaler > STM32_XSPI_CLOCK_PRESCALER_MAX) {
		LOG_ERR("XSPI could not find valid prescaler value");
		return -EINVAL;
	}

#if defined(CONFIG_STM32_HAL2)
	ret = stm32_xspi_hal2_init(dev, prescaler,
				   (dev_cfg->data_rate == XSPI_DTR_TRANSFER)
				   ? HAL_XSPI_DELAY_HOLD_QUARTCYCLE
				   : HAL_XSPI_DELAY_HOLD_NONE);
	if (ret != 0) {
		return ret;
	}
#else
	/* Initialize XSPI HAL structure completely */
	dev_data->hxspi.Init.ClockPrescaler = prescaler;
	/* The stm32 hal_xspi driver does not reduce DEVSIZE before writing the DCR1 */
	dev_data->hxspi.Init.MemorySize = find_lsb_set(dev_cfg->flash_size) - 2;
#if defined(XSPI_DCR2_WRAPSIZE)
	dev_data->hxspi.Init.WrapSize = HAL_XSPI_WRAP_NOT_SUPPORTED;
#endif /* XSPI_DCR2_WRAPSIZE */
	/* STR mode else Macronix for DTR mode */
	if (dev_cfg->data_rate == XSPI_DTR_TRANSFER) {
		dev_data->hxspi.Init.MemoryType = HAL_XSPI_MEMTYPE_MACRONIX;
		dev_data->hxspi.Init.DelayHoldQuarterCycle = HAL_XSPI_DHQC_ENABLE;
	}

	if (stm32_xspi_is_memorymap(dev)) {
		/* Memory-mapping could have been set by previous application.
		 * Force HAL instance in correct state.
		 */
		dev_data->hxspi.State = HAL_XSPI_STATE_BUSY_MEM_MAPPED;
	}

	if (HAL_XSPI_Init(&dev_data->hxspi) != HAL_OK) {
		LOG_ERR("XSPI Init failed");
		return -EIO;
	}
#endif /* CONFIG_STM32_HAL2 */

	LOG_DBG("XSPI Init'd");

#if !defined(CONFIG_STM32_HAL2) && \
	(defined(HAL_XSPIM_IOPORT_1) || defined(HAL_XSPIM_IOPORT_2)) && \
	!defined(CONFIG_STM32_XSPIM)
	/* XSPI I/O manager init Function */
	XSPIM_CfgTypeDef xspi_mgr_cfg;

	if (dev_data->hxspi.Instance == STM32_XSPI1) {
		xspi_mgr_cfg.IOPort = HAL_XSPIM_IOPORT_1;
	} else if (dev_data->hxspi.Instance == STM32_XSPI2) {
		xspi_mgr_cfg.IOPort = HAL_XSPIM_IOPORT_2;
	}
	xspi_mgr_cfg.nCSOverride = HAL_XSPI_CSSEL_OVR_DISABLED;
	xspi_mgr_cfg.Req2AckTime = 1;

	if (HAL_XSPIM_Config(&dev_data->hxspi, &xspi_mgr_cfg,
		HAL_XSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
		LOG_ERR("XSPI M config failed");
		return -EIO;
	}

#endif /* (HAL_XSPIM_IOPORT_1 || HAL_XSPIM_IOPORT_2) && !(xspim node) */

#if !defined(CONFIG_STM32_HAL2) && defined(XSPI_DCR1_DLYBYP)
	/* XSPI delay block init Function */
	HAL_XSPI_DLYB_CfgTypeDef xspi_delay_block_cfg = {0};

	(void)HAL_XSPI_DLYB_GetClockPeriod(&dev_data->hxspi, &xspi_delay_block_cfg);
	/*  with DTR, set the PhaseSel/4 (empiric value from stm32Cube) */
	xspi_delay_block_cfg.PhaseSel /= 4;

	if (HAL_XSPI_DLYB_SetConfig(&dev_data->hxspi, &xspi_delay_block_cfg) != HAL_OK) {
		LOG_ERR("XSPI DelayBlock failed");
		return -EIO;
	}

	LOG_DBG("Delay Block Init");
#endif /* XSPI_DCR1_DLYBYP */

#ifdef CONFIG_FLASH_STM32_XSPI_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);

	/* Run IRQ init */
	dev_cfg->irq_config(dev);

	if (stm32_xspi_is_memorymap(dev)) {
		/* Memory-mapping could have been set by previous application.
		 * Abort to allow following Jedec transactions, it will be
		 * re-enabled afterwards if needed by the application.
		 */
		ret = stm32_xspi_abort(dev);
		if (ret != 0) {
			LOG_ERR("Failed to abort memory-mapped access before Jedec ops");
			return ret;
		}
	}

	/* Reset NOR flash memory : still with the SPI/STR config for the NOR */
	if (stm32_xspi_mem_reset(dev) != 0) {
		LOG_ERR("XSPI reset failed");
		return -EIO;
	}

	LOG_DBG("Reset Mem (SPI/STR)");

	/* Check if memory is ready in the SPI/STR mode */
	if (stm32_xspi_mem_ready(dev,
		XSPI_SPI_MODE, XSPI_STR_TRANSFER) != 0) {
		LOG_ERR("XSPI memory not ready");
		return -EIO;
	}

	LOG_DBG("Mem Ready (SPI/STR)");

#if defined(CONFIG_FLASH_JESD216_API)
	/* Process with the RDID (jedec read ID) instruction at init and fill jedec_id Table */
	ret = stm32_xspi_read_jedec_id(dev);
	if (ret != 0) {
		LOG_ERR("Read ID failed: %d", ret);
		return ret;
	}
#endif /* CONFIG_FLASH_JESD216_API */

	if (stm32_xspi_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 = xspi_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[20];
				struct jesd216_bfp bfp;
			} u2;
			const struct jesd216_bfp *bfp = &u2.bfp;

			ret = xspi_read_sfdp(dev, jesd216_param_addr(php),
					     (uint8_t *)u2.dw,
					     MIN(sizeof(uint32_t) * php->len_dw, sizeof(u2.dw)));
			if (ret == 0) {
				ret = spi_nor_process_bfp(dev, php, bfp);
			}

			if (ret != 0) {
				LOG_ERR("SFDP BFP failed: %d", ret);
				break;
			}
		}
		if (id == JESD216_SFDP_PARAM_ID_4B_ADDR_INSTR) {

			if (dev_data->address_width == 4U) {
				/*
				 * Check table 4 byte address instruction table to get supported
				 * erase opcodes when running in 4 byte address mode
				 */
				union {
					uint32_t dw[2];
					struct {
						uint32_t dummy;
						uint8_t type[4];
					} types;
				} u2;
				ret = xspi_read_sfdp(dev, jesd216_param_addr(php),
					     (uint8_t *)u2.dw,
					     MIN(sizeof(uint32_t) * php->len_dw, sizeof(u2.dw)));
				if (ret != 0) {
					break;
				}
				for (uint8_t ei = 0; ei < JESD216_NUM_ERASE_TYPES; ++ei) {
					struct jesd216_erase_type *etp = &dev_data->erase_types[ei];
					const uint8_t cmd = u2.types.type[ei];
					/* 0xff means not supported */
					if (cmd == 0xff) {
						etp->exp = 0;
						etp->cmd = 0;
					} else {
						etp->cmd = cmd;
					};
				}
			}
		}
		++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 */

	if (dev_cfg->requires_ulbpr) {
		ret = xspi_write_unprotect(dev);
		if (ret != 0) {
			LOG_ERR("write unprotect failed: %d", ret);
			return -ENODEV;
		}
		LOG_DBG("Write Un-protected");
	}

#ifdef CONFIG_STM32_MEMMAP
	ret = stm32_xspi_set_memorymap(dev);
	if (ret != 0) {
		LOG_ERR("Failed to enable memory-mapped mode: %d", ret);
		return ret;
	}
	LOG_INF("Memory-mapped NOR-flash at 0x%lx (0x%x bytes)",
		(long)(dev_cfg->mem_map_based_address),
		dev_cfg->flash_size);
#else
	LOG_INF("NOR external-flash at 0x%lx (0x%x bytes)",
		(long)(dev_cfg->mem_map_based_address),
		dev_cfg->flash_size);
#endif /* CONFIG_STM32_MEMMAP*/
	return 0;
}


#ifdef CONFIG_FLASH_STM32_XSPI_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 DT_WRITEOC_PROP_OR(inst, default_value)							\
	COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, writeoc),					\
		    (_CONCAT(SPI_NOR_CMD_, DT_STRING_TOKEN(DT_DRV_INST(inst), writeoc))),	\
		    ((default_value)))

#define DT_QER_PROP_OR(inst, default_value)							\
	COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, quad_enable_requirements),			\
		    (_CONCAT(JESD216_DW15_QER_VAL_,						\
			     DT_STRING_TOKEN(DT_DRV_INST(inst), quad_enable_requirements))),	\
		    ((default_value)))

#if defined(HAL_XSPIM_IOPORT_1) || defined(HAL_XSPIM_IOPORT_2)
#define DT_XSPI_STM32_MEMORY_SELECT(inst)				\
		.MemorySelect = ((DT_INST_PROP(inst, ncs_line) == 1)	\
					? HAL_XSPI_CSSEL_NCS1		\
					: HAL_XSPI_CSSEL_NCS2),
#else
#define DT_XSPI_STM32_MEMORY_SELECT(inst)	 /* Not used */
#endif /* HAL_XSPIM_IOPORT_1 || HAL_XSPIM_IOPORT_2 */

#if defined(XSPI_DCR1_DLYBYP)
#define DT_XSPI_STM32_DELAY_BLOCK_BYPASS(inst)						\
		.DelayBlockBypass = (DT_PROP(STM32_XSPI_NODE(inst), dlyb_bypass)	\
					? HAL_XSPI_DELAY_BLOCK_BYPASS			\
					: HAL_XSPI_DELAY_BLOCK_ON),
#else
#define DT_XSPI_STM32_DELAY_BLOCK_BYPASS(inst)	/* Not used */
#endif /* XSPI_DCR1_DLYBYP */

#if defined(XSPI_DCR3_MAXTRAN)
#define XSPI_STM32_MAXTRAN	\
		.MaxTran = 0,
#else
#define XSPI_STM32_MAXTRAN	 /* Not used */
#endif /* XSPI_DCR3_MAXTRAN */

#if defined(XSPI_DCR4_REFRESH)
#define XSPI_STM32_REFRESH	\
		.Refresh = 0,	 /* Not used */
#else
#define XSPI_STM32_REFRESH
#endif /* XSPI_DCR4_REFRESH */

#define XSPI_NOR_STM32_INIT(inst)								\
												\
	PINCTRL_DT_DEFINE(STM32_XSPI_NODE(inst));						\
												\
	static void flash_stm32_xspi_irq_config_func_##inst(const struct device *dev)		\
	{											\
		IRQ_CONNECT(DT_IRQN(STM32_XSPI_NODE(inst)),					\
			    DT_IRQ(STM32_XSPI_NODE(inst), priority),				\
			    flash_stm32_xspi_isr, DEVICE_DT_INST_GET(inst), 0);			\
		irq_enable(DT_IRQN(STM32_XSPI_NODE(inst)));					\
	}											\
												\
	static const struct flash_stm32_xspi_config flash_stm32_xspi_cfg_##inst = {		\
		/* Properties of the controller */						\
		.pclken = STM32_CLOCK_INFO_BY_NAME(STM32_XSPI_NODE(inst), xspix),		\
		IF_ENABLED(DT_CLOCKS_HAS_NAME(STM32_XSPI_NODE(inst), xspi_ker),	(		\
			.has_pclken_ker = true,							\
			.pclken_ker = STM32_CLOCK_INFO_BY_NAME(STM32_XSPI_NODE(inst), xspi_ker),\
		))										\
		IF_ENABLED(DT_CLOCKS_HAS_NAME(STM32_XSPI_NODE(inst),  xspi_mgr), (		\
			.has_pclken_mgr = true,							\
			.pclken_mgr = STM32_CLOCK_INFO_BY_NAME(STM32_XSPI_NODE(inst), xspi_mgr),\
		))										\
		.pcfg = PINCTRL_DT_DEV_CONFIG_GET(STM32_XSPI_NODE(inst)),			\
		.irq_config = flash_stm32_xspi_irq_config_func_##inst,				\
		.mem_map_based_address = DT_REG_ADDR_BY_IDX(STM32_XSPI_NODE(inst), 1),		\
												\
		/* Properties of the flash device */						\
		.flash_size = DT_INST_PROP(inst, size) / 8,			/* In Bytes */	\
		.max_frequency = DT_INST_PROP(inst, ospi_max_frequency),			\
		.data_mode = DT_INST_PROP(inst, spi_bus_width),			/* SPI or OPI */\
		.data_rate = DT_INST_PROP(inst, data_rate),			/* DTR or STR */\
		.four_byte_opcodes = DT_INST_PROP_OR(inst, four_byte_opcodes, 0),		\
		.requires_ulbpr = DT_INST_PROP_OR(inst, requires_ulbpr, 0),			\
		IF_ENABLED(CONFIG_STM32_HAL2, (						\
			.sample_shift = (DT_PROP(STM32_XSPI_NODE(inst), ssht_enable)		\
					? HAL_XSPI_SAMPLE_SHIFT_HALFCYCLE			\
					: HAL_XSPI_SAMPLE_SHIFT_NONE),			\
			.dlyb_state = (DT_PROP(STM32_XSPI_NODE(inst), dlyb_bypass)		\
					? HAL_XSPI_DLYB_BYPASS				\
					: HAL_XSPI_DLYB_ON),				\
		))										\
		IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, reset_gpios), (				\
			.reset = GPIO_DT_SPEC_INST_GET(0, reset_gpios),				\
			.reset_gpios_duration = DT_INST_PROP_OR(inst, reset_gpios_duration, 1),	\
		))										\
	};											\
												\
	static struct flash_stm32_xspi_data flash_stm32_xspi_dev_data_##inst = {		\
		COND_CODE_1(IS_ENABLED(CONFIG_STM32_HAL2),				\
			(.hxspi = {							\
				.instance = (hal_xspi_t)DT_REG_ADDR(STM32_XSPI_NODE(inst)),	\
			},),								\
			(.hxspi = {							\
				.Instance = (XSPI_TypeDef *)DT_REG_ADDR(STM32_XSPI_NODE(inst)),	\
				.Init = {							\
					.FifoThresholdByte = STM32_XSPI_FIFO_THRESHOLD,		\
					.SampleShifting = (DT_PROP(STM32_XSPI_NODE(inst), ssht_enable)\
							? HAL_XSPI_SAMPLE_SHIFT_HALFCYCLE	\
							: HAL_XSPI_SAMPLE_SHIFT_NONE),		\
					.ChipSelectHighTimeCycle = 2,				\
					.ClockMode = HAL_XSPI_CLOCK_MODE_0,			\
					.ChipSelectBoundary = 0,				\
					.MemoryMode = HAL_XSPI_SINGLE_MEM,			\
					.FreeRunningClock = HAL_XSPI_FREERUNCLK_DISABLE,	\
					DT_XSPI_STM32_MEMORY_SELECT(inst)			\
					DT_XSPI_STM32_DELAY_BLOCK_BYPASS(inst)			\
					XSPI_STM32_MAXTRAN					\
					XSPI_STM32_REFRESH					\
				},								\
			},))									\
												\
		.qer_type = DT_QER_PROP_OR(inst, JESD216_DW15_QER_VAL_S1B6),			\
		.write_opcode = DT_WRITEOC_PROP_OR(inst, SPI_NOR_WRITEOC_NONE),			\
		.page_size = SPI_NOR_PAGE_SIZE, /* by default, to be updated by sfdp */		\
		IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, jedec_id), (				\
			.jedec_id = DT_INST_PROP(inst, jedec_id),				\
		))										\
												\
		XSPI_DMA_CHANNEL(STM32_XSPI_NODE(inst), tx, TX, MEMORY, PERIPHERAL)		\
		XSPI_DMA_CHANNEL(STM32_XSPI_NODE(inst), rx, RX, PERIPHERAL, MEMORY)		\
	};											\
												\
	DEVICE_DT_INST_DEFINE(inst, &flash_stm32_xspi_init, NULL,				\
			      &flash_stm32_xspi_dev_data_##inst, &flash_stm32_xspi_cfg_##inst,	\
			      POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,			\
			      &flash_stm32_xspi_driver_api);

#define XSPI_STM32_INIT(inst)							\
	IF_ENABLED(DT_NODE_HAS_STATUS_OKAY(STM32_XSPI_NODE(inst)),		\
		   (XSPI_NOR_STM32_INIT(inst)))

DT_INST_FOREACH_STATUS_OKAY(XSPI_STM32_INIT)
