/* * Copyright (c) 2024 Renesas Electronics Corporation * * SPDX-License-Identifier: Apache-2.0 */ #define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL #include #include #include #include #include #include #include #include #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);