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 <cong.nguyenhuu@nxp.com>
This commit is contained in:
Cong Nguyen Huu 2024-08-08 16:46:49 +07:00 committed by Benjamin Cabé
parent f0c4d1c53c
commit fd620c3ef9
4 changed files with 367 additions and 8 deletions

View file

@ -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)

View file

@ -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

View file

@ -0,0 +1,247 @@
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nxp_s32_qspi_hyperflash
#include <zephyr/kernel.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/logging/log.h>
#include <Qspi_Ip.h>
#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)

View file

@ -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