
/******************************************************************************
*@file  : spl_eflash.h
*@brief : EFlash SPL module driver header file.
*@ver   : 1.0.0
*@date  : 2022.10.20
******************************************************************************/

#ifndef __SPL_EFLASH_H__
#define __SPL_EFLASH_H__

#include "acm32g103_spl_conf.h"



typedef enum
{ 
  EFLASH_COMPLETE=0,
  EFLASH_ERROR_PG
}EFLASH_Status;


//#define EFLASH_BASE_ADDR            (0UL)

#define EFLASH_PAGE_SIZE                    (512UL)

#define EFLASH_MAIN_ADDR                    EFLASH_BASE_ADDR
#define EFLASH_MAIN_SIZE                    (0x50000UL)

#define EFLASH_NVR_ADDR                     (EFLASH_BASE_ADDR+0x80000)
#define EFLASH_NVR_SIZE                     (EFLASH_PAGE_SIZE * 8)

#define EFLASH_NVR1_ADDR                    (EFLASH_NVR_ADDR+0x00000)
#define EFLASH_NVR2_ADDR                    (EFLASH_NVR_ADDR+0x00200)
#define EFLASH_NVR3_ADDR                    (EFLASH_NVR_ADDR+0x00400)
#define EFLASH_NVR4_ADDR                    (EFLASH_NVR_ADDR+0x00600)
#define EFLASH_NVR5_ADDR                    (EFLASH_NVR_ADDR+0x00800)
#define EFLASH_NVR6_ADDR                    (EFLASH_NVR_ADDR+0x00a00)
#define EFLASH_NVR7_ADDR                    (EFLASH_NVR_ADDR+0x00c00)
#define EFLASH_NVR8_ADDR                    (EFLASH_NVR_ADDR+0x00e00)


/***************  addr definition for EFLASH_NVR1 region  *******************/

#define EFLASH_NVR_OTP1_EN_ADDR             (EFLASH_NVR_ADDR + 0x1FC)

/***************  addr definition for EFLASH_NVR2 region  *******************/

#define EFLASH_NVR_DFT_DIS_ADDR             (EFLASH_NVR_ADDR + 0x200)
#define EFLASH_NVR_BUS_CRYPT_EN_ADDR        (EFLASH_NVR_ADDR + 0x204)
#define EFLASH_NVR_UID0_ADDR                (EFLASH_NVR_ADDR + 0x208)
#define EFLASH_NVR_UID1_ADDR                (EFLASH_NVR_ADDR + 0x20C)
#define EFLASH_NVR_UID2_ADDR                (EFLASH_NVR_ADDR + 0x210)
#define EFLASH_NVR_UID3_ADDR                (EFLASH_NVR_ADDR + 0x214)
#define EFLASH_NVR_UID_CRC_ADDR             (EFLASH_NVR_ADDR + 0x218)
#define EFLASH_NVR_JTAG_DIS_ADDR            (EFLASH_NVR_ADDR + 0x21C)
#define EFLASH_NVR_CFG_DENSITY_ADDR         (EFLASH_NVR_ADDR + 0x220)
#define EFLASH_NVR_RC64M_TRIM_ADDR          (EFLASH_NVR_ADDR + 0x228)
#define EFLASH_NVR_RC64M_VAL_ADDR           (EFLASH_NVR_ADDR + 0x22C)
#define EFLASH_NVR_RC32K_VAL_ADDR           (EFLASH_NVR_ADDR + 0x230)
#define EFLASH_NVR_LDO_TRIM_ADDR            (EFLASH_NVR_ADDR + 0x234)
#define EFLASH_NVR_LDO_TRIM_NOT_ADDR        (EFLASH_NVR_ADDR + 0x238)
#define EFLASH_NVR_RC32K_TRIM_ADDR          (EFLASH_NVR_ADDR + 0x23C)
#define EFLASH_NVR_ADC_1V2_VAL_ADDR         (EFLASH_NVR_ADDR + 0x240)
#define EFLASH_NVR_ADC_VREFBI_VTRIM_ADDR    (EFLASH_NVR_ADDR + 0x244)
#define EFLASH_NVR_DAC_CAL_OS_ADDR          (EFLASH_NVR_ADDR + 0x248)
#define EFLASH_NVR_OPA1_CAL_OSPN_ADDR       (EFLASH_NVR_ADDR + 0x24C)
#define EFLASH_NVR_OPA2_CAL_OSPN_ADDR       (EFLASH_NVR_ADDR + 0x250)
#define EFLASH_NVR_OPA3_CAL_OSPN_ADDR       (EFLASH_NVR_ADDR + 0x254)
#define EFLASH_NVR_CHIP_VERSION_ADDR        (EFLASH_NVR_ADDR + 0x268)
#define EFLASH_NVR_CP1_2_FLAG_ADDR          (EFLASH_NVR_ADDR + 0x270)
#define EFLASH_NVR_BOOT_CLK_DIV_ADDR        (EFLASH_NVR_ADDR + 0x28C)
#define EFLASH_NVR_UART_AUTOBAND_EN_ADDR    (EFLASH_NVR_ADDR + 0x2B8)
#define EFLASH_NVR_USB_BOOT_EN_ADDR         (EFLASH_NVR_ADDR + 0x2BC)
#define EFLASH_NVR_CP3_FLASH_CP_ADDR        (EFLASH_NVR_ADDR + 0x2C4)
#define EFLASH_NVR_CHIP_MODEL_H_ADDR        (EFLASH_NVR_ADDR + 0x2D0)
#define EFLASH_NVR_CHIP_MODEL_L_ADDR        (EFLASH_NVR_ADDR + 0x2D4)
#define EFLASH_NVR_CFG_IP1_ADDR             (EFLASH_NVR_ADDR + 0x300)
#define EFLASH_NVR_CFG_IP2_ADDR             (EFLASH_NVR_ADDR + 0x304)
#define EFLASH_NVR_ROM_JTAG_DIS_ADDR        (EFLASH_NVR_ADDR + 0x304)
#define EFLASH_NVR_CFG_IP3_ADDR             (EFLASH_NVR_ADDR + 0x30C)
#define EFLASH_NVR_TRIM0_L_ADDR             (EFLASH_NVR_ADDR + 0x380)
#define EFLASH_NVR_TRIM0_H_ADDR             (EFLASH_NVR_ADDR + 0x384)
#define EFLASH_NVR_TRIM1_L_ADDR             (EFLASH_NVR_ADDR + 0x388)
#define EFLASH_NVR_TRIM1_H_ADDR             (EFLASH_NVR_ADDR + 0x38C)
#define EFLASH_NVR_TRIM2_L_ADDR             (EFLASH_NVR_ADDR + 0x390)
#define EFLASH_NVR_TRIM2_H_ADDR             (EFLASH_NVR_ADDR + 0x394)
#define EFLASH_NVR_OTP2_EN_ADDR             (EFLASH_NVR_ADDR + 0x3FC)

/***************  addr definition for EFLASH_NVR3 region: user region *******/

#define EFLASH_NVR_REMAP_ADDR               (EFLASH_NVR_ADDR + 0x400)
#define EFLASH_NVR_JTAG_DIS_2_ADDR          (EFLASH_NVR_ADDR + 0x41C)
#define EFLASH_NVR_CODE_CRC16_ADDR          (EFLASH_NVR_ADDR + 0x458)
#define EFLASH_NVR_CODE_LEN_ADDR            (EFLASH_NVR_ADDR + 0x45C)
#define EFLASH_NVR_CODE_START_ADDR          (EFLASH_NVR_ADDR + 0x460)
#define EFLASH_NVR_CODE_VALID_ADDR          (EFLASH_NVR_ADDR + 0x478)
#define EFLASH_NVR_ERASE_SRAM_EN_ADDR       (EFLASH_NVR_ADDR + 0x47C)
#define EFLASH_NVR_RDP1_DEGRADE_ADDR        (EFLASH_NVR_ADDR + 0x488)
#define EFLASH_NVR_OTP3_EN_ADDR             (EFLASH_NVR_ADDR + 0x5FC)

/***************  addr definition for EFLASH_NVR4 region: mem protection *****/
                                                               
#define EFLASH_NVR_PCROP_EN_ADDR            (EFLASH_NVR_ADDR + 0x600)
#define EFLASH_NVR_PCROP_AREA_A_ADDR        (EFLASH_NVR_ADDR + 0x604)
#define EFLASH_NVR_PCROP_AREA_B_ADDR        (EFLASH_NVR_ADDR + 0x608)
#define EFLASH_NVR_WRP_EN_ADDR              (EFLASH_NVR_ADDR + 0x620)
#define EFLASH_NVR_WRP_AREA_A_ADDR          (EFLASH_NVR_ADDR + 0x624)
#define EFLASH_NVR_WRP_AREA_B_ADDR          (EFLASH_NVR_ADDR + 0x628)
#define EFLASH_NVR_SEC_SIZE_VALID_ADDR      (EFLASH_NVR_ADDR + 0x640)
#define EFLASH_NVR_SEC_SIZE_ADDR            (EFLASH_NVR_ADDR + 0x644)
#define EFLASH_NVR_RDP1_EN_ADDR             (EFLASH_NVR_ADDR + 0x660)
#define EFLASH_NVR_OTP4_EN_ADDR             (EFLASH_NVR_ADDR + 0x7FC)

/***************  eflaht read wait   *******************/

#define EFLASH_RDWAIT_0WS                       ( 0U ) 
#define EFLASH_RDWAIT_1WS                       ( 1U ) 
#define EFLASH_RDWAIT_2WS                       ( 2U ) 
#define EFLASH_RDWAIT_3WS                       ( 3U ) 
#define EFLASH_RDWAIT_4WS                       ( 4U ) 
#define EFLASH_RDWAIT_5WS                       ( 5U ) 
#define EFLASH_RDWAIT_6WS                       ( 6U ) 
#define EFLASH_RDWAIT_7WS                       ( 7U ) 
#define EFLASH_RDWAIT_8WS                       ( 8U ) 
#define EFLASH_RDWAIT_MAX                       ( 0x1F ) 
#define EFLASH_RDWAIT_ENSURE_OK                 ( EFLASH_RDWAIT_8WS )

#define EFLASH_SET_RDWAIT(wait)             {EFC->CTRL   = (EFC->CTRL & ~(EFC_CTRL_RDWAIT_Msk)) | (wait << EFC_CTRL_RDWAIT_Pos);}  

#define EFLASH_READ_WORD(Addr)              (*(volatile uint32_t *)(Addr))    // Read By Word
#define EFLASH_READ_HALFWORD(Addr)          (*(volatile uint16_t *)(Addr))    // Read By Half Word
#define EFLASH_READ_BYTE(Addr)              (*(volatile uint8_t *)(Addr))     // Read By Byte

/******************************************************************************
*@brief : Configure eflash parameter by system core clock freq.
*@param : sysClkFreq: system clock freq.
*@return: none
******************************************************************************/
__STATIC_INLINE void EFLASH_SetRdwaitBySysCoreClock(uint32_t sysCoreClkFreq)
{
    volatile int delay;
    uint32_t rdWait;  

    if(sysCoreClkFreq > 120000000)
    {
        rdWait = 6;   
    }    
    else if(sysCoreClkFreq > 100000000)
    {
        rdWait = 5;   
    }  
    else if(sysCoreClkFreq > 80000000)
    {
        rdWait = 4;   
    }  
    else if(sysCoreClkFreq > 60000000) 
    {
        rdWait = 3;   
    }  
    else if(sysCoreClkFreq > 40000000) 
    {
        rdWait = 2;   
    }  
    else if(sysCoreClkFreq > 20000000) 
    {
        rdWait = 1;
    } 
    else
    {
       rdWait = 6;
    }

    if(EFC->CTRL & EFC_CTRL_RECALLREADMODE)
    {
        EFC->CTRL &= ~EFC_CTRL_RECALLREADMODE;
        
        //after modify RECALLREADMODE, must delay 10us
        delay=500;
        while(delay)
        {
            delay--;
        }
    }
    EFLASH_SET_RDWAIT(rdWait);
}

/******************************************************************************
*@brief : 恢复FLASH TRIM参数。从NVR读取，写入EFC_TRIM0/1/2 寄存器。代码必须在SRAM中。
*@return: status
******************************************************************************/
__STATIC_INLINE void EFLASH_RestoreFlashTrim(void)
{
    uint32_t trim0,trim1,trim2,dat,dat2,dat3,errflag=0;
    volatile uint32_t i;
    
    
    //read and check trim0
    
    dat = EFLASH_READ_WORD(EFLASH_NVR_TRIM0_L_ADDR);
    dat2 = dat & 0xffff;
    dat3 = ~(dat>>16);    
    if((dat2 & dat3) == 0)
    {
        trim0 = dat2;
    }
    else
    {
        errflag = 1;
    }

    dat = EFLASH_READ_WORD(EFLASH_NVR_TRIM0_H_ADDR);
    dat2 = dat & 0xffff;
    dat3 = ~(dat>>16);    
    if((dat2 & dat3) == 0)
    {
        trim0 |= (dat2<<16);
    }       
    else
    {
        errflag = 1;
    } 

    //read and check trim1    
    
    dat = EFLASH_READ_WORD(EFLASH_NVR_TRIM1_L_ADDR);
    dat2 = dat & 0xffff;
    dat3 = ~(dat>>16);    
    if((dat2 & dat3) == 0)
    {
        trim1 = dat2;
    }
    else
    {
        errflag = 1;
    }

    dat = EFLASH_READ_WORD(EFLASH_NVR_TRIM1_H_ADDR);
    dat2 = dat & 0xffff;
    dat3 = ~(dat>>16);    
    if((dat2 & dat3) == 0)
    {
        trim1 |= (dat2<<16);
    }       
    else
    {
        errflag = 1;
    }        
    
    //read and check trim2    
    
    dat = EFLASH_READ_WORD(EFLASH_NVR_TRIM2_L_ADDR);
    dat2 = dat & 0xffff;
    dat3 = ~(dat>>16);    
    if((dat2 & dat3) == 0)
    {
        trim2 = dat2;
    }
    else
    {
        errflag = 1;
    }

    dat = EFLASH_READ_WORD(EFLASH_NVR_TRIM2_H_ADDR);
    dat2 = dat & 0xffff;
    dat3 = ~(dat>>16);    
    if((dat2 & dat3) == 0)
    {
        trim2 |= (dat2<<16);
    }       
    else
    {
        errflag = 1;
    } 

    //if check error, write default
    if(errflag != 0)
    {
        trim0= 0x000d0007;
        trim1= 0xB0301800;
        trim2= 0xC002000A;     
    }
    
    EFC->TRIM0 = trim0;
    EFC->TRIM1 = trim1;
    EFC->TRIM2 = trim2;
    for(i = 0; i < 100; i++);      
}


uint32_t EFLASH_EnterCritical(void);
void EFLASH_ExitCritical(uint32_t sr);
EFLASH_Status EFLASH_ErasePage(uint32_t addr);
EFLASH_Status EFLASH_ErasePages(uint32_t addr,uint32_t pageNnum);
EFLASH_Status EFLASH_Erase(uint32_t addr, uint32_t len);
EFLASH_Status EFLASH_ProgramWord(uint32_t addrAlign4, uint32_t dat);
EFLASH_Status EFLASH_ProgramWords(uint32_t addrAlign4, void *buff, uint32_t wordNum);
void EFLASH_Read(uint32_t addr, void *buff, uint32_t byteNum);
EFLASH_Status EFLASH_BackupEraseProgram(uint32_t addr, void *buff, uint32_t byteNum);
EFLASH_Status EFLASH_EraseSecurityCode(void);
EFLASH_Status EFLASH_DisableJtag(void);
EFLASH_Status EFLASH_EnableJtag(void);

#endif
