/*******************************************************************************
** Copyright (c) 2022 KPTL
**
** This software is the property of KPTL.
** It can not be used, duplicated and/or distributed without the express
** permission of KPTL.
**
** -----------------------------------------------------------------------------
** File Name    : FlashIntf.c
** Module Name  : Flash Module BSW Source File
** -----------------------------------------------------------------------------
**
** Description : To implement routines that provide applications with a
**               simplified interface to the underlying (low-level)
**               Flash (F021) driver
**
** This file must exclusively contain information needed to
** use this component.
**
********************************************************************************
** R E V I S I O N  H I S T O R Y
********************************************************************************
** V01.00 24/12/2021    Initial Creation
********************************************************************************/
#include "system.h"
#include "sys_core.h"

#include "F021.h"

#include "FlashIntf.h"
#include "FlashIntf_Cfg.h"


/* Data structure to hold Device Flash Information */
static FlashDevInfo_t St_FDevInfo = {0};


/* Prototypes for static functions */
static uint64_t FlashIntf_GetWriteSectorMask(Fapi_FlashBankType Enm_Bank, uint32_t Ui_StartAddr, uint32_t Ui_EndAddr);
static FlashIntf_RetCode_t FlashIntf_MainBankWrite(uint32_t Ui_StartAddr, uint8_t *Ptr_UcBuf, uint16_t Us_BufLen);
static FlashIntf_RetCode_t FlashIntf_EEBankWrite(uint32_t Ui_StartAddr, uint8_t *Ptr_UcBuf, uint16_t Us_BufLen);
static uint64_t FlashIntf_GetEraseSectorMask(Fapi_FlashBankType Enm_Bank, uint32_t Ui_StartAddr, uint16_t *Ptr_UsNSectorsLeft, Fapi_FlashSectorType *Ptr_EnmFirstSector);
static FlashIntf_RetCode_t FlashIntf_EraseMainBankSectors(uint32_t Ui_StartAddr, uint16_t Us_NSectors);
static FlashIntf_RetCode_t FlashIntf_EraseEEBankSectors(uint32_t Ui_StartAddr, uint16_t Us_NSectors);
static FlashIntf_RetCode_t FlashIntf_EraseMainBanks(uint32_t Ui_StartAddr, uint16_t Us_NBanks);
static FlashIntf_RetCode_t FlashIntf_EraseEEBank(uint32_t Ui_Addr);
static FlashIntf_RetCode_t FlashIntf_MainBankRead(uint32_t Ui_StartAddr, uint8_t *Ptr_UcBuf, uint16_t Us_BufLen);
static FlashIntf_RetCode_t FlashIntf_EEBankRead(uint32_t Ui_StartAddr, uint8_t *Ptr_UcBuf, uint16_t Us_BufLen);


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_Init, ".TI.ramfunc");
#endif
/* Function to initialize the Flash Interface */
FlashIntf_RetCode_t FlashIntf_Init(void)
{
	FlashIntf_RetCode_t Enm_Ret = FLASHINTF_NULLPTR;
	Fapi_FlashBankSectorsType St_BankInfo;
	Fapi_DeviceInfoType St_DevInfo;
	Fapi_FlashBankType Enm_BankIter;
	Fapi_FlashSectorType Enm_SecIter;
	uint32_t Ui_Addr;

	/* Disable Interrupts */
	_disable_IRQ_interrupt_();

	/* Initialize the Flash Memory Banks before performing any operation */
	Enm_Ret = (FlashIntf_RetCode_t)Fapi_initializeFlashBanks((uint32_t)HCLK_FREQ);
	if (Enm_Ret == FLASHINTF_SUCCESS)
	{
		/* Retrieve device information */
		St_DevInfo = Fapi_getDeviceInfo();
		if (St_DevInfo.u16NumberOfBanks <= FLASH_MAXBANKS)
		{
			/* Set the number of banks into the Flash Information Structure */
			St_FDevInfo.Us_NBanks = St_DevInfo.u16NumberOfBanks;
			/* Iterate through the Flash Banks */
			for (Enm_BankIter = Fapi_FlashBank0; Enm_BankIter < FLASH_MAXBANKS; Enm_BankIter++)
			{
				/* Retrieve and store bank information in the Flash Information Structure */
				Enm_Ret = (FlashIntf_RetCode_t)Fapi_getBankSectors(Enm_BankIter, &St_BankInfo);
				if (Enm_Ret == FLASHINTF_SUCCESS)
				{
					St_FDevInfo.St_ArrBankInfo[Enm_BankIter].Ui_NSectors = St_BankInfo.u32NumberOfSectors;
					Ui_Addr = St_BankInfo.u32BankStartAddress;
					St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_StartAddr = Ui_Addr;

					/* Iterate through the sectors in the bank */
					for (Enm_SecIter = Fapi_FlashSector0; Enm_SecIter < St_BankInfo.u32NumberOfSectors; Enm_SecIter++)
					{
						/* Store sector information in the Flash Information Structure */
						St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_ArrSecAddr[Enm_SecIter].Ui_StartAddr = Ui_Addr;
						if (St_BankInfo.oFlashBankTech == Fapi_FLEE)
						{
							Ui_Addr += (uint32_t)St_BankInfo.au16SectorSizes[Fapi_FlashSector0] * 1024U;
						}
						else
						{
							Ui_Addr += (uint32_t)St_BankInfo.au16SectorSizes[Enm_SecIter] * 1024U;
						}

						St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_ArrSecAddr[Enm_SecIter].Ui_EndAddr = (Ui_Addr - 1U);
					}

					St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_EndAddr = (Ui_Addr - 1U);
				}
			}

			/* Store the Flash addresses in the Flash Information Structure*/
			St_FDevInfo.St_FlashAddr.Ui_StartAddr = St_FDevInfo.St_ArrBankInfo[Fapi_FlashBank0].St_ArrSecAddr[Fapi_FlashSector0].Ui_StartAddr;
			St_FDevInfo.St_FlashAddr.Ui_EndAddr = (St_FDevInfo.St_FlashAddr.Ui_StartAddr + ((uint32_t)St_DevInfo.u16DeviceMemorySize * 1024U) - 1U);
		}
		else
		{
			Enm_Ret = FLASHINTF_FAIL;
		}
	}

	/* Enable Interrupts */
	_enable_interrupt_();

	return Enm_Ret;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_GetWriteSectorMask, ".TI.ramfunc");
#endif
/* Function to retrieve the sectors to be enabled for a Flash/FEE write operation */
static uint64_t FlashIntf_GetWriteSectorMask(Fapi_FlashBankType Enm_Bank, uint32_t Ui_StartAddr, uint32_t Ui_EndAddr)
{
	uint64_t Ul_SectorMask = 0U;
	Fapi_FlashSectorType Enm_SecIter;

	/* Iterate through the sectors in the Flash */
	for (Enm_SecIter = Fapi_FlashSector0; Enm_SecIter < St_FDevInfo.St_ArrBankInfo[Enm_Bank].Ui_NSectors; Enm_SecIter++)
	{
		/* Determine the first sector for the write */
		if (Ul_SectorMask == 0U)
		{
			/* If the address falls within the sector, set the sector bit in the mask */
			if ((Ui_StartAddr >= St_FDevInfo.St_ArrBankInfo[Enm_Bank].St_ArrSecAddr[Enm_SecIter].Ui_StartAddr)
					&& (Ui_StartAddr <= St_FDevInfo.St_ArrBankInfo[Enm_Bank].St_ArrSecAddr[Enm_SecIter].Ui_EndAddr))
			{
				Ul_SectorMask |= (uint64_t)((uint64_t)1U << Enm_SecIter);
			}
		}
		else
		{
			/* Set subsequent bits in the mask till the end address is reached */
			Ul_SectorMask |= (uint64_t)((uint64_t)1U << Enm_SecIter);
		}

		/* If the end address falls within the sector, exit the loop */
		if ((Ui_EndAddr >= St_FDevInfo.St_ArrBankInfo[Enm_Bank].St_ArrSecAddr[Enm_SecIter].Ui_StartAddr)
				&& (Ui_EndAddr <= St_FDevInfo.St_ArrBankInfo[Enm_Bank].St_ArrSecAddr[Enm_SecIter].Ui_EndAddr))
		{
			break;
		}
	}

	return Ul_SectorMask;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_MainBankWrite, ".TI.ramfunc");
#endif
/* Function to write data to the Main Flash Memory */
static FlashIntf_RetCode_t FlashIntf_MainBankWrite(uint32_t Ui_StartAddr, uint8_t *Ptr_UcBuf, uint16_t Us_BufLen)
{
	FlashIntf_RetCode_t Enm_Ret = FLASHINTF_SUCCESS;
	Fapi_FlashBankType Enm_BankIter;
	uint16_t Us_SectorMask = 0U;
	uint32_t Ui_EndAddr = (Ui_StartAddr + Us_BufLen - 1U);
	uint32_t Ui_Addr = Ui_StartAddr;
	uint16_t Us_NWBytes;

	/* Disable Interrupts */
	_disable_IRQ_interrupt_();

	/* Check whether the address range falls within the Flash Memory */
	if ((Ui_StartAddr >= St_FDevInfo.St_FlashAddr.Ui_StartAddr) && (Ui_EndAddr <= St_FDevInfo.St_FlashAddr.Ui_EndAddr))
	{
		/* Iterate through the banks in the Flash */
		for (Enm_BankIter = Fapi_FlashBank0; Enm_BankIter < St_FDevInfo.Us_NBanks; Enm_BankIter++)
		{
			/* Check whether the address falls within the bank */
			if ((Ui_Addr >= St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_StartAddr)
					&& (Ui_Addr <= St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_EndAddr))
			{
				/* Set the bank to an active state */
				Enm_Ret = (FlashIntf_RetCode_t)Fapi_setActiveFlashBank(Enm_BankIter);
				if (Enm_Ret == FLASHINTF_SUCCESS)
				{
					/* Retrieve the list of sectors to be enabled */
					Us_SectorMask = (uint16_t)FlashIntf_GetWriteSectorMask(Enm_BankIter, Ui_Addr, Ui_EndAddr);
					if (Us_SectorMask != 0U)
					{
						/* Enable the sectors to be written */
						Enm_Ret = (FlashIntf_RetCode_t)Fapi_enableMainBankSectors(Us_SectorMask);
						if (Enm_Ret == FLASHINTF_SUCCESS)
						{
							/* Wait for the operation to complete */
							while ((Fapi_StatusType)FAPI_CHECK_FSM_READY_BUSY != Fapi_Status_FsmReady)
							{
							}

							/* Iterate through the addresses in the Flash Bank */
							while ((Ui_Addr < St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_EndAddr) && (Ui_Addr <= Ui_EndAddr))
							{
								/* Set the number of bytes to be written in the next operation */
								Us_NWBytes = (FLASH_MAINBANKWIDTH - (uint16_t)(Ui_Addr % FLASH_MAINBANKWIDTH));
								if (Us_NWBytes > Us_BufLen)
								{
									Us_NWBytes = Us_BufLen;
								}

								/* Write to the Flash address */
								Enm_Ret = (FlashIntf_RetCode_t)Fapi_issueProgrammingCommand((uint32_t *)Ui_Addr, Ptr_UcBuf, (uint8_t)Us_NWBytes, NULL, 0U, Fapi_AutoEccGeneration);
								if (Enm_Ret != FLASHINTF_SUCCESS)
								{
									break;
								}

								/* Wait for the operation to complete */
								while ((Fapi_StatusType)FAPI_CHECK_FSM_READY_BUSY != Fapi_Status_FsmReady)
								{
								}

								/* Check whether the operation is successful */
								if (FAPI_GET_FSM_STATUS != 0U)
								{
									Enm_Ret = FLASHINTF_FAIL;
									break;
								}

								/* Update the buffer pointer, flash address and the number of bytes left for the next operation */
								Ptr_UcBuf += Us_NWBytes;
								Ui_Addr += Us_NWBytes;
								Us_BufLen -= Us_NWBytes;
							}
						}
					}
				}
			}

			/* If an error is encountered or the write operation is complete, exit the loop */
			if ((Enm_Ret != FLASHINTF_SUCCESS) || (Us_BufLen == 0U))
			{
				break;
			}
		}
	}
	else
	{
	    Enm_Ret = FLASHINTF_INVADDR;
	}

	/* Enable Interrupts */
	_enable_interrupt_();

	return Enm_Ret;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_EEBankWrite, ".TI.ramfunc");
#endif
/* Function to write data to the Flash EEPROM Memory */
static FlashIntf_RetCode_t FlashIntf_EEBankWrite(uint32_t Ui_StartAddr, uint8_t *Ptr_UcBuf, uint16_t Us_BufLen)
{
	FlashIntf_RetCode_t Enm_Ret = FLASHINTF_INVADDR;
	uint64_t Ul_SectorMask = 0U;
	uint32_t Ui_EndAddr = (Ui_StartAddr + Us_BufLen - 1U);
	uint32_t Ui_Addr = Ui_StartAddr;
	uint16_t Us_NWBytes;

	/* Disable Interrupts */
	_disable_IRQ_interrupt_();

	/* Check whether the address range falls within the FEE Bank */
	if ((Ui_StartAddr >= St_FDevInfo.St_ArrBankInfo[FLASH_EEBANK].St_BankAddr.Ui_StartAddr) && (Ui_EndAddr <= St_FDevInfo.St_ArrBankInfo[FLASH_EEBANK].St_BankAddr.Ui_EndAddr))
	{
		/* Set the bank to an active state */
		Enm_Ret = (FlashIntf_RetCode_t)Fapi_setActiveFlashBank(FLASH_EEBANK);
		if (Enm_Ret == FLASHINTF_SUCCESS)
		{
			/* Retrieve the list of sectors to be enabled */
			Ul_SectorMask = FlashIntf_GetWriteSectorMask(FLASH_EEBANK, Ui_Addr, Ui_EndAddr);
			if (Ul_SectorMask != 0LLU)
			{
				/* Enable the sectors to be written */
				Enm_Ret = (FlashIntf_RetCode_t)Fapi_enableEepromBankSectors((uint32_t)(Ul_SectorMask & FLASH_EESECMASK), (uint32_t)((Ul_SectorMask & FLASH_EESECMASK) >> 32U));
				if (Enm_Ret == FLASHINTF_SUCCESS)
				{
					/* Wait for the operation to complete */
					while ((FlashIntf_RetCode_t)FAPI_CHECK_FSM_READY_BUSY != Fapi_Status_FsmReady)
					{
					}

					/* Iterate through the addresses in the FEE Bank */
					while ((Ui_Addr < St_FDevInfo.St_ArrBankInfo[FLASH_EEBANK].St_BankAddr.Ui_EndAddr) && (Ui_Addr <= Ui_EndAddr))
					{
						/* Set the number of bytes to be written in the next operation */
						Us_NWBytes = (FLASH_EEBANKWIDTH - (uint16_t)(Ui_Addr % FLASH_EEBANKWIDTH));
						if (Us_NWBytes > Us_BufLen)
						{
							Us_NWBytes = Us_BufLen;
						}

						/* Write to the FEE address */
						Enm_Ret = (FlashIntf_RetCode_t)Fapi_issueProgrammingCommand((uint32_t *)Ui_Addr, Ptr_UcBuf, (uint8_t)Us_NWBytes, NULL, 0U, Fapi_AutoEccGeneration);
						if (Enm_Ret != FLASHINTF_SUCCESS)
						{
							break;
						}

						/* Wait for the operation to complete */
						while ((FlashIntf_RetCode_t)FAPI_CHECK_FSM_READY_BUSY != Fapi_Status_FsmReady)
						{
						}

						/* Check whether the operation is successful */
						if (FAPI_GET_FSM_STATUS != 0U)
						{
							Enm_Ret = FLASHINTF_FAIL;
							break;
						}

						/* Update the buffer pointer, flash address and the number of bytes left for the next operation */
						Ptr_UcBuf += Us_NWBytes;
						Ui_Addr += Us_NWBytes;
						Us_BufLen -= Us_NWBytes;
					}
				}
			}
		}
	}

	/* Enable Interrupts */
	_enable_interrupt_();

	return Enm_Ret;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_Write, ".TI.ramfunc");
#endif
/* Function to write data to the Flash and FEE Memory */
FlashIntf_RetCode_t FlashIntf_Write(uint32_t Ui_Addr, uint8_t *Ptr_UcBuf, uint16_t Us_BufLen)
{
	FlashIntf_RetCode_t Enm_Ret = FLASHINTF_NULLPTR;

	/* Validate the parameters passed */
	if ((Ptr_UcBuf != NULL) && (Us_BufLen > 0U))
	{
		/* Call the appropriate function based on whether the address passed is in the Flash or the FEE */
		if (Fapi_isAddressEEPROM(Ui_Addr))
		{
			Enm_Ret = FlashIntf_EEBankWrite(Ui_Addr, Ptr_UcBuf, Us_BufLen);
		}
		else
		{
			Enm_Ret = FlashIntf_MainBankWrite(Ui_Addr, Ptr_UcBuf, Us_BufLen);
		}
	}

	return Enm_Ret;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_GetEraseSectorMask, ".TI.ramfunc");
#endif
/* Function to retrieve the sectors to be enabled for a Flash/FEE erase operation */
static uint64_t FlashIntf_GetEraseSectorMask(Fapi_FlashBankType Enm_Bank, uint32_t Ui_StartAddr, uint16_t *Ptr_UsNSectorsLeft, Fapi_FlashSectorType *Ptr_EnmFirstSector)
{
	uint64_t Ul_SectorMask = 0U;
	Fapi_FlashSectorType Enm_SecIter;

	/* Iterate through the sectors in the bank */
	for (Enm_SecIter = Fapi_FlashSector0; Enm_SecIter < St_FDevInfo.St_ArrBankInfo[Enm_Bank].Ui_NSectors; Enm_SecIter++)
	{
		/* Identify the first sector */
		if (Ul_SectorMask == 0U)
		{
			if ((Ui_StartAddr >= St_FDevInfo.St_ArrBankInfo[Enm_Bank].St_ArrSecAddr[Enm_SecIter].Ui_StartAddr)
					&& (Ui_StartAddr <= St_FDevInfo.St_ArrBankInfo[Enm_Bank].St_ArrSecAddr[Enm_SecIter].Ui_EndAddr))
			{
				Ul_SectorMask |= (uint64_t)((uint64_t)1U << Enm_SecIter);
				*Ptr_EnmFirstSector = Enm_SecIter;
			}
		}
		else
		{
			Ul_SectorMask |= (uint64_t)((uint64_t)1U << Enm_SecIter);
		}

		/* Update the number of sectors left */
		if (Ul_SectorMask != 0U)
		{
			if (--(*Ptr_UsNSectorsLeft) == 0U)
			{
				break;
			}
		}
	}

	return Ul_SectorMask;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_EraseMainBankSectors, ".TI.ramfunc");
#endif
/* Function to erase sectors in the Flash Memory */
static FlashIntf_RetCode_t FlashIntf_EraseMainBankSectors(uint32_t Ui_StartAddr, uint16_t Us_NSectors)
{
	FlashIntf_RetCode_t Enm_Ret = FLASHINTF_SUCCESS;
	Fapi_FlashBankType Enm_BankIter;
	uint16_t Us_SectorMask = 0U;
	uint32_t Ui_Addr = Ui_StartAddr;
	uint16_t Us_SectorsLeft = Us_NSectors;
	Fapi_FlashSectorType Enm_SecIter = Fapi_FlashSector0;

	/* Disable Interrupts */
	_disable_IRQ_interrupt_();

	/* Check whether the address range falls within the Flash Memory */
	if ((Ui_StartAddr >= St_FDevInfo.St_FlashAddr.Ui_StartAddr) && (Ui_StartAddr <= St_FDevInfo.St_FlashAddr.Ui_EndAddr))
	{
		/* Iterate through the banks in the Flash */
		for (Enm_BankIter = Fapi_FlashBank0; Enm_BankIter < St_FDevInfo.Us_NBanks; Enm_BankIter++)
		{
			/* Check whether the address falls within the bank */
			if ((Ui_Addr >= St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_StartAddr)
					&& (Ui_Addr <= St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_EndAddr))
			{
				/* Set the bank to an active state */
				Enm_Ret = (FlashIntf_RetCode_t)Fapi_setActiveFlashBank(Enm_BankIter);
				if (Enm_Ret == FLASHINTF_SUCCESS)
				{
					/* Retrieve the list of sectors to be enabled */
					Us_SectorMask = (uint16_t)FlashIntf_GetEraseSectorMask(Enm_BankIter, Ui_Addr, &Us_SectorsLeft, &Enm_SecIter);
					if (Us_SectorMask != 0U)
					{
						/* Enable the sectors to be erased */
						Enm_Ret = (FlashIntf_RetCode_t)Fapi_enableMainBankSectors(Us_SectorMask);
						if (Enm_Ret == FLASHINTF_SUCCESS)
						{
							/* Wait for the operation to complete */
							while ((Fapi_StatusType)FAPI_CHECK_FSM_READY_BUSY != Fapi_Status_FsmReady)
							{
							}

							/* Iterate through the sectors in the Flash Bank */
							for (; Us_NSectors > Us_SectorsLeft; Us_NSectors--)
							{
								/* Erase the Flash bank sector */
								Enm_Ret = (FlashIntf_RetCode_t)Fapi_issueAsyncCommandWithAddress(Fapi_EraseSector, (uint32_t *)Ui_Addr);
								if (Enm_Ret != FLASHINTF_SUCCESS)
								{
									break;
								}

								/* Wait for the operation to complete */
								while ((Fapi_StatusType)FAPI_CHECK_FSM_READY_BUSY != Fapi_Status_FsmReady)
								{
								}

								/* Check whether the operation is successful */
								if (FAPI_GET_FSM_STATUS != 0U)
								{
									Enm_Ret = FLASHINTF_FAIL;
									break;
								}

								/* Update the flash address and the sector for the next operation */
								Ui_Addr = (St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_ArrSecAddr[Enm_SecIter].Ui_EndAddr + 1U);
								Enm_SecIter++;
							}
						}
					}
				}
			}

			/* If an error is encountered or the erase operation is complete, exit the loop */
			if ((Enm_Ret != FLASHINTF_SUCCESS) || (Us_NSectors == 0U))
			{
				break;
			}
		}
	}
	else
	{
	    Enm_Ret = FLASHINTF_INVADDR;
	}

	/* Enable Interrupts */
	_enable_interrupt_();

	return Enm_Ret;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_EraseEEBankSectors, ".TI.ramfunc");
#endif
/* Function to erase sectors in the Flash EE Memory */
static FlashIntf_RetCode_t FlashIntf_EraseEEBankSectors(uint32_t Ui_StartAddr, uint16_t Us_NSectors)
{
	FlashIntf_RetCode_t Enm_Ret = FLASHINTF_INVADDR;
	uint64_t Ul_SectorMask;
	uint32_t Ui_Addr = Ui_StartAddr;
	uint16_t Us_SectorsLeft = Us_NSectors;
	Fapi_FlashSectorType Enm_SecIter = Fapi_FlashSector0;

	/* Disable Interrupts */
	_disable_IRQ_interrupt_();

	/* Set the bank to an active state */
	Enm_Ret = (FlashIntf_RetCode_t)Fapi_setActiveFlashBank(FLASH_EEBANK);
	if (Enm_Ret == FLASHINTF_SUCCESS)
	{
		/* Retrieve the list of sectors to be enabled */
		Ul_SectorMask = FlashIntf_GetEraseSectorMask(FLASH_EEBANK, Ui_Addr, &Us_SectorsLeft, &Enm_SecIter);
		if (Ul_SectorMask != 0LLU)
		{
			/* Enable the sectors to be erased */
			Enm_Ret = (FlashIntf_RetCode_t)Fapi_enableEepromBankSectors((uint32_t)(Ul_SectorMask & FLASH_EESECMASK), (uint32_t)((Ul_SectorMask & FLASH_EESECMASK) >> 32U));
			if (Enm_Ret == FLASHINTF_SUCCESS)
			{
				/* Wait for the operation to complete */
				while ((Fapi_StatusType)FAPI_CHECK_FSM_READY_BUSY != Fapi_Status_FsmReady)
				{
				}

				/* Iterate through the sectors in the FEE Bank */
				for (; Us_NSectors > Us_SectorsLeft; Us_NSectors--)
				{
					/* Erase the FEE bank sector */
					Enm_Ret = (FlashIntf_RetCode_t)Fapi_issueAsyncCommandWithAddress(Fapi_EraseSector, (uint32_t *)Ui_Addr);
					if (Enm_Ret != FLASHINTF_SUCCESS)
					{
						break;
					}

					/* Wait for the operation to complete */
					while ((Fapi_StatusType)FAPI_CHECK_FSM_READY_BUSY != Fapi_Status_FsmReady)
					{
					}

					/* Check whether the operation is successful */
					if (FAPI_GET_FSM_STATUS != 0U)
					{
						Enm_Ret = FLASHINTF_FAIL;
						break;
					}

					/* Update the FEE address and the sector for the next operation */
					Ui_Addr = (St_FDevInfo.St_ArrBankInfo[FLASH_EEBANK].St_ArrSecAddr[Enm_SecIter].Ui_EndAddr + 1U);
					Enm_SecIter++;
				}
			}
		}
	}

	/* Enable Interrupts */
	_enable_interrupt_();

	return Enm_Ret;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_Erase, ".TI.ramfunc");
#endif
/* Function to erase sectors in the Flash and FEE Memory */
FlashIntf_RetCode_t FlashIntf_Erase(uint32_t Ui_Addr, uint16_t Us_NSectors)
{
	FlashIntf_RetCode_t Enm_Ret = FLASHINTF_FAIL;

	/* Validate the parameters passed */
	if (Us_NSectors > 0U)
	{
		/* Call the appropriate function based on whether the address passed is in the Flash or the FEE */
		if (Fapi_isAddressEEPROM(Ui_Addr))
		{
			Enm_Ret = FlashIntf_EraseEEBankSectors(Ui_Addr, Us_NSectors);
		}
		else
		{
			Enm_Ret = FlashIntf_EraseMainBankSectors(Ui_Addr, Us_NSectors);
		}
	}

	return Enm_Ret;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_EraseMainBanks, ".TI.ramfunc");
#endif
/* Function to erase banks in the Flash and FEE Memory */
static FlashIntf_RetCode_t FlashIntf_EraseMainBanks(uint32_t Ui_StartAddr, uint16_t Us_NBanks)
{
	FlashIntf_RetCode_t Enm_Ret = FLASHINTF_SUCCESS;
	Fapi_FlashBankType Enm_BankIter;
	uint16_t Us_SectorMask;
	uint32_t Ui_Addr = Ui_StartAddr;
	Fapi_FlashSectorType Enm_SecIter;

	/* Disable Interrupts */
	_disable_IRQ_interrupt_();

	/* Check whether the address range falls within the Flash Memory */
	if ((Ui_StartAddr >= St_FDevInfo.St_FlashAddr.Ui_StartAddr) && (Ui_StartAddr <= St_FDevInfo.St_FlashAddr.Ui_EndAddr))
	{
		/* Iterate through the banks in the Flash */
		for (Enm_BankIter = Fapi_FlashBank0; Enm_BankIter < St_FDevInfo.Us_NBanks; Enm_BankIter++)
		{
			/* Check whether the address falls within the bank */
			if ((Ui_Addr >= St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_StartAddr)
					&& (Ui_Addr <= St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_EndAddr))
			{
				/* Set the bank to an active state */
				Enm_Ret = (FlashIntf_RetCode_t)Fapi_setActiveFlashBank(Enm_BankIter);
				if (Enm_Ret == FLASHINTF_SUCCESS)
				{
					/* Set the sectors in the bank to be enabled */
					for (Enm_SecIter = Fapi_FlashSector0; Enm_SecIter < St_FDevInfo.St_ArrBankInfo[Enm_BankIter].Ui_NSectors; Enm_SecIter++)
					{
						Us_SectorMask |= (uint16_t)((uint16_t)1U << Enm_SecIter);
					}

					/* Enable all the bank sectors for erase operation */
					Enm_Ret = (FlashIntf_RetCode_t)Fapi_enableMainBankSectors(Us_SectorMask);
					if (Enm_Ret == FLASHINTF_SUCCESS)
					{
						/* Wait for the operation to complete */
						while ((Fapi_StatusType)FAPI_CHECK_FSM_READY_BUSY != Fapi_Status_FsmReady)
						{
						}

						/* Erase the Flash Bank */
						Enm_Ret = (FlashIntf_RetCode_t)Fapi_issueAsyncCommandWithAddress(Fapi_EraseBank, (uint32_t *)Ui_Addr);
						if (Enm_Ret != FLASHINTF_SUCCESS)
						{
							break;
						}

						/* Wait for the operation to complete */
						while ((Fapi_StatusType)FAPI_CHECK_FSM_READY_BUSY != Fapi_Status_FsmReady)
						{
						}

						/* Check whether the operation is successful */
						if (FAPI_GET_FSM_STATUS != 0U)
						{
							Enm_Ret = FLASHINTF_FAIL;
							break;
						}

						/* Update the flash address and number of banks left for the next erase operation */
						Ui_Addr = St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_EndAddr + 1U;
						--Us_NBanks;
					}
				}
			}

			/* If an error is encountered or the erase operation is complete, exit the loop */
			if ((Enm_Ret != FLASHINTF_SUCCESS) || (Us_NBanks == 0U))
			{
				break;
			}
		}
	}
	else
	{
	    Enm_Ret = FLASHINTF_INVADDR;
	}

	/* Enable Interrupts */
	_enable_interrupt_();

	return Enm_Ret;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_EraseEEBank, ".TI.ramfunc");
#endif
/* Function to erase the FEE Memory Bank */
static FlashIntf_RetCode_t FlashIntf_EraseEEBank(uint32_t Ui_Addr)
{
	FlashIntf_RetCode_t Enm_Ret = FLASHINTF_INVADDR;
	uint64_t Ul_SectorMask;
	Fapi_FlashSectorType Enm_SecIter;

	/* Disable Interrupts */
	_disable_IRQ_interrupt_();

	/* Set the bank to an active state */
	Enm_Ret = (FlashIntf_RetCode_t)Fapi_setActiveFlashBank(FLASH_EEBANK);
	if (Enm_Ret == FLASHINTF_SUCCESS)
	{
		/* Set the sectors in the bank to be enabled */
		for (Enm_SecIter = Fapi_FlashSector0; Enm_SecIter < St_FDevInfo.St_ArrBankInfo[FLASH_EEBANK].Ui_NSectors; Enm_SecIter++)
		{
			Ul_SectorMask |= (uint64_t)((uint64_t)1U << Enm_SecIter);
		}

		/* Enable all the bank sectors for erase operation */
		Enm_Ret = (FlashIntf_RetCode_t)Fapi_enableEepromBankSectors((uint32_t)(Ul_SectorMask & FLASH_EESECMASK), (uint32_t)((Ul_SectorMask & FLASH_EESECMASK) >> 32U));
		if (Enm_Ret == FLASHINTF_SUCCESS)
		{
			/* Wait for the operation to complete */
			while ((Fapi_StatusType)FAPI_CHECK_FSM_READY_BUSY != Fapi_Status_FsmReady)
			{
			}

			/* Erase the FEE Bank */
			Enm_Ret = (FlashIntf_RetCode_t)Fapi_issueAsyncCommandWithAddress(Fapi_EraseBank, (uint32_t *)Ui_Addr);
			if (Enm_Ret == FLASHINTF_SUCCESS)
			{
				/* Wait for the operation to complete */
				while ((Fapi_StatusType)FAPI_CHECK_FSM_READY_BUSY != Fapi_Status_FsmReady)
				{
				}

				/* Check whether the operation is successful */
				if (FAPI_GET_FSM_STATUS != 0U)
				{
					Enm_Ret = FLASHINTF_FAIL;
				}
			}
		}
	}

	/* Enable Interrupts */
	_enable_interrupt_();

	return Enm_Ret;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_EraseBanks, ".TI.ramfunc");
#endif
/* Function to erase banks in the Flash and FEE Memory */
FlashIntf_RetCode_t FlashIntf_EraseBanks(uint32_t Ui_Addr, uint16_t Us_NBanks)
{
	FlashIntf_RetCode_t Enm_Ret = FLASHINTF_FAIL;

	/* Validate the parameters passed */
	if ((Us_NBanks > 0U) && (Us_NBanks <= St_FDevInfo.Us_NBanks))
	{
		/* Call the appropriate function based on whether the address passed is in the Flash or the FEE */
		if (Fapi_isAddressEEPROM(Ui_Addr))
		{
			Enm_Ret = FlashIntf_EraseEEBank(Ui_Addr);
		}
		else
		{
			Enm_Ret = FlashIntf_EraseMainBanks(Ui_Addr, Us_NBanks);
		}
	}

	return Enm_Ret;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_MainBankRead, ".TI.ramfunc");
#endif
/* Function to read data from the Main Flash Memory */
static FlashIntf_RetCode_t FlashIntf_MainBankRead(uint32_t Ui_StartAddr, uint8_t *Ptr_UcBuf, uint16_t Us_BufLen)
{
	FlashIntf_RetCode_t Enm_Ret = FLASHINTF_SUCCESS;
	Fapi_FlashBankType Enm_BankIter;
	uint32_t Ui_EndAddr = (Ui_StartAddr + Us_BufLen - 1U);
	uint32_t Ui_Addr = Ui_StartAddr;
	uint32_t Ui_NRBytes;

	/* Check whether the address range falls within the Flash Memory */
	if ((Ui_StartAddr >= St_FDevInfo.St_FlashAddr.Ui_StartAddr) && (Ui_EndAddr <= St_FDevInfo.St_FlashAddr.Ui_EndAddr))
	{
		/* Iterate through the banks in the Flash */
		for (Enm_BankIter = Fapi_FlashBank0; Enm_BankIter < St_FDevInfo.Us_NBanks; Enm_BankIter++)
		{
			/* Check whether the address falls within the bank */
			if ((Ui_Addr >= St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_StartAddr)
					&& (Ui_Addr <= St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_EndAddr))
			{
				/* Iterate through the addresses in the Flash Bank */
				if ((Ui_Addr < St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_EndAddr) && (Ui_Addr <= Ui_EndAddr))
				{
					/* Set the number of bytes to be read in the next operation */
					if (Ui_EndAddr > St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_EndAddr)
					{
						Ui_NRBytes  = (St_FDevInfo.St_ArrBankInfo[Enm_BankIter].St_BankAddr.Ui_EndAddr - Ui_Addr + 1U);
					}
					else
					{
						Ui_NRBytes = Us_BufLen;
					}

					/* Read from the Flash address */
					Enm_Ret = (FlashIntf_RetCode_t)Fapi_doMarginReadByByte((uint8_t *)Ui_Addr, Ptr_UcBuf, Ui_NRBytes, Fapi_NormalRead);
					if (Enm_Ret != FLASHINTF_SUCCESS)
					{
						break;
					}

					/* Update the buffer pointer, flash address and the number of bytes left for the next operation */
					Ptr_UcBuf += Ui_NRBytes;
					Ui_Addr += Ui_NRBytes;
					Us_BufLen -= Ui_NRBytes;
				}
			}

			/* If an error is encountered or the read operation is complete, exit the loop */
			if ((Enm_Ret != FLASHINTF_SUCCESS) || (Us_BufLen == 0U))
			{
				break;
			}
		}
	}
	else
	{
	    Enm_Ret = FLASHINTF_INVADDR;
	}

	return Enm_Ret;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_EEBankRead, ".TI.ramfunc");
#endif
/* Function to read data from the Flash EEPROM Memory */
static FlashIntf_RetCode_t FlashIntf_EEBankRead(uint32_t Ui_StartAddr, uint8_t *Ptr_UcBuf, uint16_t Us_BufLen)
{
	FlashIntf_RetCode_t Enm_Ret = FLASHINTF_INVADDR;
	uint32_t Ui_EndAddr = (Ui_StartAddr + Us_BufLen - 1U);

	/* Check whether the address range falls within the FEE Bank */
	if ((Ui_StartAddr >= St_FDevInfo.St_ArrBankInfo[FLASH_EEBANK].St_BankAddr.Ui_StartAddr) && (Ui_EndAddr <= St_FDevInfo.St_ArrBankInfo[FLASH_EEBANK].St_BankAddr.Ui_EndAddr))
	{
		/* Read from the FEE address */
		Enm_Ret = (FlashIntf_RetCode_t)Fapi_doMarginReadByByte((uint8_t *)Ui_StartAddr, Ptr_UcBuf, (uint32_t)Us_BufLen, Fapi_NormalRead);
	}

	return Enm_Ret;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_Read, ".TI.ramfunc");
#endif
/* Function to read data from the Flash and FEE Memory */
FlashIntf_RetCode_t FlashIntf_Read(uint32_t Ui_Addr, uint8_t *Ptr_UcBuf, uint16_t Us_BufLen)
{
	FlashIntf_RetCode_t Enm_Ret = FLASHINTF_NULLPTR;

	/* Validate the parameters passed */
	if ((Ptr_UcBuf != NULL) && (Us_BufLen > 0U))
	{
		/* Call the appropriate function based on whether the address passed is in the Flash or the FEE */
		if (Fapi_isAddressEEPROM(Ui_Addr))
		{
			Enm_Ret = FlashIntf_EEBankRead(Ui_Addr, Ptr_UcBuf, Us_BufLen);
		}
		else
		{
			Enm_Ret = FlashIntf_MainBankRead(Ui_Addr, Ptr_UcBuf, Us_BufLen);
		}
	}

	return Enm_Ret;
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_ClrFSMStatus, ".TI.ramfunc");
#endif
/* Function to clear FSM status */
FlashIntf_RetCode_t FlashIntf_ClrFSMStatus(void)
{
	return (FlashIntf_RetCode_t)Fapi_issueAsyncCommand(Fapi_ClearStatus);
}


#ifdef FLASH_MOVE_TO_RAM
/* Pragma directive for moving code to RAM */
#pragma CODE_SECTION(FlashIntf_GetFSMStatus, ".TI.ramfunc");
#endif
/* Function to retrieve FSM status */
FlashIntf_RetCode_t FlashIntf_GetFSMStatus(void)
{
	/* Return current FSM status */
	return (FlashIntf_RetCode_t)FAPI_GET_FSM_STATUS;
}
