/***********************************************************************
 * Copyright (c)  2017 - 2020, Unicmicro Co.,Ltd .
 * All rights reserved.
 * Filename    : flash.c
 * Description : flash source file
 * Author(s)   : Eric
 * version     : V1.0
 * Modify date : 2021-11-8
 ***********************************************************************/
#include  "flash.h"


/************************************************************************
 * function   : eflash_init
 * Description: eflash initialization function
 * input : cpu_hz: system clock, unit: Hz
 * return: none
 ************************************************************************/
void eflash_init(uint32_t cpu_hz)  
{	
	uint8_t wait_time;
	uint32_t cpu_mhz;
	wait_time = 0;

	REG_SCU_PERICLKEN |= (1<<20);
	REG_EFC_CTRL = (REG_EFC_CTRL &(~(0x0f<<3))) | (wait_time << 3);
	
	cpu_mhz = cpu_hz/1000000; 
	//REG_EFC_HALFUS = (uint32_t)((cpu_mhz*0.5)-1);
	REG_EFC_HALFUS = (uint32_t)((cpu_mhz>>1)-1);

	REG_EFC_INTSTATUS = 0x1f;    //清除中断标志位
	REG_EFC_INEN = 0x00;		 //关闭中断
}



/************************************************************************
 * function   : eflash_erase_page
 * Description: erase the eflash memory of specific page
 * input : page_addr
 * return: none
 ************************************************************************/
uint8_t eflash_erase_page(uint32_t page_addr)	  
{
	uint32_t temp;
	
	temp = NVIC->ISER[0];  
	NVIC->ICER[0]=0xffffffff ;   
	
	REG_EFC_CTRL |= EFC_PAGE_ERASE_MODE;
	REG_EFC_SEC = 0x55AAAA55;
	*((volatile uint32_t *)(page_addr)) = 0;
	while( !(REG_EFC_STATUS & 0x01) );
	REG_EFC_CTRL &= ~EFC_PAGE_ERASE_MODE;
	
	NVIC->ISER[0]=temp;	        
	return 1;
}

/************************************************************************
 * function   : eflash_write_word
 * Description: write word to eflash memory
 * input : addr, value
 * return: 
            1 : eflash_write_word success
			0 : eflash_write_word fail
 ************************************************************************/
uint8_t eflash_write_word(uint32_t addr,uint32_t value)
{
	uint32_t temp;
	
	temp = NVIC->ISER[0];
	NVIC->ICER[0]=0xffffffff ;  	
	
	REG_EFC_CTRL |= EFC_WRITE_MODE;
	REG_EFC_SEC = 0x55AAAA55;
	*((volatile uint32_t *)(addr)) = value;
	while( !(REG_EFC_STATUS & 0x01));
	REG_EFC_CTRL &= ~EFC_WRITE_MODE;
	
	NVIC->ISER[0]=temp;			
	return 1;
}
/************************************************************************
 * function   : eflash_write_halfword
 * Description: write halfword to eflash memory
 * input : addr, value
 * return: none
 ************************************************************************/
uint8_t eflash_write_halfword(uint32_t addr,uint16_t value)
{
	uint32_t temp;
	
	temp = NVIC->ISER[0];
	NVIC->ICER[0]=0xffffffff ;  
	
	REG_EFC_CTRL |= EFC_WRITE_MODE;
	REG_EFC_SEC = 0x55AAAA55;
	*((volatile uint16_t *)(addr)) = value;
	while( !(REG_EFC_STATUS & 0x01));
	REG_EFC_CTRL &= ~EFC_WRITE_MODE;
		
	NVIC->ISER[0]=temp;	
	return 1;	
}

/************************************************************************
 * function   : eflash_write_byte
 * Description: write byte to eflash memory
 * input : addr, value
 * return: none
 ************************************************************************/
uint8_t eflash_write_byte(uint32_t addr,uint8_t value)
{
	uint32_t temp;
	
	temp = NVIC->ISER[0];
	NVIC->ICER[0]=0xffffffff ;  
	
	REG_EFC_CTRL |= EFC_WRITE_MODE;
	REG_EFC_SEC = 0x55AAAA55;
	*((volatile uint8_t *)(addr)) = value;
	while( !(REG_EFC_STATUS & 0x01));
	REG_EFC_CTRL &= ~EFC_WRITE_MODE;		

	NVIC->ISER[0]=temp;		
	return 1;	  
}

/************************************************************************
 * function   : eflash_rewrite_byte
 * Description: rewrite byte to eflash memory
 * input : addr, value
 * return: none
 ************************************************************************/
uint8_t eflash_rewrite_word(uint32_t addr, uint32_t value)	
{
	uint32_t buff[128];
	uint32_t i;
	uint32_t *dst;
	uint32_t dst_addr;
	uint32_t page_addr;  		//page基地址	
	
	if(eflash_read_word(addr)==value)
	{
		return 1;
	}	
	

	page_addr = addr&0xFFFFFE00; //for page size 512
	dst = (uint32_t *)(page_addr);
	for(i=0;i<(PAGE_SIZE/4); i++)
	{
		buff[i]=*dst++;
	}
		
	buff[(addr-page_addr)/4] = value;  //更改目标字内容	
		
	eflash_erase_page(page_addr);
		
	dst_addr = page_addr;
	for(i=0;i<(PAGE_SIZE/4); i++)
	{
		eflash_write_word(dst_addr,buff[i]);
		dst_addr +=4;
	}	
		
	return 1;	
	
}

/*----------------------------SN--------------------------------*/

#define SN_ADDR     						(0x01010248 )  //SN addr
#define SN_CRC_ADDR     					(0x01010258 )  //SN CRC value

//CRC16-CCITT校验算法
#define CRC_INIT	0xffff	  //在CRC16-CCITT标准中reg_init = 0xffff
static uint16_t do_crc(uint32_t addr, uint32_t len, uint16_t crc_init)
{
    uint32_t i, j;
    uint16_t crc_reg;      //reg for calculate CRC value
    uint16_t current;

    crc_reg = crc_init;  //initial value for CRC16-CCITT
    for(i = 0; i < len; i++)
    {
        current = *(volatile uint8_t *)(addr + i);
        for(j = 0; j < 8; j++)
        {
            if((crc_reg ^ current) & 0x0001) crc_reg = (crc_reg >> 1) ^ 0x8408;  //CRC16-CCITT的生成正序多项式是0x8408（将0x1021按位颠倒后为：0x8408）
            else crc_reg >>= 1;
            current >>= 1;
        }
    }
    return crc_reg;
}

static uint16_t check_crc_sn(void)
{
    uint16_t crc_value = 0;
    uint16_t crc_count = 0;
    uint32_t temp;
    temp = (*(volatile uint32_t *)(SN_CRC_ADDR));
    crc_value = temp & 0xFFFF;
    temp  = (~temp) >> 16;
    if(temp != crc_value)
    {
        return 0xFFFF;   //CRC 未写入
    }
    else
    {
        crc_count = do_crc(SN_ADDR, 16, CRC_INIT);
        if(crc_count == crc_value)    return 0;             //CRC OK
        else    return crc_count;     //CRC fail
    }

}
/************************************************************************
 * function   : read_sequence
 * Description: read unique SN（16bytes）
 * input :
 *         uint8_t * buff：SN buff pointer 
 * return: 0：CRC pass， 
           1：CRC fail
 ************************************************************************/
uint16_t read_sequence(uint8_t *buff)
{
    uint8_t i = 0;
    uint16_t temp;
    uint8_t *p;
    p = (uint8_t *)SN_ADDR;
    for(i = 0; i < 16; i++)
    {
        *buff++ = *p++;
    }
    //check SN crc
    temp = check_crc_sn();
    return temp;
}
/************************************************************************
 * function   : read_UID
 * Description: read unique SN（8bytes）
 * input :
 *         uint8_t * buff：SN buff pointer   
 * return: 0：CRC pass， 
           1：CRC fail
 ************************************************************************/

uint16_t read_UID(uint8_t *buff)
{
    uint32_t temp = 0;
    temp =  (*(volatile uint32_t *)(SN_ADDR));
    buff[0] = temp;
    buff[1] = temp >> 8;
    buff[2] = temp >> 16;
    buff[3] = temp >> 24;

    temp =  (*(volatile uint8_t *)(SN_ADDR+4));
    buff[4] = temp;

    temp =  (*(volatile uint16_t *)(SN_ADDR+8));
    buff[5] = temp;
    temp =  (*(volatile uint16_t *)(SN_ADDR+10));
    buff[6] = temp;

    temp =  (*(volatile uint8_t *)(SN_ADDR+14));
    buff[7] = temp;

    //check SN crc
    temp = check_crc_sn();
    return temp;
}


