/*
 * Copyright 2020-2022 Yuntu Microelectronics Co., Ltd.
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
/*!
 * @file flash_driver.c
 */

#include "flash_driver.h"

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

/*******************************************************************************
 * Internal Functions
 ******************************************************************************/
/*!
 * @brief Internal flash command to execute flash commands.
 *        This function will be copy to RAM section when system boot.
 *
 * @param[in] command Target flash command.
 */
START_FUNCTION_DECLARATION_RAMSECTION
__STATIC_FORCEINLINE status_t FLASH_LaunchCommandSequence(uint8_t command)
END_FUNCTION_DECLARATION_RAMSECTION

/*!
 * @brief Internal flash command to execute flash commands.
 *        This function will be copy to RAM section when system boot.
 *
 * @param[in] command Target flash command.
 */
 uint32_t FLASH_GetSectorSize(uint32_t dest);


/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_LaunchCommandSequence
 * Description   : Perform command write sequence on Flash.
 * It is internal function, called by driver APIs only.
 *
 *END**************************************************************************/
START_FUNCTION_DEFINITION_RAMSECTION
DISABLE_CHECK_RAMSECTION_FUNCTION_CALL
status_t FLASH_LaunchCommandSequence(uint8_t command)
{
    status_t ret = STATUS_SUCCESS;    /* Return code variable */
#if defined(EFM_AUTO_DISABLE_GLOBAL_INT)
    INT_SYS_DisableIRQGlobal();
#endif /* EFM_AUTO_DISABLE_GLOBAL_INT */

    /* clear pending status */
    EFM->STS = FEATURE_EFM_CMD_ERROR_MASK;
    EFM_UNLOCK_CMD_REGISTER(EFM);
    /* Write command register to launch command */
    EFM->CMD = command;

    while ((EFM_STS_DONE_MASK != (EFM->STS & EFM_STS_DONE_MASK)) &&
           (EFM_STS_ACCERR_MASK != (EFM->STS & EFM_STS_ACCERR_MASK)))
    {
            /* Wait untill Done bit is set */
    }

    if ( (FEATURE_EFM_CMD_ERROR_MASK & EFM->STS) != 0U)
    {
        ret = STATUS_ERROR;
    }

#if defined(FEATURE_SOC_SUPPORT_CACHE) && FEATURE_SOC_SUPPORT_CACHE
    /* Invalidate flash cache */
    CACHE->CACR |= CACHE_CACR_CMDREQ_MASK | CACHE_CACR_IW1_MASK | CACHE_CACR_IW0_MASK;
    while (CACHE->CACR & CACHE_CACR_CMDREQ_MASK)
        ;
    __DSB();
    __ISB();
#endif /* FEATURE_SOC_SUPPORT_CACHE */

#if defined(__DCACHE_PRESENT) && __DCACHE_PRESENT
    if (SCB->CCR & SCB_CCR_DC_Msk)
    {
        SCB_CleanInvalidateDCache();
    }
#endif

#if defined(EFM_AUTO_DISABLE_GLOBAL_INT)
    INT_SYS_EnableIRQGlobal();
#endif /* EFM_AUTO_DISABLE_GLOBAL_INT */

    return ret;
}
ENABLE_CHECK_RAMSECTION_FUNCTION_CALL
END_FUNCTION_DEFINITION_RAMSECTION

/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_GetSectorSize
 * Description   : Get flash sector size based on the destination address.
 * It is internal function, called by driver APIs only.
 *
 *END**************************************************************************/
uint32_t FLASH_GetSectorSize(uint32_t dest)
{
    uint32_t sectorSize = 0;
    if (dest < FEATURE_EFM_MAIN_ARRAY_END_ADDRESS)
    {
        /* Flash main array */
        sectorSize = FEATURE_EFM_MAIN_ARRAY_SECTOR_SIZE;
    }
#if FEATURE_EFM_HAS_DATA_FLASH
    else if ((dest < FEATURE_EFM_DATA_ARRAY_END_ADDRESS)
            && (dest >= FEATURE_EFM_DATA_ARRAY_START_ADDRESS))
    {
        /* Flash data array */
        sectorSize = FEATURE_EFM_DATA_ARRAY_SECTOR_SIZE;
    }
#endif
#if FEATURE_EFM_HAS_NVR_FLASH
    else if ((dest < FEATURE_EFM_NVR_ARRAY_END_ADDRESS)
             && (dest >= FEATURE_EFM_NVR_ARRAY_START_ADDRESS))
    {
        /* Flash NVR array */
        sectorSize = FEATURE_EFM_NVR_ARRAY_SECTOR_SIZE;
    }
#endif
    return sectorSize;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_Init
 * Description   : Initializes the flash driver.
 * This function is called by the driver initialization function.
 *
 *END**************************************************************************/
status_t FLASH_DRV_Init(void)
{
    /* During initialization, the PRESCALER in the EFM CTRL register 
       MUST be configured to match the current clock configuration. */
#ifdef EFM_CTRL_CMD_VERIFY_EN_MASK
    /* This is confirmed by hardware. Enabling this may take more time.  
       If manual read confirmation is performed later, it can also be disabled. */
    /* Enable command verify */
    EFM->CTRL |= EFM_CTRL_CMD_VERIFY_EN_MASK;
#endif /* EFM_CTRL_CMD_VERIFY_EN_MASK */
    return STATUS_SUCCESS;
}


/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_Deinit
 * Description   : De-initializes the flash driver.
 * This function is called by the driver de-initialization function.
 *
 *END**************************************************************************/
status_t FLASH_DRV_Deinit(void)
{
    /* Nothing to do here */
    return STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_EraseBlock
 * Description   : Erases a block of Flash memory.
 * This API always returns STATUS_SUCCESS if size provided by the user is
 * zero regardless of the input validation.
 *
 * Implements    : FLASH_DRV_EraseBlock_Activity
 *END**************************************************************************/
status_t FLASH_DRV_EraseBlock(uint32_t dest)
{
    status_t status;       /* Return code variable */
    DEV_ASSERT((dest & (FEATURE_EFM_FLASH_MIN_SECTOR_SIZE - 1)) == 0U);
    /* Check IDLE to verify the previous command is completed */
    if (0U == (EFM->STS & EFM_STS_IDLE_MASK))
    {
        status = STATUS_BUSY;
    }
    else
    {
#if FEATURE_EFM_HAS_ERASE_TIMING_UNION
        EFM->TIMING2 = FEATURE_EFM_BLOCK_ERASE_TIMING;
#endif
#ifndef EFM_CTRL_WE_MASK
            /* Add NOP command to clear write buffer */
            EFM->CMD = 0;
#endif
        EFM->STS = FEATURE_EFM_CMD_ERROR_MASK | EFM_STS_DONE_MASK;
        EFM_ENABLE_WE_COMMAND(EFM);
        /* Passing parameter to the command */
        *(volatile uint32_t*)dest = 0x12345678;
#ifdef EFM_STS_ARRAY_SELECTED_MASK
        while (EFM_STS_ARRAY_SELECTED_MASK != (EFM->STS & EFM_STS_ARRAY_SELECTED_MASK))
            ;
#endif /* EFM_STS_SET_ADDR_MASK */

#ifdef EFM_STS_SET_ADDR_MASK
        while (EFM_STS_SET_ADDR_MASK != (EFM->STS & EFM_STS_SET_ADDR_MASK))
            ;
#endif /* EFM_STS_SET_ADDR_MASK */
        EFM_DISABLE_WE_COMMAND(EFM);
        /* Calling flash command sequence function to execute the command */
        status = FLASH_LaunchCommandSequence(FEATURE_EFM_ERASE_BLOCK_CMD_CODE);
    }
    return status;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_EraseSector
 * Description   : Erases one or more sectors in P-Flash or D-Flash memory.
 * This API always returns STATUS_SUCCESS if size provided by the user is
 * zero regardless of the input validation.
 *
 * Implements    : FLASH_DRV_EraseSector_Activity
 *END**************************************************************************/
status_t FLASH_DRV_EraseSector(uint32_t dest, uint32_t size)
{
    status_t status = STATUS_SUCCESS;   /* Return code variable */
    uint32_t sectorSize;                /* Size of one sector   */
    uint32_t tempSize = size;           /* Temporary of size variation */
    sectorSize = FLASH_GetSectorSize(dest);
    DEV_ASSERT((dest & (sectorSize - 1)) == 0U);
    DEV_ASSERT((size & (sectorSize - 1)) == 0U);

    while ((tempSize > 0U) && (STATUS_SUCCESS == status) && (sectorSize > 0U))
    {
        /* Check IDLE to verify the previous command is completed */
        if (0U == (EFM->STS & EFM_STS_IDLE_MASK))
        {
            status = STATUS_BUSY;
        }
        else
        {
#if FEATURE_EFM_HAS_ERASE_TIMING_UNION
            EFM->TIMING2 = FEATURE_EFM_SECTOR_ERASE_TIMING;
#endif
#ifndef EFM_CTRL_WE_MASK
            /* Add NOP command to clear write buffer */
            EFM->CMD = 0;
#endif
            EFM->STS = FEATURE_EFM_CMD_ERROR_MASK | EFM_STS_DONE_MASK;
            EFM_ENABLE_WE_COMMAND(EFM);
            /* Passing parameter to the command */
            *(volatile uint32_t *)dest = 0x12345678;
#ifdef EFM_STS_ARRAY_SELECTED_MASK
            while (EFM_STS_ARRAY_SELECTED_MASK !=
                   (EFM->STS & EFM_STS_ARRAY_SELECTED_MASK))
              ;
#endif /* EFM_STS_SET_ADDR_MASK */

#ifdef EFM_STS_SET_ADDR_MASK
            while (EFM_STS_SET_ADDR_MASK != (EFM->STS & EFM_STS_SET_ADDR_MASK))
              ;
#endif /* EFM_STS_SET_ADDR_MASK */
            EFM_DISABLE_WE_COMMAND(EFM);
            /* Calling flash command sequence function to execute the command */
            status = FLASH_LaunchCommandSequence(FEATURE_EFM_ERASE_SECTOR_CMD_CODE);
        }
        tempSize -= sectorSize;
        dest += sectorSize;
        sectorSize = FLASH_GetSectorSize(dest);
    }

    return status;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_Program
 * Description   : Program command on flash
 * This API always returns STATUS_SUCCESS if size provided by user is
 * zero regardless of the input validation.
 *
 * Implements    : FLASH_DRV_Program_Activity
 *END**************************************************************************/
status_t FLASH_DRV_Program(uint32_t dest, uint32_t size, const void * pData)
{
    status_t ret = STATUS_SUCCESS;    /* Return code variable */
    uint32_t * efm_dest;
    uint32_t * efm_data;
    uint8_t i;
    DEV_ASSERT((dest & (FEATURE_EFM_WRITE_UNIT_SIZE - 1)) == 0U);
    DEV_ASSERT(((uint32_t)pData & 0x03U) == 0U);
    DEV_ASSERT((size & (FEATURE_EFM_WRITE_UNIT_SIZE - 1)) == 0U);

    efm_dest = (uint32_t *) dest;
    efm_data = (uint32_t *) pData;

    while ((size > 0U) && (STATUS_SUCCESS == ret))
    {
        /* Check IDLE to verify the previous command is completed */
        if (0U == (EFM->STS & EFM_STS_IDLE_MASK))
        {
            ret = STATUS_EFM_BUSY;
        }
        else
        {
#ifndef EFM_CTRL_WE_MASK
            /* Add NOP command to clear write buffer */
            EFM->CMD = 0;
#endif
            EFM->STS = FEATURE_EFM_CMD_ERROR_MASK | EFM_STS_DONE_MASK;
            EFM_ENABLE_WE_COMMAND(EFM);
            /* Passing parameter to the command */
            for (i = 0U; i < FEATURE_EFM_WRITE_UNIT_WORD_SIZE; i++) {
              efm_dest[i] = efm_data[i];
#ifdef EFM_STS_ARRAY_SELECTED_MASK
              while (EFM_STS_ARRAY_SELECTED_MASK !=
                     (EFM->STS & EFM_STS_ARRAY_SELECTED_MASK))
                ;
#endif /* EFM_STS_SET_ADDR_MASK */
            }
#ifdef EFM_STS_SET_ADDR_MASK
            while (EFM_STS_SET_ADDR_MASK != (EFM->STS & EFM_STS_SET_ADDR_MASK))
              ;
#endif /* EFM_STS_SET_ADDR_MASK */
            EFM_DISABLE_WE_COMMAND(EFM);
            /* Calling flash command sequence function to execute the command */
            ret = FLASH_LaunchCommandSequence(FEATURE_EFM_PROGRAM_CMD_CODE);

            /* Update destination address for next iteration */
            efm_dest += FEATURE_EFM_WRITE_UNIT_WORD_SIZE;
            /* Update size for next iteration */
            size -= FEATURE_EFM_WRITE_UNIT_SIZE;
            /* Increment the source address by 1 */
            efm_data += FEATURE_EFM_WRITE_UNIT_WORD_SIZE;
        }
    }

    return ret;
}


/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_CheckSum
 * Description   : Performs 32 bit sum of each byte data over a specified Flash
 * memory range without carry which provides rapid method for checking data integrity.
 * The callback time period of this API is determined via FLASH_CALLBACK_CS macro in the
 * flash_driver.h which is used as a counter value for the CallBack() function calling in
 * this API. This value can be changed as per the user requirement. User can change this value
 * to obtain the maximum permissible callback time period.
 * This API always returns STATUS_SUCCESS if size provided by user is zero regardless of the input
 * validation.
 *
 * Implements    : FLASH_DRV_CheckSum_Activity
 *END**************************************************************************/
status_t FLASH_DRV_CheckSum( uint32_t dest, uint32_t size, uint32_t * pSum)
{
    DEV_ASSERT(pSum != NULL);
    status_t ret = STATUS_SUCCESS;      /* Return code variable           */
    uint32_t data;                      /* Data read from Flash address   */
    uint32_t tempSize = size;           /* Temporary of size variation    */

    *pSum = 0U;
    /* Doing sum operation */
    while (tempSize > 0U)
    {
        data = *(uint8_t *)(dest);
        *pSum += data;
        dest += 1U;
        tempSize -= 1U;
    }

    return ret;
}


/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_EnableCmdCompleteInterrupt
 * Description   : Enable the command complete interrupt is generated when
 * an EFM command completes.
 *
 * Implements    : FLASH_DRV_EnableCmdCompleteInterrupt_Activity
 *END**************************************************************************/
status_t FLASH_DRV_EnableCmdCompleteInterrupt(void)
{
    /* Enable the command complete interrupt */
    EFM->CTRL |= EFM_CTRL_DONEIE_MASK;

    return STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_DisableCmdCompleteInterrupt
 * Description   : Disable the command complete interrupt.
 *
 * Implements    : FLASH_DRV_DisableCmdCompleteInterrupt_Activity
 *END**************************************************************************/
void FLASH_DRV_DisableCmdCompleteInterrupt(void)
{
    /* Disable the command complete interrupt */
    EFM->CTRL &= ~EFM_CTRL_DONEIE_MASK;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_EnableReadCollisionInterrupt
 * Description   : Enable the read collision error interrupt generation when an
 * EFM read collision error occurs.
 *
 * Implements    : FLASH_DRV_EnableReadCollisionInterrupt_Activity
 *END**************************************************************************/
status_t FLASH_DRV_EnableReadCollisionInterrupt(void)
{
    /* Enable the read collision error interrupt */
    EFM->CTRL |= EFM_CTRL_ACCERRIE_MASK;

    return STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_DisableReadCollisionInterrupt
 * Description   : Disable the read collision error interrupt
 *
 * Implements    : FLASH_DRV_DisableReadCollisionInterrupt_Activity
 *END**************************************************************************/
void FLASH_DRV_DisableReadCollisionInterrupt(void)
{
    /* Disable the read collision error interrupt */
    EFM->CTRL &= ~EFM_CTRL_ACCERRIE_MASK;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_EnableDoubleBitFaultInterrupt
 * Description   : Enable the platform Flash double bit fault detect interrupt 
 * generation when an uncorrectable ECC fault is detected during a valid flash 
 * read access from the platform flash controller.
 *
 * Implements    : FLASH_DRV_EnableDoubleBitFaultInterrupt_Activity
 *END**************************************************************************/
status_t FLASH_DRV_EnableDoubleBitFaultInterrupt(void)
{
    /* Enable the double bit fault detect interrupt */
    EFM->CTRL |= EFM_CTRL_UNRECOVERRIE_MASK;
    return STATUS_SUCCESS;
}

/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_DisableDoubleBitFaultInterrupt
 * Description   : Disable the platform Flash double bit fault detect interrupt
 *
 * Implements    : FLASH_DRV_DisableDoubleBitFaultInterrupt_Activity
 *END**************************************************************************/
void FLASH_DRV_DisableDoubleBitFaultInterrupt(void)
{
    /* Disable the double bit fault detect interrupt */
    EFM->CTRL &= ~EFM_CTRL_UNRECOVERRIE_MASK;
}

#ifdef FEATURE_EFM_ERASE_NVR_CMD_CODE
/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_EraseNVR
 * Description   : Program NVR command on flash
 * This API always returns STATUS_SUCCESS if size provided by user is
 * zero regardless of the input validation.
 *
 * Implements    : FLASH_DRV_EraseNVR_Activity
 *END**************************************************************************/
status_t FLASH_DRV_EraseNVR(uint32_t address)
{
    status_t status;    /* Return code variable */
    EFM->STS = FEATURE_EFM_CMD_ERROR_MASK | EFM_STS_DONE_MASK;
    EFM->NVR_ADDR = address;
    /* Calling flash command sequence function to execute the command */
    status = FLASH_LaunchCommandSequence(FEATURE_EFM_ERASE_NVR_CMD_CODE);
    return status;
}
#endif /* FEATURE_EFM_ERASE_NVR_CMD_CODE */


#ifdef FEATURE_EFM_PROGRAM_NVR_CMD_CODE
/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_ProgramNVR
 * Description   : Program NVR command on flash
 * This API always returns STATUS_SUCCESS if size provided by user is
 * zero regardless of the input validation.
 *
 * Implements    : FLASH_DRV_ProgramNVR_Activity
 *END**************************************************************************/
status_t FLASH_DRV_ProgramNVR( uint32_t address, uint32_t size, const void * pData)
{
    status_t ret = STATUS_SUCCESS;    /* Return code variable */
    uint8_t i;
    uint32_t * efm_data;
    efm_data = (uint32_t *) pData;
    while ((size > 0U) && (STATUS_SUCCESS == ret))
    {
        /* Check IDLE to verify the previous command is completed */
        if (0U == (EFM->STS & EFM_STS_IDLE_MASK))
        {
            ret = STATUS_EFM_BUSY;
        }
        else
        {
            EFM->STS = FEATURE_EFM_CMD_ERROR_MASK | EFM_STS_DONE_MASK;
            EFM->NVR_ADDR = address;
            /* Passing parameter to the command */
            for (i = 0U; i < FEATURE_EFM_WRITE_UNIT_WORD_SIZE; i++)
            {
                EFM->NVR_DATA[i] = efm_data[i];
            }

            /* Calling flash command sequence function to execute the command */
            ret = FLASH_LaunchCommandSequence(FEATURE_EFM_PROGRAM_NVR_CMD_CODE);

            /* Update size for next iteration */
            size -= FEATURE_EFM_WRITE_UNIT_SIZE;
            /* Update address for next iteration */
            address += FEATURE_EFM_WRITE_UNIT_SIZE;
            /* Increment the source address by 1 */
            efm_data += FEATURE_EFM_WRITE_UNIT_WORD_SIZE;
        }
    }
    return ret;
}
#endif /* FEATURE_EFM_PROGRAM_NVR_CMD_CODE */

#ifdef FEATURE_EFM_READ_NVR_CMD_CODE
/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_ReadNVR
 * Description   : Read NVR command on flash
 * This API always returns STATUS_SUCCESS if size provided by user is
 * zero regardless of the input validation.
 *
 * Implements    : FLASH_DRV_ReadNVR_Activity
 *END**************************************************************************/
status_t FLASH_DRV_ReadNVR(uint32_t address, uint32_t size, void * dest)
{
    status_t ret = STATUS_SUCCESS;    /* Return code variable */
    uint8_t i;
    uint32_t *pData = (uint32_t *)dest;
    while ((size > 0U) && (STATUS_SUCCESS == ret))
    {
        /* Check IDLE to verify the previous command is completed */
        if (0U == (EFM->STS & EFM_STS_IDLE_MASK))
        {
            ret = STATUS_EFM_BUSY;
        }
        else
        {
            EFM->STS = FEATURE_EFM_CMD_ERROR_MASK | EFM_STS_DONE_MASK;
            EFM->NVR_ADDR = address;
            /* Calling flash command sequence function to execute the command */
            ret = FLASH_LaunchCommandSequence(FEATURE_EFM_READ_NVR_CMD_CODE);
            if (STATUS_SUCCESS != ret){
                break;
            }
            /* Passing parameter to the command */
            for (i = 0U; i < FEATURE_EFM_WRITE_UNIT_WORD_SIZE; i++)
            {
                pData[i] = EFM->NVR_DATA[i];
            }

            /* Update size for next iteration */
            size -= FEATURE_EFM_WRITE_UNIT_SIZE;
            /* Update address for next iteration */
            address += FEATURE_EFM_WRITE_UNIT_SIZE;
            /* Increment the source address by 1 */
            pData += FEATURE_EFM_WRITE_UNIT_WORD_SIZE;
        }
    }
    return ret;
}
#endif /* FEATURE_EFM_READ_NVR_CMD_CODE */

#ifdef FEATURE_EFM_BOOT_SWAP_CMD_CODE
static status_t FLASH_BootSwap(void)
{
    status_t ret;    /* Return code variable */
    /* Check IDLE to verify the previous command is completed */
    if (0U == (EFM->STS & EFM_STS_IDLE_MASK))
    {
        ret = STATUS_EFM_BUSY;
    }
    else
    {
#if defined(FEATURE_EFM_BOOT_SWAP_TAG_ADDR)
        EFM_ENABLE_WE_COMMAND(EFM);
        /* Passing parameter to the command */
        *(volatile uint32_t *)FEATURE_EFM_BOOT_SWAP_TAG_ADDR = 0x12345678;
#ifdef EFM_STS_ARRAY_SELECTED_MASK
        while (EFM_STS_ARRAY_SELECTED_MASK !=
               (EFM->STS & EFM_STS_ARRAY_SELECTED_MASK))
            ;
#endif /* EFM_STS_SET_ADDR_MASK */

#ifdef EFM_STS_SET_ADDR_MASK
        while (EFM_STS_SET_ADDR_MASK != (EFM->STS & EFM_STS_SET_ADDR_MASK))
            ;
#endif /* EFM_STS_SET_ADDR_MASK */
        EFM_DISABLE_WE_COMMAND(EFM);
#endif
        EFM->STS = FEATURE_EFM_CMD_ERROR_MASK | EFM_STS_DONE_MASK;
        /* Calling flash command sequence function to execute the command */
        ret = FLASH_LaunchCommandSequence(FEATURE_EFM_BOOT_SWAP_CMD_CODE);
    }
    return ret;
}


/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_BootSwap
 * Description   : Swap MCU boot flash blocks
 *
 * Implements    : FLASH_DRV_BootSwap_Activity
 *END**************************************************************************/
status_t FLASH_DRV_BootSwap(void)
{
    status_t ret = STATUS_SUCCESS;    /* Return code variable */
    /* Check IDLE to verify the previous command is completed */
    if (0U == (EFM->STS & EFM_STS_IDLE_MASK))
    {
        ret = STATUS_EFM_BUSY;
    }
    else
    {
        if(STATUS_SUCCESS != FLASH_BootSwap()){
#ifdef EFM_BOOT_SWAP_UNLOCK
            EFM_BOOT_SWAP_UNLOCK();
#endif /* EFM_BOOT_SWAP_UNLOCK */
#ifdef FEATURE_EFM_ERASE_NVR_CMD_CODE
            ret = FLASH_DRV_EraseNVR(FEATURE_EFM_BOOT_NVR_ADDR);
#else
            ret = FLASH_DRV_EraseSector(FEATURE_EFM_BOOT_SWAP_TAG_ADDR, FEATURE_EFM_NVR_ARRAY_SECTOR_SIZE);
#ifdef EFM_BOOT_SWAP_LOCK
            EFM_BOOT_SWAP_LOCK();
#endif /* EFM_BOOT_SWAP_LOCK */
#endif /* FEATURE_EFM_ERASE_NVR_CMD_CODE */
            if(STATUS_SUCCESS == ret){
                ret = FLASH_BootSwap();
            }
        }
    }
    return ret;
}
#endif


#ifdef FEATURE_EFM_LOAD_AES_KEY_CMD_CODE
/*FUNCTION**********************************************************************
 *
 * Function Name : FLASH_DRV_LoadAESKey
 * Description   : Load AES key for HCU
 *
 * Implements    : FLASH_DRV_LoadAESKey_Activity
 *END**************************************************************************/

status_t FLASH_DRV_LoadAESKey(uint32_t address)
{
    status_t ret;    /* Return code variable */
    /* Check address if align to 32 bytes (256-bit) */
    if (0U != (address % 32U))
    {
        return STATUS_ERROR;
    }
    /* Check IDLE to verify the previous command is completed */
    if (0U == (EFM->STS & EFM_STS_IDLE_MASK))
    {
        ret = STATUS_EFM_BUSY;
    }
    else
    {
        EFM->STS = FEATURE_EFM_CMD_ERROR_MASK | EFM_STS_DONE_MASK;

        EFM_ENABLE_WE_COMMAND(EFM);
#if defined(CPU_YTM32B1ME0)
        EFM->CTRL &= ~EFM_CTRL_AES_KEY_SEL_MASK;
        EFM->CTRL |= EFM_CTRL_AES_KEY_SEL(((uint32_t)(address) - 0x10000000U) / 0x20U);
#elif defined(CPU_YTM32B1MD1)
        EFM->NVR_ADDR = address;
#else
        *(uint32_t *)address = 0x12345678U;
#endif
        EFM_DISABLE_WE_COMMAND(EFM);
        /* Calling flash command sequence function to execute the command */
        ret = FLASH_LaunchCommandSequence(FEATURE_EFM_LOAD_AES_KEY_CMD_CODE);
    }
    return ret;
}
#endif /* FEATURE_EFM_LOAD_AES_KEY_CMD_CODE */

/*******************************************************************************
* EOF
*******************************************************************************/
