/*
  ******************************************************************************
  * @file    EFlash.c
  * @version V1.0.0
  * @date    2024
  * @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
  ******************************************************************************
*/
#include "fxx_std.h"

/*********************************************************************************
* Function    : EFlash_Init
* Description : Configure eflash parameter as system clock 
* Input       : system clock frequency 
* Output      : None
* Author      : Chris_Kyle                      
**********************************************************************************/
void EFlash_Init(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 > 40000000) 
    {
        lu32_RDWait = 1;   
    }  
    else
    {
       lu32_RDWait = 0;
    }

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

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

    *((volatile uint32_t *)fu32_Addr) = 0;
    
    while (!(EFC->STATUS & EFC_STATUS_EFLASH_RDY));
    
    EFC->CTRL &= ~EFC_CTRL_PAGE_ERASE_MODE;

    return true;
}

/*********************************************************************************
* Function    : EFlash_Programe
* Description : Program a word, TPROG has been configured in System_Clock_Init()
* Input       : 
* Output      : false: FAIL
                true:  SUCCESS
* Author      : Chris_Kyle                         
**********************************************************************************/
bool EFlash_Program_Word(uint32_t fu32_Addr, uint32_t fu32_Data)
{
    if (fu32_Addr % 4)
    {
        return false; 
    }

    EFC->CTRL |= EFC_CTRL_PROGRAM_MODE;

    EFC->SEC = 0x55AAAA55;

    *((volatile uint32_t *)fu32_Addr) = fu32_Data;

    while (!(EFC->STATUS & EFC_STATUS_EFLASH_RDY));

    EFC->CTRL &= ~EFC_CTRL_PROGRAM_MODE;
    
    return true;
}

#define REG(x) 		(*(volatile uint32_t *)(x))  

void 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;  		

	if(REG(addr) == value)
	{
		return;
	}
	
	page_addr = addr&0xFFFFFE00;
	
	dst = (uint32_t *)(page_addr);
	for(i=0;i<(EFLASH_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<(EFLASH_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);        
}  




