/*
 * @file        apm32a0xx_fmc.c
 *
 * @brief       This file provides all the FMC firmware functions
 *
 * @version     V1.0.0
 *
 * @date        2023-03-30
 *
 * @attention
 *
 *  Copyright (C) 2023 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

#include "apm32a0xx_fmc.h"

/** @addtogroup APM32A0xx_StdPeriphDriver
  @{
*/

/** @addtogroup FMC_Driver
  @{
*/

/** @defgroup FMC_Macros Macros
  @{
  */

/**@} end of group FMC_Macros */

/** @defgroup FMC_Enumerates Enumerates
  @{
  */

/**@} end of group FMC_Enumerates */

/** @defgroup FMC_Structures Structures
  @{
  */

/**@} end of group FMC_Structures */

/** @defgroup FMC_Variables Variables
  @{
  */

/**@} end of group FMC_Variables */

/** @defgroup FMC_Functions Functions
  @{
  */

/*!
 * @brief     Sets the code latency value.
 *
 * @param     latency: the flash latency value.
 *                     The parameter can be one of following values:
 *                       @arg FMC_LATENCY_0
 *                       @arg FMC_LATENCY_1
 * @retval    None
 */
void FMC_SetLatency(FMC_LATENCY_T latency)
{
    FMC->CTRL1_B.WS = latency;
}

/*!
 * @brief     Enables the Prefetch Buffer.
 *
 * @param     None
 *
 * @retval    None
 */
void FMC_EnablePrefetchBuffer(void)
{
    FMC->CTRL1_B.PBEN = ENABLE;
}

/*!
 * @brief     Disables the Prefetch Buffer.
 *
 * @param     None
 *
 * @retval    None
 */
void FMC_DisablePrefetchBuffer(void)
{
    FMC->CTRL1_B.PBEN = DISABLE;
}

/*!
 * @brief       Checks whether the flash Prefetch Buffer status is set or not
 *
 * @param       None
 *
 * @retval      flash Prefetch Buffer Status (SET or RESET)
 */
uint8_t FMC_ReadPrefetchBufferStatus(void)
{
    if (FMC->CTRL1_B.PBSF)
    {
        return SET;
    }

    return RESET;
}

/*!
 * @brief       Unlocks the flash Program Erase Controller
 *
 * @param       None
 *
 * @retval      None
 */
void FMC_Unlock(void)
{
    FMC->KEY = FMC_KEY_1;
    FMC->KEY = FMC_KEY_2;
}

/*!
 * @brief       Locks the flash Program Erase Controller
 *
 * @param       None
 *
 * @retval      None
 */
void FMC_Lock(void)
{
    FMC->CTRL2_B.LOCK = BIT_SET;
}

/*!
 * @brief       Read flash state
 *
 * @param       None
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_BUSY
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 */
FMC_STATE_T FMC_ReadState(void)
{
    uint32_t status;
    FMC_STATE_T state = FMC_STATE_COMPLETE;

    status = FMC->STS;

    if (status & FMC_FLAG_PE)
    {
        state = FMC_STATE_PG_ERR;
    }
    else if (status & FMC_FLAG_WPE)
    {
        state = FMC_STATE_WRP_ERR;
    }
    else if (status & FMC_FLAG_BUSY)
    {
        state = FMC_STATE_BUSY;
    }

    return state;
}

/*!
 * @brief       Wait for flash controler ready
 *
 * @param       timeOut:    Specifies the time to wait
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_BUSY
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 *                 @arg FMC_STATE_TIMEOUT
 */
FMC_STATE_T FMC_WaitForReady(uint32_t timeOut)
{
    FMC_STATE_T state;

    do
    {
        state = FMC_ReadState();
        timeOut--;
    }
    while ((state == FMC_STATE_BUSY) && (timeOut));

    if (!timeOut)
    {
        state = FMC_STATE_TIMEOUT;
    }

    return state;
}

/*!
 * @brief       Erases a specified flash page
 *
 * @param       pageAddr:   Specifies the page address
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 *                 @arg FMC_STATE_TIMEOUT
 */
FMC_STATE_T FMC_ErasePage(uint32_t pageAddr)
{
    FMC_STATE_T state;

    state = FMC_WaitForReady(FMC_DELAY_ERASE);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->CTRL2_B.PAGEERA = BIT_SET;

        FMC->ADDR = pageAddr;

        FMC->CTRL2_B.STA = BIT_SET;

        state = FMC_WaitForReady(FMC_DELAY_ERASE);

        FMC->CTRL2_B.PAGEERA = BIT_RESET;
    }

    return state;
}

/*!
 * @brief       Erases all flash pages
 *
 * @param       None
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 *                 @arg FMC_STATE_TIMEOUT
 * @note
 */
FMC_STATE_T FMC_EraseAllPages(void)
{
    FMC_STATE_T state;

    state = FMC_WaitForReady(FMC_DELAY_ERASE);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->CTRL2_B.MASSERA = BIT_SET;
        FMC->CTRL2_B.STA = BIT_SET;

        state = FMC_WaitForReady(FMC_DELAY_ERASE);

        FMC->CTRL2_B.MASSERA = BIT_RESET;
    }

    return state;
}

/*!
 * @brief       Program a word at a specified address
 *
 * @param       addr:   Specifies the address to be programmed
 *
 * @param       data:   Specifies the data to be programmed
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 *                 @arg FMC_STATE_TIMEOUT
 */
FMC_STATE_T FMC_ProgramWord(uint32_t addr, uint32_t data)
{
    FMC_STATE_T state;

    state = FMC_WaitForReady(FMC_DELAY_PROGRAM);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->CTRL2_B.PG = BIT_SET;

        *(__IO uint16_t*)addr = (uint16_t)data;

        state = FMC_WaitForReady(FMC_DELAY_PROGRAM);

        if (state == FMC_STATE_COMPLETE)
        {
            *(__IO uint16_t*)(addr + 2) = (uint16_t)(data >> 16);

            state = FMC_WaitForReady(FMC_DELAY_PROGRAM);
        }

        FMC->CTRL2_B.PG = BIT_RESET;
    }

    return state;
}

/*!
 * @brief       Programs a half word at a specified address
 *
 * @param       addr:   Specifies the address to be programmed
 *
 * @param       data:   Specifies the data to be programmed
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 *                 @arg FMC_STATE_TIMEOUT
 */
FMC_STATE_T FMC_ProgramHalfWord(uint32_t addr, uint16_t data)
{
    FMC_STATE_T state;

    state = FMC_WaitForReady(FMC_DELAY_PROGRAM);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->CTRL2_B.PG = BIT_SET;

        *(__IO uint16_t*)addr = data;

        state = FMC_WaitForReady(FMC_DELAY_PROGRAM);

        FMC->CTRL2_B.PG = BIT_RESET;
    }

    return state;
}

/*!
 * @brief       Unlocks the option bytes block access
 *
 * @param       None
 *
 * @retval      None
 */
void FMC_UnlockOptionByte(void)
{
    FMC->OBKEY = FMC_OB_KEY_1;
    FMC->OBKEY = FMC_OB_KEY_2;
}

/*!
 * @brief       Locks the option bytes block access
 *
 * @param       None
 *
 * @retval      None
 */
void FMC_LockOptionByte(void)
{
    FMC->CTRL2_B.OBWEN = BIT_RESET;
}

/*!
 * @brief       Launch the option byte loading
 *
 * @param       None
 *
 * @retval      None
 */
void FMC_LaunchOptionByte(void)
{
    FMC->CTRL2_B.OBLOAD = BIT_SET;
}

/*!
 * @brief       Erase the flash option bytes
 *
 * @param       None
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 *                 @arg FMC_STATE_TIMEOUT
 */
FMC_STATE_T FMC_EraseOptionByte(void)
{
    uint16_t rpKey;
    FMC_STATE_T state;

    rpKey = FMC->OBCS_B.READPROT ? 0 : FMC_RP_KEY;

    state = FMC_WaitForReady(FMC_DELAY_ERASE);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->OBKEY = FMC_KEY_1;
        FMC->OBKEY = FMC_KEY_2;

        FMC->CTRL2_B.OBE = BIT_SET;
        FMC->CTRL2_B.STA = BIT_SET;

        state = FMC_WaitForReady(FMC_DELAY_ERASE);

        if (state == FMC_STATE_COMPLETE)
        {
            FMC->CTRL2_B.OBE = BIT_RESET;

            FMC->CTRL2_B.OBP = BIT_SET;

            OB->READPROT = rpKey;

            state = FMC_WaitForReady(FMC_DELAY_PROGRAM);

            if (state != FMC_STATE_TIMEOUT)
            {
                FMC->CTRL2_B.OBP = BIT_RESET;
            }
        }
        else if (state != FMC_STATE_TIMEOUT)
        {
            FMC->CTRL2_B.OBE = BIT_RESET;
        }
    }

    return state;
}

/*!
 * @brief       Enable the specified page write protection
 *
 * @param       page:   Specifies the address of the pages to be write protected
 *                      This parameter can be any combination of the flowing values:
 *                      @arg FMC_WRP_PAGE_0_1 ... FMC_WRP_PAGE_60_61
 *                      @arg FMC_WRP_PAGE_ALL
 *
 * @retval      Returns the flash state.It can be one of value:
 *                      @arg FMC_STATE_COMPLETE
 *                      @arg FMC_STATE_PG_ERR
 *                      @arg FMC_STATE_WRP_ERR
 *                      @arg FMC_STATE_TIMEOU
 */
FMC_STATE_T FMC_EnableWriteProtection(uint32_t page)
{
    uint8_t i;
    uint16_t temp;
    __IO uint16_t* WRPT;
    FMC_STATE_T state;

    WRPT = &OB->WRTPROT0;

    state = FMC_WaitForReady(FMC_DELAY_PROGRAM);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->OBKEY = FMC_KEY_1;
        FMC->OBKEY = FMC_KEY_2;

        FMC->CTRL2_B.OBP = BIT_SET;

        for (i = 0; i < 4; i++)
        {
            temp = (uint16_t)~(page & 0xff);

            if ((temp != 0xff) && (state == FMC_STATE_COMPLETE))
            {
                WRPT[i] = temp;

                state = FMC_WaitForReady(FMC_DELAY_PROGRAM);
            }

            page >>= 8;
        }

        if (state != FMC_STATE_TIMEOUT)
        {
            FMC->CTRL2_B.OBP = BIT_RESET;
        }
    }

    return state;
}

/*!
 * @brief       Read out protection configuration.
 *
 * @param       rdp:   specifies the read protection level
 *                     This parameter can be any combination of the flowing values:
 *                     @arg FMC_RDP_LEVEL_0
 *                     @arg FMC_RDP_LEVEL_1
 *
 * @retval      Returns the flash state.It can be one of value:
 *                     @arg FMC_STATE_COMPLETE
 *                     @arg FMC_STATE_PG_ERR
 *                     @arg FMC_STATE_WRP_ERR
 *                     @arg FMC_STATE_TIMEOU
 */
FMC_STATE_T FMC_ConfigReadOutProtection(FMC_RDP_T rdp)
{
    FMC_STATE_T state;

    state = FMC_WaitForReady(FMC_DELAY_ERASE);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->OBKEY = FMC_KEY_1;
        FMC->OBKEY = FMC_KEY_2;

        FMC->CTRL2_B.OBE = BIT_SET;
        FMC->CTRL2_B.STA = BIT_SET;

        state = FMC_WaitForReady(FMC_DELAY_ERASE);

        if (state == FMC_STATE_COMPLETE)
        {
            FMC->CTRL2_B.OBE = BIT_RESET;

            FMC->CTRL2_B.OBP = BIT_SET;

            OB->READPROT = rdp;

            state = FMC_WaitForReady(FMC_DELAY_PROGRAM);

            if (state != FMC_STATE_TIMEOUT)
            {
                FMC->CTRL2_B.OBP = BIT_RESET;
            }
        }
        else if (state != FMC_STATE_TIMEOUT)
        {
            FMC->CTRL2_B.OBE = BIT_SET;
        }
    }

    return state;
}

/*!
 * @brief       User option byte configuration
 *
 * @param       userConfig: Pointer to a FMC_UserConfig_T structure that
 *                          contains the configuration information for User option byte
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 *                 @arg FMC_STATE_TIMEOU
 */
FMC_STATE_T FMC_ConfigOptionByteUser(FMC_UserConfig_T* userConfig)
{
    FMC_STATE_T state;
    uint16_t temp;

    state = FMC_WaitForReady(FMC_DELAY_PROGRAM);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->OBKEY = FMC_OB_KEY_1;
        FMC->OBKEY = FMC_OB_KEY_2;

        FMC->CTRL2_B.OBP = BIT_SET;

        temp = (uint32_t)userConfig->iwdtSw | \
               (uint32_t)userConfig->stopce | \
               (uint32_t)userConfig->stdbyce | 0xF8;

        OB->USER = temp;

        state = FMC_WaitForReady(FMC_DELAY_PROGRAM);

        if (state != FMC_STATE_TIMEOUT)
        {
            FMC->CTRL2_B.OBP = BIT_RESET;
        }
    }

    return state;
}

/*!
 * @brief       Enable the BOOT1 option bit
 *
 * @param       None
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 *                 @arg FMC_STATE_TIMEOU
 */
FMC_STATE_T FMC_EnableOptionByteBOOT(void)
{
    FMC_STATE_T state;

    state = FMC_WaitForReady(FMC_DELAY_ERASE);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->CTRL2_B.OBP = BIT_SET;

        OB->USER_B.BOT1 = BIT_SET;

        state = FMC_WaitForReady(FMC_DELAY_ERASE);

        if (state != FMC_STATE_TIMEOUT)
        {
            FMC->CTRL2_B.OBP = BIT_RESET;
        }
    }

    return state;
}

/*!
 * @brief       Disable the BOOT1 option bit
 *
 * @param       None
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 *                 @arg FMC_STATE_TIMEOU
 */
FMC_STATE_T FMC_DisableOptionByteBOOT(void)
{
    FMC_STATE_T state;

    state = FMC_WaitForReady(FMC_DELAY_ERASE);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->CTRL2_B.OBP = BIT_SET;

        OB->USER_B.BOT1 = BIT_RESET;

        state = FMC_WaitForReady(FMC_DELAY_ERASE);

        if (state != FMC_STATE_TIMEOUT)
        {
            FMC->CTRL2_B.OBP = BIT_RESET;
        }
    }

    return state;

}

/*!
 * @brief       Enable the analogue monitoring on VDDA Power source
 *
 * @param       None
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 *                 @arg FMC_STATE_TIMEOU
 */
FMC_STATE_T FMC_EnableOptionByteVDDA(void)
{
    FMC_STATE_T state;

    state = FMC_WaitForReady(FMC_DELAY_ERASE);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->CTRL2_B.OBP = BIT_SET;

        OB->USER_B.VDDAMON = BIT_SET;

        state = FMC_WaitForReady(FMC_DELAY_ERASE);

        if (state != FMC_STATE_TIMEOUT)
        {
            FMC->CTRL2_B.OBP = BIT_RESET;
        }
    }

    return state;

}

/*!
 * @brief       Disable the analogue monitoring on VDDA Power source
 *
 * @param       None
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 *                 @arg FMC_STATE_TIMEOU
 */
FMC_STATE_T FMC_DisableOptionByteVDDA(void)
{
    FMC_STATE_T state;

    state = FMC_WaitForReady(FMC_DELAY_ERASE);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->CTRL2_B.OBP = BIT_SET;

        OB->USER_B.VDDAMON = BIT_RESET;

        state = FMC_WaitForReady(FMC_DELAY_ERASE);

        if (state != FMC_STATE_TIMEOUT)
        {
            FMC->CTRL2_B.OBP = BIT_RESET;
        }
    }

    return state;

}

/*!
 * @brief       Enable the SRAM parity
 *
 * @param       None
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 *                 @arg FMC_STATE_TIMEOU
 */
FMC_STATE_T FMC_EnableOptionByteSRAMParity(void)
{
    FMC_STATE_T state;

    state = FMC_WaitForReady(FMC_DELAY_ERASE);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->CTRL2_B.OBP = BIT_SET;

        OB->USER_B.RPC = BIT_SET;

        state = FMC_WaitForReady(FMC_DELAY_ERASE);

        if (state != FMC_STATE_TIMEOUT)
        {
            FMC->CTRL2_B.OBP = BIT_RESET;
        }
    }

    return state;

}

/*!
 * @brief    Disable the SRAM parity
 *
 * @param    None
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 *                 @arg FMC_STATE_TIMEOU
 */
FMC_STATE_T FMC_DisableOptionByteSRAMParity(void)
{
    FMC_STATE_T state;

    state = FMC_WaitForReady(FMC_DELAY_ERASE);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->CTRL2_B.OBP = BIT_SET;

        OB->USER_B.RPC = BIT_RESET;

        state = FMC_WaitForReady(FMC_DELAY_ERASE);

        if (state != FMC_STATE_TIMEOUT)
        {
            FMC->CTRL2_B.OBP = BIT_RESET;
        }
    }

    return state;

}
/*!
 * @brief     Programs the FMC User Option Byte: WDT, STOP, STDBY,
 *            BOOT1 and VDDA ANALOG monitoring
 *
 * @param     ob_user: Selects all user option bytes
 *                     This parameter is a combination of the following values:
 *                     @arg FMC_OB_IWDT_HW / FMC_OB_IWDT_SW
 *                     @arg FMC_OB_STOP_RESET / FMC_OB_STOP_NRST
 *                     @arg FMC_OB_STDBY_RESET / FMC_OB_STDBY_NRST
 *                     @arg FMC_OB_BOOT0_RESET / FMC_OB_BOOT0_SET
 *                     @arg FMC_OB_BOOT1_RESET / FMC_OB_BOOT1_SET
 *                     @arg FMC_OB_VDDA_ANALOG_OFF / FMC_OB_VDDA_ANALOG_ON
 *                     @arg FMC_OB_SRAM_PARITY_SET / FMC_OB_SRAM_PARITY_RESET
 *                     @arg FMC_OB_BOOT0_SW / FMC_OB_BOOT0_HW
 *
 * @retval      Returns the flash state.It can be one of value:
 *                     @arg FMC_STATE_COMPLETE
 *                     @arg FMC_STATE_PG_ERR
 *                     @arg FMC_STATE_WRP_ERR
 *                     @arg FMC_STATE_TIMEOU
 */
FMC_STATE_T FMC_WriteOptionByteUser(uint8_t ob_user)
{
    FMC_STATE_T state;

    state = FMC_WaitForReady(FMC_DELAY_ERASE);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->CTRL2_B.OBP = BIT_SET;

        OB->USER = ob_user;

        state = FMC_WaitForReady(FMC_DELAY_ERASE);

        if (state != FMC_STATE_TIMEOUT)
        {
            FMC->CTRL2_B.OBP = BIT_RESET;
        }
    }

    return state;

}

/*!
 * @brief       Programs a half word at a specified Option Byte Data address
 *
 * @param       addr: specifies the address to be programmed.
 *              This parameter can be 0x1FFFF804 or 0x1FFFF806.
 * @param       data: specifies the data to be programmed.
 *
 * @retval      Returns the flash state.It can be one of value:
 *                 @arg FMC_STATE_COMPLETE
 *                 @arg FMC_STATE_PG_ERR
 *                 @arg FMC_STATE_WRP_ERR
 *                 @arg FMC_STATE_TIMEOU
 */
FMC_STATE_T FMC_ProgramOptionByteData(uint32_t addr, uint8_t data)
{
    FMC_STATE_T state;

    state = FMC_WaitForReady(FMC_DELAY_ERASE);

    if (state == FMC_STATE_COMPLETE)
    {
        FMC->CTRL2_B.OBP = BIT_SET;

        *(__IO uint16_t*)addr = data;

        state = FMC_WaitForReady(FMC_DELAY_ERASE);

        if (state != FMC_STATE_TIMEOUT)
        {
            FMC->CTRL2_B.OBP = BIT_RESET;
        }
    }

    return state;

}

/*!
 * @brief       Returns the Flash User Option Bytes values
 *
 * @param       None
 *
 * @retval      The flash User Option Bytes
 */
uint8_t FMC_ReadOptionByteUser(void)
{
    return (uint8_t)(FMC->OBCS >> 8);
}

/*!
 * @brief       Returns the flash Write Protection Option Bytes value:
 *
 * @param       None
 *
 * @retval      The Flash Write Protection Option Bytes value:
 */
uint32_t FMC_ReadOptionByteWriteProtection(void)
{
    return (uint32_t)(FMC->WRTPROT);
}

/*!
 * @brief       Checks whether the Flash Read Protection Status is set or not
 *
 * @param       None
 *
 * @retval      Flash ReadOut Protection Status(SET or RESET)
 */
uint8_t FMC_GetReadProtectionStatus(void)
{
    if (FMC->OBCS_B.READPROT)
    {
        return SET;
    }

    return RESET;
}
/*!
 * @brief       Enable the specified flash interrupts
 *
 * @param       interrupt:  Specifies the flash interrupt sources
 *                          The parameter can be combination of following values:
 *                          @arg FMC_INT_ERROR:       Error interruption
 *                          @arg FMC_INT_COMPLETE:    operation complete interruption
 *
 * @retval      None
 */
void FMC_EnableInterrupt(uint32_t interrupt)
{
    FMC->CTRL2 |= interrupt;
}

/*!
 * @brief       Disable the specified flash interrupts
 *
 * @param       interrupt:  Specifies the flash interrupt sources
 *                          The parameter can be combination of following values:
 *                          @arg FMC_INT_ERROR:       Error interruption
 *                          @arg FMC_INT_COMPLETE:    operation complete interruption
 *
 * @retval      None
 */
void FMC_DisableInterrupt(uint32_t interrupt)
{
    FMC->CTRL2 &= ~interrupt;
}

/*!
 * @brief       Checks whether the specified flash flag is set or not
 *

 * @param       flag:   Specifies the flash flag to check
 *                      The parameter can be one of following values:
 *                      @arg FMC_FLAG_BUSY: Busy flag
 *                      @arg FMC_FLAG_PE:   Program error flag
 *                      @arg FMC_FLAG_WPE:  Write protection flag
 *                      @arg FMC_FLAG_OC:   Operation complete flag
 *
 * @retval      None
 */
uint8_t FMC_ReadStatusFlag(FMC_FLAG_T flag)
{
    uint8_t status;

    if (flag & 0xff)
    {
        status = FMC->STS & flag;
    }
    else
    {
        status = FMC->OBCS & flag;
    }

    if (status)
    {
        return SET;
    }

    return RESET;
}

/*!
 * @brief       Clear the specified flash flag
 *
 * @param       flag:   Specifies the flash flag to clear
 *                      This parameter can be any combination of the following values:
 *                      @arg FMC_FLAG_BUSY:   Busy flag
 *                      @arg FMC_FLAG_PE:     Program error flag
 *                      @arg FMC_FLAG_WPE:    Write protection error flag
 *                      @arg FMC_FLAG_OC:     Operation complete flag
 *
 * @retval      None
 */
void FMC_ClearStatusFlag(uint8_t flag)
{
    if (flag & 0xff)
    {
        FMC->STS = flag;
    }
}

/**@} end of group FMC_Functions*/
/**@} end of group FMC_Driver*/
/**@} end of group APM32A0xx_StdPeriphDriver*/
