Zephyr SPI NOR driver gets wrong flash size

I add printk() in spi_nor_erase() of D:\ncs\v2.3.0\zephyr\drivers\flash\spi_nor.c for printing flash_size. And it shows flash_size=0x80000 in RTT log messages. But flash_size=0x80000 is same as size = <0x400000> defined in custom_nrf52832.dts.

Source code of SPI NOR driver is as follows.

static int spi_nor_erase(const struct device *dev, off_t addr, size_t size)
{
    printk("spi_nor_erase() e\n");
    const size_t flash_size = dev_flash_size(dev);
    int ret = 0;

    /* erase area must be subregion of device */
    if ((addr < 0) || ((size + addr) > flash_size)) {
        printk("addr=0x%lx, size=0x%x, flash_size=0x%x\n", addr, size, flash_size);
        return -ENODEV;
    }

...

}

custom_nrf52832.dts is as follows.

&spi1 {
compatible = "nordic,nrf-spi";
status = "okay";

cs-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
pinctrl-0 = <&spi1_default>;
pinctrl-1 = <&spi1_sleep>;
pinctrl-names = "default", "sleep";

w25q32: w25q32jv@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <8000000>;
jedec-id = [ef 40 16];
size = <0x400000>;
has-dpd;
t-enter-dpd = <3500>;
t-exit-dpd = <3500>;
};
};

RTT debug messages are as follows.

w25q32jv@0 SPI flash testing
0> ==========================
0>
0> Test 1: Flash erase
0> spi_nor_erase() e
0> addr=0xff000, size=0x1000, flash_size=0x80000
0> Flash erase failed! -19

  • /*
     * Copyright (c) 2018 Savoir-Faire Linux.
     * Copyright (c) 2020 Peter Bigot Consulting, LLC
     *
     * This driver is heavily inspired from the spi_flash_w25qxxdv.c SPI NOR driver.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #define DT_DRV_COMPAT jedec_spi_nor
    
    #include <errno.h>
    #include <zephyr/drivers/flash.h>
    #include <zephyr/drivers/spi.h>
    #include <zephyr/init.h>
    #include <string.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/sys_clock.h>
    
    #include "spi_nor.h"
    #include "jesd216.h"
    #include "flash_priv.h"
    
    LOG_MODULE_REGISTER(spi_nor, CONFIG_FLASH_LOG_LEVEL);
    
    /* Device Power Management Notes
     *
     * These flash devices have several modes during operation:
     * * When CSn is asserted (during a SPI operation) the device is
     *   active.
     * * When CSn is deasserted the device enters a standby mode.
     * * Some devices support a Deep Power-Down mode which reduces current
     *   to as little as 0.1% of standby.
     *
     * The power reduction from DPD is sufficient to warrant allowing its
     * use even in cases where Zephyr's device power management is not
     * available.  This is selected through the SPI_NOR_IDLE_IN_DPD
     * Kconfig option.
     *
     * When mapped to the Zephyr Device Power Management states:
     * * PM_DEVICE_STATE_ACTIVE covers both active and standby modes;
     * * PM_DEVICE_STATE_SUSPENDED, and PM_DEVICE_STATE_OFF all correspond to
     *   deep-power-down mode.
     */
    
    #define SPI_NOR_MAX_ADDR_WIDTH 4
    
    #if DT_INST_NODE_HAS_PROP(0, t_enter_dpd)
    #define T_DP_MS ceiling_fraction(DT_INST_PROP(0, t_enter_dpd), NSEC_PER_MSEC)
    #else /* T_ENTER_DPD */
    #define T_DP_MS 0
    #endif /* T_ENTER_DPD */
    #if DT_INST_NODE_HAS_PROP(0, t_exit_dpd)
    #define T_RES1_MS ceiling_fraction(DT_INST_PROP(0, t_exit_dpd), NSEC_PER_MSEC)
    #endif /* T_EXIT_DPD */
    #if DT_INST_NODE_HAS_PROP(0, dpd_wakeup_sequence)
    #define T_DPDD_MS ceiling_fraction(DT_INST_PROP_BY_IDX(0, dpd_wakeup_sequence, 0), NSEC_PER_MSEC)
    #define T_CRDP_MS ceiling_fraction(DT_INST_PROP_BY_IDX(0, dpd_wakeup_sequence, 1), NSEC_PER_MSEC)
    #define T_RDP_MS ceiling_fraction(DT_INST_PROP_BY_IDX(0, dpd_wakeup_sequence, 2), NSEC_PER_MSEC)
    #else /* DPD_WAKEUP_SEQUENCE */
    #define T_DPDD_MS 0
    #endif /* DPD_WAKEUP_SEQUENCE */
    
    /* Build-time data associated with the device. */
    struct spi_nor_config {
    	/* Devicetree SPI configuration */
    	struct spi_dt_spec spi;
    
    	/* Runtime SFDP stores no static configuration. */
    
    #ifndef CONFIG_SPI_NOR_SFDP_RUNTIME
    	/* Size of device in bytes, from size property */
    	uint32_t flash_size;
    
    #ifdef CONFIG_FLASH_PAGE_LAYOUT
    	/* Flash page layout can be determined from devicetree. */
    	struct flash_pages_layout layout;
    #endif /* CONFIG_FLASH_PAGE_LAYOUT */
    
    	/* Expected JEDEC ID, from jedec-id property */
    	uint8_t jedec_id[SPI_NOR_MAX_ID_LEN];
    
    #if defined(CONFIG_SPI_NOR_SFDP_MINIMAL)
    	/* Optional support for entering 32-bit address mode. */
    	uint8_t enter_4byte_addr;
    #endif /* CONFIG_SPI_NOR_SFDP_MINIMAL */
    
    #if defined(CONFIG_SPI_NOR_SFDP_DEVICETREE)
    	/* Length of BFP structure, in 32-bit words. */
    	uint8_t bfp_len;
    
    	/* Pointer to the BFP table as read from the device
    	 * (little-endian stored words), from sfdp-bfp property
    	 */
    	const struct jesd216_bfp *bfp;
    #endif /* CONFIG_SPI_NOR_SFDP_DEVICETREE */
    #endif /* CONFIG_SPI_NOR_SFDP_RUNTIME */
    
    	/* Optional bits in SR to be cleared on startup.
    	 *
    	 * This information cannot be derived from SFDP.
    	 */
    	uint8_t has_lock;
    };
    
    /**
     * struct spi_nor_data - Structure for defining the SPI NOR access
     * @sem: The semaphore to access to the flash
     */
    struct spi_nor_data {
    	struct k_sem sem;
    #if DT_INST_NODE_HAS_PROP(0, has_dpd)
    	/* Low 32-bits of uptime counter at which device last entered
    	 * deep power-down.
    	 */
    	uint32_t ts_enter_dpd;
    #endif
    
    	/* Miscellaneous flags */
    
    	/* If set addressed operations should use 32-bit rather than
    	 * 24-bit addresses.
    	 *
    	 * This is ignored if the access parameter to a command
    	 * explicitly specifies 24-bit or 32-bit addressing.
    	 */
    	bool flag_access_32bit: 1;
    
    	/* Minimal SFDP stores no dynamic configuration.  Runtime and
    	 * devicetree store page size and erase_types; runtime also
    	 * stores flash size and layout.
    	 */
    #ifndef CONFIG_SPI_NOR_SFDP_MINIMAL
    
    	struct jesd216_erase_type erase_types[JESD216_NUM_ERASE_TYPES];
    
    	/* Number of bytes per page */
    	uint16_t page_size;
    
    #ifdef CONFIG_SPI_NOR_SFDP_RUNTIME
    	/* Size of flash, in bytes */
    	uint32_t flash_size;
    
    #ifdef CONFIG_FLASH_PAGE_LAYOUT
    	struct flash_pages_layout layout;
    #endif /* CONFIG_FLASH_PAGE_LAYOUT */
    #endif /* CONFIG_SPI_NOR_SFDP_RUNTIME */
    #endif /* CONFIG_SPI_NOR_SFDP_MINIMAL */
    };
    
    #ifdef CONFIG_SPI_NOR_SFDP_MINIMAL
    /* The historically supported erase sizes. */
    static const struct jesd216_erase_type minimal_erase_types[JESD216_NUM_ERASE_TYPES] = {
    	{
    		.cmd = SPI_NOR_CMD_BE,
    		.exp = 16,
    	},
    	{
    		.cmd = SPI_NOR_CMD_SE,
    		.exp = 12,
    	},
    };
    #endif /* CONFIG_SPI_NOR_SFDP_MINIMAL */
    
    static int spi_nor_write_protection_set(const struct device *dev,
    					bool write_protect);
    
    /* Get pointer to array of supported erase types.  Static const for
     * minimal, data for runtime and devicetree.
     */
    static inline const struct jesd216_erase_type *
    dev_erase_types(const struct device *dev)
    {
    #ifdef CONFIG_SPI_NOR_SFDP_MINIMAL
    	return minimal_erase_types;
    #else /* CONFIG_SPI_NOR_SFDP_MINIMAL */
    	const struct spi_nor_data *data = dev->data;
    
    	return data->erase_types;
    #endif /* CONFIG_SPI_NOR_SFDP_MINIMAL */
    }
    
    /* Get the size of the flash device.  Data for runtime, constant for
     * minimal and devicetree.
     */
    static inline uint32_t dev_flash_size(const struct device *dev)
    {
    #ifdef CONFIG_SPI_NOR_SFDP_RUNTIME
    	const struct spi_nor_data *data = dev->data;
    
    	return data->flash_size;
    #else /* CONFIG_SPI_NOR_SFDP_RUNTIME */
    	const struct spi_nor_config *cfg = dev->config;
    
    	return cfg->flash_size;
    #endif /* CONFIG_SPI_NOR_SFDP_RUNTIME */
    }
    
    /* Get the flash device page size.  Constant for minimal, data for
     * runtime and devicetree.
     */
    static inline uint16_t dev_page_size(const struct device *dev)
    {
    #ifdef CONFIG_SPI_NOR_SFDP_MINIMAL
    	return 256;
    #else /* CONFIG_SPI_NOR_SFDP_MINIMAL */
    	const struct spi_nor_data *data = dev->data;
    
    	return data->page_size;
    #endif /* CONFIG_SPI_NOR_SFDP_MINIMAL */
    }
    
    static const struct flash_parameters flash_nor_parameters = {
    	.write_block_size = 1,
    	.erase_value = 0xff,
    };
    
    /* Capture the time at which the device entered deep power-down. */
    static inline void record_entered_dpd(const struct device *const dev)
    {
    #if DT_INST_NODE_HAS_PROP(0, has_dpd)
    	struct spi_nor_data *const driver_data = dev->data;
    
    	driver_data->ts_enter_dpd = k_uptime_get_32();
    #endif
    }
    
    /* Check the current time against the time DPD was entered and delay
     * until it's ok to initiate the DPD exit process.
     */
    static inline void delay_until_exit_dpd_ok(const struct device *const dev)
    {
    #if DT_INST_NODE_HAS_PROP(0, has_dpd)
    	struct spi_nor_data *const driver_data = dev->data;
    	int32_t since = (int32_t)(k_uptime_get_32() - driver_data->ts_enter_dpd);
    
    	/* If the time is negative the 32-bit counter has wrapped,
    	 * which is certainly long enough no further delay is
    	 * required.  Otherwise we have to check whether it's been
    	 * long enough taking into account necessary delays for
    	 * entering and exiting DPD.
    	 */
    	if (since >= 0) {
    		/* Subtract time required for DPD to be reached */
    		since -= T_DP_MS;
    
    		/* Subtract time required in DPD before exit */
    		since -= T_DPDD_MS;
    
    		/* If the adjusted time is negative we have to wait
    		 * until it reaches zero before we can proceed.
    		 */
    		if (since < 0) {
    			k_sleep(K_MSEC((uint32_t)-since));
    		}
    	}
    #endif /* DT_INST_NODE_HAS_PROP(0, has_dpd) */
    }
    
    /* Indicates that an access command includes bytes for the address.
     * If not provided the opcode is not followed by address bytes.
     */
    #define NOR_ACCESS_ADDRESSED BIT(0)
    
    /* Indicates that addressed access uses a 24-bit address regardless of
     * spi_nor_data::flag_32bit_addr.
     */
    #define NOR_ACCESS_24BIT_ADDR BIT(1)
    
    /* Indicates that addressed access uses a 32-bit address regardless of
     * spi_nor_data::flag_32bit_addr.
     */
    #define NOR_ACCESS_32BIT_ADDR BIT(2)
    
    /* Indicates that an access command is performing a write.  If not
     * provided access is a read.
     */
    #define NOR_ACCESS_WRITE BIT(7)
    
    /*
     * @brief Send an SPI command
     *
     * @param dev Device struct
     * @param opcode The command to send
     * @param access flags that determine how the command is constructed.
     *        See NOR_ACCESS_*.
     * @param addr The address to send
     * @param data The buffer to store or read the value
     * @param length The size of the buffer
     * @return 0 on success, negative errno code otherwise
     */
    static int spi_nor_access(const struct device *const dev,
    			  uint8_t opcode, unsigned int access,
    			  off_t addr, void *data, size_t length)
    {
    	const struct spi_nor_config *const driver_cfg = dev->config;
    	struct spi_nor_data *const driver_data = dev->data;
    	bool is_addressed = (access & NOR_ACCESS_ADDRESSED) != 0U;
    	bool is_write = (access & NOR_ACCESS_WRITE) != 0U;
    	uint8_t buf[5] = { 0 };
    	struct spi_buf spi_buf[2] = {
    		{
    			.buf = buf,
    			.len = 1,
    		},
    		{
    			.buf = data,
    			.len = length
    		}
    	};
    
    	buf[0] = opcode;
    	if (is_addressed) {
    		bool access_24bit = (access & NOR_ACCESS_24BIT_ADDR) != 0;
    		bool access_32bit = (access & NOR_ACCESS_32BIT_ADDR) != 0;
    		bool use_32bit = (access_32bit
    				  || (!access_24bit
    				      && driver_data->flag_access_32bit));
    		union {
    			uint32_t u32;
    			uint8_t u8[4];
    		} addr32 = {
    			.u32 = sys_cpu_to_be32(addr),
    		};
    
    		if (use_32bit) {
    			memcpy(&buf[1], &addr32.u8[0], 4);
    			spi_buf[0].len += 4;
    		} else {
    			memcpy(&buf[1], &addr32.u8[1], 3);
    			spi_buf[0].len += 3;
    		}
    	};
    
    	const struct spi_buf_set tx_set = {
    		.buffers = spi_buf,
    		.count = (length != 0) ? 2 : 1,
    	};
    
    	const struct spi_buf_set rx_set = {
    		.buffers = spi_buf,
    		.count = 2,
    	};
    
    	if (is_write) {
    		return spi_write_dt(&driver_cfg->spi, &tx_set);
    	}
    
    	return spi_transceive_dt(&driver_cfg->spi, &tx_set, &rx_set);
    }
    
    #define spi_nor_cmd_read(dev, opcode, dest, length) \
    	spi_nor_access(dev, opcode, 0, 0, dest, length)
    #define spi_nor_cmd_addr_read(dev, opcode, addr, dest, length) \
    	spi_nor_access(dev, opcode, NOR_ACCESS_ADDRESSED, addr, dest, length)
    #define spi_nor_cmd_write(dev, opcode) \
    	spi_nor_access(dev, opcode, NOR_ACCESS_WRITE, 0, NULL, 0)
    #define spi_nor_cmd_addr_write(dev, opcode, addr, src, length) \
    	spi_nor_access(dev, opcode, NOR_ACCESS_WRITE | NOR_ACCESS_ADDRESSED, \
    		       addr, (void *)src, length)
    
    /**
     * @brief Wait until the flash is ready
     *
     * @note The device must be externally acquired before invoking this
     * function.
     *
     * This function should be invoked after every ERASE, PROGRAM, or
     * WRITE_STATUS operation before continuing.  This allows us to assume
     * that the device is ready to accept new commands at any other point
     * in the code.
     *
     * @param dev The device structure
     * @return 0 on success, negative errno code otherwise
     */
    static int spi_nor_wait_until_ready(const struct device *dev)
    {
    	int ret;
    	uint8_t reg;
    
    	do {
    		ret = spi_nor_cmd_read(dev, SPI_NOR_CMD_RDSR, &reg, sizeof(reg));
    	} while (!ret && (reg & SPI_NOR_WIP_BIT));
    
    	return ret;
    }
    
    #if defined(CONFIG_SPI_NOR_SFDP_RUNTIME) || defined(CONFIG_FLASH_JESD216_API)
    /*
     * @brief Read content from the SFDP hierarchy
     *
     * @note The device must be externally acquired before invoking this
     * function.
     *
     * @param dev Device struct
     * @param addr The address to send
     * @param data The buffer to store or read the value
     * @param length The size of the buffer
     * @return 0 on success, negative errno code otherwise
     */
    static int read_sfdp(const struct device *const dev,
    		     off_t addr, void *data, size_t length)
    {
    	/* READ_SFDP requires a 24-bit address followed by a single
    	 * byte for a wait state.  This is effected by using 32-bit
    	 * address by shifting the 24-bit address up 8 bits.
    	 */
    	return spi_nor_access(dev, JESD216_CMD_READ_SFDP,
    			      NOR_ACCESS_32BIT_ADDR | NOR_ACCESS_ADDRESSED,
    			      addr << 8, data, length);
    }
    #endif /* CONFIG_SPI_NOR_SFDP_RUNTIME */
    
    static int enter_dpd(const struct device *const dev)
    {
    	int ret = 0;
    
    	if (IS_ENABLED(DT_INST_PROP(0, has_dpd))) {
    		ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_DPD);
    		if (ret == 0) {
    			record_entered_dpd(dev);
    		}
    	}
    	return ret;
    }
    
    static int exit_dpd(const struct device *const dev)
    {
    	int ret = 0;
    
    	if (IS_ENABLED(DT_INST_PROP(0, has_dpd))) {
    		delay_until_exit_dpd_ok(dev);
    
    #if DT_INST_NODE_HAS_PROP(0, dpd_wakeup_sequence)
    		/* Assert CSn and wait for tCRDP.
    		 *
    		 * Unfortunately the SPI API doesn't allow us to
    		 * control CSn so fake it by writing a known-supported
    		 * single-byte command, hoping that'll hold the assert
    		 * long enough.  This is highly likely, since the
    		 * duration is usually less than two SPI clock cycles.
    		 */
    		ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_RDID);
    
    		/* Deassert CSn and wait for tRDP */
    		k_sleep(K_MSEC(T_RDP_MS));
    #else /* DPD_WAKEUP_SEQUENCE */
    		ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_RDPD);
    
    		if (ret == 0) {
    #if DT_INST_NODE_HAS_PROP(0, t_exit_dpd)
    			k_sleep(K_MSEC(T_RES1_MS));
    #endif /* T_EXIT_DPD */
    		}
    #endif /* DPD_WAKEUP_SEQUENCE */
    	}
    	return ret;
    }
    
    /* Everything necessary to acquire owning access to the device.
     *
     * This means taking the lock and, if necessary, waking the device
     * from deep power-down mode.
     */
    static void acquire_device(const struct device *dev)
    {
    	if (IS_ENABLED(CONFIG_MULTITHREADING)) {
    		struct spi_nor_data *const driver_data = dev->data;
    
    		k_sem_take(&driver_data->sem, K_FOREVER);
    	}
    
    	if (IS_ENABLED(CONFIG_SPI_NOR_IDLE_IN_DPD)) {
    		exit_dpd(dev);
    	}
    }
    
    /* Everything necessary to release access to the device.
     *
     * This means (optionally) putting the device into deep power-down
     * mode, and releasing the lock.
     */
    static void release_device(const struct device *dev)
    {
    	if (IS_ENABLED(CONFIG_SPI_NOR_IDLE_IN_DPD)) {
    		enter_dpd(dev);
    	}
    
    	if (IS_ENABLED(CONFIG_MULTITHREADING)) {
    		struct spi_nor_data *const driver_data = dev->data;
    
    		k_sem_give(&driver_data->sem);
    	}
    }
    
    /**
     * @brief Read the status register.
     *
     * @note The device must be externally acquired before invoking this
     * function.
     *
     * @param dev Device struct
     *
     * @return the non-negative value of the status register, or an error code.
     */
    static int spi_nor_rdsr(const struct device *dev)
    {
    	uint8_t reg;
    	int ret = spi_nor_cmd_read(dev, SPI_NOR_CMD_RDSR, &reg, sizeof(reg));
    
    	if (ret == 0) {
    		ret = reg;
    	}
    
    	return ret;
    }
    
    /**
     * @brief Write the status register.
     *
     * @note The device must be externally acquired before invoking this
     * function.
     *
     * @param dev Device struct
     * @param sr The new value of the status register
     *
     * @return 0 on success or a negative error code.
     */
    static int spi_nor_wrsr(const struct device *dev,
    			uint8_t sr)
    {
    	int ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_WREN);
    
    	if (ret == 0) {
    		ret = spi_nor_access(dev, SPI_NOR_CMD_WRSR, NOR_ACCESS_WRITE, 0, &sr,
    				     sizeof(sr));
    		spi_nor_wait_until_ready(dev);
    	}
    
    	return ret;
    }
    
    #if DT_INST_NODE_HAS_PROP(0, mxicy_mx25r_power_mode)
    
    /**
     * @brief Read the configuration register.
     *
     * @note The device must be externally acquired before invoking this
     * function.
     *
     * @param dev Device struct
     *
     * @return the non-negative value of the configuration register, or an error code.
     */
    static int mxicy_rdcr(const struct device *dev)
    {
    	uint16_t cr;
    	enum { CMD_RDCR = 0x15 };
    	int ret = spi_nor_cmd_read(dev, CMD_RDCR, &cr, sizeof(cr));
    
    	if (ret < 0) {
    		return ret;
    	}
    
    	return cr;
    }
    
    /**
     * @brief Write the configuration register.
     *
     * @note The device must be externally acquired before invoking this
     * function.
     *
     * @param dev Device struct
     * @param cr  The new value of the configuration register
     *
     * @return 0 on success or a negative error code.
     */
    static int mxicy_wrcr(const struct device *dev,
    			uint16_t cr)
    {
    	/* The configuration register bytes on the Macronix MX25R devices are
    	 * written using the Write Status Register command where the configuration
    	 * register bytes are written as two extra bytes after the status register.
    	 * First read out the current status register to preserve the value.
    	 */
    	int sr = spi_nor_rdsr(dev);
    
    	if (sr < 0) {
    		LOG_ERR("Read status register failed: %d", sr);
    		return sr;
    	}
    
    	int ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_WREN);
    
    	if (ret == 0) {
    		uint8_t data[] = {
    			sr,
    			cr & 0xFF,	/* Configuration register 1 */
    			cr >> 8		/* Configuration register 2 */
    		};
    
    		ret = spi_nor_access(dev, SPI_NOR_CMD_WRSR, NOR_ACCESS_WRITE, 0, data,
    			sizeof(data));
    		spi_nor_wait_until_ready(dev);
    	}
    
    	return ret;
    }
    
    static int mxicy_configure(const struct device *dev, const uint8_t *jedec_id)
    {
    	/* Low-power/high perf mode is second bit in configuration register 2 */
    	enum { LH_SWITCH_BIT = 9 };
    	const uint8_t JEDEC_MACRONIX_ID = 0xc2;
    	const uint8_t JEDEC_MX25R_TYPE_ID = 0x28;
    	int current_cr, new_cr, ret;
    	/* lh_switch enum index:
    	 * 0: Ultra low power
    	 * 1: High performance mode
    	 */
    	const bool use_high_perf = DT_INST_ENUM_IDX(0, mxicy_mx25r_power_mode);
    
    	/* Only supported on Macronix MX25R Ultra Low Power series. */
    	if (jedec_id[0] != JEDEC_MACRONIX_ID || jedec_id[1] != JEDEC_MX25R_TYPE_ID) {
    		LOG_WRN("L/H switch not supported for device id: %02x %02x %02x", jedec_id[0],
    			jedec_id[1], jedec_id[2]);
    		/* Do not return an error here because the flash still functions */
    		return 0;
    	}
    
    	acquire_device(dev);
    
    	/* Read current configuration register */
    
    	ret = mxicy_rdcr(dev);
    	if (ret < 0) {
    		return ret;
    	}
    	current_cr = ret;
    
    	LOG_DBG("Use high performance mode? %d", use_high_perf);
    	new_cr = current_cr;
    	WRITE_BIT(new_cr, LH_SWITCH_BIT, use_high_perf);
    	if (new_cr != current_cr) {
    		ret = mxicy_wrcr(dev, new_cr);
    	} else {
    		ret = 0;
    	}
    
    	if (ret < 0) {
    		LOG_ERR("Enable high performace mode failed: %d", ret);
    	}
    
    	release_device(dev);
    
    	return ret;
    }
    
    #endif /* DT_INST_NODE_HAS_PROP(0, mxicy_mx25r_power_mode) */
    
    static int spi_nor_read(const struct device *dev, off_t addr, void *dest,
    			size_t size)
    {
    	const size_t flash_size = dev_flash_size(dev);
    	int ret;
    
    	/* should be between 0 and flash size */
    	if ((addr < 0) || ((addr + size) > flash_size)) {
    		return -EINVAL;
    	}
    
    	acquire_device(dev);
    
    	ret = spi_nor_cmd_addr_read(dev, SPI_NOR_CMD_READ, addr, dest, size);
    
    	release_device(dev);
    	return ret;
    }
    
    static int spi_nor_write(const struct device *dev, off_t addr,
    			 const void *src,
    			 size_t size)
    {
    	const size_t flash_size = dev_flash_size(dev);
    	const uint16_t page_size = dev_page_size(dev);
    	int ret = 0;
    
    	/* should be between 0 and flash size */
    	if ((addr < 0) || ((size + addr) > flash_size)) {
    		return -EINVAL;
    	}
    
    	acquire_device(dev);
    	ret = spi_nor_write_protection_set(dev, false);
    	if (ret == 0) {
    		while (size > 0) {
    			size_t to_write = size;
    
    			/* Don't write more than a page. */
    			if (to_write >= page_size) {
    				to_write = page_size;
    			}
    
    			/* Don't write across a page boundary */
    			if (((addr + to_write - 1U) / page_size)
    			!= (addr / page_size)) {
    				to_write = page_size - (addr % page_size);
    			}
    
    			spi_nor_cmd_write(dev, SPI_NOR_CMD_WREN);
    			ret = spi_nor_cmd_addr_write(dev, SPI_NOR_CMD_PP, addr,
    						src, to_write);
    			if (ret != 0) {
    				break;
    			}
    
    			size -= to_write;
    			src = (const uint8_t *)src + to_write;
    			addr += to_write;
    
    			spi_nor_wait_until_ready(dev);
    		}
    	}
    
    	int ret2 = spi_nor_write_protection_set(dev, true);
    
    	if (!ret) {
    		ret = ret2;
    	}
    
    	release_device(dev);
    	return ret;
    }
    
    static int spi_nor_erase(const struct device *dev, off_t addr, size_t size)
    {
    	printk("spi_nor_erase() e\n");
    	const size_t flash_size = dev_flash_size(dev);
    	int ret = 0;
    
    	/* erase area must be subregion of device */
    	if ((addr < 0) || ((size + addr) > flash_size)) {
    		printk("addr=0x%lx, size=0x%x, flash_size=0x%x\n", addr, size, flash_size);
    		return -ENODEV;
    	}
    
    	/* address must be sector-aligned */
    	if (!SPI_NOR_IS_SECTOR_ALIGNED(addr)) {
    		return -EINVAL;
    	}
    
    	/* size must be a multiple of sectors */
    	if ((size % SPI_NOR_SECTOR_SIZE) != 0) {
    		return -EINVAL;
    	}
    
    	acquire_device(dev);
    	ret = spi_nor_write_protection_set(dev, false);
    
    	while ((size > 0) && (ret == 0)) {
    		spi_nor_cmd_write(dev, SPI_NOR_CMD_WREN);
    
    		if (size == flash_size) {
    			/* chip erase */
    			spi_nor_cmd_write(dev, SPI_NOR_CMD_CE);
    			size -= flash_size;
    		} else {
    			const struct jesd216_erase_type *erase_types =
    				dev_erase_types(dev);
    			const struct jesd216_erase_type *bet = NULL;
    
    			for (uint8_t ei = 0; ei < JESD216_NUM_ERASE_TYPES; ++ei) {
    				const struct jesd216_erase_type *etp =
    					&erase_types[ei];
    
    				if ((etp->exp != 0)
    				    && SPI_NOR_IS_ALIGNED(addr, etp->exp)
    				    && SPI_NOR_IS_ALIGNED(size, etp->exp)
    				    && ((bet == NULL)
    					|| (etp->exp > bet->exp))) {
    					bet = etp;
    				}
    			}
    			if (bet != NULL) {
    				spi_nor_cmd_addr_write(dev, bet->cmd, addr, NULL, 0);
    				addr += BIT(bet->exp);
    				size -= BIT(bet->exp);
    			} else {
    				LOG_DBG("Can't erase %zu at 0x%lx",
    					size, (long)addr);
    				ret = -EINVAL;
    			}
    		}
    
    #ifdef __XCC__
    		/*
    		 * FIXME: remove this hack once XCC is fixed.
    		 *
    		 * Without this volatile return value, XCC would segfault
    		 * compiling this file complaining about failure in CGPREP
    		 * phase.
    		 */
    		volatile int xcc_ret =
    #endif
    		spi_nor_wait_until_ready(dev);
    	}
    
    	int ret2 = spi_nor_write_protection_set(dev, true);
    
    	if (!ret) {
    		ret = ret2;
    	}
    
    	release_device(dev);
    
    	return ret;
    }
    
    /* @note The device must be externally acquired before invoking this
     * function.
     */
    static int spi_nor_write_protection_set(const struct device *dev,
    					bool write_protect)
    {
    	int ret;
    
    	ret = spi_nor_cmd_write(dev, (write_protect) ?
    	      SPI_NOR_CMD_WRDI : SPI_NOR_CMD_WREN);
    
    	if (IS_ENABLED(DT_INST_PROP(0, requires_ulbpr))
    	    && (ret == 0)
    	    && !write_protect) {
    		ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_ULBPR);
    	}
    
    	return ret;
    }
    
    #if defined(CONFIG_FLASH_JESD216_API)
    
    static int spi_nor_sfdp_read(const struct device *dev, off_t addr,
    			     void *dest, size_t size)
    {
    	acquire_device(dev);
    
    	int ret = read_sfdp(dev, addr, dest, size);
    
    	release_device(dev);
    
    	return ret;
    }
    
    #endif /* CONFIG_FLASH_JESD216_API */
    
    static int spi_nor_read_jedec_id(const struct device *dev,
    				 uint8_t *id)
    {
    	if (id == NULL) {
    		return -EINVAL;
    	}
    
    	acquire_device(dev);
    
    	int ret = spi_nor_cmd_read(dev, SPI_NOR_CMD_RDID, id, SPI_NOR_MAX_ID_LEN);
    
    	release_device(dev);
    
    	return ret;
    }
    
    /* Put the device into the appropriate address mode, if supported.
     *
     * On successful return spi_nor_data::flag_access_32bit has been set
     * (cleared) if the device is configured for 4-byte (3-byte) addresses
     * for read, write, and erase commands.
     *
     * @param dev the device
     *
     * @param enter_4byte_addr the Enter 4-Byte Addressing bit set from
     * DW16 of SFDP BFP.  A value of all zeros or all ones is interpreted
     * as "not supported".
     *
     * @retval -ENOTSUP if 4-byte addressing is supported but not in a way
     * that the driver can handle.
     * @retval negative codes if the attempt was made and failed
     * @retval 0 if the device is successfully left in 24-bit mode or
     *         reconfigured to 32-bit mode.
     */
    static int spi_nor_set_address_mode(const struct device *dev,
    				    uint8_t enter_4byte_addr)
    {
    	int ret = 0;
    
    	/* Do nothing if not provided (either no bits or all bits
    	 * set).
    	 */
    	if ((enter_4byte_addr == 0)
    	    || (enter_4byte_addr == 0xff)) {
    		return 0;
    	}
    
    	LOG_DBG("Checking enter-4byte-addr %02x", enter_4byte_addr);
    
    	/* This currently only supports command 0xB7 (Enter 4-Byte
    	 * Address Mode), with or without preceding WREN.
    	 */
    	if ((enter_4byte_addr & 0x03) == 0) {
    		return -ENOTSUP;
    	}
    
    	acquire_device(dev);
    
    	if ((enter_4byte_addr & 0x02) != 0) {
    		/* Enter after WREN. */
    		ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_WREN);
    	}
    	if (ret == 0) {
    		ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_4BA);
    	}
    
    	if (ret == 0) {
    		struct spi_nor_data *data = dev->data;
    
    		data->flag_access_32bit = true;
    	}
    
    	release_device(dev);
    
    	return ret;
    }
    
    #ifndef CONFIG_SPI_NOR_SFDP_MINIMAL
    
    static int spi_nor_process_bfp(const struct device *dev,
    			       const struct jesd216_param_header *php,
    			       const struct jesd216_bfp *bfp)
    {
    	struct spi_nor_data *data = dev->data;
    	struct jesd216_erase_type *etp = data->erase_types;
    	const size_t flash_size = jesd216_bfp_density(bfp) / 8U;
    
    	LOG_INF("%s: %u MiBy flash", dev->name, (uint32_t)(flash_size >> 20));
    
    	/* Copy over the erase types, preserving their order.  (The
    	 * Sector Map Parameter table references them by index.)
    	 */
    	memset(data->erase_types, 0, sizeof(data->erase_types));
    	for (uint8_t ti = 1; ti <= ARRAY_SIZE(data->erase_types); ++ti) {
    		if (jesd216_bfp_erase(bfp, ti, etp) == 0) {
    			LOG_DBG("Erase %u with %02x", (uint32_t)BIT(etp->exp), etp->cmd);
    		}
    		++etp;
    	}
    
    	data->page_size = jesd216_bfp_page_size(php, bfp);
    #ifdef CONFIG_SPI_NOR_SFDP_RUNTIME
    	data->flash_size = flash_size;
    #else /* CONFIG_SPI_NOR_SFDP_RUNTIME */
    	if (flash_size != dev_flash_size(dev)) {
    		LOG_ERR("BFP flash size mismatch with devicetree");
    		return -EINVAL;
    	}
    #endif /* CONFIG_SPI_NOR_SFDP_RUNTIME */
    
    	LOG_DBG("Page size %u bytes", data->page_size);
    
    	/* If 4-byte addressing is supported, switch to it. */
    	if (jesd216_bfp_addrbytes(bfp) != JESD216_SFDP_BFP_DW1_ADDRBYTES_VAL_3B) {
    		struct jesd216_bfp_dw16 dw16;
    		int rc = 0;
    
    		if (jesd216_bfp_decode_dw16(php, bfp, &dw16) == 0) {
    			rc = spi_nor_set_address_mode(dev, dw16.enter_4ba);
    		}
    
    		if (rc != 0) {
    			LOG_ERR("Unable to enter 4-byte mode: %d\n", rc);
    			return rc;
    		}
    	}
    	return 0;
    }
    
    static int spi_nor_process_sfdp(const struct device *dev)
    {
    	int rc;
    
    #if defined(CONFIG_SPI_NOR_SFDP_RUNTIME)
    	/* For runtime we need to read the SFDP table, identify the
    	 * BFP block, and process it.
    	 */
    	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;
    
    	rc = spi_nor_sfdp_read(dev, 0, u.raw, sizeof(u.raw));
    	if (rc != 0) {
    		LOG_ERR("SFDP read failed: %d", rc);
    		return rc;
    	}
    
    	uint32_t magic = jesd216_sfdp_magic(hp);
    
    	if (magic != JESD216_SFDP_MAGIC) {
    		LOG_ERR("SFDP magic %08x invalid", magic);
    		return -EINVAL;
    	}
    
    	LOG_INF("%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_INF("PH%u: %04x rev %u.%u: %u DW @ %x",
    			(php - hp->phdr), id, php->rev_major, php->rev_minor,
    			php->len_dw, jesd216_param_addr(php));
    
    		if (id == JESD216_SFDP_PARAM_ID_BFP) {
    			union {
    				uint32_t dw[MIN(php->len_dw, 20)];
    				struct jesd216_bfp bfp;
    			} u;
    			const struct jesd216_bfp *bfp = &u.bfp;
    
    			rc = spi_nor_sfdp_read(dev, jesd216_param_addr(php), u.dw, sizeof(u.dw));
    			if (rc == 0) {
    				rc = spi_nor_process_bfp(dev, php, bfp);
    			}
    
    			if (rc != 0) {
    				LOG_INF("SFDP BFP failed: %d", rc);
    				break;
    			}
    		}
    		++php;
    	}
    #elif defined(CONFIG_SPI_NOR_SFDP_DEVICETREE)
    	/* For devicetree we need to synthesize a parameter header and
    	 * process the stored BFP data as if we had read it.
    	 */
    	const struct spi_nor_config *cfg = dev->config;
    	struct jesd216_param_header bfp_hdr = {
    		.len_dw = cfg->bfp_len,
    	};
    
    	rc = spi_nor_process_bfp(dev, &bfp_hdr, cfg->bfp);
    #else
    #error Unhandled SFDP choice
    #endif
    
    	return rc;
    }
    
    #if defined(CONFIG_FLASH_PAGE_LAYOUT)
    static int setup_pages_layout(const struct device *dev)
    {
    	int rv = 0;
    
    #if defined(CONFIG_SPI_NOR_SFDP_RUNTIME)
    	struct spi_nor_data *data = dev->data;
    	const size_t flash_size = dev_flash_size(dev);
    	const uint32_t layout_page_size = CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE;
    	uint8_t exp = 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)
    		    && ((exp == 0) || (etp->exp < exp))) {
    			exp = etp->exp;
    		}
    	}
    
    	if (exp == 0) {
    		return -ENOTSUP;
    	}
    
    	uint32_t erase_size = BIT(exp);
    
    	/* Error if layout page size is not a multiple of smallest
    	 * erase size.
    	 */
    	if ((layout_page_size % erase_size) != 0) {
    		LOG_ERR("layout page %u not compatible with erase size %u",
    			layout_page_size, erase_size);
    		return -EINVAL;
    	}
    
    	/* Warn but accept layout page sizes that leave inaccessible
    	 * space.
    	 */
    	if ((flash_size % layout_page_size) != 0) {
    		LOG_INF("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);
    #elif defined(CONFIG_SPI_NOR_SFDP_DEVICETREE)
    	const struct spi_nor_config *cfg = dev->config;
    	const struct flash_pages_layout *layout = &cfg->layout;
    	const size_t flash_size = dev_flash_size(dev);
    	size_t layout_size = layout->pages_size * layout->pages_count;
    
    	if (flash_size != layout_size) {
    		LOG_ERR("device size %u mismatch %zu * %zu By pages",
    			flash_size, layout->pages_count, layout->pages_size);
    		return -EINVAL;
    	}
    #else /* CONFIG_SPI_NOR_SFDP_RUNTIME */
    #error Unhandled SFDP choice
    #endif /* CONFIG_SPI_NOR_SFDP_RUNTIME */
    
    	return rv;
    }
    #endif /* CONFIG_FLASH_PAGE_LAYOUT */
    #endif /* CONFIG_SPI_NOR_SFDP_MINIMAL */
    
    /**
     * @brief Configure the flash
     *
     * @param dev The flash device structure
     * @param info The flash info structure
     * @return 0 on success, negative errno code otherwise
     */
    static int spi_nor_configure(const struct device *dev)
    {
    	const struct spi_nor_config *cfg = dev->config;
    	uint8_t jedec_id[SPI_NOR_MAX_ID_LEN];
    	int rc;
    
    	/* Validate bus and CS is ready */
    	if (!spi_is_ready_dt(&cfg->spi)) {
    		return -ENODEV;
    	}
    
    	/* After a soft-reset the flash might be in DPD or busy writing/erasing.
    	 * Exit DPD and wait until flash is ready.
    	 */
    	acquire_device(dev);
    	rc = spi_nor_rdsr(dev);
    	if (rc > 0 && (rc & SPI_NOR_WIP_BIT)) {
    		LOG_WRN("Waiting until flash is ready");
    		spi_nor_wait_until_ready(dev);
    	}
    	release_device(dev);
    
    	/* now the spi bus is configured, we can verify SPI
    	 * connectivity by reading the JEDEC ID.
    	 */
    
    	rc = spi_nor_read_jedec_id(dev, jedec_id);
    	if (rc != 0) {
    		LOG_ERR("JEDEC ID read failed: %d", rc);
    		return -ENODEV;
    	}
    
    #ifndef CONFIG_SPI_NOR_SFDP_RUNTIME
    	/* For minimal and devicetree we need to check the JEDEC ID
    	 * against the one from devicetree, to ensure we didn't find a
    	 * device that has different parameters.
    	 */
    
    	if (memcmp(jedec_id, cfg->jedec_id, sizeof(jedec_id)) != 0) {
    		LOG_ERR("Device id %02x %02x %02x does not match config %02x %02x %02x",
    			jedec_id[0], jedec_id[1], jedec_id[2],
    			cfg->jedec_id[0], cfg->jedec_id[1], cfg->jedec_id[2]);
    		return -EINVAL;
    	}
    #endif
    
    	/* Check for block protect bits that need to be cleared.  This
    	 * information cannot be determined from SFDP content, so the
    	 * devicetree node property must be set correctly for any device
    	 * that powers up with block protect enabled.
    	 */
    	if (cfg->has_lock != 0) {
    		acquire_device(dev);
    
    		rc = spi_nor_rdsr(dev);
    
    		/* Only clear if RDSR worked and something's set. */
    		if (rc > 0) {
    			rc = spi_nor_wrsr(dev, rc & ~cfg->has_lock);
    		}
    
    		if (rc != 0) {
    			LOG_ERR("BP clear failed: %d\n", rc);
    			return -ENODEV;
    		}
    
    		release_device(dev);
    	}
    
    #ifdef CONFIG_SPI_NOR_SFDP_MINIMAL
    	/* For minimal we support some overrides from specific
    	 * devicertee properties.
    	 */
    	if (cfg->enter_4byte_addr != 0) {
    		rc = spi_nor_set_address_mode(dev, cfg->enter_4byte_addr);
    
    		if (rc != 0) {
    			LOG_ERR("Unable to enter 4-byte mode: %d\n", rc);
    			return -ENODEV;
    		}
    	}
    
    #else /* CONFIG_SPI_NOR_SFDP_MINIMAL */
    	/* For devicetree and runtime we need to process BFP data and
    	 * set up or validate page layout.
    	 */
    	rc = spi_nor_process_sfdp(dev);
    	if (rc != 0) {
    		LOG_ERR("SFDP read failed: %d", rc);
    		return -ENODEV;
    	}
    
    #if defined(CONFIG_FLASH_PAGE_LAYOUT)
    	rc = setup_pages_layout(dev);
    	if (rc != 0) {
    		LOG_ERR("layout setup failed: %d", rc);
    		return -ENODEV;
    	}
    #endif /* CONFIG_FLASH_PAGE_LAYOUT */
    #endif /* CONFIG_SPI_NOR_SFDP_MINIMAL */
    
    #if DT_INST_NODE_HAS_PROP(0, mxicy_mx25r_power_mode)
    	/* Do not fail init if setting configuration register fails */
    	(void) mxicy_configure(dev, jedec_id);
    #endif /* DT_INST_NODE_HAS_PROP(0, mxicy_mx25r_power_mode) */
    
    	if (IS_ENABLED(CONFIG_SPI_NOR_IDLE_IN_DPD)
    	    && (enter_dpd(dev) != 0)) {
    		return -ENODEV;
    	}
    
    	return 0;
    }
    
    /**
     * @brief Initialize and configure the flash
     *
     * @param name The flash name
     * @return 0 on success, negative errno code otherwise
     */
    static int spi_nor_init(const struct device *dev)
    {
    	if (IS_ENABLED(CONFIG_MULTITHREADING)) {
    		struct spi_nor_data *const driver_data = dev->data;
    
    		k_sem_init(&driver_data->sem, 1, K_SEM_MAX_LIMIT);
    	}
    
    	return spi_nor_configure(dev);
    }
    
    #if defined(CONFIG_FLASH_PAGE_LAYOUT)
    
    static void spi_nor_pages_layout(const struct device *dev,
    				 const struct flash_pages_layout **layout,
    				 size_t *layout_size)
    {
    	/* Data for runtime, const for devicetree and minimal. */
    #ifdef CONFIG_SPI_NOR_SFDP_RUNTIME
    	const struct spi_nor_data *data = dev->data;
    
    	*layout = &data->layout;
    #else /* CONFIG_SPI_NOR_SFDP_RUNTIME */
    	const struct spi_nor_config *cfg = dev->config;
    
    	*layout = &cfg->layout;
    #endif /* CONFIG_SPI_NOR_SFDP_RUNTIME */
    
    	*layout_size = 1;
    }
    
    #endif /* CONFIG_FLASH_PAGE_LAYOUT */
    
    static const struct flash_parameters *
    flash_nor_get_parameters(const struct device *dev)
    {
    	ARG_UNUSED(dev);
    
    	return &flash_nor_parameters;
    }
    
    static const struct flash_driver_api spi_nor_api = {
    	.read = spi_nor_read,
    	.write = spi_nor_write,
    	.erase = spi_nor_erase,
    	.get_parameters = flash_nor_get_parameters,
    #if defined(CONFIG_FLASH_PAGE_LAYOUT)
    	.page_layout = spi_nor_pages_layout,
    #endif
    #if defined(CONFIG_FLASH_JESD216_API)
    	.sfdp_read = spi_nor_sfdp_read,
    	.read_jedec_id = spi_nor_read_jedec_id,
    #endif
    };
    
    #ifndef CONFIG_SPI_NOR_SFDP_RUNTIME
    /* We need to know the size and ID of the configuration data we're
     * using so we can disable the device we see at runtime if it isn't
     * compatible with what we're taking from devicetree or minimal.
     */
    BUILD_ASSERT(DT_INST_NODE_HAS_PROP(0, jedec_id),
    	     "jedec,spi-nor jedec-id required for non-runtime SFDP");
    
    #if defined(CONFIG_FLASH_PAGE_LAYOUT)
    
    /* For devicetree or minimal page layout we need to know the size of
     * the device.  We can't extract it from the raw BFP data, so require
     * it to be present in devicetree.
     */
    BUILD_ASSERT(DT_INST_NODE_HAS_PROP(0, size),
    	     "jedec,spi-nor size required for non-runtime SFDP page layout");
    
    /* instance 0 size in bytes */
    #define INST_0_BYTES (DT_INST_PROP(0, size) / 8)
    
    BUILD_ASSERT(SPI_NOR_IS_SECTOR_ALIGNED(CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE),
    	     "SPI_NOR_FLASH_LAYOUT_PAGE_SIZE must be multiple of 4096");
    
    /* instance 0 page count */
    #define LAYOUT_PAGES_COUNT (INST_0_BYTES / CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE)
    
    BUILD_ASSERT((CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE * LAYOUT_PAGES_COUNT)
    	     == INST_0_BYTES,
    	     "SPI_NOR_FLASH_LAYOUT_PAGE_SIZE incompatible with flash size");
    
    #endif /* CONFIG_FLASH_PAGE_LAYOUT */
    
    #ifdef CONFIG_SPI_NOR_SFDP_DEVICETREE
    BUILD_ASSERT(DT_INST_NODE_HAS_PROP(0, sfdp_bfp),
    	     "jedec,spi-nor sfdp-bfp required for devicetree SFDP");
    
    static const __aligned(4) uint8_t bfp_data_0[] = DT_INST_PROP(0, sfdp_bfp);
    #endif /* CONFIG_SPI_NOR_SFDP_DEVICETREE */
    
    #endif /* CONFIG_SPI_NOR_SFDP_RUNTIME */
    
    #if DT_INST_NODE_HAS_PROP(0, has_lock)
    /* Currently we only know of devices where the BP bits are present in
     * the first byte of the status register.  Complain if that changes.
     */
    BUILD_ASSERT(DT_INST_PROP(0, has_lock) == (DT_INST_PROP(0, has_lock) & 0xFF),
    	     "Need support for lock clear beyond SR1");
    #endif
    
    static const struct spi_nor_config spi_nor_config_0 = {
    	.spi = SPI_DT_SPEC_INST_GET(0, SPI_WORD_SET(8),
    				    CONFIG_SPI_NOR_CS_WAIT_DELAY),
    #if !defined(CONFIG_SPI_NOR_SFDP_RUNTIME)
    
    #if defined(CONFIG_FLASH_PAGE_LAYOUT)
    	.layout = {
    		.pages_count = LAYOUT_PAGES_COUNT,
    		.pages_size = CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE,
    	},
    #undef LAYOUT_PAGES_COUNT
    #endif /* CONFIG_FLASH_PAGE_LAYOUT */
    
    	.flash_size = DT_INST_PROP(0, size) / 8,
    	.jedec_id = DT_INST_PROP(0, jedec_id),
    
    #if DT_INST_NODE_HAS_PROP(0, has_lock)
    	.has_lock = DT_INST_PROP(0, has_lock),
    #endif
    #if defined(CONFIG_SPI_NOR_SFDP_MINIMAL)		\
    	&& DT_INST_NODE_HAS_PROP(0, enter_4byte_addr)
    	.enter_4byte_addr = DT_INST_PROP(0, enter_4byte_addr),
    #endif
    #ifdef CONFIG_SPI_NOR_SFDP_DEVICETREE
    	.bfp_len = sizeof(bfp_data_0) / 4,
    	.bfp = (const struct jesd216_bfp *)bfp_data_0,
    #endif /* CONFIG_SPI_NOR_SFDP_DEVICETREE */
    
    #endif /* CONFIG_SPI_NOR_SFDP_RUNTIME */
    };
    
    static struct spi_nor_data spi_nor_data_0;
    
    DEVICE_DT_INST_DEFINE(0, &spi_nor_init, NULL,
    		 &spi_nor_data_0, &spi_nor_config_0,
    		 POST_KERNEL, CONFIG_SPI_NOR_INIT_PRIORITY,
    		 &spi_nor_api);
    

  • /*
     * Copyright (c) 2016 Intel Corporation.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    #include <stdio.h>
    #include <string.h>
    #include <zephyr/kernel.h>
    #include <zephyr/drivers/flash.h>
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/sys/printk.h>
    
    
    #if defined(CONFIG_BOARD_ADAFRUIT_FEATHER_STM32F405)
    #define SPI_FLASH_TEST_REGION_OFFSET 0xf000
    #elif defined(CONFIG_BOARD_ARTY_A7_ARM_DESIGNSTART_M1) || \
        defined(CONFIG_BOARD_ARTY_A7_ARM_DESIGNSTART_M3)
    /* The FPGA bitstream is stored in the lower 536 sectors of the flash. */
    #define SPI_FLASH_TEST_REGION_OFFSET \
        DT_REG_SIZE(DT_NODE_BY_FIXED_PARTITION_LABEL(fpga_bitstream))
    #elif defined(CONFIG_BOARD_NPCX9M6F_EVB) || \
        defined(CONFIG_BOARD_NPCX7M6FB_EVB)
    #define SPI_FLASH_TEST_REGION_OFFSET 0x7F000
    #else
    #define SPI_FLASH_TEST_REGION_OFFSET 0xff000
    #endif
    #define SPI_FLASH_SECTOR_SIZE        4096
    
    int  flashNorInit(void)
    {
        const struct device *flash_dev;
        int rc;
    
        flash_dev = DEVICE_DT_GET(DT_ALIAS(spi1flash0));
    
        if (!device_is_ready(flash_dev)) {
            printk("%s: device not ready.\n", flash_dev->name);
            return -1;
        }
    
        printk("\n%s SPI flash testing\n", flash_dev->name);
        printk("==========================\n");
    
    
        /* Write protection needs to be disabled before each write or
         * erase, since the flash component turns on write protection
         * automatically after completion of write and erase
         * operations.
         */
        printk  ("\nTest 1: Flash erase\n");
    
        /* Full flash erase if SPI_FLASH_TEST_REGION_OFFSET = 0 and
         * SPI_FLASH_SECTOR_SIZE = flash size
         */
        rc = flash_erase(flash_dev, SPI_FLASH_TEST_REGION_OFFSET,
                 SPI_FLASH_SECTOR_SIZE);
        if (rc != 0) {
            printk("Flash erase failed! %d\n", rc);
        } else {
            printk("Flash erase succeeded!\n");
        }
    
        return 0;
    }
    

Related