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:
parent
f0c4d1c53c
commit
fd620c3ef9
4 changed files with 367 additions and 8 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
247
drivers/flash/flash_nxp_s32_qspi_hyperflash.c
Normal file
247
drivers/flash/flash_nxp_s32_qspi_hyperflash.c
Normal 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)
|
||||
99
dts/bindings/mtd/nxp,s32-qspi-hyperflash.yaml
Normal file
99
dts/bindings/mtd/nxp,s32-qspi-hyperflash.yaml
Normal 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
|
||||
Loading…
Reference in a new issue