/*
 * Copyright 2017 - 2020 NXP
 * All rights reserved.
 *
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_flash.h"

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

/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.flash"
#endif

/*!
 * @name Misc utility defines
 * @{
 */
/*! @brief Alignment utility. */
#ifndef ALIGN_DOWN
#define ALIGN_DOWN(x, a) (((uint32_t)(x)) & ~((uint32_t)(a)-1u))
#endif
#ifndef ALIGN_UP
#define ALIGN_UP(x, a) ALIGN_DOWN((uint32_t)(x) + (uint32_t)(a)-1u, a)
#endif

/*! @brief Join bytes to word utility. */
#define B1P2(b)                      (((uint32_t)(b)&0xFFU) << 8U)
#define B1P1(b)                      ((uint32_t)(b)&0xFFU)
#define B2P1(b)                      ((uint32_t)(b)&0xFFFFU)
#define BYTES_JOIN_TO_WORD_1_1(x, y) (B1P1(x) | B1P2(y))
#define BYTES_JOIN_TO_WORD_1_2(x, y) (B1P2(x) | B2P1(y))
/*@}*/

/*!
 * @name Secondary flash configuration
 * @{
 */
/*! @brief Indicates whether the secondary flash has its own protection register
 * in flash module. */
#define FLASH_SSD_SECONDARY_FLASH_HAS_ITS_OWN_PROTECTION_REGISTER (0)
/*@}*/

/*!
 * @name Flash cache ands speculation control defines
 * @{
 */
#if defined(MCM_PLACR_CFCC_MASK) || defined(MCM_CPCR2_CCBC_MASK)
#define FLASH_CACHE_IS_CONTROLLED_BY_MCM (1U)
#else
#define FLASH_CACHE_IS_CONTROLLED_BY_MCM (0U)
#endif
#if defined(FMC_PFB0CR_CINV_WAY_MASK) || defined(FMC_PFB01CR_CINV_WAY_MASK)
#define FLASH_CACHE_IS_CONTROLLED_BY_FMC (1U)
#else
#define FLASH_CACHE_IS_CONTROLLED_BY_FMC (0U)
#endif
#if defined(MCM_PLACR_DFCS_MASK)
#define FLASH_PREFETCH_SPECULATION_IS_CONTROLLED_BY_MCM (1U)
#else
#define FLASH_PREFETCH_SPECULATION_IS_CONTROLLED_BY_MCM (0U)
#endif
#if defined(FMC_PFB0CR_S_INV_MASK) || defined(FMC_PFB0CR_S_B_INV_MASK) || defined(FMC_PFB01CR_S_INV_MASK) || \
    defined(FMC_PFB01CR_S_B_INV_MASK)
#define FLASH_PREFETCH_SPECULATION_IS_CONTROLLED_BY_FMC (1U)
#else
#define FLASH_PREFETCH_SPECULATION_IS_CONTROLLED_BY_FMC (0U)
#endif
/*@}*/

/*!
 * @name Reserved EEPROM size (For a variety of purposes) defines
 * @{
 */
#define NVM_EEPROM_SIZE_FOR_EEESIZE_RESERVED 0x100UL
/*@}*/

/*!
 * @name Flash Program Once Field defines
 * @{
 */
#if defined(FSL_FEATURE_FLASH_IS_FTMRE) || defined(FSL_FEATURE_FLASH_IS_FTMRH)
/* FTMRE and FTMRH only support 8-bytes unit size */
#define FLASH_PROGRAM_ONCE_IS_4BYTES_UNIT_SUPPORT 0U
#define FLASH_PROGRAM_ONCE_IS_8BYTES_UNIT_SUPPORT 1U
#endif
/*@}*/

/*!
 * @name Flash security status defines
 * @{
 */
#define FLASH_SECURITY_STATE_KEYEN     0x80U
#define FLASH_SECURITY_STATE_UNSECURED 0x02U
#define FLASH_NOT_SECURE               0x01U
#define FLASH_SECURE_BACKDOOR_ENABLED  0x02U
#define FLASH_SECURE_BACKDOOR_DISABLED 0x04U
/*@}*/

/*!
 * @name Flash controller command numbers
 * @{
 */
#define FTMRx_ERASE_VERIFY_ALL_BLOCK      0x01U /*!< ERSVERALL*/
#define FTMRx_ERASE_VERIFY_BLOCK          0x02U /*!< ERSVERBLK*/
#define FTMRx_ERASE_VERIFY_SECTION        0x03U /*!< ERSVERSECT*/
#define FTMRx_READ_ONCE                   0x04U /*!< RDONCE or RDINDEX*/
#define FTMRx_PROGRAM                     0x06U /*!< PGM*/
#define FTMRx_PROGRAM_ONCE                0x07U /*!< PGMONCE or PGMINDEX*/
#define FTMRx_ERASE_ALL_BLOCK             0x08U /*!< ERSALL*/
#define FTMRx_ERASE_BLOCK                 0x09U /*!< ERSBLK*/
#define FTMRx_ERASE_SECTOR                0x0AU /*!< ERSSCR*/
#define FTMRx_ERASE_ALL_BLOCK_UNSECURE    0x0BU /*!< ERSALLU*/
#define FTMRx_SECURITY_BY_PASS            0x0CU /*!< VFYKEY*/
#define FTMRx_SET_USER_MARGIN_LEVEL       0x0DU /*!< SETUSERLVL*/
#define FTMRx_SET_FACTORY_MARGIN_LEVEL    0x0EU /*!< SETFTYLVL*/
#define FTMRx_CONFIGURE_NVM               0x0FU /*!< CONNVM*/
#define FTMRx_ERASE_VERIFY_EEPROM_SECTION 0x10U /*!< ERSVES*/
#define FTMRx_PROGRAM_EEPROM              0x11U /*!< PGME*/
#define FTMRx_ERASE_EEPROM_SECTOR         0x12U /*!< ERSESCR*/
                                                /*@}*/

/*!
 * @name Common flash register info defines
 * @{
 */
#if defined(FTMRE)
#define FTMRx                      FTMRE
#define FTMRx_BASE                 FTMRE_BASE
#define FTMRx_FCCOBIX_CCOBIX(x)    FTMRE_FCCOBIX_CCOBIX(x)
#define FTMRx_FCCOBLO_CCOB(x)      FTMRE_FCCOBLO_CCOB(x)
#define FTMRx_FCCOBHI_CCOB(x)      FTMRE_FCCOBHI_CCOB(x)
#define FTMRx_FCLKDIV_FDIVLCK_MASK FTMRE_FCLKDIV_FDIVLCK_MASK
#define FTMRx_FCLKDIV_FDIVLD_MASK  FTMRE_FCLKDIV_FDIVLD_MASK
#define FTMRx_FCLKDIV_FDIV_MASK    FTMRE_FCLKDIV_FDIV_MASK
#define FTMRx_FSTAT_CCIF_MASK      FTMRE_FSTAT_CCIF_MASK
#define FTMRx_FCLKDIV_FDIV(x)      FTMRE_FCLKDIV_FDIV(x)
#define FTMRx_FSTAT_ACCERR_MASK    FTMRE_FSTAT_ACCERR_MASK
#define FTMRx_FSTAT_FPVIOL_MASK    FTMRE_FSTAT_FPVIOL_MASK
#define FTMRx_FSTAT_MGBUSY_MASK    FTMRE_FSTAT_MGBUSY_MASK
#define FTMRx_FSTAT_MGSTAT_MASK    FTMRE_FSTAT_MGSTAT_MASK
#define FTMRx_FSTAT_MGSTAT0_MASK   FTMRE_FSTAT_MGSTAT(1)
#define FTMRx_FSTAT_MGSTAT1_MASK   FTMRE_FSTAT_MGSTAT(2)
#define FTMRx_FSEC_SEC_MASK        FTMRE_FSEC_SEC_MASK
#define FTMRx_FSEC_KEYEN_MASK      FTMRE_FSEC_KEYEN_MASK
#if defined(FTMRE_FPROT_FPHDIS_MASK)
#define FTMRx_FPROT_FPHDIS_MASK FTMRE_FPROT_FPHDIS_MASK
#endif
#if defined(FTMRE_FERSTAT_SFDIF_MASK)
#define FTMRx_FERSTAT_SFDIF_MASK FTMRE_FERSTAT_SFDIF_MASK
#define FTMRx_FERSTAT_DFDIF_MASK FTMRE_FERSTAT_DFDIF_MASK
#endif
#elif defined(FTMRH)
#define FTMRx                      FTMRH
#define FTMRx_BASE                 FTMRH_BASE
#define FTMRx_FCCOBIX_CCOBIX(x)    FTMRH_FCCOBIX_CCOBIX(x)
#define FTMRx_FCCOBLO_CCOB(x)      FTMRH_FCCOBLO_CCOB(x)
#define FTMRx_FCCOBHI_CCOB(x)      FTMRH_FCCOBHI_CCOB(x)
#define FTMRx_FCLKDIV_FDIVLCK_MASK FTMRH_FCLKDIV_FDIVLCK_MASK
#define FTMRx_FCLKDIV_FDIVLD_MASK  FTMRH_FCLKDIV_FDIVLD_MASK
#define FTMRx_FCLKDIV_FDIV_MASK    FTMRH_FCLKDIV_FDIV_MASK
#define FTMRx_FCLKDIV_FDIV(x)      FTMRH_FCLKDIV_FDIV(x)
#define FTMRx_FSTAT_CCIF_MASK      FTMRH_FSTAT_CCIF_MASK
#define FTMRx_FSTAT_ACCERR_MASK    FTMRH_FSTAT_ACCERR_MASK
#define FTMRx_FSTAT_FPVIOL_MASK    FTMRH_FSTAT_FPVIOL_MASK
#define FTMRx_FSTAT_MGBUSY_MASK    FTMRH_FSTAT_MGBUSY_MASK
#define FTMRx_FSTAT_MGSTAT_MASK    FTMRH_FSTAT_MGSTAT_MASK
#define FTMRx_FSTAT_MGSTAT0_MASK   FTMRH_FSTAT_MGSTAT(1U)
#define FTMRx_FSTAT_MGSTAT1_MASK   FTMRH_FSTAT_MGSTAT(2U)
#define FTMRx_FSEC_SEC_MASK        FTMRH_FSEC_SEC_MASK
#define FTMRx_FSEC_KEYEN_MASK      FTMRH_FSEC_KEYEN_MASK
#if defined(FTMRH_FPROT_FPHDIS_MASK)
#define FTMRx_FPROT_FPHDIS_MASK FTMRH_FPROT_FPHDIS_MASK
#endif
#if defined(FTMRH_FERSTAT_SFDIF_MASK)
#define FTMRx_FERSTAT_SFDIF_MASK FTMRH_FERSTAT_SFDIF_MASK
#define FTMRx_FERSTAT_DFDIF_MASK FTMRH_FERSTAT_DFDIF_MASK
#endif
#else
#error "Unknown flash controller"
#endif
/*@}*/

/*!
 * @brief Enumeration for flash config area.
 */
enum _flash_config_area_range
{
    kFLASH_ConfigAreaStart = 0x400UL,
    kFLASH_ConfigAreaEnd   = 0x40FUL
};

/*!
 * @name Flash register access type defines
 * @{
 */
#define FTMRx_REG8_ACCESS_TYPE  volatile uint8_t *
#define FTMRx_REG32_ACCESS_TYPE volatile uint32_t *
/*@}*/

/*!
 * @brief MCM cache register access info defines.
 */
#if defined(MCM_PLACR_CFCC_MASK)
#define MCM_CACHE_CLEAR_MASK  MCM_PLACR_CFCC_MASK
#define MCM_CACHE_CLEAR_SHIFT MCM_PLACR_CFCC_SHIFT
#if defined(MCM)
#define MCM0_CACHE_REG MCM->PLACR
#elif defined(MCM0)
#define MCM0_CACHE_REG MCM0->PLACR
#endif
#if defined(MCM1)
#define MCM1_CACHE_REG MCM1->PLACR
#endif
#elif defined(MCM_CPCR2_CCBC_MASK)
#define MCM_CACHE_CLEAR_MASK  MCM_CPCR2_CCBC_MASK
#define MCM_CACHE_CLEAR_SHIFT MCM_CPCR2_CCBC_SHIFT
#if defined(MCM)
#define MCM0_CACHE_REG MCM->CPCR2
#elif defined(MCM0)
#define MCM0_CACHE_REG MCM0->CPCR2
#endif
#if defined(MCM1)
#define MCM1_CACHE_REG MCM1->CPCR2
#endif
#endif

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

#if FLASH_DRIVER_IS_FLASH_RESIDENT
/*! @brief Copy flash_run_command() to RAM*/
static void copy_flash_run_command(flash_config_t *config);
/*! @brief Copy flash_cache_clear_command() to RAM*/
static void copy_flash_common_bit_operation(flash_config_t *config);
/*! @brief Check whether flash execute-in-ram functions are ready*/
static status_t flash_check_execute_in_ram_function_info(flash_config_t *config);
#endif /* FLASH_DRIVER_IS_FLASH_RESIDENT */

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

/*! @brief Internal function Flash common bit operation command sequence. Called by driver APIs
 * only
 */
#if FLASH_DRIVER_IS_FLASH_RESIDENT
static void flash_common_bit_operation_command_sequence(
    flash_config_t *config, FTMRx_REG32_ACCESS_TYPE base, uint32_t bitMask, uint32_t bitShift, uint32_t bitValue);
#endif /* FLASH_DRIVER_IS_FLASH_RESIDENT */

/*! @brief Internal function Flash set command. Called by driver APIs only*/
void flash_set_command(uint32_t index, uint32_t fValue, uint32_t sValue);

/*! @brief Perform the cache clear to the flash*/
void flash_cache_clear(flash_config_t *config);

/*! @brief Process the cache to the flash*/
static void flash_cache_clear_process(flash_config_t *config, flash_cache_clear_process_t process);

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

/*! @brief Validates the range and alignment of the given address range.*/
#if FLASH_SSD_IS_EEPROM_ENABLED
static status_t EEPROM_check_range(flash_config_t *config,
                                   uint32_t startAddress,
                                   uint32_t lengthInBytes,
                                   uint32_t alignmentBaseline);
#endif /* FLASH_SSD_IS_EEPROM_ENABLED */

/*! @brief Gets the right address, sector and block size of current flash type
 * which is indicated by address.*/
static status_t flash_get_matched_operation_info(flash_config_t *config,
                                                 uint32_t address,
                                                 flash_operation_config_t *info);
/*! @brief Validates the given user key for flash erase APIs.*/
static status_t flash_check_user_key(uint32_t key);

/*! @brief Gets the flash protection information.*/
static status_t flash_get_protection_info(flash_config_t *config, flash_protection_config_t *info);

#if FLASH_CACHE_IS_CONTROLLED_BY_MCM
/*! @brief Performs the cache clear to the flash by MCM.*/
void mcm_flash_cache_clear(flash_config_t *config);
#endif /* FLASH_CACHE_IS_CONTROLLED_BY_MCM */

#if FLASH_CACHE_IS_CONTROLLED_BY_FMC
/*! @brief Performs the cache clear to the flash by FMC.*/
void fmc_flash_cache_clear(void);
#endif /* FLASH_CACHE_IS_CONTROLLED_BY_FMC */

#if FLASH_PREFETCH_SPECULATION_IS_CONTROLLED_BY_FMC
/*! @brief Performs the prefetch speculation buffer clear to the flash by FMC.*/
void fmc_flash_prefetch_speculation_clear(void);
#endif /* FLASH_PREFETCH_SPECULATION_IS_CONTROLLED_BY_FMC */

/*! @brief Set the flash and eeprom to the specified user margin level.*/
status_t flash_setusermarginlevel(flash_config_t *config,
                                  uint32_t start,
                                  uint8_t iseeprom,
                                  flash_user_margin_value_t margin);

/*! @brief Set the flash and eeprom to the specified factory margin level.*/
status_t flash_setfactorymarginlevel(flash_config_t *config,
                                     uint32_t start,
                                     uint8_t iseeprom,
                                     flash_factory_margin_value_t margin);

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

#if FLASH_DRIVER_IS_FLASH_RESIDENT
/*! @brief A function pointer used to point to relocated flash_run_command() */
static void (*callFlashRunCommand)(FTMRx_REG8_ACCESS_TYPE FTMRx_fstat);
/*! @brief A function pointer used to point to relocated
 * flash_common_bit_operation() */
static void (*callFlashCommonBitOperation)(FTMRx_REG32_ACCESS_TYPE base,
                                           uint32_t bitMask,
                                           uint32_t bitShift,
                                           uint32_t bitValue);

/*!
 * @brief Position independent code of flash_run_command()
 *
 * Note1: The prototype of C function is shown as below:
 * @code
 *   void flash_run_command(FTMRx_REG8_ACCESS_TYPE FTMRx_fstat)
 *   {
 *       PRINT("\r\nClear CCIF bit.\r\n);
 *       *FTMRx_fstat = FTMRx_FSTAT_CCIF_MASK;
 *
 *       PRINT("\r\nCheck CCIF bit of the flash status register, wait till it is set.\r\n");
 *       PRINT("\r\nIP team indicates that this loop will always complete.\r\n");
 *       while (!((*FTMRx_fstat) & FTMRx_FSTAT_CCIF_MASK))
 *       {
 *       }
 *   }
 * @endcode
 * Note2: The binary code is generated by IAR 7.70.1
 */
const static uint16_t s_flashRunCommandFunctionCode[] = {0x2180, 0x7001, 0x7802, 0x420a, 0xd0fc, 0x4770};

/*!
 * @brief Position independent code of flash_common_bit_operation()
 *
 * Note1: The prototype of C function is shown as below:
 * @code
 *   void flash_common_bit_operation(FTMRx_REG32_ACCESS_TYPE base, uint32_t
 * bitMask, uint32_t bitShift, uint32_t
 * bitValue)
 *   {
 *       if (bitMask)
 *       {
 *           uint32_t value = (((uint32_t)(((uint32_t)(bitValue)) << bitShift))
 * & bitMask);
 *           *base = (*base & (~bitMask)) | value;
 *       }
 *
 *       __ISB();
 *       __DSB();
 *   }
 * @endcode
 * Note2: The binary code is generated by IAR 7.70.1
 */
const static uint16_t s_flashCommonBitOperationFunctionCode[] = {
    0xb510, 0x2900, 0xd005, 0x6804, 0x438c, 0x4093, 0x4019, 0x4321, 0x6001, 0xf3bf, 0x8f6f, 0xf3bf, 0x8f4f, 0xbd10};
#endif /* FLASH_DRIVER_IS_FLASH_RESIDENT */

#if (FLASH_DRIVER_IS_FLASH_RESIDENT && !FLASH_DRIVER_IS_EXPORTED)
/*! @brief A static buffer used to hold flash_run_command() */
static uint32_t s_flashRunCommand[kFLASH_ExecuteInRamFunctionMaxSizeInWords];
/*! @brief A static buffer used to hold flash_common_bit_operation() */
static uint32_t s_flashCommonBitOperation[kFLASH_ExecuteInRamFunctionMaxSizeInWords];
#endif

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

/*!
 * brief Initializes the global flash properties structure members.
 *
 * This function checks and initializes the Flash module for the other Flash
 * APIs.
 *
 * param config Pointer to the storage for the driver runtime state.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 * retval #kStatus_FLASH_ClockDivider Flash clock prescaler is wrong.
 * retval #kStatus_FLASH_ExecuteInRamFunctionNotReady Execute-in-RAM function is
 * not available.
 */
status_t FLASH_Init(flash_config_t *config)
{
    status_t returnCode;
    uint8_t clkDiver;

    if (config == NULL)
    {
        return kStatus_FLASH_InvalidArgument;
    }
    clkDiver = (uint8_t)(config->PFlashClockFreq / 1000000UL - 1UL);

    /* Initialize the flash clock to be within spec 1MHz. */
    if ((0U == (FTMRx->FCLKDIV & FTMRx_FCLKDIV_FDIVLCK_MASK)) && (0U != (FTMRx->FSTAT & FTMRx_FSTAT_CCIF_MASK)))
    {
        /* FCLKDIV register is not locked.*/
        FTMRx->FCLKDIV = (uint8_t)(FTMRx->FCLKDIV & (~FTMRx_FCLKDIV_FDIV_MASK)) | FTMRx_FCLKDIV_FDIV(clkDiver);
        if ((FTMRx->FCLKDIV & FTMRx_FCLKDIV_FDIV_MASK) != FTMRx_FCLKDIV_FDIV(clkDiver))
        {
            return kStatus_FLASH_ClockDivider;
        }
    }
    else
    {
        /* FCLKDIV register is locked. */
        if ((FTMRx->FCLKDIV & FTMRx_FCLKDIV_FDIV_MASK) != FTMRx_FCLKDIV_FDIV(clkDiver))
        {
            return kStatus_FLASH_ClockDivider;
        }
    }
#if FLASH_SSD_IS_SECONDARY_FLASH_ENABLED
    if (config->FlashMemoryIndex == (uint8_t)kFLASH_MemoryIndexSecondaryFlash)
    {
        config->PFlashBlockBase = SECONDARY_FLASH_FEATURE_PFLASH_START_ADDRESS;
        config->PFlashTotalSize =
            SECONDARY_FLASH_FEATURE_PFLASH_BLOCK_COUNT * SECONDARY_FLASH_FEATURE_PFLASH_BLOCK_SIZE;
        config->PFlashBlockCount = SECONDARY_FLASH_FEATURE_PFLASH_BLOCK_COUNT;
        config->PFlashSectorSize = SECONDARY_FLASH_FEATURE_PFLASH_BLOCK_SECTOR_SIZE;
    }
    else
#endif
    {
        /* fill out a few of the structure members */
        config->PFlashBlockBase  = FSL_FEATURE_FLASH_PFLASH_START_ADDRESS;
        config->PFlashTotalSize  = FSL_FEATURE_FLASH_PFLASH_BLOCK_COUNT * FSL_FEATURE_FLASH_PFLASH_BLOCK_SIZE;
        config->PFlashBlockCount = FSL_FEATURE_FLASH_PFLASH_BLOCK_COUNT;
        config->PFlashSectorSize = FSL_FEATURE_FLASH_PFLASH_BLOCK_SECTOR_SIZE;
    }
    config->PFlashCallback = NULL;

/* copy required flash commands to RAM */
#if (FLASH_DRIVER_IS_FLASH_RESIDENT && !FLASH_DRIVER_IS_EXPORTED)
    if (kStatus_FLASH_Success != flash_check_execute_in_ram_function_info(config))
    {
        config->executeInRamFunction.activeFunctionCount                     = 0UL;
        config->executeInRamFunction.runCmdFuncAddr.commadAddr               = (uint32_t)s_flashRunCommand;
        config->executeInRamFunction.CommonBitOperationAddr.bitOperationAddr = (uint32_t)s_flashCommonBitOperation;
        config->flashExecuteInRamFunctionInfo = &config->executeInRamFunction.activeFunctionCount;
        returnCode                            = FLASH_PrepareExecuteInRamFunctions(config);
        if (returnCode != kStatus_FLASH_Success)
        {
            return returnCode;
        }
    }
#endif
#if FLASH_SSD_IS_EEPROM_ENABLED
    config->EEpromBlockBase  = FSL_FEATURE_FLASH_EEPROM_START_ADDRESS;
    config->EEpromBlockCount = FSL_FEATURE_FLASH_EEPROM_BLOCK_COUNT;
    config->EEpromTotalSize  = FSL_FEATURE_FLASH_EEPROM_BLOCK_SIZE * FSL_FEATURE_FLASH_EEPROM_BLOCK_COUNT;
    config->EEpromSectorSize = FSL_FEATURE_FLASH_EEPROM_BLOCK_SECTOR_SIZE;
#endif
    config->PFlashMarginLevel = (uint32_t)kFLASH_MarginValueNormal;

    return kStatus_FLASH_Success;
}

/*!
 * brief Sets the desired flash callback function.
 *
 * param config Pointer to the storage for the driver runtime state.
 * param callback A callback function to be stored in the driver.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 */
status_t FLASH_SetCallback(flash_config_t *config, flash_callback_t callback)
{
    if (config == NULL)
    {
        return kStatus_FLASH_InvalidArgument;
    }

    config->PFlashCallback = callback;

    return kStatus_FLASH_Success;
}

#if defined(FLASH_DRIVER_IS_FLASH_RESIDENT) && FLASH_DRIVER_IS_FLASH_RESIDENT
status_t FLASH_PrepareExecuteInRamFunctions(flash_config_t *config)
{
    flash_execute_in_ram_function_config_t *flashExecuteInRamFunctionInfo = &config->executeInRamFunction;

    if ((config == NULL) || (config->flashExecuteInRamFunctionInfo == NULL))
    {
        return kStatus_FLASH_InvalidArgument;
    }
    /* copy flash run command to ram*/
    copy_flash_run_command(config);

    /* copy flash common bit operation command to ram*/
    copy_flash_common_bit_operation(config);
    flashExecuteInRamFunctionInfo->activeFunctionCount = kFLASH_ExecuteInRamFunctionTotalNum;

    return kStatus_FLASH_Success;
}
#endif /* FLASH_DRIVER_IS_FLASH_RESIDENT */

/*!
 * brief Erases entire flash
 *
 * param config Pointer to the storage for the driver runtime state.
 * param key A value used to validate all flash erase APIs.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 * retval #kStatus_FLASH_EraseKeyError API erase key is invalid.
 * retval #kStatus_FLASH_ExecuteInRamFunctionNotReady Execute-in-RAM function is
 * not available.
 * retval #kStatus_FLASH_AccessError Invalid instruction codes and out-of bounds
 * addresses.
 * retval #kStatus_FLASH_ProtectionViolation The program/erase operation is
 * requested to execute on protected areas.
 * retval #kStatus_FLASH_CommandFailure Run-time error during command execution.
 * retval #kStatus_FLASH_EepromSingleBitFault EEPROM single bit fault error
 * code.
 * retval #kStatus_FLASH_EepromDoubleBitFault EEPROM double bit fault error
 * code.
 */
status_t FLASH_EraseAll(flash_config_t *config, uint32_t key)
{
    status_t returnCode;

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

    /* preparing passing parameter to erase all flash & EEPROM blocks */
    FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;

    /* Write index to specify the command code to be loaded */
    flash_set_command(0UL, 0UL, FTMRx_ERASE_ALL_BLOCK);

    /* Validate the user key */
    returnCode = flash_check_user_key(key);
    if (returnCode != kStatus_FLASH_Success)
    {
        return returnCode;
    }

    flash_cache_clear_process(config, kFLASH_CacheClearProcessPre);

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

    flash_cache_clear(config);

    return returnCode;
}

/*!
 * brief Erases the flash sectors encompassed by parameters passed into
 * function.
 *
 * This function erases the appropriate number of flash sectors based on the
 * desired start address and length.
 *
 * param config The pointer to the storage for the driver runtime state.
 * param start The start address of the desired flash memory to be erased.
 *              The start address does not need to be sector-aligned but must be
 * word-aligned.
 * param lengthInBytes The length, given in bytes (not words or long-words)
 *                      to be erased. Must be word-aligned.
 * param key The value used to validate all flash erase APIs.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 * retval #kStatus_FLASH_AlignmentError The parameter is not aligned with the
 * specified baseline.
 * retval #kStatus_FLASH_AddressError The address is out of range.
 * retval #kStatus_FLASH_EraseKeyError The API erase key is invalid.
 * retval #kStatus_FLASH_ExecuteInRamFunctionNotReady Execute-in-RAM function is
 * not available.
 * retval #kStatus_FLASH_AccessError Invalid instruction codes and out-of bounds
 * addresses.
 * retval #kStatus_FLASH_ProtectionViolation The program/erase operation is
 * requested to execute on protected areas.
 * retval #kStatus_FLASH_CommandFailure Run-time error during the command
 * execution.
 */
status_t FLASH_Erase(flash_config_t *config, uint32_t start, uint32_t lengthInBytes, uint32_t key)
{
    uint32_t sectorSize;
    flash_operation_config_t flashOperationInfo;
    uint32_t endAddress;      /* storing end address */
    uint32_t numberOfSectors; /* number of sectors calculated by endAddress */
    status_t returnCode;

    returnCode = flash_get_matched_operation_info(config, start, &flashOperationInfo);
    if (returnCode != kStatus_FLASH_Success)
    {
        return returnCode;
    }

    /* Check the supplied address range. */
    returnCode = flash_check_range(config, start, lengthInBytes, flashOperationInfo.sectorCmdAddressAligment);
    if (returnCode != kStatus_FLASH_Success)
    {
        return returnCode;
    }
    /* Validate the user key */
    returnCode = flash_check_user_key(key);
    if (returnCode != kStatus_FLASH_Success)
    {
        return returnCode;
    }

    start      = flashOperationInfo.convertedAddress;
    sectorSize = flashOperationInfo.activeSectorSize;

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

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

    flash_cache_clear_process(config, kFLASH_CacheClearProcessPre);

    /* the start address will increment to the next sector address
     * until it reaches the endAdddress */
    while (start <= endAddress)
    {
        /* clear error flags */
        FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;
        /* preparing passing parameter to erase a flash block */
        /* Write index to specify the command code to be loaded */
        flash_set_command(0UL, start >> 16UL, FTMRx_ERASE_SECTOR);

        /* Write index to specify the lower byte memory address bits[15:0] to be
         * loaded */
        flash_set_command(1UL, start, start >> 8UL);

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

        /* calling flash callback function if it is available */
        if ((config->PFlashCallback) != NULL)
        {
            config->PFlashCallback();
        }

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

    flash_cache_clear(config);

    return (returnCode);
}

/*!
 * brief Erases the eeprom sectors encompassed by parameters passed into
 * function.
 *
 * This function erases the appropriate number of eeprom sectors based on the
 * desired start address and length.
 *
 * param config The pointer to the storage for the driver runtime state.
 * param start The start address of the desired eeprom memory to be erased.
 *              The start address does not need to be sector-aligned but must be
 * word-aligned.
 * param lengthInBytes The length, given in bytes (not words or long-words)
 *                      to be erased. Must be word-aligned.
 * param key The value used to validate all eeprom erase APIs.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 * retval #kStatus_FLASH_AlignmentError The parameter is not aligned with the
 * specified baseline.
 * retval #kStatus_FLASH_AddressError The address is out of range.
 * retval #kStatus_FLASH_EraseKeyError The API erase key is invalid.
 * retval #kStatus_FLASH_ExecuteInRamFunctionNotReady Execute-in-RAM function is
 * not available.
 * retval #kStatus_FLASH_AccessError Invalid instruction codes and out-of bounds
 * addresses.
 * retval #kStatus_FLASH_ProtectionViolation The program/erase operation is
 * requested to execute on protected areas.
 * retval #kStatus_FLASH_CommandFailure Run-time error during the command
 * execution.
 */
#if defined(FLASH_SSD_IS_EEPROM_ENABLED) && FLASH_SSD_IS_EEPROM_ENABLED
status_t FLASH_EraseEEprom(flash_config_t *config, uint32_t start, uint32_t lengthInBytes, uint32_t key)
{
    uint32_t sectorSize;
    flash_operation_config_t flashOperationInfo;
    uint32_t endAddress;      /* storing end address */
    uint32_t numberOfSectors; /* number of sectors calculated by endAddress */
    status_t returnCode;

    returnCode = flash_get_matched_operation_info(config, start, &flashOperationInfo);
    if (returnCode != kStatus_FLASH_Success)
    {
        return returnCode;
    }
    /* Check the supplied address range. */
    returnCode = EEPROM_check_range(config, start, lengthInBytes, config->EEpromSectorSize);
    if (returnCode != kStatus_FLASH_Success)
    {
        return returnCode;
    }
    /* Validate the user key */
    returnCode = flash_check_user_key(key);
    if (returnCode != kStatus_FLASH_Success)
    {
        return returnCode;
    }

    sectorSize = config->EEpromSectorSize;

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

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

    flash_cache_clear_process(config, kFLASH_CacheClearProcessPre);

    /* the start address will increment to the next sector address
     * until it reaches the endAdddress */
    while (start <= endAddress)
    {
        /* clear error flags */
        FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;
        /* preparing passing parameter to erase a flash block */
        /* Write index to specify the command code to be loaded */
        flash_set_command(0UL, start >> 16UL, FTMRx_ERASE_EEPROM_SECTOR);

        /* Write index to specify the lower byte memory address bits[15:0] to be
         * loaded */
        flash_set_command(1UL, start, start >> 8UL);

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

        /* calling flash callback function if it is available */
        if (config->PFlashCallback != NULL)
        {
            config->PFlashCallback();
        }

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

    flash_cache_clear(config);

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

#if defined(FSL_FEATURE_FLASH_HAS_UNSECURE_FLASH_CMD) && FSL_FEATURE_FLASH_HAS_UNSECURE_FLASH_CMD
status_t FLASH_EraseAllUnsecure(flash_config_t *config, uint32_t key)
{
    status_t returnCode;

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

    /* Prepare passing parameter to erase all flash blocks (unsecure). */
    /* clear error flags */
    FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;

    /* Write index to specify the command code to be loaded */
    flash_set_command(0UL, 0UL, FTMRx_ERASE_ALL_BLOCK_UNSECURE);

    /* Validate the user key */
    returnCode = flash_check_user_key(key);
    if (returnCode != kStatus_FLASH_Success)
    {
        return returnCode;
    }

    flash_cache_clear_process(config, kFLASH_CacheClearProcessPre);

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

    flash_cache_clear(config);

    return returnCode;
}
#endif /* FSL_FEATURE_FLASH_HAS_UNSECURE_FLASH_CMD */

/*!
 * brief Programs flash with data at locations passed in through parameters.
 *
 * This function programs the flash memory with the desired data for a given
 * flash area as determined by the start address and the length.
 *
 * param config A pointer to the storage for the driver runtime state.
 * param start The start address of the desired flash memory to be programmed.
 * Must be
 *              word-aligned.
 * param src A pointer to the source buffer of data that is to be programmed
 *            into the flash.
 * param lengthInBytes The length, given in bytes (not words or long-words),
 *                      to be programmed. Must be word-aligned.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 * retval #kStatus_FLASH_AlignmentError Parameter is not aligned with the
 * specified baseline.
 * retval #kStatus_FLASH_AddressError Address is out of range.
 * retval #kStatus_FLASH_ExecuteInRamFunctionNotReady Execute-in-RAM function is
 * not available.
 * retval #kStatus_FLASH_AccessError Invalid instruction codes and out-of bounds
 * addresses.
 * retval #kStatus_FLASH_ProtectionViolation The program/erase operation is
 * requested to execute on protected areas.
 * retval #kStatus_FLASH_CommandFailure Run-time error during the command
 * execution.
 */
status_t FLASH_Program(flash_config_t *config, uint32_t start, uint32_t *src, uint32_t lengthInBytes)
{
    status_t returnCode;
    flash_operation_config_t flashOperationInfo;

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

    returnCode = flash_get_matched_operation_info(config, start, &flashOperationInfo);
    if (returnCode != kStatus_FLASH_Success)
    {
        return returnCode;
    }

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

    flash_cache_clear_process(config, kFLASH_CacheClearProcessPre);

    while (lengthInBytes > 0UL)
    {
        /* pass paramters to FTMRx */
        FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;

        /* Write index to specify the command code to be loaded */
        flash_set_command(0UL, start >> 16UL, FTMRx_PROGRAM);
        flash_set_command(1UL, start, start >> 8UL);
        /*Small endian by default.*/
        /* Write index to specify the word0 (MSB word) to be programmed */
        flash_set_command(2UL, *src, *src >> 8UL);

        /* Write index to specify the word1 (LSB word) to be programmed */
        flash_set_command(3UL, *src >> 16UL, *src >> 24UL);
        /* preparing passing parameter to program the flash block */

        if (4UL == flashOperationInfo.blockWriteUnitSize)
        {
            /* calling flash command sequence function to execute the command */
            returnCode = flash_command_sequence(config);
        }
        else if (8UL == flashOperationInfo.blockWriteUnitSize)
        {
            src++;
            /* Write index to specify the word (MSB word) to be programmed */
            flash_set_command(4UL, *src, *src >> 8UL);
            /* Write index to specify the word (LSB word) to be programmed */
            flash_set_command(5UL, *src >> 16UL, *src >> 24UL);

            /* calling flash command sequence function to execute the command */
            returnCode = flash_command_sequence(config);
        }
        else
        {
            /*"There's nothing to do here."*/
        }
        src++;
        /* calling flash callback function if it is available */
        if (config->PFlashCallback != NULL)
        {
            config->PFlashCallback();
        }

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

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

    flash_cache_clear(config);

    return (returnCode);
}

/*!
 * brief Programs Program Once Field through parameters.
 *
 * This function programs the Program Once Field with the desired data for a
 * given
 * flash area as determined by the index and length.
 *
 * param config A pointer to the storage for the driver runtime state.
 * param index The index indicating which area of the Program Once Field to be
 * programmed.
 * param src A pointer to the source buffer of data that is to be programmed
 *            into the Program Once Field.
 * param lengthInBytes The length, given in bytes (not words or long-words),
 *                      to be programmed. Must be word-aligned.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 * retval #kStatus_FLASH_ExecuteInRamFunctionNotReady Execute-in-RAM function is
 * not available.
 * retval #kStatus_FLASH_AccessError Invalid instruction codes and out-of bounds
 * addresses.
 * retval #kStatus_FLASH_ProtectionViolation The program/erase operation is
 * requested to execute on protected areas.
 * retval #kStatus_FLASH_CommandFailure Run-time error during the command
 * execution.
 */
status_t FLASH_ProgramOnce(flash_config_t *config, uint32_t index, uint32_t *src, uint32_t lengthInBytes)
{
    status_t returnCode;

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

    /* pass paramters to FTMRx */
    FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;

    /* Write index to specify the command code to be loaded. */
    flash_set_command(0UL, 0UL, FTMRx_PROGRAM_ONCE);
    flash_set_command(1UL, index, index >> 8UL);

    /* Write index to specify the word0 (MSB word) to be programmed. */
    flash_set_command(2UL, *src, *src >> 8UL);

    /* Write index to specify the word1 (LSB word) to be programmed. */
    flash_set_command(3UL, *src >> 16UL, *src >> 24UL);

#if FLASH_PROGRAM_ONCE_IS_8BYTES_UNIT_SUPPORT

    /* Write index to specify the word2 (MSB word) to be programmed. */
    flash_set_command(4UL, *(src + 1UL), *(src + 1UL) >> 8UL);

    /* Write index to specify the word3 (LSB word) to be programmed. */
    flash_set_command(5UL, *(src + 1UL) >> 16UL, *(src + 1UL) >> 24UL);
#endif
    flash_cache_clear_process(config, kFLASH_CacheClearProcessPre);

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

    flash_cache_clear(config);

    return returnCode;
}

#if defined(FLASH_SSD_IS_EEPROM_ENABLED) && FLASH_SSD_IS_EEPROM_ENABLED
status_t FLASH_EepromWrite(flash_config_t *config, uint32_t start, uint8_t *src, uint32_t lengthInBytes)
{
    status_t returnCode;
    uint32_t i;
    if ((config == NULL) || (src == NULL))
    {
        return kStatus_FLASH_InvalidArgument;
    }
    if ((lengthInBytes > 4UL) || (0UL == lengthInBytes))
    {
        return kStatus_FLASH_InvalidArgument;
    }

    /* Check the supplied address range. */
    /* Validates the range of the given address */
    if ((start < config->EEpromBlockBase) ||
        ((start + lengthInBytes) > (config->EEpromBlockBase + config->EEpromTotalSize)))
    {
        return kStatus_FLASH_AddressError;
    }

    returnCode = kStatus_FLASH_Success;

    flash_cache_clear_process(config, kFLASH_CacheClearProcessPre);

    while (lengthInBytes > 3UL)
    {
        /* pass paramters to FTMRx */
        FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;

        /* Write index to specify the command code to be loaded */
        flash_set_command(0UL, start >> 16UL, FTMRx_PROGRAM_EEPROM);
        flash_set_command(1UL, start, start >> 8UL);

        /* Write index to specify the byte (MSB word) to be programmed */
        for (i = 0UL; i < lengthInBytes; i++)
        {
            flash_set_command(0x02UL + i, *src++, 0UL);
        }

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

        /* calling flash callback function if it is available */
        if (config->PFlashCallback != NULL)
        {
            config->PFlashCallback();
        }

        /* checking for the success of command execution */
        if (returnCode != kStatus_FLASH_Success)
        {
            break;
        }
        else
        {
            /* update start address for next iteration */
            start += 4U;
            src += 4U;
            /* update lengthInBytes for next iteration */
            lengthInBytes -= 4UL;
        }
    }
    if ((lengthInBytes > 0UL) && (kStatus_FLASH_Success == returnCode))
    {
        /* pass paramters to FTMRx */
        FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;

        /* Write index to specify the command code to be loaded */
        flash_set_command(0UL, start >> 16UL, FTMRx_PROGRAM_EEPROM);
        flash_set_command(1UL, start, start >> 8UL);

        /* Write index to specify the byte (MSB word) to be programmed */
        for (i = 0UL; i < lengthInBytes; i++)
        {
            flash_set_command(0x02UL + i, *src++, 0UL);
        }
        /* calling flash command sequence function to execute the command */
        returnCode = flash_command_sequence(config);
    }
    flash_cache_clear(config);

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

/*!
 * brief Reads the Program Once Field through parameters.
 *
 * This function reads the read once feild with given index and length.
 *
 * param config A pointer to the storage for the driver runtime state.
 * param index The index indicating the area of program once field to be read.
 * param dst A pointer to the destination buffer of data that is used to store
 *        data to be read.
 * param lengthInBytes The length, given in bytes (not words or long-words),
 *        to be programmed. Must be word-aligned.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 * retval #kStatus_FLASH_ExecuteInRamFunctionNotReady Execute-in-RAM function is
 * not available.
 * retval #kStatus_FLASH_AccessError Invalid instruction codes and out-of bounds
 * addresses.
 * retval #kStatus_FLASH_ProtectionViolation The program/erase operation is
 * requested to execute on protected areas.
 * retval #kStatus_FLASH_CommandFailure Run-time error during the command
 * execution.
 */
status_t FLASH_ReadOnce(flash_config_t *config, uint32_t index, uint32_t *dst, uint32_t lengthInBytes)
{
    uint32_t fccobloValue, fccobhiValue;
    status_t returnCode;

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

    flash_cache_clear_process(config, kFLASH_CacheClearProcessPre);
    /* clear error flags */
    FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;

    /* Write index to specify the command code to be loaded */
    flash_set_command(0UL, 0UL, FTMRx_READ_ONCE);
    flash_set_command(1UL, index, index >> 8UL);

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

    if (kStatus_FLASH_Success == returnCode)
    {
        fccobloValue = FTMRx->FCCOBLO;
        fccobhiValue = FTMRx->FCCOBHI;
        /* Read lengthInBytes words. */
        FTMRx->FCCOBIX = FTMRx_FCCOBIX_CCOBIX(2UL);
        *dst           = (uint32_t)BYTES_JOIN_TO_WORD_1_1(fccobloValue, fccobhiValue);
        FTMRx->FCCOBIX = FTMRx_FCCOBIX_CCOBIX(3UL);
        *dst |= ((uint32_t)BYTES_JOIN_TO_WORD_1_1(fccobloValue, fccobhiValue)) << 16UL;
#if FLASH_PROGRAM_ONCE_IS_8BYTES_UNIT_SUPPORT
        FTMRx->FCCOBIX = FTMRx_FCCOBIX_CCOBIX(4UL);
        *(dst + 1UL)   = (uint32_t)BYTES_JOIN_TO_WORD_1_1(fccobloValue, fccobhiValue);
        FTMRx->FCCOBIX = FTMRx_FCCOBIX_CCOBIX(5UL);
        *(dst + 1UL) |= ((uint32_t)BYTES_JOIN_TO_WORD_1_1(fccobloValue, fccobhiValue)) << 16UL;
#endif
    }
    flash_cache_clear(config);

    return returnCode;
}

/*!
 * brief Returns the security state via the pointer passed into the function.
 *
 * This function retrieves the current flash security status, including the
 * security enabling state and the backdoor key enabling state.
 *
 * param config A pointer to storage for the driver runtime state.
 * param state A pointer to the value returned for the current security status
 * code:
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 */
status_t FLASH_GetSecurityState(flash_config_t *config, flash_security_state_t *state)
{
    /* store data read from flash register */
    uint8_t registerValue;

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

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

    /* check the status of the flash security bits in the security register */
    if (FLASH_SECURITY_STATE_UNSECURED == (registerValue & FTMRx_FSEC_SEC_MASK))
    {
        /* Flash in unsecured state */
        *state = kFLASH_SecurityStateNotSecure;
    }
    else
    {
        /* Flash in secured state
         * check for backdoor key security enable bit */
        if (FLASH_SECURITY_STATE_KEYEN == (registerValue & FTMRx_FSEC_KEYEN_MASK))
        {
            /* Backdoor key security enabled */
            *state = kFLASH_SecurityStateBackdoorEnabled;
        }
        else
        {
            /* Backdoor key security disabled */
            *state = kFLASH_SecurityStateBackdoorDisabled;
        }
    }

    return (kStatus_FLASH_Success);
}

/*!
 * brief Allows users to bypass security with a backdoor key.
 *
 * If the MCU is in secured state, this function unsecures the MCU by
 * comparing the provided backdoor key with ones in the flash configuration
 * field.
 *
 * param config A pointer to the storage for the driver runtime state.
 * param backdoorKey A pointer to the user buffer containing the backdoor key.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 * retval #kStatus_FLASH_ExecuteInRamFunctionNotReady Execute-in-RAM function is
 * not available.
 * retval #kStatus_FLASH_AccessError Invalid instruction codes and out-of bounds
 * addresses.
 * retval #kStatus_FLASH_ProtectionViolation The program/erase operation is
 * requested to execute on protected areas.
 * retval #kStatus_FLASH_CommandFailure Run-time error during the command
 * execution.
 */
status_t FLASH_SecurityBypass(flash_config_t *config, const uint8_t *backdoorKey)
{
    uint8_t registerValue; /* registerValue */
    status_t returnCode;   /* return code variable */
    uint8_t tmpKey;
    uint32_t i;

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

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

    /* Get flash security register value */
    registerValue = FTMRx->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 (0x02UL != (registerValue & 0x03UL))
    {
        /* clear error flags */
        FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;
        /* preparing passing parameter to erase a flash block */
        /* Write index to specify the command code to be loaded */
        flash_set_command(0UL, 0UL, FTMRx_SECURITY_BY_PASS);

        for (i = 0UL; i < 4UL; i++)
        {
            tmpKey = *backdoorKey++;
            flash_set_command(i + 1UL, tmpKey, *backdoorKey++);
        }

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

    return (returnCode);
}

/*!
 * brief Verifies erasure of the entire flash at a specified margin level.
 *
 * This function checks whether the flash is erased to the
 * specified read margin level.
 *
 * param config A pointer to the storage for the driver runtime state.
 * param margin Read margin choice.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 * retval #kStatus_FLASH_ExecuteInRamFunctionNotReady Execute-in-RAM function is
 * not available.
 * retval #kStatus_FLASH_AccessError Invalid instruction codes and out-of bounds
 * addresses.
 * retval #kStatus_FLASH_ProtectionViolation The program/erase operation is
 * requested to execute on protected areas.
 * retval #kStatus_FLASH_CommandFailure Run-time error during the command
 * execution.
 * retval #kStatus_FLASH_EepromSingleBitFault EEPROM single bit fault error
 * code.
 * retval #kStatus_FLASH_EepromDoubleBitFault EEPROM double bit fault error
 * code.
 */
status_t FLASH_VerifyEraseAll(flash_config_t *config, flash_margin_value_t margin)
{
    status_t returnCode;
    if (config == NULL)
    {
        return kStatus_FLASH_InvalidArgument;
    }
    /* set the default return code as kStatus_Success */
    returnCode = kStatus_FLASH_Success;

    if ((uint32_t)margin != config->PFlashMarginLevel)
    {
        switch (margin)
        {
            case kFLASH_MarginValueUser:
                returnCode = flash_setusermarginlevel(config, config->PFlashBlockBase, 0U, kFLASH_UserMarginValue1);
                break;
            case kFLASH_MarginValueFactory:
                returnCode =
                    flash_setfactorymarginlevel(config, config->PFlashBlockBase, 0U, kFLASH_FactoryMarginValue1);
                break;
            case kFLASH_MarginValueNormal:
                returnCode =
                    flash_setusermarginlevel(config, config->PFlashBlockBase, 0U, kFLASH_ReadMarginValueNormal);
                break;
            default:
                returnCode = kStatus_FLASH_InvalidArgument;
                break;
        }
        config->PFlashMarginLevel = (uint32_t)margin;
    }
    if (returnCode != kStatus_FLASH_Success)
    {
        return returnCode;
    }
#if FLASH_SSD_IS_EEPROM_ENABLED
    if ((uint32_t)margin != config->PFlashMarginLevel)
    {
        switch (margin)
        {
            case kFLASH_MarginValueUser:
                returnCode = flash_setusermarginlevel(config, config->EEpromBlockBase, 1U, kFLASH_UserMarginValue1);
                break;
            case kFLASH_MarginValueFactory:
                returnCode =
                    flash_setfactorymarginlevel(config, config->EEpromBlockBase, 1U, kFLASH_FactoryMarginValue1);
                break;
            case kFLASH_MarginValueNormal:
                returnCode =
                    flash_setusermarginlevel(config, config->EEpromBlockBase, 1U, kFLASH_ReadMarginValueNormal);
                break;
            default:
                returnCode = kStatus_FLASH_InvalidArgument;
                break;
        }
        config->PFlashMarginLevel = (uint32_t)margin;
    }
    if (returnCode != kStatus_FLASH_Success)
    {
        return returnCode;
    }
#endif

    /* clear error flags */
    FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;

    /* Erase verify all flash & EEPROM blocks */
    flash_set_command(0UL, 0UL, FTMRx_ERASE_VERIFY_ALL_BLOCK);

    returnCode = flash_command_sequence(config);

    return returnCode;
}

/*!
 * brief Verifies an erasure of the desired flash area at a specified margin
 * level.
 *
 * This function checks the appropriate number of flash sectors based on
 * the desired start address and length to check whether the flash is erased
 * to the specified read margin level.
 *
 * param config A pointer to the storage for the driver runtime state.
 * param margin Read margin choice.
 * param start The start address of the desired flash memory to be verified.
 *        The start address does not need to be sector-aligned but must be
 * word-aligned.
 * param lengthInBytes The length, given in bytes (not words or long-words),
 *        to be verified. Must be word-aligned.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 * retval #kStatus_FLASH_AlignmentError Parameter is not aligned with specified
 * baseline.
 * retval #kStatus_FLASH_AddressError Address is out of range.
 * retval #kStatus_FLASH_ExecuteInRamFunctionNotReady Execute-in-RAM function is
 * not available.
 * retval #kStatus_FLASH_AccessError Invalid instruction codes and out-of bounds
 * addresses.
 * retval #kStatus_FLASH_ProtectionViolation The program/erase operation is
 * requested to execute on protected areas.
 * retval #kStatus_FLASH_CommandFailure Run-time error during the command
 * execution.
 */
status_t FLASH_VerifyErase(flash_config_t *config, uint32_t start, uint32_t lengthInBytes, flash_margin_value_t margin)
{
    uint32_t blockSize;
    flash_operation_config_t flashOperationInfo;
    uint32_t nextBlockStartAddress;
    uint32_t remainingBytes;
    status_t returnCode;

    returnCode = flash_get_matched_operation_info(config, start, &flashOperationInfo);
    if (kStatus_FLASH_Success != returnCode)
    {
        return returnCode;
    }

    returnCode = flash_check_range(config, start, lengthInBytes, flashOperationInfo.sectionCmdAddressAligment);
    if (kStatus_FLASH_Success != returnCode)
    {
        return returnCode;
    }

    returnCode = flash_get_matched_operation_info(config, start, &flashOperationInfo);
    if (kStatus_FLASH_Success != returnCode)
    {
        return returnCode;
    }
    start     = flashOperationInfo.convertedAddress;
    blockSize = flashOperationInfo.activeBlockSize;

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

    remainingBytes = lengthInBytes;

    while (remainingBytes != 0UL)
    {
        uint32_t numberOfPhrases;
        uint32_t verifyLength = nextBlockStartAddress - start;
        if (verifyLength > remainingBytes)
        {
            verifyLength = remainingBytes;
        }
        numberOfPhrases = verifyLength >> flashOperationInfo.sectionCmdAddressAligment;
        if ((uint32_t)margin != config->PFlashMarginLevel)
        {
            switch (margin)
            {
                case kFLASH_MarginValueUser:
                    returnCode = flash_setusermarginlevel(config, start, 0U, kFLASH_UserMarginValue1);
                    break;
                case kFLASH_MarginValueFactory:
                    returnCode = flash_setfactorymarginlevel(config, start, 0U, kFLASH_FactoryMarginValue1);
                    break;
                case kFLASH_MarginValueNormal:
                    returnCode = flash_setusermarginlevel(config, start, 0U, kFLASH_ReadMarginValueNormal);
                    break;
                default:
                    returnCode = kStatus_FLASH_InvalidArgument;
                    break;
            }
            config->PFlashMarginLevel = (uint32_t)margin;
        }
        if (kStatus_FLASH_Success != returnCode)
        {
            return returnCode;
        }
        flash_cache_clear_process(config, kFLASH_CacheClearProcessPre);
        /* clear error flags */
        FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;

        /* Fill in verify FLASH section command parameters. */
        flash_set_command(0UL, start >> 16UL, FTMRx_ERASE_VERIFY_SECTION);
        flash_set_command(1UL, start, start >> 8UL);
        /* Write index to specify the # of longwords to be verified */
        flash_set_command(2UL, numberOfPhrases, numberOfPhrases >> 8UL);

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

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

    return kStatus_FLASH_Success;
}

/*!
 * brief Returns the protection state of the desired flash area via the pointer
 * passed into the function.
 *
 * This function retrieves the current flash protect status for a given
 * flash area as determined by the start address and length.
 *
 * param config A pointer to the storage for the driver runtime state.
 * param start The start address of the desired flash memory to be checked. Must
 * be word-aligned.
 * param lengthInBytes The length, given in bytes (not words or long-words)
 *        to be checked.  Must be word-aligned.
 * param protection_state A pointer to the value returned for the current
 *        protection status code for the desired flash area.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 * retval #kStatus_FLASH_AlignmentError Parameter is not aligned with specified
 * baseline.
 * retval #kStatus_FLASH_AddressError The address is out of range.
 */
status_t FLASH_IsProtected(flash_config_t *config,
                           uint32_t start,
                           uint32_t lengthInBytes,
                           flash_protection_state_t *protection_state)
{
    flash_protection_config_t flashProtectionInfo; /* flash protection information */
    uint32_t endAddress;                           /* end address for protection check */
    status_t returnCode;

    if ((protection_state == NULL) || (lengthInBytes == 0UL))
    {
        return kStatus_FLASH_InvalidArgument;
    }

    /* Check the supplied address range. */
    returnCode = flash_check_range(config, start, lengthInBytes, FSL_FEATURE_FLASH_PFLASH_BLOCK_WRITE_UNIT_SIZE);
    if (returnCode != kStatus_FLASH_Success)
    {
        return returnCode;
    }

    /* Get necessary flash protection information. */
    returnCode = flash_get_protection_info(config, &flashProtectionInfo);
    if (returnCode != kStatus_FLASH_Success)
    {
        return returnCode;
    }

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

    if (((start >= flashProtectionInfo.lowRegionStart) && (endAddress <= flashProtectionInfo.lowRegionEnd)) ||
        ((start >= flashProtectionInfo.highRegionStart) && (endAddress <= flashProtectionInfo.highRegionEnd)))
    {
        *protection_state = kFLASH_ProtectionStateProtected;
    }
    else if (((start >= flashProtectionInfo.lowRegionEnd) && (endAddress <= flashProtectionInfo.highRegionStart)) ||
             (start >= flashProtectionInfo.highRegionEnd) || (endAddress <= flashProtectionInfo.lowRegionStart))
    {
        *protection_state = kFLASH_ProtectionStateUnprotected;
    }
    else
    {
        *protection_state = kFLASH_ProtectionStateMixed;
    }

    return kStatus_FLASH_Success;
}

/*!
 * brief Returns the desired flash property.
 *
 * param config A pointer to the storage for the driver runtime state.
 * param whichProperty The desired property from the list of properties in
 *        enum flash_property_tag_t
 * param value A pointer to the value returned for the desired flash property.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 * retval #kStatus_FLASH_UnknownProperty An unknown property tag.
 */
status_t FLASH_GetProperty(flash_config_t *config, flash_property_tag_t whichProperty, uint32_t *value)
{
    status_t status = kStatus_FLASH_UnknownProperty;
    if ((config == NULL) || (value == NULL))
    {
        return kStatus_FLASH_InvalidArgument;
    }

    switch (whichProperty)
    {
        case kFLASH_PropertyPflashSectorSize:
            *value = config->PFlashSectorSize;
            status = kStatus_FLASH_Success;
            break;

        case kFLASH_PropertyPflashTotalSize:
            *value = config->PFlashTotalSize;
            status = kStatus_FLASH_Success;
            break;

        case kFLASH_PropertyPflashBlockSize:
            *value = config->PFlashTotalSize / (uint32_t)config->PFlashBlockCount;
            status = kStatus_FLASH_Success;
            break;

        case kFLASH_PropertyPflashBlockCount:
            *value = (uint32_t)config->PFlashBlockCount;
            status = kStatus_FLASH_Success;
            break;

        case kFLASH_PropertyPflashBlockBaseAddr:
            *value = config->PFlashBlockBase;
            status = kStatus_FLASH_Success;
            break;

        case kFLASH_PropertyPflashFacSupport:
#if defined(FSL_FEATURE_FLASH_HAS_ACCESS_CONTROL)
            *value = FSL_FEATURE_FLASH_HAS_ACCESS_CONTROL;
#else
            *value = 0UL;
#endif /* FSL_FEATURE_FLASH_HAS_ACCESS_CONTROL */
            status = kStatus_FLASH_Success;
            break;

        case kFLASH_PropertyFlashClockFrequency:
            *value = config->PFlashClockFreq;
            status = kStatus_FLASH_Success;
            break;

#if FLASH_SSD_IS_EEPROM_ENABLED
        case kFLASH_PropertyEepromBlockBaseAddr:
            *value = config->EEpromBlockBase;
            status = kStatus_FLASH_Success;
            break;

        case kFLASH_PropertyEepromTotalSize:
            *value = config->EEpromTotalSize;
            status = kStatus_FLASH_Success;
            break;

        case kFLASH_PropertyEepromSectorSize:
            *value = (uint32_t)config->EEpromSectorSize;
            status = kStatus_FLASH_Success;
            break;

        case kFLASH_PropertyEepromBlockSize:
            *value = config->EEpromTotalSize / (uint32_t)config->EEpromBlockCount;
            status = kStatus_FLASH_Success;
            break;

        case kFLASH_PropertyEepromBlockCount:
            *value = (uint32_t)config->EEpromBlockCount;
            status = kStatus_FLASH_Success;
            break;
#endif /* FLASH_SSD_IS_EEPROM_ENABLED */

        default: /* catch inputs that are not recognized */
            status = kStatus_FLASH_UnknownProperty;
            break;
    }

    return status;
}

/*!
 * brief Sets the desired flash property.
 *
 * param config A pointer to the storage for the driver runtime state.
 * param whichProperty The desired property from the list of properties in
 *        enum flash_property_tag_t
 * param value A to set for the desired flash property.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 * retval #kStatus_FLASH_UnknownProperty An unknown property tag.
 * retval #kStatus_FLASH_InvalidPropertyValue An invalid property value.
 * retval #kStatus_FLASH_ReadOnlyProperty An read-only property tag.
 */
status_t FLASH_SetProperty(flash_config_t *config, flash_property_tag_t whichProperty, uint32_t value)
{
    status_t status = kStatus_FLASH_Success;

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

    switch (whichProperty)
    {
#if FLASH_SSD_IS_SECONDARY_FLASH_ENABLED
        case kFLASH_PropertyFlashMemoryIndex:
            if ((value != (uint32_t)kFLASH_MemoryIndexPrimaryFlash) &&
                (value != (uint32_t)kFLASH_MemoryIndexSecondaryFlash))
            {
                return kStatus_FLASH_InvalidPropertyValue;
            }
            config->FlashMemoryIndex = (uint8_t)value;
            break;
#endif /* FLASH_SSD_IS_SECONDARY_FLASH_ENABLED */

        case kFLASH_PropertyFlashCacheControllerIndex:
            if ((value != (uint32_t)kFLASH_CacheControllerIndexForCore0) &&
                (value != (uint32_t)kFLASH_CacheControllerIndexForCore1))
            {
                return kStatus_FLASH_InvalidPropertyValue;
            }
            config->FlashCacheControllerIndex = (uint8_t)value;
            break;
        case kFLASH_PropertyFlashClockFrequency:
            config->PFlashClockFreq = value;
            break;
        case kFLASH_PropertyPflashSectorSize:
        case kFLASH_PropertyPflashTotalSize:
        case kFLASH_PropertyPflashBlockSize:
        case kFLASH_PropertyPflashBlockCount:
        case kFLASH_PropertyPflashBlockBaseAddr:
        case kFLASH_PropertyPflashFacSupport:
#if FLASH_SSD_IS_EEPROM_ENABLED
        case kFLASH_PropertyEepromBlockBaseAddr:
        case kFLASH_PropertyEepromTotalSize:
        case kFLASH_PropertyEepromSectorSize:
        case kFLASH_PropertyEepromBlockSize:
        case kFLASH_PropertyEepromBlockCount:
#endif /* FLASH_SSD_IS_EEPROM_ENABLED */
            status = kStatus_FLASH_ReadOnlyProperty;
            break;
        default: /* catch inputs that are not recognized */
            status = kStatus_FLASH_UnknownProperty;
            break;
    }

    return status;
}

/*!
 * brief Sets the PFlash Protection to the intended protection status.
 *
 * param config A pointer to storage for the driver runtime state.
 * param protectStatus The expected protect status to set to the PFlash
 * protection register.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 * retval #kStatus_FLASH_CommandFailure Run-time error during command execution.
 */
status_t FLASH_PflashSetProtection(flash_config_t *config, pflash_protection_status_t *protectStatus)
{
    if (config == NULL)
    {
        return kStatus_FLASH_InvalidArgument;
    }
    /* The reserved bit6 value of FPROT is reset value, which cannot change.
     * If flash memory is only 8KB, the bit3 to bit5 of FPROT are also reserved.*/
    FTMRx->FPROT = protectStatus->fprotvalue;
    if (protectStatus->fprotvalue != FTMRx->FPROT)
    {
        return kStatus_FLASH_CommandFailure;
    }

    return kStatus_FLASH_Success;
}

/*!
 * brief Gets the PFlash protection status.
 *
 * param config A pointer to the storage for the driver runtime state.
 * param protectStatus  Protect status returned by the PFlash IP.
 *
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidArgument An invalid argument is provided.
 */
status_t FLASH_PflashGetProtection(flash_config_t *config, pflash_protection_status_t *protectStatus)
{
    if ((config == NULL) || (protectStatus == NULL))
    {
        return kStatus_FLASH_InvalidArgument;
    }

    protectStatus->fprotvalue = FTMRx->FPROT;

    return kStatus_FLASH_Success;
}

#if defined(FLASH_SSD_IS_EEPROM_ENABLED) && FLASH_SSD_IS_EEPROM_ENABLED
status_t FLASH_EepromSetProtection(flash_config_t *config, uint8_t protectStatus)
{
    if (config == NULL)
    {
        return kStatus_FLASH_InvalidArgument;
    }

    if ((config->EEpromTotalSize == 0UL) || (config->EEpromTotalSize == NVM_EEPROM_SIZE_FOR_EEESIZE_RESERVED))
    {
        return kStatus_FLASH_CommandNotSupported;
    }

    FTMRx->EEPROT = protectStatus;

    if (FTMRx->EEPROT != protectStatus)
    {
        return kStatus_FLASH_CommandFailure;
    }

    return kStatus_FLASH_Success;
}
#endif /* FLASH_SSD_IS_EEPROM_ENABLED */

#if defined(FLASH_SSD_IS_EEPROM_ENABLED) && FLASH_SSD_IS_EEPROM_ENABLED
status_t FLASH_EepromGetProtection(flash_config_t *config, uint8_t *protectStatus)
{
    if ((config == NULL) || (protectStatus == NULL))
    {
        return kStatus_FLASH_InvalidArgument;
    }

    if ((config->EEpromTotalSize == 0UL) || (config->EEpromTotalSize == NVM_EEPROM_SIZE_FOR_EEESIZE_RESERVED))
    {
        return kStatus_FLASH_CommandNotSupported;
    }

    *protectStatus = FTMRx->EEPROT;

    return kStatus_FLASH_Success;
}
#endif /* FLASH_SSD_IS_EEPROM_ENABLED */

/*!
 * brief Sets the PFlash prefetch speculation to the intended speculation
 * status.
 *
 * param speculationStatus The expected protect status to set to the PFlash
 * protection register. Each bit is
 * retval #kStatus_FLASH_Success API was executed successfully.
 * retval #kStatus_FLASH_InvalidSpeculationOption An invalid speculation option
 * argument is provided.
 */
status_t FLASH_PflashSetPrefetchSpeculation(flash_prefetch_speculation_status_t *speculationStatus)
{
#if FLASH_PREFETCH_SPECULATION_IS_CONTROLLED_BY_MCM
    {
        FTMRx_REG32_ACCESS_TYPE regBase;
#if defined(MCM)
        regBase = (FTMRx_REG32_ACCESS_TYPE)&MCM->PLACR;
#elif defined(MCM0)
        regBase = (FTMRx_REG32_ACCESS_TYPE)&MCM0->PLACR;
#endif
        if (speculationStatus->instructionOption == kFLASH_prefetchSpeculationOptionDisable)
        {
            if (speculationStatus->dataOption == kFLASH_prefetchSpeculationOptionEnable)
            {
                return kStatus_FLASH_InvalidSpeculationOption;
            }
            else
            {
                *regBase |= MCM_PLACR_DFCS_MASK;
            }
        }
        else
        {
            *regBase &= ~MCM_PLACR_DFCS_MASK;
            if (speculationStatus->dataOption == kFLASH_prefetchSpeculationOptionEnable)
            {
                *regBase |= MCM_PLACR_EFDS_MASK;
            }
            else
            {
                *regBase &= ~MCM_PLACR_EFDS_MASK;
            }
        }
    }
#elif FLASH_PREFETCH_SPECULATION_IS_CONTROLLED_BY_FMC
    {
        FTMRx_REG32_ACCESS_TYPE regBase;
        uint32_t b0dpeMask, b0ipeMask;
#if defined(FMC_PFB01CR_B0DPE_MASK)
        regBase = (FTMRx_REG32_ACCESS_TYPE)&FMC->PFB01CR;
        b0dpeMask = FMC_PFB01CR_B0DPE_MASK;
        b0ipeMask = FMC_PFB01CR_B0IPE_MASK;
#elif defined(FMC_PFB0CR_B0DPE_MASK)
        regBase   = (FTMRx_REG32_ACCESS_TYPE)&FMC->PFB0CR;
        b0dpeMask = FMC_PFB0CR_B0DPE_MASK;
        b0ipeMask = FMC_PFB0CR_B0IPE_MASK;
#endif
        if (speculationStatus->instructionOption == kFLASH_prefetchSpeculationOptionEnable)
        {
            *regBase |= b0ipeMask;
        }
        else
        {
            *regBase &= ~b0ipeMask;
        }
        if (speculationStatus->dataOption == kFLASH_prefetchSpeculationOptionEnable)
        {
            *regBase |= b0dpeMask;
        }
        else
        {
            *regBase &= ~b0dpeMask;
        }

/* Invalidate Prefetch Speculation Buffer */
#if defined(FMC_PFB01CR_S_INV_MASK)
        FMC->PFB01CR |= FMC_PFB01CR_S_INV_MASK;
#elif defined(FMC_PFB01CR_S_B_INV_MASK)
        FMC->PFB01CR |= FMC_PFB01CR_S_B_INV_MASK;
#elif defined(FMC_PFB0CR_S_INV_MASK)
        FMC->PFB0CR |= FMC_PFB0CR_S_INV_MASK;
#elif defined(FMC_PFB0CR_S_B_INV_MASK)
        FMC->PFB0CR |= FMC_PFB0CR_S_B_INV_MASK;
#endif
    }
#endif /* FSL_FEATURE_FTMRx_MCM_FLASH_CACHE_CONTROLS */

    return kStatus_FLASH_Success;
}

/*!
 * brief Gets the PFlash prefetch speculation status.
 *
 * param speculationStatus  Speculation status returned by the PFlash IP.
 * retval #kStatus_FLASH_Success API was executed successfully.
 */
status_t FLASH_PflashGetPrefetchSpeculation(flash_prefetch_speculation_status_t *speculationStatus)
{
    (void)memset(speculationStatus, 0, sizeof(flash_prefetch_speculation_status_t));

    /* Assuming that all speculation options are enabled. */
    speculationStatus->instructionOption = kFLASH_prefetchSpeculationOptionEnable;
    speculationStatus->dataOption        = kFLASH_prefetchSpeculationOptionEnable;

#if FLASH_PREFETCH_SPECULATION_IS_CONTROLLED_BY_MCM
    {
        uint32_t value;
#if defined(MCM)
        value = MCM->PLACR;
#elif defined(MCM0)
        value   = MCM0->PLACR;
#endif
        if (0UL != (value & MCM_PLACR_DFCS_MASK))
        {
            /* Speculation buffer is off. */
            speculationStatus->instructionOption = kFLASH_prefetchSpeculationOptionDisable;
            speculationStatus->dataOption        = kFLASH_prefetchSpeculationOptionDisable;
        }
        else
        {
            /* Speculation buffer is on for instruction. */
            if (0UL == (value & MCM_PLACR_EFDS_MASK))
            {
                /* Speculation buffer is off for data. */
                speculationStatus->dataOption = kFLASH_prefetchSpeculationOptionDisable;
            }
        }
    }
#elif FLASH_PREFETCH_SPECULATION_IS_CONTROLLED_BY_FMC
    {
        uint32_t value;
        uint32_t b0dpeMask, b0ipeMask;
#if defined(FMC_PFB01CR_B0DPE_MASK)
        value = FMC->PFB01CR;
        b0dpeMask = FMC_PFB01CR_B0DPE_MASK;
        b0ipeMask = FMC_PFB01CR_B0IPE_MASK;
#elif defined(FMC_PFB0CR_B0DPE_MASK)
        value     = FMC->PFB0CR;
        b0dpeMask = FMC_PFB0CR_B0DPE_MASK;
        b0ipeMask = FMC_PFB0CR_B0IPE_MASK;
#endif
        if (0UL == (value & b0dpeMask))
        {
            /* Do not prefetch in response to data references. */
            speculationStatus->dataOption = kFLASH_prefetchSpeculationOptionDisable;
        }
        if (0UL == (value & b0ipeMask))
        {
            /* Do not prefetch in response to instruction fetches. */
            speculationStatus->instructionOption = kFLASH_prefetchSpeculationOptionDisable;
        }
    }
#endif

    return kStatus_FLASH_Success;
}

#if FLASH_DRIVER_IS_FLASH_RESIDENT
/*!
 * @brief Copy PIC of flash_run_command() to RAM
 */
static void copy_flash_run_command(flash_config_t *config)
{
    assert(sizeof(s_flashRunCommandFunctionCode) <= ((uint32_t)kFLASH_ExecuteInRamFunctionMaxSizeInWords * 4U));

    uint32_t flashRunCommand = config->executeInRamFunction.runCmdFuncAddr.commadAddr;

    /* copy required flash command to RAM */
    (void)memcpy((uint8_t *)flashRunCommand, (const uint8_t *)s_flashRunCommandFunctionCode,
                 sizeof(s_flashRunCommandFunctionCode));
}
#endif /* FLASH_DRIVER_IS_FLASH_RESIDENT */

/*!
 * @brief Flash Set Command
 *
 * This function is used to write the command sequence to the flash reg.
 */
void flash_set_command(uint32_t index, uint32_t fValue, uint32_t sValue)
{
    FTMRx->FCCOBIX = FTMRx_FCCOBIX_CCOBIX(index);
    FTMRx->FCCOBLO = FTMRx_FCCOBLO_CCOB(fValue);
    FTMRx->FCCOBHI = FTMRx_FCCOBHI_CCOB(sValue);
}

/*!
 * @brief Flash 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_FLASH_Success
 */
static status_t flash_command_sequence(flash_config_t *config)
{
    uint8_t registerValue;

#if FLASH_DRIVER_IS_FLASH_RESIDENT
    /* clear ACCERR & FPVIOL flag in flash status register */
    FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;

    status_t returnCode = flash_check_execute_in_ram_function_info(config);
    if (kStatus_FLASH_Success != returnCode)
    {
        return returnCode;
    }

    /* 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. */
    config->executeInRamFunction.runCmdFuncAddr.commadAddr += 1UL;
    callFlashRunCommand = config->executeInRamFunction.runCmdFuncAddr.callFlashCommand;

    /* We pass the FTMRx_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). */
    callFlashRunCommand((FTMRx_REG8_ACCESS_TYPE)(&FTMRx->FSTAT));
    config->executeInRamFunction.runCmdFuncAddr.commadAddr -= 1UL;
#else
#if FLASH_ENABLE_STALLING_FLASH_CONTROLLER
    MCM->PLACR |= MCM_PLACR_ESFC_MASK; /* enable stalling flash controller when
                                          flash is busy */
#endif
    /* clear ACCERR & FPVIOL flag in flash status register */
    FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;
    /* clear CCIF bit */
    FTMRx->FSTAT = FTMRx_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 (!(FTMRx->FSTAT & FTMRx_FSTAT_CCIF_MASK))
        ;
    {
    }
#endif /* FLASH_DRIVER_IS_FLASH_RESIDENT */
    /* Check error bits */
    /* Get flash status register value */
    registerValue = FTMRx->FSTAT;
    /* checking access error */
    if (0U != (registerValue & FTMRx_FSTAT_ACCERR_MASK))
    {
        return kStatus_FLASH_AccessError;
    }
    /* checking protection error */
    if (0U != (registerValue & FTMRx_FSTAT_FPVIOL_MASK))
    {
        return kStatus_FLASH_ProtectionViolation;
    }
    /* checking MGSTAT non-correctable error */
    if (0U != (registerValue & FTMRx_FSTAT_MGSTAT_MASK))
    {
        return kStatus_FLASH_CommandFailure;
    }

#if FLASH_SSD_IS_EEPROM_ENABLED
    registerValue = FTMRx->FERSTAT;

    if (0U != (registerValue & (FTMRx_FERSTAT_SFDIF_MASK)))
    {
        return kStatus_FLASH_EepromDoubleBitFault;
    }
    if (0U != (registerValue & (FTMRx_FERSTAT_DFDIF_MASK)))
    {
        return kStatus_FLASH_EepromSingleBitFault;
    }
#endif
    return kStatus_FLASH_Success;
}

/*!
 * @brief Flash common bit operation sequence Command Sequence
 */
#if FLASH_DRIVER_IS_FLASH_RESIDENT
static void flash_common_bit_operation_command_sequence(
    flash_config_t *config, FTMRx_REG32_ACCESS_TYPE base, uint32_t bitMask, uint32_t bitShift, uint32_t bitValue)
{
    uint32_t *flashCommonBitOperationAddr;
    flashCommonBitOperationAddr = &config->executeInRamFunction.CommonBitOperationAddr.bitOperationAddr;
    /* 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. */
    *flashCommonBitOperationAddr += 1UL;
    callFlashCommonBitOperation = config->executeInRamFunction.CommonBitOperationAddr.callCommonBitOperationCommand;
    /* Workround for some devices which doesn't need this function */
    callFlashCommonBitOperation((FTMRx_REG32_ACCESS_TYPE)base, bitMask, bitShift, bitValue);
    *flashCommonBitOperationAddr -= 1UL;
}
#endif /*FLASH_DRIVER_IS_FLASH_RESIDENT*/

#if FLASH_DRIVER_IS_FLASH_RESIDENT
/*!
 * @brief Copy PIC of flash_common_bit_operation() to RAM
 *
 */
static void copy_flash_common_bit_operation(flash_config_t *config)
{
    assert(sizeof(s_flashCommonBitOperationFunctionCode) <=
           ((uint32_t)kFLASH_ExecuteInRamFunctionMaxSizeInWords * 4UL));

    uint32_t *flashCommonBitOperationAddr;
    flashCommonBitOperationAddr = &config->executeInRamFunction.CommonBitOperationAddr.bitOperationAddr;
    /* 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. */
    (void)memcpy((uint8_t *)(*flashCommonBitOperationAddr), (const uint8_t *)s_flashCommonBitOperationFunctionCode,
                 sizeof(s_flashCommonBitOperationFunctionCode));
}
#endif /* FLASH_DRIVER_IS_FLASH_RESIDENT */

#if FLASH_CACHE_IS_CONTROLLED_BY_MCM
/*! @brief Performs the cache clear to the flash by MCM.*/
void mcm_flash_cache_clear(flash_config_t *config)
{
    FTMRx_REG32_ACCESS_TYPE regBase = (FTMRx_REG32_ACCESS_TYPE)&MCM0_CACHE_REG;

#if defined(MCM0) && defined(MCM1)
    if (config->FlashCacheControllerIndex == (uint8_t)kFLASH_CacheControllerIndexForCore1)
    {
        regBase = (FTMRx_REG32_ACCESS_TYPE)&MCM1_CACHE_REG;
    }
#endif

#if FLASH_DRIVER_IS_FLASH_RESIDENT
    flash_common_bit_operation_command_sequence(config, regBase, MCM_CACHE_CLEAR_MASK, MCM_CACHE_CLEAR_SHIFT, 1U);
#else  /* !FLASH_DRIVER_IS_FLASH_RESIDENT */
    *regBase |= MCM_CACHE_CLEAR_MASK;

    /* Memory barriers for good measure.
     * All Cache, Branch predictor and TLB maintenance operations before this
     * instruction complete */
    __ISB();
    __DSB();
#endif /* FLASH_DRIVER_IS_FLASH_RESIDENT */
}
#endif /* FLASH_CACHE_IS_CONTROLLED_BY_MCM */

#if FLASH_CACHE_IS_CONTROLLED_BY_FMC
/*! @brief Performs the cache clear to the flash by FMC.*/
void fmc_flash_cache_clear(void)
{
#if FLASH_DRIVER_IS_FLASH_RESIDENT
    FTMRx_REG32_ACCESS_TYPE regBase = (FTMRx_REG32_ACCESS_TYPE)0;
#if defined(FMC_PFB01CR_CINV_WAY_MASK)
    regBase = (FTMRx_REG32_ACCESS_TYPE)&FMC->PFB01CR;
    flash_common_bit_operation_command_sequence(config, regBase, FMC_PFB01CR_CINV_WAY_MASK, FMC_PFB01CR_CINV_WAY_SHIFT,
                                                0xFUL);
#else
    regBase = (FTMRx_REG32_ACCESS_TYPE)&FMC->PFB0CR;
    flash_common_bit_operation_command_sequence(config, regBase, FMC_PFB0CR_CINV_WAY_MASK, FMC_PFB0CR_CINV_WAY_SHIFT,
                                                0xFUL);
#endif
#else /* !FLASH_DRIVER_IS_FLASH_RESIDENT */
#if defined(FMC_PFB01CR_CINV_WAY_MASK)
    FMC->PFB01CR = (FMC->PFB01CR & ~FMC_PFB01CR_CINV_WAY_MASK) | FMC_PFB01CR_CINV_WAY(~0);
#else
    FMC->PFB0CR = (FMC->PFB0CR & ~FMC_PFB0CR_CINV_WAY_MASK) | FMC_PFB0CR_CINV_WAY(~0);
#endif
    /* Memory barriers for good measure.
     * All Cache, Branch predictor and TLB maintenance operations before this
     * instruction complete */
    __ISB();
    __DSB();
#endif /* FLASH_DRIVER_IS_FLASH_RESIDENT */
}
#endif /* FLASH_CACHE_IS_CONTROLLED_BY_FMC */

#if FLASH_PREFETCH_SPECULATION_IS_CONTROLLED_BY_FMC
/*! @brief Performs the prefetch speculation buffer clear to the flash by FMC.*/
void fmc_flash_prefetch_speculation_clear(void)
{
#if FLASH_DRIVER_IS_FLASH_RESIDENT
    FTMRx_REG32_ACCESS_TYPE regBase = (FTMRx_REG32_ACCESS_TYPE)0UL;
#if defined(FMC_PFB01CR_S_INV_MASK)
    regBase = (FTMRx_REG32_ACCESS_TYPE)&FMC->PFB01CR;
    flash_common_bit_operation_command_sequence(config, regBase, FMC_PFB01CR_S_INV_MASK, FMC_PFB01CR_S_INV_SHIFT, 1UL);
#elif defined(FMC_PFB01CR_S_B_INV_MASK)
    regBase = (FTMRx_REG32_ACCESS_TYPE)&FMC->PFB01CR;
    flash_common_bit_operation_command_sequence(config, regBase, FMC_PFB01CR_S_B_INV_MASK, FMC_PFB01CR_S_B_INV_SHIFT,
                                                1UL);
#elif defined(FMC_PFB0CR_S_INV_MASK)
    regBase = (FTMRx_REG32_ACCESS_TYPE)&FMC->PFB0CR;
    flash_common_bit_operation_command_sequence(config, regBase, FMC_PFB0CR_S_INV_MASK, FMC_PFB0CR_S_INV_SHIFT, 1UL);
#elif defined(FMC_PFB0CR_S_B_INV_MASK)
    regBase = (FTMRx_REG32_ACCESS_TYPE)&FMC->PFB0CR;
    flash_common_bit_operation_command_sequence(config, regBase, FMC_PFB0CR_S_B_INV_MASK, FMC_PFB0CR_S_B_INV_SHIFT,
                                                1UL);
#endif
#else /* !FLASH_DRIVER_IS_FLASH_RESIDENT */
#if defined(FMC_PFB01CR_S_INV_MASK)
    FMC->PFB01CR |= FMC_PFB01CR_S_INV_MASK;
#elif defined(FMC_PFB01CR_S_B_INV_MASK)
    FMC->PFB01CR |= FMC_PFB01CR_S_B_INV_MASK;
#elif defined(FMC_PFB0CR_S_INV_MASK)
    FMC->PFB0CR |= FMC_PFB0CR_S_INV_MASK;
#elif defined(FMC_PFB0CR_S_B_INV_MASK)
    FMC->PFB0CR |= FMC_PFB0CR_S_B_INV_MASK;
#endif
    /* Memory barriers for good measure.
     * All Cache, Branch predictor and TLB maintenance operations before this
     * instruction complete */
    __ISB();
    __DSB();
#endif /* FLASH_DRIVER_IS_FLASH_RESIDENT */
}
#endif /* FLASH_PREFETCH_SPECULATION_IS_CONTROLLED_BY_FMC */

/*!
 * @brief Flash Cache Clear
 *
 * This function is used to perform the cache and prefetch speculation clear to
 * the flash.
 */
void flash_cache_clear(flash_config_t *config)
{
    flash_cache_clear_process(config, kFLASH_CacheClearProcessPost);
}

/*!
 * @brief Flash Cache Clear Process
 *
 * This function is used to perform the cache and prefetch speculation clear
 * process to the flash.
 */
static void flash_cache_clear_process(flash_config_t *config, flash_cache_clear_process_t process)
{
#if FLASH_DRIVER_IS_FLASH_RESIDENT
    status_t returnCode = flash_check_execute_in_ram_function_info(config);
    if (kStatus_FLASH_Success != returnCode)
    {
        return;
    }
#endif /* FLASH_DRIVER_IS_FLASH_RESIDENT */

    /* We pass the FTMRx register address as a parameter to
     * flash_common_bit_operation() instead of using
     * pre-processed MACROs or a global variable in flash_common_bit_operation()
     * to make sure that flash_common_bit_operation() will be compiled into
     * position-independent code (PIC). */
    if (process == kFLASH_CacheClearProcessPost)
    {
#if FLASH_CACHE_IS_CONTROLLED_BY_MCM
        mcm_flash_cache_clear(config);
#endif
#if FLASH_CACHE_IS_CONTROLLED_BY_FMC
        fmc_flash_cache_clear();
#endif

#if FLASH_PREFETCH_SPECULATION_IS_CONTROLLED_BY_FMC
        fmc_flash_prefetch_speculation_clear();
#endif
    }
    if (process == kFLASH_CacheClearProcessPre)
    {
    }
}

#if FLASH_DRIVER_IS_FLASH_RESIDENT
/*! @brief Check whether flash execute-in-ram functions are ready  */
static status_t flash_check_execute_in_ram_function_info(flash_config_t *config)
{
    flash_execute_in_ram_function_config_t *flashExecuteInRamFunctionInfo;

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

    flashExecuteInRamFunctionInfo =
        (flash_execute_in_ram_function_config_t *)((void *)(config->flashExecuteInRamFunctionInfo));

    if ((config->flashExecuteInRamFunctionInfo != NULL) &&
        ((uint32_t)kFLASH_ExecuteInRamFunctionTotalNum == flashExecuteInRamFunctionInfo->activeFunctionCount))
    {
        return kStatus_FLASH_Success;
    }

    return kStatus_FLASH_ExecuteInRamFunctionNotReady;
}
#endif /* FLASH_DRIVER_IS_FLASH_RESIDENT */

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

    /* Verify the start and length are alignmentBaseline aligned, Global address
     * [1:0] must be 00.*/
    if ((0UL != (startAddress & (alignmentBaseline - 1UL))) || (0UL != (lengthInBytes & (alignmentBaseline - 1UL))))
    {
        return kStatus_FLASH_AlignmentError;
    }

    /* check for valid range of the target addresses */
    if ((startAddress >= config->PFlashBlockBase) &&
        ((startAddress + lengthInBytes) <= (config->PFlashBlockBase + config->PFlashTotalSize)))
    {
        return kStatus_FLASH_Success;
    }

    return kStatus_FLASH_AddressError;
}

/*! @brief Validates the range and alignment of the given address range.*/
#if FLASH_SSD_IS_EEPROM_ENABLED
static status_t EEPROM_check_range(flash_config_t *config,
                                   uint32_t startAddress,
                                   uint32_t lengthInBytes,
                                   uint32_t alignmentBaseline)
{
    if (config == NULL)
    {
        return kStatus_FLASH_InvalidArgument;
    }

    /* Verify the start and length are alignmentBaseline aligned, Global address
     * [1:0] must be 00.*/
    if ((0UL != (startAddress & (alignmentBaseline - 1UL))) || (0UL != (lengthInBytes & (alignmentBaseline - 1UL))))
    {
        return kStatus_FLASH_AlignmentError;
    }

    /* check for valid range of the target addresses */
    if ((startAddress >= config->EEpromBlockBase) &&
        ((startAddress + lengthInBytes) <= (config->EEpromBlockBase + config->EEpromTotalSize)))
    {
        return kStatus_FLASH_Success;
    }

    return kStatus_FLASH_AddressError;
}
#endif /* FLASH_SSD_IS_EEPROM_ENABLED */

/*! @brief Gets the right address, sector and block size of current flash type
 * which is indicated by address.*/
static status_t flash_get_matched_operation_info(flash_config_t *config,
                                                 uint32_t address,
                                                 flash_operation_config_t *info)
{
    if ((config == NULL) || (info == NULL))
    {
        return kStatus_FLASH_InvalidArgument;
    }

    /* Clean up info Structure*/
    (void)memset(info, 0, sizeof(flash_operation_config_t));

    info->convertedAddress = address - config->PFlashBlockBase;
    info->activeSectorSize = config->PFlashSectorSize;
    info->activeBlockSize  = config->PFlashTotalSize / config->PFlashBlockCount;
#if FLASH_SSD_IS_SECONDARY_FLASH_ENABLED
    if (config->FlashMemoryIndex == (uint8_t)kFLASH_MemoryIndexSecondaryFlash)
    {
#if FLASH_SSD_SECONDARY_FLASH_HAS_ITS_OWN_PROTECTION_REGISTER || FLASH_SSD_SECONDARY_FLASH_HAS_ITS_OWN_ACCESS_REGISTER
        /* When required by the command, address bit 23 selects between main flash
         * memory
         * (=0) and secondary flash memory (=1).*/
        info->convertedAddress += 0x800000UL;
#endif
        info->blockWriteUnitSize = SECONDARY_FLASH_FEATURE_PFLASH_BLOCK_WRITE_UNIT_SIZE;
    }
    else
#endif /* FLASH_SSD_IS_SECONDARY_FLASH_ENABLED */
    {
        info->blockWriteUnitSize = FSL_FEATURE_FLASH_PFLASH_BLOCK_WRITE_UNIT_SIZE;
    }

    info->sectorCmdAddressAligment  = FSL_FEATURE_FLASH_PFLASH_SECTOR_CMD_ADDRESS_ALIGMENT;
    info->sectionCmdAddressAligment = FSL_FEATURE_FLASH_PFLASH_SECTION_CMD_ADDRESS_ALIGMENT;
    info->programCmdAddressAligment = FSL_FEATURE_FLASH_PFLASH_PROGRAM_CMD_ADDRESS_ALIGMENT;

    return kStatus_FLASH_Success;
}

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

    return kStatus_FLASH_Success;
}

/*! @brief Gets the flash protection information.*/
static status_t flash_get_protection_info(flash_config_t *config, flash_protection_config_t *info)
{
    uint32_t pflashTotalSize, regionBase;
    uint32_t pflashProtectionValue, pflashFPLS;
#if defined(FTMRx_FPROT_FPHDIS_MASK)
    uint32_t pflashFPHS;
#endif

    if ((config == NULL) || (info == NULL))
    {
        return kStatus_FLASH_InvalidArgument;
    }

    /* Clean up info Structure*/
    (void)memset(info, 0, sizeof(flash_protection_config_t));

/* Note: KW40 has a secondary flash, but it doesn't have independent protection
 * register*/
#if FLASH_SSD_IS_SECONDARY_FLASH_ENABLED && (!FLASH_SSD_SECONDARY_FLASH_HAS_ITS_OWN_PROTECTION_REGISTER)
    pflashTotalSize = FSL_FEATURE_FLASH_PFLASH_BLOCK_COUNT * FSL_FEATURE_FLASH_PFLASH_BLOCK_SIZE +
                      FSL_FEATURE_FLASH_PFLASH_1_BLOCK_COUNT * FSL_FEATURE_FLASH_PFLASH_1_BLOCK_SIZE;
    regionBase = FSL_FEATURE_FLASH_PFLASH_START_ADDRESS;
#else
    pflashTotalSize = config->PFlashTotalSize;
    regionBase = config->PFlashBlockBase;
#endif

    /* Calculate the flash protection region */
    pflashProtectionValue = FTMRx->FPROT & 0xA4UL;
    pflashFPLS            = (FTMRx->FPROT & 0x03UL);
#if defined(FTMRx_FPROT_FPHDIS_MASK)
    pflashFPHS = (FTMRx->FPROT & 0x18UL) >> 3UL;
#endif

    switch (pflashProtectionValue)
    {
#if defined(FTMRx_FPROT_FPHDIS_MASK)
        case 0xA4UL:
            /*!< All flash region is not protected.*/
            info->lowRegionStart  = regionBase;
            info->lowRegionEnd    = regionBase;
            info->highRegionStart = regionBase + pflashTotalSize;
            info->highRegionEnd   = regionBase + pflashTotalSize;
            break;
#endif
        case 0x84UL:
            info->lowRegionStart = regionBase;
            info->lowRegionEnd   = regionBase;
#if defined(FTMRx_FPROT_FPHDIS_MASK)
            /*!< Only flash high region is protected.*/
            info->highRegionStart =
                (uint32_t)FSL_FEATURE_FLASH_PFLASH_HIGH_START_ADDRESS - ((1UL << pflashFPLS) << 10UL);
            info->highRegionEnd = FSL_FEATURE_FLASH_PFLASH_HIGH_START_ADDRESS;
#else
            /*!< All flash region is not protected.*/
            info->highRegionStart = regionBase + pflashTotalSize;
            info->highRegionEnd = regionBase + pflashTotalSize;
#endif
            break;
#if defined(FTMRx_FPROT_FPHDIS_MASK)
        case 0xA0UL:
            /*!< Only flash low region is protected.*/
            info->lowRegionStart  = regionBase;
            info->lowRegionEnd    = regionBase + ((2UL << pflashFPLS) << 10UL);
            info->highRegionStart = regionBase + pflashTotalSize;
            info->highRegionEnd   = regionBase + pflashTotalSize;
            break;
#endif
        case 0x80UL:
            info->lowRegionStart = regionBase;
#if defined(FTMRx_FPROT_FPHDIS_MASK)
            /*!< Flash high and low region are protected.*/
            info->lowRegionEnd = regionBase + ((2UL << pflashFPLS) << 10UL);
            info->highRegionStart =
                (uint32_t)FSL_FEATURE_FLASH_PFLASH_HIGH_START_ADDRESS - ((1UL << pflashFPLS) << 10UL);
            info->highRegionEnd = FSL_FEATURE_FLASH_PFLASH_HIGH_START_ADDRESS;
#else
            /*!< Only flash low region is protected.*/
            info->lowRegionEnd = regionBase + ((1UL << pflashFPLS) << 10UL);
            info->highRegionStart = regionBase + pflashTotalSize;
            info->highRegionEnd = regionBase + pflashTotalSize;
#endif
            break;
#if defined(FTMRx_FPROT_FPHDIS_MASK)
        case 0x24UL:
            /*!< All Flash region is protected.*/
            info->lowRegionStart  = regionBase;
            info->lowRegionEnd    = regionBase + pflashTotalSize;
            info->highRegionStart = regionBase;
            info->highRegionEnd   = regionBase + pflashTotalSize;
            break;
#endif
        case 0x04UL:
            info->lowRegionStart = regionBase;
#if defined(FTMRx_FPROT_FPHDIS_MASK)
            /*!< Flash high region is not protected.*/
            info->lowRegionEnd = (uint32_t)FSL_FEATURE_FLASH_PFLASH_HIGH_START_ADDRESS - ((1UL << pflashFPHS) << 10UL);
            info->highRegionStart = FSL_FEATURE_FLASH_PFLASH_HIGH_START_ADDRESS;

#else
            /*!< All Flash region is protected.*/
            info->lowRegionEnd = regionBase + pflashTotalSize;
            info->highRegionStart = regionBase;
#endif
            info->highRegionEnd = regionBase + pflashTotalSize;
            break;
#if defined(FTMRx_FPROT_FPHDIS_MASK)
        case 0x20UL:
            /*!< Flash low region is not protected.*/
            info->lowRegionStart  = regionBase + ((2UL << pflashFPLS) << 10UL);
            info->lowRegionEnd    = regionBase + pflashTotalSize;
            info->highRegionStart = regionBase + ((2UL << pflashFPLS) << 10UL);
            info->highRegionEnd   = regionBase + pflashTotalSize;
            break;
#endif
        case 0x00UL:
#if defined(FTMRx_FPROT_FPHDIS_MASK)
            /*!< Flash high and low region are not protected.*/
            info->lowRegionStart = regionBase + ((2UL << pflashFPLS) << 10UL);
            info->lowRegionEnd = (uint32_t)FSL_FEATURE_FLASH_PFLASH_HIGH_START_ADDRESS - ((1UL << pflashFPHS) << 10UL);
            info->highRegionStart = FSL_FEATURE_FLASH_PFLASH_HIGH_START_ADDRESS;
#else
            /*!< Flash low region is not protected.*/
            info->lowRegionStart = regionBase;
            info->lowRegionEnd = regionBase;
            info->highRegionStart = regionBase + ((1UL << pflashFPLS) << 10UL);
#endif
            info->highRegionEnd = regionBase + pflashTotalSize;
            break;
        default:
            /* Do Nothing*/
            break;
    }

    return kStatus_FLASH_Success;
}

/*! @brief Set the flash and eeprom to the specified user margin level.*/
status_t flash_setusermarginlevel(flash_config_t *config,
                                  uint32_t start,
                                  uint8_t iseeprom,
                                  flash_user_margin_value_t margin)
{
    uint32_t marginValue;
    status_t returnCode;
    if (config == NULL)
    {
        return kStatus_FLASH_InvalidArgument;
    }
    marginValue = (uint32_t)margin;
    flash_cache_clear_process(config, kFLASH_CacheClearProcessPre);

    /* clear error flags */
    FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;

    /* Write index to specify the command code to be loaded */
    flash_set_command(0UL, start >> 16UL, FTMRx_SET_USER_MARGIN_LEVEL);

    /* memory address bits[23:16] with bit23 = 0 for Flash block, 1 for EEPROM
     * block */
    if (0UL != iseeprom)
    {
        FTMRx->FCCOBLO |= FTMRx_FCCOBLO_CCOB(128UL);
    }
    flash_set_command(1UL, start, start >> 8UL);

    flash_set_command(2UL, marginValue, marginValue >> 8UL);

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

    flash_cache_clear(config);
    return returnCode;
}

/*! @brief Set the flash and eeprom to the specified factory margin level.*/
status_t flash_setfactorymarginlevel(flash_config_t *config,
                                     uint32_t start,
                                     uint8_t iseeprom,
                                     flash_factory_margin_value_t margin)
{
    uint32_t marginValue;
    status_t returnCode;
    if (config == NULL)
    {
        return kStatus_FLASH_InvalidArgument;
    }

    marginValue = (uint32_t)margin;
    flash_cache_clear_process(config, kFLASH_CacheClearProcessPre);
    /* clear error flags */
    FTMRx->FSTAT = FTMRx_FSTAT_ACCERR_MASK | FTMRx_FSTAT_FPVIOL_MASK;

    /* Write index to specify the command code to be loaded */
    flash_set_command(0UL, start >> 16UL, FTMRx_SET_FACTORY_MARGIN_LEVEL);
    /* memory address bits[23:16] with bit23 = 0 for Flash block, 1 for EEPROM
     * block */
    if (0UL != iseeprom)
    {
        FTMRx->FCCOBLO |= FTMRx_FCCOBLO_CCOB(128UL);
    }
    flash_set_command(1UL, start, start >> 8UL);

    flash_set_command(2UL, marginValue, marginValue >> 8UL);

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

    flash_cache_clear(config);
    return returnCode;
}
