diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index fd9aff229e1..50058198de3 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -106,6 +106,12 @@ zephyr_library_include_directories_ifdef( ${ZEPHYR_BASE}/drivers/memc ) +zephyr_library_sources_ifdef(CONFIG_FLASH_NXP_S32_QSPI_NOR flash_nxp_s32_qspi_nor.c) +zephyr_library_include_directories_ifdef( + CONFIG_FLASH_NXP_S32_QSPI_NOR + ${ZEPHYR_BASE}/drivers/memc +) + zephyr_library_sources_ifdef(CONFIG_FLASH_SHELL flash_shell.c) zephyr_library_sources_ifdef(CONFIG_FLASH_JESD216 jesd216.c) zephyr_library_sources_ifdef(CONFIG_FLASH_INFINEON_CAT1 flash_ifx_cat1.c) diff --git a/drivers/flash/Kconfig b/drivers/flash/Kconfig index 8d50a77e78d..9d2192571f9 100644 --- a/drivers/flash/Kconfig +++ b/drivers/flash/Kconfig @@ -154,4 +154,6 @@ source "drivers/flash/Kconfig.ifx_cat1" source "drivers/flash/Kconfig.numaker" +source "drivers/flash/Kconfig.nxp_s32" + endif # FLASH diff --git a/drivers/flash/Kconfig.nxp_s32 b/drivers/flash/Kconfig.nxp_s32 new file mode 100644 index 00000000000..08a96f1cec9 --- /dev/null +++ b/drivers/flash/Kconfig.nxp_s32 @@ -0,0 +1,54 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +config FLASH_NXP_S32_QSPI_NOR + bool "NXP S32 QSPI NOR driver" + default y + depends on DT_HAS_NXP_S32_QSPI_NOR_ENABLED + select MEMC + select FLASH_HAS_PAGE_LAYOUT + select FLASH_HAS_DRIVER_ENABLED + select FLASH_JESD216 + help + Enable the Flash driver for a NOR Serial Flash Memory device connected + to an NXP S32 QSPI bus. + +if FLASH_NXP_S32_QSPI_NOR + +config FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME + bool "Read flash parameters at runtime" + help + Read flash device characteristics from the device at runtime. + This option should provide functionality for all supported + JESD216-compatible devices, with the following notes: + - Quad Enable Requirements bitfield (DW15) must be present in the SFDP + tables to configure Quad mode. Otherwise it defaults to Dual or + Single mode as supported by the device. + - Soft Reset bitfield (DW16) must be present in the SFDP tables to + automatically reset the device at initialization time. + - 0-X-X mode discovery not yet implemented by the HAL. + + If not selected, the driver uses a fixed configuration assuming 256 By + page size and 4 KiBy, 32 KiBy and 64 KiBy erase instructions. The + device size and jedec-id properties must be set in devicetree node. + +config FLASH_NXP_S32_QSPI_VERIFY_ERASE + bool "Verify memory after erased" + help + Verify contents of memory after erased. + +config FLASH_NXP_S32_QSPI_VERIFY_WRITE + bool "Verify memory after written" + help + Verify contents of memory after written. + +config FLASH_NXP_S32_QSPI_LAYOUT_PAGE_SIZE + int "Page size to use for FLASH_LAYOUT feature" + default 4096 + help + When CONFIG_FLASH_PAGE_LAYOUT is used this driver will support that API. + By default the page size corresponds to the sector size (4096) for a NOR + flash memory. Other options may include the 32K-byte erase size (32768), + the block size (65536), or any non-zero multiple of the sector size. + +endif # FLASH_NXP_S32_QSPI_NOR diff --git a/drivers/flash/flash_nxp_s32_qspi_nor.c b/drivers/flash/flash_nxp_s32_qspi_nor.c new file mode 100644 index 00000000000..defd1e110bf --- /dev/null +++ b/drivers/flash/flash_nxp_s32_qspi_nor.c @@ -0,0 +1,1106 @@ +/* + * 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 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) diff --git a/dts/bindings/mtd/nxp,s32-qspi-device.yaml b/dts/bindings/mtd/nxp,s32-qspi-device.yaml new file mode 100644 index 00000000000..23de077121e --- /dev/null +++ b/dts/bindings/mtd/nxp,s32-qspi-device.yaml @@ -0,0 +1,22 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: | + QSPI memory device supporting xSPI or Hyperbus. + +compatible: "nxp,s32-qspi-device" + +include: [base.yaml, "jedec,jesd216.yaml"] + +on-bus: qspi + +properties: + reg: + required: true + + memory-alignment: + type: int + description: | + Memory alignment in bytes, used to calculate padding when performing + unaligned accesses. + If not provided, 1 byte alignment will be selected. diff --git a/dts/bindings/mtd/nxp,s32-qspi-nor.yaml b/dts/bindings/mtd/nxp,s32-qspi-nor.yaml new file mode 100644 index 00000000000..3abe66673b8 --- /dev/null +++ b/dts/bindings/mtd/nxp,s32-qspi-nor.yaml @@ -0,0 +1,38 @@ +# Copyright 2023 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: | + QSPI NOR flash connected to the NXP S32 QSPI bus. + +compatible: "nxp,s32-qspi-nor" + +include: "nxp,s32-qspi-device.yaml" + +properties: + has-32k-erase: + type: boolean + description: | + Set if the memory device supports 32 KiBy block erase operation. + + readoc: + type: string + enum: + - "1-1-1" # 0x0B + - "1-1-2" # 0x3B + - "1-2-2" # 0xBB + - "1-1-4" # 0x6B + - "1-4-4" # 0xEB + description: | + Specify the number of data lines and opcode used for reading. + If not provided, 1-1-1 will be selected. + + writeoc: + type: string + enum: + - "1-1-1" # 0x02 + - "1-1-2" # 0xA2 + - "1-1-4" # 0x32 + - "1-4-4" # 0x38 + description: | + Specify the number of data lines and opcode used for writing. + If not provided, 1-1-1 will be selected.