Browse Source
Create common source code to use for supporting HyperFlash. Rename 'FLASH_NXP_S32_QSPI_NOR_SFDP_RUNTIME' to 'FLASH_NXP_S32_QSPI_SFDP_RUNTIME' as a common kconfig. Add the 'max-program-buffer-size' property to use for setting memory pageSize, instead of using 'CONFIG_FLASH_NXP_S32_QSPI_LAYOUT_PAGE_SIZE' for setting. Add the 'write-block-size' propertyto use for setting the number of bytes used in write operations, it also uses to instead of the 'memory-alignment' property. Signed-off-by: Cong Nguyen Huu <cong.nguyenhuu@nxp.com>pull/84302/head
8 changed files with 420 additions and 355 deletions
@ -0,0 +1,274 @@
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright 2023-2024 NXP |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#include <zephyr/kernel.h> |
||||
#include <zephyr/drivers/flash.h> |
||||
#include <zephyr/logging/log.h> |
||||
|
||||
#include <Qspi_Ip.h> |
||||
|
||||
#include "flash_nxp_s32_qspi.h" |
||||
|
||||
LOG_MODULE_REGISTER(flash_nxp_s32_qspi, CONFIG_FLASH_LOG_LEVEL); |
||||
|
||||
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) && |
||||
((memory_cfg->memSize - offset) >= size)); |
||||
} |
||||
|
||||
uint8_t nxp_s32_qspi_register_device(void) |
||||
{ |
||||
static uint8_t instance_cnt; |
||||
|
||||
return instance_cnt++; |
||||
} |
||||
|
||||
/* Must be called with lock */ |
||||
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; |
||||
} |
||||
|
||||
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 -EINVAL; |
||||
} |
||||
|
||||
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; |
||||
} |
||||
|
||||
int nxp_s32_qspi_write(const struct device *dev, off_t offset, const void *src, size_t size) |
||||
{ |
||||
const struct nxp_s32_qspi_config *config = dev->config; |
||||
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 || !size) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
if (!area_is_subregion(dev, offset, size) || |
||||
(offset % config->flash_parameters.write_block_size) || |
||||
(size % config->flash_parameters.write_block_size)) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
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; |
||||
} |
||||
|
||||
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) || !size) { |
||||
return -EINVAL; |
||||
} |
||||
|
||||
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; |
||||
} |
||||
|
||||
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) |
||||
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 */ |
||||
|
||||
#if defined(CONFIG_FLASH_JESD216_API) || !defined(CONFIG_FLASH_NXP_S32_QSPI_SFDP_RUNTIME) |
||||
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_SFDP_RUNTIME */ |
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2023-2024 NXP |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#ifndef ZEPHYR_DRIVERS_FLASH_NXP_S32_QSPI_H_ |
||||
#define ZEPHYR_DRIVERS_FLASH_NXP_S32_QSPI_H_ |
||||
|
||||
#include "jesd216.h" |
||||
|
||||
#define QSPI_ERASE_VALUE 0xff |
||||
|
||||
#define QSPI_IS_ALIGNED(addr, bits) (((addr) & BIT_MASK(bits)) == 0) |
||||
|
||||
#if defined(CONFIG_FLASH_NXP_S32_QSPI_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_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_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_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 |
||||
}; |
||||
|
||||
static ALWAYS_INLINE Qspi_Ip_MemoryConfigType *get_memory_config(const struct device *dev) |
||||
{ |
||||
#if defined(CONFIG_FLASH_NXP_S32_QSPI_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 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 |
||||
} |
||||
|
||||
/*
|
||||
* This function retrieves the device instance used by the HAL |
||||
* to access the internal driver state. |
||||
*/ |
||||
uint8_t nxp_s32_qspi_register_device(void); |
||||
|
||||
int nxp_s32_qspi_wait_until_ready(const struct device *dev); |
||||
|
||||
int nxp_s32_qspi_read(const struct device *dev, off_t offset, void *dest, size_t size); |
||||
|
||||
int nxp_s32_qspi_write(const struct device *dev, off_t offset, const void *src, size_t size); |
||||
|
||||
int nxp_s32_qspi_erase(const struct device *dev, off_t offset, size_t size); |
||||
|
||||
const struct flash_parameters *nxp_s32_qspi_get_parameters(const struct device *dev); |
||||
|
||||
#if defined(CONFIG_FLASH_PAGE_LAYOUT) |
||||
void nxp_s32_qspi_pages_layout(const struct device *dev, const struct flash_pages_layout **layout, |
||||
size_t *layout_size); |
||||
#endif /* CONFIG_FLASH_PAGE_LAYOUT */ |
||||
|
||||
#if defined(CONFIG_FLASH_JESD216_API) || !defined(CONFIG_FLASH_NXP_S32_QSPI_SFDP_RUNTIME) |
||||
int nxp_s32_qspi_read_id(const struct device *dev, uint8_t *id); |
||||
#endif /* CONFIG_FLASH_JESD216_API || !CONFIG_FLASH_NXP_S32_QSPI_SFDP_RUNTIME */ |
||||
|
||||
#endif /* ZEPHYR_DRIVERS_FLASH_NXP_S32_QSPI_H_ */ |
Loading…
Reference in new issue