/** * @file flash.c * @author chipsea * @brief Contains all functions support for flash driver * @version 0.1 * @date 2020-11-30 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd. * @note */ #include "sdk_config.h" #include "flash.h" #include "log.h" #include "types.h" #include "error.h" #include "pwrmgr.h" #include #include "rom_sym_def.h" #define FLASH_LOCK_504K 0x04//0x00-0x7DFFF #define FLASH_LOCK_496K 0x08//0x00-0x7BFFF #define FLASH_LOCK_480K 0x0C//0x00-0x77FFF #define FLASH_LOCK_448K 0x10//0x00-0x6FFFF #define FLASH_LOCK_384K 0x14//0x00-0x5FFFF #define FLASH_LOCK_HALF 0x18//0x00-0x3FFFF #define FLASH_LOCK_ALL 0x1C//0x00-0x7FFFF #define FLASH_UNLOCK 0x00 #define WRITE_ENABLE 0x6000001 #define WRITE_PROTECT 0x1008001 #define REG_FLASH_BP (*((volatile unsigned int *)(0x4000c8a8))) #define REG_FLASH_WRITE_CTRL (*((volatile unsigned int *)(0x4000c890))) #define SPIF_WAIT_IDLE_CYC (32) #define SPIF_STATUS_WAIT_IDLE(n) \ do \ { \ while((AP_SPIF->fcmd &0x02)==0x02); \ { \ volatile int delay_cycle = n; \ while (delay_cycle--){;} \ } \ while ((AP_SPIF->config & 0x80000000) == 0);\ } while (0); #define HAL_CACHE_ENTER_BYPASS_SECTION() do{ \ HAL_ENTER_CRITICAL_SECTION();\ AP_CACHE->CTRL0 = 0x02; \ AP_PCR->CACHE_RST = 0x02;\ AP_PCR->CACHE_BYPASS = 1; \ HAL_EXIT_CRITICAL_SECTION();\ }while(0); #define HAL_CACHE_EXIT_BYPASS_SECTION() do{ \ HAL_ENTER_CRITICAL_SECTION();\ AP_CACHE->CTRL0 = 0x00;\ AP_PCR->CACHE_RST = 0x03;\ AP_PCR->CACHE_BYPASS = 0;\ HAL_EXIT_CRITICAL_SECTION();\ }while(0); #define spif_wait_nobusy(flg, tout_ns, return_val) {if(_spif_wait_nobusy_x(flg, tout_ns)){if(return_val){ return return_val;}}} typedef struct { sysclk_t spif_ref_clk; uint32_t rd_instr; }xflash_Ctx_t; static xflash_Ctx_t s_xflashCtx = {.spif_ref_clk=SYS_CLK_RC_32M,.rd_instr=XFRD_FCMD_READ_DUAL}; extern int _spif_wait_nobusy(uint8_t flg, uint32_t tout_ns); extern int spif_write(uint32_t addr, uint8_t* data, uint32_t size); extern int spif_write_dma(uint32_t addr, uint8_t* data, uint32_t size); extern int spif_read(uint32_t addr, uint8_t* data, uint32_t size); extern int spif_read_dma(uint32_t addr, uint8_t* data, uint32_t size); extern int spif_erase_sector(unsigned int addr); extern int spif_erase_block64(unsigned int addr); extern int spif_erase_all(void); extern uint8_t spif_flash_status_reg_0(void); extern int spif_write_protect(bool en); extern void spif_cmd(uint8_t op, uint8_t addrlen, uint8_t rdlen, uint8_t wrlen, uint8_t mbit, uint8_t dummy); extern void spif_rddata(uint8_t* data, uint8_t len); extern int spif_config(sysclk_t ref_clk, uint8_t div, uint32_t rd_instr, uint8_t mode_bit, uint8_t QE); static inline uint32_t spif_lock() { HAL_ENTER_CRITICAL_SECTION(); uint32_t vic_iser = NVIC->ISER[0]; //mask all irq NVIC->ICER[0] = 0xFFFFFFFF; //enable ll irq and tim1 irq NVIC->ISER[0] = 0x100010; HAL_EXIT_CRITICAL_SECTION(); return vic_iser; } static inline void spif_unlock(uint32_t vic_iser) { HAL_ENTER_CRITICAL_SECTION(); NVIC->ISER[0] = vic_iser; HAL_EXIT_CRITICAL_SECTION(); } static void hal_cache_tag_flush(void) { HAL_ENTER_CRITICAL_SECTION(); uint32_t cb = AP_PCR->CACHE_BYPASS; volatile int dly = 8; if(cb==0) { AP_PCR->CACHE_BYPASS = 1; } AP_CACHE->CTRL0 = 0x02; while (dly--){;}; AP_CACHE->CTRL0 = 0x03; dly = 8;while (dly--){;}; AP_CACHE->CTRL0 = 0x00; if(cb==0) { AP_PCR->CACHE_BYPASS = 0; } HAL_EXIT_CRITICAL_SECTION(); } static uint8_t _spif_read_status_reg_x(void) { uint8_t status; spif_cmd(FCMD_RDST, 0, 2, 0, 0, 0); SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC); spif_rddata(&status, 1); return status; } static int _spif_wait_nobusy_x(uint8_t flg, uint32_t tout_ns) { uint8_t status; volatile int tout = (int )(tout_ns); for(; tout ; tout --){ status = _spif_read_status_reg_x(); if((status & flg) == 0) return ERR_NONE; //insert polling interval //5*32us WaitRTCCount(5); } return ERR_BUSY; } static void hal_cache_init(void) { volatile int dly=100; //clock gate hal_clk_gate_enable(MOD_HCLK_CACHE); hal_clk_gate_enable(MOD_PCLK_CACHE); //cache rst ahp AP_PCR->CACHE_RST=0x02; while(dly--){}; AP_PCR->CACHE_RST=0x03; hal_cache_tag_flush(); //cache enable AP_PCR->CACHE_BYPASS = 0; } static void hw_spif_cache_config(void) { spif_config(s_xflashCtx.spif_ref_clk,/*div*/1,s_xflashCtx.rd_instr,0,0); AP_SPIF->wr_completion_ctrl=0xff010005;//set longest polling interval NVIC_DisableIRQ(SPIF_IRQn); NVIC_SetPriority((IRQn_Type)SPIF_IRQn, IRQ_PRIO_HAL); hal_cache_init(); } int hal_flash_erase_all(void) { uint8_t retval; uint32_t cb = AP_PCR->CACHE_BYPASS; HAL_CACHE_ENTER_BYPASS_SECTION(); SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC); spif_wait_nobusy(SFLG_WIP, SPIF_TIMEOUT, ERR_BUSY); retval = spif_erase_all(); SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC); spif_wait_nobusy(SFLG_WELWIP, SPIF_TIMEOUT, ERR_BUSY); HAL_CACHE_EXIT_BYPASS_SECTION(); if(cb == 0) { hal_cache_tag_flush(); } return retval; } /** * @fn void HalFlashInit(void) * @brief flash Initialization function. * @param NONE. * @return NONE. */ ErrCode_t HalFlashInit(void) { hw_spif_cache_config(); hal_pwrmgr_register(MOD_SPIF, NULL, hw_spif_cache_config); return ERR_NONE; } /** * @fn void HalFlashRead(uint32_t offset, uint8_t *buf, uint32_t len) * @brief flash read function. * @param[offset] read address in flash. * @param[buf] receive data buffer. * @param[len] receive data length. * @return NONE. */ void HalFlashRead(uint32_t addr, uint8_t *buf, uint32_t len) { volatile uint8_t *u8_spif_addr = (volatile uint8_t *)((addr & 0x7ffff) | FLASH_BASE_ADDR); uint32_t cb = AP_PCR->CACHE_BYPASS; uint32_t cs = spif_lock(); #if(SPIF_FLASH_SIZE==FLASH_SIZE_1MB) uint32_t remap = addr & 0xf80000; if(remap) { AP_SPIF->remap = remap; AP_SPIF->config |= 0x10000; } #endif //read flash addr direct access //bypass cache if(cb == 0) { HAL_CACHE_ENTER_BYPASS_SECTION(); } for(int i=0; iremap = 0; AP_SPIF->config &= ~0x10000ul; } #endif spif_unlock(cs); } /** * @fn ErrCode_t HalFlashWrite(uint32_t offset, uint8_t *buf, uint16_t len) * @brief flash write function.(offset + len) must be smaller than the current sector * @param offset: write address in flash. * @param buf: write data buffer. * @param len: write data length. * @return ErrCode_t:return ERR_INVALID_PARAM when the input parameter is incorrect; return ERR_NONE when the operation is complete; */ ErrCode_t HalFlashWrite(uint32_t addr, uint8_t* buf, uint32_t len) { ErrCode_t retval; if((buf == NULL) || (len == 0)) return ERR_INVALID_DATA; uint32_t cs = spif_lock(); addr &= 0x00FFFFFF; if( (addr < 0x2000) || (addr > 0x7FFFF) || ((addr+len) > 0x80000) ) { return ERR_INVALID_PARAM; } if(len > 4096) { return ERR_INVALID_LENGTH; } HAL_CACHE_ENTER_BYPASS_SECTION(); SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC); spif_wait_nobusy(SFLG_WIP, SPIF_TIMEOUT, ERR_BUSY); retval = (ErrCode_t)spif_write(addr, buf, len); SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC); spif_wait_nobusy(SFLG_WIP, SPIF_TIMEOUT, ERR_BUSY); HAL_CACHE_EXIT_BYPASS_SECTION(); spif_unlock(cs); return retval; } /** * @fn ErrCode_t HalFlashWrite(uint32_t offset, uint8_t *buf, uint16_t len) * @brief flash write function.(offset + len) must be smaller than the current sector * @param offset: write address in flash. * @param buf: write data buffer. * @param len: write data length. * @return ErrCode_t:return ERR_INVALID_PARAM when the input parameter is incorrect; return ERR_NONE when the operation is complete; */ ErrCode_t HalFlashWriteByDma(uint32_t addr, uint8_t* buf, uint32_t len) { ErrCode_t retval; if(buf == NULL) { return ERR_INVALID_DATA; } if((len == 0) || (len > 4096)) { return ERR_INVALID_LENGTH; } uint32_t cs = spif_lock(); addr &= 0x00FFFFFF; if( (addr < 0x2000) || (addr > 0x7FFFF) || ((addr+len) > 0x80000) ) { return ERR_INVALID_PARAM; } HAL_CACHE_ENTER_BYPASS_SECTION(); SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC); spif_wait_nobusy(SFLG_WIP, SPIF_TIMEOUT, ERR_BUSY); retval = (ErrCode_t)spif_write_dma(addr, buf, len); SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC); spif_wait_nobusy(SFLG_WIP, SPIF_TIMEOUT, ERR_BUSY); HAL_CACHE_EXIT_BYPASS_SECTION(); spif_unlock(cs); return retval; } /** * @fn ErrCode_t HalFlashWriteWord(uint32_t offset, uint32_t value) * @brief flash write function.(offset + len) must be smaller than the current sector * @param offset: write address in flash. * @param value: write data. * @return ErrCode_t:return ERR_INVALID_PARAM when the input parameter is incorrect; return ERR_NONE when the operation is complete; */ ErrCode_t HalFlashWriteWord(uint32_t offset, uint32_t value) { offset &= 0x00FFFFFF; if( (offset < 0x2000) || (offset > 0x7FFFC) ) { return ERR_INVALID_PARAM; } uint32_t temp = value; return (HalFlashWrite(offset, (uint8_t *) &temp, 4)); } /** * @fn ErrCode_t HalFlashErase(uint32_t sectorNum) * @brief erase one sector(4K byte) from falsh. * @param[sectorNum] erase sector number. * @return ErrCode_t:return ERR_INVALID_PARAM when the input parameter is incorrect; return ERR_NONE when the operation is complete; */ ErrCode_t HalFlashErase(uint32_t sectorNum) { sectorNum &= 0x00FFFFFF; if( (sectorNum < 2) || (sectorNum > 0x7F) ) { return ERR_INVALID_PARAM; } uint8_t status = _spif_read_status_reg_x() & FLASH_LOCK_ALL; if(status) { switch(status) { case FLASH_LOCK_504K: if(sectorNum > 0x7D) { return ERR_INVALID_PARAM; } break; case FLASH_LOCK_496K: if(sectorNum > 0x7B) { return ERR_INVALID_PARAM; } break; case FLASH_LOCK_480K: if(sectorNum > 0x77) { return ERR_INVALID_PARAM; } break; case FLASH_LOCK_448K: if(sectorNum > 0x6F) { return ERR_INVALID_PARAM; } break; case FLASH_LOCK_384K: if(sectorNum > 0x5F) { return ERR_INVALID_PARAM; } break; case FLASH_LOCK_HALF: if(sectorNum > 0x3F) { return ERR_INVALID_PARAM; } break; case FLASH_LOCK_ALL: return ERR_INVALID_PARAM; } } ErrCode_t retval; uint32_t cs = spif_lock(); uint32_t cb = AP_PCR->CACHE_BYPASS; HAL_CACHE_ENTER_BYPASS_SECTION(); SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC); spif_wait_nobusy(SFLG_WIP, SPIF_TIMEOUT, ERR_BUSY); retval = (ErrCode_t)spif_erase_sector((sectorNum<<12)); SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC); spif_wait_nobusy(SFLG_WELWIP, SPIF_TIMEOUT, ERR_BUSY); HAL_CACHE_EXIT_BYPASS_SECTION(); if(cb == 0) { hal_cache_tag_flush(); } spif_unlock(cs); return retval; } /** * @fn ErrCode_t HalFlashErase64K(uint32_t addr) * @brief erase (64 Kbyte) from falsh. * @param[addr] erase addr. * @return ErrCode_t:return ERR_INVALID_PARAM when the input parameter is incorrect; return ERR_NONE when the operation is complete; */ ErrCode_t HalFlashErase64K(uint32_t addr) { addr &= 0x00FFFFFF; if( (addr < 0x10000) || (addr > 0x70000) ) { return ERR_INVALID_PARAM; } ErrCode_t retval; uint32_t cs = spif_lock(); uint32_t cb = AP_PCR->CACHE_BYPASS; HAL_CACHE_ENTER_BYPASS_SECTION(); SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC); spif_wait_nobusy(SFLG_WIP, SPIF_TIMEOUT, ERR_BUSY); retval = (ErrCode_t)spif_erase_block64(addr); SPIF_STATUS_WAIT_IDLE(SPIF_WAIT_IDLE_CYC); spif_wait_nobusy(SFLG_WELWIP, SPIF_TIMEOUT, ERR_BUSY); HAL_CACHE_EXIT_BYPASS_SECTION(); if(cb == 0) { hal_cache_tag_flush(); } spif_unlock(cs); return retval; } /** * @fn void HalFlashLock(flash_lock_t lock) * @brief flash lock. * @param none. * @return none */ void HalFlashLock(flash_lock_t lock) { REG_FLASH_WRITE_CTRL = WRITE_ENABLE; REG_FLASH_BP = lock; REG_FLASH_WRITE_CTRL = WRITE_PROTECT; } /** * @fn void HalFlashUnlock(void) * @brief flash unlock. * @param none. * @return none */ void HalFlashUnlock(void) { while( _spif_read_status_reg_x() ) { REG_FLASH_WRITE_CTRL = WRITE_ENABLE; REG_FLASH_BP = FLASH_UNLOCK; REG_FLASH_WRITE_CTRL = WRITE_PROTECT; WaitUs(50); } }