| /* |
| * Copyright 2023 NXP |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT nxp_s32_qspi_nor |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(nxp_s32_qspi_nor, CONFIG_FLASH_LOG_LEVEL); |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/drivers/flash.h> |
| #include <zephyr/sys/util.h> |
| |
| #include <Qspi_Ip.h> |
| |
| #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 const struct flash_driver_api 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) |