// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// File name    : eflash_drv.c
// Version      : V0.1
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//
// 2023/09/06 Bert Lee modified some function's return value to coordinate with LT776 
//

#include "common.h"
#include "libEflash.h"
#include "eflash_drv.h"
#include <string.h>
#include "memmap.h"

UINT32  eflash_buffer[EFLASH_PAGE_SIZE_BYTE/4];

/*******************************************************************************
* Function Name  : EFLASH_Set_RWSC
* Description    : Set FLASH RWSC
* Input          : rwsc: rwsc default value is 0x07
*
* Output         : None
* Return         : None
******************************************************************************/

void EFLASH_Set_RWSC(UINT8 rwsc)
{
	UINT32 efcr_reg = readl(EFM_BASE_ADDR);
	
	efcr_reg &= (~0x0000000F);
	efcr_reg |= (rwsc & 0x0F);
	
	writel(EFM_BASE_ADDR,efcr_reg);
}

/*******************************************************************************
* Function Name  : EFLASH_Init
* Description    : EFLASH initialize 
* Input          : f_sys_khz: Eflash clock (unit is KHz)
* Output         : None
* Return         : None
******************************************************************************/
void EFLASH_Init(UINT32 sys_clk_khz)
{
	EFM_Init(sys_clk_khz);
}

/*******************************************************************************
* Function Name  : EFLASH_SetWritePermission
* Description    : Enable write permission
* Input          : None
*
* Output         : None
* Return         : None
******************************************************************************/
void EFLASH_SetWritePermission(void)
{
	EFlash_Set_Main_Permission(EFLASH_WRITEABLE, EFLASH_READABLE);
}

/*******************************************************************************
* Function Name  : EFLASH_ClrWritePermission
* Description    : Disable write permission
* Input          : None
*
* Output         : None
* Return         : None
******************************************************************************/
void EFLASH_ClrWritePermission(void)
{
	EFlash_Set_Main_Permission(EFLASH_DISWRITEABLE, EFLASH_READABLE);
}

/*******************************************************************************
* Function Name  : EFLASH_ErasePage
* Description    : EFLASH erase page  
* Input          : addr: 要擦除的EFLASH页地址
*
* Output         : None
* Return         :  0: Okay, 1: Failure (Bert changed)
******************************************************************************/
UINT8 EFLASH_ErasePage(UINT32 addr)
{
	u8 status;
	EFLASH_SetWritePermission();
	status=EFlash_Page_Erase(addr);
	EFLASH_ClrWritePermission();
	return(status);
}

/*******************************************************************************
* Function Name  : EFLASH_ReadByte
* Description    : Read a byte from addr
* Input          : - addr: EFLASH destination address
*
* Output         : None
* Return         : a byte data
******************************************************************************/
UINT8 EFLASH_ReadByte(UINT32 addr)
{
	return (*(volatile UINT8 *)(addr));
}

/*******************************************************************************
* Function Name  : EFLASH_ReadBytes
* Description    : Read consecutive bytes from addr
* Input          : addr: EFLASH destination address
*					  : *buffer: read buffer pointer
*					  : length: byte count
*
* Output         : Data in *bufffer 
* Return         : None 
******************************************************************************/

void EFLASH_ReadBytes(UINT32 address, UINT8 *buffer, UINT32 length)
{
  UINT32 i;
  for (i = 0; i < length; i++)
  {
    buffer[i] = (*(volatile UINT8 *)(address + i));
  }
}


/*******************************************************************************
* Function Name  : EFLASH_HWordRead
* Description    : Read a 16-bit data from addr
* Input          : - addr: EFLASH destination address
*
* Output         : None
* Return         : a 16-bit data
******************************************************************************/
UINT16 EFLASH_ReadHWord(UINT32 addr)
{
	return (*(volatile UINT16 *)(addr));
}

/*******************************************************************************
* Function Name  : EFLASH_ReadWord
* Description    : Read a 32-bit data from addr
* Input          : - addr: EFLASH destination address
*
* Output         : None
* Return         : a 32-bit data
******************************************************************************/
UINT32 EFLASH_ReadWord(UINT32 addr)
{
	return (*(volatile UINT32 *)(addr));
}

/*******************************************************************************
* Function Name  : EFLASH_ReadWords
* Description    : Read consecutive 32-bit data from addr
* Input          : - addr: EFLASH destination address
*					    - **buffer: data buffer pointer
*                  - length: word count
*
* Output         : - des: Data in *bufffer
* Return         : None
******************************************************************************/
void EFLASH_ReadWords(UINT32 addr, UINT32 *buffer, UINT32 length)
{
	UINT32 i;

	for(i = 0; i < length; i ++)
	{
		buffer[i] = (*(volatile UINT32 *)(addr+i*4));
	}
}

/*******************************************************************************
* Function Name  : EFLASH_WriteWord
* Description    : Write a 32-bit data in addr
* Input          : - addr: EFLASH destination address
*                  - data: data  
*
* Output         : None
* Return         :  0: Okay, 1: Failure
******************************************************************************/
UINT8 EFLASH_WriteWord(UINT32 addr, UINT32 data)
{
	u8 status;
	EFLASH_SetWritePermission();
	status=EFlash_Bulk_Program((addr), 1, &data);
	EFLASH_ClrWritePermission();
	return(status);
}

/*******************************************************************************
* Function Name  : EFLASH_WriteWords
* Description    : Write consecutive 32-bit data in addr
* Input          : - addr: EFLASH destination address
*                  - buffer: data
*                  - length: word count
*
* Output         : None
* Return         :  0: Okay, 1: Failure
******************************************************************************/
UINT8 EFLASH_WriteWords(UINT32 addr, UINT32 *buffer, UINT32 length)
{
	u8 status;
	EFLASH_SetWritePermission();
	status=EFlash_Bulk_Program((addr), length, buffer);
	EFLASH_ClrWritePermission();
	return(status);
}

/*******************************************************************************
* Function Name  : FLASH_CheckAddr
Description      : Check if the Eflash address is available 
* Return         :  0: available, 1: Not available
******************************************************************************/
static UINT8 EFLASH_CheckAddr(UINT32 flash_addr,UINT32 len)
{
	if((flash_addr%4) != 0) return 1;
	if((flash_addr < EFLASH_BASE_ADDR+BOOTLOADER_SIZE) || (flash_addr+len) > (EFLASH_BASE_ADDR + EFLASH_BYTE_SIZE - 0x1000))
		return 1;
	return 0;
}

/*******************************************************************************
* Function Name  : EFLASH_WritePageBytes
* Description    : Write consecutive byte data in address which is page begin address 
*                  , and erase before writing 
* Input          : - addr: EFLASH destination address (Must be aligned each page bagin address)
*                  - buffer: data buffer pointer
*                  - length: byte count
*
* Output         : None
* Return         :  0: Okay, 1: Failure (Bert changed)
******************************************************************************/
UINT8 EFLASH_WritePage(UINT32 addr, const UINT8* buffer, UINT32 length)
{
	UINT16 offset;
	UINT16 pageleft;
	UINT16 pageright;
	UINT32 dest = addr;
	UINT8* src  = (UINT8*)buffer;
	UINT8 result=0;
	
	pageright = pageright;

	if (EFLASH_CheckAddr(addr,length)) return 1;

	offset  = ((UINT32)dest & PAGE_MASK);
	pageleft  = EFLASH_PAGE_SIZE_BYTE - offset;
	pageright = ((UINT16)(dest + length) & PAGE_MASK);

	EFLASH_SetWritePermission();
	
	/*如果页没有对齐，则先对第一页操作*/
	if (offset != 0)
	{
		dest -= offset;/*dest指针跳转到当前页头*/
		memcpy((UINT8*)eflash_buffer, (UINT8*)dest, EFLASH_PAGE_SIZE_BYTE);
		if (length>pageleft)
		{
			memcpy((UINT8*)eflash_buffer + offset, src, pageleft);
			length  -= pageleft;
			src  += pageleft;
		}
		else
		{
			memcpy((UINT8*)eflash_buffer + offset, src, length);
			length=0;
		}
		//result = EFLASH_ErasePage(dest);
		result =EFlash_Page_Erase(addr);
		result =EFlash_Bulk_Program((dest), length, eflash_buffer);
		//result = EFLASH_WriteWords(dest, eflash_buffer, EFLASH_PAGE_SIZE_BYTE/4);
		dest += EFLASH_PAGE_SIZE_BYTE;
		//offset = 0;
	}
	while (length > 0)
	{
		/*如果存在中间页*/
		if (length >= EFLASH_PAGE_SIZE_BYTE)
		{
			memcpy((UINT8*)eflash_buffer, src, EFLASH_PAGE_SIZE_BYTE);
			//result = EFLASH_ErasePage(dest);
			result =EFlash_Page_Erase(addr);
			//result = EFLASH_WriteWords(dest, eflash_buffer, EFLASH_PAGE_SIZE_BYTE/4);
			result =EFlash_Bulk_Program((dest), length, eflash_buffer);
			dest += EFLASH_PAGE_SIZE_BYTE;
			src  += EFLASH_PAGE_SIZE_BYTE;
			length  -= EFLASH_PAGE_SIZE_BYTE;
		}
		else
		{   /*做最后页操作*/
			memcpy((UINT8*)eflash_buffer, (UINT8*)dest, EFLASH_PAGE_SIZE_BYTE);
			memcpy((UINT8*)eflash_buffer, src, length);
			//result = EFLASH_ErasePage(dest);
			result =EFlash_Page_Erase(addr);
			//result = EFLASH_WriteWords(dest, eflash_buffer, EFLASH_PAGE_SIZE_BYTE/4);
			result =EFlash_Bulk_Program((dest), length, eflash_buffer);
			length = 0;
		}
	}

	EFLASH_ClrWritePermission();
	return result;
}
