1107 lines
32 KiB
C
1107 lines
32 KiB
C
/*
|
|
* 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)
|