From fd620c3ef93d3357ff2c2beb419920f83c023a31 Mon Sep 17 00:00:00 2001 From: Cong Nguyen Huu Date: Thu, 8 Aug 2024 16:46:49 +0700 Subject: [PATCH] drivers: flash: add NXP S32 QSPI HyperFlash driver Add support HyperFlash memory devices on a NXP S32 QSPI bus. This driver uses a fixed LUT configuration that defined in HAL RTD HyperFlash driver. Driver allows to read, write and erase HyperFlash devices. Signed-off-by: Cong Nguyen Huu --- drivers/flash/CMakeLists.txt | 10 +- drivers/flash/Kconfig.nxp_s32 | 19 +- drivers/flash/flash_nxp_s32_qspi_hyperflash.c | 247 ++++++++++++++++++ dts/bindings/mtd/nxp,s32-qspi-hyperflash.yaml | 99 +++++++ 4 files changed, 367 insertions(+), 8 deletions(-) create mode 100644 drivers/flash/flash_nxp_s32_qspi_hyperflash.c create mode 100644 dts/bindings/mtd/nxp,s32-qspi-hyperflash.yaml diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index ef7e779fdd6..b8b345bb6d9 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -149,11 +149,11 @@ zephyr_library_include_directories_ifdef( ) zephyr_library_sources_ifdef(CONFIG_FLASH_NXP_S32_QSPI_NOR flash_nxp_s32_qspi_nor.c) -zephyr_library_sources_ifdef(CONFIG_FLASH_NXP_S32_QSPI_NOR flash_nxp_s32_qspi.c) -zephyr_library_include_directories_ifdef( - CONFIG_FLASH_NXP_S32_QSPI_NOR - ${ZEPHYR_BASE}/drivers/memc -) +zephyr_library_sources_ifdef(CONFIG_FLASH_NXP_S32_QSPI_HYPERFLASH flash_nxp_s32_qspi_hyperflash.c) +if(CONFIG_FLASH_NXP_S32_QSPI_NOR OR CONFIG_FLASH_NXP_S32_QSPI_HYPERFLASH) + zephyr_library_sources(flash_nxp_s32_qspi.c) + zephyr_library_include_directories(${ZEPHYR_BASE}/drivers/memc) +endif() if(CONFIG_RA_FLASH_HP) zephyr_library_sources(flash_hp_ra.c) diff --git a/drivers/flash/Kconfig.nxp_s32 b/drivers/flash/Kconfig.nxp_s32 index 459a7b2376f..08cc11a6a86 100644 --- a/drivers/flash/Kconfig.nxp_s32 +++ b/drivers/flash/Kconfig.nxp_s32 @@ -1,4 +1,4 @@ -# Copyright 2023 NXP +# Copyright 2023-2024 NXP # SPDX-License-Identifier: Apache-2.0 config FLASH_NXP_S32_QSPI_NOR @@ -14,10 +14,23 @@ config FLASH_NXP_S32_QSPI_NOR 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_HYPERFLASH + bool "NXP S32 QSPI HYPERFLASH driver" + default y + depends on DT_HAS_NXP_S32_QSPI_HYPERFLASH_ENABLED + select MEMC + select FLASH_HAS_PAGE_LAYOUT + select FLASH_HAS_DRIVER_ENABLED + select FLASH_HAS_EXPLICIT_ERASE + help + Enable the Flash driver for a HyperFlash Memory device connected + to an NXP S32 QSPI bus. + +if FLASH_NXP_S32_QSPI_NOR || FLASH_NXP_S32_QSPI_HYPERFLASH config FLASH_NXP_S32_QSPI_SFDP_RUNTIME bool "Read flash parameters at runtime" + depends on FLASH_NXP_S32_QSPI_NOR help Read flash device characteristics from the device at runtime. This option should provide functionality for all supported @@ -52,4 +65,4 @@ config FLASH_NXP_S32_QSPI_LAYOUT_PAGE_SIZE 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 +endif # FLASH_NXP_S32_QSPI_NOR || FLASH_NXP_S32_QSPI_HYPERFLASH diff --git a/drivers/flash/flash_nxp_s32_qspi_hyperflash.c b/drivers/flash/flash_nxp_s32_qspi_hyperflash.c new file mode 100644 index 00000000000..4428aaded1f --- /dev/null +++ b/drivers/flash/flash_nxp_s32_qspi_hyperflash.c @@ -0,0 +1,247 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_s32_qspi_hyperflash + +#include +#include +#include + +#include + +#include "memc_nxp_s32_qspi.h" +#include "flash_nxp_s32_qspi.h" + +LOG_MODULE_REGISTER(nxp_s32_qspi_hyperflash, CONFIG_FLASH_LOG_LEVEL); + +/* Use the fixed command sets from Qspi_Ip_Hyperflash.c */ +extern Qspi_Ip_InstrOpType QSPI_IP_HF_LUT_NAME[QSPI_IP_HF_LUT_SIZE]; + +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); + uint8_t dev_id[memory_cfg->readIdSettings.readIdSize]; + Qspi_Ip_StatusType status; + int ret = 0; + + /* Used by the HAL to retrieve the internal driver state */ + data->instance = nxp_s32_qspi_register_device(); + __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 (!device_is_ready(config->controller)) { + LOG_ERR("Memory control device not ready"); + return -ENODEV; + } + + 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; + } + + /* Verify connectivity by reading the device ID */ + ret = nxp_s32_qspi_read_id(dev, dev_id); + if (ret != 0) { + LOG_ERR("Device ID read failed (%d)", ret); + return -ENODEV; + } + + if (memcmp(dev_id, memory_cfg->readIdSettings.readIdExpected, sizeof(dev_id))) { + LOG_ERR("Device id does not match config"); + return -EINVAL; + } + + return ret; +} + +static DEVICE_API(flash, nxp_s32_qspi_api) = { + .erase = nxp_s32_qspi_erase, + .write = nxp_s32_qspi_write, + .read = nxp_s32_qspi_read, + .get_parameters = nxp_s32_qspi_get_parameters, +#if defined(CONFIG_FLASH_PAGE_LAYOUT) + .page_layout = nxp_s32_qspi_pages_layout, +#endif /* CONFIG_FLASH_PAGE_LAYOUT */ +}; + +#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_IP_HF_LUT_READ, \ + .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(n, write_block_size) \ + } + +#define QSPI_ERASE_CFG(n) \ + { \ + .eraseTypes = { \ + { \ + .eraseLut = QSPI_IP_HF_LUT_SE, \ + .size = 12, /* 4 KB */ \ + }, \ + { \ + .eraseLut = QSPI_IP_HF_LUT_SE, \ + .size = 18, /* 256 KB */ \ + }, \ + { \ + .eraseLut = QSPI_IP_LUT_INVALID, \ + .size = 0, \ + }, \ + { \ + .eraseLut = QSPI_IP_LUT_INVALID, \ + .size = 0, \ + }, \ + }, \ + .chipEraseLut = QSPI_IP_HF_LUT_CE, \ + } + +#define QSPI_RESET_CFG(n) \ + { \ + .resetCmdLut = QSPI_IP_HF_LUT_RST, \ + .resetCmdCount = QSPI_IP_HF_RST_CNT, \ + } + +#define QSPI_STATUS_REG_CFG(n) \ + { \ + .statusRegInitReadLut = QSPI_IP_HF_LUT_RDSR, \ + .statusRegReadLut = QSPI_IP_HF_LUT_RDSR, \ + .statusRegWriteLut = QSPI_IP_LUT_INVALID, \ + .writeEnableSRLut = QSPI_IP_LUT_INVALID, \ + .writeEnableLut = QSPI_IP_LUT_INVALID, \ + .regSize = 1U, \ + .busyOffset = 0U, \ + .busyValue = 1U, \ + .writeEnableOffset = 1U, \ + } + +#define QSPI_INIT_CFG(n) \ + { \ + .opCount = 0U, \ + .operations = NULL, \ + } + +#define QSPI_LUT_CFG(n) \ + { \ + .opCount = QSPI_IP_HF_LUT_SIZE, \ + .lutOps = (Qspi_Ip_InstrOpType *)QSPI_IP_HF_LUT_NAME, \ + } + +#define QSPI_SUSPEND_CFG(n) \ + { \ + .eraseSuspendLut = QSPI_IP_HF_LUT_ES, \ + .eraseResumeLut = QSPI_IP_HF_LUT_ER, \ + .programSuspendLut = QSPI_IP_HF_LUT_PS, \ + .programResumeLut = QSPI_IP_HF_LUT_PR, \ + } + +#define QSPI_MEMORY_CFG(n) \ + { \ + .memType = QSPI_IP_HYPER_FLASH, \ + .hfConfig = &hyperflash_config_##n, \ + .memSize = DT_INST_PROP(n, size) / 8, \ + .pageSize = DT_INST_PROP(n, max_program_buffer_size), \ + .writeLut = QSPI_IP_HF_LUT_WRITE, \ + .readLut = QSPI_IP_HF_LUT_READ, \ + .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), \ + .readIdSettings = QSPI_READ_ID_CFG(n), \ + .suspendSettings = QSPI_SUSPEND_CFG(n), \ + .initCallout = NULL, \ + .resetCallout = NULL, \ + .errorCheckCallout = NULL, \ + .eccCheckCallout = NULL, \ + .ctrlAutoCfgPtr = NULL, \ + } + +#define FLASH_NXP_S32_QSPI_DRV_STRENGTH(n) \ + COND_CODE_1(DT_INST_ENUM_IDX(n, vcc_mv), \ + (DT_INST_PROP(n, drive_strength_ohm) == 12 ? QSPI_IP_HF_DRV_STRENGTH_007 : \ + (DT_INST_PROP(n, drive_strength_ohm) == 14 ? QSPI_IP_HF_DRV_STRENGTH_006 : \ + (DT_INST_PROP(n, drive_strength_ohm) == 16 ? QSPI_IP_HF_DRV_STRENGTH_005 : \ + (DT_INST_PROP(n, drive_strength_ohm) == 20 ? QSPI_IP_HF_DRV_STRENGTH_000 : \ + (DT_INST_PROP(n, drive_strength_ohm) == 27 ? QSPI_IP_HF_DRV_STRENGTH_003 : \ + (DT_INST_PROP(n, drive_strength_ohm) == 40 ? QSPI_IP_HF_DRV_STRENGTH_002 : \ + (DT_INST_PROP(n, drive_strength_ohm) == 71 ? QSPI_IP_HF_DRV_STRENGTH_001 : \ + QSPI_IP_HF_DRV_STRENGTH_000))))))), \ + (DT_INST_PROP(n, drive_strength_ohm) == 20 ? QSPI_IP_HF_DRV_STRENGTH_007 : \ + (DT_INST_PROP(n, drive_strength_ohm) == 24 ? QSPI_IP_HF_DRV_STRENGTH_006 : \ + (DT_INST_PROP(n, drive_strength_ohm) == 27 ? QSPI_IP_HF_DRV_STRENGTH_000 : \ + (DT_INST_PROP(n, drive_strength_ohm) == 34 ? QSPI_IP_HF_DRV_STRENGTH_004 : \ + (DT_INST_PROP(n, drive_strength_ohm) == 45 ? QSPI_IP_HF_DRV_STRENGTH_003 : \ + (DT_INST_PROP(n, drive_strength_ohm) == 68 ? QSPI_IP_HF_DRV_STRENGTH_002 : \ + (DT_INST_PROP(n, drive_strength_ohm) == 117 ? QSPI_IP_HF_DRV_STRENGTH_001 : \ + QSPI_IP_HF_DRV_STRENGTH_000)))))))) + +#define FLASH_NXP_S32_QSPI_SECTOR_MAP(n) \ + COND_CODE_1(DT_INST_PROP(n, support_only_uniform_sectors), \ + (DT_INST_ENUM_IDX(n, ppw_sectors_addr_mapping) ? \ + QSPI_IP_HF_UNIFORM_SECTORS_READ_PASSWORD_HIGH : \ + QSPI_IP_HF_UNIFORM_SECTORS_READ_PASSWORD_LOW), \ + (DT_INST_ENUM_IDX(n, ppw_sectors_addr_mapping) ? \ + QSPI_IP_HF_PARAM_AND_PASSWORD_MAP_HIGH : \ + QSPI_IP_HF_PARAM_AND_PASSWORD_MAP_LOW)) + +#define FLASH_NXP_S32_QSPI_INIT_DEVICE(n) \ + static Qspi_Ip_HyperFlashConfigType hyperflash_config_##n = \ + { \ + .outputDriverStrength = FLASH_NXP_S32_QSPI_DRV_STRENGTH(n), \ + .RWDSLowOnDualError = DT_INST_PROP(n, rwds_low_dual_error), \ + .secureRegionUnlocked = !DT_INST_PROP(n, secure_region_locked), \ + .readLatency = DT_INST_ENUM_IDX(n, read_latency_cycles), \ + .paramSectorMap = FLASH_NXP_S32_QSPI_SECTOR_MAP(n), \ + .deviceIdWordAddress = DT_INST_PROP(n, device_id_word_addr), \ + }; \ + 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 = DT_INST_PROP(n, write_block_size), \ + .erase_value = QSPI_ERASE_VALUE, \ + }, \ + IF_ENABLED(CONFIG_FLASH_PAGE_LAYOUT, \ + (QSPI_PAGE_LAYOUT(n),)) \ + .memory_cfg = QSPI_MEMORY_CFG(n), \ + }; \ + \ + static struct nxp_s32_qspi_data nxp_s32_qspi_data_##n = { \ + .memory_conn_cfg = QSPI_MEMORY_CONN_CFG(n), \ + }; \ + \ + 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-hyperflash.yaml b/dts/bindings/mtd/nxp,s32-qspi-hyperflash.yaml new file mode 100644 index 00000000000..3fe64dd9aba --- /dev/null +++ b/dts/bindings/mtd/nxp,s32-qspi-hyperflash.yaml @@ -0,0 +1,99 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: | + QSPI hyperflash connected to the NXP S32 QSPI bus. + +compatible: "nxp,s32-qspi-hyperflash" + +include: "nxp,s32-qspi-device.yaml" + +properties: + device-id-word-addr: + required: true + type: int + description: | + The word address of the device ID in ASO (Application-Specific Object). + This address specifies the exact location within the memory where the device ID is stored. + + rwds-low-dual-error: + type: boolean + description: | + Enable Read-Write Data Strobe (RWDS) dual error detect. + + secure-region-locked: + type: boolean + description: | + The secure region is locked and cannot be accessed or modified. + This is particularly useful in scenarios where sensitive data needs protection from + unauthorized access, such as in financial applications or secure communication systems. + If it is disable, having access to all memory regions is beneficial during development + or debugging phases. + + vcc-mv: + type: int + required: true + enum: + - 1800 + - 3000 + description: | + The memory operating voltage supply in mV. + + drive-strength-ohm: + type: int + required: true + enum: + - 12 + - 14 + - 16 + - 20 + - 24 + - 27 + - 34 + - 40 + - 45 + - 68 + - 71 + - 117 + description: | + Specifies the output drive strength in ohm, which based on the operating device VCC. + The supported typical impedance settings: + For 1.8V: 117 Ohm, 68 Ohm, 45 Ohm, 34 Ohm, 27 Ohm, 24 Ohm, 20 Ohm + For 3V: 71 Ohm, 40 Ohm, 27 Ohm, 20 Ohm, 16 Ohm, 14 Ohm, 12 Ohm + See the xVCR[14:12] field in VCR configuration register in the memory device datasheet. + + read-latency-cycles: + type: int + required: true + enum: + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - 12 + - 13 + - 14 + - 15 + - 16 + description: | + Specifies the read latency in cycles, which is determined based on the operating frequency + as specified in the memory device datasheet. + + support-only-uniform-sectors: + type: boolean + description: | + The memory device supports only uniform (256KB) sectors. + + ppw-sectors-addr-mapping: + type: string + required: true + enum: + - LOW + - HIGH + description: | + The mapping of the parameter and read password sectors: + - LOW: Parameter and read password sectors mapped into lowest addresses + - HIGH: Parameter and read password sectors mapped into highest addresses