/*
  ******************************************************************************
  * @file    FXX_EFlash.c
  * @author  AisinoChip Firmware Team
  * @version V1.0.0
  * @date    2021  
  * @brief   EFlash module driver.
  *          This file provides firmware functions to manage the following 
  *          functionalities of the internal FLASH memory:
  *           @ Program operations functions
  *           @ Erase   operations functions
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2021 AisinoChip.
  * All rights reserved.
  ******************************************************************************
*/
#include "fxx_std.h"

#define REG(x) 		(*(volatile UINT32 *)(x))  
#define PAGE_SIZE		512U 

void EFlash_ReWrite_Word(UINT32 addr, UINT32 value)	
{
	UINT32 buff[128];
	UINT32 i;
	UINT32 *dst;
	UINT32 dst_addr;
	UINT32 page_addr;  		

	if(REG(addr)==value)
	{
		return;
	}
	
	page_addr = addr&0xFFFFFE00;
	
	dst = (UINT32 *)(page_addr);
	for(i=0;i<(PAGE_SIZE/4); i++)
	{
		buff[i]=*dst++;
	}
	buff[(addr-page_addr)/4] = value; 
	
	EFlash_ErasePage(page_addr);
	
	dst_addr = page_addr;
	for(i=0;i<(PAGE_SIZE/4); i++)
	{
		EFlash_Program_Word(dst_addr,buff[i]);
		dst_addr +=4;
	}		
}

void EFlash_Return_to_Boot(void)	 
{
    EFlash_ReWrite_Word(0x00080400, 0xFFFFFFFFU);    
}

void EFlash_Remap_Enable(void)	       
{
    EFlash_ReWrite_Word(0x00080400, 0x89BC3F51U);      
}

void EFlash_JTAG_Enable(void)	 
{
    EFlash_ReWrite_Word(0x0008041C, 0xFFFFFFFFU);      
}  

void EFlash_JTAG_Diable(void)	 
{
    EFlash_ReWrite_Word(0x0008041C, 0x89BC3F51U);        
}

void EFlash_Option_LOCK(void) 	 
{
    EFlash_ReWrite_Word(0x000805FC, 0x55AA77EEU);        
} 


void Verify_Chip(void)
{
    __asm volatile ("PUSH {R0-R2}");    
    
    __asm volatile ("MOVS R0, #0x10000"); 
    __asm volatile ("MOVS R1, #0x20000000");  
    __asm volatile ("ADDS R1, R1, R0");  
    __asm volatile ("LDR  R2, [R1]");    
    __asm volatile ("POP {R0-R2}");     
}

void EFlash_Init_Para(uint32_t fu32_freq)
{
    uint32_t lu32_RDWait;
    uint32_t lu32_TERASE;
    uint32_t lu32_TPROG;
    uint32_t lu32_TNVS;
    
    /* Eflash Config */
    
    lu32_TERASE = 35 * (fu32_freq/10000U)   + 1;   // 3.5 ms 
    lu32_TPROG  = 9  * (fu32_freq/1000000U) + 1;   // 9us
    lu32_TNVS   = (51 * (fu32_freq/1000000U))/10 + 5;   // 5.1us
    
    if (fu32_freq >= 180000000) 
    {
        lu32_RDWait = 8;
    }
    else if (fu32_freq > 156000000)
    {
        lu32_RDWait = 7; 
    }
    else if(fu32_freq > 136000000)
    {
        lu32_RDWait = 6;   
    } 
    else if(fu32_freq > 116000000)
    {
        lu32_RDWait = 5;   
    }  
    else if(fu32_freq > 92000000)
    {
        lu32_RDWait = 4;   
    }  
    else if(fu32_freq > 74000000) 
    {
        lu32_RDWait = 3;   
    }  
    else if(fu32_freq > 52000000) 
    {
        lu32_RDWait = 2;   
    }  
    else if(fu32_freq > 32000000) 
    {
        lu32_RDWait = 1;
    } 
    else
    {
       lu32_RDWait = 0;
    }

    EFC->TNVS   = lu32_TNVS;
    EFC->CTRL   = (EFC->CTRL & ~(0x1F << 7)) | (lu32_RDWait << 7);
    EFC->TERASE = lu32_TERASE;
    EFC->TPROG  = lu32_TPROG;  

    if (fu32_freq >  126003008)    
    {
        Verify_Chip();             
        SCU->SYSCFG1 |= BIT8;  // clear sram parity error flag   
    }
      
}  

/*********************************************************************************
* Function    : EFlash_ErasePage_Ex
* Description : Erase a Page, TERASE has been configured in System_Clock_Init()
* Input       : 
* Output      : false: FAIL
                true:  SUCCESS
* Author      : Chris_Kyle                         
**********************************************************************************/
void EFlash_ErasePage_EX(uint32_t fu32_Addr)
{
    __set_PRIMASK(1); 
    
    EFC->CTRL |= EFC_CTRL_PAGE_ERASE_MODE;
    
    EFC->SEC = 0x55AAAA55;

    *((volatile uint32_t *)fu32_Addr) = 0;
    
    System_InvalidateDAccelerate_by_Addr((uint32_t *)0x00100014, 32);  // 32 bytes = 1 cache line , cache line one time   
    
    while (!(EFC->STATUS & EFC_STATUS_EFLASH_RDY))   
    {
        System_InvalidateDAccelerate_by_Addr((uint32_t *)0x00100014, 32);     
    } 
    
    EFC->CTRL &= ~EFC_CTRL_PAGE_ERASE_MODE;
    System_InvalidateDAccelerate_by_Addr((uint32_t *)fu32_Addr, PAGE_SIZE);                   
    
    __set_PRIMASK(0);     
}

/*********************************************************************************
* Function    : EFlash_Program_Word_Ex  
* Description : Program a word, TPROG has been configured in System_Clock_Init()
* Input       : 
* Output      : false: FAIL
                true:  SUCCESS 
* Author      : Chris_Kyle                         
**********************************************************************************/
void EFlash_Program_Word_EX(uint32_t fu32_Addr, uint32_t fu32_Data)
{
    __set_PRIMASK(1); 
    
    EFC->CTRL |= EFC_CTRL_PROGRAM_MODE;

    EFC->SEC = 0x55AAAA55;

    *((volatile uint32_t *)fu32_Addr) = fu32_Data;
    System_InvalidateDAccelerate_by_Addr((uint32_t *)0x00100014, 32);       
    
    while (!(EFC->STATUS & EFC_STATUS_EFLASH_RDY))
    {
        System_InvalidateDAccelerate_by_Addr((uint32_t *)0x00100014, 32);     
    } 

    EFC->CTRL &= ~EFC_CTRL_PROGRAM_MODE;        
    
    __set_PRIMASK(0);     
} 




