/**
 ******************************************************************************
 * @file    bl702_sec_ef_ctrl.c
 * @version V1.0
 * @date
 * @brief   This file is the standard driver c file
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; COPYRIGHT(c) 2020 Bouffalo Lab</center></h2>
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *   1. Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice,
 *      this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 *   3. Neither the name of Bouffalo Lab nor the names of its contributors
 *      may be used to endorse or promote products derived from this software
 *      without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************
 */

#include "bl702_ef_ctrl.h"
#include "ef_data_0_reg.h"
#include "string.h"

/** @addtogroup  BL702_Peripheral_Driver
 *  @{
 */

/** @addtogroup  SEC_EF_CTRL
 *  @{
 */

/** @defgroup  SEC_EF_CTRL_Private_Macros
 *  @{
 */
#define EF_CTRL_EFUSE_CYCLE_PROTECT (0xbf << 24)
#define EF_CTRL_EFUSE_CTRL_PROTECT  (0xbf << 8)
#define EF_CTRL_DFT_TIMEOUT_VAL     (160 * 1000)
#ifndef BOOTROM
#define EF_CTRL_LOAD_BEFORE_READ_R0 EF_Ctrl_Load_Efuse_R0()
#else
#define EF_CTRL_LOAD_BEFORE_READ_R0
#endif
#define EF_CTRL_DATA0_CLEAR EF_Ctrl_Clear(0, EF_CTRL_EFUSE_R0_SIZE / 4)

/*@} end of group SEC_EF_CTRL_Private_Macros */

/** @defgroup  SEC_EF_CTRL_Private_Types
 *  @{
 */

/*@} end of group SEC_EF_CTRL_Private_Types */

/** @defgroup  SEC_EF_CTRL_Private_Variables
 *  @{
 */

/*@} end of group SEC_EF_CTRL_Private_Variables */

/** @defgroup  SEC_EF_CTRL_Global_Variables
 *  @{
 */

/*@} end of group SEC_EF_CTRL_Global_Variables */

/** @defgroup  SEC_EF_CTRL_Private_Fun_Declaration
 *  @{
 */

/*@} end of group SEC_EF_CTRL_Private_Fun_Declaration */

/** @defgroup  SEC_EF_CTRL_Private_Functions
 *  @{
 */

/****************************************************************************
 * @brief  Switch efuse region 0 control to AHB clock
 *
 * @param  None
 *
 * @return None
 *
 *******************************************************************************/
#ifndef BFLB_USE_ROM_DRIVER
__WEAK
/* static */ void ATTR_TCM_SECTION EF_Ctrl_Sw_AHB_Clk_0(void) {
  uint32_t tmpVal;
  uint32_t timeout = EF_CTRL_DFT_TIMEOUT_VAL;

  while (EF_Ctrl_Busy() == SET) {
    timeout--;

    if (timeout == 0) {
      break;
    }
  }

  tmpVal = (EF_CTRL_EFUSE_CTRL_PROTECT) | (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS) | (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
           | (EF_CTRL_SAHB_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS) | (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS) | (0 << EF_CTRL_EF_IF_POR_DIG_POS) | (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
           | (0 << EF_CTRL_EF_IF_0_RW_POS) | (0 << EF_CTRL_EF_IF_0_TRIG_POS);

  BL_WR_REG(EF_CTRL_BASE, EF_CTRL_EF_IF_CTRL_0, tmpVal);
}
#endif

/****************************************************************************
 * @brief  Program efuse region 0
 *
 * @param  None
 *
 * @return None
 *
 *******************************************************************************/
static void EF_Ctrl_Program_Efuse_0(void) {
  uint32_t tmpVal;

  /* Select auto mode and select ef clock */
  tmpVal = (EF_CTRL_EFUSE_CTRL_PROTECT) | (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS) | (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
           | (EF_CTRL_EF_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS) | (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS) | (0 << EF_CTRL_EF_IF_POR_DIG_POS) | (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
           | (0 << EF_CTRL_EF_IF_0_RW_POS) | (0 << EF_CTRL_EF_IF_0_TRIG_POS);
  BL_WR_REG(EF_CTRL_BASE, EF_CTRL_EF_IF_CTRL_0, tmpVal);

  /* Program */
  tmpVal = (EF_CTRL_EFUSE_CTRL_PROTECT) | (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS) | (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
           | (EF_CTRL_EF_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS) | (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS) | (1 << EF_CTRL_EF_IF_POR_DIG_POS) | (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
           | (1 << EF_CTRL_EF_IF_0_RW_POS) | (0 << EF_CTRL_EF_IF_0_TRIG_POS);
  BL_WR_REG(EF_CTRL_BASE, EF_CTRL_EF_IF_CTRL_0, tmpVal);

  /* Add delay for POR to be stable */
  BL702_Delay_US(4);

  /* Trigger */
  tmpVal = (EF_CTRL_EFUSE_CTRL_PROTECT) | (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS) | (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
           | (EF_CTRL_EF_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS) | (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS) | (1 << EF_CTRL_EF_IF_POR_DIG_POS) | (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
           | (1 << EF_CTRL_EF_IF_0_RW_POS) | (1 << EF_CTRL_EF_IF_0_TRIG_POS);
  BL_WR_REG(EF_CTRL_BASE, EF_CTRL_EF_IF_CTRL_0, tmpVal);
}

/*@} end of group SEC_EF_CTRL_Private_Functions */

/** @defgroup  SEC_EF_CTRL_Public_Functions
 *  @{
 */

/****************************************************************************
 * @brief  Load efuse region 0
 *
 * @param  None
 *
 * @return None
 *
 *******************************************************************************/
#ifndef BFLB_USE_ROM_DRIVER
__WEAK
void ATTR_TCM_SECTION EF_Ctrl_Load_Efuse_R0(void) {
  uint32_t tmpVal;
  uint32_t timeout = EF_CTRL_DFT_TIMEOUT_VAL;

  EF_CTRL_DATA0_CLEAR;

  /* Trigger read */
  tmpVal = (EF_CTRL_EFUSE_CTRL_PROTECT) | (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS) | (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
           | (EF_CTRL_EF_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS) | (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS) | (0 << EF_CTRL_EF_IF_POR_DIG_POS) | (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
           | (0 << EF_CTRL_EF_IF_0_RW_POS) | (0 << EF_CTRL_EF_IF_0_TRIG_POS);
  BL_WR_REG(EF_CTRL_BASE, EF_CTRL_EF_IF_CTRL_0, tmpVal);

  tmpVal = (EF_CTRL_EFUSE_CTRL_PROTECT) | (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS) | (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
           | (EF_CTRL_EF_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS) | (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS) | (0 << EF_CTRL_EF_IF_POR_DIG_POS) | (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
           | (0 << EF_CTRL_EF_IF_0_RW_POS) | (1 << EF_CTRL_EF_IF_0_TRIG_POS);
  BL_WR_REG(EF_CTRL_BASE, EF_CTRL_EF_IF_CTRL_0, tmpVal);

  BL702_Delay_US(10);

  /* Wait for efuse control idle*/
  do {
    tmpVal = BL_RD_REG(EF_CTRL_BASE, EF_CTRL_EF_IF_CTRL_0);
    timeout--;

    if (timeout == 0) {
      break;
    }
  } while (BL_IS_REG_BIT_SET(tmpVal, EF_CTRL_EF_IF_0_BUSY) ||

           (!BL_IS_REG_BIT_SET(tmpVal, EF_CTRL_EF_IF_0_AUTOLOAD_DONE)));

  /* Switch to AHB clock */
  tmpVal = (EF_CTRL_EFUSE_CTRL_PROTECT) | (EF_CTRL_OP_MODE_AUTO << EF_CTRL_EF_IF_0_MANUAL_EN_POS) | (EF_CTRL_PARA_DFT << EF_CTRL_EF_IF_0_CYC_MODIFY_POS)
           | (EF_CTRL_SAHB_CLK << EF_CTRL_EF_CLK_SAHB_DATA_SEL_POS) | (1 << EF_CTRL_EF_IF_AUTO_RD_EN_POS) | (0 << EF_CTRL_EF_IF_POR_DIG_POS) | (1 << EF_CTRL_EF_IF_0_INT_CLR_POS)
           | (0 << EF_CTRL_EF_IF_0_RW_POS) | (0 << EF_CTRL_EF_IF_0_TRIG_POS);
  BL_WR_REG(EF_CTRL_BASE, EF_CTRL_EF_IF_CTRL_0, tmpVal);
}
#endif

/****************************************************************************
 * @brief  Check efuse busy status
 *
 * @param  None
 *
 * @return SET or RESET
 *
 *******************************************************************************/
#ifndef BFLB_USE_ROM_DRIVER
__WEAK
BL_Sts_Type ATTR_TCM_SECTION EF_Ctrl_Busy(void) {
  uint32_t tmpVal;

  tmpVal = BL_RD_REG(EF_CTRL_BASE, EF_CTRL_EF_IF_CTRL_0);

  if (BL_IS_REG_BIT_SET(tmpVal, EF_CTRL_EF_IF_0_BUSY)) {
    return SET;
  }

  return RESET;
}
#endif

/****************************************************************************
 * @brief  Check efuse whether finish loading
 *
 * @param  None
 *
 * @return SET or RESET
 *
 *******************************************************************************/
BL_Sts_Type EF_Ctrl_AutoLoad_Done(void) {
  uint32_t tmpVal;

  tmpVal = BL_RD_REG(EF_CTRL_BASE, EF_CTRL_EF_IF_CTRL_0);

  if (BL_IS_REG_BIT_SET(tmpVal, EF_CTRL_EF_IF_0_AUTOLOAD_DONE)) {
    return SET;
  } else {
    return RESET;
  }
}

/****************************************************************************
 * @brief  Efuse write debug password
 *
 * @param  passWdLow: password low 32 bits
 * @param  passWdHigh: password high 32 bits
 * @param  program: program to efuse entity or not
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Write_Dbg_Pwd(uint32_t passWdLow, uint32_t passWdHigh, uint8_t program) {
  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_DBG_PWD_LOW, passWdLow);
  BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_DBG_PWD_HIGH, passWdHigh);

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }
}

/****************************************************************************
 * @brief  Efuse read debug password
 *
 * @param  passWdLow: password low 32 bits pointer to store value
 * @param  passWdHigh: password high 32 bits pointer to store value
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Read_Dbg_Pwd(uint32_t *passWdLow, uint32_t *passWdHigh) {
  /* Trigger read data from efuse */
  EF_CTRL_LOAD_BEFORE_READ_R0;

  *passWdLow  = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_DBG_PWD_LOW);
  *passWdHigh = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_DBG_PWD_HIGH);
}

/****************************************************************************
 * @brief  Efuse lock reading for passwd
 *
 * @param  program: program to efuse entity or not
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Readlock_Dbg_Pwd(uint8_t program) {
  uint32_t tmpVal;

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_LOCK);
  tmpVal = BL_SET_REG_BIT(tmpVal, EF_DATA_0_RD_LOCK_DBG_PWD);
  BL_WR_REG(EF_DATA_BASE, EF_DATA_0_LOCK, tmpVal);

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }
}

/****************************************************************************
 * @brief  Efuse lock writing for passwd
 *
 * @param  program: program to efuse entity or not
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Writelock_Dbg_Pwd(uint8_t program) {
  uint32_t tmpVal;

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_LOCK);
  tmpVal = BL_SET_REG_BIT(tmpVal, EF_DATA_0_WR_LOCK_DBG_PWD);
  BL_WR_REG(EF_DATA_BASE, EF_DATA_0_LOCK, tmpVal);

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }
}

/****************************************************************************
 * @brief  Efuse read security configuration
 *
 * @param  cfg: security configuration pointer
 * @param  program: program to efuse entity or not
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Write_Secure_Cfg(EF_Ctrl_Sec_Param_Type *cfg, uint8_t program) {
  uint32_t tmpVal;

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_CFG_0);
  tmpVal = BL_SET_REG_BITS_VAL(tmpVal, EF_DATA_0_EF_DBG_MODE, cfg->ef_dbg_mode);
  tmpVal = BL_SET_REG_BITS_VAL(tmpVal, EF_DATA_0_EF_DBG_JTAG_0_DIS, cfg->ef_dbg_jtag_0_dis);
  tmpVal = BL_SET_REG_BITS_VAL(tmpVal, EF_DATA_0_EF_SBOOT_EN, cfg->ef_sboot_en);
  BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_CFG_0, tmpVal);

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }
}

/****************************************************************************
 * @brief  Efuse read security configuration
 *
 * @param  cfg: security configuration pointer
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Read_Secure_Cfg(EF_Ctrl_Sec_Param_Type *cfg) {
  uint32_t tmpVal;

  /* Trigger read data from efuse */
  EF_CTRL_LOAD_BEFORE_READ_R0;

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_CFG_0);

  cfg->ef_dbg_mode       = (EF_Ctrl_Dbg_Mode_Type)BL_GET_REG_BITS_VAL(tmpVal, EF_DATA_0_EF_DBG_MODE);
  cfg->ef_dbg_jtag_0_dis = BL_GET_REG_BITS_VAL(tmpVal, EF_DATA_0_EF_DBG_JTAG_0_DIS);
  cfg->ef_sboot_en       = BL_GET_REG_BITS_VAL(tmpVal, EF_DATA_0_EF_SBOOT_EN);
}

/****************************************************************************
 * @brief  Efuse write security boot configuration
 *
 * @param  sign[1]: Sign configuration pointer
 * @param  aes[1]: AES configuration pointer
 * @param  program: program to efuse entity or not
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Write_Secure_Boot(EF_Ctrl_Sign_Type sign[1], EF_Ctrl_SF_AES_Type aes[1], uint8_t program) {
  uint32_t tmpVal;

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_CFG_0);

  tmpVal = BL_SET_REG_BITS_VAL(tmpVal, EF_DATA_0_EF_SBOOT_SIGN_MODE, sign[0]);

  if (aes[0] != EF_CTRL_SF_AES_NONE) {
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, EF_DATA_0_EF_SF_AES_MODE, aes[0]);
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, EF_DATA_0_EF_CPU0_ENC_EN, 1);
  }

  BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_CFG_0, tmpVal);

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }
}

/****************************************************************************
 * @brief  Efuse write security boot configuration
 *
 * @param  sign[1]: Sign configuration pointer
 * @param  aes[1]: AES configuration pointer
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Read_Secure_Boot(EF_Ctrl_Sign_Type sign[1], EF_Ctrl_SF_AES_Type aes[1]) {
  uint32_t tmpVal;
  uint32_t tmpVal2;

  /* Trigger read data from efuse */
  EF_CTRL_LOAD_BEFORE_READ_R0;

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_CFG_0);

  tmpVal2 = BL_GET_REG_BITS_VAL(tmpVal, EF_DATA_0_EF_SBOOT_SIGN_MODE);
  sign[0] = (EF_Ctrl_Sign_Type)(tmpVal2 & 0x01);

  tmpVal2 = BL_GET_REG_BITS_VAL(tmpVal, EF_DATA_0_EF_CPU0_ENC_EN);

  if (tmpVal2) {
    aes[0] = (EF_Ctrl_SF_AES_Type)BL_GET_REG_BITS_VAL(tmpVal, EF_DATA_0_EF_SF_AES_MODE);
  } else {
    aes[0] = EF_CTRL_SF_AES_NONE;
  }
}

/****************************************************************************
 * @brief  Analog Trim parity calculate
 *
 * @param  val: Value of efuse trim data
 * @param  len: Length of bit to calculate
 *
 * @return Parity bit value
 *
 *******************************************************************************/
#ifndef BFLB_USE_ROM_DRIVER
__WEAK
uint8_t ATTR_CLOCK_SECTION EF_Ctrl_Get_Trim_Parity(uint32_t val, uint8_t len) {
  uint8_t cnt = 0;
  uint8_t i   = 0;

  for (i = 0; i < len; i++) {
    if (val & (1 << i)) {
      cnt++;
    }
  }

  return cnt & 0x01;
}
#endif

/****************************************************************************
 * @brief  Efuse write analog trim
 *
 * @param  index: index of analog trim
 * @param  trim: trim value
 * @param  program: program to efuse entity or not
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Write_Ana_Trim(uint32_t index, uint32_t trim, uint8_t program) {
  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  if (index == 0) {
    BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_ANA_TRIM_0, trim);
  }

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }
}

/****************************************************************************
 * @brief  Efuse read analog trim
 *
 * @param  index: index of analog trim
 * @param  trim: trim value
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Read_Ana_Trim(uint32_t index, uint32_t *trim) {
  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  EF_CTRL_LOAD_BEFORE_READ_R0;

  if (index == 0) {
    *trim = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_ANA_TRIM_0);
  }
}

/****************************************************************************
 * @brief  Efuse read RC32M trim
 *
 * @param  trim: Trim data pointer
 *
 * @return None
 *
 *******************************************************************************/
#ifndef BFLB_USE_ROM_DRIVER
__WEAK
void ATTR_CLOCK_SECTION EF_Ctrl_Read_RC32M_Trim(Efuse_Ana_RC32M_Trim_Type *trim) {
  uint32_t tmpVal = 0;
  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  EF_CTRL_LOAD_BEFORE_READ_R0;

  tmpVal                         = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_ANA_TRIM_0);
  trim->trimRc32mCodeFrExt       = (tmpVal >> 10) & 0xff;
  trim->trimRc32mCodeFrExtParity = (tmpVal >> 18) & 0x01;
  trim->trimRc32mExtCodeEn       = (tmpVal >> 19) & 0x01;
}
#endif

/****************************************************************************
 * @brief  Efuse read RC32K trim
 *
 * @param  trim: Trim data pointer
 *
 * @return None
 *
 *******************************************************************************/
#ifndef BFLB_USE_ROM_DRIVER
__WEAK
void ATTR_CLOCK_SECTION EF_Ctrl_Read_RC32K_Trim(Efuse_Ana_RC32K_Trim_Type *trim) {
  uint32_t tmpVal = 0;
  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  EF_CTRL_LOAD_BEFORE_READ_R0;

  tmpVal                         = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_ANA_TRIM_0);
  trim->trimRc32kCodeFrExt       = (tmpVal >> 20) & 0x3ff;
  trim->trimRc32kCodeFrExtParity = (tmpVal >> 30) & 0x01;
  trim->trimRc32kExtCodeEn       = (tmpVal >> 31) & 0x01;
}
#endif

/****************************************************************************
 * @brief  Efuse read TSEN trim
 *
 * @param  trim: Trim data pointer
 *
 * @return None
 *
 *******************************************************************************/
void ATTR_CLOCK_SECTION EF_Ctrl_Read_TSEN_Trim(Efuse_TSEN_Refcode_Corner_Type *trim) {
  uint32_t tmpVal = 0;
  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  EF_CTRL_LOAD_BEFORE_READ_R0;

  tmpVal                    = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W3);
  trim->tsenRefcodeCornerEn = tmpVal & 0x01;

  tmpVal                        = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_LOCK);
  trim->tsenRefcodeCorner       = tmpVal & 0xfff;
  trim->tsenRefcodeCornerParity = (tmpVal >> 12) & 0x01;

  trim->tsenRefcodeCornerVersion = 0;
}

/****************************************************************************
 * @brief  Efuse read ADC Gain trim
 *
 * @param  trim: Trim data pointer
 *
 * @return None
 *
 *******************************************************************************/
void ATTR_CLOCK_SECTION EF_Ctrl_Read_ADC_Gain_Trim(Efuse_ADC_Gain_Coeff_Type *trim) {
  uint32_t tmpVal = 0;
  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  EF_CTRL_LOAD_BEFORE_READ_R0;

  tmpVal                   = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W3);
  trim->adcGainCoeff       = (tmpVal >> 1) & 0xfff;
  trim->adcGainCoeffParity = (tmpVal >> 13) & 0x01;
  trim->adcGainCoeffEn     = (tmpVal >> 14) & 0x01;
}

/****************************************************************************
 * @brief  Efuse write software usage
 *
 * @param  index: index of software usage
 * @param  usage: usage value
 * @param  program: program to efuse entity or not
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Write_Sw_Usage(uint32_t index, uint32_t usage, uint8_t program) {
  /* switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  if (index == 0) {
    BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_SW_USAGE_0, usage);
  }

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }
}

/****************************************************************************
 * @brief  Efuse read software usage
 *
 * @param  index: index of software usage
 * @param  usage: usage value
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Read_Sw_Usage(uint32_t index, uint32_t *usage) {
  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  EF_CTRL_LOAD_BEFORE_READ_R0;

  if (index == 0) {
    *usage = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_SW_USAGE_0);
  }
}

/****************************************************************************
 * @brief  Efuse read software usage
 *
 * @param  index: index of software usage
 * @param  program: usage value
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Writelock_Sw_Usage(uint32_t index, uint8_t program) {
  uint32_t tmpVal;

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_LOCK);

  if (index == 0) {
    tmpVal = BL_SET_REG_BIT(tmpVal, EF_DATA_0_WR_LOCK_SW_USAGE_0);
  }

  BL_WR_REG(EF_DATA_BASE, EF_DATA_0_LOCK, tmpVal);

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }
}

/****************************************************************************
 * @brief  Efuse write MAC address
 *
 * @param  mac[6]: MAC address buffer
 * @param  program: program to efuse entity or not
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Write_MAC_Address(uint8_t mac[6], uint8_t program) {
  uint8_t *maclow  = (uint8_t *)mac;
  uint8_t *machigh = (uint8_t *)(mac + 4);
  uint32_t tmpVal;

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  /* The low 32 bits */
  BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_WIFI_MAC_LOW, BL_RDWD_FRM_BYTEP(maclow));
  /* The high 16 bits */
  tmpVal = machigh[0] + (machigh[1] << 8);
  BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_WIFI_MAC_HIGH, tmpVal);

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }
}

/****************************************************************************
 * @brief  Efuse Ctrl get zero bit count
 *
 * @param  val: Value to count
 *
 * @return Zero bit count
 *
 *******************************************************************************/
static uint32_t EF_Ctrl_Get_Byte_Zero_Cnt(uint8_t val) {
  uint32_t cnt = 0;
  uint32_t i   = 0;

  for (i = 0; i < 8; i++) {
    if ((val & (1 << i)) == 0) {
      cnt += 1;
    }
  }

  return cnt;
}

/****************************************************************************
 * @brief  Efuse read MAC address
 *
 * @param  mac[8]: MAC address buffer
 *
 * @return SUCCESS or ERROR
 *
 *******************************************************************************/
BL_Err_Type EF_Ctrl_Read_MAC_Address(uint8_t mac[8]) {
  uint8_t *maclow  = (uint8_t *)mac;
  uint8_t *machigh = (uint8_t *)(mac + 4);
  uint32_t tmpVal;
  uint32_t i   = 0;
  uint32_t cnt = 0;

  /* Trigger read data from efuse */
  EF_CTRL_LOAD_BEFORE_READ_R0;

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_WIFI_MAC_LOW);
  BL_WRWD_TO_BYTEP(maclow, tmpVal);

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_WIFI_MAC_HIGH);
  BL_WRWD_TO_BYTEP(machigh, tmpVal);

  /* Get original parity */
  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W2);

  /* Check parity */
  for (i = 0; i < 8; i++) {
    cnt += EF_Ctrl_Get_Byte_Zero_Cnt(mac[i]);
  }

  if ((cnt & 0x3f) == (tmpVal & 0x3f)) {
    return SUCCESS;
  } else {
    return ERROR;
  }
}

/****************************************************************************
 * @brief  Efuse read MAC address
 *
 * @param  mac[7]: MAC address buffer
 *
 * @return SUCCESS or ERROR
 *
 *******************************************************************************/
BL_Err_Type EF_Ctrl_Read_MAC_Address_Raw(uint8_t mac[7]) {
  uint8_t *maclow  = (uint8_t *)mac;
  uint8_t *machigh = (uint8_t *)(mac + 4);
  uint32_t tmpVal;

  /* Trigger read data from efuse */
  EF_CTRL_LOAD_BEFORE_READ_R0;

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_WIFI_MAC_LOW);
  BL_WRWD_TO_BYTEP(maclow, tmpVal);

  tmpVal     = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_WIFI_MAC_HIGH);
  machigh[0] = tmpVal & 0xff;
  machigh[1] = (tmpVal >> 8) & 0xff;
  machigh[2] = (tmpVal >> 16) & 0xff;

  return SUCCESS;
}

/****************************************************************************
 * @brief  Efuse lock writing for MAC address
 *
 * @param  program: program to efuse entity or not
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Writelock_MAC_Address(uint8_t program) {
  uint32_t tmpVal;

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_LOCK);
  tmpVal = BL_SET_REG_BIT(tmpVal, EF_DATA_0_WR_LOCK_WIFI_MAC);
  BL_WR_REG(EF_DATA_BASE, EF_DATA_0_LOCK, tmpVal);

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }
}

/****************************************************************************
 * @brief  Whether a value bits is all zero
 *
 * @param  val: value to check
 * @param  start: start bit
 * @param  len: total length of bits to check
 *
 * @return 1 for all bits zero 0 for others
 *
 *******************************************************************************/
uint8_t EF_Ctrl_Is_All_Bits_Zero(uint32_t val, uint8_t start, uint8_t len) {
  uint32_t mask = 0;

  val = (val >> start);

  if (len >= 32) {
    mask = 0xffffffff;
  } else {
    mask = (1 << len) - 1;
  }

  if ((val & mask) == 0) {
    return 1;
  } else {
    return 0;
  }
}

/****************************************************************************
 * @brief  Whether MAC address slot is empty
 *
 * @param  slot: MAC address slot
 * @param  reload: whether  reload to check
 *
 * @return 0 for all slots full,1 for others
 *
 *******************************************************************************/
uint8_t EF_Ctrl_Is_MAC_Address_Slot_Empty(uint8_t slot, uint8_t reload) {
  uint32_t tmp1 = 0xffffffff, tmp2 = 0xffffffff;
  uint32_t part1Empty = 0, part2Empty = 0;

  if (slot == 0) {
    /* Switch to AHB clock */
    EF_Ctrl_Sw_AHB_Clk_0();

    if (reload) {
      EF_CTRL_LOAD_BEFORE_READ_R0;
    }

    tmp1 = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_WIFI_MAC_LOW);
    tmp2 = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_WIFI_MAC_HIGH);
  } else if (slot == 1) {
    /* Switch to AHB clock */
    EF_Ctrl_Sw_AHB_Clk_0();

    if (reload) {
      EF_CTRL_LOAD_BEFORE_READ_R0;
    }

    tmp1 = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W0);
    tmp2 = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W1);
  } else if (slot == 2) {
    /* Switch to AHB clock */
    EF_Ctrl_Sw_AHB_Clk_0();

    if (reload) {
      EF_CTRL_LOAD_BEFORE_READ_R0;
    }

    tmp1 = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_DBG_PWD_LOW);
    tmp2 = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_DBG_PWD_HIGH);
  }

  part1Empty = (EF_Ctrl_Is_All_Bits_Zero(tmp1, 0, 32));
  part2Empty = (EF_Ctrl_Is_All_Bits_Zero(tmp2, 0, 22));

  return (part1Empty && part2Empty);
}

/****************************************************************************
 * @brief  Efuse write optional MAC address
 *
 * @param  slot: MAC address slot
 * @param  mac[8]: MAC address buffer
 * @param  program: Whether program
 *
 * @return SUCCESS or ERROR
 *
 *******************************************************************************/
BL_Err_Type EF_Ctrl_Write_MAC_Address_Opt(uint8_t slot, uint8_t mac[8], uint8_t program) {
  uint8_t *maclow  = (uint8_t *)mac;
  uint8_t *machigh = (uint8_t *)(mac + 4);
  uint32_t tmpVal  = 0;
  uint32_t i       = 0;
  uint32_t cnt     = 0;

  if (slot >= 3) {
    return ERROR;
  }

  if (slot == 2) {
    /* Switch to AHB clock */
    EF_Ctrl_Sw_AHB_Clk_0();
  } else {
    /* Switch to AHB clock */
    EF_Ctrl_Sw_AHB_Clk_0();
  }

  /* The low 32 bits */
  if (slot == 0) {
    BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_WIFI_MAC_LOW, BL_RDWD_FRM_BYTEP(maclow));
  } else if (slot == 1) {
    BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W0, BL_RDWD_FRM_BYTEP(maclow));
  } else if (slot == 2) {
    BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_DBG_PWD_LOW, BL_RDWD_FRM_BYTEP(maclow));
  }

  /* The high 32 bits */
  if (slot == 0) {
    BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_WIFI_MAC_HIGH, BL_RDWD_FRM_BYTEP(machigh));
  } else if (slot == 1) {
    BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W1, BL_RDWD_FRM_BYTEP(machigh));
  } else if (slot == 2) {
    BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_DBG_PWD_HIGH, BL_RDWD_FRM_BYTEP(machigh));
  }

  cnt = 0;
  for (i = 0; i < 8; i++) {
    cnt += EF_Ctrl_Get_Byte_Zero_Cnt(mac[i]);
  }
  cnt &= 0x3f;
  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W2);
  if (slot == 0) {
    tmpVal |= (cnt << 0);
  } else if (slot == 1) {
    tmpVal |= (cnt << 6);
  } else if (slot == 2) {
    tmpVal |= (cnt << 12);
  }
  BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W2, tmpVal);

  if (program) {
    if (slot == 2) {
      EF_Ctrl_Program_Efuse_0();
    } else {
      EF_Ctrl_Program_Efuse_0();
    }
  }
  return SUCCESS;
}

/****************************************************************************
 * @brief  Efuse read optional MAC address
 *
 * @param  slot: MAC address slot
 * @param  mac[8]: MAC address buffer
 * @param  reload: Whether reload
 *
 * @return SUCCESS or ERROR
 *
 *******************************************************************************/
BL_Err_Type EF_Ctrl_Read_MAC_Address_Opt(uint8_t slot, uint8_t mac[8], uint8_t reload) {
  uint8_t *maclow  = (uint8_t *)mac;
  uint8_t *machigh = (uint8_t *)(mac + 4);
  uint32_t tmpVal  = 0;
  uint32_t i       = 0;
  uint32_t cnt     = 0;
  uint32_t crc     = 0;

  if (slot >= 3) {
    return ERROR;
  }

  /* Trigger read data from efuse */
  if (reload) {
    if (slot == 2) {
      EF_CTRL_LOAD_BEFORE_READ_R0;
    } else {
      EF_CTRL_LOAD_BEFORE_READ_R0;
    }
  }

  if (slot == 0) {
    tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_WIFI_MAC_LOW);
  } else if (slot == 1) {
    tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W0);
  } else if (slot == 2) {
    tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_DBG_PWD_LOW);
  }
  BL_WRWD_TO_BYTEP(maclow, tmpVal);

  if (slot == 0) {
    tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_WIFI_MAC_HIGH);
  } else if (slot == 1) {
    tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W1);
  } else if (slot == 2) {
    tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_DBG_PWD_HIGH);
  }
  BL_WRWD_TO_BYTEP(machigh, tmpVal);

  /* Get original parity */
  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W2);
  if (slot == 0) {
    crc = ((tmpVal >> 0) & 0x3f);
  } else if (slot == 1) {
    crc = ((tmpVal >> 6) & 0x3f);
  } else if (slot == 2) {
    crc = ((tmpVal >> 12) & 0x3f);
  }

  /* Check parity */
  for (i = 0; i < 8; i++) {
    cnt += EF_Ctrl_Get_Byte_Zero_Cnt(mac[i]);
  }
  if ((cnt & 0x3f) == crc) {
    return SUCCESS;
  } else {
    return ERROR;
  }
}

/****************************************************************************
 * @brief  Efuse read chip ID
 *
 * @param  chipID[8]: Chip ID buffer
 *
 * @return SUCCESS or ERROR
 *
 *******************************************************************************/
BL_Err_Type EF_Ctrl_Read_Chip_ID(uint8_t chipID[8]) { return EF_Ctrl_Read_MAC_Address(chipID); }

/****************************************************************************
 * @brief  Efuse get chip PID&&VID
 *
 * @param  pid[1]: Chip PID
 * @param  vid[1]: Chip VID
 *
 * @return SUCCESS or ERROR
 *
 *******************************************************************************/
BL_Err_Type EF_Ctrl_Get_Chip_PIDVID(uint16_t pid[1], uint16_t vid[1]) {
  uint32_t tmpVal;

  /* Trigger read data from efuse */
  EF_CTRL_LOAD_BEFORE_READ_R0;

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_4_W3);
  pid[0] = (uint16_t)(tmpVal & 0xFFFF);
  vid[0] = (uint16_t)(tmpVal >> 16);

  return SUCCESS;
}

uint32_t EF_Ctrl_Get_Key_Slot_w0() {

  /* Trigger read data from efuse */
  EF_CTRL_LOAD_BEFORE_READ_R0;

  return BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_4_W0);
}
uint32_t EF_Ctrl_Get_Key_Slot_w1() {

  /* Trigger read data from efuse */
  EF_CTRL_LOAD_BEFORE_READ_R0;

  return BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_4_W1);
}

/****************************************************************************
 * @brief  Efuse get customer PID&&VID
 *
 * @param  pid[1]: Customer PID
 * @param  vid[1]: Customer VID
 *
 * @return SUCCESS or ERROR
 *
 *******************************************************************************/
BL_Err_Type EF_Ctrl_Get_Customer_PIDVID(uint16_t pid[1], uint16_t vid[1]) {
  uint32_t tmpVal;

  /* Trigger read data from efuse */
  EF_CTRL_LOAD_BEFORE_READ_R0;

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_4_W2);
  pid[0] = (uint16_t)(tmpVal & 0xFFFF);
  vid[0] = (uint16_t)(tmpVal >> 16);

  return SUCCESS;
}

/****************************************************************************
 * @brief  Efuse read device info
 *
 * @param  deviceInfo: Device info pointer
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Read_Device_Info(Efuse_Device_Info_Type *deviceInfo) {
  uint32_t  tmpVal;
  uint32_t *p = (uint32_t *)deviceInfo;

  /* Trigger read data from efuse */
  EF_CTRL_LOAD_BEFORE_READ_R0;

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W2);
  *p     = tmpVal;
}

/****************************************************************************
 * @brief  Whether Capcode is empty
 *
 * @param  slot: Cap code slot
 * @param  reload: Whether reload
 *
 * @return 0 for all slots full,1 for others
 *
 *******************************************************************************/
uint8_t EF_Ctrl_Is_CapCode_Empty(uint8_t slot, uint8_t reload) {
  uint32_t tmp = 0xffffffff;

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  if (reload) {
    EF_CTRL_LOAD_BEFORE_READ_R0;
  }

  if (slot == 0) {
    tmp = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W3);
    return (EF_Ctrl_Is_All_Bits_Zero(tmp, 25, 7));
  } else if (slot == 1) {
    tmp = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3);
    return (EF_Ctrl_Is_All_Bits_Zero(tmp, 9, 7));
  } else if (slot == 2) {
    tmp = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3);
    return (EF_Ctrl_Is_All_Bits_Zero(tmp, 25, 7));
  } else {
    return 0;
  }
}

/****************************************************************************
 * @brief  Efuse write Cap code
 *
 * @param  slot: Cap code slot
 * @param  code: Cap code value
 * @param  program: Whether program
 *
 * @return SUCCESS or ERROR
 *
 *******************************************************************************/
BL_Err_Type EF_Ctrl_Write_CapCode_Opt(uint8_t slot, uint8_t code, uint8_t program) {
  uint32_t tmp;
  uint8_t  trim;

  if (slot >= 3) {
    return ERROR;
  }

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();
  EF_CTRL_LOAD_BEFORE_READ_R0;

  trim = (code << 1);
  trim |= (1 << 0);

  if (slot == 0) {
    tmp = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W3);
    BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W3, tmp | (trim << 25));
  } else if (slot == 1) {
    tmp = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3);
    BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3, tmp | (trim << 9));
  } else if (slot == 2) {
    tmp = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3);
    BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3, tmp | (trim << 25));
  }

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }
  while (SET == EF_Ctrl_Busy())
    ;

  return SUCCESS;
}

/****************************************************************************
 * @brief  Efuse read Cap code
 *
 * @param  slot: Cap code slot
 * @param  code: Cap code pointer
 * @param  reload: Whether reload
 *
 * @return SUCCESS or ERROR
 *
 *******************************************************************************/
BL_Err_Type EF_Ctrl_Read_CapCode_Opt(uint8_t slot, uint8_t *code, uint8_t reload) {
  uint32_t tmp;
  uint8_t  trim = 0;

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  if (reload) {
    EF_CTRL_LOAD_BEFORE_READ_R0;
  }

  if (slot == 0) {
    tmp  = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W3);
    trim = (tmp >> 25) & 0x7f;
  } else if (slot == 1) {
    tmp  = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3);
    trim = (tmp >> 9) & 0x7f;
  } else if (slot == 2) {
    tmp  = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3);
    trim = (tmp >> 25) & 0x7f;
  }

  if (trim & 0x01) {
    *code = trim >> 1;
    return SUCCESS;
  }
  return ERROR;
}

/****************************************************************************
 * @brief  Whether power offset slot is empty
 *
 * @param  slot: Power offset code slot
 * @param  reload: Whether reload
 *
 * @return 0 for all slots full,1 for others
 *
 *******************************************************************************/
uint8_t EF_Ctrl_Is_PowerOffset_Slot_Empty(uint8_t slot, uint8_t reload) {
  uint32_t tmp1       = 0xffffffff;
  uint32_t part1Empty = 0, part2Empty = 0;

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  if (reload) {
    EF_CTRL_LOAD_BEFORE_READ_R0;
  }

  if (slot == 0) {
    tmp1       = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W3);
    part1Empty = (EF_Ctrl_Is_All_Bits_Zero(tmp1, 16, 9));
    part2Empty = 1;
  } else if (slot == 1) {
    tmp1       = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3);
    part1Empty = (EF_Ctrl_Is_All_Bits_Zero(tmp1, 0, 9));
    part2Empty = 1;
  } else if (slot == 2) {
    tmp1       = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3);
    part1Empty = (EF_Ctrl_Is_All_Bits_Zero(tmp1, 16, 9));
    part2Empty = 1;
  }

  return (part1Empty && part2Empty);
}

/****************************************************************************
 * @brief  Efuse write power offset
 *
 * @param  slot: Power offset slot
 * @param  pwrOffset[2]: Power offset value array
 * @param  program: Whether program
 *
 * @return SUCCESS or ERROR
 *
 *******************************************************************************/
BL_Err_Type EF_Ctrl_Write_PowerOffset_Opt(uint8_t slot, int8_t pwrOffset[2], uint8_t program) {
  uint32_t tmp   = 0;
  uint32_t k     = 0;
  uint32_t Value = 0;

  if (slot >= 3) {
    return ERROR;
  }

  for (k = 0; k < 2; k++) {
    /* Use 4 bits as signed value */
    if (pwrOffset[k] > 7) {
      pwrOffset[k] = 7;
    }
    if (pwrOffset[k] < -8) {
      pwrOffset[k] = -8;
    }
    Value += (uint32_t)(pwrOffset[k] & 0x0f) << (k * 4);
  }

  if (slot == 0) {
    tmp = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W3);
    tmp |= (Value << 17);
    tmp |= (1 << 16);
    BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W3, tmp);
  } else if (slot == 1) {
    tmp = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3);
    tmp |= (Value << 1);
    tmp |= (1 << 0);
    BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3, tmp);
  } else if (slot == 2) {
    tmp = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3);
    tmp |= (Value << 17);
    tmp |= (1 << 16);
    BL_WR_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3, tmp);
  }

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }

  while (SET == EF_Ctrl_Busy())
    ;

  return SUCCESS;
}

/****************************************************************************
 * @brief  Efuse read poweroffset value
 *
 * @param  slot: Power offset slot
 * @param  pwrOffset[2]: Power offset array
 * @param  reload: Whether reload
 *
 * @return SUCCESS or ERROR
 *
 *******************************************************************************/
BL_Err_Type EF_Ctrl_Read_PowerOffset_Opt(uint8_t slot, int8_t pwrOffset[2], uint8_t reload) {
  uint32_t pwrOffsetValue = 0;
  uint32_t tmp            = 0, k;
  uint8_t  en             = 0;

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  if (reload) {
    EF_CTRL_LOAD_BEFORE_READ_R0;
  }

  if (slot == 0) {
    tmp            = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_5_W3);
    en             = (tmp >> 16) & 0x01;
    pwrOffsetValue = (tmp >> 17) & 0xff;
  } else if (slot == 1) {
    tmp            = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3);
    en             = (tmp >> 0) & 0x01;
    pwrOffsetValue = (tmp >> 1) & 0xff;
  } else if (slot == 2) {
    tmp            = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_EF_KEY_SLOT_3_W3);
    en             = (tmp >> 16) & 0x01;
    pwrOffsetValue = (tmp >> 17) & 0xff;
  }

  if (en) {
    for (k = 0; k < 2; k++) {
      tmp = (pwrOffsetValue >> (k * 4)) & 0x0f;
      if (tmp >= 8) {
        pwrOffset[k] = tmp - 16;
      } else {
        pwrOffset[k] = tmp;
      }
    }
    return SUCCESS;
  }
  return ERROR;
}

/****************************************************************************
 * @brief  Efuse write AES key
 *
 * @param  index: index of key slot
 * @param  keyData: key data buffer
 * @param  len: key data length in words
 * @param  program: program to efuse entity or not
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Write_AES_Key(uint8_t index, uint32_t *keyData, uint32_t len, uint8_t program) {
  uint32_t *pAESKeyStart0 = (uint32_t *)(EF_DATA_BASE + 0x1C);

  if (index > 5) {
    return;
  }

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  /* Every key is 4 words len*/
  BL702_MemCpy4(pAESKeyStart0 + index * 4, keyData, len);

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }
}

/****************************************************************************
 * @brief  Efuse read AES key from specified region and index
 *
 * @param  index: index of key slot
 * @param  keyData: key data buffer
 * @param  len: key data length in words
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Read_AES_Key(uint8_t index, uint32_t *keyData, uint32_t len) {
  uint32_t *pAESKeyStart0 = (uint32_t *)(EF_DATA_BASE + 0x1C);

  if (index > 5) {
    return;
  }

  /* Trigger read data from efuse*/
  EF_CTRL_LOAD_BEFORE_READ_R0;

  /* Every key is 4 words len*/
  BL702_MemCpy4(keyData, pAESKeyStart0 + index * 4, len);
}

/****************************************************************************
 * @brief  Efuse lock writing for aes key
 *
 * @param  index: index of key slot
 * @param  program: program to efuse entity or not
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Writelock_AES_Key(uint8_t index, uint8_t program) {
  uint32_t tmpVal;

  if (index > 5) {
    return;
  }

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_LOCK);

  if (index <= 3) {
    tmpVal |= (1 << (index + 19));
  } else {
    tmpVal |= (1 << (index + 19));
    tmpVal |= (1 << (index - 4 + 13));
  }

  BL_WR_REG(EF_DATA_BASE, EF_DATA_0_LOCK, tmpVal);

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }
}

/****************************************************************************
 * @brief  Efuse lock reading for aes key
 *
 * @param  index: index of key slot
 * @param  program: program to efuse entity or not
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Readlock_AES_Key(uint8_t index, uint8_t program) {
  uint32_t tmpVal;

  if (index > 5) {
    return;
  }

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_DATA_0_LOCK);
  tmpVal |= (1 << (index + 26));
  BL_WR_REG(EF_DATA_BASE, EF_DATA_0_LOCK, tmpVal);

  if (program) {
    EF_Ctrl_Program_Efuse_0();
  }
}

/****************************************************************************
 * @brief  Program data to efuse region 0
 *
 * @param  index: index of efuse in word
 * @param  data: data buffer
 * @param  len: data length
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Program_Direct_R0(uint32_t index, uint32_t *data, uint32_t len) {
  uint32_t *pEfuseStart0 = (uint32_t *)(EF_DATA_BASE + 0x00);

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  /* Add delay for CLK to be stable */
  BL702_Delay_US(4);

  BL702_MemCpy4(pEfuseStart0 + index, data, len);

  EF_Ctrl_Program_Efuse_0();
}

/****************************************************************************
 * @brief  Read data from efuse region 0
 *
 * @param  index: index of efuse in word
 * @param  data: data buffer
 * @param  len: data length
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Read_Direct_R0(uint32_t index, uint32_t *data, uint32_t len) {
  uint32_t *pEfuseStart0 = (uint32_t *)(EF_DATA_BASE + 0x00);

  EF_CTRL_LOAD_BEFORE_READ_R0;

  BL702_MemCpy4(data, pEfuseStart0 + index, len);
}

/****************************************************************************
 * @brief  Clear efuse data register
 *
 * @param  index: index of efuse in word
 * @param  len: data length
 *
 * @return None
 *
 *******************************************************************************/
#ifndef BFLB_USE_ROM_DRIVER
__WEAK
void ATTR_TCM_SECTION EF_Ctrl_Clear(uint32_t index, uint32_t len) {
  uint32_t *pEfuseStart0 = (uint32_t *)(EF_DATA_BASE + 0x00);
  uint32_t  i            = 0;

  /* Switch to AHB clock */
  EF_Ctrl_Sw_AHB_Clk_0();

  /* Clear data */
  for (i = 0; i < len; i++) {
    pEfuseStart0[index + i] = 0;
  }
}
#endif

/****************************************************************************
 * @brief  efuse ctrl crc enable
 *
 * @param  None
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Crc_Enable(void) {
  uint32_t tmpVal;

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_CTRL_EF_CRC_CTRL_0);
  tmpVal = BL_SET_REG_BIT(tmpVal, EF_CTRL_EF_CRC_TRIG);
  tmpVal = BL_CLR_REG_BIT(tmpVal, EF_CTRL_EF_CRC_MODE);
  tmpVal = BL_SET_REG_BIT(tmpVal, EF_CTRL_EF_CRC_DOUT_INV_EN);
  tmpVal = BL_CLR_REG_BIT(tmpVal, EF_CTRL_EF_CRC_DOUT_ENDIAN);
  tmpVal = BL_CLR_REG_BIT(tmpVal, EF_CTRL_EF_CRC_DIN_ENDIAN);
  tmpVal = BL_CLR_REG_BIT(tmpVal, EF_CTRL_EF_CRC_INT_CLR);
  tmpVal = BL_CLR_REG_BIT(tmpVal, EF_CTRL_EF_CRC_INT_SET);
  BL_WR_REG(EF_DATA_BASE, EF_CTRL_EF_CRC_CTRL_0, tmpVal);

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_CTRL_EF_CRC_CTRL_0);
  tmpVal = BL_SET_REG_BIT(tmpVal, EF_CTRL_EF_CRC_EN);
  BL_WR_REG(EF_DATA_BASE, EF_CTRL_EF_CRC_CTRL_0, tmpVal);
}

/****************************************************************************
 * @brief  efuse ctrl get crc busy status
 *
 * @param  None
 *
 * @return DISABLE or ENABLE
 *
 *******************************************************************************/
BL_Sts_Type EF_Ctrl_Crc_Is_Busy(void) {
  uint32_t tmpVal;

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_CTRL_EF_CRC_CTRL_0);
  return (BL_Sts_Type)BL_IS_REG_BIT_SET(tmpVal, EF_CTRL_EF_CRC_BUSY);
}

/****************************************************************************
 * @brief  efuse ctrl set golden value
 *
 * @param  goldenValue: Crc golden value
 *
 * @return None
 *
 *******************************************************************************/
void EF_Ctrl_Crc_Set_Golden(uint32_t goldenValue) { BL_WR_REG(EF_DATA_BASE, EF_CTRL_EF_CRC_CTRL_4, goldenValue); }

/****************************************************************************
 * @brief  efuse ctrl get crc result
 *
 * @param  None
 *
 * @return SUCCESS or ERROR
 *
 *******************************************************************************/
BL_Err_Type EF_Ctrl_Crc_Result(void) {
  uint32_t tmpVal;

  tmpVal = BL_RD_REG(EF_DATA_BASE, EF_CTRL_EF_CRC_CTRL_0);
  return (BL_Err_Type)BL_IS_REG_BIT_SET(tmpVal, EF_CTRL_EF_CRC_ERROR);
}

/*@} end of group SEC_EF_CTRL_Public_Functions */

/*@} end of group SEC_EF_CTRL */

/*@} end of group BL702_Peripheral_Driver */
