/**
  ******************************************************************************
  * @file    stm32l5xx_hal_flash_ex.c
  * @author  MCD Application Team
  * @brief   Extended FLASH HAL module driver.
  *          This file provides firmware functions to manage the following
  *          functionalities of the FLASH extended peripheral:
  *           + Extended programming operations functions
  *
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2019 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file in
  * the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  ******************************************************************************
 @verbatim
 ==============================================================================
                   ##### Flash Extended features #####
 ==============================================================================

  [..] Comparing to other previous devices, the FLASH interface for STM32L5xx
       devices contains the following additional features

       (+) Capacity up to 512 Kbytes with dual bank architecture supporting read-while-write
           capability (RWW)
       (+) Dual bank memory organization with possibility of 128-bits single bank
       (+) Watermark-based secure area including secure hide areas
       (+) Block-based secure pages

                        ##### How to use this driver #####
 ==============================================================================
  [..] This driver provides functions to configure and program the FLASH memory
       of all STM32L5xx devices. It includes
      (#) Flash Memory Erase functions:
           (++) Lock and Unlock the FLASH interface using HAL_FLASH_Unlock() and
                HAL_FLASH_Lock() functions
           (++) Erase function: Erase page, mass erase of one or two banks
           (++) There are two modes of erase :
             (+++) Polling Mode using HAL_FLASHEx_Erase()
             (+++) Interrupt Mode using HAL_FLASHEx_Erase_IT()

      (#) Option Bytes Programming function: Use HAL_FLASHEx_OBProgram() to :
        (++) Configure the write protection for each area
        (++) Set the Read protection Level
        (++) Program the user Option Bytes
        (++) Configure the watermark security for each area including secure hide areas
        (++) Configure the boot lock (BOOT_LOCK)
        (++) Configure the Boot addresses

      (#) Get Option Bytes Configuration function: Use HAL_FLASHEx_OBGetConfig() to :
        (++) Get the value of a write protection area
        (++) Know if the read protection is activated
        (++) Get the value of the user Option Bytes
        (++) Get the configuration of a watermark security area including secure hide areas
        (++) Get the boot lock (BOOT_LOCK) configuration
        (++) Get the value of a boot address

      (#) Block-based secure area configuration function: Use HAL_FLASHEx_ConfigBBAttributes()
        (++) Bit-field allowing to secure or un-secure each page

      (#) Get the block-based secure area configuration function: Use HAL_FLASHEx_GetConfigBBAttributes()
        (++) Returns the configuration of the block-based security for all the pages

      (#) Activation of the secure hide area function: Use HAL_FLASHEx_EnableSecHideProtection()
        (++) Deny the access to the secure hide area

      (#) Privilege mode configuration function: Use HAL_FLASHEx_ConfigPrivMode()
        (++) FLASH register can be protected against non-privilege accesses

      (#) Get the privilege mode configuration function: Use HAL_FLASHEx_GetPrivMode()
        (++) Returns if the FLASH registers are protected against non-privilege accesses

      (#) Security inversion configuration function: Use HAL_FLASHEx_ConfigSecInversion()
        (++) FLASH secure state can be override

      (#) Get the security inversion configuration function: Use HAL_FLASHEx_GetSecInversion()
        (++) Returns if FLASH secure state is override

      (#) LVE pin management configuration function: Use HAL_FLASHEx_ConfigLVEPin()
        (++) LVE FLASH pin can be controlled either by power controller or enforced
             to low in order to use external SMPS

      (#) Get LVE pin management configuration function: Use HAL_FLASHEx_GetLVEPin()
        (++) Returns if LVE FLASH pin is controlled by power controller or enforced to low

 @endverbatim
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "stm32l5xx_hal.h"

/** @addtogroup STM32L5xx_HAL_Driver
  * @{
  */

/** @defgroup FLASHEx FLASHEx
  * @brief FLASH Extended HAL module driver
  * @{
  */

#ifdef HAL_FLASH_MODULE_ENABLED

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/** @defgroup FLASHEx_Private_Functions FLASHEx Private Functions
 * @{
 */
static void     FLASH_MassErase(uint32_t Banks);
static void     FLASH_OB_WRPConfig(uint32_t WRPArea, uint32_t WRPStartOffset, uint32_t WRPEndOffset);
static void     FLASH_OB_RDPConfig(uint32_t RDPLevel);
static void     FLASH_OB_UserConfig(uint32_t UserType, uint32_t UserConfig);
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
static void     FLASH_OB_WMSECConfig(uint32_t WMSecConfig, uint32_t WMSecStartPage, uint32_t WMSecEndPage, uint32_t WMHDPEndPage);
static void     FLASH_OB_BootLockConfig(uint32_t BootLockConfig);
#endif
static void     FLASH_OB_BootAddrConfig(uint32_t BootAddrConfig, uint32_t BootAddr);
static void     FLASH_OB_GetWRP(uint32_t WRPArea, uint32_t * WRPStartOffset, uint32_t * WRPEndOffset);
static uint32_t FLASH_OB_GetRDP(void);
static uint32_t FLASH_OB_GetUser(void);
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
static void     FLASH_OB_GetWMSEC(uint32_t * WMSecConfig, uint32_t * WMSecStartPage, uint32_t * WMSecEndPage, uint32_t * WMHDPEndPage);
static uint32_t FLASH_OB_GetBootLock(void);
#endif
static void     FLASH_OB_GetBootAddr(uint32_t BootAddrConfig, uint32_t * BootAddr);
/**
  * @}
  */

/* Exported functions -------------------------------------------------------*/
/** @defgroup FLASHEx_Exported_Functions FLASH Extended Exported Functions
  * @{
  */

/** @defgroup FLASHEx_Exported_Functions_Group1 Extended Programming Operation functions
 *  @brief   Extended Programming Operation functions
 *
@verbatim
 ===============================================================================
                ##### Extended Programming Operation functions #####
 ===============================================================================
    [..]
    This subsection provides a set of functions allowing to manage the Extended FLASH
    programming operations Operations.

@endverbatim
  * @{
  */

/**
  * @brief  Perform a mass erase or erase the specified FLASH memory pages.
  * @param[in]  pEraseInit pointer to an FLASH_EraseInitTypeDef structure that
  *         contains the configuration information for the erasing.
  *
  * @param[out]  PageError   pointer to variable that contains the configuration
  *         information on faulty page in case of error (0xFFFFFFFF means that all
  *         the pages have been correctly erased)
  *
  * @retval HAL Status
  */
HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError)
{
  HAL_StatusTypeDef status;
  uint32_t page_index;
  __IO uint32_t *reg;

  /* Check the parameters */
  assert_param(IS_FLASH_TYPEERASE(pEraseInit->TypeErase));

  /* Process Locked */
  __HAL_LOCK(&pFlash);

  /* Reset error code */
  pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);

  if (status == HAL_OK)
  {
    pFlash.ProcedureOnGoing = pEraseInit->TypeErase;

    reg = IS_FLASH_SECURE_OPERATION() ? &(FLASH->SECCR) : &(FLASH_NS->NSCR);

    if ((pFlash.ProcedureOnGoing & ~(FLASH_NON_SECURE_MASK)) == FLASH_TYPEERASE_MASSERASE)
    {
      /* Mass erase to be done */
      FLASH_MassErase(pEraseInit->Banks);

      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    }
    else
    {
      /*Initialization of PageError variable*/
      *PageError = 0xFFFFFFFFU;

      for(page_index = pEraseInit->Page; page_index < (pEraseInit->Page + pEraseInit->NbPages); page_index++)
      {
        FLASH_PageErase(page_index, pEraseInit->Banks);

        /* Wait for last operation to be completed */
        status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);

        if (status != HAL_OK)
        {
          /* In case of error, stop erase procedure and return the faulty page */
          *PageError = page_index;
          break;
        }
      }
    }

    /* If the erase operation is completed, disable the associated bits */
    CLEAR_BIT((*reg), (pFlash.ProcedureOnGoing & ~(FLASH_NON_SECURE_MASK)));
  }

  /* Process Unlocked */
  __HAL_UNLOCK(&pFlash);

  return status;
}

/**
  * @brief  Perform a mass erase or erase the specified FLASH memory pages with interrupt enabled.
  * @param  pEraseInit pointer to an FLASH_EraseInitTypeDef structure that
  *         contains the configuration information for the erasing.
  *
  * @retval HAL Status
  */
HAL_StatusTypeDef HAL_FLASHEx_Erase_IT(FLASH_EraseInitTypeDef *pEraseInit)
{
  HAL_StatusTypeDef status;
  __IO uint32_t *reg_cr;

  /* Check the parameters */
  assert_param(IS_FLASH_TYPEERASE(pEraseInit->TypeErase));

  /* Process Locked */
  __HAL_LOCK(&pFlash);

  /* Reset error code */
  pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);

  if (status != HAL_OK)
  {
    /* Process Unlocked */
    __HAL_UNLOCK(&pFlash);
  }
  else
  {
    /* Set internal variables used by the IRQ handler */
    pFlash.ProcedureOnGoing = pEraseInit->TypeErase;
    pFlash.Bank = pEraseInit->Banks;
    
    /* Access to SECCR or NSCR depends on operation type */
    reg_cr = IS_FLASH_SECURE_OPERATION() ? &(FLASH->SECCR) : &(FLASH->NSCR);

    /* Enable End of Operation and Error interrupts */
    (*reg_cr) |= (FLASH_IT_EOP | FLASH_IT_OPERR);

    if ((pFlash.ProcedureOnGoing & ~(FLASH_NON_SECURE_MASK)) == FLASH_TYPEERASE_MASSERASE)
    {
      /* Mass erase to be done */
      FLASH_MassErase(pEraseInit->Banks);
    }
    else
    {
      /* Erase by page to be done */
      pFlash.NbPagesToErase = pEraseInit->NbPages;
      pFlash.Page = pEraseInit->Page;

      /*Erase 1st page and wait for IT */
      FLASH_PageErase(pEraseInit->Page, pEraseInit->Banks);
    }
  }

  return status;
}

/**
  * @brief  Program Option bytes.
  * @param  pOBInit pointer to an FLASH_OBInitStruct structure that
  *         contains the configuration information for the programming.
  *
  * @note   To configure any option bytes, the option lock bit OPTLOCK must be
  *         cleared with the call of HAL_FLASH_OB_Unlock() function.
  * @note   New option bytes configuration will be taken into account in two cases:
  *         - after an option bytes launch through the call of HAL_FLASH_OB_Launch()
  *         - after a power reset (BOR reset or exit from Standby/Shutdown modes)
  *
  * @retval HAL Status
  */
HAL_StatusTypeDef HAL_FLASHEx_OBProgram(FLASH_OBProgramInitTypeDef *pOBInit)
{
  HAL_StatusTypeDef status;

  /* Check the parameters */
  assert_param(IS_OPTIONBYTE(pOBInit->OptionType));

  /* Process Locked */
  __HAL_LOCK(&pFlash);

  /* Reset error code */
  pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);

  if(status == HAL_OK)
  {
    /* Write protection configuration */
    if((pOBInit->OptionType & OPTIONBYTE_WRP) != 0U)
    {
      /* Configure of Write protection on the selected area */
      FLASH_OB_WRPConfig(pOBInit->WRPArea, pOBInit->WRPStartOffset, pOBInit->WRPEndOffset);
    }

    /* Read protection configuration */
    if((pOBInit->OptionType & OPTIONBYTE_RDP) != 0U)
    {
      /* Configure the Read protection level */
      FLASH_OB_RDPConfig(pOBInit->RDPLevel);
    }

    /* User Configuration */
    if((pOBInit->OptionType & OPTIONBYTE_USER) != 0U)
    {
      /* Configure the user option bytes */
      FLASH_OB_UserConfig(pOBInit->USERType, pOBInit->USERConfig);
    }

#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
    /* Watermark secure configuration */
    if((pOBInit->OptionType & OPTIONBYTE_WMSEC) != 0U)
    {
      /* Configure the watermark-based secure area */
      FLASH_OB_WMSECConfig(pOBInit->WMSecConfig, pOBInit->WMSecStartPage, pOBInit->WMSecEndPage, pOBInit->WMHDPEndPage);
    }

    /* Unique boot entry point configuration */
    if((pOBInit->OptionType & OPTIONBYTE_BOOT_LOCK) != 0U)
    {
      /* Configure the unique boot entry point */
      FLASH_OB_BootLockConfig(pOBInit->BootLock);
    }
#endif

    /* Boot address configuration */
    if((pOBInit->OptionType & OPTIONBYTE_BOOTADDR) != 0U)
    {
      /* Configure the boot address */
      FLASH_OB_BootAddrConfig(pOBInit->BootAddrConfig, pOBInit->BootAddr);
    }

    /* Set OPTSTRT Bit */
    SET_BIT(FLASH->NSCR, FLASH_NSCR_OPTSTRT);

    /* Wait for last operation to be completed */
    status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
  }

  /* Process Unlocked */
  __HAL_UNLOCK(&pFlash);

  return status;
}

/**
  * @brief  Get the Option bytes configuration.
  * @param  pOBInit pointer to an FLASH_OBInitStruct structure that contains the
  *                  configuration information.
  * @note   The fields pOBInit->WRPArea, pOBInit->WMSecConfig and pOBInit->BootAddrConfig
  *         should indicate which area/address is requested for the WRP, WM Security or
  *         Boot Address, else no information will be returned
  *
  * @retval None
  */
void HAL_FLASHEx_OBGetConfig(FLASH_OBProgramInitTypeDef *pOBInit)
{
  pOBInit->OptionType = (OPTIONBYTE_RDP | OPTIONBYTE_USER);

  if((pOBInit->WRPArea == OB_WRPAREA_BANK1_AREAA) || (pOBInit->WRPArea == OB_WRPAREA_BANK1_AREAB) ||
     (pOBInit->WRPArea == OB_WRPAREA_BANK2_AREAA) || (pOBInit->WRPArea == OB_WRPAREA_BANK2_AREAB))
  {
    pOBInit->OptionType |= OPTIONBYTE_WRP;
    /* Get write protection on the selected area */
    FLASH_OB_GetWRP(pOBInit->WRPArea, &(pOBInit->WRPStartOffset), &(pOBInit->WRPEndOffset));
  }

  /* Get Read protection level */
  pOBInit->RDPLevel = FLASH_OB_GetRDP();

  /* Get the user option bytes */
  pOBInit->USERConfig = FLASH_OB_GetUser();

#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
  /* Get the configuration of the watermark secure area for the selected area */
  if ((pOBInit->WMSecConfig == OB_WMSEC_AREA1) || (pOBInit->WMSecConfig == OB_WMSEC_AREA2))
  {
    pOBInit->OptionType |= OPTIONBYTE_WMSEC;
    FLASH_OB_GetWMSEC(&(pOBInit->WMSecConfig), &(pOBInit->WMSecStartPage), &(pOBInit->WMSecEndPage), &(pOBInit->WMHDPEndPage));
  }

  pOBInit->OptionType |= OPTIONBYTE_BOOT_LOCK;

  /* Get the configuration of the unique boot entry point */
  pOBInit->BootLock = FLASH_OB_GetBootLock();
#endif

  /* Get the value of the selected boot address */
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
  if ((pOBInit->BootAddrConfig == OB_BOOTADDR_NS0) || (pOBInit->BootAddrConfig == OB_BOOTADDR_NS1) ||
      (pOBInit->BootAddrConfig == OB_BOOTADDR_SEC0))
#else
  if ((pOBInit->BootAddrConfig == OB_BOOTADDR_NS0) || (pOBInit->BootAddrConfig == OB_BOOTADDR_NS1))
#endif
  {
    pOBInit->OptionType |= OPTIONBYTE_BOOTADDR;
    FLASH_OB_GetBootAddr(pOBInit->BootAddrConfig, &(pOBInit->BootAddr));
  }
}

#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
/**
  * @brief  Configure the block-based secure area.
  *
  * @param  pBBAttributes pointer to an FLASH_BBAttributesTypeDef structure that
  *         contains the configuration information for the programming.
  *
  * @retval HAL Status
  */
HAL_StatusTypeDef HAL_FLASHEx_ConfigBBAttributes(FLASH_BBAttributesTypeDef *pBBAttributes)
{
  HAL_StatusTypeDef status;
  uint8_t index;
  __IO uint32_t *reg;

  /* Check the parameters */
  assert_param(IS_FLASH_BANK_EXCLUSIVE(pBBAttributes->Bank));

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);

  if (status == HAL_OK)
  {
    if (pBBAttributes->BBAttributesType == FLASH_BB_SEC)
    {
      if (pBBAttributes->Bank == FLASH_BANK_1)
      {
        reg = &(FLASH->SECBB1R1);
      }
      else
      {
        reg = &(FLASH->SECBB2R1);
      }

      /* Modify the register values and check that new attributes are taken in account */
      for (index = 0; index < FLASH_BLOCKBASED_NB_REG; index++)
      {
        *reg = pBBAttributes->BBAttributes_array[index];
        if (*reg != pBBAttributes->BBAttributes_array[index])
        {
          status = HAL_ERROR;
        }
        reg++;
      }
    }

    /* ISB instruction is called to be sure next instructions are performed with correct attributes */
    __ISB();
  }

  /* Process Unlocked */
  __HAL_UNLOCK(&pFlash);

  return status;
}

/**
  * @brief  Return the block-based secure area.
  *
  * @param  pBBAttributes [in/out] pointer to an FLASH_BBAttributesTypeDef structure
  *                  that contains theconfiguration information.
  * @note   The fields pBBAttributes->BBSecConfig should indicate which area is requested
  *          for the block-based security, else no information will be returned
  *
  * @retval None
  */
void HAL_FLASHEx_GetConfigBBAttributes(FLASH_BBAttributesTypeDef *pBBAttributes)
{
  uint8_t index;
  __IO uint32_t *reg;

  /* Check the parameters */
  assert_param(IS_FLASH_BANK_EXCLUSIVE(pBBAttributes->Bank));

  if (pBBAttributes->BBAttributesType == FLASH_BB_SEC)
  {
    if (pBBAttributes->Bank == FLASH_BANK_1)
    {
      reg = &(FLASH->SECBB1R1);
    }
    else
    {
      reg = &(FLASH->SECBB2R1);
    }

    for (index = 0; index < FLASH_BLOCKBASED_NB_REG; index++)
    {
      pBBAttributes->BBAttributes_array[index] = *reg;
      reg++;
    }
  }
}

/**
  * @brief  Activation of the protection of the secure hide area.
  *
  * @param  Banks indicate the bank concerned by the activation
  *          This parameter can be one of the following values:
  *            @arg FLASH_BANK_1: Bank1
  *            @arg FLASH_BANK_2: Bank2
  *            @arg FLASH_BANK_BOTH: Bank1 & Bank2
  *
  * @retval None
  */
void HAL_FLASHEx_EnableSecHideProtection(uint32_t Banks)
{
  /* Check the parameters */
  assert_param(IS_FLASH_BANK(Banks));

  if ((Banks & FLASH_BANK_1) != 0U)
  {
    SET_BIT(FLASH->SECHDPCR, FLASH_SECHDPCR_HDP1_ACCDIS);
  }
  if((Banks & FLASH_BANK_2) != 0U)
  {
    SET_BIT(FLASH->SECHDPCR, FLASH_SECHDPCR_HDP2_ACCDIS);
  }
}
#endif

/**
  * @}
  */

/** @defgroup FLASHEx_Exported_Functions_Group2 Extended Peripheral Control functions
 *  @brief   Extended Peripheral Control functions
 *
@verbatim
 ===============================================================================
                      ##### Extended Peripheral Control functions #####
 ===============================================================================
    [..]
    This subsection provides a set of functions allowing to control the FLASH
    memory operations.

@endverbatim
  * @{
  */

/**
  * @brief  Configuration of the privilege attribute.
  *
  * @param  PrivMode indicate privilege mode configuration
  *          This parameter can be one of the following values:
  *            @arg FLASH_PRIV_GRANTED: access to Flash registers is granted
  *            @arg FLASH_PRIV_DENIED: access to Flash registers is denied to non-privilege access
  *
  * @retval None
  */
void HAL_FLASHEx_ConfigPrivMode(uint32_t PrivMode)
{
  /* Check the parameters */
  assert_param(IS_FLASH_CFGPRIVMODE(PrivMode));

  MODIFY_REG(FLASH->PRIVCFGR, FLASH_PRIVCFGR_PRIV, PrivMode);
}

/**
  * @brief  Return the value of the privilege attribute.
  *
  * @retval  It indicates privilege mode configuration.
  *          This return value can be one of the following values:
  *            @arg FLASH_PRIV_GRANTED: access to Flash registers is granted
  *            @arg FLASH_PRIV_DENIED: access to Flash registers is denied to non-privilege access
  */
uint32_t HAL_FLASHEx_GetPrivMode(void)
{
  return (FLASH->PRIVCFGR & FLASH_PRIVCFGR_PRIV);
}

#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
/**
  * @brief  Configuration of the security inversion.
  *
  * @param  SecInvState indicate the flash security state configuration
  *          This parameter can be one of the following values:
  *            @arg FLASH_SEC_INV_DISABLE: Security state of Flash is not inverted
  *            @arg FLASH_SEC_INV_ENABLE: Security state of Flash is inverted
  *
  * @retval HAL Status
  */
HAL_StatusTypeDef HAL_FLASHEx_ConfigSecInversion(uint32_t SecInvState)
{
  HAL_StatusTypeDef status;

  /* Check the parameters */
  assert_param(IS_FLASH_CFGSECINV(SecInvState));

  /* Process Locked */
  __HAL_LOCK(&pFlash);

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);

  if (status == HAL_OK)
  {
    MODIFY_REG(FLASH->SECCR, FLASH_SECCR_SECINV, SecInvState);
  }

  /* Process Unlocked */
  __HAL_UNLOCK(&pFlash);

  return status;
}

/**
  * @brief  Return the value of the security inversion.
  *
  * @retval  It indicates the flash security state configuration
  *          This return value can be one of the following values:
  *            @arg FLASH_SEC_INV_DISABLE: Security state of Flash is not inverted
  *            @arg FLASH_SEC_INV_ENABLE: Security state of Flash is inverted
  */
uint32_t HAL_FLASHEx_GetSecInversion(void)
{
  return (FLASH->SECCR & FLASH_SECCR_SECINV);
}
#endif

/**
  * @brief  Configuration of the LVE pin management.
  *
  * @param  ConfigLVE indicate the LVE pin management configuration
  *          This parameter can be one of the following values:
  *            @arg FLASH_LVE_PIN_CTRL: LVEA/B FLASH pin controlled by power controller
  *            @arg FLASH_LVE_PIN_FORCED: LVEA/B FLASH pin enforced to low
  *
  * @retval HAL Status
  */
HAL_StatusTypeDef HAL_FLASHEx_ConfigLVEPin(uint32_t ConfigLVE)
{
  HAL_StatusTypeDef status;

  /* Check the parameters */
  assert_param(IS_FLASH_CFGLVEPIN(ConfigLVE));

  /* Process Locked */
  __HAL_LOCK(&pFlash);

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);

  if (status == HAL_OK)
  {
    /* Check that the voltage scaling is range 2 */
    if (HAL_PWREx_GetVoltageRange() == PWR_REGULATOR_VOLTAGE_SCALE2)
    {
      /* Unlock the bit LVEN */
      FLASH->LVEKEYR = FLASH_LVEKEY1;
      FLASH->LVEKEYR = FLASH_LVEKEY2;

      /* Modify the value of LVEN bit */
      MODIFY_REG(FLASH->ACR, FLASH_ACR_LVEN, ConfigLVE);

      /* Check that the bit has been correctly configured */
      if (READ_BIT(FLASH->ACR, FLASH_ACR_LVEN) != ConfigLVE)
      {
        status = HAL_ERROR;
      }
    }
    else
    {
      /* Not allow to force Flash LVE pin if not in voltage range 2 */
      status = HAL_ERROR;
    }
  }

  /* Process Unlocked */
  __HAL_UNLOCK(&pFlash);

  return status;
}

/**
  * @brief  Return the value of the LVE pin management.
  *
  * @retval  It indicates the LVE pin management configuration
  *          This return value can be one of the following values:
  *            @arg FLASH_LVE_PIN_CTRL: LVEA/B FLASH pin controlled by power controller
  *            @arg FLASH_LVE_PIN_FORCED: LVEA/B FLASH pin enforces to low
  */
uint32_t HAL_FLASHEx_GetLVEPin(void)
{
  return (FLASH->ACR & FLASH_ACR_LVEN);
}

/**
  * @}
  */

/**
  * @}
  */

/* Private functions ---------------------------------------------------------*/

/** @addtogroup FLASHEx_Private_Functions
  * @{
  */
/**
  * @brief  Mass erase of FLASH memory.
  * @param  Banks Banks to be erased
  *          This parameter can be one of the following values:
  *            @arg FLASH_BANK_1: Bank1 to be erased
  *            @arg FLASH_BANK_2: Bank2 to be erased
  *            @arg FLASH_BANK_BOTH: Bank1 and Bank2 to be erased
  * @retval None
  */
static void FLASH_MassErase(uint32_t Banks)
{
  __IO uint32_t *reg;
  
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
  uint32_t primask_bit;

  /* Disable interrupts to avoid any interruption */
  primask_bit = __get_PRIMASK();
  __disable_irq();
#endif
  
  /* Access to SECCR or NSCR registers depends on operation type */
  reg = IS_FLASH_SECURE_OPERATION() ? &(FLASH->SECCR) : &(FLASH_NS->NSCR);

  if (READ_BIT(FLASH->OPTR, FLASH_OPTR_DBANK) != 0U)
  {
    /* Check the parameters */
    assert_param(IS_FLASH_BANK(Banks));

    /* Set the Mass Erase Bit for the bank 1 if requested */
    if((Banks & FLASH_BANK_1) != 0U)
    {
      SET_BIT((*reg), FLASH_NSCR_NSMER1);
    }

    /* Set the Mass Erase Bit for the bank 2 if requested */
    if((Banks & FLASH_BANK_2) != 0U)
    {
      SET_BIT((*reg), FLASH_NSCR_NSMER2);
    }
  }
  else
  {
    SET_BIT((*reg), (FLASH_NSCR_NSMER1 | FLASH_NSCR_NSMER2));
  }

  /* Proceed to erase all sectors */
  SET_BIT((*reg), FLASH_NSCR_NSSTRT);

#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
  /* Re-enable the interrupts */
  __set_PRIMASK(primask_bit);
#endif
}

/**
  * @brief  Erase the specified FLASH memory page.
  * @param  Page FLASH page to erase
  *         This parameter must be a value between 0 and (max number of pages in the bank - 1)
  * @param  Banks Bank(s) where the page will be erased
  *          This parameter can be one of the following values:
  *            @arg FLASH_BANK_1: Page in bank 1 to be erased
  *            @arg FLASH_BANK_2: Page in bank 2 to be erased
  * @retval None
  */
void FLASH_PageErase(uint32_t Page, uint32_t Banks)
{
  __IO uint32_t *reg;
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
  uint32_t primask_bit;
#endif
  /* Check the parameters */
  assert_param(IS_FLASH_PAGE(Page));

#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
  /* Disable interrupts to avoid any interruption */
  primask_bit = __get_PRIMASK();
  __disable_irq();
#endif

  /* Access to SECCR or NSCR registers depends on operation type */
  reg = IS_FLASH_SECURE_OPERATION() ? &(FLASH->SECCR) : &(FLASH_NS->NSCR);

  if(READ_BIT(FLASH->OPTR, FLASH_OPTR_DBANK) == 0U)
  {
    CLEAR_BIT((*reg), FLASH_NSCR_NSBKER);
  }
  else
  {
    assert_param(IS_FLASH_BANK_EXCLUSIVE(Banks));

    if((Banks & FLASH_BANK_1) != 0U)
    {
      CLEAR_BIT((*reg), FLASH_NSCR_NSBKER);
    }
    else
    {
      SET_BIT((*reg), FLASH_NSCR_NSBKER);
    }
  }

  /* Proceed to erase the page */
  MODIFY_REG((*reg), (FLASH_NSCR_NSPNB | FLASH_NSCR_NSPER), ((Page << FLASH_NSCR_NSPNB_Pos) | FLASH_NSCR_NSPER));
  SET_BIT((*reg), FLASH_NSCR_NSSTRT);

#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
  /* Re-enable the interrupts */
  __set_PRIMASK(primask_bit);
#endif
}

/**
  * @brief  Configure the write protection of the desired pages.
  *
  * @note   When the memory read protection level is selected (RDP level = 1),
  *         it is not possible to program or erase Flash memory if the CPU debug
  *         features are connected (JTAG or single wire) or boot code is being
  *         executed from RAM or System flash, even if WRP is not activated.
  * @note   To configure the WRP options, the option lock bit OPTLOCK must be
  *         cleared with the call of the HAL_FLASH_OB_Unlock() function.
  * @note   To validate the WRP options, the option bytes must be reloaded
  *         through the call of the HAL_FLASH_OB_Launch() function.
  *
  * @param  WRPArea specifies the area to be configured.
  *          This parameter can be one of the following values:
  *            @arg OB_WRPAREA_BANK1_AREAA: Flash Bank 1 Area A
  *            @arg OB_WRPAREA_BANK1_AREAB: Flash Bank 1 Area B
  *            @arg OB_WRPAREA_BANK2_AREAA: Flash Bank 2 Area A
  *            @arg OB_WRPAREA_BANK2_AREAB: Flash Bank 2 Area B
  *
  * @param  WRPStartOffset specifies the start page of the write protected area
  *          This parameter can be page number between 0 and (max number of pages in the bank - 1)
  *
  * @param  WRPEndOffset specifies the end page of the write protected area
  *          This parameter can be page number between WRPStartOffset and (max number of pages in the bank - 1)
  *
  * @retval None
  */
static void FLASH_OB_WRPConfig(uint32_t WRPArea, uint32_t WRPStartOffset, uint32_t WRPEndOffset)
{
  /* Check the parameters */
  assert_param(IS_OB_WRPAREA(WRPArea));
  assert_param(IS_FLASH_PAGE(WRPStartOffset));
  assert_param(IS_FLASH_PAGE(WRPEndOffset));

  /* Configure the write protected area */
  if(WRPArea == OB_WRPAREA_BANK1_AREAA)
  {
    MODIFY_REG(FLASH->WRP1AR, (FLASH_WRP1AR_WRP1A_PSTRT | FLASH_WRP1AR_WRP1A_PEND),
               (WRPStartOffset | (WRPEndOffset << FLASH_WRP1AR_WRP1A_PEND_Pos)));
  }
  else if(WRPArea == OB_WRPAREA_BANK1_AREAB)
  {
    MODIFY_REG(FLASH->WRP1BR, (FLASH_WRP1BR_WRP1B_PSTRT | FLASH_WRP1BR_WRP1B_PEND),
               (WRPStartOffset | (WRPEndOffset << FLASH_WRP1BR_WRP1B_PEND_Pos)));
  }
  else if(WRPArea == OB_WRPAREA_BANK2_AREAA)
  {
    MODIFY_REG(FLASH->WRP2AR, (FLASH_WRP2AR_WRP2A_PSTRT | FLASH_WRP2AR_WRP2A_PEND),
               (WRPStartOffset | (WRPEndOffset << FLASH_WRP2AR_WRP2A_PEND_Pos)));
  }
  else if(WRPArea == OB_WRPAREA_BANK2_AREAB)
  {
    MODIFY_REG(FLASH->WRP2BR, (FLASH_WRP2BR_WRP2B_PSTRT | FLASH_WRP2BR_WRP2B_PEND),
               (WRPStartOffset | (WRPEndOffset << FLASH_WRP2BR_WRP2B_PEND_Pos)));
  }
  else
  {
    /* Empty statement (to be compliant MISRA 15.7) */
  }
}

/**
  * @brief  Set the read protection level.
  *
  * @note   To configure the RDP level, the option lock bit OPTLOCK must be
  *         cleared with the call of the HAL_FLASH_OB_Unlock() function.
  * @note   To validate the RDP level, the option bytes must be reloaded
  *         through the call of the HAL_FLASH_OB_Launch() function.
  * @note   !!! Warning : When enabling OB_RDP level 2 it's no more possible
  *         to go back to other levels !!!
  *
  * @param  RDPLevel specifies the read protection level.
  *         This parameter can be one of the following values:
  *            @arg OB_RDP_LEVEL_0: No protection
  *            @arg OB_RDP_LEVEL_0_5: No debug access to secure area
  *            @arg OB_RDP_LEVEL_1: Read protection of the memory
  *            @arg OB_RDP_LEVEL_2: Full chip protection
  *
  * @retval None
  */
static void FLASH_OB_RDPConfig(uint32_t RDPLevel)
{
  /* Check the parameters */
  assert_param(IS_OB_RDP_LEVEL(RDPLevel));

  /* Configure the RDP level in the option bytes register */
  MODIFY_REG(FLASH->OPTR, FLASH_OPTR_RDP, RDPLevel);
}

/**
  * @brief  Program the FLASH User Option Byte.
  *
  * @note   To configure the user option bytes, the option lock bit OPTLOCK must
  *         be cleared with the call of the HAL_FLASH_OB_Unlock() function.
  * @note   To validate the user option bytes, the option bytes must be reloaded
  *         through the call of the HAL_FLASH_OB_Launch() function.
  *
  * @param  UserType The FLASH User Option Bytes to be modified
  *         This parameter can be a combination of @ref FLASH_OB_USER_Type
  * @param  UserConfig The FLASH User Option Bytes values.
  *         It can be a combination of @ref FLASH_OB_USER_BOR_LEVEL,
  *         @ref FLASH_OB_USER_nRST_STOP, @ref FLASH_OB_USER_nRST_STANDBY,
  *         @ref FLASH_OB_USER_nRST_SHUTDOWN, @ref FLASH_OB_USER_IWDG_SW,
  *         @ref FLASH_OB_USER_IWDG_STOP, @ref FLASH_OB_USER_IWDG_STANDBY,
  *         @ref FLASH_OB_USER_WWDG_SW, @ref OB_USER_SWAP_BANK,
  *         @ref FLASH_OB_USER_DUALBANK, @ref FLASH_OB_USER_DBANK,
  *         @ref FLASH_OB_USER_SRAM2_PAR, @ref FLASH_OB_USER_SRAM2_RST,
  *         @ref OB_USER_nSWBOOT0, @ref OB_USER_nBOOT0, @ref FLASH_OB_USER_PA15_PUPEN
  *         and @ref OB_USER_TZEN
  *
  * @retval None
  */
static void FLASH_OB_UserConfig(uint32_t UserType, uint32_t UserConfig)
{
  uint32_t optr_reg_val = 0;
  uint32_t optr_reg_mask = 0;

  /* Check the parameters */
  assert_param(IS_OB_USER_TYPE(UserType));

  if((UserType & OB_USER_BOR_LEV) != 0U)
  {
    /* BOR level option byte should be modified */
    assert_param(IS_OB_USER_BOR_LEVEL(UserConfig & FLASH_OPTR_BOR_LEV));

    /* Set value and mask for BOR level option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_BOR_LEV);
    optr_reg_mask |= FLASH_OPTR_BOR_LEV;
  }

  if((UserType & OB_USER_nRST_STOP) != 0U)
  {
    /* nRST_STOP option byte should be modified */
    assert_param(IS_OB_USER_STOP(UserConfig & FLASH_OPTR_nRST_STOP));

    /* Set value and mask for nRST_STOP option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_nRST_STOP);
    optr_reg_mask |= FLASH_OPTR_nRST_STOP;
  }

  if((UserType & OB_USER_nRST_STDBY) != 0U)
  {
    /* nRST_STDBY option byte should be modified */
    assert_param(IS_OB_USER_STANDBY(UserConfig & FLASH_OPTR_nRST_STDBY));

    /* Set value and mask for nRST_STDBY option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_nRST_STDBY);
    optr_reg_mask |= FLASH_OPTR_nRST_STDBY;
  }

  if((UserType & OB_USER_nRST_SHDW) != 0U)
  {
    /* nRST_SHDW option byte should be modified */
    assert_param(IS_OB_USER_SHUTDOWN(UserConfig & FLASH_OPTR_nRST_SHDW));

    /* Set value and mask for nRST_SHDW option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_nRST_SHDW);
    optr_reg_mask |= FLASH_OPTR_nRST_SHDW;
  }

  if((UserType & OB_USER_IWDG_SW) != 0U)
  {
    /* IWDG_SW option byte should be modified */
    assert_param(IS_OB_USER_IWDG(UserConfig & FLASH_OPTR_IWDG_SW));

    /* Set value and mask for IWDG_SW option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_IWDG_SW);
    optr_reg_mask |= FLASH_OPTR_IWDG_SW;
  }

  if((UserType & OB_USER_IWDG_STOP) != 0U)
  {
    /* IWDG_STOP option byte should be modified */
    assert_param(IS_OB_USER_IWDG_STOP(UserConfig & FLASH_OPTR_IWDG_STOP));

    /* Set value and mask for IWDG_STOP option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_IWDG_STOP);
    optr_reg_mask |= FLASH_OPTR_IWDG_STOP;
  }

  if((UserType & OB_USER_IWDG_STDBY) != 0U)
  {
    /* IWDG_STDBY option byte should be modified */
    assert_param(IS_OB_USER_IWDG_STDBY(UserConfig & FLASH_OPTR_IWDG_STDBY));

    /* Set value and mask for IWDG_STDBY option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_IWDG_STDBY);
    optr_reg_mask |= FLASH_OPTR_IWDG_STDBY;
  }

  if((UserType & OB_USER_WWDG_SW) != 0U)
  {
    /* WWDG_SW option byte should be modified */
    assert_param(IS_OB_USER_WWDG(UserConfig & FLASH_OPTR_WWDG_SW));

    /* Set value and mask for WWDG_SW option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_WWDG_SW);
    optr_reg_mask |= FLASH_OPTR_WWDG_SW;
  }

  if((UserType & OB_USER_SWAP_BANK) != 0U)
  {
    /* SWAP_BANK option byte should be modified */
    assert_param(IS_OB_USER_SWAP_BANK(UserConfig & FLASH_OPTR_SWAP_BANK));

    /* Set value and mask for SWAP_BANK option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_SWAP_BANK);
    optr_reg_mask |= FLASH_OPTR_SWAP_BANK;
  }

  if((UserType & OB_USER_DUALBANK) != 0U)
  {
    /* DUALBANK option byte should be modified */
    assert_param(IS_OB_USER_DUALBANK(UserConfig & FLASH_OPTR_DB256K));

    /* Set value and mask for DB256K option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_DB256K);
    optr_reg_mask |= FLASH_OPTR_DB256K;
  }

  if((UserType & OB_USER_SRAM2_PE) != 0U)
  {
    /* SRAM2_PAR option byte should be modified */
    assert_param(IS_OB_USER_SRAM2_PARITY(UserConfig & FLASH_OPTR_SRAM2_PE));

    /* Set value and mask for SRAM2_PAR option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_SRAM2_PE);
    optr_reg_mask |= FLASH_OPTR_SRAM2_PE;
  }

  if((UserType & OB_USER_SRAM2_RST) != 0U)
  {
    /* SRAM2_RST option byte should be modified */
    assert_param(IS_OB_USER_SRAM2_RST(UserConfig & FLASH_OPTR_SRAM2_RST));

    /* Set value and mask for SRAM2_RST option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_SRAM2_RST);
    optr_reg_mask |= FLASH_OPTR_SRAM2_RST;
  }

  if((UserType & OB_USER_nSWBOOT0) != 0U)
  {
    /* nSWBOOT0 option byte should be modified */
    assert_param(IS_OB_USER_SWBOOT0(UserConfig & FLASH_OPTR_nSWBOOT0));

    /* Set value and mask for nSWBOOT0 option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_nSWBOOT0);
    optr_reg_mask |= FLASH_OPTR_nSWBOOT0;
  }

  if((UserType & OB_USER_nBOOT0) != 0U)
  {
    /* nBOOT0 option byte should be modified */
    assert_param(IS_OB_USER_BOOT0(UserConfig & FLASH_OPTR_nBOOT0));

    /* Set value and mask for nBOOT0 option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_nBOOT0);
    optr_reg_mask |= FLASH_OPTR_nBOOT0;
  }

  if((UserType & OB_USER_PA15_PUPEN) != 0U)
  {
    /* nBOOT0 option byte should be modified */
    assert_param(IS_OB_USER_PA15_PUPEN(UserConfig & FLASH_OPTR_PA15_PUPEN));

    /* Set value and mask for nBOOT0 option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_PA15_PUPEN);
    optr_reg_mask |= FLASH_OPTR_PA15_PUPEN;
  }

  if((UserType & OB_USER_TZEN) != 0U)
  {
    /* TZEN option byte should be modified */
    assert_param(IS_OB_USER_TZEN(UserConfig & FLASH_OPTR_TZEN));

    /* Set value and mask for TZEN option byte */
    optr_reg_val |= (UserConfig & FLASH_OPTR_TZEN);
    optr_reg_mask |= FLASH_OPTR_TZEN;
  }

  /* Configure the option bytes register */
  MODIFY_REG(FLASH->OPTR, optr_reg_mask, optr_reg_val);
}

#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
/**
  * @brief  Configure the watermarked-based secure area.
  *
  * @param  WMSecConfig specifies the area to be configured.
  *          This parameter can be a combination of the following values:
  *            @arg OB_WMSEC_AREA1 or @arg OB_WMSEC_AREA2: Select Flash Secure Area 1 or Area 2
  *            @arg OB_WMSEC_SECURE_AREA_CONFIG: configure Flash Secure Area
  *            @arg OB_WMSEC_HDP_AREA_CONFIG: configure Flash secure hide Area
  *            @arg OB_WMSEC_HDP_AREA_ENABLE: enable secure hide Area in Secure Area
  *            @arg OB_WMSEC_HDP_AREA_DISABLE: disable secure hide Area in Secure Area
  *
  * @param  WMSecStartPage specifies the start page of the secure area
  *          This parameter can be page number between 0 and (max number of pages in the bank - 1)
  *
  * @param  WMSecEndPage specifies the end page of the secure area
  *          This parameter can be page number between WMSecStartPage and (max number of pages in the bank - 1)
  *
  * @param  WMHDPEndPage specifies the end page of the secure hide area
  *          This parameter can be page number between WMSecStartPage and WMPCROPStartPage
  *
  * @retval None
  */
static void FLASH_OB_WMSECConfig(uint32_t WMSecConfig, uint32_t WMSecStartPage, uint32_t WMSecEndPage, uint32_t WMHDPEndPage)
{
  uint32_t tmp_secwm1 = 0U, tmp_secwm2 = 0U;

  /* Check the parameters */
  assert_param(IS_OB_WMSEC_CONFIG(WMSecConfig));
  assert_param(IS_OB_WMSEC_AREA_EXCLUSIVE(WMSecConfig & 0x3U));
  assert_param(IS_FLASH_PAGE(WMSecStartPage));
  assert_param(IS_FLASH_PAGE(WMSecEndPage));
  assert_param(IS_FLASH_PAGE(WMHDPEndPage));

  /* Read SECWM registers */
  if ((WMSecConfig & OB_WMSEC_AREA1) != 0U)
  {
    tmp_secwm1 = FLASH->SECWM1R1;
    tmp_secwm2 = FLASH->SECWM1R2;
  }
  else if ((WMSecConfig & OB_WMSEC_AREA2) != 0U)
  {
    tmp_secwm1 = FLASH->SECWM2R1;
    tmp_secwm2 = FLASH->SECWM2R2;
  }
  else
  {
    /* Nothing to do */
  }

  /* Configure Secure Area */
  if ((WMSecConfig & OB_WMSEC_SECURE_AREA_CONFIG) != 0U)
  {
    MODIFY_REG(tmp_secwm1, (FLASH_SECWM1R1_SECWM1_PSTRT | FLASH_SECWM1R1_SECWM1_PEND),
               ((WMSecEndPage << FLASH_SECWM1R1_SECWM1_PEND_Pos) | WMSecStartPage));
  }

  /* Configure Secure Hide Area */
  if ((WMSecConfig & OB_WMSEC_HDP_AREA_CONFIG) != 0U)
  {
    tmp_secwm2 &= (~(FLASH_SECWM1R2_HDP1_PEND));
    tmp_secwm2 |= (WMHDPEndPage << FLASH_SECWM1R2_HDP1_PEND_Pos);
  }

  /* Enable Secure Hide Area */
  if ((WMSecConfig & OB_WMSEC_HDP_AREA_ENABLE) != 0U)
  {
    tmp_secwm2 |= FLASH_SECWM1R2_HDP1EN;
  }

  /* Disable Secure Hide Area */
  if ((WMSecConfig & OB_WMSEC_HDP_AREA_DISABLE) != 0U)
  {
    tmp_secwm2 &= (~FLASH_SECWM1R2_HDP1EN);
  }

  /* Write SECWM registers */
  if ((WMSecConfig & OB_WMSEC_AREA1) != 0U)
  {
    FLASH->SECWM1R1 = tmp_secwm1;
    FLASH->SECWM1R2 = tmp_secwm2;
  }
  else if ((WMSecConfig & OB_WMSEC_AREA2) != 0U)
  {
    FLASH->SECWM2R1 = tmp_secwm1;
    FLASH->SECWM2R2 = tmp_secwm2;
  }
  else
  {
    /* Nothing to do */
  }
}

/**
  * @brief  Configure the boot lock.
  *
  * @param  BootLockConfig specifies the activation of the BOOT_LOCK.
  *          This parameter can be one of the following values:
  *            @arg OB_BOOT_LOCK_DISABLE: Boot Lock mode deactivated
  *            @arg OB_BOOT_LOCK_ENABLE: Boot Lock mode activated
  *
  * @retval None
  */
static void FLASH_OB_BootLockConfig(uint32_t BootLockConfig)
{
  /* Check the parameters */
  assert_param(IS_OB_BOOT_LOCK(BootLockConfig));

  /* Configure the option bytes register */
  MODIFY_REG(FLASH->SECBOOTADD0R, FLASH_SECBOOTADD0R_BOOT_LOCK, BootLockConfig);
}
#endif

/**
  * @brief  Configure the boot address.
  *
  * @param  BootAddrConfig specifies the area to be configured.
  *          This parameter can be one of the following values:
  *            @arg OB_BOOTADDR_NS0: Non-secure boot address 0
  *            @arg OB_BOOTADDR_NS1: Non-secure boot address 1
  *            @arg OB_BOOTADDR_SEC0: Secure boot address 0
  *
  * @param  BootAddr: specifies the address used for the boot
  *          This parameter can be page number between 0 and 0xFFFFFF00
  *
  * @retval None
  */
static void FLASH_OB_BootAddrConfig(uint32_t BootAddrConfig, uint32_t BootAddr)
{
  /* Check the parameters */
  assert_param(IS_OB_BOOTADDR_CONFIG(BootAddrConfig));

  if (BootAddrConfig == OB_BOOTADDR_NS0)
  {
    MODIFY_REG(FLASH->NSBOOTADD0R, FLASH_NSBOOTADD0R_NSBOOTADD0, BootAddr);
  }
  else if (BootAddrConfig == OB_BOOTADDR_NS1)
  {
    MODIFY_REG(FLASH->NSBOOTADD1R, FLASH_NSBOOTADD1R_NSBOOTADD1, BootAddr);
  }
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
  else if (BootAddrConfig == OB_BOOTADDR_SEC0)
  {
    MODIFY_REG(FLASH->SECBOOTADD0R, FLASH_SECBOOTADD0R_SECBOOTADD0, BootAddr);
  }
#endif
  else
  {
    /* Empty statement (to be compliant MISRA 15.7) */
  }
}

/**
  * @brief  Return the FLASH Write Protection Option Bytes value.
  *
  * @param[in]  WRPArea specifies the area to be returned.
  *          This parameter can be one of the following values:
  *            @arg OB_WRPAREA_BANK1_AREAA: Flash Bank 1 Area A
  *            @arg OB_WRPAREA_BANK1_AREAB: Flash Bank 1 Area B
  *            @arg OB_WRPAREA_BANK2_AREAA: Flash Bank 2 Area A
  *            @arg OB_WRPAREA_BANK2_AREAB: Flash Bank 2 Area B
  *
  * @param[out]  WRPStartOffset specifies the address where to copied the start page
  *                         of the write protected area
  *
  * @param[out]  WRPEndOffset specifies the address where to copied the end page of
  *                        the write protected area
  *
  * @retval None
  */
static void FLASH_OB_GetWRP(uint32_t WRPArea, uint32_t * WRPStartOffset, uint32_t * WRPEndOffset)
{
  /* Get the configuration of the write protected area */
  if(WRPArea == OB_WRPAREA_BANK1_AREAA)
  {
    *WRPStartOffset = READ_BIT(FLASH->WRP1AR, FLASH_WRP1AR_WRP1A_PSTRT);
    *WRPEndOffset = (READ_BIT(FLASH->WRP1AR, FLASH_WRP1AR_WRP1A_PEND) >> FLASH_WRP1AR_WRP1A_PEND_Pos);
  }
  else if(WRPArea == OB_WRPAREA_BANK1_AREAB)
  {
    *WRPStartOffset = READ_BIT(FLASH->WRP1BR, FLASH_WRP1BR_WRP1B_PSTRT);
    *WRPEndOffset = (READ_BIT(FLASH->WRP1BR, FLASH_WRP1BR_WRP1B_PEND) >> FLASH_WRP1BR_WRP1B_PEND_Pos);
  }
  else if(WRPArea == OB_WRPAREA_BANK2_AREAA)
  {
    *WRPStartOffset = READ_BIT(FLASH->WRP2AR, FLASH_WRP2AR_WRP2A_PSTRT);
    *WRPEndOffset = (READ_BIT(FLASH->WRP2AR, FLASH_WRP2AR_WRP2A_PEND) >> FLASH_WRP2AR_WRP2A_PEND_Pos);
  }
  else if(WRPArea == OB_WRPAREA_BANK2_AREAB)
  {
    *WRPStartOffset = READ_BIT(FLASH->WRP2BR, FLASH_WRP2BR_WRP2B_PSTRT);
    *WRPEndOffset = (READ_BIT(FLASH->WRP2BR, FLASH_WRP2BR_WRP2B_PEND) >> FLASH_WRP2BR_WRP2B_PEND_Pos);
  }
  else
  {
    /* Empty statement (to be compliant MISRA 15.7) */
  }
}

/**
  * @brief  Return the FLASH Read Protection level.
  * @retval FLASH ReadOut Protection Status:
  *         This return value can be one of the following values:
  *            @arg OB_RDP_LEVEL_0: No protection
  *            @arg OB_RDP_LEVEL_0_5: No debug access to secure area
  *            @arg OB_RDP_LEVEL_1: Read protection of the memory
  *            @arg OB_RDP_LEVEL_2: Full chip protection
  */
static uint32_t FLASH_OB_GetRDP(void)
{
  uint32_t rdp_level = READ_BIT(FLASH->OPTR, FLASH_OPTR_RDP);

  if ((rdp_level != OB_RDP_LEVEL_0) && (rdp_level != OB_RDP_LEVEL_0_5) && (rdp_level != OB_RDP_LEVEL_2))
  {
    return (OB_RDP_LEVEL_1);
  }
  else
  {
    return (rdp_level);
  }
}

/**
  * @brief  Return the FLASH User Option Byte value.
  * @retval The FLASH User Option Bytes values,
  *         it can be a combination of @ref FLASH_OB_USER_BOR_LEVEL,
  *         @ref FLASH_OB_USER_nRST_STOP, @ref FLASH_OB_USER_nRST_STANDBY,
  *         @ref FLASH_OB_USER_nRST_SHUTDOWN, @ref FLASH_OB_USER_IWDG_SW,
  *         @ref FLASH_OB_USER_IWDG_STOP, @ref FLASH_OB_USER_IWDG_STANDBY,
  *         @ref FLASH_OB_USER_WWDG_SW, @ref OB_USER_SWAP_BANK,
  *         @ref FLASH_OB_USER_DUALBANK, @ref FLASH_OB_USER_DBANK,
  *         @ref FLASH_OB_USER_SRAM2_PAR, @ref FLASH_OB_USER_SRAM2_RST,
  *         @ref OB_USER_nSWBOOT0, @ref OB_USER_nBOOT0, @ref FLASH_OB_USER_PA15_PUPEN
  *         and @ref OB_USER_TZEN
  */
static uint32_t FLASH_OB_GetUser(void)
{
  uint32_t user_config = READ_REG(FLASH->OPTR);
  CLEAR_BIT(user_config, FLASH_OPTR_RDP);

  return user_config;
}

#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
/**
  * @brief  Return the watermarked-based secure area.
  *
  * @param  WMSecConfig [in/out] specifies the area to be returned.
  *                  This parameter can be one of the following values:
  *                   @arg OB_WMSEC_AREA1: Flash Secure Area 1
  *                   @arg OB_WMSEC_AREA2: Flash Secure Area 2
  *                  When return from the function, this parameter will have a combination of the following values:
  *                   @arg OB_WMSEC_AREA1 or @arg OB_WMSEC_AREA2: selected Flash Secure Area 1 or Area 2
  *                   @arg OB_WMSEC_HDP_AREA_ENABLE: Secure Hide Area in Secure Area enabled
  *                   @arg OB_WMSEC_HDP_AREA_DISABLE: Secure Hide Area in Secure Area disabled
  *
  * @param  WMSecStartPage [out] specifies the address where to copied the start page of the secure area
  *
  * @param  WMSecEndPage [out] specifies the address where to copied the end page of the secure area
  *
  * @param  WMHDPEndPage [out] specifies the address where to copied the end page of the secure hide area
  *
  * @retval None
  */
static void FLASH_OB_GetWMSEC(uint32_t * WMSecConfig, uint32_t * WMSecStartPage, uint32_t * WMSecEndPage, uint32_t * WMHDPEndPage)
{
  uint32_t tmp_secwm1 = 0U, tmp_secwm2 = 0U;

  /* Check the parameters */
  assert_param(IS_OB_WMSEC_CONFIG(*WMSecConfig));
  assert_param(IS_FLASH_BANK_EXCLUSIVE((*WMSecConfig) & 0x3U));

  /* Read SECWM registers */
  if (((*WMSecConfig) & OB_WMSEC_AREA1) != 0U)
  {
    tmp_secwm1 = FLASH->SECWM1R1;
    tmp_secwm2 = FLASH->SECWM1R2;
  }
  else if (((*WMSecConfig) & OB_WMSEC_AREA2) != 0U)
  {
    tmp_secwm1 = FLASH->SECWM2R1;
    tmp_secwm2 = FLASH->SECWM2R2;
  }
  else
  {
    /* Nothing to do */
  }

  /* Configuration of secure area */
  *WMSecStartPage = (tmp_secwm1 & FLASH_SECWM1R1_SECWM1_PSTRT);
  *WMSecEndPage = ((tmp_secwm1 & FLASH_SECWM1R1_SECWM1_PEND) >> FLASH_SECWM1R1_SECWM1_PEND_Pos);

  /* Configuration of secure hide area */
  *WMHDPEndPage = ((tmp_secwm2 & FLASH_SECWM1R2_HDP1_PEND) >> FLASH_SECWM1R2_HDP1_PEND_Pos);

  if ((tmp_secwm2 & FLASH_SECWM1R2_HDP1EN) == 0U)
  {
    *WMSecConfig = ((*WMSecConfig) | OB_WMSEC_HDP_AREA_DISABLE);
  }
  else
  {
    *WMSecConfig = ((*WMSecConfig) | OB_WMSEC_HDP_AREA_ENABLE);
  }
}

/**
  * @brief  Return the boot lock configuration.
  *
  * @retval  Value of Boot Lock configuration.
  *          It can be one of the following values:
  *            @arg OB_BOOT_LOCK_DISABLE: Boot Lock mode deactivated
  *            @arg OB_BOOT_LOCK_ENABLE: Boot Lock mode activated
  */
static uint32_t FLASH_OB_GetBootLock(void)
{
  return (FLASH->SECBOOTADD0R & FLASH_SECBOOTADD0R_BOOT_LOCK);
}
#endif

/**
  * @brief  Return the boot address.
  *
  * @param[in]  BootAddrConfig specifies the area to be returned.
  *              This parameter can be one of the following values:
  *                @arg OB_BOOTADDR_NS0: Non-secure boot address 0
  *                @arg OB_BOOTADDR_NS1: Non-secure boot address 1
  *                @arg OB_BOOTADDR_SEC0: Secure boot address 0
  *
  * @param[out]  BootAddr specifies the boot address value
  *
  * @retval None
  */
static void FLASH_OB_GetBootAddr(uint32_t BootAddrConfig, uint32_t * BootAddr)
{
  if (BootAddrConfig == OB_BOOTADDR_NS0)
  {
    *BootAddr = (FLASH->NSBOOTADD0R & FLASH_NSBOOTADD0R_NSBOOTADD0);
  }
  else if (BootAddrConfig == OB_BOOTADDR_NS1)
  {
    *BootAddr = (FLASH->NSBOOTADD1R & FLASH_NSBOOTADD1R_NSBOOTADD1);
  }
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
  else if (BootAddrConfig == OB_BOOTADDR_SEC0)
  {
    *BootAddr = (FLASH->SECBOOTADD0R & FLASH_SECBOOTADD0R_SECBOOTADD0);
  }
#endif
  else
  {
    /* Empty statement (to be compliant MISRA 15.7) */
  }
}

/**
  * @}
  */

/**
  * @}
  */

#endif /* HAL_FLASH_MODULE_ENABLED */

/**
  * @}
  */

/**
  * @}
  */

