398 lines
12 KiB
C
398 lines
12 KiB
C
|
/*
|
||
|
* Copyright (c) 2024 Renesas Electronics Corporation
|
||
|
*
|
||
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
*/
|
||
|
|
||
|
#define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL
|
||
|
#include <zephyr/logging/log.h>
|
||
|
#include <string.h>
|
||
|
#include <soc.h>
|
||
|
#include <zephyr/kernel.h>
|
||
|
#include <zephyr/device.h>
|
||
|
#include <zephyr/devicetree.h>
|
||
|
#include <zephyr/init.h>
|
||
|
#include <zephyr/irq.h>
|
||
|
#include "flash_hp_ra.h"
|
||
|
|
||
|
#define DT_DRV_COMPAT renesas_ra_flash_hp_controller
|
||
|
|
||
|
LOG_MODULE_REGISTER(flash_hp_ra, CONFIG_FLASH_LOG_LEVEL);
|
||
|
|
||
|
#define ERASE_BLOCK_SIZE_0 DT_PROP(DT_INST(0, renesas_ra_nv_flash), erase_block_size)
|
||
|
#define ERASE_BLOCK_SIZE_1 DT_PROP(DT_INST(1, renesas_ra_nv_flash), erase_block_size)
|
||
|
|
||
|
BUILD_ASSERT((ERASE_BLOCK_SIZE_0 % FLASH_HP_CF_BLOCK_8KB_SIZE) == 0,
|
||
|
"erase-block-size expected to be a multiple of a block size");
|
||
|
BUILD_ASSERT((ERASE_BLOCK_SIZE_1 % FLASH_HP_DF_BLOCK_SIZE) == 0,
|
||
|
"erase-block-size expected to be a multiple of a block size");
|
||
|
|
||
|
/* Flags, set from Callback function */
|
||
|
static volatile struct event_flash g_event_flash = {
|
||
|
.erase_complete = false,
|
||
|
.write_complete = false,
|
||
|
};
|
||
|
|
||
|
void fcu_frdyi_isr(void);
|
||
|
void fcu_fiferr_isr(void);
|
||
|
|
||
|
static int flash_controller_ra_init(const struct device *dev);
|
||
|
static int flash_ra_init(const struct device *dev);
|
||
|
static int flash_ra_erase(const struct device *dev, off_t offset, size_t len);
|
||
|
static int flash_ra_read(const struct device *dev, off_t offset, void *data, size_t len);
|
||
|
static int flash_ra_write(const struct device *dev, off_t offset, const void *data, size_t len);
|
||
|
static const struct flash_parameters *flash_ra_get_parameters(const struct device *dev);
|
||
|
#ifdef CONFIG_FLASH_PAGE_LAYOUT
|
||
|
void flash_ra_page_layout(const struct device *dev, const struct flash_pages_layout **layout,
|
||
|
size_t *layout_size);
|
||
|
#endif
|
||
|
|
||
|
#ifdef CONFIG_FLASH_EX_OP_ENABLED
|
||
|
static int flash_ra_ex_op(const struct device *dev, uint16_t code, const uintptr_t in, void *out);
|
||
|
#endif
|
||
|
|
||
|
static const struct flash_driver_api flash_ra_api = {
|
||
|
.erase = flash_ra_erase,
|
||
|
.write = flash_ra_write,
|
||
|
.read = flash_ra_read,
|
||
|
.get_parameters = flash_ra_get_parameters,
|
||
|
#ifdef CONFIG_FLASH_PAGE_LAYOUT
|
||
|
.page_layout = flash_ra_page_layout,
|
||
|
#endif
|
||
|
#ifdef CONFIG_FLASH_EX_OP_ENABLED
|
||
|
.ex_op = flash_ra_ex_op,
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
static struct flash_pages_layout flash_ra_layout[5];
|
||
|
|
||
|
void bgo_callback(flash_callback_args_t *p_args)
|
||
|
{
|
||
|
if (FLASH_EVENT_ERASE_COMPLETE == p_args->event) {
|
||
|
g_event_flash.erase_complete = true;
|
||
|
} else {
|
||
|
g_event_flash.write_complete = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool flash_ra_valid_range(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 flash_ra_read(const struct device *dev, off_t offset, void *data, size_t len)
|
||
|
{
|
||
|
struct flash_hp_ra_data *flash_data = dev->data;
|
||
|
|
||
|
if (!flash_ra_valid_range(flash_data->area_size, offset, len)) {
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (!len) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
LOG_DBG("flash: read 0x%lx, len: %u", (long)(offset + flash_data->area_address), len);
|
||
|
|
||
|
memcpy(data, (uint8_t *)(offset + flash_data->area_address), len);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int flash_ra_erase(const struct device *dev, off_t offset, size_t len)
|
||
|
{
|
||
|
struct flash_hp_ra_data *flash_data = dev->data;
|
||
|
struct flash_hp_ra_controller *dev_ctrl = flash_data->controller;
|
||
|
static struct flash_pages_info page_info_off, page_info_len;
|
||
|
fsp_err_t err = FSP_ERR_ASSERTION;
|
||
|
uint32_t block_num;
|
||
|
int rc, rc2;
|
||
|
|
||
|
if (!flash_ra_valid_range(flash_data->area_size, offset, len)) {
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (!len) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
LOG_DBG("flash: erase 0x%lx, len: %u", (long)(offset + flash_data->area_address), len);
|
||
|
|
||
|
rc = flash_get_page_info_by_offs(dev, offset, &page_info_off);
|
||
|
|
||
|
if (rc != 0) {
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (offset != page_info_off.start_offset) {
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
rc2 = flash_get_page_info_by_offs(dev, (offset + len), &page_info_len);
|
||
|
if (rc2 != 0) {
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
#if defined(CONFIG_DUAL_BANK_MODE)
|
||
|
/* Invalid offset in dual bank mode, this is reversed area. */
|
||
|
if ((page_info_off.index > FLASH_HP_CF_BLOCK_32KB_DUAL_LOW_END &&
|
||
|
page_info_off.index < FLASH_HP_CF_BLOCK_8KB_HIGH_START) ||
|
||
|
(page_info_len.index > FLASH_HP_CF_BLOCK_32KB_DUAL_LOW_END &&
|
||
|
page_info_len.index < FLASH_HP_CF_BLOCK_8KB_HIGH_START)) {
|
||
|
return -EIO;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if ((offset + len) != (page_info_len.start_offset)) {
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
block_num = (uint32_t)((page_info_len.index) - page_info_off.index);
|
||
|
|
||
|
if (block_num > 0) {
|
||
|
k_sem_take(&dev_ctrl->ctrl_sem, K_FOREVER);
|
||
|
|
||
|
err = R_FLASH_HP_Erase(&dev_ctrl->flash_ctrl,
|
||
|
(long)(flash_data->area_address + offset), block_num);
|
||
|
|
||
|
if (err != FSP_SUCCESS) {
|
||
|
k_sem_give(&dev_ctrl->ctrl_sem);
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
if (flash_data->FlashRegion == DATA_FLASH) {
|
||
|
/* Wait for the erase complete event flag, if BGO is SET */
|
||
|
if (true == dev_ctrl->fsp_config.data_flash_bgo) {
|
||
|
while (!g_event_flash.erase_complete) {
|
||
|
k_sleep(K_USEC(10));
|
||
|
}
|
||
|
g_event_flash.erase_complete = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
k_sem_give(&dev_ctrl->ctrl_sem);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int flash_ra_write(const struct device *dev, off_t offset, const void *data, size_t len)
|
||
|
{
|
||
|
fsp_err_t err = FSP_ERR_ASSERTION;
|
||
|
struct flash_hp_ra_data *flash_data = dev->data;
|
||
|
struct flash_hp_ra_controller *dev_ctrl = flash_data->controller;
|
||
|
|
||
|
if (!flash_ra_valid_range(flash_data->area_size, offset, len)) {
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (!len) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
LOG_DBG("flash: write 0x%lx, len: %u", (long)(offset + flash_data->area_address), len);
|
||
|
|
||
|
k_sem_take(&dev_ctrl->ctrl_sem, K_FOREVER);
|
||
|
|
||
|
err = R_FLASH_HP_Write(&dev_ctrl->flash_ctrl, (uint32_t)data,
|
||
|
(long)(offset + flash_data->area_address), len);
|
||
|
|
||
|
if (err != FSP_SUCCESS) {
|
||
|
k_sem_give(&dev_ctrl->ctrl_sem);
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
if (flash_data->FlashRegion == DATA_FLASH) {
|
||
|
/* Wait for the write complete event flag, if BGO is SET */
|
||
|
if (true == dev_ctrl->fsp_config.data_flash_bgo) {
|
||
|
while (!g_event_flash.write_complete) {
|
||
|
k_sleep(K_USEC(10));
|
||
|
}
|
||
|
g_event_flash.write_complete = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
k_sem_give(&dev_ctrl->ctrl_sem);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void flash_ra_page_layout(const struct device *dev, const struct flash_pages_layout **layout,
|
||
|
size_t *layout_size)
|
||
|
{
|
||
|
struct flash_hp_ra_data *flash_data = dev->data;
|
||
|
|
||
|
if (flash_data->FlashRegion == DATA_FLASH) {
|
||
|
flash_ra_layout[0].pages_count = flash_data->area_size / FLASH_HP_DF_BLOCK_SIZE;
|
||
|
flash_ra_layout[0].pages_size = FLASH_HP_DF_BLOCK_SIZE;
|
||
|
|
||
|
*layout_size = 1;
|
||
|
} else {
|
||
|
#if defined(CONFIG_DUAL_BANK_MODE)
|
||
|
flash_ra_layout[0].pages_count =
|
||
|
(FLASH_HP_CF_BLOCK_8KB_LOW_END - FLASH_HP_CF_BLOCK_8KB_LOW_START) + 1;
|
||
|
flash_ra_layout[0].pages_size = FLASH_HP_CF_BLOCK_8KB_SIZE;
|
||
|
|
||
|
flash_ra_layout[1].pages_count = (FLASH_HP_CF_BLOCK_32KB_DUAL_LOW_END -
|
||
|
FLASH_HP_CF_BLOCK_32KB_DUAL_LOW_START) +
|
||
|
1;
|
||
|
flash_ra_layout[1].pages_size = FLASH_HP_CF_BLOCK_32KB_SIZE;
|
||
|
|
||
|
flash_ra_layout[2].pages_count = FLASH_RESERVED_AREA_NUM;
|
||
|
flash_ra_layout[2].pages_size =
|
||
|
(FLASH_HP_BANK2_OFFSET -
|
||
|
(flash_ra_layout[0].pages_count * flash_ra_layout[0].pages_size) -
|
||
|
(flash_ra_layout[1].pages_count * flash_ra_layout[1].pages_size)) /
|
||
|
FLASH_RESERVED_AREA_NUM;
|
||
|
|
||
|
flash_ra_layout[3].pages_count =
|
||
|
(FLASH_HP_CF_BLOCK_8KB_HIGH_END - FLASH_HP_CF_BLOCK_8KB_HIGH_START) + 1;
|
||
|
flash_ra_layout[3].pages_size = FLASH_HP_CF_BLOCK_8KB_SIZE;
|
||
|
|
||
|
/* The final block is the dummy block */
|
||
|
flash_ra_layout[4].pages_count = (FLASH_HP_CF_BLOCK_32KB_DUAL_HIGH_END + 1 -
|
||
|
FLASH_HP_CF_BLOCK_32KB_DUAL_HIGH_START) +
|
||
|
1;
|
||
|
flash_ra_layout[4].pages_size = FLASH_HP_CF_BLOCK_32KB_SIZE;
|
||
|
|
||
|
*layout_size = 5;
|
||
|
#else
|
||
|
flash_ra_layout[0].pages_count =
|
||
|
(FLASH_HP_CF_BLOCK_8KB_LOW_END - FLASH_HP_CF_BLOCK_8KB_LOW_START) + 1;
|
||
|
flash_ra_layout[0].pages_size = FLASH_HP_CF_BLOCK_8KB_SIZE;
|
||
|
flash_ra_layout[1].pages_count =
|
||
|
(FLASH_HP_CF_BLOCK_32KB_LINEAR_END - FLASH_HP_CF_BLOCK_32KB_LINEAR_START) +
|
||
|
1;
|
||
|
flash_ra_layout[1].pages_size = FLASH_HP_CF_BLOCK_32KB_SIZE;
|
||
|
|
||
|
*layout_size = 2;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
*layout = flash_ra_layout;
|
||
|
}
|
||
|
|
||
|
static const struct flash_parameters *flash_ra_get_parameters(const struct device *dev)
|
||
|
{
|
||
|
const struct flash_hp_ra_config *config = dev->config;
|
||
|
|
||
|
return &config->flash_ra_parameters;
|
||
|
}
|
||
|
|
||
|
static struct flash_hp_ra_controller flash_hp_ra_controller = {
|
||
|
.fsp_config = {
|
||
|
.data_flash_bgo = true,
|
||
|
.p_callback = bgo_callback,
|
||
|
.p_context = NULL,
|
||
|
.irq = (IRQn_Type)DT_INST_IRQ_BY_NAME(0, frdyi, irq),
|
||
|
.err_irq = (IRQn_Type)DT_INST_IRQ_BY_NAME(0, fiferr, irq),
|
||
|
.err_ipl = DT_INST_IRQ_BY_NAME(0, fiferr, priority),
|
||
|
.ipl = DT_INST_IRQ_BY_NAME(0, frdyi, priority),
|
||
|
}};
|
||
|
|
||
|
#ifdef CONFIG_FLASH_EX_OP_ENABLED
|
||
|
static int flash_ra_ex_op(const struct device *dev, uint16_t code, const uintptr_t in, void *out)
|
||
|
{
|
||
|
int err = -ENOTSUP;
|
||
|
|
||
|
switch (code) {
|
||
|
#if defined(CONFIG_FLASH_RA_WRITE_PROTECT)
|
||
|
case FLASH_RA_EX_OP_WRITE_PROTECT:
|
||
|
err = flash_ra_ex_op_write_protect(dev, in, out);
|
||
|
break;
|
||
|
#endif /* CONFIG_FLASH_RA_WRITE_PROTECT */
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static int flash_ra_init(const struct device *dev)
|
||
|
{
|
||
|
const struct device *dev_ctrl = DEVICE_DT_INST_GET(0);
|
||
|
struct flash_hp_ra_data *flash_data = dev->data;
|
||
|
|
||
|
if (!device_is_ready(dev_ctrl)) {
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
if (flash_data->area_address == FLASH_HP_DF_START) {
|
||
|
flash_data->FlashRegion = DATA_FLASH;
|
||
|
} else {
|
||
|
flash_data->FlashRegion = CODE_FLASH;
|
||
|
}
|
||
|
|
||
|
flash_data->controller = dev_ctrl->data;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void flash_controller_ra_irq_config_func(const struct device *dev)
|
||
|
{
|
||
|
ARG_UNUSED(dev);
|
||
|
|
||
|
R_ICU->IELSR[DT_IRQ_BY_NAME(DT_DRV_INST(0), frdyi, irq)] = ELC_EVENT_FCU_FRDYI;
|
||
|
R_ICU->IELSR[DT_IRQ_BY_NAME(DT_DRV_INST(0), fiferr, irq)] = ELC_EVENT_FCU_FIFERR;
|
||
|
|
||
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), frdyi, irq),
|
||
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), frdyi, priority), fcu_frdyi_isr,
|
||
|
DEVICE_DT_INST_GET(0), 0);
|
||
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), fiferr, irq),
|
||
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), fiferr, priority), fcu_fiferr_isr,
|
||
|
DEVICE_DT_INST_GET(0), 0);
|
||
|
|
||
|
irq_enable(DT_INST_IRQ_BY_NAME(0, frdyi, irq));
|
||
|
irq_enable(DT_INST_IRQ_BY_NAME(0, fiferr, irq));
|
||
|
}
|
||
|
|
||
|
static int flash_controller_ra_init(const struct device *dev)
|
||
|
{
|
||
|
fsp_err_t err = FSP_SUCCESS;
|
||
|
const struct flash_hp_ra_controller_config *cfg = dev->config;
|
||
|
struct flash_hp_ra_controller *data = dev->data;
|
||
|
|
||
|
cfg->irq_config(dev);
|
||
|
|
||
|
err = R_FLASH_HP_Open(&data->flash_ctrl, &data->fsp_config);
|
||
|
|
||
|
if (err != FSP_SUCCESS) {
|
||
|
LOG_DBG("flash: open error=%d", (int)err);
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
k_sem_init(&data->ctrl_sem, 1, 1);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct flash_hp_ra_controller_config flash_hp_ra_controller_config = {
|
||
|
.irq_config = flash_controller_ra_irq_config_func,
|
||
|
};
|
||
|
|
||
|
#define RA_FLASH_INIT(index) \
|
||
|
struct flash_hp_ra_data flash_hp_ra_data_##index = {.area_address = DT_REG_ADDR(index), \
|
||
|
.area_size = DT_REG_SIZE(index)}; \
|
||
|
static struct flash_hp_ra_config flash_hp_ra_config_##index = { \
|
||
|
.flash_ra_parameters = { \
|
||
|
.write_block_size = GET_SIZE( \
|
||
|
(CHECK_EQ(DT_REG_ADDR(index), FLASH_HP_DF_START)), 4, 128), \
|
||
|
.erase_value = 0xff, \
|
||
|
}}; \
|
||
|
\
|
||
|
DEVICE_DT_DEFINE(index, flash_ra_init, NULL, &flash_hp_ra_data_##index, \
|
||
|
&flash_hp_ra_config_##index, POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY, \
|
||
|
&flash_ra_api);
|
||
|
|
||
|
DT_FOREACH_CHILD_STATUS_OKAY(DT_DRV_INST(0), RA_FLASH_INIT);
|
||
|
|
||
|
/* define the flash controller device just to run the init. */
|
||
|
DEVICE_DT_DEFINE(DT_DRV_INST(0), flash_controller_ra_init, NULL, &flash_hp_ra_controller,
|
||
|
&flash_hp_ra_controller_config, PRE_KERNEL_1, CONFIG_FLASH_INIT_PRIORITY, NULL);
|