/* * Copyright 2023 NXP * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nxp_s32_qspi_nor #include LOG_MODULE_REGISTER(nxp_s32_qspi_nor, CONFIG_FLASH_LOG_LEVEL); #include #include #include #include #include "spi_nor.h" #include "jesd216.h" #include "memc_nxp_s32_qspi.h" #define QSPI_INST_NODE_HAS_PROP_EQ_AND_OR(n, prop, val) \ COND_CODE_1(DT_INST_NODE_HAS_PROP(n, prop), \ (IS_EQ(DT_INST_ENUM_IDX(n, prop), val)), \ (0)) || #define QSPI_ANY_INST_HAS_PROP_EQ(prop, val) \ (DT_INST_FOREACH_STATUS_OKAY_VARGS(QSPI_INST_NODE_HAS_PROP_EQ_AND_OR, prop, val) 0) #define QSPI_INST_NODE_NOT_HAS_PROP_AND_OR(n, prop) \ !DT_INST_NODE_HAS_PROP(n, prop) || #define QSPI_ANY_INST_HAS_PROP_STATUS_NOT_OKAY(prop) \ (DT_INST_FOREACH_STATUS_OKAY_VARGS(QSPI_INST_NODE_NOT_HAS_PROP_AND_OR, prop) 0) #define QSPI_QER_TYPE(n) \ _CONCAT(JESD216_DW15_QER_VAL_, \ DT_INST_STRING_TOKEN_OR(n, quad_enable_requirements, S1B6)) #define QSPI_HAS_QUAD_MODE(n) \ (QSPI_INST_NODE_HAS_PROP_EQ_AND_OR(n, readoc, 3) \ QSPI_INST_NODE_HAS_PROP_EQ_AND_OR(n, readoc, 4) \ QSPI_INST_NODE_HAS_PROP_EQ_AND_OR(n, writeoc, 2) \ QSPI_INST_NODE_HAS_PROP_EQ_AND_OR(n, writeoc, 3) \ 0) #define QSPI_WRITE_SEQ(n) \ COND_CODE_1(DT_INST_NODE_HAS_PROP(n, writeoc), \ (_CONCAT(QSPI_SEQ_PP_, DT_INST_STRING_UPPER_TOKEN(n, writeoc))),\ (QSPI_SEQ_PP_1_1_1)) #define QSPI_READ_SEQ(n) \ COND_CODE_1(DT_INST_NODE_HAS_PROP(n, readoc), \ (_CONCAT(QSPI_SEQ_READ_, DT_INST_STRING_UPPER_TOKEN(n, readoc))),\ (QSPI_SEQ_READ_1_1_1)) #define QSPI_ERASE_VALUE 0xff #define QSPI_WRITE_BLOCK_SIZE 1U #define QSPI_IS_ALIGNED(addr, bits) (((addr) & BIT_MASK(bits)) == 0) #define QSPI_LUT_ENTRY_SIZE (FEATURE_QSPI_LUT_SEQUENCE_SIZE * 2) #define QSPI_LUT_IDX(n) (n * QSPI_LUT_ENTRY_SIZE) #if defined(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME) /* Size of LUT */ #define QSPI_SFDP_LUT_SIZE 130U /* Size of init operations */ #define QSPI_SFDP_INIT_OP_SIZE 8U #if defined(CONFIG_FLASH_JESD216_API) /* Size of all LUT sequences for JESD216 operations */ #define QSPI_JESD216_SEQ_SIZE 8U #endif /* CONFIG_FLASH_JESD216_API */ #endif /* CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME */ struct nxp_s32_qspi_config { const struct device *controller; struct flash_parameters flash_parameters; #if defined(CONFIG_FLASH_PAGE_LAYOUT) struct flash_pages_layout layout; #endif #if !defined(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME) const Qspi_Ip_MemoryConfigType memory_cfg; enum jesd216_dw15_qer_type qer_type; bool quad_mode; #endif }; struct nxp_s32_qspi_data { uint8_t instance; Qspi_Ip_MemoryConnectionType memory_conn_cfg; uint8_t read_sfdp_lut_idx; #if defined(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME) Qspi_Ip_MemoryConfigType memory_cfg; Qspi_Ip_InstrOpType lut_ops[QSPI_SFDP_LUT_SIZE]; Qspi_Ip_InitOperationType init_ops[QSPI_SFDP_INIT_OP_SIZE]; #endif #if defined(CONFIG_MULTITHREADING) struct k_sem sem; #endif }; enum { QSPI_SEQ_RDSR, QSPI_SEQ_RDSR2, QSPI_SEQ_WRSR, QSPI_SEQ_WRSR2, QSPI_SEQ_WREN, QSPI_SEQ_RESET, QSPI_SEQ_SE, #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(has_32k_erase) QSPI_SEQ_BE_32K, #endif QSPI_SEQ_BE, QSPI_SEQ_CE, QSPI_SEQ_READ_SFDP, QSPI_SEQ_RDID, #if QSPI_ANY_INST_HAS_PROP_EQ(readoc, 0) || QSPI_ANY_INST_HAS_PROP_STATUS_NOT_OKAY(readoc) QSPI_SEQ_READ_1_1_1, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(readoc, 1) QSPI_SEQ_READ_1_1_2, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(readoc, 2) QSPI_SEQ_READ_1_2_2, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(readoc, 3) QSPI_SEQ_READ_1_1_4, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(readoc, 4) QSPI_SEQ_READ_1_4_4, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(writeoc, 0) || QSPI_ANY_INST_HAS_PROP_STATUS_NOT_OKAY(writeoc) QSPI_SEQ_PP_1_1_1, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(writeoc, 1) QSPI_SEQ_PP_1_1_2, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(writeoc, 2) QSPI_SEQ_PP_1_1_4, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(writeoc, 3) QSPI_SEQ_PP_1_4_4, #endif }; #if !defined(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME) static const Qspi_Ip_InstrOpType nxp_s32_qspi_lut[][QSPI_LUT_ENTRY_SIZE] = { [QSPI_SEQ_RDSR] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_RDSR), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_READ, QSPI_IP_LUT_PADS_1, 1U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, [QSPI_SEQ_RDSR2] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_RDSR2), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_READ, QSPI_IP_LUT_PADS_1, 1U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, [QSPI_SEQ_WRSR] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_WRSR), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_WRITE, QSPI_IP_LUT_PADS_1, 1U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, [QSPI_SEQ_WRSR2] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_WRSR2), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_WRITE, QSPI_IP_LUT_PADS_1, 1U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, [QSPI_SEQ_WREN] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_WREN), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, [QSPI_SEQ_RESET] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_RESET_EN), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_PADS_1, 0U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_RESET_MEM), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_PADS_1, 0U), }, [QSPI_SEQ_SE] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_SE), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_ADDR, QSPI_IP_LUT_PADS_1, 24U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(has_32k_erase) [QSPI_SEQ_BE_32K] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_BE_32K), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_ADDR, QSPI_IP_LUT_PADS_1, 24U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, #endif [QSPI_SEQ_BE] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_BE), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_ADDR, QSPI_IP_LUT_PADS_1, 24U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, [QSPI_SEQ_CE] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_CE), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, [QSPI_SEQ_READ_SFDP] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, JESD216_CMD_READ_SFDP), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_ADDR, QSPI_IP_LUT_PADS_1, 24U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_DUMMY, QSPI_IP_LUT_PADS_1, 8U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_READ, QSPI_IP_LUT_PADS_1, 16U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, [QSPI_SEQ_RDID] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, JESD216_CMD_READ_ID), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_READ, QSPI_IP_LUT_PADS_1, JESD216_READ_ID_LEN), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, #if QSPI_ANY_INST_HAS_PROP_EQ(readoc, 0) || QSPI_ANY_INST_HAS_PROP_STATUS_NOT_OKAY(readoc) [QSPI_SEQ_READ_1_1_1] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_READ_FAST), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_ADDR, QSPI_IP_LUT_PADS_1, 24U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_DUMMY, QSPI_IP_LUT_PADS_1, 8U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_READ, QSPI_IP_LUT_PADS_1, 8U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(readoc, 1) [QSPI_SEQ_READ_1_1_2] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_DREAD), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_ADDR, QSPI_IP_LUT_PADS_1, 24U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_DUMMY, QSPI_IP_LUT_PADS_1, 8U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_READ, QSPI_IP_LUT_PADS_2, 8U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(readoc, 2) [QSPI_SEQ_READ_1_2_2] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_2READ), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_ADDR, QSPI_IP_LUT_PADS_2, 24U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_DUMMY, QSPI_IP_LUT_PADS_2, 4U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_READ, QSPI_IP_LUT_PADS_2, 8U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(readoc, 3) [QSPI_SEQ_READ_1_1_4] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_QREAD), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_ADDR, QSPI_IP_LUT_PADS_1, 24U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_DUMMY, QSPI_IP_LUT_PADS_1, 8U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_READ, QSPI_IP_LUT_PADS_4, 8U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(readoc, 4) [QSPI_SEQ_READ_1_4_4] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_4READ), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_ADDR, QSPI_IP_LUT_PADS_4, 24U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_MODE, QSPI_IP_LUT_PADS_4, 0U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_DUMMY, QSPI_IP_LUT_PADS_4, 4U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_READ, QSPI_IP_LUT_PADS_4, 8U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(writeoc, 0) || QSPI_ANY_INST_HAS_PROP_STATUS_NOT_OKAY(writeoc) [QSPI_SEQ_PP_1_1_1] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_PP), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_ADDR, QSPI_IP_LUT_PADS_1, 24U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_WRITE, QSPI_IP_LUT_PADS_1, 8U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(writeoc, 1) [QSPI_SEQ_PP_1_1_2] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_PP_1_1_2), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_ADDR, QSPI_IP_LUT_PADS_1, 24U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_WRITE, QSPI_IP_LUT_PADS_2, 8U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(writeoc, 2) [QSPI_SEQ_PP_1_1_4] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_PP_1_1_4), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_ADDR, QSPI_IP_LUT_PADS_1, 24U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_WRITE, QSPI_IP_LUT_PADS_4, 8U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, #endif #if QSPI_ANY_INST_HAS_PROP_EQ(writeoc, 3) [QSPI_SEQ_PP_1_4_4] = { QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, SPI_NOR_CMD_PP_1_4_4), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_ADDR, QSPI_IP_LUT_PADS_4, 24U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_WRITE, QSPI_IP_LUT_PADS_4, 16U), QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END), }, #endif }; #endif /* !defined(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME) */ static ALWAYS_INLINE Qspi_Ip_MemoryConfigType *get_memory_config(const struct device *dev) { #if defined(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME) return &((struct nxp_s32_qspi_data *)dev->data)->memory_cfg; #else return ((Qspi_Ip_MemoryConfigType *) &((const struct nxp_s32_qspi_config *)dev->config)->memory_cfg); #endif } static ALWAYS_INLINE bool area_is_subregion(const struct device *dev, off_t offset, size_t size) { Qspi_Ip_MemoryConfigType *memory_cfg = get_memory_config(dev); return ((offset >= 0) && (offset < memory_cfg->memSize) && ((size + offset) <= memory_cfg->memSize)); } static inline void nxp_s32_qspi_lock(const struct device *dev) { #ifdef CONFIG_MULTITHREADING struct nxp_s32_qspi_data *data = dev->data; k_sem_take(&data->sem, K_FOREVER); #else ARG_UNUSED(dev); #endif } static inline void nxp_s32_qspi_unlock(const struct device *dev) { #ifdef CONFIG_MULTITHREADING struct nxp_s32_qspi_data *data = dev->data; k_sem_give(&data->sem); #else ARG_UNUSED(dev); #endif } /* Must be called with lock */ static int nxp_s32_qspi_wait_until_ready(const struct device *dev) { struct nxp_s32_qspi_data *data = dev->data; Qspi_Ip_StatusType status; uint32_t timeout = 0xFFFFFF; int ret = 0; do { status = Qspi_Ip_GetMemoryStatus(data->instance); timeout--; } while ((status == STATUS_QSPI_IP_BUSY) && (timeout > 0)); if (status != STATUS_QSPI_IP_SUCCESS) { LOG_ERR("Failed to read memory status (%d)", status); ret = -EIO; } else if (timeout <= 0) { LOG_ERR("Timeout, memory is busy"); ret = -ETIMEDOUT; } return ret; } #if !defined(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME) static int nxp_s32_qspi_read_status_register(const struct device *dev, uint8_t reg_num, uint8_t *val) { struct nxp_s32_qspi_data *data = dev->data; uint16_t lut_idx; Qspi_Ip_StatusType status; int ret = 0; switch (reg_num) { case 1U: lut_idx = QSPI_LUT_IDX(QSPI_SEQ_RDSR); break; case 2U: lut_idx = QSPI_LUT_IDX(QSPI_SEQ_RDSR2); break; default: LOG_ERR("Reading SR%u is not supported", reg_num); return -EINVAL; } nxp_s32_qspi_lock(dev); status = Qspi_Ip_RunReadCommand(data->instance, lut_idx, 0U, val, NULL, sizeof(*val)); if (status != STATUS_QSPI_IP_SUCCESS) { LOG_ERR("Failed to read SR%u (%d)", reg_num, status); ret = -EIO; } nxp_s32_qspi_unlock(dev); return ret; } static int nxp_s32_qspi_write_enable(const struct device *dev) { struct nxp_s32_qspi_data *data = dev->data; Qspi_Ip_MemoryConfigType *memory_cfg = get_memory_config(dev); Qspi_Ip_StatusType status; int ret = 0; nxp_s32_qspi_lock(dev); status = Qspi_Ip_RunCommand(data->instance, memory_cfg->statusConfig.writeEnableSRLut, 0U); if (status != STATUS_QSPI_IP_SUCCESS) { LOG_ERR("Failed to enable SR write (%d)", status); ret = -EIO; } nxp_s32_qspi_unlock(dev); return ret; } static int nxp_s32_qspi_write_status_register(const struct device *dev, uint8_t reg_num, uint8_t val) { const struct nxp_s32_qspi_config *config = dev->config; struct nxp_s32_qspi_data *data = dev->data; Qspi_Ip_StatusType status; uint8_t buf[2] = { 0 }; uint16_t lut_idx; size_t size; int ret; if (reg_num == 1) { /* buf = [val] or [val, SR2] */ lut_idx = QSPI_LUT_IDX(QSPI_SEQ_WRSR); size = 1U; buf[0] = val; if (config->qer_type == JESD216_DW15_QER_S2B1v1) { /* Writing SR1 clears SR2 */ size = 2U; ret = nxp_s32_qspi_read_status_register(dev, 2, &buf[1]); if (ret < 0) { return ret; } } } else if (reg_num == 2) { /* buf = [val] or [SR1, val] */ if ((config->qer_type == JESD216_DW15_QER_VAL_S2B1v1) || (config->qer_type == JESD216_DW15_QER_VAL_S2B1v4) || (config->qer_type == JESD216_DW15_QER_VAL_S2B1v5)) { /* Writing SR2 requires writing SR1 as well */ lut_idx = QSPI_LUT_IDX(QSPI_SEQ_WRSR); size = 2U; buf[1] = val; ret = nxp_s32_qspi_read_status_register(dev, 1, &buf[0]); if (ret < 0) { return ret; } } else { lut_idx = QSPI_LUT_IDX(QSPI_SEQ_WRSR2); size = 1U; buf[0] = val; } } else { return -EINVAL; } nxp_s32_qspi_lock(dev); status = Qspi_Ip_RunWriteCommand(data->instance, lut_idx, 0U, (const uint8_t *)buf, (uint32_t)size); if (status == STATUS_QSPI_IP_SUCCESS) { /* Wait for the write command to complete */ ret = nxp_s32_qspi_wait_until_ready(dev); } else { LOG_ERR("Failed to write to SR%u (%d)", reg_num, status); ret = -EIO; } nxp_s32_qspi_unlock(dev); return ret; } static int nxp_s32_qspi_set_quad_mode(const struct device *dev, bool enabled) { const struct nxp_s32_qspi_config *config = dev->config; uint8_t sr_num; uint8_t sr_val; uint8_t qe_mask; bool qe_state; int ret; switch (config->qer_type) { case JESD216_DW15_QER_NONE: /* no QE bit, device detects reads based on opcode */ return 0; case JESD216_DW15_QER_S1B6: sr_num = 1U; qe_mask = BIT(6U); break; case JESD216_DW15_QER_S2B7: sr_num = 2U; qe_mask = 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: sr_num = 2U; qe_mask = BIT(1U); break; default: return -ENOTSUP; } ret = nxp_s32_qspi_read_status_register(dev, sr_num, &sr_val); if (ret < 0) { return ret; } qe_state = ((sr_val & qe_mask) != 0U); if (qe_state == enabled) { return 0; } sr_val ^= qe_mask; ret = nxp_s32_qspi_write_enable(dev); if (ret < 0) { return ret; } ret = nxp_s32_qspi_write_status_register(dev, sr_num, sr_val); if (ret < 0) { return ret; } /* Verify write was successful */ ret = nxp_s32_qspi_read_status_register(dev, sr_num, &sr_val); if (ret < 0) { return ret; } qe_state = ((sr_val & qe_mask) != 0U); if (qe_state != enabled) { LOG_ERR("Failed to %s Quad mode", enabled ? "enable" : "disable"); return -EIO; } return ret; } #endif /* !defined(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME) */ static int nxp_s32_qspi_read(const struct device *dev, off_t offset, void *dest, size_t size) { struct nxp_s32_qspi_data *data = dev->data; Qspi_Ip_StatusType status; int ret = 0; if (!dest) { return -EINVAL; } if (!area_is_subregion(dev, offset, size)) { return -ENODEV; } if (size) { nxp_s32_qspi_lock(dev); status = Qspi_Ip_Read(data->instance, (uint32_t)offset, (uint8_t *)dest, (uint32_t)size); if (status != STATUS_QSPI_IP_SUCCESS) { LOG_ERR("Failed to read %zu bytes at 0x%lx (%d)", size, offset, status); ret = -EIO; } nxp_s32_qspi_unlock(dev); } return ret; } static int nxp_s32_qspi_write(const struct device *dev, off_t offset, const void *src, size_t size) { struct nxp_s32_qspi_data *data = dev->data; Qspi_Ip_MemoryConfigType *memory_cfg = get_memory_config(dev); Qspi_Ip_StatusType status; size_t max_write = (size_t)MIN(QSPI_IP_MAX_WRITE_SIZE, memory_cfg->pageSize); size_t len; int ret = 0; if (!src) { return -EINVAL; } if (!area_is_subregion(dev, offset, size)) { return -ENODEV; } nxp_s32_qspi_lock(dev); while (size) { len = MIN(max_write - (offset % max_write), size); status = Qspi_Ip_Program(data->instance, (uint32_t)offset, (const uint8_t *)src, (uint32_t)len); if (status != STATUS_QSPI_IP_SUCCESS) { LOG_ERR("Failed to write %zu bytes at 0x%lx (%d)", len, offset, status); ret = -EIO; break; } ret = nxp_s32_qspi_wait_until_ready(dev); if (ret != 0) { break; } if (IS_ENABLED(CONFIG_FLASH_NXP_S32_QSPI_VERIFY_WRITE)) { status = Qspi_Ip_ProgramVerify(data->instance, (uint32_t)offset, (const uint8_t *)src, (uint32_t)len); if (status != STATUS_QSPI_IP_SUCCESS) { LOG_ERR("Write verification failed at 0x%lx (%d)", offset, status); ret = -EIO; break; } } size -= len; src = (const uint8_t *)src + len; offset += len; } nxp_s32_qspi_unlock(dev); return ret; } static int nxp_s32_qspi_erase_block(const struct device *dev, off_t offset, size_t size, size_t *erase_size) { struct nxp_s32_qspi_data *data = dev->data; Qspi_Ip_MemoryConfigType *memory_cfg = get_memory_config(dev); Qspi_Ip_EraseVarConfigType *etp = NULL; Qspi_Ip_EraseVarConfigType *etp_tmp; Qspi_Ip_StatusType status; int ret = 0; /* * Find the erase type with bigger size that can erase all or part of the * requested memory size */ for (uint8_t i = 0; i < QSPI_IP_ERASE_TYPES; i++) { etp_tmp = (Qspi_Ip_EraseVarConfigType *)&(memory_cfg->eraseSettings.eraseTypes[i]); if ((etp_tmp->eraseLut != QSPI_IP_LUT_INVALID) && QSPI_IS_ALIGNED(offset, etp_tmp->size) && (BIT(etp_tmp->size) <= size) && ((etp == NULL) || (etp_tmp->size > etp->size))) { etp = etp_tmp; } } if (etp != NULL) { *erase_size = BIT(etp->size); status = Qspi_Ip_EraseBlock(data->instance, (uint32_t)offset, *erase_size); if (status != STATUS_QSPI_IP_SUCCESS) { LOG_ERR("Failed to erase %zu bytes at 0x%lx (%d)", *erase_size, (long)offset, status); ret = -EIO; } } else { LOG_ERR("Can't find erase size to erase %zu bytes", size); ret = -EINVAL; } return ret; } static int nxp_s32_qspi_erase(const struct device *dev, off_t offset, size_t size) { struct nxp_s32_qspi_data *data = dev->data; Qspi_Ip_MemoryConfigType *memory_cfg = get_memory_config(dev); Qspi_Ip_StatusType status; size_t erase_size; int ret = 0; if (!area_is_subregion(dev, offset, size)) { return -ENODEV; } nxp_s32_qspi_lock(dev); if (size == memory_cfg->memSize) { status = Qspi_Ip_EraseChip(data->instance); if (status != STATUS_QSPI_IP_SUCCESS) { LOG_ERR("Failed to erase chip (%d)", status); ret = -EIO; } } else { while (size > 0) { erase_size = 0; ret = nxp_s32_qspi_erase_block(dev, offset, size, &erase_size); if (ret != 0) { break; } ret = nxp_s32_qspi_wait_until_ready(dev); if (ret != 0) { break; } if (IS_ENABLED(CONFIG_FLASH_NXP_S32_QSPI_VERIFY_ERASE)) { status = Qspi_Ip_EraseVerify(data->instance, (uint32_t)offset, erase_size); if (status != STATUS_QSPI_IP_SUCCESS) { LOG_ERR("Erase verification failed at 0x%lx (%d)", offset, status); ret = -EIO; break; } } offset += erase_size; size -= erase_size; } } nxp_s32_qspi_unlock(dev); return ret; } #if defined(CONFIG_FLASH_JESD216_API) || !defined(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME) static int nxp_s32_qspi_read_id(const struct device *dev, uint8_t *id) { struct nxp_s32_qspi_data *data = dev->data; Qspi_Ip_StatusType status; int ret = 0; nxp_s32_qspi_lock(dev); status = Qspi_Ip_ReadId(data->instance, id); if (status != STATUS_QSPI_IP_SUCCESS) { LOG_ERR("Failed to read device ID (%d)", status); ret = -EIO; } nxp_s32_qspi_unlock(dev); return ret; } #endif /* CONFIG_FLASH_JESD216_API || !CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME */ #if defined(CONFIG_FLASH_JESD216_API) static int nxp_s32_qspi_sfdp_read(const struct device *dev, off_t offset, void *buf, size_t len) { struct nxp_s32_qspi_data *data = dev->data; Qspi_Ip_StatusType status; int ret = 0; nxp_s32_qspi_lock(dev); status = Qspi_Ip_RunReadCommand(data->instance, data->read_sfdp_lut_idx, (uint32_t)offset, (uint8_t *)buf, NULL, (uint32_t)len); if (status != STATUS_QSPI_IP_SUCCESS) { LOG_ERR("Failed to read SFDP at 0x%lx (%d)", offset, status); ret = -EIO; } nxp_s32_qspi_unlock(dev); return ret; } #endif /* CONFIG_FLASH_JESD216_API */ #if defined(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME) static int nxp_s32_qspi_sfdp_config(const struct device *dev) { struct nxp_s32_qspi_data *data = dev->data; Qspi_Ip_MemoryConfigType *memory_cfg = get_memory_config(dev); Qspi_Ip_StatusType status; /* Populate memory configuration with values obtained from SFDP */ memory_cfg->memType = QSPI_IP_SERIAL_FLASH; memory_cfg->lutSequences.opCount = QSPI_SFDP_LUT_SIZE; memory_cfg->lutSequences.lutOps = (Qspi_Ip_InstrOpType *)data->lut_ops; memory_cfg->initConfiguration.opCount = QSPI_SFDP_INIT_OP_SIZE; memory_cfg->initConfiguration.operations = (Qspi_Ip_InitOperationType *)data->init_ops; status = Qspi_Ip_ReadSfdp(memory_cfg, &data->memory_conn_cfg); if (status != STATUS_QSPI_IP_SUCCESS) { LOG_ERR("Fail to read SFDP (%d)", status); return -EIO; } #if defined(CONFIG_FLASH_JESD216_API) /* The HAL does not populate LUTs for read SFDP and read ID */ uint8_t lut_idx = QSPI_SFDP_LUT_SIZE; for (int i = 0; i < QSPI_SFDP_LUT_SIZE - 1; i++) { if ((data->lut_ops[i] == QSPI_IP_LUT_SEQ_END) && (data->lut_ops[i+1] == QSPI_IP_LUT_SEQ_END)) { lut_idx = i + 1; break; } } /* Make sure there's enough space to add the LUT sequences */ if ((lut_idx + QSPI_JESD216_SEQ_SIZE - 1) >= QSPI_SFDP_LUT_SIZE) { return -ENOMEM; } data->read_sfdp_lut_idx = lut_idx; data->lut_ops[lut_idx++] = QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, JESD216_CMD_READ_SFDP); data->lut_ops[lut_idx++] = QSPI_LUT_OP(QSPI_IP_LUT_INSTR_ADDR, QSPI_IP_LUT_PADS_1, 24U); data->lut_ops[lut_idx++] = QSPI_LUT_OP(QSPI_IP_LUT_INSTR_DUMMY, QSPI_IP_LUT_PADS_1, 8U); data->lut_ops[lut_idx++] = QSPI_LUT_OP(QSPI_IP_LUT_INSTR_READ, QSPI_IP_LUT_PADS_1, 16U); data->lut_ops[lut_idx++] = QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END); memory_cfg->readIdSettings.readIdLut = lut_idx; memory_cfg->readIdSettings.readIdSize = JESD216_READ_ID_LEN; data->lut_ops[lut_idx++] = QSPI_LUT_OP(QSPI_IP_LUT_INSTR_CMD, QSPI_IP_LUT_PADS_1, JESD216_CMD_READ_ID); data->lut_ops[lut_idx++] = QSPI_LUT_OP(QSPI_IP_LUT_INSTR_READ, QSPI_IP_LUT_PADS_1, JESD216_READ_ID_LEN); data->lut_ops[lut_idx++] = QSPI_LUT_OP(QSPI_IP_LUT_INSTR_STOP, QSPI_IP_LUT_SEQ_END, QSPI_IP_LUT_SEQ_END); #endif /* CONFIG_FLASH_JESD216_API */ return 0; } #endif static const struct flash_parameters *nxp_s32_qspi_get_parameters(const struct device *dev) { const struct nxp_s32_qspi_config *config = dev->config; return &config->flash_parameters; } #if defined(CONFIG_FLASH_PAGE_LAYOUT) static void nxp_s32_qspi_pages_layout(const struct device *dev, const struct flash_pages_layout **layout, size_t *layout_size) { const struct nxp_s32_qspi_config *config = dev->config; *layout = &config->layout; *layout_size = 1; } #endif /* CONFIG_FLASH_PAGE_LAYOUT */ static int nxp_s32_qspi_init(const struct device *dev) { struct nxp_s32_qspi_data *data = dev->data; const struct nxp_s32_qspi_config *config = dev->config; Qspi_Ip_MemoryConfigType *memory_cfg = get_memory_config(dev); Qspi_Ip_StatusType status; static uint8_t instance_cnt; int ret = 0; /* Used by the HAL to retrieve the internal driver state */ data->instance = instance_cnt++; __ASSERT_NO_MSG(data->instance < QSPI_IP_MEM_INSTANCE_COUNT); data->memory_conn_cfg.qspiInstance = memc_nxp_s32_qspi_get_instance(config->controller); #if defined(CONFIG_MULTITHREADING) k_sem_init(&data->sem, 1, 1); #endif #if defined(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME) nxp_s32_qspi_sfdp_config(dev); #endif /* Init memory device connected to the bus */ status = Qspi_Ip_Init(data->instance, (const Qspi_Ip_MemoryConfigType *)memory_cfg, (const Qspi_Ip_MemoryConnectionType *)&data->memory_conn_cfg); if (status != STATUS_QSPI_IP_SUCCESS) { LOG_ERR("Fail to init memory device %d (%d)", data->instance, status); return -EIO; } #if !defined(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME) uint8_t jedec_id[JESD216_READ_ID_LEN]; /* Verify connectivity by reading the device ID */ ret = nxp_s32_qspi_read_id(dev, jedec_id); if (ret != 0) { LOG_ERR("JEDEC ID read failed (%d)", ret); return -ENODEV; } /* * Check the memory device ID against the one configured from devicetree * to verify we are talking to the correct device. */ if (memcmp(jedec_id, memory_cfg->readIdSettings.readIdExpected, 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], memory_cfg->readIdSettings.readIdExpected[0], memory_cfg->readIdSettings.readIdExpected[1], memory_cfg->readIdSettings.readIdExpected[2]); return -EINVAL; } ret = nxp_s32_qspi_set_quad_mode(dev, config->quad_mode); if (ret < 0) { return ret; } #endif /* !CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME */ return ret; } static DEVICE_API(flash, nxp_s32_qspi_api) = { .erase = nxp_s32_qspi_erase, .write = nxp_s32_qspi_write, .read = nxp_s32_qspi_read, .get_parameters = nxp_s32_qspi_get_parameters, #if defined(CONFIG_FLASH_PAGE_LAYOUT) .page_layout = nxp_s32_qspi_pages_layout, #endif /* CONFIG_FLASH_PAGE_LAYOUT */ #if defined(CONFIG_FLASH_JESD216_API) .sfdp_read = nxp_s32_qspi_sfdp_read, .read_jedec_id = nxp_s32_qspi_read_id, #endif /* CONFIG_FLASH_JESD216_API */ }; #define QSPI_PAGE_LAYOUT(n) \ .layout = { \ .pages_count = (DT_INST_PROP(n, size) / 8) \ / CONFIG_FLASH_NXP_S32_QSPI_LAYOUT_PAGE_SIZE, \ .pages_size = CONFIG_FLASH_NXP_S32_QSPI_LAYOUT_PAGE_SIZE, \ } #define QSPI_READ_ID_CFG(n) \ { \ .readIdLut = QSPI_LUT_IDX(QSPI_SEQ_RDID), \ .readIdSize = DT_INST_PROP_LEN(n, jedec_id), \ .readIdExpected = DT_INST_PROP(n, jedec_id), \ } #define QSPI_MEMORY_CONN_CFG(n) \ { \ .connectionType = (Qspi_Ip_ConnectionType)DT_INST_REG_ADDR(n), \ .memAlignment = DT_INST_PROP_OR(n, memory_alignment, 1) \ } #define QSPI_ERASE_CFG(n) \ { \ .eraseTypes = { \ { \ .eraseLut = QSPI_LUT_IDX(QSPI_SEQ_SE), \ .size = 12, /* 4 KB */ \ }, \ { \ .eraseLut = QSPI_LUT_IDX(QSPI_SEQ_BE), \ .size = 16, /* 64 KB */ \ }, \ COND_CODE_1(DT_INST_PROP(n, has_32k_erase), ( \ { \ .eraseLut = QSPI_LUT_IDX(QSPI_SEQ_BE_32K), \ .size = 15, /* 32 KB */ \ }, \ ), ( \ { \ .eraseLut = QSPI_IP_LUT_INVALID, \ }, \ )) \ { \ .eraseLut = QSPI_IP_LUT_INVALID, \ }, \ }, \ .chipEraseLut = QSPI_LUT_IDX(QSPI_SEQ_CE), \ } #define QSPI_RESET_CFG(n) \ { \ .resetCmdLut = QSPI_LUT_IDX(QSPI_SEQ_RESET), \ .resetCmdCount = 4U, \ } /* * SR information used internally by the HAL to access fields BUSY and WEL * during read/write/erase and polling status operations. */ #define QSPI_STATUS_REG_CFG(n) \ { \ .statusRegInitReadLut = QSPI_LUT_IDX(QSPI_SEQ_RDSR), \ .statusRegReadLut = QSPI_LUT_IDX(QSPI_SEQ_RDSR), \ .statusRegWriteLut = QSPI_LUT_IDX(QSPI_SEQ_WRSR), \ .writeEnableSRLut = QSPI_LUT_IDX(QSPI_SEQ_WREN), \ .writeEnableLut = QSPI_LUT_IDX(QSPI_SEQ_WREN), \ .regSize = 1U, \ .busyOffset = 0U, \ .busyValue = 1U, \ .writeEnableOffset = 1U, \ } #define QSPI_INIT_CFG(n) \ { \ .opCount = 0U, \ .operations = NULL, \ } #define QSPI_LUT_CFG(n) \ { \ .opCount = ARRAY_SIZE(nxp_s32_qspi_lut), \ .lutOps = (Qspi_Ip_InstrOpType *)nxp_s32_qspi_lut, \ } #define QSPI_MEMORY_CFG(n) \ { \ .memType = QSPI_IP_SERIAL_FLASH, \ .hfConfig = NULL, \ .memSize = DT_INST_PROP(n, size) / 8, \ .pageSize = CONFIG_FLASH_NXP_S32_QSPI_LAYOUT_PAGE_SIZE, \ .writeLut = QSPI_LUT_IDX(QSPI_WRITE_SEQ(n)), \ .readLut = QSPI_LUT_IDX(QSPI_READ_SEQ(n)), \ .read0xxLut = QSPI_IP_LUT_INVALID, \ .read0xxLutAHB = QSPI_IP_LUT_INVALID, \ .eraseSettings = QSPI_ERASE_CFG(n), \ .statusConfig = QSPI_STATUS_REG_CFG(n), \ .resetSettings = QSPI_RESET_CFG(n), \ .initResetSettings = QSPI_RESET_CFG(n), \ .initConfiguration = QSPI_INIT_CFG(n), \ .lutSequences = QSPI_LUT_CFG(n), \ COND_CODE_1(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME, (), ( \ .readIdSettings = QSPI_READ_ID_CFG(n),) \ ) \ .suspendSettings = { \ .eraseSuspendLut = QSPI_IP_LUT_INVALID, \ .eraseResumeLut = QSPI_IP_LUT_INVALID, \ .programSuspendLut = QSPI_IP_LUT_INVALID, \ .programResumeLut = QSPI_IP_LUT_INVALID, \ }, \ .initCallout = NULL, \ .resetCallout = NULL, \ .errorCheckCallout = NULL, \ .eccCheckCallout = NULL, \ .ctrlAutoCfgPtr = NULL, \ } #define FLASH_NXP_S32_QSPI_INIT_DEVICE(n) \ COND_CODE_1(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME, (), ( \ BUILD_ASSERT(DT_INST_NODE_HAS_PROP(n, jedec_id), \ "jedec-id is required for non-runtime SFDP"); \ BUILD_ASSERT(DT_INST_PROP_LEN(n, jedec_id) == JESD216_READ_ID_LEN,\ "jedec-id must be of size JESD216_READ_ID_LEN bytes"); \ )) \ \ static const struct nxp_s32_qspi_config nxp_s32_qspi_config_##n = { \ .controller = DEVICE_DT_GET(DT_INST_BUS(n)), \ .flash_parameters = { \ .write_block_size = QSPI_WRITE_BLOCK_SIZE, \ .erase_value = QSPI_ERASE_VALUE, \ }, \ IF_ENABLED(CONFIG_FLASH_PAGE_LAYOUT, \ (QSPI_PAGE_LAYOUT(n),)) \ COND_CODE_1(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME, (), ( \ .memory_cfg = QSPI_MEMORY_CFG(n), \ .qer_type = QSPI_QER_TYPE(n), \ .quad_mode = QSPI_HAS_QUAD_MODE(n) \ )) \ }; \ \ static struct nxp_s32_qspi_data nxp_s32_qspi_data_##n = { \ .memory_conn_cfg = QSPI_MEMORY_CONN_CFG(n), \ COND_CODE_1(CONFIG_FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME, (), ( \ .read_sfdp_lut_idx = QSPI_LUT_IDX(QSPI_SEQ_READ_SFDP), \ )) \ }; \ \ DEVICE_DT_INST_DEFINE(n, \ nxp_s32_qspi_init, \ NULL, \ &nxp_s32_qspi_data_##n, \ &nxp_s32_qspi_config_##n, \ POST_KERNEL, \ CONFIG_FLASH_INIT_PRIORITY, \ &nxp_s32_qspi_api); DT_INST_FOREACH_STATUS_OKAY(FLASH_NXP_S32_QSPI_INIT_DEVICE)