blob: aa3b56c164879bcd97c9080d7f7487a4d50005df [file] [log] [blame]
/*
* Copyright (c) 2024-2025 Renesas Electronics Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#define DT_DRV_COMPAT renesas_ra_qspi_nor
#include <zephyr/init.h>
#include <soc.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/drivers/flash/ra_flash_api_extensions.h>
#include "spi_nor.h"
#include "r_spi_flash_api.h"
#include "r_qspi.h"
/* Flash QPI (4-4-4) opcodes */
#define QSPI_QPI_CMD_QPIID (0xAF) /* QPI ID Read */
#define QSPI_QPI_CMD_RDSFDP (0x5A) /* Read SFDP */
#define QSPI_QPI_CMD_RSTQIO (0xF5) /* Reset QPI */
#define QSPI_QPI_CMD_EQIO (0x35) /* Enable QPI */
/* XIP (Execute In Place) mode */
#define QSPI_CMD_XIP_ENTER (0x20) /* XIP Enter command */
#define QSPI_CMD_XIP_EXIT (0xFF) /* XIP Exit command */
#define WRITE_STATUS_BIT 0
#if defined(CONFIG_SOC_SERIES_RA6E2)
#define STATUS_REG_PAYLOAD {0x01, 0x00}
#define SET_SREG_VALUE (0x00)
#else
#define STATUS_REG_PAYLOAD {0x01, 0x40, 0x00}
#define SET_SREG_VALUE (0x40)
#endif
/* one byte data transfer */
#define ONE_BYTE (1)
#define THREE_BYTE (3)
#define FOUR_BYTE (4)
#define RESET_VALUE (0x00)
/* default memory value */
#define QSPI_DEFAULT_MEM_VAL (0xFF)
#define QSPI0_NODE DT_INST_PARENT(0)
#define RA_QSPI_NOR_NODE DT_INST(0, renesas_ra_qspi_nor)
#define QSPI_WRITE_BLK_SZ DT_PROP(RA_QSPI_NOR_NODE, write_block_size)
#define QSPI_ERASE_BLK_SZ DT_PROP(RA_QSPI_NOR_NODE, erase_block_size)
/* QSPI flash page Size */
#define PAGE_SIZE_BYTE SPI_NOR_PAGE_SIZE
/* sector size of QSPI flash device */
#define BLOCK_SIZE_4K (4096U)
#define BLOCK_SIZE_32K (32768U)
#define BLOCK_SIZE_64K (65536U)
/* Flash Size*/
#define QSPI_NOR_FLASH_SIZE DT_REG_SIZE(RA_QSPI_NOR_NODE)
#define ERASE_COMMAND_LENGTH(arr) (sizeof(arr) / sizeof((arr)[0]))
#define QSPI_ENABLE_QUAD_MODE DT_PROP(RA_QSPI_NOR_NODE, qpi_enable)
PINCTRL_DT_DEFINE(QSPI0_NODE);
LOG_MODULE_REGISTER(flash_qspi_renesas_ra, CONFIG_FLASH_LOG_LEVEL);
struct qspi_flash_ra_data {
struct st_qspi_instance_ctrl qspi_ctrl;
struct st_spi_flash_cfg qspi_cfg;
struct k_sem sem;
};
struct ra_qspi_nor_flash_config {
const struct pinctrl_dev_config *pcfg;
};
static const spi_flash_erase_command_t g_qspi_erase_command_list[4] = {
{.command = 0x20, .size = 4096},
{.command = 0x52, .size = 32768},
{.command = 0xD8, .size = 65536},
{.command = 0xC7, .size = SPI_FLASH_ERASE_SIZE_CHIP_ERASE},
};
static const struct ra_qspi_nor_flash_config qspi_nor_dev_config = {
.pcfg = PINCTRL_DT_DEV_CONFIG_GET(QSPI0_NODE),
};
static const struct flash_parameters qspi_flash_ra_config_para = {
.write_block_size = QSPI_WRITE_BLK_SZ,
.erase_value = 0xff,
};
static const qspi_extended_cfg_t g_qspi_extended_cfg = {
.min_qssl_deselect_cycles = QSPI_QSSL_MIN_HIGH_LEVEL_8_QSPCLK,
.qspclk_div = QSPI_QSPCLK_DIV_2,
};
static struct qspi_flash_ra_data qspi_flash_data = {
.qspi_cfg = {
.spi_protocol = SPI_FLASH_PROTOCOL_EXTENDED_SPI,
.read_mode = SPI_FLASH_READ_MODE_FAST_READ_QUAD_IO,
.address_bytes = SPI_FLASH_ADDRESS_BYTES_3,
.dummy_clocks = SPI_FLASH_DUMMY_CLOCKS_DEFAULT,
.page_program_address_lines = SPI_FLASH_DATA_LINES_1,
.page_size_bytes = PAGE_SIZE_BYTE,
.page_program_command = (SPI_NOR_CMD_PP),
.write_enable_command = (SPI_NOR_CMD_WREN),
.status_command = (SPI_NOR_CMD_RDSR),
.write_status_bit = WRITE_STATUS_BIT,
.xip_enter_command = QSPI_CMD_XIP_ENTER,
.xip_exit_command = QSPI_CMD_XIP_EXIT,
.p_erase_command_list = &g_qspi_erase_command_list[0],
.erase_command_list_length = ERASE_COMMAND_LENGTH(g_qspi_erase_command_list),
.p_extend = &g_qspi_extended_cfg,
}};
static void acquire_device(const struct device *dev)
{
struct qspi_flash_ra_data *dev_data = dev->data;
k_sem_take(&dev_data->sem, K_FOREVER);
}
static void release_device(const struct device *dev)
{
struct qspi_flash_ra_data *dev_data = dev->data;
k_sem_give(&dev_data->sem);
}
static int get_flash_status(const struct device *dev)
{
struct qspi_flash_ra_data *qspi_data = dev->data;
spi_flash_status_t status = {.write_in_progress = true};
int32_t time_out = (INT32_MAX);
int err;
do {
err = R_QSPI_StatusGet(&qspi_data->qspi_ctrl, &status);
if (err != FSP_SUCCESS) {
LOG_ERR("Status get failed");
return -EIO;
}
--time_out;
if (RESET_VALUE >= time_out) {
return -EIO;
}
} while (false != status.write_in_progress);
return 0;
}
#if defined(CONFIG_FLASH_EX_OP_ENABLED)
static int qspi_flash_ra_ex_op(const struct device *dev, uint16_t code, const uintptr_t in,
void *out)
{
int err = 0;
uint8_t cmd;
struct qspi_flash_ra_data *qspi_data = dev->data;
ARG_UNUSED(in);
ARG_UNUSED(out);
acquire_device(dev);
switch (code) {
case QSPI_FLASH_EX_OP_EXIT_QPI:
if (SPI_FLASH_PROTOCOL_QPI != qspi_data->qspi_cfg.spi_protocol) {
err = 0;
break;
}
cmd = QSPI_QPI_CMD_RSTQIO;
err = R_QSPI_DirectWrite(&qspi_data->qspi_ctrl, &cmd, ONE_BYTE, false);
if (err != FSP_SUCCESS) {
LOG_ERR("Direct write for EXIT QPI failed");
err = -EIO;
}
break;
case FLASH_EX_OP_RESET:
cmd = SPI_NOR_CMD_RESET_EN;
err = R_QSPI_DirectWrite(&qspi_data->qspi_ctrl, &cmd, ONE_BYTE, false);
if (err == FSP_SUCCESS) {
cmd = SPI_NOR_CMD_RESET_MEM;
err = R_QSPI_DirectWrite(&qspi_data->qspi_ctrl, &cmd, ONE_BYTE, false);
if (err != FSP_SUCCESS) {
LOG_ERR("Direct write for RESET MEM failed");
err = -EIO;
}
} else {
if (err != FSP_SUCCESS) {
LOG_ERR("Direct write for RESET Flash failed");
err = -EIO;
}
}
break;
default:
break;
}
release_device(dev);
return err;
}
#endif
#if CONFIG_FLASH_PAGE_LAYOUT
static const struct flash_pages_layout qspi_flash_ra_layout = {
.pages_count = QSPI_NOR_FLASH_SIZE / QSPI_ERASE_BLK_SZ,
.pages_size = QSPI_ERASE_BLK_SZ,
};
void qspi_flash_ra_page_layout(const struct device *dev, const struct flash_pages_layout **layout,
size_t *layout_size)
{
ARG_UNUSED(dev);
*layout = &qspi_flash_ra_layout;
*layout_size = 1;
}
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
#if defined(CONFIG_FLASH_JESD216_API)
static int qspi_flash_ra_read_jedec_id(const struct device *dev, uint8_t *id)
{
struct qspi_flash_ra_data *qspi_data = dev->data;
int err = 0;
uint8_t cmd;
if (id == NULL) {
return -EINVAL;
}
acquire_device(dev);
if (SPI_FLASH_PROTOCOL_QPI == qspi_data->qspi_cfg.spi_protocol) {
cmd = QSPI_QPI_CMD_QPIID;
} else {
cmd = SPI_NOR_CMD_RDID;
}
err = R_QSPI_DirectWrite(&qspi_data->qspi_ctrl, &cmd, ONE_BYTE, true);
if (err != FSP_SUCCESS) {
LOG_ERR("Direct write for READ ID failed");
err = -EIO;
goto out;
}
err = R_QSPI_DirectRead(&qspi_data->qspi_ctrl, id, THREE_BYTE);
if (err != FSP_SUCCESS) {
LOG_ERR("Direct read failed");
err = -EIO;
goto out;
}
err = get_flash_status(dev);
if (err != FSP_SUCCESS) {
LOG_ERR("Failed to get status for QSPI operation");
err = -EIO;
}
out:
release_device(dev);
return err;
}
static int qspi_flash_ra_sfdp_read(const struct device *dev, off_t addr, void *data, size_t size)
{
struct qspi_flash_ra_data *qspi_data = dev->data;
int err = 0;
uint8_t offset;
SPI_FLASH_PROTOCOL_QPI == qspi_data->qspi_cfg.spi_protocol ? (offset = 4) : (offset = 1);
uint8_t *buffer = k_malloc((size + offset) > 4 ? (size + offset) : 4);
if (!buffer) {
LOG_ERR("Failed to allocate buffer for SFDP read");
return -ENOMEM;
}
acquire_device(dev);
memset(&buffer[0], 0, sizeof(buffer));
buffer[0] = QSPI_QPI_CMD_RDSFDP;
buffer[1] = addr;
buffer[2] = addr >> 8;
buffer[3] = addr >> 16;
err = R_QSPI_DirectWrite(&qspi_data->qspi_ctrl, &buffer[0], FOUR_BYTE, true);
if (err != FSP_SUCCESS) {
LOG_ERR("Direct write for READ SFDP failed");
err = -EIO;
goto out;
}
err = R_QSPI_DirectRead(&qspi_data->qspi_ctrl, &buffer[0], size + offset);
if (err != FSP_SUCCESS) {
LOG_ERR("Direct read failed");
err = -EIO;
goto out;
}
err = get_flash_status(dev);
if (err != FSP_SUCCESS) {
LOG_ERR("Failed to get status for QSPI operation");
err = -EIO;
goto out;
}
if (SPI_FLASH_PROTOCOL_QPI == qspi_data->qspi_cfg.spi_protocol) {
/* 3 dummy byte */
memcpy(data, &buffer[4], size);
} else {
/* 1 dummy byte */
memcpy(data, &buffer[1], size);
}
out:
release_device(dev);
return err;
}
#endif
static bool qspi_flash_ra_valid(off_t area_size, off_t offset, size_t len)
{
if ((offset < 0) || (offset >= area_size) || ((area_size - offset) < len)) {
return false;
}
return true;
}
static int qspi_flash_ra_erase(const struct device *dev, off_t offset, size_t len)
{
struct qspi_flash_ra_data *qspi_data = dev->data;
int err = 0;
struct flash_pages_info page_info_start, page_info_end;
uint32_t erase_size;
int rc;
if (!len) {
return 0;
}
if (!qspi_flash_ra_valid(QSPI_NOR_FLASH_SIZE, offset, len)) {
LOG_ERR("The offset 0x%lx is invalid", (long)offset);
return -EINVAL;
}
if (len % QSPI_ERASE_BLK_SZ != 0) {
LOG_ERR("The size %u is not align with block size (%u)", len, QSPI_ERASE_BLK_SZ);
return -EINVAL;
}
rc = flash_get_page_info_by_offs(dev, offset, &page_info_start);
if ((rc != 0) || (offset != page_info_start.start_offset)) {
LOG_ERR("The offset 0x%lx is not aligned with the starting sector", (long)offset);
return -EINVAL;
}
rc = flash_get_page_info_by_offs(dev, (offset + len), &page_info_end);
if ((rc != 0) || ((offset + len) != page_info_end.start_offset)) {
LOG_ERR("The size %u is not aligned with the ending sector", len);
return -EINVAL;
}
acquire_device(dev);
while (len > 0) {
if (len < BLOCK_SIZE_32K) {
erase_size = BLOCK_SIZE_4K;
} else if (len < BLOCK_SIZE_64K) {
erase_size = BLOCK_SIZE_32K;
} else {
erase_size = BLOCK_SIZE_64K;
}
err = R_QSPI_Erase(&qspi_data->qspi_ctrl,
(uint8_t *)(QSPI_DEVICE_START_ADDRESS + offset), erase_size);
if (err) {
LOG_ERR("Erase failed");
err = -EIO;
break;
}
err = get_flash_status(dev);
if (err) {
LOG_ERR("failed to get status for QSPI operation");
err = -EIO;
break;
}
offset += erase_size;
len -= erase_size;
}
release_device(dev);
return err;
}
static int qspi_flash_ra_read(const struct device *dev, off_t offset, void *data, size_t len)
{
if (!len) {
return 0;
}
if (!qspi_flash_ra_valid(QSPI_NOR_FLASH_SIZE, offset, len)) {
return -EINVAL;
}
acquire_device(dev);
memcpy(data, (uint8_t *)(QSPI_DEVICE_START_ADDRESS + offset), len);
release_device(dev);
return 0;
}
static int qspi_flash_ra_write(const struct device *dev, off_t offset, const void *data, size_t len)
{
struct qspi_flash_ra_data *qspi_data = dev->data;
int err = 0;
uint32_t remaining_bytes = len;
const uint8_t *p_data = data;
uint32_t size = len;
if (!len) {
return 0;
}
if (!qspi_flash_ra_valid(QSPI_NOR_FLASH_SIZE, offset, len)) {
return -EINVAL;
}
acquire_device(dev);
while (remaining_bytes > 0) {
size = remaining_bytes > PAGE_SIZE_BYTE ? PAGE_SIZE_BYTE : remaining_bytes;
err = R_QSPI_Write(&qspi_data->qspi_ctrl, p_data,
(uint8_t *)(QSPI_DEVICE_START_ADDRESS + offset), size);
if (err) {
LOG_ERR("Direct write failed");
err = -EIO;
break;
}
err = get_flash_status(dev);
if (err) {
LOG_ERR("Failed to get status for QSPI operation");
err = -EIO;
break;
}
remaining_bytes -= size;
offset += size;
p_data += size;
}
release_device(dev);
return err;
}
static int qspi_flash_ra_get_size(const struct device *dev, uint64_t *size)
{
*size = (uint64_t)QSPI_NOR_FLASH_SIZE;
return 0;
}
static const struct flash_parameters *qspi_flash_ra_get_parameters(const struct device *dev)
{
ARG_UNUSED(dev);
return &qspi_flash_ra_config_para;
}
static const struct flash_driver_api qspi_flash_ra_api = {
.erase = qspi_flash_ra_erase,
.write = qspi_flash_ra_write,
.read = qspi_flash_ra_read,
.get_parameters = qspi_flash_ra_get_parameters,
.get_size = qspi_flash_ra_get_size,
#ifdef CONFIG_FLASH_PAGE_LAYOUT
.page_layout = qspi_flash_ra_page_layout,
#endif
#if defined(CONFIG_FLASH_JESD216_API)
.sfdp_read = qspi_flash_ra_sfdp_read,
.read_jedec_id = qspi_flash_ra_read_jedec_id,
#endif
#if defined(CONFIG_FLASH_EX_OP_ENABLED)
.ex_op = qspi_flash_ra_ex_op,
#endif /* CONFIG_FLASH_EX_OP_ENABLED */
};
static int set_qspi_flash_status(const struct device *dev)
{
struct qspi_flash_ra_data *qspi_data = dev->data;
uint8_t data_sreg[] = STATUS_REG_PAYLOAD;
uint8_t sreg_data = 0;
int ret;
ret = R_QSPI_DirectWrite(&qspi_data->qspi_ctrl, data_sreg, sizeof(data_sreg), false);
if (ret) {
LOG_ERR("Direct write for STATUS_REG_PAYLOAD fail");
return -EIO;
}
ret = get_flash_status(dev);
if (ret) {
LOG_ERR("Failed to get status for QSPI operation");
return -EIO;
}
ret = R_QSPI_DirectWrite(&qspi_data->qspi_ctrl, &(qspi_data->qspi_cfg.status_command),
ONE_BYTE, true);
if (ret) {
LOG_ERR("Direct write for status command fail ");
return -EIO;
}
ret = R_QSPI_DirectRead(&qspi_data->qspi_ctrl, &sreg_data, ONE_BYTE);
if (ret) {
LOG_ERR("Direct read fail");
return -EIO;
}
if (SET_SREG_VALUE != sreg_data) {
LOG_ERR("Verify status register data failed");
return -EIO;
}
return ret;
}
static int qspi_flash_ra_init(const struct device *dev)
{
const struct ra_qspi_nor_flash_config *config = dev->config;
struct qspi_flash_ra_data *qspi_data = dev->data;
int ret;
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
if (ret) {
LOG_ERR("Failed to configure pins for QSPI");
return -EIO;
}
k_sem_init(&qspi_data->sem, 1, 1);
ret = R_QSPI_Open(&qspi_data->qspi_ctrl, &qspi_data->qspi_cfg);
if (ret) {
LOG_ERR("Open failed");
return -EIO;
}
ret = R_QSPI_DirectWrite(&qspi_data->qspi_ctrl, &(qspi_data->qspi_cfg.write_enable_command),
ONE_BYTE, false);
if (ret) {
LOG_ERR("Direct write enable command failed");
return -EIO;
}
ret = get_flash_status(dev);
if (ret) {
LOG_ERR("Failed to get status for QSPI operation");
return -EIO;
}
ret = set_qspi_flash_status(dev);
if (ret) {
LOG_ERR("Set qspi flash status failed");
return -EIO;
}
#if QSPI_ENABLE_QUAD_MODE
uint8_t data_qpi_en = QSPI_QPI_CMD_EQIO;
qspi_data->qspi_cfg.spi_protocol = SPI_FLASH_PROTOCOL_QPI;
ret = R_QSPI_DirectWrite(&qspi_data->qspi_ctrl, &data_qpi_en, ONE_BYTE, false);
if (ret) {
LOG_ERR("Direct write SPI_FLASH_PROTOCOL_QPI failed");
return -EIO;
}
ret = R_QSPI_SpiProtocolSet(&qspi_data->qspi_ctrl, SPI_FLASH_PROTOCOL_QPI);
if (ret) {
LOG_ERR("Set SpiProtocol failed");
return -EIO;
}
#endif
return 0;
}
DEVICE_DT_INST_DEFINE(0, qspi_flash_ra_init, NULL, &qspi_flash_data, &qspi_nor_dev_config,
POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY, &qspi_flash_ra_api);