/*
* The Clear BSD License
* Copyright 2013-2016 Freescale Semiconductor, Inc.
* Copyright 2016-2018 NXP
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted (subject to the limitations in the
* disclaimer below) provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
*   notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
*   notice, this list of conditions and the following disclaimer in the
*   documentation and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
*   contributors may be used to endorse or promote products derived from
*   this software without specific prior written permission.
*
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
* HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

#include "fsl_ftfx_controller.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/*!
 * @name Flash controller command numbers
 * @{
 */
#define FTFx_VERIFY_BLOCK 0x00U                    /*!< RD1BLK*/
#define FTFx_VERIFY_SECTION 0x01U                  /*!< RD1SEC*/
#define FTFx_PROGRAM_CHECK 0x02U                   /*!< PGMCHK*/
#define FTFx_READ_RESOURCE 0x03U                   /*!< RDRSRC*/
#define FTFx_PROGRAM_LONGWORD 0x06U                /*!< PGM4*/
#define FTFx_PROGRAM_PHRASE 0x07U                  /*!< PGM8*/
#define FTFx_ERASE_BLOCK 0x08U                     /*!< ERSBLK*/
#define FTFx_ERASE_SECTOR 0x09U                    /*!< ERSSCR*/
#define FTFx_PROGRAM_SECTION 0x0BU                 /*!< PGMSEC*/
#define FTFx_GENERATE_CRC 0x0CU                    /*!< CRCGEN*/
#define FTFx_VERIFY_ALL_BLOCK 0x40U                /*!< RD1ALL*/
#define FTFx_READ_ONCE 0x41U                       /*!< RDONCE or RDINDEX*/
#define FTFx_PROGRAM_ONCE 0x43U                    /*!< PGMONCE or PGMINDEX*/
#define FTFx_ERASE_ALL_BLOCK 0x44U                 /*!< ERSALL*/
#define FTFx_SECURITY_BY_PASS 0x45U                /*!< VFYKEY*/
#define FTFx_SWAP_CONTROL 0x46U                    /*!< SWAP*/
#define FTFx_ERASE_ALL_BLOCK_UNSECURE 0x49U        /*!< ERSALLU*/
#define FTFx_VERIFY_ALL_EXECUTE_ONLY_SEGMENT 0x4AU /*!< RD1XA*/
#define FTFx_ERASE_ALL_EXECUTE_ONLY_SEGMENT 0x4BU  /*!< ERSXA*/
#define FTFx_PROGRAM_PARTITION 0x80U               /*!< PGMPART*/
#define FTFx_SET_FLEXRAM_FUNCTION 0x81U            /*!< SETRAM*/
/*@}*/

/*!
 * @brief Constants for execute-in-RAM flash function.
 */
enum _ftfx_ram_func_constants
{
    kFTFx_RamFuncMaxSizeInWords = 16U, /*!< The maximum size of execute-in-RAM function.*/
};

/*! @brief A function pointer used to point to relocated flash_run_command() */
typedef void (*callFtfxRunCommand_t)(FTFx_REG8_ACCESS_TYPE ftfx_fstat);

/*!
 * @name Enumeration for Flash security register code
 * @{
 */
enum _ftfx_fsec_register_code
{
    kFTFx_FsecRegCode_KEYEN_Enabled = 0x80U,
    kFTFx_FsecRegCode_SEC_Unsecured = 0x02U
};
/*@}*/

/*!
 * @brief Enumeration for flash config area.
 */
enum _ftfx_pflash_config_area_range
{
    kFTFx_PflashConfigAreaStart = 0x400U,
    kFTFx_PflashConfigAreaEnd = 0x40FU
};


/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*! @brief Init IFR memory related info */
static status_t ftfx_init_ifr(ftfx_config_t *config);

#if FTFx_DRIVER_IS_FLASH_RESIDENT
/*! @brief Copy flash_run_command() to RAM*/
static void ftfx_copy_run_command_to_ram(uint32_t *ftfxRunCommand);
#endif /* FTFx_DRIVER_IS_FLASH_RESIDENT */

/*! @brief Internal function Flash command sequence. Called by driver APIs only*/
static status_t ftfx_command_sequence(ftfx_config_t *config);

/*! @brief Validates the range and alignment of the given address range.*/
static status_t ftfx_check_mem_range(ftfx_config_t *config,
                                     uint32_t startAddress,
                                     uint32_t lengthInBytes,
                                     uint8_t alignmentBaseline);

/*! @brief Validates the given user key for flash erase APIs.*/
static status_t ftfx_check_user_key(uint32_t key);

/*! @brief Reads word from byte address.*/
static uint32_t ftfx_read_word_from_byte_address(const uint8_t *src);

/*! @brief Writes word to byte address.*/
static void ftfx_write_word_to_byte_address(uint8_t *dst, uint32_t word);

#if defined(FSL_FEATURE_FLASH_HAS_READ_RESOURCE_CMD) && FSL_FEATURE_FLASH_HAS_READ_RESOURCE_CMD
/*! @brief Validates the range of the given resource address.*/
static status_t ftfx_check_resource_range(ftfx_config_t *config,
                                          uint32_t start,
                                          uint32_t lengthInBytes,
                                          uint32_t alignmentBaseline,
                                          ftfx_read_resource_opt_t option);
#endif /* FSL_FEATURE_FLASH_HAS_READ_RESOURCE_CMD */

#if defined(FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD) && FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD
/*! @brief Validates the gived flexram function option.*/
static inline status_t ftfx_check_flexram_function_option(ftfx_flexram_func_opt_t option);
#endif /* FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD */

#if defined(FSL_FEATURE_FLASH_HAS_SWAP_CONTROL_CMD) && FSL_FEATURE_FLASH_HAS_SWAP_CONTROL_CMD
/*! @brief Validates the gived swap control option.*/
static status_t ftfx_check_swap_control_option(ftfx_swap_control_opt_t option);
#endif /* FSL_FEATURE_FLASH_HAS_SWAP_CONTROL_CMD */

/*******************************************************************************
 * Variables
 ******************************************************************************/

#if FTFx_DRIVER_IS_FLASH_RESIDENT
/*!
 * @brief Position independent code of flash_run_command()
 *
 * Note1: The prototype of C function is shown as below:
 * @code
 *   void flash_run_command(FTFx_REG8_ACCESS_TYPE ftfx_fstat)
 *   {
 *       // clear CCIF bit
 *       *ftfx_fstat = FTFx_FSTAT_CCIF_MASK;
 *
 *       // Check CCIF bit of the flash status register, wait till it is set.
 *       // IP team indicates that this loop will always complete.
 *       while (!((*ftfx_fstat) & FTFx_FSTAT_CCIF_MASK))
 *       {
 *       }
 *   }
 * @endcode
 * Note2: The binary code is generated by IAR 7.70.1
 */
static const uint16_t s_ftfxRunCommandFunctionCode[] = {
    0x2180, /* MOVS  R1, #128 ; 0x80 */
    0x7001, /* STRB  R1, [R0] */
    /* @4: */
    0x7802, /* LDRB  R2, [R0] */
    0x420a, /* TST   R2, R1 */
    0xd0fc, /* BEQ.N @4 */
    0x4770  /* BX    LR */
};
#if (!FTFx_DRIVER_IS_EXPORTED)
/*! @brief A static buffer used to hold flash_run_command() */
static uint32_t s_ftfxRunCommand[kFTFx_RamFuncMaxSizeInWords];
#endif /* (!FTFx_DRIVER_IS_EXPORTED) */
#endif /* FTFx_DRIVER_IS_FLASH_RESIDENT */

/*! @brief Access to FTFx Registers */
static volatile uint32_t *const kFCCOBx = (volatile uint32_t *)&FTFx_FCCOB3_REG;

#if FSL_FEATURE_FLASH_HAS_FLEX_NVM
/*! @brief Table of eeprom sizes. */
static const uint16_t kEepromDensities[16] = {
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_0000,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_0001,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_0010,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_0011,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_0100,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_0101,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_0110,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_0111,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_1000,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_1001,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_1010,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_1011,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_1100,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_1101,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_1110,
    FSL_FEATURE_FLASH_FLEX_NVM_EEPROM_SIZE_FOR_EEESIZE_1111
};
/*! @brief Table of dflash sizes. */
static const uint32_t kDflashDensities[16] = {
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_0000,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_0001,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_0010,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_0011,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_0100,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_0101,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_0110,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_0111,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_1000,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_1001,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_1010,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_1011,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_1100,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_1101,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_1110,
    FSL_FEATURE_FLASH_FLEX_NVM_DFLASH_SIZE_FOR_DEPART_1111
};
#endif /* FSL_FEATURE_FLASH_HAS_FLEX_NVM */

/*******************************************************************************
 * Code
 ******************************************************************************/

status_t FTFx_API_Init(ftfx_config_t *config)
{
    if (config == NULL)
    {
        return kStatus_FTFx_InvalidArgument;
    }

    config->flexramBlockBase = FSL_FEATURE_FLASH_FLEX_RAM_START_ADDRESS;
    config->flexramTotalSize = FSL_FEATURE_FLASH_FLEX_RAM_SIZE;

    /* copy required flash command to RAM */
#if FTFx_DRIVER_IS_FLASH_RESIDENT
    if (NULL == config->runCmdFuncAddr)
    {
#if FTFx_DRIVER_IS_EXPORTED
        return kStatus_FTFx_ExecuteInRamFunctionNotReady;
#else
        config->runCmdFuncAddr = s_ftfxRunCommand;
#endif /* FTFx_DRIVER_IS_EXPORTED */
    }
    ftfx_copy_run_command_to_ram(config->runCmdFuncAddr);
#endif /* FTFx_DRIVER_IS_FLASH_RESIDENT */

    ftfx_init_ifr(config);

    return kStatus_FTFx_Success;
}

#if FSL_FEATURE_FLASH_HAS_FLEX_NVM
status_t FTFx_API_UpdateFlexnvmPartitionStatus(ftfx_config_t *config)
{
    struct _dflash_ifr_field_config
    {
        uint32_t reserved0;
        uint8_t FlexNVMPartitionCode;
        uint8_t EEPROMDataSetSize;
        uint16_t reserved1;
    } dataIFRReadOut;
    uint32_t flexnvmInfoIfrAddr;
    status_t returnCode;

    if (config == NULL)
    {
        return kStatus_FTFx_InvalidArgument;
    }

    flexnvmInfoIfrAddr = config->ifrDesc.resRange.dflashIfrStart + config->ifrDesc.resRange.ifrMemSize - sizeof(dataIFRReadOut);

#if defined(FSL_FEATURE_FLASH_HAS_READ_RESOURCE_CMD) && FSL_FEATURE_FLASH_HAS_READ_RESOURCE_CMD
    /* Get FlexNVM memory partition info from data flash IFR */
    returnCode = FTFx_CMD_ReadResource(config, flexnvmInfoIfrAddr, (uint8_t *)&dataIFRReadOut,
                                       sizeof(dataIFRReadOut), kFTFx_ResourceOptionFlashIfr);
    if (returnCode != kStatus_FTFx_Success)
    {
        return kStatus_FTFx_PartitionStatusUpdateFailure;
    }
#else
#error "Cannot get FlexNVM memory partition info"
#endif

    /* Fill out partitioned EEPROM size */
    dataIFRReadOut.EEPROMDataSetSize &= 0x0FU;
    config->eepromTotalSize = kEepromDensities[dataIFRReadOut.EEPROMDataSetSize];

    /* Fill out partitioned DFlash size */
    dataIFRReadOut.FlexNVMPartitionCode &= 0x0FU;
    config->flashDesc.totalSize = kDflashDensities[dataIFRReadOut.FlexNVMPartitionCode];

    return kStatus_FTFx_Success;
}
#endif /* FSL_FEATURE_FLASH_HAS_FLEX_NVM */

status_t FTFx_CMD_Erase(ftfx_config_t *config,
                        uint32_t start,
                        uint32_t lengthInBytes,
                        uint32_t key)
{
    uint32_t sectorSize;
    uint32_t endAddress;      /* storing end address */
    uint32_t numberOfSectors; /* number of sectors calculated by endAddress */
    status_t returnCode;

    /* Check the supplied address range. */
    returnCode = ftfx_check_mem_range(config, start, lengthInBytes, config->opsConfig.addrAligment.sectorCmd);
    if (returnCode)
    {
        return returnCode;
    }

    /* Validate the user key */
    returnCode = ftfx_check_user_key(key);
    if (returnCode)
    {
        return returnCode;
    }

    start = config->opsConfig.convertedAddress;
    sectorSize = config->flashDesc.sectorSize;

    /* calculating Flash end address */
    endAddress = start + lengthInBytes - 1;

    /* re-calculate the endAddress and align it to the start of the next sector
     * which will be used in the comparison below */
    if (endAddress % sectorSize)
    {
        numberOfSectors = endAddress / sectorSize + 1;
        endAddress = numberOfSectors * sectorSize - 1;
    }

    /* the start address will increment to the next sector address
     * until it reaches the endAdddress */
    while (start <= endAddress)
    {
        /* preparing passing parameter to erase a flash block */
        kFCCOBx[0] = BYTE2WORD_1_3(FTFx_ERASE_SECTOR, start);

        /* calling flash command sequence function to execute the command */
        returnCode = ftfx_command_sequence(config);

        /* checking the success of command execution */
        if (kStatus_FTFx_Success != returnCode)
        {
            break;
        }
        else
        {
            /* Increment to the next sector */
            start += sectorSize;
        }
    }

    return (returnCode);
}

status_t FTFx_CMD_EraseAll(ftfx_config_t *config, uint32_t key)
{
    status_t returnCode;

    if (config == NULL)
    {
        return kStatus_FTFx_InvalidArgument;
    }

    /* preparing passing parameter to erase all flash blocks */
    kFCCOBx[0] = BYTE2WORD_1_3(FTFx_ERASE_ALL_BLOCK, 0xFFFFFFU);

    /* Validate the user key */
    returnCode = ftfx_check_user_key(key);
    if (returnCode)
    {
        return returnCode;
    }

    /* calling flash command sequence function to execute the command */
    returnCode = ftfx_command_sequence(config);

#if FSL_FEATURE_FLASH_HAS_FLEX_NVM
    /* Data flash IFR will be erased by erase all command, so we need to
     *  update FlexNVM memory partition status synchronously */
    if (returnCode == kStatus_FTFx_Success)
    {
        returnCode = FTFx_API_UpdateFlexnvmPartitionStatus(config);
    }
#endif

    return returnCode;
}

#if defined(FSL_FEATURE_FLASH_HAS_ERASE_ALL_BLOCKS_UNSECURE_CMD) && FSL_FEATURE_FLASH_HAS_ERASE_ALL_BLOCKS_UNSECURE_CMD
status_t FTFx_CMD_EraseAllUnsecure(ftfx_config_t *config, uint32_t key)
{
    status_t returnCode;

    if (config == NULL)
    {
        return kStatus_FTFx_InvalidArgument;
    }

    /* Prepare passing parameter to erase all flash blocks (unsecure). */
    kFCCOBx[0] = BYTE2WORD_1_3(FTFx_ERASE_ALL_BLOCK_UNSECURE, 0xFFFFFFU);

    /* Validate the user key */
    returnCode = ftfx_check_user_key(key);
    if (returnCode)
    {
        return returnCode;
    }

    /* calling flash command sequence function to execute the command */
    returnCode = ftfx_command_sequence(config);

#if FSL_FEATURE_FLASH_HAS_FLEX_NVM
    /* Data flash IFR will be erased by erase all unsecure command, so we need to
     *  update FlexNVM memory partition status synchronously */
    if (returnCode == kStatus_FTFx_Success)
    {
        returnCode = FTFx_API_UpdateFlexnvmPartitionStatus(config);
    }
#endif

    return returnCode;
}
#endif /* FSL_FEATURE_FLASH_HAS_ERASE_ALL_BLOCKS_UNSECURE_CMD */

status_t FTFx_CMD_EraseAllExecuteOnlySegments(ftfx_config_t *config, uint32_t key)
{
    status_t returnCode;

    if (config == NULL)
    {
        return kStatus_FTFx_InvalidArgument;
    }

    /* preparing passing parameter to erase all execute-only segments
     * 1st element for the FCCOB register */
    kFCCOBx[0] = BYTE2WORD_1_3(FTFx_ERASE_ALL_EXECUTE_ONLY_SEGMENT, 0xFFFFFFU);

    /* Validate the user key */
    returnCode = ftfx_check_user_key(key);
    if (returnCode)
    {
        return returnCode;
    }

    /* calling flash command sequence function to execute the command */
    returnCode = ftfx_command_sequence(config);

    return returnCode;
}

status_t FTFx_CMD_Program(ftfx_config_t *config,
                          uint32_t start,
                          uint8_t *src,
                          uint32_t lengthInBytes)
{
    status_t returnCode;
    uint8_t blockWriteUnitSize = config->opsConfig.addrAligment.blockWriteUnitSize;

    if (src == NULL)
    {
        return kStatus_FTFx_InvalidArgument;
    }

    /* Check the supplied address range. */
    returnCode = ftfx_check_mem_range(config, start, lengthInBytes, blockWriteUnitSize);
    if (returnCode)
    {
        return returnCode;
    }

    start = config->opsConfig.convertedAddress;

    while (lengthInBytes > 0)
    {
        /* preparing passing parameter to program the flash block */
        kFCCOBx[1] = ftfx_read_word_from_byte_address((const uint8_t*)src);
        src += 4;

        if (4 == blockWriteUnitSize)
        {
            kFCCOBx[0] = BYTE2WORD_1_3(FTFx_PROGRAM_LONGWORD, start);
        }
        else if (8 == blockWriteUnitSize)
        {
            kFCCOBx[2] = ftfx_read_word_from_byte_address((const uint8_t*)src);
            src += 4;
            kFCCOBx[0] = BYTE2WORD_1_3(FTFx_PROGRAM_PHRASE, start);
        }
        else
        {
        }

        /* calling flash command sequence function to execute the command */
        returnCode = ftfx_command_sequence(config);

        /* checking for the success of command execution */
        if (kStatus_FTFx_Success != returnCode)
        {
            break;
        }
        else
        {
            /* update start address for next iteration */
            start += blockWriteUnitSize;

            /* update lengthInBytes for next iteration */
            lengthInBytes -= blockWriteUnitSize;
        }
    }

    return (returnCode);
}

status_t FTFx_CMD_ProgramOnce(ftfx_config_t *config, uint32_t index, uint8_t *src, uint32_t lengthInBytes)
{
    status_t returnCode;

    if ((config == NULL) || (src == NULL))
    {
        return kStatus_FTFx_InvalidArgument;
    }

    /* pass paramters to FTFx */
    kFCCOBx[0] = BYTE2WORD_1_1_2(FTFx_PROGRAM_ONCE, index, 0xFFFFU);

    kFCCOBx[1] = ftfx_read_word_from_byte_address((const uint8_t*)src);

    /* Note: Have to seperate the first index from the rest if it equals 0
     * to avoid a pointless comparison of unsigned int to 0 compiler warning */
    if (config->ifrDesc.feature.has8ByteIdxSupport)
    {
        if (config->ifrDesc.feature.has4ByteIdxSupport)
        {
            if (((index == config->ifrDesc.idxInfo.mix8byteIdxStart) ||
                 ((index >= config->ifrDesc.idxInfo.mix8byteIdxStart + 1) && (index <= config->ifrDesc.idxInfo.mix8byteIdxStart))) &&
                  (lengthInBytes == 8))
            {
                kFCCOBx[2] = ftfx_read_word_from_byte_address((const uint8_t*)src + 4);
            }
        }
        else
        {
            kFCCOBx[2] = ftfx_read_word_from_byte_address((const uint8_t*)src + 4);
        }
    }

    /* calling flash command sequence function to execute the command */
    returnCode = ftfx_command_sequence(config);

    return returnCode;
}

#if defined(FSL_FEATURE_FLASH_HAS_PROGRAM_SECTION_CMD) && FSL_FEATURE_FLASH_HAS_PROGRAM_SECTION_CMD
status_t FTFx_CMD_ProgramSection(ftfx_config_t *config,
                                 uint32_t start,
                                 uint8_t *src,
                                 uint32_t lengthInBytes)
{
    status_t returnCode;
    uint32_t sectorSize;
    uint8_t aligmentInBytes = config->opsConfig.addrAligment.sectionCmd;
#if defined(FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD) && FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD
    bool needSwitchFlexRamMode = false;
#endif /* FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD */

    if (src == NULL)
    {
        return kStatus_FTFx_InvalidArgument;
    }

    /* Check the supplied address range. */
    returnCode = ftfx_check_mem_range(config, start, lengthInBytes, aligmentInBytes);
    if (returnCode)
    {
        return returnCode;
    }

    start = config->opsConfig.convertedAddress;
    sectorSize = config->flashDesc.sectorSize;

#if defined(FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD) && FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD
    /* Switch function of FlexRAM if needed */
    if (!(FTFx->FCNFG & FTFx_FCNFG_RAMRDY_MASK))
    {
        needSwitchFlexRamMode = true;

        returnCode = FTFx_CMD_SetFlexramFunction(config, kFTFx_FlexramFuncOptAvailableAsRam);
        if (returnCode != kStatus_FTFx_Success)
        {
            return kStatus_FTFx_SetFlexramAsRamError;
        }
    }
#endif /* FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD */

    while (lengthInBytes > 0)
    {
        /* Make sure the write operation doesn't span two sectors */
        uint32_t endAddressOfCurrentSector = ALIGN_UP(start, sectorSize);
        uint32_t lengthTobeProgrammedOfCurrentSector;
        uint32_t currentOffset = 0;

        if (endAddressOfCurrentSector == start)
        {
            endAddressOfCurrentSector += sectorSize;
        }

        if (lengthInBytes + start > endAddressOfCurrentSector)
        {
            lengthTobeProgrammedOfCurrentSector = endAddressOfCurrentSector - start;
        }
        else
        {
            lengthTobeProgrammedOfCurrentSector = lengthInBytes;
        }

        /* Program Current Sector */
        while (lengthTobeProgrammedOfCurrentSector > 0)
        {
            /* Make sure the program size doesn't exceeds Acceleration RAM size */
            uint32_t programSizeOfCurrentPass;
            uint32_t numberOfPhases;

            if (lengthTobeProgrammedOfCurrentSector > config->flexramTotalSize)
            {
                programSizeOfCurrentPass = config->flexramTotalSize;
            }
            else
            {
                programSizeOfCurrentPass = lengthTobeProgrammedOfCurrentSector;
            }

            /* Copy data to FlexRAM */
            memcpy((void *)config->flexramBlockBase, src + currentOffset, programSizeOfCurrentPass);
            /* Set start address of the data to be programmed */
            kFCCOBx[0] = BYTE2WORD_1_3(FTFx_PROGRAM_SECTION, start + currentOffset);
            /* Set program size in terms of FEATURE_FLASH_SECTION_CMD_ADDRESS_ALIGMENT */
            numberOfPhases = programSizeOfCurrentPass / aligmentInBytes;

            kFCCOBx[1] = BYTE2WORD_2_2(numberOfPhases, 0xFFFFU);

            /* Peform command sequence */
            returnCode = ftfx_command_sequence(config);

            if (returnCode != kStatus_FTFx_Success)
            {
                return returnCode;
            }

            lengthTobeProgrammedOfCurrentSector -= programSizeOfCurrentPass;
            currentOffset += programSizeOfCurrentPass;
        }

        src += currentOffset;
        start += currentOffset;
        lengthInBytes -= currentOffset;
    }

#if defined(FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD) && FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD
    /* Restore function of FlexRAM if needed. */
    if (needSwitchFlexRamMode)
    {
        returnCode = FTFx_CMD_SetFlexramFunction(config, kFTFx_FlexramFuncOptAvailableForEeprom);
        if (returnCode != kStatus_FTFx_Success)
        {
            return kStatus_FTFx_RecoverFlexramAsEepromError;
        }
    }
#endif /* FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD */

    return returnCode;
}
#endif /* FSL_FEATURE_FLASH_HAS_PROGRAM_SECTION_CMD */

#if defined(FSL_FEATURE_FLASH_HAS_PROGRAM_PARTITION_CMD) && FSL_FEATURE_FLASH_HAS_PROGRAM_PARTITION_CMD
status_t FTFx_CMD_ProgramPartition(ftfx_config_t *config,
                                   ftfx_partition_flexram_load_opt_t option,
                                   uint32_t eepromDataSizeCode,
                                   uint32_t flexnvmPartitionCode)
{
    status_t returnCode;

    if (config == NULL)
    {
        return kStatus_FTFx_InvalidArgument;
    }

    /* eepromDataSizeCode[7:6], flexnvmPartitionCode[7:4] should be all 1'b0
     *  or it will cause access error. */
    /* eepromDataSizeCode &= 0x3FU;  */
    /* flexnvmPartitionCode &= 0x0FU; */

    /* preparing passing parameter to program the flash block */
    kFCCOBx[0] = BYTE2WORD_1_2_1(FTFx_PROGRAM_PARTITION, 0xFFFFU, option);
    kFCCOBx[1] = BYTE2WORD_1_1_2(eepromDataSizeCode, flexnvmPartitionCode, 0xFFFFU);

    /* calling flash command sequence function to execute the command */
    returnCode = ftfx_command_sequence(config);

#if FSL_FEATURE_FLASH_HAS_FLEX_NVM
    /* Data flash IFR will be updated by program partition command during reset sequence,
     * so we just set reserved values for partitioned FlexNVM size here */
    config->eepromTotalSize = 0xFFFFU;
    config->flashDesc.totalSize = 0xFFFFFFFFU;
#endif

    return (returnCode);
}
#endif /* FSL_FEATURE_FLASH_HAS_PROGRAM_PARTITION_CMD */

status_t FTFx_CMD_ReadOnce(ftfx_config_t *config, uint32_t index, uint8_t *dst, uint32_t lengthInBytes)
{
    status_t returnCode;

    if ((config == NULL) || (dst == NULL))
    {
        return kStatus_FTFx_InvalidArgument;
    }

    /* pass paramters to FTFx */
    kFCCOBx[0] = BYTE2WORD_1_1_2(FTFx_READ_ONCE, index, 0xFFFFU);

    /* calling flash command sequence function to execute the command */
    returnCode = ftfx_command_sequence(config);

    if (kStatus_FTFx_Success == returnCode)
    {
        ftfx_write_word_to_byte_address(dst, kFCCOBx[1]);
        /* Note: Have to seperate the first index from the rest if it equals 0
         *       to avoid a pointless comparison of unsigned int to 0 compiler warning */
        if (config->ifrDesc.feature.has8ByteIdxSupport)
        {
            if (config->ifrDesc.feature.has4ByteIdxSupport)
            {
                if (((index == config->ifrDesc.idxInfo.mix8byteIdxStart) ||
                     ((index >= config->ifrDesc.idxInfo.mix8byteIdxStart + 1) && (index <= config->ifrDesc.idxInfo.mix8byteIdxStart))) &&
                      (lengthInBytes == 8))
                {
                    ftfx_write_word_to_byte_address(dst + 4, kFCCOBx[2]);
                }
            }
            else
            {
                ftfx_write_word_to_byte_address(dst + 4, kFCCOBx[2]);
            }
        }
    }

    return returnCode;
}

#if defined(FSL_FEATURE_FLASH_HAS_READ_RESOURCE_CMD) && FSL_FEATURE_FLASH_HAS_READ_RESOURCE_CMD
status_t FTFx_CMD_ReadResource(ftfx_config_t *config,
                               uint32_t start,
                               uint8_t *dst,
                               uint32_t lengthInBytes,
                               ftfx_read_resource_opt_t option)
{
    status_t returnCode;

    if ((config == NULL) || (dst == NULL))
    {
        return kStatus_FTFx_InvalidArgument;
    }

    uint8_t aligmentInBytes = config->opsConfig.addrAligment.resourceCmd;

    /* Check the supplied address range. */
    returnCode = ftfx_check_resource_range(config, start, lengthInBytes, aligmentInBytes, option);
    if (returnCode != kStatus_FTFx_Success)
    {
        return returnCode;
    }

    while (lengthInBytes > 0)
    {
        /* preparing passing parameter */
        kFCCOBx[0] = BYTE2WORD_1_3(FTFx_READ_RESOURCE, start);
        if (aligmentInBytes == 4)
        {
            kFCCOBx[2] = BYTE2WORD_1_3(option, 0xFFFFFFU);
        }
        else if (aligmentInBytes == 8)
        {
            kFCCOBx[1] = BYTE2WORD_1_3(option, 0xFFFFFFU);
        }
        else
        {
        }

        /* calling flash command sequence function to execute the command */
        returnCode = ftfx_command_sequence(config);

        if (kStatus_FTFx_Success != returnCode)
        {
            break;
        }

        /* fetch data */
        ftfx_write_word_to_byte_address(dst, kFCCOBx[1]);
        dst += 4;
        if (aligmentInBytes == 8)
        {
            ftfx_write_word_to_byte_address(dst, kFCCOBx[2]);
            dst += 4;
        }
        /* update start address for next iteration */
        start += aligmentInBytes;
        /* update lengthInBytes for next iteration */
        lengthInBytes -= aligmentInBytes;
    }

    return (returnCode);
}
#endif /* FSL_FEATURE_FLASH_HAS_READ_RESOURCE_CMD */

status_t FTFx_CMD_VerifyErase(ftfx_config_t *config,
                              uint32_t start,
                              uint32_t lengthInBytes,
                              ftfx_margin_value_t margin)
{
    /* Check arguments. */
    uint32_t blockSize;
    uint32_t nextBlockStartAddress;
    uint32_t remainingBytes;
    uint8_t aligmentInBytes = config->opsConfig.addrAligment.sectionCmd;
    status_t returnCode;

    returnCode = ftfx_check_mem_range(config, start, lengthInBytes, aligmentInBytes);
    if (returnCode)
    {
        return returnCode;
    }

    start = config->opsConfig.convertedAddress;
    blockSize = config->flashDesc.totalSize / config->flashDesc.blockCount;

    nextBlockStartAddress = ALIGN_UP(start, blockSize);
    if (nextBlockStartAddress == start)
    {
        nextBlockStartAddress += blockSize;
    }

    remainingBytes = lengthInBytes;

    while (remainingBytes)
    {
        uint32_t numberOfPhrases;
        uint32_t verifyLength = nextBlockStartAddress - start;
        if (verifyLength > remainingBytes)
        {
            verifyLength = remainingBytes;
        }

        numberOfPhrases = verifyLength / aligmentInBytes;

        /* Fill in verify section command parameters. */
        kFCCOBx[0] = BYTE2WORD_1_3(FTFx_VERIFY_SECTION, start);
        kFCCOBx[1] = BYTE2WORD_2_1_1(numberOfPhrases, margin, 0xFFU);

        /* calling flash command sequence function to execute the command */
        returnCode = ftfx_command_sequence(config);
        if (returnCode)
        {
            return returnCode;
        }

        remainingBytes -= verifyLength;
        start += verifyLength;
        nextBlockStartAddress += blockSize;
    }

    return kStatus_FTFx_Success;
}

status_t FTFx_CMD_VerifyEraseAll(ftfx_config_t *config, ftfx_margin_value_t margin)
{
    if (config == NULL)
    {
        return kStatus_FTFx_InvalidArgument;
    }

    /* preparing passing parameter to verify all block command */
    kFCCOBx[0] = BYTE2WORD_1_1_2(FTFx_VERIFY_ALL_BLOCK, margin, 0xFFFFU);

    /* calling flash command sequence function to execute the command */
    return ftfx_command_sequence(config);
}

status_t FTFx_CMD_VerifyEraseAllExecuteOnlySegments(ftfx_config_t *config, ftfx_margin_value_t margin)
{
    if (config == NULL)
    {
        return kStatus_FTFx_InvalidArgument;
    }

    /* preparing passing parameter to verify erase all execute-only segments command */
    kFCCOBx[0] = BYTE2WORD_1_1_2(FTFx_VERIFY_ALL_EXECUTE_ONLY_SEGMENT, margin, 0xFFFFU);

    /* calling flash command sequence function to execute the command */
    return ftfx_command_sequence(config);
}

status_t FTFx_CMD_VerifyProgram(ftfx_config_t *config,
                                uint32_t start,
                                uint32_t lengthInBytes,
                                const uint8_t *expectedData,
                                ftfx_margin_value_t margin,
                                uint32_t *failedAddress,
                                uint32_t *failedData)
{
    status_t returnCode;
    uint8_t aligmentInBytes = config->opsConfig.addrAligment.checkCmd;
    if (expectedData == NULL)
    {
        return kStatus_FTFx_InvalidArgument;
    }

    returnCode = ftfx_check_mem_range(config, start, lengthInBytes, aligmentInBytes);
    if (returnCode)
    {
        return returnCode;
    }

    start = config->opsConfig.convertedAddress;

    while (lengthInBytes)
    {
        /* preparing passing parameter to program check the flash block */
        kFCCOBx[0] = BYTE2WORD_1_3(FTFx_PROGRAM_CHECK, start);
        kFCCOBx[1] = BYTE2WORD_1_3(margin, 0xFFFFFFU);
        kFCCOBx[2] = ftfx_read_word_from_byte_address((const uint8_t*)expectedData);

        /* calling flash command sequence function to execute the command */
        returnCode = ftfx_command_sequence(config);

        /* checking for the success of command execution */
        if (kStatus_FTFx_Success != returnCode)
        {
            if (failedAddress)
            {
                *failedAddress = start;
            }
            if (failedData)
            {
                *failedData = 0;
            }
            break;
        }

        lengthInBytes -= aligmentInBytes;
        expectedData += aligmentInBytes;
        start += aligmentInBytes;
    }

    return (returnCode);
}

status_t FTFx_CMD_SecurityBypass(ftfx_config_t *config, const uint8_t *backdoorKey)
{
    uint8_t registerValue; /* registerValue */
    status_t returnCode;   /* return code variable */

    if ((config == NULL) || (backdoorKey == NULL))
    {
        return kStatus_FTFx_InvalidArgument;
    }

    /* set the default return code as kStatus_Success */
    returnCode = kStatus_FTFx_Success;

    /* Get flash security register value */
    registerValue = FTFx->FSEC;

    /* Check to see if flash is in secure state (any state other than 0x2)
     * If not, then skip this since flash is not secure */
    if (0x02 != (registerValue & 0x03))
    {
        /* preparing passing parameter to erase a flash block */
        kFCCOBx[0] = BYTE2WORD_1_3(FTFx_SECURITY_BY_PASS, 0xFFFFFFU);
        kFCCOBx[1] = BYTE2WORD_1_1_1_1(backdoorKey[0], backdoorKey[1], backdoorKey[2], backdoorKey[3]);
        kFCCOBx[2] = BYTE2WORD_1_1_1_1(backdoorKey[4], backdoorKey[5], backdoorKey[6], backdoorKey[7]);

        /* calling flash command sequence function to execute the command */
        returnCode = ftfx_command_sequence(config);
    }

    return (returnCode);
}

status_t FTFx_REG_GetSecurityState(ftfx_config_t *config, ftfx_security_state_t *state)
{
    /* store data read from flash register */
    uint8_t registerValue;

    if ((config == NULL) || (state == NULL))
    {
        return kStatus_FTFx_InvalidArgument;
    }

    /* Get flash security register value */
    registerValue = FTFx->FSEC;

    /* check the status of the flash security bits in the security register */
    if (kFTFx_FsecRegCode_SEC_Unsecured == (registerValue & FTFx_FSEC_SEC_MASK))
    {
        /* Flash in unsecured state */
        *state = kFTFx_SecurityStateNotSecure;
    }
    else
    {
        /* Flash in secured state
         * check for backdoor key security enable bit */
        if (kFTFx_FsecRegCode_KEYEN_Enabled == (registerValue & FTFx_FSEC_KEYEN_MASK))
        {
            /* Backdoor key security enabled */
            *state = kFTFx_SecurityStateBackdoorEnabled;
        }
        else
        {
            /* Backdoor key security disabled */
            *state = kFTFx_SecurityStateBackdoorDisabled;
        }
    }

    return (kStatus_FTFx_Success);
}

#if defined(FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD) && FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD
 status_t FTFx_CMD_SetFlexramFunction(ftfx_config_t *config, ftfx_flexram_func_opt_t option)
 {
     status_t status;

     if (config == NULL)
     {
         return kStatus_FTFx_InvalidArgument;
     }

     status = ftfx_check_flexram_function_option(option);
     if (status != kStatus_FTFx_Success)
     {
         return status;
     }

     /* preparing passing parameter to verify all block command */
     kFCCOBx[0] = BYTE2WORD_1_1_2(FTFx_SET_FLEXRAM_FUNCTION, option, 0xFFFFU);

     /* calling flash command sequence function to execute the command */
     return ftfx_command_sequence(config);
 }
#endif /* FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD */

#if defined(FSL_FEATURE_FLASH_HAS_SWAP_CONTROL_CMD) && FSL_FEATURE_FLASH_HAS_SWAP_CONTROL_CMD
status_t FTFx_CMD_SwapControl(ftfx_config_t *config,
                              uint32_t address,
                              ftfx_swap_control_opt_t option,
                              ftfx_swap_state_config_t *returnInfo)
{
    status_t returnCode;

    if ((config == NULL) || (returnInfo == NULL))
    {
        return kStatus_FTFx_InvalidArgument;
    }

    if (address & (FSL_FEATURE_FLASH_PFLASH_SWAP_CONTROL_CMD_ADDRESS_ALIGMENT - 1))
    {
        return kStatus_FTFx_AlignmentError;
    }

    /* Make sure address provided is in the lower half of Program flash but not in the Flash Configuration Field */
    if ((address >= (config->flashDesc.totalSize / 2)) ||
        ((address >= kFTFx_PflashConfigAreaStart) && (address <= kFTFx_PflashConfigAreaEnd)))
    {
        return kStatus_FTFx_SwapIndicatorAddressError;
    }

    /* Check the option. */
    returnCode = ftfx_check_swap_control_option(option);
    if (returnCode)
    {
        return returnCode;
    }

    kFCCOBx[0] = BYTE2WORD_1_3(FTFx_SWAP_CONTROL, address);
    kFCCOBx[1] = BYTE2WORD_1_3(option, 0xFFFFFFU);

    returnCode = ftfx_command_sequence(config);

    returnInfo->flashSwapState = (ftfx_swap_state_t)FTFx_FCCOB5_REG;
    returnInfo->currentSwapBlockStatus = (ftfx_swap_block_status_t)FTFx_FCCOB6_REG;
    returnInfo->nextSwapBlockStatus = (ftfx_swap_block_status_t)FTFx_FCCOB7_REG;

    return returnCode;
}
#endif /* FSL_FEATURE_FLASH_HAS_SWAP_CONTROL_CMD */

static status_t ftfx_init_ifr(ftfx_config_t *config)
{
    if (config == NULL)
    {
        return kStatus_FTFx_InvalidArgument;
    }

#if FSL_FEATURE_FLASH_IS_FTFA
    /* FTFA parts(eg. K80, KL80, L5K) support both 4-bytes and 8-bytes unit size */
    config->ifrDesc.feature.has4ByteIdxSupport = 1;
    config->ifrDesc.feature.has8ByteIdxSupport = 1;
    config->ifrDesc.idxInfo.mix8byteIdxStart = 0x10U;
    config->ifrDesc.idxInfo.mix8byteIdxEnd = 0x13U;
#elif FSL_FEATURE_FLASH_IS_FTFE
    /* FTFE parts(eg. K65, KE18) only support 8-bytes unit size */
    config->ifrDesc.feature.has4ByteIdxSupport = 0;
    config->ifrDesc.feature.has8ByteIdxSupport = 1;
#elif FSL_FEATURE_FLASH_IS_FTFL
    /* FTFL parts(eg. K20) only support 4-bytes unit size */
    config->ifrDesc.feature.has4ByteIdxSupport = 1;
    config->ifrDesc.feature.has8ByteIdxSupport = 0;
#endif

    config->ifrDesc.resRange.pflashIfrStart = 0x0000U;
    config->ifrDesc.resRange.versionIdSize = 0x08U;
#if FSL_FEATURE_FLASH_IS_FTFE
    config->ifrDesc.resRange.versionIdStart = 0x08U;
    config->ifrDesc.resRange.ifrMemSize = 0x0400U;
#else /* FSL_FEATURE_FLASH_IS_FTFL == 1 or FSL_FEATURE_FLASH_IS_FTFA = =1 */
    config->ifrDesc.resRange.versionIdStart = 0x00U;
    config->ifrDesc.resRange.ifrMemSize = 0x0100U;
#endif

#if FSL_FEATURE_FLASH_HAS_FLEX_NVM
    config->ifrDesc.resRange.dflashIfrStart = 0x800000U;
#endif

#if FSL_FEATURE_FLASH_HAS_SWAP_CONTROL_CMD
#if FSL_FEATURE_FLASH_IS_FTFE
    config->ifrDesc.resRange.pflashSwapIfrStart = 0x40000U;
#else /* FSL_FEATURE_FLASH_IS_FTFL == 1 or FSL_FEATURE_FLASH_IS_FTFA == 1 */
    config->ifrDesc.resRange.pflashSwapIfrStart = config->flashDesc.totalSize / 4;
#endif
#endif /* FSL_FEATURE_FLASH_HAS_SWAP_CONTROL_CMD */

    return kStatus_FTFx_Success;
}

#if FTFx_DRIVER_IS_FLASH_RESIDENT
/*!
 * @brief Copy PIC of flash_run_command() to RAM
 */
static void ftfx_copy_run_command_to_ram(uint32_t *ftfxRunCommand)
{
    assert(sizeof(s_ftfxRunCommandFunctionCode) <= (kFTFx_RamFuncMaxSizeInWords * 4));

    /* Since the value of ARM function pointer is always odd, but the real start address
     * of function memory should be even, that's why +1 operation exist. */
    memcpy((uint8_t *)ftfxRunCommand, (const uint8_t *)s_ftfxRunCommandFunctionCode, sizeof(s_ftfxRunCommandFunctionCode));
}
#endif /* FTFx_DRIVER_IS_FLASH_RESIDENT */

/*!
 * @brief FTFx Command Sequence
 *
 * This function is used to perform the command write sequence to the flash.
 *
 * @param driver Pointer to storage for the driver runtime state.
 * @return An error code or kStatus_FTFx_Success
 */
static status_t ftfx_command_sequence(ftfx_config_t *config)
{
    uint8_t registerValue;

#if FTFx_DRIVER_IS_FLASH_RESIDENT
    /* clear RDCOLERR & ACCERR & FPVIOL flag in flash status register */
    FTFx->FSTAT = FTFx_FSTAT_RDCOLERR_MASK | FTFx_FSTAT_ACCERR_MASK | FTFx_FSTAT_FPVIOL_MASK;

    /* Since the value of ARM function pointer is always odd, but the real start address
     * of function memory should be even, that's why +1 operation exist. */
    callFtfxRunCommand_t callFtfxRunCommand = (callFtfxRunCommand_t)((uint32_t)config->runCmdFuncAddr + 1);

    /* We pass the ftfx_fstat address as a parameter to flash_run_comamnd() instead of using
     * pre-processed MICRO sentences or operating global variable in flash_run_comamnd()
     * to make sure that flash_run_command() will be compiled into position-independent code (PIC). */
    callFtfxRunCommand((FTFx_REG8_ACCESS_TYPE)(&FTFx->FSTAT));
#else
    /* clear RDCOLERR & ACCERR & FPVIOL flag in flash status register */
    FTFx->FSTAT = FTFx_FSTAT_RDCOLERR_MASK | FTFx_FSTAT_ACCERR_MASK | FTFx_FSTAT_FPVIOL_MASK;

    /* clear CCIF bit */
    FTFx->FSTAT = FTFx_FSTAT_CCIF_MASK;

    /* Check CCIF bit of the flash status register, wait till it is set.
     * IP team indicates that this loop will always complete. */
    while (!(FTFx->FSTAT & FTFx_FSTAT_CCIF_MASK))
    {
    }
#endif /* FTFx_DRIVER_IS_FLASH_RESIDENT */

    /* Check error bits */
    /* Get flash status register value */
    registerValue = FTFx->FSTAT;

    /* checking access error */
    if (registerValue & FTFx_FSTAT_ACCERR_MASK)
    {
        return kStatus_FTFx_AccessError;
    }
    /* checking protection error */
    else if (registerValue & FTFx_FSTAT_FPVIOL_MASK)
    {
        return kStatus_FTFx_ProtectionViolation;
    }
    /* checking MGSTAT0 non-correctable error */
    else if (registerValue & FTFx_FSTAT_MGSTAT0_MASK)
    {
        return kStatus_FTFx_CommandFailure;
    }
    else
    {
        return kStatus_FTFx_Success;
    }
}

/*! @brief Validates the range and alignment of the given address range.*/
static status_t ftfx_check_mem_range(ftfx_config_t *config,
                                     uint32_t startAddress,
                                     uint32_t lengthInBytes,
                                     uint8_t alignmentBaseline)
{
    if (config == NULL)
    {
        return kStatus_FTFx_InvalidArgument;
    }

    /* Verify the start and length are alignmentBaseline aligned. */
    if ((startAddress & (alignmentBaseline - 1)) || (lengthInBytes & (alignmentBaseline - 1)))
    {
        return kStatus_FTFx_AlignmentError;
    }

    /* check for valid range of the target addresses */
    if ((startAddress >= config->flashDesc.blockBase) &&
        ((startAddress + lengthInBytes) <= (config->flashDesc.blockBase + config->flashDesc.totalSize)))
    {
        return kStatus_FTFx_Success;
    }

    return kStatus_FTFx_AddressError;
}

/*! @brief Validates the given user key for flash erase APIs.*/
static status_t ftfx_check_user_key(uint32_t key)
{
    /* Validate the user key */
    if (key != kFTFx_ApiEraseKey)
    {
        return kStatus_FTFx_EraseKeyError;
    }

    return kStatus_FTFx_Success;
}

/*! @brief Reads word from byte address.*/
static uint32_t ftfx_read_word_from_byte_address(const uint8_t *src)
{
    uint32_t word = 0;

    if (!((uint32_t)src % 4))
    {
        word = *(const uint32_t *)src;
    }
    else
    {
        for (uint32_t i = 0; i < 4; i++)
        {
            word |= (uint32_t)(*src) << (i * 8);
            src++;
        }
    }

   return word;
}

/*! @brief Writes word to byte address.*/
static void ftfx_write_word_to_byte_address(uint8_t *dst, uint32_t word)
{
    if (!((uint32_t)dst % 4))
    {
        *(uint32_t *)dst = word;
    }
    else
    {
        for (uint32_t i = 0; i < 4; i++)
        {
            *dst = (uint8_t)((word >> (i * 8)) & 0xFFU);
            dst++;
        }
    }
}

#if defined(FSL_FEATURE_FLASH_HAS_READ_RESOURCE_CMD) && FSL_FEATURE_FLASH_HAS_READ_RESOURCE_CMD
/*! @brief Validates the range of the given resource address.*/
static status_t ftfx_check_resource_range(ftfx_config_t *config,
                                          uint32_t start,
                                          uint32_t lengthInBytes,
                                          uint32_t alignmentBaseline,
                                          ftfx_read_resource_opt_t option)
{
    status_t status;
    uint32_t maxReadbleAddress;

    if ((start & (alignmentBaseline - 1)) || (lengthInBytes & (alignmentBaseline - 1)))
    {
        return kStatus_FTFx_AlignmentError;
    }

    status = kStatus_FTFx_Success;

    maxReadbleAddress = start + lengthInBytes - 1;
    if (option == kFTFx_ResourceOptionVersionId)
    {
        if ((start != config->ifrDesc.resRange.versionIdStart) ||
            (lengthInBytes != config->ifrDesc.resRange.versionIdSize))
        {
            status = kStatus_FTFx_InvalidArgument;
        }
    }
    else if (option == kFTFx_ResourceOptionFlashIfr)
    {
        if ((start >= config->ifrDesc.resRange.pflashIfrStart) &&
            (maxReadbleAddress < (config->ifrDesc.resRange.pflashIfrStart + config->ifrDesc.resRange.ifrMemSize)))
        {
        }
#if FSL_FEATURE_FLASH_HAS_FLEX_NVM
        else if ((start >= config->ifrDesc.resRange.dflashIfrStart) &&
                 (maxReadbleAddress < (config->ifrDesc.resRange.dflashIfrStart + config->ifrDesc.resRange.ifrMemSize)))
        {
        }
#endif
#if FSL_FEATURE_FLASH_HAS_SWAP_CONTROL_CMD
        else if ((start >= config->ifrDesc.resRange.pflashSwapIfrStart) &&
                 (maxReadbleAddress < (config->ifrDesc.resRange.pflashSwapIfrStart + config->ifrDesc.resRange.ifrMemSize)))
        {
        }
#endif
        else
        {
            status = kStatus_FTFx_InvalidArgument;
        }
    }
    else
    {
        status = kStatus_FTFx_InvalidArgument;
    }

    return status;
}
#endif /* FSL_FEATURE_FLASH_HAS_READ_RESOURCE_CMD */

#if defined(FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD) && FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD
/*! @brief Validates the gived flexram function option.*/
static inline status_t ftfx_check_flexram_function_option(ftfx_flexram_func_opt_t option)
{
    if ((option != kFTFx_FlexramFuncOptAvailableAsRam) &&
        (option != kFTFx_FlexramFuncOptAvailableForEeprom))
    {
        return kStatus_FTFx_InvalidArgument;
    }

    return kStatus_FTFx_Success;
}
#endif /* FSL_FEATURE_FLASH_HAS_SET_FLEXRAM_FUNCTION_CMD */

#if defined(FSL_FEATURE_FLASH_HAS_SWAP_CONTROL_CMD) && FSL_FEATURE_FLASH_HAS_SWAP_CONTROL_CMD
/*! @brief Validates the gived swap control option.*/
static status_t ftfx_check_swap_control_option(ftfx_swap_control_opt_t option)
{
    if ((option == kFTFx_SwapControlOptionIntializeSystem) || (option == kFTFx_SwapControlOptionSetInUpdateState) ||
        (option == kFTFx_SwapControlOptionSetInCompleteState) || (option == kFTFx_SwapControlOptionReportStatus) ||
        (option == kFTFx_SwapControlOptionDisableSystem))
    {
        return kStatus_FTFx_Success;
    }

    return kStatus_FTFx_InvalidArgument;
}
#endif /* FSL_FEATURE_FLASH_HAS_SWAP_CONTROL_CMD */
