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

#include "fsl_k4_flash.h"

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

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

#if defined(FLASH_DRIVER_IS_FLASH_RESIDENT) && FLASH_DRIVER_IS_FLASH_RESIDENT
/*!
 * @brief Constants for execute-in-RAM flash function.
 */
enum _ftfx_ram_func_constants
{
    Flash_RunCommandCodeSize     = 6U,  /*!< The flash_run_command code size of execute-in-RAM.*/
    Flash_ErsSequenceCommandSize = 25U, /*!< The flash_erase_sequence func code size of execute-in-RAM.*/
    Flash_PgmSequenceCommandSize = 30U, /*!< The flash_program_sequence func size of execute-in-RAM.*/
};
#endif
/*******************************************************************************
 * Prototypes
 ******************************************************************************/
/*! @brief Init IFR memory related info */
static status_t flash_check_param(
    flash_config_t *config, FMU_Type *base, uint32_t start, uint32_t lengthInBytes, uint32_t alignmentBaseline);
static status_t pflash_check_param(
    flash_config_t *config, FMU_Type *base, uint32_t *start, uint32_t lengthInBytes, uint32_t alignmentBaseline);
static status_t ifr_check_param(
    flash_config_t *config, FMU_Type *base, uint32_t *start, uint32_t lengthInBytes, uint32_t alignmentBaseline);
static status_t flash_check_user_key(uint32_t key);

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

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

status_t FLASH_Init(flash_config_t *config)
{
    status_t status = kStatus_Fail;

    if (config == NULL)
    {
        status = kStatus_FLASH_InvalidArgument;
    }
    else
    {
        /* CM33 flash */
        config->msf1Config[0].flashDesc.blockBase  = FLASH_FEATURE_PFLASH0_START_ADDRESS;
        config->msf1Config[0].flashDesc.blockCount = FLASH_FEATURE_PFLASH0_BLOCK_COUNT;

#if (defined(CPU_KW45B41Z82AFPA) || defined(CPU_KW45B41Z82AFTA) || defined(CPU_KW45B41Z83AFPA) || \
     defined(CPU_KW45B41Z83AFTA) || defined(CPU_KW45Z41082AFPA) || defined(CPU_KW45Z41082AFTA) || \
     defined(CPU_KW45Z41083AFPA) || defined(CPU_KW45Z41083AFTA) || defined(CPU_K32W1480VFTA))
        /* M33 flash size 1MB */
        config->msf1Config[0].flashDesc.totalSize =
            FLASH_FEATURE_PFLASH0_BLOCK_COUNT * FLASH_FEATURE_PFLASH0_BLOCK_SIZE;
#elif (defined(CPU_KW45B41Z52AFPA) || defined(CPU_KW45B41Z52AFTA) || defined(CPU_KW45B41Z53AFPA) || \
       defined(CPU_KW45B41Z53AFTA) || defined(CPU_KW45Z41052AFPA) || defined(CPU_KW45Z41052AFTA) || \
       defined(CPU_KW45Z41053AFPA) || defined(CPU_KW45Z41053AFTA))
        /* M33 flash size 512KB */
        config->msf1Config[0].flashDesc.totalSize =
            FLASH_FEATURE_PFLASH0_BLOCK_COUNT * FLASH_FEATURE_PFLASH0_BLOCK_SIZE_512KB;
#else
#error "No valid CPU defined!"
#endif

        config->msf1Config[0].ifrDesc.pflashIfr0Start   = FLASH_FEATURE_PFLASH0_IFR0_START_ADDRESS;
        config->msf1Config[0].ifrDesc.pflashIfr0MemSize = FLASH_FEATURE_PFLASH0_IFR0_SIZE;

        /* RF flash */
#if (defined(CPU_KW45B41Z82AFPA) || defined(CPU_KW45B41Z82AFTA) || defined(CPU_KW45B41Z83AFPA) || \
     defined(CPU_KW45B41Z83AFTA) || defined(CPU_KW45B41Z52AFPA) || defined(CPU_KW45B41Z52AFTA) || \
     defined(CPU_KW45B41Z53AFPA) || defined(CPU_KW45B41Z53AFTA) || defined(CPU_K32W1480VFTA))
        config->msf1Config[1].flashDesc.blockBase  = FLASH_FEATURE_PFLASH1_START_ADDRESS;
        config->msf1Config[1].flashDesc.blockCount = FLASH_FEATURE_PFLASH1_BLOCK_COUNT;
        config->msf1Config[1].flashDesc.totalSize =
            FLASH_FEATURE_PFLASH1_BLOCK_COUNT * FLASH_FEATURE_PFLASH1_BLOCK_SIZE;

        config->msf1Config[1].ifrDesc.pflashIfr0Start   = FLASH_FEATURE_PFLASH1_IFR0_START_ADDRESS;
        config->msf1Config[1].ifrDesc.pflashIfr0MemSize = FLASH_FEATURE_PFLASH1_IFR0_SIZE;

#elif (defined(CPU_KW45Z41082AFPA) || defined(CPU_KW45Z41082AFTA) || defined(CPU_KW45Z41083AFPA) || \
       defined(CPU_KW45Z41083AFTA) || defined(CPU_KW45Z41052AFPA) || defined(CPU_KW45Z41052AFTA) || \
       defined(CPU_KW45Z41053AFPA) || defined(CPU_KW45Z41053AFTA))
        config->msf1Config[1].flashDesc.blockBase  = FLASH_FEATURE_PFLASH1_START_ADDRESS;
        config->msf1Config[1].flashDesc.blockCount = 0U;
        config->msf1Config[1].flashDesc.totalSize  = 0U;

        config->msf1Config[1].ifrDesc.pflashIfr0Start   = FLASH_FEATURE_PFLASH1_IFR0_START_ADDRESS;
        config->msf1Config[1].ifrDesc.pflashIfr0MemSize = 0U;
#else
#error "No valid CPU defined!"
#endif

        status = kStatus_FLASH_Success;
    }

    return status;
}

status_t FLASH_Erase(flash_config_t *config, FMU_Type *base, uint32_t start, uint32_t lengthInBytes, uint32_t key)
{
    status_t status = kStatus_Fail;

    status = flash_check_param(config, base, start, lengthInBytes, FLASH_FEATURE_PHRASE_SIZE);
    if ((status == kStatus_FLASH_Success) && (flash_check_user_key(key) == kStatus_FLASH_Success))
    {
        uint32_t endAddress = start + lengthInBytes - 1U;

        while (start <= endAddress)
        {
            status = FLASH_CMD_EraseSector(base, start);
            if (kStatus_FLASH_Success != status)
            {
                break;
            }
            else
            {
                /* Increment to the next sector */
                start += FLASH_FEATURE_SECTOR_SIZE;
            }
        }
    }
    else
    {
        ; // MISRA
    }

    return status;
}

status_t FLASH_EraseAll(FMU_Type *base, uint32_t key)
{
    status_t status   = kStatus_Fail;
    FMU_Type *baseTmp = (FMU_Type *)((uint32_t)base & FLASH_ADDR_MASK);
#if defined(RF_FMU)
    if (base == NULL || (baseTmp != FLASH && baseTmp != NBU_FLASH))
#else
    if (base == NULL || baseTmp != FLASH)
#endif
    {
        status = kStatus_FLASH_InvalidArgument;
    }
    else
    {
        /* Validate the user key */
        status = flash_check_user_key(key);
    }

    if (kStatus_FLASH_Success == status)
    {
        status = FLASH_CMD_EraseAll(base);
    }
    else
    {
        ; // MISRA
    }

    return status;
}

status_t FLASH_Program(flash_config_t *config, FMU_Type *base, uint32_t start, uint8_t *src, uint32_t lengthInBytes)
{
    status_t status = kStatus_Fail;

    status = flash_check_param(config, base, start, lengthInBytes, FLASH_FEATURE_PHRASE_SIZE);
    if (status == kStatus_FLASH_Success)
    {
        // Align length to whole phrase.
        uint32_t alignedLength = ALIGN_DOWN(lengthInBytes, sizeof(uint8_t) * FLASH_FEATURE_PHRASE_SIZE);
        uint32_t extraBytes    = lengthInBytes - alignedLength;
        uint32_t *srcWord      = (uint32_t *)(uintptr_t)src;

        if (alignedLength > 0U)
        {
            uint32_t endAddress = start + alignedLength - 1U;
            while (start <= endAddress)
            {
                status = FLASH_CMD_ProgramPhrase(base, start, srcWord);
                if (kStatus_FLASH_Success != status)
                {
                    break;
                }
                else
                {
                    /* Increment to the next sector */
                    start += FLASH_FEATURE_PHRASE_SIZE;
                    srcWord += FLASH_FEATURE_PHRASE_SIZE_IN_WORD;
                }
            }
        }
        else
        {
            ; // MISRA
        }

        if ((kStatus_FLASH_Success == status) && (extraBytes > 0U))
        {
            uint32_t extraData[4] = {0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu};

            // Copy extra bytes to phrase buffer.
            union
            {
                uint32_t *src;
                const void *srcVoid;
            } srcPtr;
            srcPtr.src = srcWord;

            union
            {
                uint32_t *xData;
                void *xDataVoid;
            } xDataPtr;
            xDataPtr.xData = (uint32_t *)&extraData[0];

            (void)memcpy(xDataPtr.xDataVoid, srcPtr.srcVoid, extraBytes);

            status = FLASH_CMD_ProgramPhrase(base, start, extraData);
        }
    }
    else
    {
        ; // MISRA
    }

    return status;
}

status_t FLASH_ProgramPage(flash_config_t *config, FMU_Type *base, uint32_t start, uint8_t *src, uint32_t lengthInBytes)
{
    status_t status = kStatus_Fail;

    status = flash_check_param(config, base, start, lengthInBytes, FLASH_FEATURE_PAGE_SIZE);
    if (status == kStatus_FLASH_Success)
    {
        // Align length to whole phrase.
        uint32_t alignedLength = ALIGN_DOWN(lengthInBytes, sizeof(uint8_t) * FLASH_FEATURE_PAGE_SIZE);
        uint32_t extraBytes    = lengthInBytes - alignedLength;
        uint32_t *srcWord      = (uint32_t *)(uintptr_t)src;

        if (alignedLength > 0U)
        {
            uint32_t endAddress = start + alignedLength - 1U;
            while (start <= endAddress)
            {
                status = FLASH_CMD_ProgramPage(base, start, srcWord);
                if (kStatus_FLASH_Success != status)
                {
                    break;
                }
                else
                {
                    /* Increment to the next sector */
                    start += FLASH_FEATURE_PAGE_SIZE;
                    srcWord += FLASH_FEATURE_PAGE_SIZE_IN_WORD;
                }
            }
        }
        else
        {
            ; // MISRA
        }

        if ((kStatus_FLASH_Success == status) && (extraBytes > 0U))
        {
            uint32_t extraData[32] = {
                0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu,
                0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu,
                0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu,
                0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu};

            // Copy extra bytes to page buffer.
            union
            {
                uint32_t *src;
                const void *srcVoid;
            } srcPtr;
            srcPtr.src = srcWord;

            union
            {
                uint32_t *xData;
                void *xDataVoid;
            } xDataPtr;
            xDataPtr.xData = (uint32_t *)&extraData[0];

            (void)memcpy(xDataPtr.xDataVoid, srcPtr.srcVoid, extraBytes);

            status = FLASH_CMD_ProgramPage(base, start, extraData);
        }
    }
    else
    {
        ; // MISRA
    }

    return status;
}

status_t FLASH_VerifyErasePhrase(flash_config_t *config, FMU_Type *base, uint32_t start, uint32_t lengthInBytes)
{
    status_t status    = kStatus_Fail;
    uint32_t startaddr = start;

    status = pflash_check_param(config, base, &startaddr, lengthInBytes, FLASH_FEATURE_PHRASE_SIZE);
    if (status == kStatus_FLASH_Success)
    {
        uint32_t endAddress = startaddr + lengthInBytes - 1U;
        while (startaddr <= endAddress)
        {
            status = FLASH_CMD_VerifyErasePhrase(base, startaddr);
            if (kStatus_FLASH_Success != status)
            {
                break;
            }
            else
            {
                /* Increment to the next phrase */
                startaddr += FLASH_FEATURE_PHRASE_SIZE;
            }
        }
    }
    else
    {
        ; // MISRA
    }

    return status;
}

status_t FLASH_VerifyErasePage(flash_config_t *config, FMU_Type *base, uint32_t start, uint32_t lengthInBytes)
{
    status_t status    = kStatus_Fail;
    uint32_t startaddr = start;

    status = pflash_check_param(config, base, &startaddr, lengthInBytes, FLASH_FEATURE_PAGE_SIZE);
    if (status == kStatus_FLASH_Success)
    {
        uint32_t endAddress = startaddr + lengthInBytes - 1U;
        while (startaddr <= endAddress)
        {
            status = FLASH_CMD_VerifyErasePage(base, startaddr);
            if (kStatus_FLASH_Success != status)
            {
                break;
            }
            else
            {
                /* Increment to the next page */
                startaddr += FLASH_FEATURE_PAGE_SIZE;
            }
        }
    }
    else
    {
        ; // MISRA
    }

    return status;
}

status_t FLASH_VerifyEraseSector(flash_config_t *config, FMU_Type *base, uint32_t start, uint32_t lengthInBytes)
{
    status_t status    = kStatus_Fail;
    uint32_t startaddr = start;

    status = pflash_check_param(config, base, &startaddr, lengthInBytes, FLASH_FEATURE_SECTOR_SIZE);
    if (status == kStatus_FLASH_Success)
    {
        uint32_t endAddress = startaddr + lengthInBytes - 1U;
        while (startaddr <= endAddress)
        {
            status = FLASH_CMD_VerifyEraseSector(base, startaddr);
            if (kStatus_FLASH_Success != status)
            {
                break;
            }
            else
            {
                /* Increment to the next sector */
                startaddr += FLASH_FEATURE_SECTOR_SIZE;
            }
        }
    }
    else
    {
        ; // MISRA
    }

    return status;
}

status_t FLASH_VerifyEraseIFRPhrase(flash_config_t *config, FMU_Type *base, uint32_t start, uint32_t lengthInBytes)
{
    status_t status    = kStatus_Fail;
    uint32_t startaddr = start;

    status = ifr_check_param(config, base, &startaddr, lengthInBytes, FLASH_FEATURE_PHRASE_SIZE);
    if (status == kStatus_FLASH_Success)
    {
        uint32_t endAddress = startaddr + lengthInBytes - 1U;
        while (startaddr <= endAddress)
        {
            status = FLASH_CMD_VerifyEraseIFRPhrase(base, startaddr);
            if (kStatus_FLASH_Success != status)
            {
                break;
            }
            else
            {
                /* Increment to the next phrase */
                startaddr += FLASH_FEATURE_PHRASE_SIZE;
            }
        }
    }
    else
    {
        ; // MISRA
    }

    return status;
}

status_t FLASH_VerifyEraseIFRPage(flash_config_t *config, FMU_Type *base, uint32_t start, uint32_t lengthInBytes)
{
    status_t status    = kStatus_Fail;
    uint32_t startaddr = start;

    status = ifr_check_param(config, base, &startaddr, lengthInBytes, FLASH_FEATURE_PAGE_SIZE);
    if (status == kStatus_FLASH_Success)
    {
        uint32_t endAddress = startaddr + lengthInBytes - 1U;
        while (startaddr <= endAddress)
        {
            status = FLASH_CMD_VerifyEraseIFRPage(base, startaddr);
            if (kStatus_FLASH_Success != status)
            {
                break;
            }
            else
            {
                /* Increment to the next page */
                startaddr += FLASH_FEATURE_PAGE_SIZE;
            }
        }
    }
    else
    {
        ; // MISRA
    }

    return status;
}

status_t FLASH_VerifyEraseIFRSector(flash_config_t *config, FMU_Type *base, uint32_t start, uint32_t lengthInBytes)
{
    status_t status    = kStatus_Fail;
    uint32_t startaddr = start;

    status = ifr_check_param(config, base, &startaddr, lengthInBytes, FLASH_FEATURE_SECTOR_SIZE);
    if (status == kStatus_FLASH_Success)
    {
        uint32_t endAddress = startaddr + lengthInBytes - 1U;
        while (startaddr <= endAddress)
        {
            status = FLASH_CMD_VerifyEraseIFRSector(base, startaddr);
            if (kStatus_FLASH_Success != status)
            {
                break;
            }
            else
            {
                /* Increment to the next sector */
                startaddr += FLASH_FEATURE_SECTOR_SIZE;
            }
        }
    }
    else
    {
        ; // MISRA
    }

    return status;
}

status_t FLASH_VerifyEraseAll(FMU_Type *base)
{
    status_t status   = kStatus_Fail;
    FMU_Type *baseTmp = (FMU_Type *)((uint32_t)base & FLASH_ADDR_MASK);
#if defined(RF_FMU)
    if (base == NULL || ((baseTmp != FLASH) && (baseTmp != NBU_FLASH)))
#else
    if (base == NULL || baseTmp != FLASH)
#endif
    {
        status = kStatus_FLASH_InvalidArgument;
    }
    else
    {
        status = FLASH_CMD_VerifyEraseAll(base);
    }

    return status;
}

status_t FLASH_VerifyEraseBlock(flash_config_t *config, FMU_Type *base, uint32_t blockaddr)
{
    status_t status      = kStatus_Fail;
    uint32_t nsblockaddr = blockaddr & FLASH_ADDR_MASK;
    FMU_Type *baseTmp    = (FMU_Type *)((uint32_t)base & FLASH_ADDR_MASK);

#if defined(RF_FMU)
    if (config == NULL || base == NULL || ((baseTmp != FLASH) && (baseTmp != NBU_FLASH)))
#else
    if (config == NULL || base == NULL || baseTmp != FLASH)
#endif
    {
        status = kStatus_FLASH_InvalidArgument;
    }
    else if ((nsblockaddr != config->msf1Config[0].flashDesc.blockBase) &&
             (nsblockaddr != config->msf1Config[1].flashDesc.blockBase))
    {
        status = kStatus_FLASH_AddressError;
    }
    else
    {
        /* K4W1 M33 and NBU flash both have only one block, so 0U is sufficient here */
        status = FLASH_CMD_VerifyEraseBlock(base, 0U);
    }

    return status;
}

status_t Read_Into_MISR(
    flash_config_t *config, FMU_Type *base, uint32_t start, uint32_t ending, uint32_t *seed, uint32_t *signature)
{
    status_t status    = kStatus_Fail;
    uint32_t startAddr = start;

    if ((start >= ending) || (seed == NULL) ||
        (((ending + FLASH_FEATURE_PHRASE_SIZE) & (FLASH_FEATURE_PAGE_SIZE - 1U)) != 0u))
    {
        status = kStatus_FLASH_InvalidArgument;
    }
    else if ((ending & (FLASH_FEATURE_PHRASE_SIZE - 1U)) != 0u)
    {
        status = kStatus_FLASH_AlignmentError;
    }
    else
    {
        status = pflash_check_param(config, base, &startAddr, (ending - start), FLASH_FEATURE_PAGE_SIZE);
        if (status == kStatus_FLASH_Success)
        {
            uint32_t endAddr = startAddr + ending - start;
            status           = FLASH_CMD_ReadIntoMISR(base, startAddr, endAddr, seed, signature);
        }
        else
        {
            ; // MISRA
        }
    }

    return status;
}

status_t Read_IFR_Into_MISR(
    flash_config_t *config, FMU_Type *base, uint32_t start, uint32_t ending, uint32_t *seed, uint32_t *signature)
{
    status_t status    = kStatus_Fail;
    uint32_t startAddr = start;

    if ((start >= ending) || (seed == NULL) ||
        (((ending + FLASH_FEATURE_PHRASE_SIZE) & (FLASH_FEATURE_PAGE_SIZE - 1U)) != 0U))
    {
        status = kStatus_FLASH_InvalidArgument;
    }
    else if ((ending & (FLASH_FEATURE_PHRASE_SIZE - 1U)) != 0u)
    {
        status = kStatus_FLASH_AlignmentError;
    }
    else
    {
        status = ifr_check_param(config, base, &startAddr, (ending - start), FLASH_FEATURE_PAGE_SIZE);
        if (status == kStatus_FLASH_Success)
        {
            uint32_t endAddr = startAddr + ending - start;
            status           = FLASH_CMD_ReadIFRIntoMISR(base, startAddr, endAddr, seed, signature);
        }
        else
        {
            ; // MISRA
        }
    }

    return status;
}

status_t FLASH_GetProperty(flash_config_t *config, flash_property_tag_t whichProperty, uint32_t *value)
{
    status_t status = kStatus_FLASH_Success;

    switch (whichProperty)
    {
        case kFLASH_PropertyPflash0TotalSize:
            *value = config->msf1Config[0].flashDesc.totalSize;
            break;
        case kFLASH_PropertyPflash0BlockSize:
            *value = config->msf1Config[0].flashDesc.totalSize / config->msf1Config[0].flashDesc.blockCount;
            break;
        case kFLASH_PropertyPflash0BlockCount:
            *value = config->msf1Config[0].flashDesc.blockCount;
            break;
        case kFLASH_PropertyPflash0BlockBaseAddr:
            *value = config->msf1Config[0].flashDesc.blockBase;
            break;
        case kFLASH_PropertyPflash0SectorSize:
            *value = FLASH_FEATURE_SECTOR_SIZE;
            break;
        case kFLASH_PropertyPflash1TotalSize:
            *value = config->msf1Config[1].flashDesc.totalSize;
            break;
        case kFLASH_PropertyPflash1BlockSize:
            *value = config->msf1Config[1].flashDesc.totalSize / config->msf1Config[1].flashDesc.blockCount;
            break;
        case kFLASH_PropertyPflash1BlockCount:
            *value = config->msf1Config[1].flashDesc.blockCount;
            break;
        case kFLASH_PropertyPflash1BlockBaseAddr:
            *value = config->msf1Config[1].flashDesc.blockBase;
            break;
        default:
            status = kStatus_FLASH_UnknownProperty;
            /* To avoid MISRA-C 2012 rule 16.4 issue. */
            break;
    }
    return status;
}

#if defined(FLASH_DRIVER_IS_FLASH_RESIDENT) && (FLASH_DRIVER_IS_FLASH_RESIDENT == 1)
#if defined(__IAR_SYSTEMS_ICC__)
__ramfunc
#elif defined(__GNUC__)
__attribute__ ((section (".ramfunc")))
#endif
#endif
    void
    flash_cache_disable(void)
{
    SMSCM->OCMDR0 = (SMSCM->OCMDR0 & (~SMSCM_FLASH_CACHE_CTRL_MASK)) | SMSCM_FLASH_CACHE_CTRL(0x1);
    SMSCM->OCMDR0 = (SMSCM->OCMDR0 & (~SMSCM_FLASH_CACHE_CTRL_MASK)) | SMSCM_FLASH_CACHE_CTRL(0x8);
    SMSCM->OCMDR0 = (SMSCM->OCMDR0 & (~SMSCM_FLASH_SPECULATION_CTRL_MASK)) | SMSCM_FLASH_SPECULATION_CTRL(0x3);
    __ISB();
    __DSB();
}

#if defined(FLASH_DRIVER_IS_FLASH_RESIDENT) && (FLASH_DRIVER_IS_FLASH_RESIDENT == 1)
#if defined(__IAR_SYSTEMS_ICC__)
__ramfunc
#elif defined(__GNUC__)
__attribute__ ((section (".ramfunc")))
#endif
#endif
    void
    flash_cache_speculation_control(bool isPreProcess, FMU_Type *base)
{
    if (base == FLASH)
    {
        if (isPreProcess == false)
        {
            SMSCM->OCMDR0 = (SMSCM->OCMDR0 & (~SMSCM_FLASH_CACHE_CTRL_MASK)) | SMSCM_FLASH_CACHE_CTRL(0x1);
            SMSCM->OCMDR0 = (SMSCM->OCMDR0 & (~SMSCM_FLASH_SPECULATION_CTRL_MASK)) | SMSCM_FLASH_SPECULATION_CTRL(0x0);
        }
        else
        {
            SMSCM->OCMDR0 = (SMSCM->OCMDR0 & (~SMSCM_FLASH_SPECULATION_CTRL_MASK)) | SMSCM_FLASH_SPECULATION_CTRL(0x3);
        }
    }
#if defined(RF_FMU)
    else if (base == NBU_FLASH)
    {
        if (isPreProcess == false)
        {
            RF_FMCCFG->RFMCCFG = (RF_FMCCFG->RFMCCFG & (~NBU_FLASH_CACHE_CTRL_MASK)) | NBU_FLASH_CACHE_CTRL(0x1);
            RF_FMCCFG->RFMCCFG =
                (RF_FMCCFG->RFMCCFG & (~NBU_FLASH_SPECULATION_CTRL_MASK)) | NBU_FLASH_SPECULATION_CTRL(0x0);
        }
        else
        {
            RF_FMCCFG->RFMCCFG =
                (RF_FMCCFG->RFMCCFG & (~NBU_FLASH_SPECULATION_CTRL_MASK)) | NBU_FLASH_SPECULATION_CTRL(0x3);
        }
    }
#endif
    else
    {
        ; /* No action required */
    }
    /* Memory barriers for good measure.
     * All Cache, Branch predictor and TLB maintenance operations before this instruction complete */
    __ISB();
    __DSB();
}

static status_t flash_check_param(
    flash_config_t *config, FMU_Type *base, uint32_t start, uint32_t lengthInBytes, uint32_t alignmentBaseline)
{
    status_t status   = kStatus_FLASH_InvalidArgument;
    FMU_Type *baseTmp = (FMU_Type *)((uint32_t)base & FLASH_ADDR_MASK);

    if (lengthInBytes == 0U)
    {
        status = kStatus_FLASH_Success;
    }
#if defined(RF_FMU)
    else if (config == NULL || base == NULL || ((baseTmp != FLASH) && (baseTmp != NBU_FLASH)))
#else
    else if (config == NULL || base == NULL || baseTmp != FLASH)
#endif
    {
        status = kStatus_FLASH_InvalidArgument;
    }
    else if ((start & (alignmentBaseline - 1U)) != 0u)
    {
        /* Verify the start is alignmentBaseline aligned. */
        status = kStatus_FLASH_AlignmentError;
    }
    else
    {
        start = start & FLASH_ADDR_MASK;

        if (baseTmp == FLASH)
        {
            /* Validates the range of the given address */
            if ((start >= config->msf1Config[0].flashDesc.blockBase) &&
                ((start + lengthInBytes) <=
                 (config->msf1Config[0].flashDesc.blockBase + config->msf1Config[0].flashDesc.totalSize)))
            {
                status = kStatus_FLASH_Success;
            }
            else

                if ((start >= config->msf1Config[0].ifrDesc.pflashIfr0Start) &&
                    ((start + lengthInBytes) <=
                     (config->msf1Config[0].ifrDesc.pflashIfr0Start + config->msf1Config[0].ifrDesc.pflashIfr0MemSize)))
            {
                status = kStatus_FLASH_Success;
            }
            else
            {
                status = kStatus_FLASH_AddressError;
            }
        }
        else // if (base == NBU_FLASH || base == NBU_FLASH_NS)
        {
            /* Validates the range of the given address */
            if ((start >= config->msf1Config[1].flashDesc.blockBase) &&
                ((start + lengthInBytes) <=
                 (config->msf1Config[1].flashDesc.blockBase + config->msf1Config[1].flashDesc.totalSize)))
            {
                status = kStatus_FLASH_Success;
            }
            else

                if ((start >= config->msf1Config[1].ifrDesc.pflashIfr0Start) &&
                    ((start + lengthInBytes) <=
                     (config->msf1Config[1].ifrDesc.pflashIfr0Start + config->msf1Config[1].ifrDesc.pflashIfr0MemSize)))
            {
                status = kStatus_FLASH_Success;
            }
            else
            {
                status = kStatus_FLASH_AddressError;
            }
        }
    }

    return status;
}

static status_t pflash_check_param(
    flash_config_t *config, FMU_Type *base, uint32_t *start, uint32_t lengthInBytes, uint32_t alignmentBaseline)
{
    status_t status   = kStatus_FLASH_InvalidArgument;
    FMU_Type *baseTmp = (FMU_Type *)((uint32_t)base & FLASH_ADDR_MASK);

    if (lengthInBytes == 0U)
    {
        status = kStatus_FLASH_Success;
    }
#if defined(RF_FMU)
    else if ((config == NULL) || (base == NULL) || ((baseTmp != FLASH) && (baseTmp != NBU_FLASH)))
#else
    else if ((config == NULL) || (base == NULL) || (baseTmp != FLASH))
#endif
    {
        status = kStatus_FLASH_InvalidArgument;
    }
    else if ((*start & (alignmentBaseline - 1U)) != 0u)
    {
        /* Verify the start is alignmentBaseline aligned. */
        status = kStatus_FLASH_AlignmentError;
    }
    else
    {
        *start &= FLASH_ADDR_MASK;

        if (baseTmp == FLASH)
        {
            /* Validates the range of the given address */
            if ((*start >= config->msf1Config[0].flashDesc.blockBase) &&
                ((*start + lengthInBytes) <=
                 (config->msf1Config[0].flashDesc.blockBase + config->msf1Config[0].flashDesc.totalSize)))
            {
                *start -= config->msf1Config[0].flashDesc.blockBase;
                status = kStatus_FLASH_Success;
            }
            else
            {
                status = kStatus_FLASH_AddressError;
            }
        }
        else // if (base == NBU_FLASH || base == NBU_FLASH_NS)
        {
            /* Validates the range of the given address */
            if ((*start >= config->msf1Config[1].flashDesc.blockBase) &&
                ((*start + lengthInBytes) <=
                 (config->msf1Config[1].flashDesc.blockBase + config->msf1Config[1].flashDesc.totalSize)))
            {
                *start -= config->msf1Config[1].flashDesc.blockBase;
                status = kStatus_FLASH_Success;
            }
            else
            {
                status = kStatus_FLASH_AddressError;
            }
        }
    }

    return status;
}

static status_t ifr_check_param(
    flash_config_t *config, FMU_Type *base, uint32_t *start, uint32_t lengthInBytes, uint32_t alignmentBaseline)
{
    status_t status   = kStatus_FLASH_InvalidArgument;
    FMU_Type *baseTmp = (FMU_Type *)((uint32_t)base & FLASH_ADDR_MASK);

    if (lengthInBytes == 0U)
    {
        status = kStatus_FLASH_Success;
    }
#if defined(RF_FMU)
    else if (config == NULL || base == NULL || ((baseTmp != FLASH) && (baseTmp != NBU_FLASH)))
#else
    else if (config == NULL || base == NULL || baseTmp != FLASH)
#endif
    {
        status = kStatus_FLASH_InvalidArgument;
    }
    else

        /* Verify the start is alignmentBaseline aligned. */
        if ((*start & (alignmentBaseline - 1U)) != 0u)
    {
        status = kStatus_FLASH_AlignmentError;
    }
    else
    {
        *start &= FLASH_ADDR_MASK;

        if (baseTmp == FLASH)
        {
            if ((*start >= config->msf1Config[0].ifrDesc.pflashIfr0Start) &&
                ((*start + lengthInBytes) <=
                 (config->msf1Config[0].ifrDesc.pflashIfr0Start + config->msf1Config[0].ifrDesc.pflashIfr0MemSize)))
            {
                *start -= config->msf1Config[0].ifrDesc.pflashIfr0Start;
                status = kStatus_FLASH_Success;
            }
            else
            {
                status = kStatus_FLASH_AddressError;
            }
        }
        else // if (base == NBU_FLASH || base == NBU_FLASH_NS)
        {
            if ((*start >= config->msf1Config[1].ifrDesc.pflashIfr0Start) &&
                ((*start + lengthInBytes) <=
                 (config->msf1Config[1].ifrDesc.pflashIfr0Start + config->msf1Config[1].ifrDesc.pflashIfr0MemSize)))
            {
                *start -= config->msf1Config[1].ifrDesc.pflashIfr0Start;
                status = kStatus_FLASH_Success;
            }
            else
            {
                status = kStatus_FLASH_AddressError;
            }
        }
    }

    return status;
}

/*! @brief Validates the given user key for flash erase APIs.*/
static status_t flash_check_user_key(uint32_t key)
{
    status_t status = kStatus_Fail;

    /* Validate the user key */
    if (key != (uint32_t)kFLASH_ApiEraseKey)
    {
        status = kStatus_FLASH_EraseKeyError;
    }
    else
    {
        status = kStatus_FLASH_Success;
    }

    return status;
}
