/*==================================================================================================
*   Project              : RTD AUTOSAR 4.4
*   Platform             : CORTEXM
*   Peripheral           : IPV_FTFC
*   Dependencies         : 
*
*   Autosar Version      : 4.4.0
*   Autosar Revision     : ASR_REL_4_4_REV_0000
*   Autosar Conf.Variant :
*   SW Version           : 1.0.0
*   Build Version        : S32K1_RTD_1_0_0_HF01_D2109_ASR_REL_4_4_REV_0000_20210907
*
*   (c) Copyright 2020-2021 NXP Semiconductors
*   All Rights Reserved.
*
*   NXP Confidential. This software is owned or controlled by NXP and may only be
*   used strictly in accordance with the applicable license terms. By expressly
*   accepting such terms or by downloading, installing, activating and/or otherwise
*   using the software, you are agreeing that you have read, and that you agree to
*   comply with and are bound by, such license terms. If you do not agree to be
*   bound by the applicable license terms, then you may not retain, install,
*   activate or otherwise use the software.
==================================================================================================*/

/**
*   @file Ftfc_Eep_Ip.c
*
*   @addtogroup FTFC_EEP_IP
*   @{
*/

#ifdef __cplusplus
extern "C"{
#endif

/*==================================================================================================
*                                          INCLUDE FILES
* 1) system and project includes
* 2) needed interfaces from external units
* 3) internal and external interfaces from this unit
==================================================================================================*/

#include "OsIf.h"
#include "Mcal.h"
#include "SchM_Eep.h"
#include "Ftfc_Eep_Ip.h"

#if (FTFC_EEP_IP_DEV_ERROR_DETECT == STD_ON)
#include "Devassert.h"
#endif

/*==================================================================================================
*                                 SOURCE FILE VERSION INFORMATION
==================================================================================================*/

#define FTFC_EEP_IP_VENDOR_ID_C                      43
#define FTFC_EEP_IP_AR_RELEASE_MAJOR_VERSION_C       4
#define FTFC_EEP_IP_AR_RELEASE_MINOR_VERSION_C       4
#define FTFC_EEP_IP_AR_RELEASE_REVISION_VERSION_C    0
#define FTFC_EEP_IP_SW_MAJOR_VERSION_C               1
#define FTFC_EEP_IP_SW_MINOR_VERSION_C               0
#define FTFC_EEP_IP_SW_PATCH_VERSION_C               0

/*==================================================================================================
*                                       FILE VERSION CHECKS
==================================================================================================*/
#ifndef DISABLE_MCAL_INTERMODULE_ASR_CHECK
    /* Version checks for OsIf.h */
    #if ((FTFC_EEP_IP_AR_RELEASE_MAJOR_VERSION_C != OSIF_AR_RELEASE_MAJOR_VERSION) || \
        (FTFC_EEP_IP_AR_RELEASE_MINOR_VERSION_C != OSIF_AR_RELEASE_MINOR_VERSION) \
        )
        #error "Ftfc_Eep_Ip.c and OsIf.h are for different AUTOSAR versions!"
    #endif
#endif

#ifndef DISABLE_MCAL_INTERMODULE_ASR_CHECK
    /* Version checks for Mcal.h */
    #if ((FTFC_EEP_IP_AR_RELEASE_MAJOR_VERSION_C != MCAL_AR_RELEASE_MAJOR_VERSION) || \
        (FTFC_EEP_IP_AR_RELEASE_MINOR_VERSION_C != MCAL_AR_RELEASE_MINOR_VERSION) \
        )
        #error "Ftfc_Eep_Ip.c and Mcal.h are for different AUTOSAR versions!"
    #endif
#endif

#ifndef DISABLE_MCAL_INTERMODULE_ASR_CHECK
    /* Version checks for SchM_Eep.h */
    #if ((FTFC_EEP_IP_AR_RELEASE_MAJOR_VERSION_C != SCHM_EEP_AR_RELEASE_MAJOR_VERSION) || \
        (FTFC_EEP_IP_AR_RELEASE_MINOR_VERSION_C != SCHM_EEP_AR_RELEASE_MINOR_VERSION) \
        )
        #error "Ftfc_Eep_Ip.c and SchM_Eep.h are for different AUTOSAR versions!"
    #endif
#endif

/* Version checks for Ftfc_Eep_Ip.h */
#if (FTFC_EEP_IP_VENDOR_ID_C != FTFC_EEP_IP_VENDOR_ID_H)
    #error "Ftfc_Eep_Ip.c and Ftfc_Eep_Ip.h have different vendor IDs!"
#endif
#if ((FTFC_EEP_IP_AR_RELEASE_MAJOR_VERSION_C    != FTFC_EEP_IP_AR_RELEASE_MAJOR_VERSION_H) || \
     (FTFC_EEP_IP_AR_RELEASE_MINOR_VERSION_C    != FTFC_EEP_IP_AR_RELEASE_MINOR_VERSION_H) || \
     (FTFC_EEP_IP_AR_RELEASE_REVISION_VERSION_C != FTFC_EEP_IP_AR_RELEASE_REVISION_VERSION_H) \
    )
    #error "Ftfc_Eep_Ip.c and Ftfc_Eep_Ip.h are for different AUTOSAR versions!"
#endif
#if ((FTFC_EEP_IP_SW_MAJOR_VERSION_C != FTFC_EEP_IP_SW_MAJOR_VERSION_H) || \
     (FTFC_EEP_IP_SW_MINOR_VERSION_C != FTFC_EEP_IP_SW_MINOR_VERSION_H) || \
     (FTFC_EEP_IP_SW_PATCH_VERSION_C != FTFC_EEP_IP_SW_PATCH_VERSION_H) \
    )
    #error "Ftfc_Eep_Ip.c and Ftfc_Eep_Ip.h have different SW versions!"
#endif

#if (FTFC_EEP_IP_DEV_ERROR_DETECT == STD_ON)
    #ifndef DISABLE_MCAL_INTERMODULE_ASR_CHECK
        /* Version checks for Devassert.h */
        #if ((FTFC_EEP_IP_AR_RELEASE_MAJOR_VERSION_C != DEVASSERT_AR_RELEASE_MAJOR_VERSION) || \
             (FTFC_EEP_IP_AR_RELEASE_MINOR_VERSION_C != DEVASSERT_AR_RELEASE_MINOR_VERSION) \
            )
            #error "Ftfc_Eep_Ip.c and Devassert.h are for different AUTOSAR versions!"
        #endif
    #endif
#endif /* FTFC_EEP_IP_DEV_ERROR_DETECT == STD_ON */

/*==================================================================================================
*                                          LOCAL MACROS
==================================================================================================*/

/** Switch FlexRAM function between RAM and emulated EEPROM. */
#define FTFC_EEP_IP_FLASH_CMD_SETRAM_U8         0x81U

/** Minimum number of bytes allowed for a quick write. */
#define FTFC_EEP_IP_QUICK_WRITES_LENGTH_MIN     16U

/** Maximum number of bytes allowed for a quick write. */
#define FTFC_EEP_IP_QUICK_WRITES_LENGTH_MAX     512U

#if (FTFC_EEP_IP_HIGH_TEMP_CHIP == STD_ON)

#define IP_EEPROM               IP_FTFM                     /**< ptr to the IP_FTFM structure                                               */
#define FCNFG_EEERDY_MASK       FTFM_FCNFG_EEERDY_MASK      /**< set when FlexRAM is ready for emulated EEPROM operations                   */
#define FSTAT_CCIF_MASK         FTFM_FSTAT_CCIF_MASK        /**< zero while a FTFC command or an EEPROM operation is in progress            */
#define FSTAT_ACCERR_MASK       FTFM_FSTAT_ACCERR_MASK      /**< flash access error flag                                                    */
#define FSTAT_FPVIOL_MASK       FTFM_FSTAT_FPVIOL_MASK      /**< a write was attempted to a protected area                                  */
#define FSTAT_RDCOLERR_MASK     FTFM_FSTAT_RDCOLERR_MASK    /**< collision over a FTFC resource that was being manipulated by a command     */
#define FSTAT_MGSTAT0_MASK      FTFM_FSTAT_MGSTAT0_MASK     /**< set if an error is detected during execution of an FTFC command            */
#define FSTAT_MGSTAT1_MASK      FTFM_FSTAT_MGSTAT1_MASK     /**< uncorrectable ECC fault was detected during execution of a flash command   */
#define FSTAT_MGSTAT2_MASK      FTFM_FSTAT_MGSTAT2_MASK     /**< uncorrectable ECC fault was detected during emulated EEPROM initialization */
#define FSTAT_MGSTAT3_MASK      FTFM_FSTAT_MGSTAT3_MASK     /**< uncorrectable ECC fault was detected during the flash reset sequence       *
                                                                 unless the error occurred while reading the flash configuration field,     *
                                                                 the FlexNVM EEPROM partition field, or CSEc strict boot firmware.          */
#else /* FTFC_EEP_IP_HIGH_TEMP_CHIP */

#define IP_EEPROM               IP_FTFC                     /**< ptr to the IP_FTFC structure                                               */
#define FCNFG_EEERDY_MASK       FTFC_FCNFG_EEERDY_MASK      /**< FlexRAM ready for emulated EEPROM operations                               */
#define FSTAT_CCIF_MASK         FTFC_FSTAT_CCIF_MASK        /**< if zero, a FTFC command or an EEPROM operation is in progress              */
#define FSTAT_ACCERR_MASK       FTFC_FSTAT_ACCERR_MASK      /**< flash access error flag                                                    */
#define FSTAT_FPVIOL_MASK       FTFC_FSTAT_FPVIOL_MASK      /**< a write was attempted to a protected area                                  */
#define FSTAT_RDCOLERR_MASK     FTFC_FSTAT_RDCOLERR_MASK    /**< collision over a FTFC resource that was being manipulated by a command     */
#define FSTAT_MGSTAT0_MASK      FTFC_FSTAT_MGSTAT0_MASK     /**< set if an error is detected during execution of an FTFC command            */

#endif /* FTFC_EEP_IP_HIGH_TEMP_CHIP */

/** Toggle the development errors detection. */
#if (FTFC_EEP_IP_DEV_ERROR_DETECT == STD_ON)
#define FTFC_DEV_ASSERT(X)      DevAssert(X)
#else
#define FTFC_DEV_ASSERT(X)
#endif

#define FTFC_EEP_IP_NO_BO_DETECTED_U8               0x00    /**< No EEPROM issues detected */
#define FTFC_EEP_IP_BO_DURING_MAINTENANCE_U8        0x01    /**< Quick write maintenance has to be completed. */
#define FTFC_EEP_IP_BO_DURING_QUICK_WRITES_U8       0x02    /**< Quick writes were discarded due to a reset. */
#define FTFC_EEP_IP_BO_DURING_NORMAL_WRITES_U8      0x04    /**< A normal write was interrupted by reset. */

/*==================================================================================================
*                          LOCAL TYPEDEFS (STRUCTURES, UNIONS, ENUMS)
==================================================================================================*/

/** FCCOB registers and their position in the IP_EEPROM->FCCOB[] array */
enum
{
    FTFC_EEP_IP_FCCOB_3,    FTFC_EEP_IP_FCCOB_2,    FTFC_EEP_IP_FCCOB_1,    FTFC_EEP_IP_FCCOB_0,
    FTFC_EEP_IP_FCCOB_7,    FTFC_EEP_IP_FCCOB_6,    FTFC_EEP_IP_FCCOB_5,    FTFC_EEP_IP_FCCOB_4,
    FTFC_EEP_IP_FCCOB_B,    FTFC_EEP_IP_FCCOB_A,    FTFC_EEP_IP_FCCOB_9,    FTFC_EEP_IP_FCCOB_8
};

/** Input params written in the FCCOB registers for the flash command: Set FlexRAM Function */
enum
{
    FTFC_EEP_IP_FCCOB_SETRAM_WRITE_CMD_SETRAM               = FTFC_EEP_IP_FCCOB_0,    /**< Flash command: SETRAM (0x81) */
    FTFC_EEP_IP_FCCOB_SETRAM_WRITE_FR_FCC                   = FTFC_EEP_IP_FCCOB_1,    /**< FlexRAM Function Control Code */
    FTFC_EEP_IP_FCCOB_SETRAM_WRITE_QUICKWRITES_LENGTH_MSB   = FTFC_EEP_IP_FCCOB_4,    /**< Number of FlexRAM bytes allocated for EEPROM quick writes [15:8] */
    FTFC_EEP_IP_FCCOB_SETRAM_WRITE_QUICKWRITES_LENGTH_LSB   = FTFC_EEP_IP_FCCOB_5     /**< Number of FlexRAM bytes allocated for EEPROM quick writes [7:0] */
};

/** Returned values read from the FCCOB registers for the flash command: Set FlexRAM Function */
enum
{
    FTFC_EEP_IP_FCCOB_SETRAM_READ_BROWNOUT_CODE             = FTFC_EEP_IP_FCCOB_5,    /**< Brown-out (BO) Detection Codes */
    FTFC_EEP_IP_FCCOB_SETRAM_READ_QWRITES_MAINTENANCE_MSB   = FTFC_EEP_IP_FCCOB_6,    /**< Number of EEPROM quick write records requiring maintenance [15:8] */
    FTFC_EEP_IP_FCCOB_SETRAM_READ_QWRITES_MAINTENANCE_LSB   = FTFC_EEP_IP_FCCOB_7,    /**< Number of EEPROM quick write records requiring maintenance [7:0] */
    FTFC_EEP_IP_FCCOB_SETRAM_READ_SECTOR_ERASE_COUNT_MSB    = FTFC_EEP_IP_FCCOB_8,    /**< EEPROM sector erase count [15:8] */
    FTFC_EEP_IP_FCCOB_SETRAM_READ_SECTOR_ERASE_COUNT_LSB    = FTFC_EEP_IP_FCCOB_9     /**< EEPROM sector erase count [7:0] */
};

/** FLEXRAM Function Control Codes */
typedef enum
{
    FLEXRAM_FCC_MODE_EEERAM_NORMAL      = 0x00,     /**< Make FlexRAM available for emulated EEPROM */
    FLEXRAM_FCC_MODE_EEERAM_QWRITES     = 0x55,     /**< Make FlexRAM available for EEPROM quick writes */
    FLEXRAM_FCC_GET_FLEXRAM_STATUS      = 0x77,     /**< EEPROM quick write status query */
    FLEXRAM_FCC_COMPLETE_MAINTENANCE    = 0xAA,     /**< Complete interrupted EEPROM quick write process */
    FLEXRAM_FCC_MODE_RAM                = 0xFF      /**< Make FlexRAM available as RAM */
} Ftfc_Eep_Ip_FlexramFccType;

/** Data about the last asynchronous job */
typedef struct
{
    /** updated at the beginning of a new async job with OsIf_GetCounter() and with each OsIf_GetElapsed() call */
    uint32 TicksStarted;
    /** here OsIf_GetElapsed() stores the time elapsed since TicksStarted */
    uint32 TicksElapsed;
    /** the status of the last asynchronous job may be: ok, pending or failed */
    Ftfc_Eep_Ip_StatusType Result;
} Ftfc_Eep_Ip_AsyncJobType;

/*==================================================================================================
*                                        GLOBAL CONSTANTS
==================================================================================================*/

/*==================================================================================================
*                                        GLOBAL VARIABLES
==================================================================================================*/

/*==================================================================================================
*                                         LOCAL CONSTANTS
==================================================================================================*/

#define EEP_START_SEC_CONST_32
#include "Eep_MemMap.h"

    /** The contents of an erased EEPROM record.
     *  This variable needs to be aligned on LONGWORD bytes. */
    static uint32 const Ftfc_Eep_Ip_u32EraseValue = 0xFFFFFFFFU;

#define EEP_STOP_SEC_CONST_32
#include "Eep_MemMap.h"

/*==================================================================================================
*                                         LOCAL VARIABLES
==================================================================================================*/

#define EEP_START_SEC_VAR_CLEARED_UNSPECIFIED
#include "Eep_MemMap.h"

    static Ftfc_Eep_Ip_ConfigType const * Ftfc_Eep_Ip_pxConfiguration;
    static Ftfc_Eep_Ip_BrownOutCodeType Ftfc_Eep_Ip_eBrownOutCode;
    static Ftfc_Eep_Ip_AsyncJobType Ftfc_Eep_Ip_xAsyncJob;

#define EEP_STOP_SEC_VAR_CLEARED_UNSPECIFIED
#include "Eep_MemMap.h"

/*==================================================================================================
*                                   LOCAL FUNCTION PROTOTYPES
==================================================================================================*/

#define EEP_START_SEC_CODE
#include "Eep_MemMap.h"

    static Ftfc_Eep_Ip_PageSizeType Ftfc_Eep_Ip_AlignedPageSize (Ftfc_Eep_Ip_AddressType FlexramAddress,
                                                                 Ftfc_Eep_Ip_AddressType RamAddress,
                                                                 Ftfc_Eep_Ip_LengthType Length
                                                                );

    static boolean Ftfc_Eep_Ip_IsFtfcReady (void);

    static Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_CmdSetFlexramFunction (Ftfc_Eep_Ip_FlexramFccType FlexramFuncControlCode,
                                                                     uint16 QuickWritesLength
                                                                    );

    static Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_CheckFstatErrors (void);

    static Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_AcEflashCmd (void);

    static Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_AcEflashWrite (Ftfc_Eep_Ip_AddressType DestAddress,
                                                             uint8 const * pu8SrcAddress,
                                                             Ftfc_Eep_Ip_PageSizeType PageSize,
                                                             boolean Async
                                                            );

#define EEP_STOP_SEC_CODE
#include "Eep_MemMap.h"


#if (FTFC_EEP_IP_LOAD_AC_INTO_RAM == STD_ON)
#define EEP_START_SEC_RAMCODE
#else
#define EEP_START_SEC_CODE
#endif
#include "Eep_MemMap.h"

    static Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_StartFlashCommand (void);

    static Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_WriteIntoFlexram (Ftfc_Eep_Ip_AddressType DestAddress,
                                                                uint8 const * pu8SrcAddress,
                                                                Ftfc_Eep_Ip_PageSizeType PageSize,
                                                                boolean Async
                                                               );

    static Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_WaitCcifTimeout (uint32 TimeoutCounter);

#if (FTFC_EEP_IP_LOAD_AC_INTO_RAM == STD_ON)
#define EEP_STOP_SEC_RAMCODE
#else
#define EEP_STOP_SEC_CODE
#endif
#include "Eep_MemMap.h"

/*==================================================================================================
*                                        GLOBAL FUNCTIONS
==================================================================================================*/

#define EEP_START_SEC_CODE
#include "Eep_MemMap.h"

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief   Initialize the module.
 * @details Set FlexRAM to work as EEERAM.
 *          Query EEPROM quick write status and complete the maintenance, if needed.
 *
 * Out of reset with the FSTAT[CCIF] bit clear, the partition settings (EEESIZE, DEPART)
 * are read from the data flash IFR and the emulated EEPROM file system is initialized
 * accordingly. The emulated EEPROM file system locates all valid EEPROM data records
 * in EEPROM backup and copies the newest data to FlexRAM.
 *
 * CCIF is cleared throughout the reset sequence. Completion of the reset sequence is marked by
 * setting CCIF which enables flash user commands.
 *
 * @param[in]  pConfig pointer stored in Ftfc_Eep_Ip_pxConfiguration
 *
 * @return the initialization result
 * @retval FTFC_EEP_IP_STATUS_OK             initialization successful
 * @retval FTFC_EEP_IP_STATUS_TIMEOUT        a flash cmd timeout has occurred
 * @retval FTFC_EEP_IP_STATUS_FAILED         a flash command failed to execute
 * @retval FTFC_EEP_IP_STATUS_FAILED_MGSTAT  one or more MGSTAT 1/2/3 bits were set
 *
 * @implements Ftfc_Eep_Ip_Init_Activity
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_Init (Ftfc_Eep_Ip_ConfigType const * pConfig)
{
    Ftfc_Eep_Ip_StatusType Status = FTFC_EEP_IP_STATUS_OK;
    uint8 BrownoutCode = 0U;

    FTFC_DEV_ASSERT(NULL_PTR != pConfig);

    /* Overwrite all global variables */
    Ftfc_Eep_Ip_pxConfiguration = pConfig;
    Ftfc_Eep_Ip_eBrownOutCode = FTFC_EEP_IP_NO_BO_DETECTED;
    Ftfc_Eep_Ip_xAsyncJob.TicksStarted = 0U;
    Ftfc_Eep_Ip_xAsyncJob.TicksElapsed = 0U;
    Ftfc_Eep_Ip_xAsyncJob.Result = FTFC_EEP_IP_STATUS_OK;

    if (0U == (IP_EEPROM->FCNFG & FCNFG_EEERDY_MASK))
    {
        /* When the memory partitioning is done without the option to load the EEPROM data
         * to FLEXRAM at reset, a FLEXRAM_FCC_MODE_EEERAM_NORMAL command is needed (so EEERDy gets set).
         * Also, the FLEXRAM_FCC_GET_FLEXRAM_STATUS cmd is not allowed unless the EEERDY bit is set. */
        Status = Ftfc_Eep_Ip_CmdSetFlexramFunction(FLEXRAM_FCC_MODE_EEERAM_NORMAL, 0U);
    }

    if (FTFC_EEP_IP_STATUS_OK == Status)
    {
        Status = Ftfc_Eep_Ip_CmdSetFlexramFunction(FLEXRAM_FCC_GET_FLEXRAM_STATUS, 0U);
        if (FTFC_EEP_IP_STATUS_OK == Status)
        {
            BrownoutCode = IP_EEPROM->FCCOB[FTFC_EEP_IP_FCCOB_SETRAM_READ_BROWNOUT_CODE];
            switch (BrownoutCode)
            {
                case FTFC_EEP_IP_BO_DURING_MAINTENANCE_U8:
                    Ftfc_Eep_Ip_eBrownOutCode = FTFC_EEP_IP_BO_DURING_MAINTENANCE;
                break;

                case FTFC_EEP_IP_BO_DURING_QUICK_WRITES_U8:
                    Ftfc_Eep_Ip_eBrownOutCode = FTFC_EEP_IP_BO_DURING_QUICK_WRITES;
                break;

                case FTFC_EEP_IP_BO_DURING_NORMAL_WRITES_U8:
                    Ftfc_Eep_Ip_eBrownOutCode = FTFC_EEP_IP_BO_DURING_NORMAL_WRITES;
                break;

                case FTFC_EEP_IP_NO_BO_DETECTED_U8:
                    Ftfc_Eep_Ip_eBrownOutCode = FTFC_EEP_IP_NO_BO_DETECTED;
                break;

                default:
                    Status = FTFC_EEP_IP_STATUS_FAILED;
                break;
            }

            if (FTFC_EEP_IP_NO_BO_DETECTED != Ftfc_Eep_Ip_eBrownOutCode)
            {
                Status = Ftfc_Eep_Ip_CmdSetFlexramFunction(FLEXRAM_FCC_COMPLETE_MAINTENANCE, 0U);
            }
        }
    }

    return Status;
}

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief  Read @p Length bytes from EEPROM @p SrcAddress to @p pu8DestAddress.
 *
 * @param[in]  SrcAddress EEPROM address to read from
 * @param[out] pu8DestAddress buffer to store the read data
 * @param[in]  Length how many bytes to read
 *
 * @return the read operation status
 * @retval FTFC_EEP_IP_STATUS_OK      the requested bytes were copied into the destination buffer
 * @retval FTFC_EEP_IP_STATUS_FAILED  FTFC not ready
 * @retval FTFC_EEP_IP_STATUS_FAILED  a read was attempted on an invalid page size
 *
 * @implements Ftfc_Eep_Ip_Read_Activity
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_Read (Ftfc_Eep_Ip_AddressType SrcAddress,
                                         uint8 * pu8DestAddress,
                                         Ftfc_Eep_Ip_LengthType Length
                                        )
{
    Ftfc_Eep_Ip_StatusType Status = FTFC_EEP_IP_STATUS_OK;
    Ftfc_Eep_Ip_AddressType FlexramAddress = FTFC_EEP_IP_FLEXRAM_ADDRESS + SrcAddress;
    Ftfc_Eep_Ip_AddressType DestinationAddress = (Ftfc_Eep_Ip_AddressType)pu8DestAddress;
    Ftfc_Eep_Ip_PageSizeType PageSize = FTFC_EEP_IP_PAGE_BYTE;
    Ftfc_Eep_Ip_LengthType LengthRemaining = Length;

    FTFC_DEV_ASSERT(pu8DestAddress != NULL_PTR);
    FTFC_DEV_ASSERT(SrcAddress < FTFC_EEP_IP_EEP_SIZE);
    FTFC_DEV_ASSERT((SrcAddress + LengthRemaining) <= FTFC_EEP_IP_EEP_SIZE);
    FTFC_DEV_ASSERT(LengthRemaining > 0U);

    MCAL_FAULT_INJECTION_POINT(EEP_FIP_01_EEP_AFTER_READ_SYNC);

    if (Ftfc_Eep_Ip_IsFtfcReady())
    {
        while ((LengthRemaining > 0U) && (FTFC_EEP_IP_STATUS_OK == Status))
        {
            PageSize = Ftfc_Eep_Ip_AlignedPageSize(FlexramAddress, DestinationAddress, LengthRemaining);

        #if (FTFC_EEP_IP_HIGH_TEMP_CHIP == STD_ON)
            switch (PageSize)
            {
                case FTFC_EEP_IP_PAGE_LONGWORD:
                    *((uint32 *)DestinationAddress) = *((uint32 *)FlexramAddress);
                break;

                case FTFC_EEP_IP_PAGE_WORD:
                {
                    uint32 Offset = FlexramAddress % FTFC_EEP_IP_PAGE_LONGWORD;
                    uint32 PageContents = *(uint32 *)(FlexramAddress - Offset);
                    *((uint16 *)DestinationAddress) = (uint16)(PageContents >> (Offset * 8U));
                }
                break;

                case FTFC_EEP_IP_PAGE_BYTE:
                {
                    uint32 Offset = FlexramAddress % FTFC_EEP_IP_PAGE_LONGWORD;
                    uint32 PageContents = *(uint32 *)(FlexramAddress - Offset);
                    *((uint8 *)DestinationAddress) = (uint8)(PageContents >> (Offset * 8U));
                }
                break;

                default:
                    Status = FTFC_EEP_IP_STATUS_FAILED;
                break;
            }

        #else /* FTFC_EEP_IP_HIGH_TEMP_CHIP */

            switch (PageSize)
            {
                case FTFC_EEP_IP_PAGE_LONGWORD:
                    *((uint32 *)DestinationAddress) = *((uint32 *)FlexramAddress);
                break;

                case FTFC_EEP_IP_PAGE_WORD:
                    *((uint16 *)DestinationAddress) = *((uint16 *)FlexramAddress);
                break;

                case FTFC_EEP_IP_PAGE_BYTE:
                    *((uint8 *)DestinationAddress) = *((uint8 *)FlexramAddress);
                break;

                default:
                    Status = FTFC_EEP_IP_STATUS_FAILED;
                break;
            }
        #endif /* FTFC_EEP_IP_HIGH_TEMP_CHIP */

            FlexramAddress += (Ftfc_Eep_Ip_AddressType)PageSize;
            DestinationAddress += (Ftfc_Eep_Ip_AddressType)PageSize;
            LengthRemaining -= (Ftfc_Eep_Ip_LengthType)PageSize;
        }
    }
    else
    {
        Status = FTFC_EEP_IP_STATUS_FAILED;
    }

    return Status;
}

#if (FTFC_EEP_IP_COMPARE_API == STD_ON)

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief   Compare the first @p Length bytes of @p pu8DestAddress to the contents found at @p SrcAddress.
 *
 * @param[in] pu8DestAddress pointer to the data buffer
 * @param[in] SrcAddress where the contents in EEPROM are stored
 * @param[in] Length how many bytes to compare
 *
 * @return the comparison result
 * @retval FTFC_EEP_IP_STATUS_OK                  the contents match
 * @retval FTFC_EEP_IP_STATUS_BLOCK_INCONSISTENT  the contents do not match
 * @retval FTFC_EEP_IP_STATUS_FAILED              FTFC not ready
 * @retval FTFC_EEP_IP_STATUS_FAILED              a read was attempted on an invalid page size
 *
 * @implements Ftfc_Eep_Ip_Compare_Activity
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_Compare (Ftfc_Eep_Ip_AddressType SrcAddress,
                                            uint8 const * pu8DestAddress,
                                            Ftfc_Eep_Ip_LengthType Length
                                           )
{
    Ftfc_Eep_Ip_StatusType Status = FTFC_EEP_IP_STATUS_OK;
    Ftfc_Eep_Ip_AddressType FlexramAddress = FTFC_EEP_IP_FLEXRAM_ADDRESS + SrcAddress;
    Ftfc_Eep_Ip_AddressType RamAddress = (Ftfc_Eep_Ip_AddressType)pu8DestAddress;
    Ftfc_Eep_Ip_PageSizeType PageSize = FTFC_EEP_IP_PAGE_BYTE;
    Ftfc_Eep_Ip_LengthType LengthRemaining = Length;

    FTFC_DEV_ASSERT(pu8DestAddress != NULL_PTR);
    FTFC_DEV_ASSERT(SrcAddress < FTFC_EEP_IP_EEP_SIZE);
    FTFC_DEV_ASSERT((SrcAddress + LengthRemaining) <= FTFC_EEP_IP_EEP_SIZE);
    FTFC_DEV_ASSERT(LengthRemaining > 0U);

    MCAL_FAULT_INJECTION_POINT(EEP_FIP_02_EEP_AFTER_COMPARE_SYNC);

    if (Ftfc_Eep_Ip_IsFtfcReady())
    {
        while ((LengthRemaining > 0U) && (FTFC_EEP_IP_STATUS_OK == Status))
        {
            PageSize = Ftfc_Eep_Ip_AlignedPageSize(FlexramAddress, RamAddress, LengthRemaining);

        #if (FTFC_EEP_IP_HIGH_TEMP_CHIP == STD_ON)
            switch (PageSize)
            {
                case FTFC_EEP_IP_PAGE_LONGWORD:
                    if (*((uint32 *)RamAddress) != *((uint32 *)FlexramAddress))
                    {
                        Status = FTFC_EEP_IP_STATUS_BLOCK_INCONSISTENT;
                    }
                break;

                case FTFC_EEP_IP_PAGE_WORD:
                {
                    uint32 Offset = FlexramAddress % FTFC_EEP_IP_PAGE_LONGWORD;
                    uint32 PageContents = *(uint32 *)(FlexramAddress - Offset);
                    if (*((uint16 *)RamAddress) != (uint16)(PageContents >> (Offset * 8U)))
                    {
                        Status = FTFC_EEP_IP_STATUS_BLOCK_INCONSISTENT;
                    }
                }
                break;

                case FTFC_EEP_IP_PAGE_BYTE:
                {
                    uint32 Offset = FlexramAddress % FTFC_EEP_IP_PAGE_LONGWORD;
                    uint32 PageContents = *(uint32 *)(FlexramAddress - Offset);
                    if (*((uint8 *)RamAddress) != (uint8)(PageContents >> (Offset * 8U)))
                    {
                        Status = FTFC_EEP_IP_STATUS_BLOCK_INCONSISTENT;
                    }
                }
                break;

                default:
                    Status = FTFC_EEP_IP_STATUS_FAILED;
                break;
            }

        #else /* FTFC_EEP_IP_HIGH_TEMP_CHIP */

            switch (PageSize)
            {
                case FTFC_EEP_IP_PAGE_LONGWORD:
                    if (*((uint32 *)RamAddress) != *((uint32 *)FlexramAddress))
                    {
                        Status = FTFC_EEP_IP_STATUS_BLOCK_INCONSISTENT;
                    }
                break;

                case FTFC_EEP_IP_PAGE_WORD:
                    if (*((uint16 *)RamAddress) != *((uint16 *)FlexramAddress))
                    {
                        Status = FTFC_EEP_IP_STATUS_BLOCK_INCONSISTENT;
                    }
                break;

                case FTFC_EEP_IP_PAGE_BYTE:
                    if (*((uint8 *)RamAddress) != *((uint8 *)FlexramAddress))
                    {
                        Status = FTFC_EEP_IP_STATUS_BLOCK_INCONSISTENT;
                    }
                break;

                default:
                    Status = FTFC_EEP_IP_STATUS_FAILED;
                break;
            }
        #endif /* FTFC_EEP_IP_HIGH_TEMP_CHIP */

            FlexramAddress += (Ftfc_Eep_Ip_AddressType)PageSize;
            RamAddress += (Ftfc_Eep_Ip_AddressType)PageSize;
            LengthRemaining -= (Ftfc_Eep_Ip_LengthType)PageSize;
        }
    }
    else
    {
        Status = FTFC_EEP_IP_STATUS_FAILED;
    }

    return Status;
}

#endif /* FTFC_EEP_IP_COMPARE_API */

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief   Write @p PageSize bytes from @p pu8SrcAddress buffer to EEPROM at @p offset DestAddress.
 *
 * @param[out] DestAddress EEPROM offset
 * @param[in]  pu8SrcAddress buffer containing the data to be written
 * @param[in]  PageSize must be a valid PageSize: 1, 2 or 4 bytes for FTFC and only 4 bytes for FTFM
 * @param[in]  Async choose between a synchronous and an asynchronous job
 *
 * @return the write operation result
 * @retval FTFC_EEP_IP_STATUS_FAILED         FTFC not ready
 * @retval FTFC_EEP_IP_STATUS_FAILED         an invalid page alignment was given
 * @retval FTFC_EEP_IP_STATUS_FAILED         sync: some of the FSTAT error bits were set
 * @retval FTFC_EEP_IP_STATUS_TIMEOUT        sync: timeout occurred while waiting for CCIF
 * @retval FTFC_EEP_IP_STATUS_OK             sync: page successfully written to EFLASH
 * @retval FTFC_EEP_IP_STATUS_PENDING        async: the page was written to FlexRAM, but the status of
 *                                           the EFLASH record shall be interrogated with GetJobResult
 *
 * @implements Ftfc_Eep_Ip_Write_Activity
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_Write (Ftfc_Eep_Ip_AddressType DestAddress,
                                          uint8 const * pu8SrcAddress,
                                          Ftfc_Eep_Ip_PageSizeType PageSize,
                                          boolean Async
                                         )
{
    Ftfc_Eep_Ip_StatusType Status = FTFC_EEP_IP_STATUS_FAILED;
    Ftfc_Eep_Ip_AddressType DestAddr = DestAddress;

    FTFC_DEV_ASSERT(pu8SrcAddress != NULL_PTR);
    FTFC_DEV_ASSERT(DestAddr < FTFC_EEP_IP_EEP_SIZE);
    FTFC_DEV_ASSERT((DestAddr + (Ftfc_Eep_Ip_AddressType)PageSize) <= FTFC_EEP_IP_EEP_SIZE);
    FTFC_DEV_ASSERT(PageSize == Ftfc_Eep_Ip_AlignedPageSize(DestAddr, \
                                                            (Ftfc_Eep_Ip_AddressType)pu8SrcAddress, \
                                                            (Ftfc_Eep_Ip_LengthType)PageSize));

    /* Convert the EEPROM location to a FlexRAM address */
    DestAddr += FTFC_EEP_IP_FLEXRAM_ADDRESS;

    if (Ftfc_Eep_Ip_IsFtfcReady())
    {
    #if (FTFC_EEP_IP_HIGH_TEMP_CHIP == STD_ON)
        if ((FTFC_EEP_IP_PAGE_BYTE == PageSize) ||
            (FTFC_EEP_IP_PAGE_WORD == PageSize))
        {
            uint8 Offset = DestAddr % FTFC_EEP_IP_PAGE_LONGWORD;
            uint32 * PageAddress = (uint32 *)(DestAddr - Offset);
            uint32 PageContents = *PageAddress;

            uint8 Position = 0U;
            uint8 SrcByte = 0U;
            uint32 Mask = 0U;

            if (FTFC_EEP_IP_PAGE_LONGWORD - Offset >= PageSize)
            {
                for (Position = Offset; Position - Offset < PageSize; ++Position)
                {
                    Mask = 0xFFU << (8U * Position);                /* construct the mask */
                    PageContents &= ~Mask;                          /* clear the targeted byte */
                    SrcByte = *(pu8SrcAddress + Position - Offset); /* extract the new byte */
                    PageContents |= SrcByte << (Position * 8U);     /* set the desired bits */
                }
                Status = Ftfc_Eep_Ip_AcEflashWrite((Ftfc_Eep_Ip_AddressType)PageAddress, (uint8 *)&PageContents, PageSize, Async);
            }
            else
            {
                /* the request crosses the 4-byte aligned page boundary */
                Status = FTFC_EEP_IP_STATUS_FAILED;
            }
        }
        else
    #endif /* FTFC_EEP_IP_HIGH_TEMP_CHIP */
        {
            Status = Ftfc_Eep_Ip_AcEflashWrite(DestAddr, pu8SrcAddress, PageSize, Async);
        }

        if ((FTFC_EEP_IP_STATUS_PENDING == Status) && (TRUE == Async))
        {
            /* In async mode the user can skip the waiting part, but he must make sure that
            * the DFLASH partition is not accessed (for r/w) until the CCIF flag is set. */
            SchM_Enter_Eep_EEP_EXCLUSIVE_AREA_10();
            Ftfc_Eep_Ip_xAsyncJob.Result = FTFC_EEP_IP_STATUS_PENDING;
            Ftfc_Eep_Ip_xAsyncJob.TicksStarted = OsIf_GetCounter(FTFC_EEP_IP_TIMEOUT_TYPE);
            Ftfc_Eep_Ip_xAsyncJob.TicksElapsed = 0U;
            SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_10();
        }
    }

    return Status;
}

#if (FTFC_EEP_IP_QUICK_WRITES_API == STD_ON)

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief   QuickWrite API
 * @details
 *
 * For configurations with interleaved flash blocks for EEPROM backup, quick writes
 * should be restricted to either within the first half of EEERAM or within the second half
 * of EEERAM. Otherwise, FSTAT[ACCERR] will be returned.
 *
 * Once a quick write is started, user must finish the entire quick write activity prior to
 * starting another FTFC or CSE command.
 *
 * @param[out] DestAddress EEPROM offset needs to be 4-bytes aligned
 * @param[in]  pu8SrcAddress
 * @param[in]  Length
 *
 * @return quick write status
 * @retval FTFC_EEP_IP_STATUS_FAILED         FTFC not ready
 * @retval FTFC_EEP_IP_STATUS_OK             everything ok
 * @retval FTFC_EEP_IP_STATUS_FAILED         invalid page size/alignment
 * @retval FTFC_EEP_IP_STATUS_FAILED         some of the FSTAT error bits were set
 * @retval FTFC_EEP_IP_STATUS_FAILED_MGSTAT  one or more MGSTAT 1/2/3 bits were set
 * @retval FTFC_EEP_IP_STATUS_TIMEOUT        timeout occurred while waiting for CCIF
 *
 * @implements Ftfc_Eep_Ip_QuickWrite_Activity
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_QuickWrite (Ftfc_Eep_Ip_AddressType DestAddress,
                                               uint8 const * pu8SrcAddress,
                                               Ftfc_Eep_Ip_LengthType Length
                                              )
{
    Ftfc_Eep_Ip_StatusType Status = FTFC_EEP_IP_STATUS_FAILED;
    Ftfc_Eep_Ip_AddressType SrcAddress = (Ftfc_Eep_Ip_AddressType)pu8SrcAddress;
    Ftfc_Eep_Ip_AddressType DestAddr = DestAddress;
    Ftfc_Eep_Ip_LengthType LengthRemaining = Length;

    /* Quick write mode only accepts 4-byte aligned writes */
    FTFC_DEV_ASSERT((DestAddr % (Ftfc_Eep_Ip_AddressType)FTFC_EEP_IP_PAGE_LONGWORD) == 0U);
    FTFC_DEV_ASSERT((SrcAddress % (Ftfc_Eep_Ip_AddressType)FTFC_EEP_IP_PAGE_LONGWORD) == 0U);

    /* FSTAT[ACCERR] is Set if FlexRAM Function Control Code is set to make FlexRAM available for
     * EEPROM quick writes but the number of bytes allocated for quick write is less than 16,
     * more than 512, or not divisible by 4 (only 32-bit quick writes are allowed) */
    FTFC_DEV_ASSERT(LengthRemaining >= FTFC_EEP_IP_QUICK_WRITES_LENGTH_MIN);
    FTFC_DEV_ASSERT(LengthRemaining <= FTFC_EEP_IP_QUICK_WRITES_LENGTH_MAX);
    FTFC_DEV_ASSERT((LengthRemaining % (Ftfc_Eep_Ip_LengthType)FTFC_EEP_IP_PAGE_LONGWORD) == 0U);

    Status = Ftfc_Eep_Ip_CmdSetFlexramFunction(FLEXRAM_FCC_MODE_EEERAM_QWRITES, (uint16)LengthRemaining);
    if (FTFC_EEP_IP_STATUS_OK == Status)
    {
        while ((LengthRemaining > 0U) && (FTFC_EEP_IP_STATUS_OK == Status))
        {
            Status = Ftfc_Eep_Ip_Write(DestAddr, (uint8 *)SrcAddress, FTFC_EEP_IP_PAGE_LONGWORD, FALSE);
            LengthRemaining -= (Ftfc_Eep_Ip_LengthType)FTFC_EEP_IP_PAGE_LONGWORD;
            DestAddr += (Ftfc_Eep_Ip_AddressType)FTFC_EEP_IP_PAGE_LONGWORD;
            SrcAddress += (Ftfc_Eep_Ip_AddressType)FTFC_EEP_IP_PAGE_LONGWORD;
        }

        if (FTFC_EEP_IP_STATUS_OK == Status)
        {
            Status = Ftfc_Eep_Ip_CmdSetFlexramFunction(FLEXRAM_FCC_MODE_EEERAM_NORMAL, 0U);
        }
        else
        {
            (void) Ftfc_Eep_Ip_CmdSetFlexramFunction(FLEXRAM_FCC_MODE_EEERAM_NORMAL, 0U);
        }
    }

    return Status;
}

#endif /* FTFC_EEP_IP_QUICK_WRITES_API */

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief   Erase API
 * @details Invokes a write with ERASED_CELL_VALUE.
 *
 * @return the erase operation result
 * @retval FTFC_EEP_IP_STATUS_FAILED         FTFC not ready
 * @retval FTFC_EEP_IP_STATUS_FAILED         an invalid page size/alignment was given
 * @retval FTFC_EEP_IP_STATUS_FAILED         sync: some of the FSTAT error bits were set
 * @retval FTFC_EEP_IP_STATUS_TIMEOUT        sync: timeout occurred while waiting for CCIF
 * @retval FTFC_EEP_IP_STATUS_OK             sync: page successfully written to EFLASH
 * @retval FTFC_EEP_IP_STATUS_PENDING        async: the page was written to FlexRAM, but the status of
 *                                           the EFLASH record shall be interrogated with GetJobResult
 *
 * @implements Ftfc_Eep_Ip_Erase_Activity
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_Erase (Ftfc_Eep_Ip_AddressType Address,
                                          Ftfc_Eep_Ip_PageSizeType PageSize,
                                          boolean Async
                                         )
{
    return Ftfc_Eep_Ip_Write(Address, (uint8 const *) &Ftfc_Eep_Ip_u32EraseValue, PageSize, Async);
}

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief  Interrogate the result of the last async job, considering the timeout and FSTAT errors.
 *
 * @return the result of the last async job
 * @retval FTFC_EEP_IP_STATUS_OK            the job finished successfully
 * @retval FTFC_EEP_IP_STATUS_FAILED        FSTAT error bits were set
 * @retval FTFC_EEP_IP_STATUS_PENDING       the job is still waiting for CCIF
 * @retval FTFC_EEP_IP_STATUS_TIMEOUT       a timeout has occurred while waiting for CCIF
 *
 * @implements Ftfc_Eep_Ip_GetJobResult_Activity
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_GetJobResult (void)
{
    uint32 Timeout = 0U;

    SchM_Enter_Eep_EEP_EXCLUSIVE_AREA_11();

    if (FTFC_EEP_IP_STATUS_PENDING == Ftfc_Eep_Ip_xAsyncJob.Result)
    {
        if (Ftfc_Eep_Ip_IsFtfcReady())
        {
            Ftfc_Eep_Ip_xAsyncJob.Result = Ftfc_Eep_Ip_CheckFstatErrors();
            Ftfc_Eep_Ip_xAsyncJob.TicksElapsed = 0U;
        }
        else
        {
            Timeout = OsIf_MicrosToTicks(FTFC_EEP_IP_ASYNC_WRITE_TIMEOUT, FTFC_EEP_IP_TIMEOUT_TYPE);
            Ftfc_Eep_Ip_xAsyncJob.TicksElapsed += OsIf_GetElapsed(&Ftfc_Eep_Ip_xAsyncJob.TicksStarted, FTFC_EEP_IP_TIMEOUT_TYPE);
            if (Ftfc_Eep_Ip_xAsyncJob.TicksElapsed >= Timeout)
            {
                Ftfc_Eep_Ip_xAsyncJob.Result = FTFC_EEP_IP_STATUS_TIMEOUT;
                Ftfc_Eep_Ip_xAsyncJob.TicksElapsed = 0U;
            }
        }
    }

    SchM_Exit_Eep_EEP_EXCLUSIVE_AREA_11();

    return Ftfc_Eep_Ip_xAsyncJob.Result;
}

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief  Getter for Ftfc_Eep_Ip_eBrownOutCode.
 *
 * @return the brownout code read after reset
 * @retval 0x04 normal write was interrupted
 * @retval 0x02 quick write was interrupted before writing all bytes to flash
 * @retval 0x01 quick write was interrupted before maintenance completed
 *
 * @implements Ftfc_Eep_Ip_GetBrownOutCode_Activity
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Ftfc_Eep_Ip_BrownOutCodeType Ftfc_Eep_Ip_GetBrownOutCode (void)
{
    return Ftfc_Eep_Ip_eBrownOutCode;
}

#define EEP_STOP_SEC_CODE
#include "Eep_MemMap.h"

/*==================================================================================================
*                                        LOCAL FUNCTIONS
==================================================================================================*/

#define EEP_START_SEC_CODE
#include "Eep_MemMap.h"

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief Compute the page size, considering the addresses alignment and the remaining bytes.
 *
 * @param[in]  FlexramAddress the FlexRAM address
 * @param[in]  RamAddress the RAM address
 * @param[in]  Length how many bytes are left to process
 *
 * @return the page size that FlexRAM will accept given the address/buffer alignments
 * @retval FTFC_EEP_IP_PAGE_BYTE     for unaligned operations
 * @retval FTFC_EEP_IP_PAGE_WORD     16-bit aligned operations
 * @retval FTFC_EEP_IP_PAGE_LONGWORD 32-bit aligned operations
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static Ftfc_Eep_Ip_PageSizeType Ftfc_Eep_Ip_AlignedPageSize (Ftfc_Eep_Ip_AddressType FlexramAddress,
                                                             Ftfc_Eep_Ip_AddressType RamAddress,
                                                             Ftfc_Eep_Ip_LengthType Length
                                                            )
{
    Ftfc_Eep_Ip_PageSizeType PageSize = FTFC_EEP_IP_PAGE_BYTE;

#if (FTFC_EEP_IP_ALIGNED_RAM_ACCESS == STD_OFF)
    (void) RamAddress; /* avoid compiler warning */
#endif

    if ((0U == (FlexramAddress % (Ftfc_Eep_Ip_AddressType)FTFC_EEP_IP_PAGE_LONGWORD))
    #if (FTFC_EEP_IP_ALIGNED_RAM_ACCESS == STD_ON)
        && (0U == (RamAddress  % (Ftfc_Eep_Ip_AddressType)FTFC_EEP_IP_PAGE_LONGWORD))
    #endif
    )
    {
        if (Length >= (Ftfc_Eep_Ip_LengthType)FTFC_EEP_IP_PAGE_LONGWORD)
        {
            /* 4 bytes aligned and length >= 4 bytes: 4 bytes operation */
            PageSize = FTFC_EEP_IP_PAGE_LONGWORD;
        }
        else if (Length >= (Ftfc_Eep_Ip_LengthType)FTFC_EEP_IP_PAGE_WORD)
        {
            /* 4 bytes aligned and 4bytes > length >= 2 bytes : 2 bytes operation */
            PageSize = FTFC_EEP_IP_PAGE_WORD;
        }
        else
        {
            /* 4 bytes aligned and length < 2 bytes : 1 byte operation */
            PageSize = FTFC_EEP_IP_PAGE_BYTE;
        }
    }
    else if ((0U == (FlexramAddress % (Ftfc_Eep_Ip_AddressType)FTFC_EEP_IP_PAGE_WORD))
        #if (FTFC_EEP_IP_ALIGNED_RAM_ACCESS == STD_ON)
             && (0U == (RamAddress  % (Ftfc_Eep_Ip_AddressType)FTFC_EEP_IP_PAGE_WORD))
        #endif
            )
    {
        if (Length >= (Ftfc_Eep_Ip_LengthType)FTFC_EEP_IP_PAGE_WORD)
        {
            /* 2 bytes aligned and length >= 2 bytes : 2 bytes operation */
            PageSize = FTFC_EEP_IP_PAGE_WORD;
        }
        else
        {
            /* 2 bytes aligned and length < 2 bytes : 1 byte operation */
            PageSize = FTFC_EEP_IP_PAGE_BYTE;
        }
    }
    else
    {
        /* unaligned operation */
        PageSize = FTFC_EEP_IP_PAGE_BYTE;
    }

    return PageSize;
}

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief   Interrogate the FTFC status.
 *
 * @details If CCIF is zero, the previous command execution is still active, a new command write
 *          sequence cannot be started, and all writes to the FCCOB registers are ignored.
 *
 * @details The CCIF and EEERDY flag will remain negated until all EEPROM maintenance activities
 *          have been completed.
 *
 * @return FTFC status
 * @retval TRUE  FTFC ready
 * @retval FALSE FTFC not ready
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static boolean Ftfc_Eep_Ip_IsFtfcReady (void)
{
    boolean Status = FALSE;

    if (0U != (IP_EEPROM->FCNFG & FCNFG_EEERDY_MASK))
    {
        if (0U != (IP_EEPROM->FSTAT & FSTAT_CCIF_MASK))
        {
            Status = TRUE;
        }
    }

    return Status;
}

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief   Construct the SETRAM flash command (Set FlexRAM Function)
 *
 * @details First, set up all required FCCOB fields and then initiate the command's execution
 * by writing a 1 to the FSTAT[CCIF] bit.
 *
 * @param[in] FlexramFuncControlCode the control code for the SETRAM command
 * @param[in] QuickWritesLength only used for FLEXRAM_FCC_MODE_EEERAM_QWRITES
 *
 * @return flash command execution status
 * @retval FTFC_EEP_IP_STATUS_OK             the command has completed successfully
 * @retval FTFC_EEP_IP_STATUS_TIMEOUT        a timeout has occurred
 * @retval FTFC_EEP_IP_STATUS_FAILED         the flash command execution failed
 * @retval FTFC_EEP_IP_STATUS_FAILED_MGSTAT  one or more MGSTAT 1/2/3 bits were set (FTFM only)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_CmdSetFlexramFunction (Ftfc_Eep_Ip_FlexramFccType FlexramFuncControlCode,
                                                                 uint16 QuickWritesLength
                                                                )
{
    Ftfc_Eep_Ip_StatusType Status = FTFC_EEP_IP_STATUS_FAILED;

    /* If CCIF is zero:                                 *
     * - the previous command execution is still active *
     * - a new command write sequence cannot be started *
     * - all writes to the FCCOB registers are ignored. */
    if (0U != (IP_EEPROM->FSTAT & FSTAT_CCIF_MASK))
    {
        /* The individual registers that make up the FCCOB data set can be written in any order. */

        IP_EEPROM->FCCOB[FTFC_EEP_IP_FCCOB_SETRAM_WRITE_CMD_SETRAM] = (uint8)FTFC_EEP_IP_FLASH_CMD_SETRAM_U8;
        IP_EEPROM->FCCOB[FTFC_EEP_IP_FCCOB_SETRAM_WRITE_FR_FCC] = (uint8)FlexramFuncControlCode;

        if (FLEXRAM_FCC_MODE_EEERAM_QWRITES == FlexramFuncControlCode)
        {
            IP_EEPROM->FCCOB[FTFC_EEP_IP_FCCOB_SETRAM_WRITE_QUICKWRITES_LENGTH_MSB] = (uint8)(QuickWritesLength >> 8U);
            IP_EEPROM->FCCOB[FTFC_EEP_IP_FCCOB_SETRAM_WRITE_QUICKWRITES_LENGTH_LSB] = (uint8) QuickWritesLength;
        }

        /* This clears the CCIF bit, which locks all FCCOB parameter fields and
         * they cannot be changed by the user until the command completes (CCIF returns to 1). */
        Status = Ftfc_Eep_Ip_AcEflashCmd();
    }

    return Status;
}

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief   Check the FSTAT error flags.
 *
 * @details If the parameter check fails, the FSTAT[ACCERR] (access error) flag is set.
 * If the protection check fails, the FSTAT[FPVIOL] (protection error) flag is set.
 * Run-time errors are reported in the FSTAT[MGSTAT0] bit.
 *
 * @return the kind of error detected:
 * @retval FTFC_EEP_IP_STATUS_OK            no error flags were set
 * @retval FTFC_EEP_IP_STATUS_FAILED        FSTAT error bits were set
 * @retval FTFC_EEP_IP_STATUS_FAILED_MGSTAT MGSTAT1/2/3 bits were set (FTFM only)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_CheckFstatErrors (void)
{
    Ftfc_Eep_Ip_StatusType Status = FTFC_EEP_IP_STATUS_OK;

#if (FTFC_EEP_IP_HIGH_TEMP_CHIP == STD_ON)
    uint8 ErrorFlagsHT = FSTAT_MGSTAT1_MASK | FSTAT_MGSTAT2_MASK | FSTAT_MGSTAT3_MASK;
#endif
    uint8 ErrorFlags = FSTAT_MGSTAT0_MASK | FSTAT_FPVIOL_MASK | FSTAT_ACCERR_MASK | FSTAT_RDCOLERR_MASK;
    uint8 FlashStatus = IP_EEPROM->FSTAT;

    if (0U != (FlashStatus & ErrorFlags))
    {
        Status = FTFC_EEP_IP_STATUS_FAILED;
    }

#if (FTFC_EEP_IP_HIGH_TEMP_CHIP == STD_ON)
    if (0U != (FlashStatus & ErrorFlagsHT))
    {
        Status = FTFC_EEP_IP_STATUS_FAILED_MGSTAT;
    }
#endif

    return Status;
}

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief   Implements the steps needed to be followed when accessing the EFLASH.
 *
 * @details Clear the FSTAT errors, call the notification functions, run the AC and check for errors.
 *
 * Do not attempt to read a flash block while the FTFC is running a command (CCIF = 0)
 * on that same block.
 *
 * @return the EFLASH operation result
 * @retval FTFC_EEP_IP_STATUS_OK             cmd executed successfully
 * @retval FTFC_EEP_IP_STATUS_TIMEOUT        timeout occurred while waiting for CCIF to be set
 * @retval FTFC_EEP_IP_STATUS_FAILED         some of the FSTAT error bits were set
 * @retval FTFC_EEP_IP_STATUS_FAILED_MGSTAT  one or more MGSTAT 1/2/3 bits were set (FTFM only)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_AcEflashCmd (void)
{
    Ftfc_Eep_Ip_StatusType Status = FTFC_EEP_IP_STATUS_OK;

    /* When set, the Access Error (ACCERR) and Flash Protection Violation (FPVIOL) bits in this
     * register prevent the launch of any more commands until the flag is cleared. */
    IP_EEPROM->FSTAT = (FSTAT_ACCERR_MASK | FSTAT_FPVIOL_MASK); /* W1C */

    if (Ftfc_Eep_Ip_pxConfiguration->startEepromAccessNotifPtr != NULL_PTR)
    {
        Ftfc_Eep_Ip_pxConfiguration->startEepromAccessNotifPtr();
    }

    Status = Ftfc_Eep_Ip_StartFlashCommand();

    if (Ftfc_Eep_Ip_pxConfiguration->finishedEepromAccessNotifPtr != NULL_PTR)
    {
        Ftfc_Eep_Ip_pxConfiguration->finishedEepromAccessNotifPtr();
    }

    if (FTFC_EEP_IP_STATUS_OK == Status)
    {
        Status = Ftfc_Eep_Ip_CheckFstatErrors();
    }

    return Status;
}

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief   Implements the steps needed to be followed when accessing the EFLASH.
 *
 * @details Clear the FSTAT errors, call the notification functions, run the AC and check for errors.
 *
 * @param[in] DestAddress
 * @param[in] pu8SrcAddress
 * @param[in] PageSize
 * @param[in] Async
 *
 * @return the write operation result
 * @retval FTFC_EEP_IP_STATUS_OK             sync: page successfully written to EFLASH
 * @retval FTFC_EEP_IP_STATUS_FAILED         sync: some of the FSTAT error bits were set
 * @retval FTFC_EEP_IP_STATUS_FAILED_MGSTAT  sync: one or more MGSTAT 1/2/3 bits were set
 * @retval FTFC_EEP_IP_STATUS_TIMEOUT        sync: timeout occurred while waiting for CCIF
 * @retval FTFC_EEP_IP_STATUS_PENDING        async: the page was written to FlexRAM, but the status of
 *                                           the EFLASH record shall be interrogated with GetJobResult
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_AcEflashWrite (Ftfc_Eep_Ip_AddressType DestAddress,
                                                         uint8 const * pu8SrcAddress,
                                                         Ftfc_Eep_Ip_PageSizeType PageSize,
                                                         boolean Async
                                                        )
{
    Ftfc_Eep_Ip_StatusType Status = FTFC_EEP_IP_STATUS_OK;

    /* When set, the Access Error (ACCERR) and Flash Protection Violation (FPVIOL) bits in this
     * register prevent writes to the FlexRAM (when EEERDY is set) until the flag is cleared */
    IP_EEPROM->FSTAT = (FSTAT_ACCERR_MASK | FSTAT_FPVIOL_MASK); /* W1C */

    if (Ftfc_Eep_Ip_pxConfiguration->startEepromAccessNotifPtr != NULL_PTR)
    {
        Ftfc_Eep_Ip_pxConfiguration->startEepromAccessNotifPtr();
    }

    /* When configured for emulated EEPROM use, writes to an unprotected location in FlexRAM invoke
     * the emulated EEPROM file system to program a new EEPROM data record in the emulated EEPROM
     * backup memory in a round-robin fashion. This is why we can't write into FlexRAM
     * while fetching code from the DFLASH partition. */
    Status = Ftfc_Eep_Ip_WriteIntoFlexram(DestAddress, pu8SrcAddress, PageSize, Async);

    if (Ftfc_Eep_Ip_pxConfiguration->finishedEepromAccessNotifPtr != NULL_PTR)
    {
        Ftfc_Eep_Ip_pxConfiguration->finishedEepromAccessNotifPtr();
    }

    if ((FTFC_EEP_IP_STATUS_OK == Status) && (FALSE == Async))
    {
        /* the FSTAT register is ready to be interrogated only after
        * the FlexRAM write has successfully completed */
        Status = Ftfc_Eep_Ip_CheckFstatErrors();
    }

    return Status;
}

#define EEP_STOP_SEC_CODE
#include "Eep_MemMap.h"

#if (FTFC_EEP_IP_LOAD_AC_INTO_RAM == STD_ON)
#define EEP_START_SEC_RAMCODE
#else
#define EEP_START_SEC_CODE
#endif
#include "Eep_MemMap.h"

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief   Start the previously constructed Flash Command and wait for it to complete.
 *
 * @details This function must not run from the DFLASH partition shared with EFLASH.
 *
 * Before launching a command, the ACCERR and FPVIOL bits in the FSTAT register  must be zero and
 * the CCIF flag must read 1 to verify that any previous command has completed. If CCIF is zero,
 * the previous command execution is still active, a new command write sequence cannot be started,
 * and all writes to the FCCOB registers are ignored.
 *
 * Once all relevant command parameters have been loaded, the user launches the command
 * by clearing the FSTAT[CCIF] bit by writing a '1' to it.
 *
 * Do not attempt to read a flash block while the FTFC is running a command (CCIF = 0)
 * on that same block.

 * @return the cmd execution result
 * @retval FTFC_EEP_IP_STATUS_OK       successful cmd execution
 * @retval FTFC_EEP_IP_STATUS_TIMEOUT  a timeout has occurred while waiting for the CCIF flag
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_StartFlashCommand (void)
{
    /* W1C the CCIF bit to start the command */
    IP_EEPROM->FSTAT = FSTAT_CCIF_MASK;

    /* The CCIF flag remains zero until the FTFC command completes */
    return Ftfc_Eep_Ip_WaitCcifTimeout(FTFC_EEP_IP_ABORT_TIMEOUT);
}

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief   Write a page into FlexRAM.
 * @details This function must not run from the DFLASH partition shared with EFLASH.
 *
 * @param[in] DestAddress
 * @param[in] pu8SrcAddress
 * @param[in] PageSize
 * @param[in] Async
 *
 * @return the result of the write request
 * @retval FTFC_EEP_IP_STATUS_PENDING  async: GetJobResult shall be used to check the FSTAT errors
 * @retval FTFC_EEP_IP_STATUS_OK       sync: the write has succeeded
 * @retval FTFC_EEP_IP_STATUS_TIMEOUT  sync: a timeout has occured while waiting for the CCIF flag
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_WriteIntoFlexram (Ftfc_Eep_Ip_AddressType DestAddress,
                                                            uint8 const * pu8SrcAddress,
                                                            Ftfc_Eep_Ip_PageSizeType PageSize,
                                                            boolean Async
                                                           )
{
    Ftfc_Eep_Ip_StatusType Status = FTFC_EEP_IP_STATUS_PENDING;
    Ftfc_Eep_Ip_AddressType SrcAdd = (Ftfc_Eep_Ip_AddressType)pu8SrcAddress;

#if (FTFC_EEP_IP_HIGH_TEMP_CHIP == STD_ON)

    *(uint32 *)DestAddress = *(uint32 const *)SrcAdd;
    (void) PageSize; /* Page size is always LONGWORD for FTFM */

#else /* FTFC_EEP_IP_HIGH_TEMP_CHIP */

    switch (PageSize)
    {
        case FTFC_EEP_IP_PAGE_BYTE:
            *(uint8 *)DestAddress = *(uint8 const *)SrcAdd;
        break;

        case FTFC_EEP_IP_PAGE_WORD:
            *(uint16 *)DestAddress = *(uint16 const *)SrcAdd;
        break;

        case FTFC_EEP_IP_PAGE_LONGWORD:
            *(uint32 *)DestAddress = *(uint32 const *)SrcAdd;
        break;

        default:
            /* page size checked in the upper call stack */
        break;
    }

#endif /* FTFC_EEP_IP_HIGH_TEMP_CHIP */

    MCAL_FAULT_INJECTION_POINT(EEP_FIP_FTFx_CHECK_BO);

    /* When a write occurs the FlexRAM is not accessible until the CCIF bit is set. */
    if (FALSE == Async)
    {
        Status = Ftfc_Eep_Ip_WaitCcifTimeout(FTFC_EEP_IP_SYNC_WRITE_TIMEOUT);
    }

    return Status;
}

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * @brief   Wait for the CCIF flag to be set or exit with a given timeout.
 *
 * @details This function must not run from the DFLASH partition shared with EFLASH.
 *
 * @param[in] TimeoutCounter given as a dummy counter that is decremented in a while loop
 *
 * @return the status of the flash operation
 * @retval FTFC_EEP_IP_STATUS_OK       the flash cmd/write has completed
 * @retval FTFC_EEP_IP_STATUS_TIMEOUT  a timeout has occurred
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static Ftfc_Eep_Ip_StatusType Ftfc_Eep_Ip_WaitCcifTimeout (uint32 TimeoutCounter)
{
    Ftfc_Eep_Ip_StatusType Status = FTFC_EEP_IP_STATUS_OK;
    uint32 counter = TimeoutCounter;

    while (0U == (IP_EEPROM->FSTAT & FSTAT_CCIF_MASK))
    {
        --counter;
        if (0U == counter)
        {
            Status = FTFC_EEP_IP_STATUS_TIMEOUT;
            break;
        }

        /* feed the watchdog */
        if (NULL_PTR != Ftfc_Eep_Ip_pxConfiguration->acCallBackPtr)
        {
            Ftfc_Eep_Ip_pxConfiguration->acCallBackPtr();
        }
    }

    return Status;
}

#if (FTFC_EEP_IP_LOAD_AC_INTO_RAM == STD_ON)
#define EEP_STOP_SEC_RAMCODE
#else
#define EEP_STOP_SEC_CODE
#endif
#include "Eep_MemMap.h"

#ifdef __cplusplus
}
#endif

/** @} */
