/**
 *******************************************************************************
 * @file  hc32_ll_efm.c
 * @brief This file provides firmware functions to manage the Embedded Flash
 *        Memory unit (EFM).
 @verbatim
   Change Logs:
   Date             Author          Notes
   2022-03-31       CDT             First version
   2022-10-31       CDT             Add API EFM_Protect_Enable & EFM_WriteSecurityCode
                                    Modify API EFM_Read & EFM_Program
   2023-01-15       CDT             Code refine
   2023-06-30       CDT             Modify API EFM_Program()
                                    Modify assert IS_EFM_ADDR() range
                                    Modify API EFM_Protect_Enable()
                                    Modify typo
                                    Modify API EFM_WriteSecurityCode(), switch to read_only mode before exit
   2023-09-30       CDT             Remove address assert from EFM_ReadByte()
                                    Refine EFM_SequenceProgram() & EFM_ChipErase(), and put them in RAM
                                    Fix bug of EFM_GetSwapStatus()
   2023-12-15       CDT             Rename EFM_DataCacheResetCmd() as EFM_CacheRamReset() and modify comment
   2024-06-30       CDT             Optimize EFM_ClearStatus()
   2024-08-31       CDT             Optimize condition judgment
   2024-10-17       CDT             Add const before buffer pointer to cater top-level calls
                                    Bug Fixed # judge the EFM_FLAG_OPTEND whether set o not before clear EFM_FLAG_OPTEND
 @endverbatim
 *******************************************************************************
 * Copyright (C) 2022-2025, Xiaohua Semiconductor Co., Ltd. All rights reserved.
 *
 * This software component is licensed by XHSC under BSD 3-Clause license
 * (the "License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                    opensource.org/licenses/BSD-3-Clause
 *
 *******************************************************************************
 */

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "hc32_ll_efm.h"
#include "hc32_ll_utility.h"

/**
 * @addtogroup LL_Driver
 * @{
 */

/**
 * @defgroup LL_EFM EFM
 * @brief Embedded Flash Management Driver Library
 * @{
 */

#if (LL_EFM_ENABLE == DDL_ON)

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/
/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/
/**
 * @defgroup EFM_Local_Macros EFM Local Macros
 * @{
 */
#ifndef __EFM_FUNC
#define __EFM_FUNC                      __RAM_FUNC
#endif

/**
 * @defgroup EFM_Timeout_Definition EFM timeout definition
 * @{
 */
#define EFM_TIMEOUT                     (HCLK_VALUE / 20000UL)  /* EFM wait read timeout */
#define EFM_PGM_TIMEOUT                 (HCLK_VALUE / 20000UL)  /* EFM Program timeout max 53us */
#define EFM_ERASE_TIMEOUT               (HCLK_VALUE / 50UL)     /* EFM Erase timeout max 20ms */
#define EFM_SEQ_PGM_TIMEOUT             (HCLK_VALUE / 62500UL)  /* EFM Sequence Program timeout max 16us */
/**
 * @}
 */

#define REMCR_REG(x)                    (*(__IO uint32_t *)((uint32_t)(&CM_EFM->MMF_REMCR0) + (4UL * (x))))

/**
 * @defgroup EFM_protect EFM protect define
 * @{
 */
#define EFM_SECURITY_LEN                (12UL)
#define EFM_PROTECT1_KEY                (0xAF180402UL)
#define EFM_PROTECT2_KEY                (0xA85173AEUL)

#define EFM_PROTECT1_ADDR               (0x00000410UL)
#define EFM_PROTECT2_ADDR               (0x00000414UL)
#define EFM_SECURITY_ADDR               (0x0317FFECUL)
#define EFM_SECURITY_ADDR1              (0x0317FFE0UL)

/**
 * @}
 */

/**
 * @defgroup EFM_Check_Parameters_Validity EFM Check Parameters Validity
 * @{
 */
/* Parameter validity check for efm chip . */
#define IS_EFM_CHIP(x)                  ((x) == EFM_CHIP_ALL)

/* Parameter validity check for flash latency. */
#define IS_EFM_WAIT_CYCLE(x)            ((x) <= EFM_WAIT_CYCLE15)

/* Parameter validity check for operate mode. */
#define IS_EFM_OP_MD(x)                                                         \
(   ((x) == EFM_MD_PGM_SINGLE)          ||                                      \
    ((x) == EFM_MD_PGM_READBACK)        ||                                      \
    ((x) == EFM_MD_PGM_SEQ)             ||                                      \
    ((x) == EFM_MD_ERASE_SECTOR)        ||                                      \
    ((x) == EFM_MD_ERASE_ALL_CHIP)      ||                                      \
    ((x) == EFM_MD_READONLY))

/* Parameter validity check for flash interrupt select. */
#define IS_EFM_INT_SEL(x)               (((x) | EFM_INT_ALL) == EFM_INT_ALL)

/* Parameter validity check for flash flag. */
#define IS_EFM_FLAG(x)                  (((x) | EFM_FLAG_ALL) == EFM_FLAG_ALL)

/* Parameter validity check for flash clear flag. */
#define IS_EFM_CLRFLAG(x)               (((x) | EFM_FLAG_ALL) == EFM_FLAG_ALL)

/* Parameter validity check for bus status while flash program or erase. */
#define IS_EFM_BUS_STATUS(x)                                                    \
(   ((x) == EFM_BUS_HOLD)               ||                                      \
    ((x) == EFM_BUS_RELEASE))

/* Parameter validity check for efm address. */
#define IS_EFM_ADDR(x)                                                          \
(   ((x) <= EFM_END_ADDR)               ||                                      \
    (((x) >= EFM_OTP_START_ADDR) && ((x) <= EFM_OTP_END_ADDR)) ||               \
    (((x) >= EFM_SECURITY_START_ADDR) && ((x) <= EFM_SECURITY_END_ADDR)))

/* Parameter validity check for efm erase address. */
#define IS_EFM_ERASE_ADDR(x)            ((x) <= EFM_END_ADDR)

/* Parameter validity check for efm erase mode . */
#define IS_EFM_ERASE_MD(x)                                                      \
(   ((x) == EFM_MD_ERASE_ONE_CHIP)      ||                                      \
    ((x) == EFM_MD_ERASE_FULL))

/* Parameter validity check for EFM lock status. */
#define IS_EFM_REG_UNLOCK()             (CM_EFM->FAPRT == 0x00000001UL)

/* Parameter validity check for EFM_FWMC register lock status. */
#define IS_EFM_FWMC_UNLOCK()            (bCM_EFM->FWMC_b.PEMODE == 1U)

/* Parameter validity check for EFM remap lock status. */
#define IS_EFM_REMAP_UNLOCK()           (CM_EFM->MMF_REMPRT == 0x00000001UL)

/* Parameter validity check for EFM remap index */
#define IS_EFM_REMAP_IDX(x)                                                     \
(   ((x) == EFM_REMAP_IDX0)             ||                                      \
    ((x) == EFM_REMAP_IDX1))

/* Parameter validity check for EFM remap size */
#define IS_EFM_REMAP_SIZE(x)                                                    \
(   ((x) >= EFM_REMAP_4K)               &&                                      \
    ((x) <= EFM_REMAP_SIZE_MAX))

/* Parameter validity check for EFM remap address */
#define IS_EFM_REMAP_ADDR(x)                                                    \
(   ((x) <= EFM_REMAP_ROM_END_ADDR)     ||                                      \
    (((x) >= EFM_REMAP_RAM_START_ADDR)  &&                                      \
    ((x) <= EFM_REMAP_RAM_END_ADDR)))

/* Parameter validity check for EFM remap state */
#define IS_EFM_REMAP_STATE(x)                                                   \
(   ((x) == EFM_REMAP_OFF)             ||                                       \
    ((x) == EFM_REMAP_ON))

/* Parameter validity check for EFM security code length */
#define IS_EFM_SECURITY_CODE_LEN(x)    ((x) <= EFM_SECURITY_LEN)

/**
 * @}
 */

/**
 * @}
 */

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/
/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/
/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/

/**
 * @defgroup EFM_Local_Functions EFM Local Functions
 * @{
 */

/**
 * @brief  Wait EFM flag.
 * @param  [in] u32Flag     Specifies the flag to be wait. @ref EFM_Flag_Sel
 * @param  [in] u32Time     Specifies the time to wait while the flag not be set.
 * @retval int32_t:
 *         - LL_OK: Flag was set.
 *         - LL_ERR_TIMEOUT: Flag was not set.
 */
static int32_t EFM_WaitFlag(uint32_t u32Flag, uint32_t u32Time)
{
    __IO uint32_t u32Timeout = 0UL;
    int32_t i32Ret = LL_OK;

    while (SET != EFM_GetStatus(u32Flag)) {
        u32Timeout++;
        if (u32Timeout > u32Time) {
            i32Ret = LL_ERR_TIMEOUT;
            break;
        }
    }

    return i32Ret;
}

/**
 * @brief  Wait EFM operate end.
 * @param  [in] u8Shift     Specifies the flag shift bit. @ref EFM_Flag_Sel
 * @param  [in] u32Time     Specifies the time to wait while the flag not be set.
 * @retval int32_t:
 *         - LL_OK: Flash program or erase success.
 *         - LL_ERR_NOT_RDY: Flash program or erase not ready.
 *         - LL_ERR: Flash program or erase error.
 */
static int32_t EFM_WaitEnd(uint8_t u8Shift, uint32_t u32Time)
{
    int32_t i32Ret = LL_OK;
    /* Wait for ready flag. */
    if (LL_ERR_TIMEOUT == EFM_WaitFlag(EFM_FLAG_RDY << u8Shift, u32Time)) {
        i32Ret = LL_ERR_NOT_RDY;
    }
    if (LL_OK == i32Ret) {
        if (SET == EFM_GetStatus(EFM_FLAG_OPTEND << u8Shift)) {
            /* Clear the operation end flag */
            EFM_ClearStatus(EFM_FLAG_OPTEND << u8Shift);
        } else {
            i32Ret = LL_ERR;
        }
    }
    return i32Ret;
}
/**
 * @brief  Wait EFM program and ReadBack operate end.
 * @param  [in] u8Shift     Specifies the flag shift bit. @ref EFM_Flag_Sel
 * @retval int32_t:
 *         - LL_OK: Flash program or erase success.
 *         - LL_ERR_NOT_RDY: Flash program or erase not ready.
 *         - LL_ERR: Flash program or erase error.
 */
static int32_t EFM_WaitReadBackEnd(uint8_t u8Shift)
{
    int32_t i32Ret = LL_OK;
    /* Wait for ready flag. */
    if (LL_ERR_TIMEOUT == EFM_WaitFlag(EFM_FLAG_RDY << u8Shift, EFM_PGM_TIMEOUT)) {
        i32Ret = LL_ERR_NOT_RDY;
    }
    if (LL_OK == i32Ret) {
        /* Get the flag PGMISMTCH */
        if (SET == EFM_GetStatus(EFM_FLAG_PGMISMTCH << u8Shift)) {
            /* Clear flag PGMISMTCH */
            EFM_ClearStatus(EFM_FLAG_PGMISMTCH << u8Shift);
            i32Ret = LL_ERR;
        }
        if (LL_OK == i32Ret) {
            if (SET == EFM_GetStatus(EFM_FLAG_OPTEND << u8Shift)) {
                /* Clear the operation end flag. */
                EFM_ClearStatus(EFM_FLAG_OPTEND << u8Shift);
            } else {
                i32Ret = LL_ERR;
            }
        }
    }
    return i32Ret;
}

/**
 * @}
 */

/**
 * @defgroup EFM_Global_Functions EFM Global Functions
 * @{
 */

/**
 * @brief  Enable or disable EFM.
 * @param  [in] u32Flash        Specifies the FLASH. @ref EFM_Chip_Sel
 * @param  [in] enNewState      An @ref en_functional_state_t enumeration value.
 * @retval None
 */
void EFM_Cmd(uint32_t u32Flash, en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));
    DDL_ASSERT(IS_EFM_REG_UNLOCK());
    DDL_ASSERT(IS_EFM_CHIP(u32Flash));

    if (ENABLE == enNewState) {
        CLR_REG32_BIT(CM_EFM->FSTP, u32Flash);
    } else {
        SET_REG32_BIT(CM_EFM->FSTP, u32Flash);
    }
}

/**
 * @brief  Set the efm read wait cycles.
 * @param  [in] u32WaitCycle            Specifies the efm read wait cycles.
 *    @arg  This parameter can be of a value of @ref EFM_Wait_Cycle
 * @retval int32_t:
 *         - LL_OK: Program successfully.
 *         - LL_ERR_TIMEOUT: EFM is not ready.
 * @note   Call EFM_REG_Unlock() unlock EFM register first.
 */
int32_t EFM_SetWaitCycle(uint32_t u32WaitCycle)
{
    uint32_t u32Timeout = 0UL;

    /* Param valid check */
    DDL_ASSERT(IS_EFM_REG_UNLOCK());
    DDL_ASSERT(IS_EFM_WAIT_CYCLE(u32WaitCycle));

    MODIFY_REG32(CM_EFM->FRMC, EFM_FRMC_FLWT, u32WaitCycle);
    while (u32WaitCycle != READ_REG32_BIT(CM_EFM->FRMC, EFM_FRMC_FLWT)) {
        u32Timeout++;
        if (u32Timeout > EFM_TIMEOUT) {
            return LL_ERR_TIMEOUT;
        }
    }
    return LL_OK;
}

/**
 * @brief  Reset cache RAM or cache ram release reset.
 * @param  [in] enNewState           An @ref en_functional_state_t enumeration value.
 * @retval None
 */
void EFM_CacheRamReset(en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));
    DDL_ASSERT(IS_EFM_REG_UNLOCK());

    WRITE_REG32(bCM_EFM->FRMC_b.CRST, enNewState);
}

/**
 * @brief  Enable or disable the flash data cache and instruction cache.
 * @param  [in] enNewState                An @ref en_functional_state_t enumeration value.
 * @retval None
 * @note   Call EFM_REG_Unlock() unlock EFM register first.
 */
void EFM_CacheCmd(en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));
    DDL_ASSERT(IS_EFM_REG_UNLOCK());

    WRITE_REG32(bCM_EFM->FRMC_b.CACHE, enNewState);
}

/**
 * @brief  Enable or disable the Read of low-voltage mode.
 * @param  [in] enNewState                An @ref en_functional_state_t enumeration value.
 * @retval None
 * @note   Call EFM_REG_Unlock() unlock EFM register first.
 */
void EFM_LowVoltageReadCmd(en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));
    DDL_ASSERT(IS_EFM_REG_UNLOCK());

    WRITE_REG32(bCM_EFM->FRMC_b.SLPMD, enNewState);
}

/**
 * @brief  Enable or disable the EFM swap function.
 * @param  [in] enNewState                An @ref en_functional_state_t enumeration value.
 * @retval int32_t:
 *         - LL_OK: Program successfully.
 *         - LL_ERR_NOT_RDY: EFM is not ready.
 * @note   Call EFM_REG_Unlock() unlock EFM register first.
 */
int32_t EFM_SwapCmd(en_functional_state_t enNewState)
{
    int32_t i32Ret;
    uint32_t u32Tmp;

    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));
    DDL_ASSERT(IS_EFM_REG_UNLOCK());
    DDL_ASSERT(IS_EFM_FWMC_UNLOCK());

    /* Get CACHE status */
    u32Tmp = READ_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);
    /* Disable CACHE */
    CLR_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);

    if (enNewState == ENABLE) {
        /* Set Program single mode. */
        MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_PGM_SINGLE);
        /* Enable flash swap function */
        RW_MEM32(EFM_SWAP_ADDR) = EFM_SWAP_DATA;
        /* Wait operate end. */
        i32Ret = EFM_WaitEnd(0U, EFM_PGM_TIMEOUT);
    } else {
        /* Set Sector erase mode. */
        MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_ERASE_SECTOR);
        /* Disable flash switch function */
        RW_MEM32(EFM_SWAP_ADDR) = 0x0UL;
        /* Wait for ready flag. */
        /* Wait operate end. */
        i32Ret = EFM_WaitEnd(0U, EFM_ERASE_TIMEOUT);
    }

    /* Set read only mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_READONLY);

    /* recover CACHE */
    MODIFY_REG32(CM_EFM->FRMC, EFM_CACHE_ALL, u32Tmp);

    return i32Ret;
}

/**
 * @brief  Checks whether the swap function enable or disable.
 * @param  None
 * @retval An @ref en_flag_status_t enumeration type value.
 */
en_flag_status_t EFM_GetSwapStatus(void)
{
    return ((0UL == READ_REG32(bCM_EFM->FSWP_b.FSWP)) ? SET : RESET);
}

/**
 * @brief  Enable or disable the EFM low-voltage mode.
 * @param  [in] enNewState                An @ref en_functional_state_t enumeration value.
 * @retval None
 * @note   Call EFM_REG_Unlock() unlock EFM register first.
 */
void EFM_LowVoltageCmd(en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));
    WRITE_REG32(bCM_EFM->FRMC_b.LVM, enNewState);
}

/**
 * @brief  Set the FLASH erase program mode .
 * @param  [in] u32Mode                   Specifies the FLASH erase program mode.
 *    @arg  This parameter can be of a value of @ref EFM_OperateMode_Sel
 * @retval int32_t:
 *         - LL_OK: Set mode successfully.
 *         - LL_ERR_NOT_RDY: EFM is not ready.
 */
int32_t EFM_SetOperateMode(uint32_t u32Mode)
{
    int32_t i32Ret = LL_OK;
    DDL_ASSERT(IS_EFM_OP_MD(u32Mode));
    DDL_ASSERT(IS_EFM_REG_UNLOCK());
    DDL_ASSERT(IS_EFM_FWMC_UNLOCK());
    if (LL_ERR_TIMEOUT == EFM_WaitFlag(EFM_FLAG_RDY, EFM_SEQ_PGM_TIMEOUT)) {
        i32Ret = LL_ERR_NOT_RDY;
    }

    if (i32Ret == LL_OK) {
        /* Set the program or erase mode. */
        MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, u32Mode);
    }
    return i32Ret;
}

/**
 * @brief  Enable or Disable EFM interrupt.
 * @param  [in] u32EfmInt               Specifies the FLASH interrupt source and status. @ref EFM_Interrupt_Sel
 *   @arg  EFM_INT_OPTEND:              End of EFM Operation Interrupt source
 *   @arg  EFM_INT_PEERR:               Program/erase error Interrupt source
 *   @arg  EFM_INT_COLERR:              Read collide error Interrupt source
 * @param  [in] enNewState              An @ref en_functional_state_t enumeration value.
 * @retval None
 * @note   Call EFM_REG_Unlock() unlock EFM register first.
 */
void EFM_IntCmd(uint32_t u32EfmInt, en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_EFM_REG_UNLOCK());
    DDL_ASSERT(IS_EFM_INT_SEL(u32EfmInt));
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));

    if (ENABLE == enNewState) {
        SET_REG32_BIT(CM_EFM->FITE, u32EfmInt);
    } else {
        CLR_REG32_BIT(CM_EFM->FITE, u32EfmInt);
    }
}

/**
 * @brief  Check any of the specified flag is set or not.
 * @param  [in] u32Flag                    Specifies the FLASH flag to check.
 *   @arg  This parameter can be of a value of @ref EFM_Flag_Sel
 * @retval An @ref en_flag_status_t enumeration type value.
 */
en_flag_status_t EFM_GetAnyStatus(uint32_t u32Flag)
{
    DDL_ASSERT(IS_EFM_FLAG(u32Flag));

    return ((0UL == READ_REG32_BIT(CM_EFM->FSR, u32Flag)) ? RESET : SET);
}

/**
 * @brief  Check all the specified flag is set or not.
 * @param  [in] u32Flag                    Specifies the FLASH flag to check.
 *   @arg  This parameter can be of a value of @ref EFM_Flag_Sel
 * @retval An @ref en_flag_status_t enumeration type value.
 */
en_flag_status_t EFM_GetStatus(uint32_t u32Flag)
{
    DDL_ASSERT(IS_EFM_FLAG(u32Flag));

    return ((u32Flag == READ_REG32_BIT(CM_EFM->FSR, u32Flag)) ? SET : RESET);
}

/**
 * @brief  Clear the flash flag.
 * @param  [in] u32Flag                  Specifies the FLASH flag to clear.
 *   @arg  This parameter can be of a value of @ref EFM_Flag_Sel
 * @retval None
 * @note   Call EFM_REG_Unlock() unlock EFM register first.
 */
void EFM_ClearStatus(uint32_t u32Flag)
{
    DDL_ASSERT(IS_EFM_REG_UNLOCK());
    DDL_ASSERT(IS_EFM_CLRFLAG(u32Flag));

    WRITE_REG32(CM_EFM->FSCLR, u32Flag);
}

/**
 * @brief  Set bus status while flash program or erase.
 * @param  [in] u32Status                  Specifies the new bus status while flash program or erase.
 *  This parameter can be one of the following values:
 *   @arg  EFM_BUS_HOLD:                   Bus busy while flash program or erase.
 *   @arg  EFM_BUS_RELEASE:                Bus release while flash program or erase.
 * @retval None
 */
void EFM_SetBusStatus(uint32_t u32Status)
{
    DDL_ASSERT(IS_EFM_REG_UNLOCK());
    DDL_ASSERT(IS_EFM_BUS_STATUS(u32Status));
    DDL_ASSERT(IS_EFM_FWMC_UNLOCK());

    WRITE_REG32(bCM_EFM->FWMC_b.BUSHLDCTL, u32Status);
}

/**
 * @brief  EFM read byte.
 * @param  [in] u32Addr               The specified address to read.
 * @param  [in] pu8ReadBuf            The specified read buffer.
 * @param  [in] u32ByteLen            The specified length to read.
 * @retval int32_t:
 *         - LL_OK: Read successfully
 *         - LL_ERR_INVD_PARAM: Invalid parameter
 *         - LL_ERR_NOT_RDY: EFM is not ready.
 */
int32_t EFM_ReadByte(uint32_t u32Addr, uint8_t *pu8ReadBuf, uint32_t u32ByteLen)
{
    int32_t i32Ret = LL_ERR_INVD_PARAM;
    __IO uint8_t *pu8Buf = (uint8_t *)u32Addr;
    uint32_t u32Len = u32ByteLen;
    uint32_t u32ReadyFlag = EFM_FLAG_RDY;

    DDL_ASSERT(IS_EFM_ADDR(u32Addr));
    DDL_ASSERT(IS_EFM_ADDR(u32Addr + u32ByteLen - 1UL));

    if (NULL != pu8ReadBuf) {
        if (LL_OK == EFM_WaitFlag(u32ReadyFlag, EFM_TIMEOUT)) {
            while (0UL != u32Len) {
                *(pu8ReadBuf++) = *(pu8Buf++);
                u32Len--;
            }
            i32Ret = LL_OK;
        } else {
            i32Ret = LL_ERR_NOT_RDY;
        }

    }

    return i32Ret;
}

/**
 * @brief  EFM program (single program mode).
 * @param  [in] u32Addr                   The specified program address.
 * @param  [in] pu8Buf                    The pointer of specified program data.
 * @param  [in] u32Len                    The length of specified program data.
 * @retval int32_t:
 *         - LL_OK: Program successful.
 *         - LL_ERR_NOT_RDY: EFM if not ready.
 * @note  Call EFM_REG_Unlock() unlock EFM register first.
 */
int32_t EFM_Program(uint32_t u32Addr, const uint8_t *pu8Buf, uint32_t u32Len)
{
    int32_t i32Ret = LL_OK;
    uint32_t u32Tmp;
    uint8_t u8Shift;
    uint32_t u32LoopWords = u32Len >> 2UL;
    uint32_t u32RemainBytes = u32Len % 4UL;
    uint32_t *u32pSource = (uint32_t *)(uint32_t)pu8Buf;
    uint32_t *u32pDest = (uint32_t *)u32Addr;
    uint32_t u32LastWord;

    DDL_ASSERT(IS_EFM_REG_UNLOCK());
    DDL_ASSERT(IS_EFM_FWMC_UNLOCK());
    DDL_ASSERT(IS_EFM_ADDR(u32Addr));
    DDL_ASSERT(IS_EFM_ADDR(u32Addr + u32Len - 1UL));
    DDL_ASSERT(IS_ADDR_ALIGN_WORD(u32Addr));

    u8Shift = 0U;

    /* Clear the error flag. */
    EFM_ClearStatus(EFM_FLAG_ALL);
    /* Get CACHE status */
    u32Tmp = READ_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);
    /* Disable CACHE */
    CLR_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);

    /* Set single program mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_PGM_SINGLE);

    while ((u32LoopWords-- > 0UL) && (LL_OK == i32Ret)) {
        /* program data. */
        *u32pDest++ = *u32pSource++;
        /* Wait operate end. */
        i32Ret = EFM_WaitEnd(u8Shift, EFM_PGM_TIMEOUT);
    }

    if ((0U != u32RemainBytes) && (LL_OK == i32Ret)) {
        u32LastWord = *u32pSource;
        u32LastWord |= 0xFFFFFFFFUL << (u32RemainBytes * 8UL);
        *u32pDest++ = u32LastWord;
        /* Wait operate end. */
        i32Ret = EFM_WaitEnd(u8Shift, EFM_PGM_TIMEOUT);

    }
    /* Set read only mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_READONLY);
    /* Recover CACHE function */
    MODIFY_REG32(CM_EFM->FRMC, EFM_CACHE_ALL, u32Tmp);
    return i32Ret;
}

/**
 * @brief  EFM single program mode(Word).
 * @param  [in] u32Addr                   The specified program address.
 * @param  [in] u32Data                   The specified program data.
 * @retval int32_t:
 *         - LL_OK: Program successfully
 *         - LL_ERR_NOT_RDY: EFM is not ready.
 * @note  Call EFM_REG_Unlock() unlock EFM register first.
 */
int32_t EFM_ProgramWord(uint32_t u32Addr, uint32_t u32Data)
{
    int32_t i32Ret;
    uint32_t u32Tmp;
    uint8_t u8Shift;

    DDL_ASSERT(IS_EFM_REG_UNLOCK());
    DDL_ASSERT(IS_EFM_FWMC_UNLOCK());
    DDL_ASSERT(IS_EFM_ADDR(u32Addr));
    DDL_ASSERT(IS_ADDR_ALIGN_WORD(u32Addr));

    /* Clear the error flag. */
    EFM_ClearStatus(EFM_FLAG_ALL);
    /* Get CACHE status */
    u32Tmp = READ_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);
    /* Disable CACHE function */
    CLR_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);
    u8Shift = 0U;
    /* Set single program mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_PGM_SINGLE);
    /* Program data. */
    RW_MEM32(u32Addr) = u32Data;

    /* Wait operate end. */
    i32Ret = EFM_WaitEnd(u8Shift, EFM_PGM_TIMEOUT);

    /* Set read only mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_READONLY);

    /* Recover CACHE function */
    MODIFY_REG32(CM_EFM->FRMC, EFM_CACHE_ALL, u32Tmp);

    return i32Ret;
}

/**
 * @brief  EFM single program with read back(Word).
 * @param  [in] u32Addr                   The specified program address.
 * @param  [in] u32Data                   The specified program data.
 * @retval int32_t:
 *         - LL_OK: Program successfully
 *         - LL_ERR: program error
 *         - LL_ERR_NOT_RDY: EFM is not ready.
 * @note  Call EFM_REG_Unlock() unlock EFM register first.
 */
int32_t EFM_ProgramWordReadBack(uint32_t u32Addr, uint32_t u32Data)
{
    int32_t i32Ret;
    uint32_t u32Tmp;
    uint8_t u8Shift;

    DDL_ASSERT(IS_EFM_REG_UNLOCK());
    DDL_ASSERT(IS_EFM_FWMC_UNLOCK());
    DDL_ASSERT(IS_EFM_ADDR(u32Addr));
    DDL_ASSERT(IS_ADDR_ALIGN_WORD(u32Addr));

    /* Clear the error flag. */
    EFM_ClearStatus(EFM_FLAG_ALL);
    /* Get CACHE status */
    u32Tmp = READ_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);
    /* Disable CACHE */
    CLR_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);
    u8Shift = 0U;
    /* Set Program and read back mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_PGM_READBACK);
    /* Program data. */
    RW_MEM32(u32Addr) = (uint32_t)u32Data;
    /* Wait operate end. */
    i32Ret = EFM_WaitReadBackEnd(u8Shift);
    /* Set read only mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_READONLY);
    /* recover CACHE function */
    MODIFY_REG32(CM_EFM->FRMC, EFM_CACHE_ALL, u32Tmp);

    return i32Ret;
}

/**
 * @brief  EFM program (sequence program mode).
 * @param  [in] u32Addr                   The specified program address.
 * @param  [in] pu8Buf                    The pointer of specified program data.
 * @param  [in] u32Len                    The length of specified program data.
 * @retval int32_t:
 *         - LL_OK: Program successfully
 *         - LL_ERR_TIMEOUT: program error timeout
 * @note  Call EFM_REG_Unlock() unlock EFM register first.
 *        __EFM_FUNC default value is __RAM_FUNC.
 */
__NOINLINE __EFM_FUNC int32_t EFM_SequenceProgram(uint32_t u32Addr, const uint8_t *pu8Buf, uint32_t u32Len)
{
    int32_t i32Ret = LL_OK;
    uint32_t u32Tmp;
    uint32_t u32LoopWords = u32Len >> 2UL;
    uint32_t u32RemainBytes = u32Len % 4UL;
    uint32_t *u32pSource = (uint32_t *)(uint32_t)pu8Buf;
    uint32_t *u32pDest = (uint32_t *)u32Addr;
    uint32_t u32Timeout;
    uint32_t u32LastWord;

    /* Assert */
    if (!IS_EFM_REG_UNLOCK()) {
        return LL_ERR_NOT_RDY;
    }
    if ((!IS_EFM_FWMC_UNLOCK()) || (!IS_EFM_ADDR(u32Addr)) || (!IS_EFM_ADDR(u32Addr + u32Len - 1UL))) {
        return LL_ERR_INVD_PARAM;
    }
    if (!IS_ADDR_ALIGN_WORD(u32Addr)) {
        return LL_ERR_INVD_PARAM;
    }

    /* Clear the error flag. */
    SET_REG32_BIT(CM_EFM->FSCLR, EFM_FLAG_ALL);
    /* Get CACHE status */
    u32Tmp = READ_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);
    /* Disable CACHE */
    CLR_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);
    /* Set sequence program mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_PGM_SEQ);

    while (u32LoopWords-- > 0UL) {
        /* program data. */
        *u32pDest++ = *u32pSource++;
        /* wait for operation end flag. */
        u32Timeout = 0UL;
        while (EFM_FLAG_OPTEND != READ_REG32_BIT(CM_EFM->FSR, EFM_FLAG_OPTEND)) {
            if (u32Timeout++ >= EFM_PGM_TIMEOUT) {
                i32Ret = LL_ERR_TIMEOUT;
                break;
            }
        }
        /* Clear operation end flag */
        u32Timeout = 0UL;
        while (EFM_FLAG_OPTEND == READ_REG32_BIT(CM_EFM->FSR, EFM_FLAG_OPTEND)) {
            SET_REG32_BIT(CM_EFM->FSCLR, EFM_FLAG_OPTEND);
            if (u32Timeout++ >= EFM_TIMEOUT) {
                i32Ret = LL_ERR_TIMEOUT;
                break;
            }
        }
    }

    if (0U != u32RemainBytes) {
        u32LastWord = *u32pSource;
        u32LastWord |= 0xFFFFFFFFUL << (u32RemainBytes * 8UL);
        *u32pDest++ = u32LastWord;

        /* wait for operation end flag. */
        u32Timeout = 0UL;
        while (EFM_FLAG_OPTEND != READ_REG32_BIT(CM_EFM->FSR, EFM_FLAG_OPTEND)) {
            if (u32Timeout++ >= EFM_PGM_TIMEOUT) {
                i32Ret = LL_ERR_TIMEOUT;
                break;
            }
        }
        /* Clear operation end flag */
        u32Timeout = 0UL;
        while (EFM_FLAG_OPTEND == READ_REG32_BIT(CM_EFM->FSR, EFM_FLAG_OPTEND)) {
            SET_REG32_BIT(CM_EFM->FSCLR, EFM_FLAG_OPTEND);
            if (u32Timeout++ >= EFM_TIMEOUT) {
                i32Ret = LL_ERR_TIMEOUT;
                break;
            }
        }

    }

    /* Set read only mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_READONLY);
    /* Wait for ready flag. */
    u32Timeout = 0UL;
    while (EFM_FLAG_RDY != READ_REG32_BIT(CM_EFM->FSR, EFM_FLAG_RDY)) {
        if (u32Timeout++ >= EFM_PGM_TIMEOUT) {
            i32Ret = LL_ERR_TIMEOUT;
            break;
        }
    }

    /* Recover CACHE */
    MODIFY_REG32(CM_EFM->FRMC, EFM_CACHE_ALL, u32Tmp);
    return i32Ret;
}

/**
 * @brief  EFM sector erase.
 * @param  [in] u32Addr                   The address in the specified sector.
 * @retval int32_t:
 *         - LL_OK: Erase successful.
 *         - LL_ERR_NOT_RDY: EFM is not ready.
 * @note   Call EFM_REG_Unlock() unlock EFM register first.
 */
int32_t EFM_SectorErase(uint32_t u32Addr)
{
    int32_t i32Ret;
    uint32_t u32Tmp;
    uint8_t u8Shift;

    DDL_ASSERT(IS_EFM_ERASE_ADDR(u32Addr));
    DDL_ASSERT(IS_ADDR_ALIGN_WORD(u32Addr));
    DDL_ASSERT(IS_EFM_REG_UNLOCK());
    DDL_ASSERT(IS_EFM_FWMC_UNLOCK());

    /* Clear the error flag. */
    EFM_ClearStatus(EFM_FLAG_ALL);
    /* Get CACHE status */
    u32Tmp = READ_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);
    /* Disable CACHE */
    CLR_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);
    u8Shift = 0U;
    /* Set sector erase mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_ERASE_SECTOR);

    /* Erase */
    RW_MEM32(u32Addr) = 0UL;

    /* Wait operate end. */
    i32Ret = EFM_WaitEnd(u8Shift, EFM_ERASE_TIMEOUT);

    /* Set read only mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_READONLY);
    /* Recover CACHE */
    MODIFY_REG32(CM_EFM->FRMC, EFM_CACHE_ALL, u32Tmp);

    return i32Ret;
}

/**
 * @brief  EFM chip erase.
 * @param  [in]  u8Chip      Specifies the chip to be erased @ref EFM_Chip_Sel
 * @retval int32_t:
 *         - LL_OK: Erase successfully
 *         - LL_ERR_NOT_RDY: EFM is not ready.
 * @note   Call EFM_REG_Unlock() unlock EFM register first.
 *         __EFM_FUNC default value is __RAM_FUNC.
 */
__NOINLINE __EFM_FUNC int32_t EFM_ChipErase(uint8_t u8Chip)
{
    int32_t i32Ret = LL_OK;
    uint32_t u32Tmp;
    uint32_t u32Addr = 0UL;
    uint8_t u8Shift;
    uint32_t u32Timeout;

    /* Assert */
    if (!IS_EFM_REG_UNLOCK()) {
        return LL_ERR_NOT_RDY;
    }
    if ((!IS_EFM_FWMC_UNLOCK()) || !IS_EFM_CHIP(u8Chip)) {
        return LL_ERR_INVD_PARAM;
    }

    u8Shift = 0U;

    /* Clear the error flag. */
    SET_REG32_BIT(CM_EFM->FSCLR, EFM_FLAG_ALL);
    /* Get CACHE status */
    u32Tmp = READ_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);
    /* Disable CACHE */
    CLR_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);

    if (1UL == (READ_REG32(bCM_EFM->FSWP_b.FSWP))) {
        /* Set Sector erase mode. */
        MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_ERASE_SECTOR);
        /* Disable flash switch function */
        RW_MEM32(EFM_SWAP_ADDR) = 0x0UL;
        /* wait for operation end flag. */
        u32Timeout = 0UL;
        while (EFM_FLAG_OPTEND != READ_REG32_BIT(CM_EFM->FSR, EFM_FLAG_OPTEND)) {
            if (u32Timeout++ >= EFM_PGM_TIMEOUT) {
                i32Ret = LL_ERR_TIMEOUT;
                break;
            }
        }
        /* Clear the operation end flag */
        SET_REG32_BIT(CM_EFM->FSCLR, EFM_FLAG_OPTEND);
    }

    /* Set chip erase mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_ERASE_ALL_CHIP);
    /* Erase */
    RW_MEM32(u32Addr) = 0UL;
    /* Wait for ready flag. */
    u32Timeout = 0UL;
    while ((EFM_FLAG_RDY << u8Shift) != READ_REG32_BIT(CM_EFM->FSR, EFM_FLAG_RDY << u8Shift)) {
        if (u32Timeout++ >= EFM_ERASE_TIMEOUT) {
            i32Ret = LL_ERR_TIMEOUT;
            break;
        }
    }
    /* Clear the operation end flag. */
    SET_REG32_BIT(CM_EFM->FSCLR, EFM_FLAG_OPTEND << u8Shift);
    /* Set read only mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_READONLY);

    /* recover CACHE */
    MODIFY_REG32(CM_EFM->FRMC, EFM_CACHE_ALL, u32Tmp);
    return i32Ret;
}

/**
 * @brief  FWMC register write enable or disable.
 * @param  [in] enNewState                An @ref en_functional_state_t enumeration value.
 * @retval None
 */
void EFM_FWMC_Cmd(en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));

    WRITE_REG32(bCM_EFM->FWMC_b.PEMODE, enNewState);
}

/**
 * @brief  EFM OTP lock.
 * @param  [in]  u32Addr           Specifies the OTP block
 * @retval int32_t:
 *         - LL_OK: Lock successfully
 *         - LL_ERR_NOT_RDY: EFM is not ready.
 * @note   The address should be word align.
 *         Call EFM_REG_Unlock() and EFM_OTP_WP_Unlock() unlock EFM_FWMC register first.
 */
int32_t EFM_OTP_Lock(uint32_t u32Addr)
{
    int32_t i32Ret = LL_OK;
    uint32_t u32Tmp;

    if ((u32Addr >= EFM_OTP_LOCK_ADDR_START) && (u32Addr < EFM_OTP_LOCK_ADDR_END)) {
        DDL_ASSERT(IS_ADDR_ALIGN_WORD(u32Addr));
        DDL_ASSERT(IS_EFM_FWMC_UNLOCK());
        DDL_ASSERT(IS_EFM_REG_UNLOCK());
        /* Get CACHE status */
        u32Tmp = READ_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);
        /* Disable CACHE */
        CLR_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);
        /* Set single program mode. */
        MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_PGM_SINGLE);
        /* OTP latch */
        RW_MEM32(u32Addr) = (uint32_t)0UL;
        /* Wait operate end. */
        i32Ret = EFM_WaitEnd(0U, EFM_ERASE_TIMEOUT);
        /* Set read only mode. */
        MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_READONLY);
        /* Recover CACHE */
        MODIFY_REG32(CM_EFM->FRMC, EFM_CACHE_ALL, u32Tmp);
    }

    return i32Ret;
}

/**
 * @brief  Set flash protect area.
 * @param  [in] u32StartAddr       Start address of protect area.
 * @param  [in] u32EndAddr         End address of protect area.
 * @retval None
 * @note   Call EFM_REG_Unlock() unlock EFM register first.
 */
void EFM_SetWindowProtectAddr(uint32_t u32StartAddr, uint32_t u32EndAddr)
{
    DDL_ASSERT(IS_EFM_REG_UNLOCK());
    DDL_ASSERT(IS_EFM_ADDR(u32StartAddr));
    DDL_ASSERT(IS_EFM_ADDR(u32EndAddr));
    /* Set protect area start address */
    WRITE_REG32(CM_EFM->FPMTSW, u32StartAddr);
    /* Set protect area end address */
    WRITE_REG32(CM_EFM->FPMTEW, u32EndAddr);
}

/**
 * @brief  Get unique ID.
 * @param  [out] pstcUID     Unique ID struct
 * @retval Returns the value of the unique ID
 */
void EFM_GetUID(stc_efm_unique_id_t *pstcUID)
{
    if (NULL != pstcUID) {
        pstcUID->u32UniqueID0 = READ_REG32(CM_EFM->UQID0);
        pstcUID->u32UniqueID1 = READ_REG32(CM_EFM->UQID1);
        pstcUID->u32UniqueID2 = READ_REG32(CM_EFM->UQID2);
    }
}

/**
 * @brief  Init REMAP initial structure with default value.
 * @param  [in] pstcEfmRemapInit specifies the Parameter of REMAP.
 * @retval int32_t:
 *         - LL_OK: Initialize success
 *         - LL_ERR_INVD_PARAM: NULL pointer
 */
int32_t EFM_REMAP_StructInit(stc_efm_remap_init_t *pstcEfmRemapInit)
{
    int32_t i32Ret = LL_OK;
    if (NULL == pstcEfmRemapInit) {
        i32Ret = LL_ERR_INVD_PARAM;
    } else {
        pstcEfmRemapInit->u32State = EFM_REMAP_OFF;
        pstcEfmRemapInit->u32Addr = 0UL;
        pstcEfmRemapInit->u32Size = EFM_REMAP_4K;
    }
    return i32Ret;
}

/**
 * @brief  REMAP initialize.
 * @param  [in] u8RemapIdx      Specifies the remap ID.
 * @param  [in] pstcEfmRemapInit specifies the Parameter of REMAP.
 * @retval int32_t:
 *         - LL_OK: Initialize success
 *         - LL_ERR_INVD_PARAM: NULL pointer
 */
int32_t EFM_REMAP_Init(uint8_t u8RemapIdx, stc_efm_remap_init_t *pstcEfmRemapInit)
{
    int32_t i32Ret = LL_OK;
    __IO uint32_t *REMCRx;

    if (NULL == pstcEfmRemapInit) {
        i32Ret = LL_ERR_INVD_PARAM;
    } else {
        DDL_ASSERT(IS_EFM_REMAP_UNLOCK());
        DDL_ASSERT(IS_EFM_REMAP_IDX(u8RemapIdx));
        DDL_ASSERT(IS_EFM_REMAP_SIZE(pstcEfmRemapInit->u32Size));
        DDL_ASSERT(IS_EFM_REMAP_ADDR(pstcEfmRemapInit->u32Addr));
        DDL_ASSERT(IS_EFM_REMAP_STATE(pstcEfmRemapInit->u32State));
        if ((pstcEfmRemapInit->u32Addr % (1UL << pstcEfmRemapInit->u32Size)) != 0U) {
            i32Ret = LL_ERR_INVD_PARAM;
        } else {
            REMCRx = &REMCR_REG(u8RemapIdx);
            MODIFY_REG32(*REMCRx, EFM_MMF_REMCR_EN | EFM_MMF_REMCR_RMTADDR | EFM_MMF_REMCR_RMSIZE, \
                         pstcEfmRemapInit->u32State | pstcEfmRemapInit->u32Addr | pstcEfmRemapInit->u32Size);
        }
    }
    return i32Ret;
}

/**
 * @brief  EFM REMAP de-initialize.
 * @param  None
 * @retval None
 */
void EFM_REMAP_DeInit(void)
{
    DDL_ASSERT(IS_EFM_REMAP_UNLOCK());

    WRITE_REG32(CM_EFM->MMF_REMCR0, 0UL);
    WRITE_REG32(CM_EFM->MMF_REMCR1, 0UL);
}

/**
 * @brief  Enable or disable REMAP function.
 * @param  [in] u8RemapIdx      Specifies the remap ID.
 * @param  [in] enNewState      An @ref en_functional_state_t enumeration value.
 * @retval None
 */
void EFM_REMAP_Cmd(uint8_t u8RemapIdx, en_functional_state_t enNewState)
{
    __IO uint32_t *REMCRx;

    DDL_ASSERT(IS_EFM_REMAP_UNLOCK());
    DDL_ASSERT(IS_EFM_REMAP_IDX(u8RemapIdx));
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));

    REMCRx = &REMCR_REG(u8RemapIdx);
    if (ENABLE == enNewState) {
        SET_REG32_BIT(*REMCRx, EFM_MMF_REMCR_EN);
    } else {
        CLR_REG32_BIT(*REMCRx, EFM_MMF_REMCR_EN);
    }
}

/**
 * @brief  Set specified REMAP target address.
 * @param  [in] u8RemapIdx      Specifies the remap ID.
 * @param  [in] u32Addr         Specifies the target address.
 * @retval None
 */
void EFM_REMAP_SetAddr(uint8_t u8RemapIdx, uint32_t u32Addr)
{
    __IO uint32_t *REMCRx;

    DDL_ASSERT(IS_EFM_REMAP_UNLOCK());
    DDL_ASSERT(IS_EFM_REMAP_IDX(u8RemapIdx));
    DDL_ASSERT(IS_EFM_REMAP_ADDR(u32Addr));

    REMCRx = &REMCR_REG(u8RemapIdx);
    MODIFY_REG32(*REMCRx, EFM_MMF_REMCR_RMTADDR, u32Addr);
}

/**
 * @brief  Set specified REMAP size.
 * @param  [in] u8RemapIdx      Specifies the remap ID.
 * @param  [in] u32Size         Specifies the remap size.
 * @retval None
 */
void EFM_REMAP_SetSize(uint8_t u8RemapIdx, uint32_t u32Size)
{
    __IO uint32_t *REMCRx;

    DDL_ASSERT(IS_EFM_REMAP_UNLOCK());
    DDL_ASSERT(IS_EFM_REMAP_IDX(u8RemapIdx));
    DDL_ASSERT(IS_EFM_REMAP_SIZE(u32Size));

    REMCRx = &REMCR_REG(u8RemapIdx);
    MODIFY_REG32(*REMCRx, EFM_MMF_REMCR_RMSIZE, u32Size);
}

/**
 * @brief  Enable efm protect.
 * @param  [in] u8Level      Specifies the protect level. @ref EFM_Protect_Level
 * @retval None
 */
void EFM_Protect_Enable(uint8_t u8Level)
{
    uint8_t u8Code[12] = {0};

    (void)EFM_Program(EFM_SECURITY_ADDR1, u8Code, sizeof(u8Code));
    if (EFM_PROTECT_LEVEL1 == u8Level) {
        (void)EFM_ProgramWord(EFM_PROTECT1_ADDR, EFM_PROTECT1_KEY);
    } else if (EFM_PROTECT_LEVEL2 == u8Level) {
        (void)EFM_ProgramWord(EFM_PROTECT2_ADDR, EFM_PROTECT2_KEY);
    } else {
        /* rsvd */
    }
}

/**
 * @brief  Write the security code.
 * @param  [in] pu8Buf       Specifies the security code.
 * @param  [in] u32Len       Specified the length of the security code.
 * @retval int32_t
 */
int32_t EFM_WriteSecurityCode(const uint8_t *pu8Buf, uint32_t u32Len)
{
    int32_t i32Ret;
    uint32_t u32Tmp;
    uint32_t u32LoopWords = u32Len >> 2UL;
    uint32_t *u32pSource = (uint32_t *)(uint32_t)pu8Buf;
    uint32_t *u32pDest = (uint32_t *)EFM_SECURITY_ADDR;

    DDL_ASSERT(IS_EFM_REG_UNLOCK());
    DDL_ASSERT(IS_EFM_FWMC_UNLOCK());
    DDL_ASSERT(IS_EFM_SECURITY_CODE_LEN(u32Len));

    /* Clear the error flag. */
    EFM_ClearStatus(EFM_FLAG_ALL);
    /* Get CACHE status */
    u32Tmp = READ_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);
    /* Disable CACHE */
    CLR_REG32_BIT(CM_EFM->FRMC, EFM_CACHE_ALL);

    /* Set sector erase mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_ERASE_SECTOR);
    /* Erase */
    RW_MEM32(EFM_SECURITY_ADDR) = 0UL;
    /* Wait operate end */
    i32Ret = EFM_WaitEnd(0U, EFM_PGM_TIMEOUT);

    /* Set single program mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_PGM_SINGLE);
    while ((u32LoopWords-- > 0UL) && (LL_OK == i32Ret)) {
        /* program data. */
        *u32pDest++ = *u32pSource++;
        /* Wait operate end */
        i32Ret = EFM_WaitEnd(0U, EFM_PGM_TIMEOUT);
    }

    /* Set read only mode. */
    MODIFY_REG32(CM_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MD_READONLY);

    /* Recover CACHE function */
    MODIFY_REG32(CM_EFM->FRMC, EFM_CACHE_ALL, u32Tmp);
    return i32Ret;
}

/**
 * @}
 */

#endif  /* LL_EFM_ENABLE */

/**
 * @}
 */

/**
 * @}
 */

/******************************************************************************
 * EOF (not truncated)
 *****************************************************************************/
