/*
 * Copyright (c) 2019-2021, Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT nordic_qspi_nor

#include <errno.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/init.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/device_runtime.h>
#include <zephyr/drivers/pinctrl.h>
#include <soc.h>
#include <string.h>
#include <zephyr/logging/log.h>
#include <zephyr/irq.h>
LOG_MODULE_REGISTER(qspi_nor, CONFIG_FLASH_LOG_LEVEL);

#include "spi_nor.h"
#include "jesd216.h"
#include "flash_priv.h"
#include <nrf_erratas.h>
#include <nrfx_qspi.h>
#include <hal/nrf_clock.h>
#include <hal/nrf_gpio.h>

struct qspi_nor_data {
#if !defined(CONFIG_PM_DEVICE_RUNTIME) && defined(CONFIG_MULTITHREADING)
	/* A semaphore to control QSPI deactivation. */
	struct k_sem count;
#endif
#ifdef CONFIG_MULTITHREADING
	/* The semaphore to control exclusive access to the device. */
	struct k_sem sem;
	/* The semaphore to indicate that transfer has completed. */
	struct k_sem sync;
#else /* CONFIG_MULTITHREADING */
	/* A flag that signals completed transfer when threads are
	 * not enabled.
	 */
	volatile bool ready;
#endif /* CONFIG_MULTITHREADING */
	bool xip_enabled;
};

struct qspi_nor_config {
	nrfx_qspi_config_t nrfx_cfg;

	/* Size from devicetree, in bytes */
	uint32_t size;

	/* JEDEC id from devicetree */
	uint8_t id[SPI_NOR_MAX_ID_LEN];

	const struct pinctrl_dev_config *pcfg;
};

/* Status register bits */
#define QSPI_SECTOR_SIZE SPI_NOR_SECTOR_SIZE
#define QSPI_BLOCK_SIZE SPI_NOR_BLOCK_SIZE

/* instance 0 flash size in bytes */
#if DT_INST_NODE_HAS_PROP(0, size_in_bytes)
#define INST_0_BYTES (DT_INST_PROP(0, size_in_bytes))
#elif DT_INST_NODE_HAS_PROP(0, size)
#define INST_0_BYTES (DT_INST_PROP(0, size) / 8)
#else
#error "No size specified. 'size' or 'size-in-bytes' must be set"
#endif

BUILD_ASSERT(!(DT_INST_NODE_HAS_PROP(0, size_in_bytes) && DT_INST_NODE_HAS_PROP(0, size)),
	     "Node " DT_NODE_PATH(DT_DRV_INST(0)) " has both size and size-in-bytes "
	     "properties; use exactly one");


#define INST_0_SCK_FREQUENCY DT_INST_PROP(0, sck_frequency)
/*
 * According to the respective specifications, the nRF52 QSPI supports clock
 * frequencies 2 - 32 MHz and the nRF53 one supports 6 - 96 MHz.
 */
BUILD_ASSERT(INST_0_SCK_FREQUENCY >= (NRF_QSPI_BASE_CLOCK_FREQ / 16),
	     "Unsupported SCK frequency.");

/*
 * Determine a configuration value (INST_0_SCK_CFG) and, if needed, a divider
 * (BASE_CLOCK_DIV) for the clock from which the SCK frequency is derived that
 * need to be used to achieve the SCK frequency as close as possible (but not
 * higher) to the one specified in DT.
 */
#if defined(CONFIG_SOC_SERIES_NRF53X)
/*
 * On nRF53 Series SoCs, the default /4 divider for the HFCLK192M clock can
 * only be used when the QSPI peripheral is idle. When a QSPI operation is
 * performed, the divider needs to be changed to /1 or /2 (particularly,
 * the specification says that the peripheral "supports 192 MHz and 96 MHz
 * PCLK192M frequency"), but after that operation is complete, the default
 * divider needs to be restored to avoid increased current consumption.
 */
#if (INST_0_SCK_FREQUENCY >= NRF_QSPI_BASE_CLOCK_FREQ)
/* For requested SCK >= 96 MHz, use HFCLK192M / 1 / (2*1) = 96 MHz */
#define BASE_CLOCK_DIV NRF_CLOCK_HFCLK_DIV_1
#define INST_0_SCK_CFG NRF_QSPI_FREQ_DIV1
/* If anomaly 159 is to be prevented, only /1 divider can be used. */
#elif NRF53_ERRATA_159_ENABLE_WORKAROUND
#define BASE_CLOCK_DIV NRF_CLOCK_HFCLK_DIV_1
#define INST_0_SCK_CFG (DIV_ROUND_UP(NRF_QSPI_BASE_CLOCK_FREQ, \
				     INST_0_SCK_FREQUENCY) - 1)
#elif (INST_0_SCK_FREQUENCY >= (NRF_QSPI_BASE_CLOCK_FREQ / 2))
/* For 96 MHz > SCK >= 48 MHz, use HFCLK192M / 2 / (2*1) = 48 MHz */
#define BASE_CLOCK_DIV NRF_CLOCK_HFCLK_DIV_2
#define INST_0_SCK_CFG NRF_QSPI_FREQ_DIV1
#elif (INST_0_SCK_FREQUENCY >= (NRF_QSPI_BASE_CLOCK_FREQ / 3))
/* For 48 MHz > SCK >= 32 MHz, use HFCLK192M / 1 / (2*3) = 32 MHz */
#define BASE_CLOCK_DIV NRF_CLOCK_HFCLK_DIV_1
#define INST_0_SCK_CFG NRF_QSPI_FREQ_DIV3
#else
/* For requested SCK < 32 MHz, use divider /2 for HFCLK192M. */
#define BASE_CLOCK_DIV NRF_CLOCK_HFCLK_DIV_2
#define INST_0_SCK_CFG (DIV_ROUND_UP(NRF_QSPI_BASE_CLOCK_FREQ / 2, \
				     INST_0_SCK_FREQUENCY) - 1)
#endif
/* After the base clock divider is changed, some time is needed for the new
 * setting to take effect. This value specifies the delay (in microseconds)
 * to be applied to ensure that the clock is ready when the QSPI operation
 * starts. It was measured with a logic analyzer (unfortunately, the nRF5340
 * specification does not provide any numbers in this regard).
 */
#define BASE_CLOCK_SWITCH_DELAY_US 7

#else
/*
 * On nRF52 Series SoCs, the base clock divider is not configurable,
 * so BASE_CLOCK_DIV is not defined.
 */
#if (INST_0_SCK_FREQUENCY >= NRF_QSPI_BASE_CLOCK_FREQ)
#define INST_0_SCK_CFG NRF_QSPI_FREQ_DIV1
#else
#define INST_0_SCK_CFG (DIV_ROUND_UP(NRF_QSPI_BASE_CLOCK_FREQ, \
					 INST_0_SCK_FREQUENCY) - 1)

#endif

#endif /* defined(CONFIG_SOC_SERIES_NRF53X) */

/* 0 for MODE0 (CPOL=0, CPHA=0), 1 for MODE3 (CPOL=1, CPHA=1). */
#define INST_0_SPI_MODE DT_INST_PROP(0, cpol)
BUILD_ASSERT(DT_INST_PROP(0, cpol) == DT_INST_PROP(0, cpha),
	     "Invalid combination of \"cpol\" and \"cpha\" properties.");

/* for accessing devicetree properties of the bus node */
#define QSPI_NODE DT_INST_BUS(0)
#define QSPI_PROP_AT(prop, idx) DT_PROP_BY_IDX(QSPI_NODE, prop, idx)
#define QSPI_PROP_LEN(prop) DT_PROP_LEN(QSPI_NODE, prop)

#define INST_0_QER _CONCAT(JESD216_DW15_QER_VAL_, \
			   DT_STRING_TOKEN(DT_DRV_INST(0), \
					   quad_enable_requirements))

#define IS_EQUAL(x, y) ((x) == (y))
#define SR1_WRITE_CLEARS_SR2 IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S2B1v1)

#define SR2_WRITE_NEEDS_SR1  (IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S2B1v1) || \
			      IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S2B1v4) || \
			      IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S2B1v5))

#define QER_IS_S2B1 (IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S2B1v1) || \
		     IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S2B1v4) || \
		     IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S2B1v5) || \
		     IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S2B1v6))

BUILD_ASSERT((IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_NONE)
	      || IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S1B6)
	      || IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S2B1v1)
	      || IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S2B1v4)
	      || IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S2B1v5)
	      || IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S2B1v6)),
	     "Driver only supports NONE, S1B6, S2B1v1, S2B1v4, S2B1v5 or S2B1v6 for quad-enable-requirements");

#define INST_0_4BA DT_INST_PROP_OR(0, enter_4byte_addr, 0)
#if (INST_0_4BA != 0)
BUILD_ASSERT(((INST_0_4BA & 0x03) != 0),
	     "Driver only supports command (0xB7) for entering 4 byte addressing mode");
BUILD_ASSERT(DT_INST_PROP(0, address_size_32),
	    "After entering 4 byte addressing mode, 4 byte addressing is expected");
#endif

void z_impl_nrf_qspi_nor_xip_enable(const struct device *dev, bool enable);
void z_vrfy_nrf_qspi_nor_xip_enable(const struct device *dev, bool enable);

#define WORD_SIZE 4

/**
 * @brief QSPI buffer structure
 * Structure used both for TX and RX purposes.
 *
 * @param buf is a valid pointer to a data buffer.
 * Can not be NULL.
 * @param len is the length of the data to be handled.
 * If no data to transmit/receive - pass 0.
 */
struct qspi_buf {
	uint8_t *buf;
	size_t len;
};

/**
 * @brief QSPI command structure
 * Structure used for custom command usage.
 *
 * @param op_code is a command value (i.e 0x9F - get Jedec ID)
 * @param tx_buf structure used for TX purposes. Can be NULL if not used.
 * @param rx_buf structure used for RX purposes. Can be NULL if not used.
 */
struct qspi_cmd {
	uint8_t op_code;
	const struct qspi_buf *tx_buf;
	const struct qspi_buf *rx_buf;
};

static int qspi_nor_write_protection_set(const struct device *dev,
					 bool write_protect);

static int exit_dpd(const struct device *const dev);

/**
 * @brief Test whether offset is aligned.
 */
#define QSPI_IS_SECTOR_ALIGNED(_ofs) (((_ofs) & (QSPI_SECTOR_SIZE - 1U)) == 0)
#define QSPI_IS_BLOCK_ALIGNED(_ofs) (((_ofs) & (QSPI_BLOCK_SIZE - 1U)) == 0)

/**
 * @brief Converts NRFX return codes to the zephyr ones
 */
static inline int qspi_get_zephyr_ret_code(nrfx_err_t res)
{
	switch (res) {
	case NRFX_SUCCESS:
		return 0;
	case NRFX_ERROR_INVALID_PARAM:
	case NRFX_ERROR_INVALID_ADDR:
		return -EINVAL;
	case NRFX_ERROR_INVALID_STATE:
		return -ECANCELED;
#if NRF53_ERRATA_159_ENABLE_WORKAROUND
	case NRFX_ERROR_FORBIDDEN:
		LOG_ERR("nRF5340 anomaly 159 conditions detected");
		LOG_ERR("Set the CPU clock to 64 MHz before starting QSPI operation");
		return -ECANCELED;
#endif
	case NRFX_ERROR_BUSY:
	case NRFX_ERROR_TIMEOUT:
	default:
		return -EBUSY;
	}
}

static inline void qspi_lock(const struct device *dev)
{
#ifdef CONFIG_MULTITHREADING
	struct qspi_nor_data *dev_data = dev->data;

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

static inline void qspi_unlock(const struct device *dev)
{
#ifdef CONFIG_MULTITHREADING
	struct qspi_nor_data *dev_data = dev->data;

	k_sem_give(&dev_data->sem);
#endif
}

static inline void qspi_clock_div_change(void)
{
#ifdef CONFIG_SOC_SERIES_NRF53X
	/* Make sure the base clock divider is changed accordingly
	 * before a QSPI transfer is performed.
	 */
	nrf_clock_hfclk192m_div_set(NRF_CLOCK, BASE_CLOCK_DIV);
	k_busy_wait(BASE_CLOCK_SWITCH_DELAY_US);
#endif
}

static inline void qspi_clock_div_restore(void)
{
#ifdef CONFIG_SOC_SERIES_NRF53X
	/* Restore the default base clock divider to reduce power
	 * consumption when the QSPI peripheral is idle.
	 */
	nrf_clock_hfclk192m_div_set(NRF_CLOCK, NRF_CLOCK_HFCLK_DIV_4);
#endif
}

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

#if defined(CONFIG_PM_DEVICE_RUNTIME)
	int rc = pm_device_runtime_get(dev);

	if (rc < 0) {
		LOG_ERR("pm_device_runtime_get failed: %d", rc);
	}
#elif defined(CONFIG_MULTITHREADING)
	/* In multithreading, the driver can call qspi_acquire more than once
	 * before calling qspi_release. Keeping count, so QSPI is deactivated
	 * only at the last call (count == 0).
	 */
	k_sem_give(&dev_data->count);
#endif

	qspi_lock(dev);

	if (!dev_data->xip_enabled) {
		qspi_clock_div_change();

		pm_device_busy_set(dev);
	}
}

static void qspi_release(const struct device *dev)
{
	struct qspi_nor_data *dev_data = dev->data;
	bool deactivate = true;

#if !defined(CONFIG_PM_DEVICE_RUNTIME) && defined(CONFIG_MULTITHREADING)
	/* The last thread to finish using the driver deactivates the QSPI */
	(void) k_sem_take(&dev_data->count, K_NO_WAIT);
	deactivate = (k_sem_count_get(&dev_data->count) == 0);
#endif

	if (!dev_data->xip_enabled) {
		qspi_clock_div_restore();

		if (deactivate && !IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
			(void) nrfx_qspi_deactivate();
		}

		pm_device_busy_clear(dev);
	}

	qspi_unlock(dev);

#if defined(CONFIG_PM_DEVICE_RUNTIME)
	int rc = pm_device_runtime_put(dev);

	if (rc < 0) {
		LOG_ERR("pm_device_runtime_put failed: %d", rc);
	}
#endif
}

static inline void qspi_wait_for_completion(const struct device *dev,
					    nrfx_err_t res)
{
	struct qspi_nor_data *dev_data = dev->data;

	if (res == NRFX_SUCCESS) {
#ifdef CONFIG_MULTITHREADING
		k_sem_take(&dev_data->sync, K_FOREVER);
#else /* CONFIG_MULTITHREADING */
		unsigned int key = irq_lock();

		while (!dev_data->ready) {
			k_cpu_atomic_idle(key);
			key = irq_lock();
		}
		dev_data->ready = false;
		irq_unlock(key);
#endif /* CONFIG_MULTITHREADING */
	}
}

static inline void qspi_complete(struct qspi_nor_data *dev_data)
{
#ifdef CONFIG_MULTITHREADING
	k_sem_give(&dev_data->sync);
#else /* CONFIG_MULTITHREADING */
	dev_data->ready = true;
#endif /* CONFIG_MULTITHREADING */
}

/**
 * @brief QSPI handler
 *
 * @param event Driver event type
 * @param p_context Pointer to context. Use in interrupt handler.
 * @retval None
 */
static void qspi_handler(nrfx_qspi_evt_t event, void *p_context)
{
	struct qspi_nor_data *dev_data = p_context;

	if (event == NRFX_QSPI_EVENT_DONE) {
		qspi_complete(dev_data);
	}
}

/* QSPI send custom command.
 *
 * If this is used for both send and receive the buffer sizes must be
 * equal and cover the whole transaction.
 */
static int qspi_send_cmd(const struct device *dev, const struct qspi_cmd *cmd,
			 bool wren)
{
	/* Check input parameters */
	if (!cmd) {
		return -EINVAL;
	}

	const void *tx_buf = NULL;
	size_t tx_len = 0;
	void *rx_buf = NULL;
	size_t rx_len = 0;
	size_t xfer_len = sizeof(cmd->op_code);

	if (cmd->tx_buf) {
		tx_buf = cmd->tx_buf->buf;
		tx_len = cmd->tx_buf->len;
	}

	if (cmd->rx_buf) {
		rx_buf = cmd->rx_buf->buf;
		rx_len = cmd->rx_buf->len;
	}

	if ((rx_len != 0) && (tx_len != 0)) {
		if (rx_len != tx_len) {
			return -EINVAL;
		}

		xfer_len += tx_len;
	} else {
		/* At least one of these is zero. */
		xfer_len += tx_len + rx_len;
	}

	if (xfer_len > NRF_QSPI_CINSTR_LEN_9B) {
		LOG_WRN("cinstr %02x transfer too long: %zu",
			cmd->op_code, xfer_len);
		return -EINVAL;
	}

	nrf_qspi_cinstr_conf_t cinstr_cfg = {
		.opcode = cmd->op_code,
		.length = xfer_len,
		.io2_level = true,
		.io3_level = true,
		.wipwait = false,
		.wren = wren,
	};

	int res = nrfx_qspi_cinstr_xfer(&cinstr_cfg, tx_buf, rx_buf);

	return qspi_get_zephyr_ret_code(res);
}

#if !IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_NONE)
/* RDSR.  Negative value is error. */
static int qspi_rdsr(const struct device *dev, uint8_t sr_num)
{
	uint8_t opcode = SPI_NOR_CMD_RDSR;

	if (sr_num > 2 || sr_num == 0) {
		return -EINVAL;
	}
	if (sr_num == 2) {
		opcode = SPI_NOR_CMD_RDSR2;
	}
	uint8_t sr = 0xFF;
	const struct qspi_buf sr_buf = {
		.buf = &sr,
		.len = sizeof(sr),
	};
	struct qspi_cmd cmd = {
		.op_code = opcode,
		.rx_buf = &sr_buf,
	};
	int rc = qspi_send_cmd(dev, &cmd, false);

	return (rc < 0) ? rc : sr;
}

/* Wait until RDSR confirms write is not in progress. */
static int qspi_wait_while_writing(const struct device *dev)
{
	int rc;

	do {
		rc = qspi_rdsr(dev, 1);
	} while ((rc >= 0)
		 && ((rc & SPI_NOR_WIP_BIT) != 0U));

	return (rc < 0) ? rc : 0;
}

static int qspi_wrsr(const struct device *dev, uint8_t sr_val, uint8_t sr_num)
{
	int rc = 0;
	uint8_t opcode = SPI_NOR_CMD_WRSR;
	uint8_t length = 1;
	uint8_t sr_array[2] = {0};

	if (sr_num > 2 || sr_num == 0) {
		return -EINVAL;
	}

	if (sr_num == 1) {
		sr_array[0] = sr_val;
#if SR1_WRITE_CLEARS_SR2
		/* Writing sr1 clears sr2. need to read/modify/write both. */
		rc = qspi_rdsr(dev, 2);
		if (rc < 0) {
			LOG_ERR("RDSR for WRSR failed: %d", rc);
			return rc;
		}
		sr_array[1] = rc;
		length = 2;
#endif
	} else { /* sr_num == 2 */

#if SR2_WRITE_NEEDS_SR1
		/* Writing sr2 requires writing sr1 as well.
		 * Uses standard WRSR opcode
		 */
		sr_array[1] = sr_val;
		rc = qspi_rdsr(dev, 1);
		if (rc < 0) {
			LOG_ERR("RDSR for WRSR failed: %d", rc);
			return rc;
		}
		sr_array[0] = rc;
		length = 2;
#elif IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S2B1v6)
		/* Writing sr2 uses a dedicated WRSR2 command */
		sr_array[0] = sr_val;
		opcode = SPI_NOR_CMD_WRSR2;
#else
		LOG_ERR("Attempted to write status register 2, but no known method to write sr2");
		return -EINVAL;
#endif
	}

	const struct qspi_buf sr_buf = {
		.buf = sr_array,
		.len = length,
	};
	struct qspi_cmd cmd = {
		.op_code = opcode,
		.tx_buf = &sr_buf,
	};

	rc = qspi_send_cmd(dev, &cmd, true);

	/* Writing SR can take some time, and further
	 * commands sent while it's happening can be
	 * corrupted.  Wait.
	 */
	if (rc == 0) {
		rc = qspi_wait_while_writing(dev);
	}

	return rc;
}
#endif /* !IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_NONE) */

/* QSPI erase */
static int qspi_erase(const struct device *dev, uint32_t addr, uint32_t size)
{
	const struct qspi_nor_config *params = dev->config;
	int rc, rc2;

	rc = qspi_nor_write_protection_set(dev, false);
	if (rc != 0) {
		return rc;
	}
	while (size > 0) {
		nrfx_err_t res = !NRFX_SUCCESS;
		uint32_t adj = 0;

		if (size == params->size) {
			/* chip erase */
			res = nrfx_qspi_chip_erase();
			adj = size;
		} else if ((size >= QSPI_BLOCK_SIZE) &&
			   QSPI_IS_BLOCK_ALIGNED(addr)) {
			/* 64 kB block erase */
			res = nrfx_qspi_erase(NRF_QSPI_ERASE_LEN_64KB, addr);
			adj = QSPI_BLOCK_SIZE;
		} else if ((size >= QSPI_SECTOR_SIZE) &&
			   QSPI_IS_SECTOR_ALIGNED(addr)) {
			/* 4kB sector erase */
			res = nrfx_qspi_erase(NRF_QSPI_ERASE_LEN_4KB, addr);
			adj = QSPI_SECTOR_SIZE;
		} else {
			/* minimal erase size is at least a sector size */
			LOG_ERR("unsupported at 0x%lx size %zu", (long)addr, size);
			res = NRFX_ERROR_INVALID_PARAM;
		}

		qspi_wait_for_completion(dev, res);
		if (res == NRFX_SUCCESS) {
			addr += adj;
			size -= adj;
		} else {
			LOG_ERR("erase error at 0x%lx size %zu", (long)addr, size);
			rc = qspi_get_zephyr_ret_code(res);
			break;
		}
	}

	rc2 = qspi_nor_write_protection_set(dev, true);

	return rc != 0 ? rc : rc2;
}

static int configure_chip(const struct device *dev)
{
	const struct qspi_nor_config *dev_config = dev->config;
	int rc = 0;

	/* Set QE to match transfer mode.  If not using quad
	 * it's OK to leave QE set, but doing so prevents use
	 * of WP#/RESET#/HOLD# which might be useful.
	 *
	 * Note build assert above ensures QER is S1B6 or
	 * S2B1v1/4/5/6. Other options require more logic.
	 */
#if !IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_NONE)
		nrf_qspi_prot_conf_t const *prot_if =
			&dev_config->nrfx_cfg.prot_if;
		bool qe_value = (prot_if->writeoc == NRF_QSPI_WRITEOC_PP4IO) ||
				(prot_if->writeoc == NRF_QSPI_WRITEOC_PP4O)  ||
				(prot_if->readoc == NRF_QSPI_READOC_READ4IO) ||
				(prot_if->readoc == NRF_QSPI_READOC_READ4O)  ||
				(prot_if->readoc == NRF_QSPI_READOC_READ2IO);
		uint8_t sr_num = 0;
		uint8_t qe_mask = 0;

#if IS_EQUAL(INST_0_QER, JESD216_DW15_QER_VAL_S1B6)
		sr_num = 1;
		qe_mask = BIT(6);
#elif QER_IS_S2B1
		sr_num = 2;
		qe_mask = BIT(1);
#else
		LOG_ERR("Unsupported QER type");
		return -EINVAL;
#endif

		rc = qspi_rdsr(dev, sr_num);
		if (rc < 0) {
			LOG_ERR("RDSR failed: %d", rc);
			return rc;
		}

		uint8_t sr = (uint8_t)rc;
		bool qe_state = ((sr & qe_mask) != 0U);

		LOG_DBG("RDSR %02x QE %d need %d: %s", sr, qe_state, qe_value,
			(qe_state != qe_value) ? "updating" : "no-change");

		rc = 0;
		if (qe_state != qe_value) {
			sr ^= qe_mask;
			rc = qspi_wrsr(dev, sr, sr_num);
		}

		if (rc < 0) {
			LOG_ERR("QE %s failed: %d", qe_value ? "set" : "clear",
				rc);
			return rc;
		}
#endif

	if (INST_0_4BA != 0) {
		struct qspi_cmd cmd = {
			.op_code = SPI_NOR_CMD_4BA,
		};

		/* Call will send write enable before instruction if that
		 * requirement is encoded in INST_0_4BA.
		 */
		rc = qspi_send_cmd(dev, &cmd, (INST_0_4BA & 0x02));

		if (rc < 0) {
			LOG_ERR("E4BA cmd issue failed: %d.", rc);
		} else {
			LOG_DBG("E4BA cmd issued.");
		}
	}

	return rc;
}

static int qspi_rdid(const struct device *dev, uint8_t *id)
{
	const struct qspi_buf rx_buf = {
		.buf = id,
		.len = 3
	};
	const struct qspi_cmd cmd = {
		.op_code = SPI_NOR_CMD_RDID,
		.rx_buf = &rx_buf,
	};

	return qspi_send_cmd(dev, &cmd, false);
}

#if defined(CONFIG_FLASH_JESD216_API)

static int qspi_read_jedec_id(const struct device *dev, uint8_t *id)
{
	int rc;

	qspi_acquire(dev);

	rc = qspi_rdid(dev, id);

	qspi_release(dev);

	return rc;
}

static int qspi_sfdp_read(const struct device *dev, off_t offset,
			  void *data, size_t len)
{
	__ASSERT(data != NULL, "null destination");

	uint8_t addr_buf[] = {
		offset >> 16,
		offset >> 8,
		offset,
		0,		/* wait state */
	};
	nrf_qspi_cinstr_conf_t cinstr_cfg = {
		.opcode = JESD216_CMD_READ_SFDP,
		.length = NRF_QSPI_CINSTR_LEN_1B,
		.io2_level = true,
		.io3_level = true,
	};
	nrfx_err_t res;

	qspi_acquire(dev);

	res = nrfx_qspi_lfm_start(&cinstr_cfg);
	if (res != NRFX_SUCCESS) {
		LOG_DBG("lfm_start: %x", res);
		goto out;
	}

	res = nrfx_qspi_lfm_xfer(addr_buf, NULL, sizeof(addr_buf), false);
	if (res != NRFX_SUCCESS) {
		LOG_DBG("lfm_xfer addr: %x", res);
		goto out;
	}

	res = nrfx_qspi_lfm_xfer(NULL, data, len, true);
	if (res != NRFX_SUCCESS) {
		LOG_DBG("lfm_xfer read: %x", res);
		goto out;
	}

out:
	qspi_release(dev);

	return qspi_get_zephyr_ret_code(res);
}

#endif /* CONFIG_FLASH_JESD216_API */

static inline nrfx_err_t read_non_aligned(const struct device *dev,
					  off_t addr,
					  void *dest, size_t size)
{
	uint8_t __aligned(WORD_SIZE) buf[WORD_SIZE * 2];
	uint8_t *dptr = dest;

	off_t flash_prefix = (WORD_SIZE - (addr % WORD_SIZE)) % WORD_SIZE;

	if (flash_prefix > size) {
		flash_prefix = size;
	}

	off_t dest_prefix = (WORD_SIZE - (off_t)dptr % WORD_SIZE) % WORD_SIZE;

	if (dest_prefix > size) {
		dest_prefix = size;
	}

	off_t flash_suffix = (size - flash_prefix) % WORD_SIZE;
	off_t flash_middle = size - flash_prefix - flash_suffix;
	off_t dest_middle = size - dest_prefix -
			    (size - dest_prefix) % WORD_SIZE;

	if (flash_middle > dest_middle) {
		flash_middle = dest_middle;
		flash_suffix = size - flash_prefix - flash_middle;
	}

	nrfx_err_t res = NRFX_SUCCESS;

	/* read from aligned flash to aligned memory */
	if (flash_middle != 0) {
		res = nrfx_qspi_read(dptr + dest_prefix, flash_middle,
				     addr + flash_prefix);
		qspi_wait_for_completion(dev, res);
		if (res != NRFX_SUCCESS) {
			return res;
		}

		/* perform shift in RAM */
		if (flash_prefix != dest_prefix) {
			memmove(dptr + flash_prefix, dptr + dest_prefix, flash_middle);
		}
	}

	/* read prefix */
	if (flash_prefix != 0) {
		res = nrfx_qspi_read(buf, WORD_SIZE, addr -
				     (WORD_SIZE - flash_prefix));
		qspi_wait_for_completion(dev, res);
		if (res != NRFX_SUCCESS) {
			return res;
		}
		memcpy(dptr, buf + WORD_SIZE - flash_prefix, flash_prefix);
	}

	/* read suffix */
	if (flash_suffix != 0) {
		res = nrfx_qspi_read(buf, WORD_SIZE * 2,
				     addr + flash_prefix + flash_middle);
		qspi_wait_for_completion(dev, res);
		if (res != NRFX_SUCCESS) {
			return res;
		}
		memcpy(dptr + flash_prefix + flash_middle, buf, flash_suffix);
	}

	return res;
}

static int qspi_nor_read(const struct device *dev, off_t addr, void *dest,
			 size_t size)
{
	const struct qspi_nor_config *params = dev->config;
	nrfx_err_t res;

	if (!dest) {
		return -EINVAL;
	}

	/* read size must be non-zero */
	if (!size) {
		return 0;
	}

	/* affected region should be within device */
	if (addr < 0 ||
	    (addr + size) > params->size) {
		LOG_ERR("read error: address or size "
			"exceeds expected values."
			"Addr: 0x%lx size %zu", (long)addr, size);
		return -EINVAL;
	}

	qspi_acquire(dev);

	res = read_non_aligned(dev, addr, dest, size);

	qspi_release(dev);

	return qspi_get_zephyr_ret_code(res);
}

/* addr aligned, sptr not null, slen less than 4 */
static inline nrfx_err_t write_sub_word(const struct device *dev, off_t addr,
					const void *sptr, size_t slen)
{
	uint8_t __aligned(4) buf[4];
	nrfx_err_t res;

	/* read out the whole word so that unchanged data can be
	 * written back
	 */
	res = nrfx_qspi_read(buf, sizeof(buf), addr);
	qspi_wait_for_completion(dev, res);

	if (res == NRFX_SUCCESS) {
		memcpy(buf, sptr, slen);
		res = nrfx_qspi_write(buf, sizeof(buf), addr);
		qspi_wait_for_completion(dev, res);
	}

	return res;
}

BUILD_ASSERT((CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE % 4) == 0,
	     "NOR stack buffer must be multiple of 4 bytes");

/* If enabled write using a stack-allocated aligned SRAM buffer as
 * required for DMA transfers by QSPI peripheral.
 *
 * If not enabled return the error the peripheral would have produced.
 */
static nrfx_err_t write_through_buffer(const struct device *dev, off_t addr,
				       const void *sptr, size_t slen)
{
	nrfx_err_t res = NRFX_SUCCESS;

	if (CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE > 0) {
		uint8_t __aligned(4) buf[CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE];
		const uint8_t *sp = sptr;

		while ((slen > 0) && (res == NRFX_SUCCESS)) {
			size_t len = MIN(slen, sizeof(buf));

			memcpy(buf, sp, len);
			res = nrfx_qspi_write(buf, len, addr);
			qspi_wait_for_completion(dev, res);

			if (res == NRFX_SUCCESS) {
				slen -= len;
				sp += len;
				addr += len;
			}
		}
	} else {
		res = NRFX_ERROR_INVALID_ADDR;
	}
	return res;
}

static int qspi_nor_write(const struct device *dev, off_t addr,
			  const void *src,
			  size_t size)
{
	const struct qspi_nor_config *params = dev->config;
	int rc, rc2;

	if (!src) {
		return -EINVAL;
	}

	/* write size must be non-zero, less than 4, or a multiple of 4 */
	if ((size == 0)
	    || ((size > 4) && ((size % 4U) != 0))) {
		return -EINVAL;
	}
	/* address must be 4-byte aligned */
	if ((addr % 4U) != 0) {
		return -EINVAL;
	}

	/* affected region should be within device */
	if (addr < 0 ||
	    (addr + size) > params->size) {
		LOG_ERR("write error: address or size "
			"exceeds expected values."
			"Addr: 0x%lx size %zu", (long)addr, size);
		return -EINVAL;
	}

	qspi_acquire(dev);

	rc = qspi_nor_write_protection_set(dev, false);
	if (rc == 0) {
		nrfx_err_t res;

		if (size < 4U) {
			res = write_sub_word(dev, addr, src, size);
		} else if (!nrfx_is_in_ram(src) ||
			   !nrfx_is_word_aligned(src)) {
			res = write_through_buffer(dev, addr, src, size);
		} else {
			res = nrfx_qspi_write(src, size, addr);
			qspi_wait_for_completion(dev, res);
		}

		rc = qspi_get_zephyr_ret_code(res);
	}

	rc2 = qspi_nor_write_protection_set(dev, true);

	qspi_release(dev);

	return rc != 0 ? rc : rc2;
}

static int qspi_nor_erase(const struct device *dev, off_t addr, size_t size)
{
	const struct qspi_nor_config *params = dev->config;
	int rc;

	/* address must be sector-aligned */
	if ((addr % QSPI_SECTOR_SIZE) != 0) {
		return -EINVAL;
	}

	/* size must be a non-zero multiple of sectors */
	if ((size == 0) || (size % QSPI_SECTOR_SIZE) != 0) {
		return -EINVAL;
	}

	/* affected region should be within device */
	if (addr < 0 ||
	    (addr + size) > params->size) {
		LOG_ERR("erase error: address or size "
			"exceeds expected values."
			"Addr: 0x%lx size %zu", (long)addr, size);
		return -EINVAL;
	}

	qspi_acquire(dev);

	rc = qspi_erase(dev, addr, size);

	qspi_release(dev);

	return rc;
}

static int qspi_nor_write_protection_set(const struct device *dev,
					 bool write_protect)
{
	int rc = 0;
	struct qspi_cmd cmd = {
		.op_code = ((write_protect) ? SPI_NOR_CMD_WRDI : SPI_NOR_CMD_WREN),
	};

	if (qspi_send_cmd(dev, &cmd, false) != 0) {
		rc = -EIO;
	}

	return rc;
}

static int qspi_init(const struct device *dev)
{
	const struct qspi_nor_config *dev_config = dev->config;
	uint8_t id[SPI_NOR_MAX_ID_LEN];
	nrfx_err_t res;
	int rc;

	res = nrfx_qspi_init(&dev_config->nrfx_cfg, qspi_handler, dev->data);
	rc = qspi_get_zephyr_ret_code(res);
	if (rc < 0) {
		return rc;
	}

#if DT_INST_NODE_HAS_PROP(0, rx_delay)
	if (!nrf53_errata_121()) {
		nrf_qspi_iftiming_set(NRF_QSPI, DT_INST_PROP(0, rx_delay));
	}
#endif

	/* It may happen that after the flash chip was previously put into
	 * the DPD mode, the system was reset but the flash chip was not.
	 * Consequently, the flash chip can be in the DPD mode at this point.
	 * Some flash chips will just exit the DPD mode on the first CS pulse,
	 * but some need to receive the dedicated command to do it, so send it.
	 * This can be the case even if the current image does not have
	 * CONFIG_PM_DEVICE set to enter DPD mode, as a previously executing image
	 * (for example the main image if the currently executing image is the
	 * bootloader) might have set DPD mode before reboot. As a result,
	 * attempt to exit DPD mode regardless of whether CONFIG_PM_DEVICE is set.
	 */
	rc = exit_dpd(dev);
	if (rc < 0) {
		return rc;
	}

	/* Retrieve the Flash JEDEC ID and compare it with the one expected. */
	rc = qspi_rdid(dev, id);
	if (rc < 0) {
		return rc;
	}

	if (memcmp(dev_config->id, id, SPI_NOR_MAX_ID_LEN) != 0) {
		LOG_ERR("JEDEC id [%02x %02x %02x] expect [%02x %02x %02x]",
			id[0], id[1], id[2], dev_config->id[0],
			dev_config->id[1], dev_config->id[2]);
		return -ENODEV;
	}

	/* The chip is correct, it can be configured now. */
	return configure_chip(dev);
}

static int qspi_nor_init(const struct device *dev)
{
	const struct qspi_nor_config *dev_config = dev->config;
	int rc;

	rc = pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_DEFAULT);
	if (rc < 0) {
		return rc;
	}

	IRQ_CONNECT(DT_IRQN(QSPI_NODE), DT_IRQ(QSPI_NODE, priority),
		    nrfx_isr, nrfx_qspi_irq_handler, 0);

	qspi_clock_div_change();

	rc = qspi_init(dev);

	qspi_clock_div_restore();

	if (!IS_ENABLED(CONFIG_NORDIC_QSPI_NOR_XIP) && nrfx_qspi_init_check()) {
		(void)nrfx_qspi_deactivate();
	}

#ifdef CONFIG_PM_DEVICE_RUNTIME
	int rc2 = pm_device_runtime_enable(dev);

	if (rc2 < 0) {
		LOG_ERR("Failed to enable runtime power management: %d", rc2);
	} else {
		LOG_DBG("Runtime power management enabled");
	}
#endif

#ifdef CONFIG_NORDIC_QSPI_NOR_XIP
	if (rc == 0) {
		/* Enable XIP mode for QSPI NOR flash, this will prevent the
		 * flash from being powered down
		 */
		z_impl_nrf_qspi_nor_xip_enable(dev, true);
	}
#endif

	return rc;
}

#if defined(CONFIG_FLASH_PAGE_LAYOUT)

/* instance 0 page count */
#define LAYOUT_PAGES_COUNT (INST_0_BYTES / \
			    CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE)

BUILD_ASSERT((CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE *
	      LAYOUT_PAGES_COUNT)
	     == INST_0_BYTES,
	     "QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE incompatible with flash size");

static const struct flash_pages_layout dev_layout = {
	.pages_count = LAYOUT_PAGES_COUNT,
	.pages_size = CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE,
};
#undef LAYOUT_PAGES_COUNT

static void qspi_nor_pages_layout(const struct device *dev,
				  const struct flash_pages_layout **layout,
				  size_t *layout_size)
{
	*layout = &dev_layout;
	*layout_size = 1;
}
#endif /* CONFIG_FLASH_PAGE_LAYOUT */

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

	static const struct flash_parameters qspi_flash_parameters = {
		.write_block_size = 4,
		.erase_value = 0xff,
	};

	return &qspi_flash_parameters;
}

static const struct flash_driver_api qspi_nor_api = {
	.read = qspi_nor_read,
	.write = qspi_nor_write,
	.erase = qspi_nor_erase,
	.get_parameters = qspi_flash_get_parameters,
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
	.page_layout = qspi_nor_pages_layout,
#endif
#if defined(CONFIG_FLASH_JESD216_API)
	.sfdp_read = qspi_sfdp_read,
	.read_jedec_id = qspi_read_jedec_id,
#endif /* CONFIG_FLASH_JESD216_API */
};

#ifdef CONFIG_PM_DEVICE
static int enter_dpd(const struct device *const dev)
{
	if (IS_ENABLED(DT_INST_PROP(0, has_dpd))) {
		struct qspi_cmd cmd = {
			.op_code = SPI_NOR_CMD_DPD,
		};
		uint32_t t_enter_dpd = DT_INST_PROP_OR(0, t_enter_dpd, 0);
		int rc;

		rc = qspi_send_cmd(dev, &cmd, false);
		if (rc < 0) {
			return rc;
		}

		if (t_enter_dpd) {
			uint32_t t_enter_dpd_us =
				DIV_ROUND_UP(t_enter_dpd, NSEC_PER_USEC);

			k_busy_wait(t_enter_dpd_us);
		}
	}

	return 0;
}
#endif /* CONFIG_PM_DEVICE */

static int exit_dpd(const struct device *const dev)
{
	if (IS_ENABLED(DT_INST_PROP(0, has_dpd))) {
		nrf_qspi_pins_t pins;
		nrf_qspi_pins_t disconnected_pins = {
			.sck_pin = NRF_QSPI_PIN_NOT_CONNECTED,
			.csn_pin = NRF_QSPI_PIN_NOT_CONNECTED,
			.io0_pin = NRF_QSPI_PIN_NOT_CONNECTED,
			.io1_pin = NRF_QSPI_PIN_NOT_CONNECTED,
			.io2_pin = NRF_QSPI_PIN_NOT_CONNECTED,
			.io3_pin = NRF_QSPI_PIN_NOT_CONNECTED,
		};
		struct qspi_cmd cmd = {
			.op_code = SPI_NOR_CMD_RDPD,
		};
		uint32_t t_exit_dpd = DT_INST_PROP_OR(0, t_exit_dpd, 0);
		nrfx_err_t res;
		int rc;

		nrf_qspi_pins_get(NRF_QSPI, &pins);
		nrf_qspi_pins_set(NRF_QSPI, &disconnected_pins);
		res = nrfx_qspi_activate(true);
		nrf_qspi_pins_set(NRF_QSPI, &pins);

		if (res != NRFX_SUCCESS) {
			return -EIO;
		}

		rc = qspi_send_cmd(dev, &cmd, false);
		if (rc < 0) {
			return rc;
		}

		if (t_exit_dpd) {
			uint32_t t_exit_dpd_us =
				DIV_ROUND_UP(t_exit_dpd, NSEC_PER_USEC);

			k_busy_wait(t_exit_dpd_us);
		}
	}

	return 0;
}

#ifdef CONFIG_PM_DEVICE
static int qspi_suspend(const struct device *dev)
{
	const struct qspi_nor_config *dev_config = dev->config;
	nrfx_err_t res;
	int rc;

	res = nrfx_qspi_mem_busy_check();
	if (res != NRFX_SUCCESS) {
		return -EBUSY;
	}

	rc = enter_dpd(dev);
	if (rc < 0) {
		return rc;
	}

	nrfx_qspi_uninit();

	return pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_SLEEP);
}

static int qspi_resume(const struct device *dev)
{
	const struct qspi_nor_config *dev_config = dev->config;
	nrfx_err_t res;
	int rc;

	rc = pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_DEFAULT);
	if (rc < 0) {
		return rc;
	}

	res = nrfx_qspi_init(&dev_config->nrfx_cfg, qspi_handler, dev->data);
	if (res != NRFX_SUCCESS) {
		return -EIO;
	}

	return exit_dpd(dev);
}

static int qspi_nor_pm_action(const struct device *dev,
			      enum pm_device_action action)
{
	int rc;

	if (pm_device_is_busy(dev)) {
		return -EBUSY;
	}

	qspi_lock(dev);
	qspi_clock_div_change();

	switch (action) {
	case PM_DEVICE_ACTION_SUSPEND:
		rc = qspi_suspend(dev);
		break;

	case PM_DEVICE_ACTION_RESUME:
		rc = qspi_resume(dev);
		break;

	default:
		rc = -ENOTSUP;
	}

	qspi_clock_div_restore();
	qspi_unlock(dev);

	return rc;
}
#endif /* CONFIG_PM_DEVICE */

void z_impl_nrf_qspi_nor_xip_enable(const struct device *dev, bool enable)
{
	struct qspi_nor_data *dev_data = dev->data;

	if (dev_data->xip_enabled == enable) {
		return;
	}

	qspi_acquire(dev);

#if NRF_QSPI_HAS_XIPEN
	nrf_qspi_xip_set(NRF_QSPI, enable);
#endif
	if (enable) {
		(void)nrfx_qspi_activate(false);
	}
	dev_data->xip_enabled = enable;

	qspi_release(dev);
}

#ifdef CONFIG_USERSPACE
#include <zephyr/internal/syscall_handler.h>

void z_vrfy_nrf_qspi_nor_xip_enable(const struct device *dev, bool enable)
{
	K_OOPS(K_SYSCALL_SPECIFIC_DRIVER(dev, K_OBJ_DRIVER_FLASH,
					 &qspi_nor_api));

	z_impl_nrf_qspi_nor_xip_enable(dev, enable);
}

#include <syscalls/nrf_qspi_nor_xip_enable_mrsh.c>
#endif /* CONFIG_USERSPACE */

static struct qspi_nor_data qspi_nor_dev_data = {
#if !defined(CONFIG_PM_DEVICE_RUNTIME) && defined(CONFIG_MULTITHREADING)
	.count = Z_SEM_INITIALIZER(qspi_nor_dev_data.count, 0, K_SEM_MAX_LIMIT),
#endif
#ifdef CONFIG_MULTITHREADING
	.sem = Z_SEM_INITIALIZER(qspi_nor_dev_data.sem, 1, 1),
	.sync = Z_SEM_INITIALIZER(qspi_nor_dev_data.sync, 0, 1),
#endif /* CONFIG_MULTITHREADING */
};

NRF_DT_CHECK_NODE_HAS_PINCTRL_SLEEP(QSPI_NODE);

PINCTRL_DT_DEFINE(QSPI_NODE);

static const struct qspi_nor_config qspi_nor_dev_config = {
	.nrfx_cfg.skip_gpio_cfg = true,
	.nrfx_cfg.skip_psel_cfg = true,
	.pcfg = PINCTRL_DT_DEV_CONFIG_GET(QSPI_NODE),
	.nrfx_cfg.prot_if = {
		.readoc = COND_CODE_1(DT_INST_NODE_HAS_PROP(0, readoc),
			(_CONCAT(NRF_QSPI_READOC_,
				 DT_STRING_UPPER_TOKEN(DT_DRV_INST(0),
						       readoc))),
			(NRF_QSPI_READOC_FASTREAD)),
		.writeoc = COND_CODE_1(DT_INST_NODE_HAS_PROP(0, writeoc),
			(_CONCAT(NRF_QSPI_WRITEOC_,
				 DT_STRING_UPPER_TOKEN(DT_DRV_INST(0),
						       writeoc))),
			(NRF_QSPI_WRITEOC_PP)),
		.addrmode = DT_INST_PROP(0, address_size_32)
			    ? NRF_QSPI_ADDRMODE_32BIT
			    : NRF_QSPI_ADDRMODE_24BIT,
	},
	.nrfx_cfg.phy_if = {
		.sck_freq = INST_0_SCK_CFG,
		.sck_delay = DT_INST_PROP(0, sck_delay),
		.spi_mode = INST_0_SPI_MODE,
	},
	.nrfx_cfg.timeout = CONFIG_NORDIC_QSPI_NOR_TIMEOUT_MS,

	.size = INST_0_BYTES,
	.id = DT_INST_PROP(0, jedec_id),
};

PM_DEVICE_DT_INST_DEFINE(0, qspi_nor_pm_action);

DEVICE_DT_INST_DEFINE(0, qspi_nor_init, PM_DEVICE_DT_INST_GET(0),
		      &qspi_nor_dev_data, &qspi_nor_dev_config,
		      POST_KERNEL, CONFIG_NORDIC_QSPI_NOR_INIT_PRIORITY,
		      &qspi_nor_api);
