269 lines
8.1 KiB
C
269 lines
8.1 KiB
C
/*
|
|
* Copyright (c) 2022-2023 HPMicro
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2022-03-09 hpmicro First implementation
|
|
* 2022-08-01 hpmicro Fixed random crashing during kvdb_init
|
|
* 2022-08-03 hpmicro Improved erase speed
|
|
* 2023-05-15 hpmicro Disable global interrupt during FLASH operation for FLASH build
|
|
*
|
|
*/
|
|
#include <rtthread.h>
|
|
#include <rthw.h>
|
|
#ifdef RT_USING_FAL
|
|
#include "fal.h"
|
|
#include "hpm_romapi.h"
|
|
#include "board.h"
|
|
#include "hpm_l1c_drv.h"
|
|
|
|
#if defined(FLASH_XIP) && (FLASH_XIP == 1)
|
|
|
|
static rt_base_t s_interrupt_level;
|
|
#define FAL_ENTER_CRITICAL() do {\
|
|
rt_exit_critical();\
|
|
fencei();\
|
|
s_interrupt_level = rt_hw_interrupt_disable();\
|
|
} while(0)
|
|
|
|
#define FAL_EXIT_CRITICAL() do {\
|
|
ROM_API_TABLE_ROOT->xpi_driver_if->software_reset(BOARD_APP_XPI_NOR_XPI_BASE);\
|
|
fencei();\
|
|
rt_exit_critical();\
|
|
rt_hw_interrupt_enable(s_interrupt_level);\
|
|
} while(0)
|
|
|
|
#define FAL_RAMFUNC __attribute__((section(".isr_vector")))
|
|
|
|
#else
|
|
#define FAL_ENTER_CRITICAL() rt_enter_critical()
|
|
|
|
#define FAL_EXIT_CRITICAL() rt_exit_critical()
|
|
|
|
#define FAL_RAMFUNC
|
|
|
|
#endif
|
|
|
|
/***************************************************************************************************
|
|
* FAL Porting Guide
|
|
*
|
|
* 1. Most FLASH devices do not support RWW (Read-while-Write), the codes to access the FLASH
|
|
* must be placed at RAM or ROM code
|
|
* 2. During FLASH erase/program, it is recommended to disable the interrupt, or place the
|
|
* interrupt related codes to RAM
|
|
*
|
|
***************************************************************************************************/
|
|
|
|
static int init(void);
|
|
static int read(long offset, uint8_t *buf, size_t size);
|
|
static int write(long offset, const uint8_t *buf, size_t size);
|
|
static int erase(long offset, size_t size);
|
|
|
|
static xpi_nor_config_t s_flashcfg;
|
|
|
|
/**
|
|
* @brief FAL Flash device context
|
|
*/
|
|
struct fal_flash_dev nor_flash0 =
|
|
{
|
|
.name = NOR_FLASH_DEV_NAME,
|
|
/* If porting this code to the device with FLASH connected to XPI1, the address must be changed to 0x90000000 */
|
|
.addr = NOR_FLASH_MEM_BASE,
|
|
.len = 8 * 1024 * 1024,
|
|
.blk_size = 4096,
|
|
.ops = { .init = init, .read = read, .write = write, .erase = erase },
|
|
.write_gran = 1
|
|
};
|
|
|
|
/**
|
|
* @brief FAL initialization
|
|
* This function probes the FLASH using the ROM API
|
|
*/
|
|
FAL_RAMFUNC static int init(void)
|
|
{
|
|
int ret = RT_EOK;
|
|
xpi_nor_config_option_t cfg_option;
|
|
cfg_option.header.U = BOARD_APP_XPI_NOR_CFG_OPT_HDR;
|
|
cfg_option.option0.U = BOARD_APP_XPI_NOR_CFG_OPT_OPT0;
|
|
cfg_option.option1.U = BOARD_APP_XPI_NOR_CFG_OPT_OPT1;
|
|
|
|
FAL_ENTER_CRITICAL();
|
|
hpm_stat_t status = rom_xpi_nor_auto_config(BOARD_APP_XPI_NOR_XPI_BASE, &s_flashcfg, &cfg_option);
|
|
FAL_EXIT_CRITICAL();
|
|
if (status != status_success)
|
|
{
|
|
ret = -RT_ERROR;
|
|
}
|
|
else
|
|
{
|
|
/* update the flash chip information */
|
|
uint32_t sector_size;
|
|
rom_xpi_nor_get_property(BOARD_APP_XPI_NOR_XPI_BASE, &s_flashcfg, xpi_nor_property_sector_size, §or_size);
|
|
uint32_t flash_size;
|
|
rom_xpi_nor_get_property(BOARD_APP_XPI_NOR_XPI_BASE, &s_flashcfg, xpi_nor_property_total_size, &flash_size);
|
|
nor_flash0.blk_size = sector_size;
|
|
nor_flash0.len = flash_size;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief FAL read function
|
|
* Read data from FLASH
|
|
* @param offset FLASH offset
|
|
* @param buf Buffer to hold data read by this API
|
|
* @param size Size of data to be read
|
|
* @return actual read bytes
|
|
*/
|
|
FAL_RAMFUNC static int read(long offset, uint8_t *buf, size_t size)
|
|
{
|
|
uint32_t flash_addr = nor_flash0.addr + offset;
|
|
uint32_t aligned_start = HPM_L1C_CACHELINE_ALIGN_DOWN(flash_addr);
|
|
uint32_t aligned_end = HPM_L1C_CACHELINE_ALIGN_UP(flash_addr + size);
|
|
uint32_t aligned_size = aligned_end - aligned_start;
|
|
rt_base_t level = rt_hw_interrupt_disable();
|
|
l1c_dc_invalidate(aligned_start, aligned_size);
|
|
rt_hw_interrupt_enable(level);
|
|
|
|
(void) rt_memcpy(buf, (void*) flash_addr, size);
|
|
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* @brief Write unaligned data to the page
|
|
* @param offset FLASH offset
|
|
* @param buf Data buffer
|
|
* @param size Size of data to be written
|
|
* @return actual size of written data or error code
|
|
*/
|
|
FAL_RAMFUNC static int write_unaligned_page_data(long offset, const uint32_t *buf, size_t size)
|
|
{
|
|
hpm_stat_t status;
|
|
|
|
FAL_ENTER_CRITICAL();
|
|
status = rom_xpi_nor_program(BOARD_APP_XPI_NOR_XPI_BASE, xpi_xfer_channel_auto, &s_flashcfg, buf, offset, size);
|
|
FAL_EXIT_CRITICAL();
|
|
|
|
if (status != status_success)
|
|
{
|
|
return -RT_ERROR;
|
|
rt_kprintf("write failed, status=%d\n", status);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* @brief FAL write function
|
|
* Write data to specified FLASH address
|
|
* @param offset FLASH offset
|
|
* @param buf Data buffer
|
|
* @param size Size of data to be written
|
|
* @return actual size of written data or error code
|
|
*/
|
|
FAL_RAMFUNC static int write(long offset, const uint8_t *buf, size_t size)
|
|
{
|
|
uint32_t *src = NULL;
|
|
uint32_t buf_32[64];
|
|
uint32_t write_size;
|
|
size_t remaining_size = size;
|
|
int ret = (int)size;
|
|
|
|
uint32_t page_size;
|
|
rom_xpi_nor_get_property(BOARD_APP_XPI_NOR_XPI_BASE, &s_flashcfg, xpi_nor_property_page_size, &page_size);
|
|
uint32_t offset_in_page = offset % page_size;
|
|
if (offset_in_page != 0)
|
|
{
|
|
uint32_t write_size_in_page = page_size - offset_in_page;
|
|
uint32_t write_page_size = MIN(write_size_in_page, size);
|
|
(void) rt_memcpy(buf_32, buf, write_page_size);
|
|
write_size = write_unaligned_page_data(offset, buf_32, write_page_size);
|
|
if (write_size < 0)
|
|
{
|
|
ret = -RT_ERROR;
|
|
goto write_quit;
|
|
}
|
|
|
|
remaining_size -= write_page_size;
|
|
offset += write_page_size;
|
|
buf += write_page_size;
|
|
}
|
|
|
|
while (remaining_size > 0)
|
|
{
|
|
write_size = MIN(remaining_size, sizeof(buf_32));
|
|
rt_memcpy(buf_32, buf, write_size);
|
|
src = &buf_32[0];
|
|
|
|
FAL_ENTER_CRITICAL();
|
|
hpm_stat_t status = rom_xpi_nor_program(BOARD_APP_XPI_NOR_XPI_BASE, xpi_xfer_channel_auto, &s_flashcfg, src,
|
|
offset, write_size);
|
|
FAL_EXIT_CRITICAL();
|
|
|
|
if (status != status_success)
|
|
{
|
|
ret = -RT_ERROR;
|
|
rt_kprintf("write failed, status=%d\n", status);
|
|
break;
|
|
}
|
|
|
|
remaining_size -= write_size;
|
|
buf += write_size;
|
|
offset += write_size;
|
|
}
|
|
|
|
write_quit:
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief FAL erase function
|
|
* Erase specified FLASH region
|
|
* @param offset the start FLASH address to be erased
|
|
* @param size size of the region to be erased
|
|
* @ret RT_EOK Erase operation is successful
|
|
* @retval -RT_ERROR Erase operation failed
|
|
*/
|
|
FAL_RAMFUNC static int erase(long offset, size_t size)
|
|
{
|
|
uint32_t aligned_size = (size + nor_flash0.blk_size - 1U) & ~(nor_flash0.blk_size - 1U);
|
|
hpm_stat_t status;
|
|
int ret = (int)size;
|
|
|
|
uint32_t block_size;
|
|
uint32_t sector_size;
|
|
(void) rom_xpi_nor_get_property(BOARD_APP_XPI_NOR_XPI_BASE, &s_flashcfg, xpi_nor_property_sector_size, §or_size);
|
|
(void) rom_xpi_nor_get_property(BOARD_APP_XPI_NOR_XPI_BASE, &s_flashcfg, xpi_nor_property_block_size, &block_size);
|
|
uint32_t erase_unit;
|
|
while (aligned_size > 0)
|
|
{
|
|
FAL_ENTER_CRITICAL();
|
|
if ((offset % block_size == 0) && (aligned_size >= block_size))
|
|
{
|
|
erase_unit = block_size;
|
|
status = rom_xpi_nor_erase_block(BOARD_APP_XPI_NOR_XPI_BASE, xpi_xfer_channel_auto, &s_flashcfg, offset);
|
|
}
|
|
else
|
|
{
|
|
erase_unit = sector_size;
|
|
status = rom_xpi_nor_erase_sector(BOARD_APP_XPI_NOR_XPI_BASE, xpi_xfer_channel_auto, &s_flashcfg, offset);
|
|
}
|
|
FAL_EXIT_CRITICAL();
|
|
|
|
if (status != status_success)
|
|
{
|
|
ret = -RT_ERROR;
|
|
break;
|
|
}
|
|
offset += erase_unit;
|
|
aligned_size -= erase_unit;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* RT_USING_FAL */
|