/*!
 * @file        bmp561_flash_ctrl.c
 *
 * @brief       This file provides all the FLASH_CTRL firmware functions
 *
 * @version     V1.0.0
 *
 * @date        2025-01-01
 *
 * @attention
 *
 *  Copyright (C) 2025 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 "bmp561_flash_ctrl.h"
#include "bmp561_sysctrl.h"

/** @addtogroup Peripherals_Library Standard Peripheral Library
  @{
*/

/** @addtogroup FLASH_CTRL_Driver FLASH_CTRL Driver
  @{
*/

/** @addtogroup FLASH_CTRL_Functions Functions
  @{
*/

/*!
 * @brief     Resets the FLASH_CTRL peripheral registers to their default reset values.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void FLASH_CTRL_Reset(void)
{
    SYSCTRL_Unlock();
    SYSCTRL_EnableAHBPeriphReset(SYSCTRL_AHB_FLASH);
    SYSCTRL_DisableAHBPeriphReset(SYSCTRL_AHB_FLASH);
    SYSCTRL_Lock();
}

/*!
 * @brief     Writes the flash key.
 *
 * @param     keyVal: flash key value.
 *
 * @retval    None
 *
 */
void FLASH_WriteFlashKey(uint32_t keyVal)
{
    FLASH->KEY = keyVal;
}

/*!
 * @brief     Reads the flash key status.
 *
 * @param     None
 *
 * @retval    The status of flash key.
 *            This value can be one of the following values:
 *            @arg FLASH_KEY_WP_NOKEY        : Write protect
 *            @arg FLASH_KEY_MAIN_UNLOCK     : Unlock Main zone
 *            @arg FLASH_KEY_DATA_UNLOCK     : Unlock Data-Flash zone
 *            @arg FLASH_KEY_KEYERR_LOCK     : Key error
 *            @arg FLASH_KEY_ONLY_ONEKEY_EER0: Only write one key
 *            @arg FLASH_KEY_ONLY_ONEKEY_EER1: Only write one key
 *            @arg FLASH_KEY_ONLY_ONEKEY_EER2: Only write one key
 *            @arg FLASH_KEY_ONLY_ONEKEY_EER3: Only write one key
 *
 */
FLASH_KEY_T FLASH_ReadKeyStatus(void)
{
    return (FLASH_KEY_T)FLASH->SR_B.KEYSTA;
}

/*!
 * @brief     Writes the option byte key.
 *
 * @param     keyVal: option byte key value.
 *
 * @retval    None
 *
 */
void FLASH_WriteOptionByteKey(uint32_t keyVal)
{
    FLASH->OPTKEY = keyVal;
}

/*!
 * @brief     Reads the option byte key status.
 *
 * @param     None
 *
 * @retval    The status of option byte key.
 *            This value can be one of the following values:
 *            @arg FLASH_OPTKEY_WP_NOKEY        : Write protect
 *            @arg FLASH_OPTKEY_OPT0_UNLOCK     : Unlock Option byte 0
 *            @arg FLASH_OPTKEY_OPT1_UNLOCK     : Unlock Option byte 1
 *            @arg FLASH_OPTKEY_ERROR_LOCK      : Key error
 *            @arg FLASH_OPTKEY_ONLY_ONEKEY_EER0: Only write one key
 *            @arg FLASH_OPTKEY_ONLY_ONEKEY_EER1: Only write one key
 *            @arg FLASH_OPTKEY_ONLY_ONEKEY_EER2: Only write one key
 *            @arg FLASH_OPTKEY_ONLY_ONEKEY_EER3: Only write one key
 *
 */
FLASH_OPTKEY_T FLASH_ReadOptionByteKeyStatus(void)
{
    return (FLASH_OPTKEY_T)FLASH->SR_B.OPTKEYSTA;
}

/*!
 * @brief     Reads the specified FLASH flag.
 *
 * @param     flag: specifies the FLASH flag.
 *                  This parameter can be one of the following values:
 *                  @arg FLASH_FLAG_ERD    : Erase done flag
 *                  @arg FLASH_FLAG_PRD    : Program done flag
 *                  @arg FLASH_FLAG_BUSY   : Busy flag
 *                  @arg FLASH_FLAG_KEYERR : Flash key error flag
 *                  @arg FLASH_FLAG_WRPRTER: Write protect error flag
 *                  @arg FLASH_FLAG_WADRERR: Address error flag
 *                  @arg FLASH_FLAG_TRIMERR: Trim error flag
 *
 * @retval    The new state of flag(SET or RESET).
 *
 */
uint8_t FLASH_ReadFlag(FLASH_FLAG_T flag)
{
    uint8_t status = RESET;

    if((FLASH->SR & flag) != (uint32_t)RESET)
    {
        status = SET;
    }
    else
    {
        status = RESET;
    }
    return status;
}

/*!
 * @brief     Enables FLASH option byte force update.
 *
 * @param     None
 *
 * @retval    None
 *
 * @note      Once FLASH_CR_FORCE_OPTLOAD bit is write to 1, Option byte will reload and occurs system reset.
 *
 */
void FLASH_EnableOptionByteForceUpdate(void)
{
    FLASH->CR |= (((uint32_t)0xA5A5 << 0x10) | 0x8000);
}

/*!
 * @brief     Enables FLASH interrupt.
 *
 * @param     interrupt: specifies the FLASH interrupt.
 *                       This parameter can be one of the following values:
 *                       @arg FLASH_INT_EOP: End of program/erase interrupt
 *                       @arg FLASH_INT_ERR: Error occur interrupt
 *
 * @retval    None
 *
 */
void FLASH_EnableInterrupt(FLASH_INT_T interrupt)
{
    FLASH->CR |= interrupt;
}

/*!
 * @brief     Disables FLASH interrupt.
 *
 * @param     interrupt: specifies the FLASH interrupt.
 *                       This parameter can be one of the following values:
 *                       @arg FLASH_INT_EOP: End of program/erase interrupt
 *                       @arg FLASH_INT_ERR: Error occur interrupt
 *
 * @retval    None
 *
 */
void FLASH_DisableInterrupt(FLASH_INT_T interrupt)
{
    FLASH->CR |= interrupt;
}

/*!
 * @brief     Configures flash erase type.
 *
 * @param     eraseType: specifies the flash erase type.
 *                       This parameter can be one of the following values:
 *                       @arg FLASH_ERASE_SECTOR: Sector erase
 *                       @arg FLASH_ERASE_MAIN  : Main erase
 *                       @arg FLASH_ERASE_CHIP  : Chip erase(SWD only)
 *
 * @retval    None
 *
 */
void FLASH_ConfigEraseOption(uint8_t eraseType)
{
    FLASH->CR_B.ERTYPE = eraseType;
}

/*!
 * @brief     Start flash erase.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void FLASH_StartErase(void)
{
    FLASH->CR_B.EREQ = BIT_SET;
}

/*!
 * @brief     Start flash program.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void FLASH_StartProgram(void)
{
    FLASH->CR_B.PREQ = BIT_SET;
}

/*!
 * @brief     Configures flash Erase or Programming base time.
 *
 * @param     counter: flash 1us unit counter.
 *
 * @retval    None
 *
 * @note      For example, system clock is 4MHz, so 1us unit counter is 4 cycle, FLASH_TMCTRL = 0x03.
 *            Flash Erase or Programming clock can not lower than 1MHz.
 *
 */
void FLASH_ConfigBaseTime(uint8_t counter)
{
    FLASH->TMCTRL_B.UNIT = (uint32_t)counter;
}

/*!
 * @brief     Reads flash option byte flag.
 *
 * @param     flag: specifies the flash option byte flag.
 *                  This parameter can be one of the following values:
 *                  @arg FLASH_OB_OPTERR     : Option byte error flag
 *                  @arg FLASH_OB_LEVEL1_PROT: Level 1 is protected flag
 *                  @arg FLASH_OB_LEVEL2_PROT: Level 2 is protected flag
 *                  @arg FLASH_OB_NBOOT0     : Nboot0 flag
 *                  @arg FLASH_OB_NBOOT1     : Nboot1 flag
 *                  @arg FLASH_OB_WWDTSW     : "0" means hardware activate WWDT, "1" means software activate WWDT
 *                  @arg FLASH_OB_IWDTSW     : "0" means hardware activate IWDT, "1" means software activate IWDT
 *                  @arg FLASH_OB_WLOCKEN    : Write protect flag
 *
 * @retval    The new state of flag(SET or RESET)
 *
 */
uint8_t FLASH_ReadOptionByteFlag(FLASH_OB_T flag)
{
    uint8_t status = RESET;

    if((FLASH->OBR & flag) != (uint32_t)RESET)
    {
        status = SET;
    }
    else
    {
        status = RESET;
    }
    return status;
}

/*!
 * @brief     Reads data flash protect(DFP) flag.
 *
 * @param     None
 *
 * @retval    The 8 bit value of DFP flag.
 *
 * @note      DFP[0] = 0x0, SWD change RDP to decryption will not erase sector.
 *            DFP[0] = 0x1, SWD change RDP to decryption will erase sector.
 *            DFP[7:1] indicate each sectors' data flash zone.
 */
uint8_t FLASH_ReadDataFlashProtectFlag(void)
{
    return FLASH->OBR_B.DFP;
}

/*!
 * @brief     Reads LVD enable flag.
 *
 * @param     None
 *
 * @retval    None
 *
 */
uint8_t FLASH_ReadLVDEnableFlag(void)
{
    return FLASH->OBR_B.LVDEN;
}

/*!
 * @brief     Reads LVDV in FLASH_CTRL peripheral.
 *
 * @param     None
 *
 * @retval    specifies LVD mode.
 *            This parameter can be one of the following values:
 *            @arg FLASH_LVDV_1P8V: 1.8V
 *            @arg FLASH_LVDV_1P9V: 1.9V
 *            @arg FLASH_LVDV_2P0V: 2.0V
 *            @arg FLASH_LVDV_2P1V: 2.1V
 *            @arg FLASH_LVDV_2P2V: 2.2V
 *            @arg FLASH_LVDV_2P3V: 2.3V
 *            @arg FLASH_LVDV_2P4V: 2.4V
 *            @arg FLASH_LVDV_2P5V: 2.5V
 *            @arg FLASH_LVDV_2P6V: 2.6V
 *            @arg FLASH_LVDV_2P7V: 2.7V
 *            @arg FLASH_LVDV_2P8V: 2.8V
 *            @arg FLASH_LVDV_2P9V: 2.9V
 *            @arg FLASH_LVDV_3P0V: 3.0V
 *            @arg FLASH_LVDV_3P1V: 3.1V
 *            @arg FLASH_LVDV_3P2V: 3.2V
 *            @arg FLASH_LVDV_3P3V: 3.3V
 *
 */
FLASH_LVDV_T FLASH_ReadLVDV(void)
{
    return (FLASH_LVDV_T)FLASH->OBR_B.LVDSEL;
}

/*!
 * @brief     Reads data flash write protect flag.
 *
 * @param     sectorNum: specifies the check sector number.
 *                       This parameter can be 0x00 ~ 0x07 corresponding data flash sector0 ~ sector7.
 *
 * @retval    Data flash sector write protect status.
 *            This parameter can be one of the following values:
 *            @arg FLASH_WP_WP : Write protect, not support erase or programming.
 *            @arg FLASH_WP_NWP: Not write protect status, support erase or programming.
 *
 */
FLASH_WP_T FLASH_ReadDataFlashLockFlag(uint8_t sectorNum)
{
    FLASH_WP_T status;

    if(((FLASH->WLOCKDF >> sectorNum) & 0x01) != (uint32_t)FLASH_WP_WP)
    {
        status = FLASH_WP_NWP;
    }
    else
    {
        status = FLASH_WP_WP;
    }
    return status;
}

/*!
 * @brief     Reads main flash write protect flag.
 *
 * @param     sectorBlock: specifies the check sector number.
 *                         This parameter can be 0x00 ~ 0x1F.
 *                         SectorBlock = 0x00: main flash sector0 ~ sector3.
 *                         SectorBlock = 0x01: main flash sector4 ~ sector7.
 *                         SectorBlock = 0x02: main flash sector8 ~ sector11.
 *                         SectorBlock = 0x03: main flash sector12 ~ sector15.
 *                         ...
 *                         SectorBlock = 0x1F: main flash sector124 ~ sector127.
 *
 * @retval    Main flash sector write protect status.
 *            This parameter can be one of the following values:
 *            @arg FLASH_WP_WP : Write protect, not support erase or programming.
 *            @arg FLASH_WP_NWP: Not write protect status, support erase or programming.
 *
 */
FLASH_WP_T FLASH_ReadMainFlashLockFlag(uint8_t sectorBlock)
{
    FLASH_WP_T status;

    if(((FLASH->WLOCKM >> sectorBlock) & 0x01) != (uint32_t)FLASH_WP_WP)
    {
        status = FLASH_WP_NWP;
    }
    else
    {
        status = FLASH_WP_WP;
    }
    return status;
}

/*!
 * @brief     Enables FLASH low power.
 *
 * @param     None
 *
 * @retval    None
 *
 * @note      It's not allowed flash enable low power mode when code running in flash.
 *
 */
void FLASH_EnableLowPower(void)
{
    uint32_t reg;
    reg = FLASH->IPCTRL & 0x7;
    reg |= (0xA5A50000 | 0x01);
    FLASH->IPCTRL = reg;
}

/*!
 * @brief     Disables FLASH low power.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void FLASH_DisableLowPower(void)
{
    uint32_t reg;
    reg = FLASH->IPCTRL & 0x6;
    reg |= 0xA5A50000;
    FLASH->IPCTRL = reg;
}

/*!
 * @brief     Enables FLASH fast wake up.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void FLASH_EnableFastWakeUp(void)
{
    uint32_t reg;
    reg = FLASH->IPCTRL & 0x7;
    reg |= 0xA5A50000 | 0x02;
    FLASH->IPCTRL = reg;
}

/*!
 * @brief     Disables FLASH fast wake up.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void FLASH_DisableFastWakeUp(void)
{
    uint32_t reg;
    reg = FLASH->IPCTRL & 0x5;
    reg |= 0xA5A50000;
    FLASH->IPCTRL = reg;
}

/*!
 * @brief     Enables FLASH read only.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void FLASH_EnableReadOnly(void)
{
    uint32_t reg;
    reg = FLASH->IPCTRL & 0x7;
    reg |= 0xA5A50000 | 0x04;
    FLASH->IPCTRL = reg;
}

/*!
 * @brief     Disables FLASH read only.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void FLASH_DisableReadOnly(void)
{
    uint32_t reg;
    reg = FLASH->IPCTRL & 0x3;
    reg |= 0xA5A50000;
    FLASH->IPCTRL = reg;
}

/*!
 * @brief     Reads flash trim tag.
 *
 * @param     None
 *
 * @retval    The new states of the trim tag.
 *
 */
uint8_t FLASH_ReadTrimTag(void)
{
    return FLASH->TRIM_TAG_B.FIRST_FAIL;
}

/*!
 * @brief     Flash chip erase.
 *
 * @param     None
 *
 * @retval    None
 *
 * @note      User code can't execute chip erase.
 *
 */
void FLASH_EraseChip(void)
{
    while((FLASH->SR & 0x04));
    /** Unlock Flash */
    FLASH->KEY = 0x96969696;
    FLASH->KEY = 0x3C3C3C3C;
    /** Wait main flash unlock */
    while((FLASH->SR_B.KEYSTA) != FLASH_KEY_MAIN_UNLOCK);
    /** Start chip erase */
    FLASH->CR_B.ERTYPE = FLASH_ERASE_CHIP;
    FLASH->CR_B.EREQ = BIT_SET;
    M32(MAINFLASH_BASEADDR) = 0x0000A5A5;
    /** Wait chip erase done */
    while((FLASH->SR & 0x04));
    /** Lock Flash */
    FLASH->KEY = 0x12345678;
    FLASH->SR_B.ERD = BIT_RESET;
}

/*!
 * @brief     Flash main block erase.
 *
 * @param     None
 *
 * @retval    None
 *
 */
void FLASH_EraseMainBlock(void)
{
    while((FLASH->SR & 0x04));
    /** Unlock Flash */
    FLASH->KEY = 0x96969696;
    FLASH->KEY = 0x3C3C3C3C;
    /** Wait main flash unlock */
    while((FLASH->SR_B.KEYSTA) != FLASH_KEY_MAIN_UNLOCK);
    /** Start main block erase */
    FLASH->CR_B.ERTYPE = FLASH_ERASE_MAIN;
    FLASH->CR_B.EREQ = BIT_SET;
    M32(MAINFLASH_BASEADDR) = 0x0000A5A5;
    /** Wait main block erase done */
    while((FLASH->SR & 0x04));
    /** Lock Flash */
    FLASH->KEY = 0x12345678;
    FLASH->SR_B.ERD = BIT_RESET;
}

/*!
 * @brief     Flash sector erase.
 *
 * @param     addr: erased sector addr.
 *
 * @retval    None
 *
 * @note      Erase area support main flash, data flash, option1(NVR12).
 *
 */
void FLASH_EraseSector(uint32_t addr)
{
    uint8_t zone = 0;
    while((FLASH->SR & 0x04));
    if((addr >= MAINFLASH_BASEADDR) && (addr < (MAINFLASH_BASEADDR + MAINFLASH_SIZE)))    //!< main flash
    {
        zone = 0;
        FLASH->KEY = 0x96969696;
        FLASH->KEY = 0x3C3C3C3C;
		    while((FLASH->SR_B.KEYSTA) != FLASH_KEY_MAIN_UNLOCK);
    }
    else if((addr >= DATAFLASH_BASEADDR) && (addr < (DATAFLASH_BASEADDR + DATAFLASH_SIZE)))    //!< data flash
    {
        zone = 0;
        FLASH->KEY = 0xF3E2D1C0;
        FLASH->KEY = 0x01FE2C3D;
		    while((FLASH->SR_B.KEYSTA) != FLASH_KEY_DATA_UNLOCK);
    }
    else if((addr >= OPTION1_BASEADDR) && (addr < (OPTION1_BASEADDR + OPTION1_SIZE)))    //!< option1 ~ NVR12
    {
        zone = 1;
        FLASH->OPTKEY = 0xCCCCDDDD;
        FLASH->OPTKEY = 0x55667788;
		    while((FLASH->SR_B.OPTKEYSTA) != FLASH_OPTKEY_OPT1_UNLOCK);
    }
    else
    {
        return ;
    }
    /** Start sector erase */
    FLASH->CR_B.ERTYPE = FLASH_ERASE_SECTOR;
    FLASH->CR_B.EREQ = BIT_SET;
    M32(addr) = 0x0000A5A5;
    /** Wait sector erase done */
    while((FLASH->SR & 0x04));
    /** Lock Flash */
    if(zone == 0)
    {
        FLASH->KEY = 0x12345678;
    }
		else
    {
        FLASH->OPTKEY = 0x12345678;
    }
    FLASH->SR_B.ERD = BIT_SET;
}

/*!
 * @brief     Flash page program.
 *
 * @param     addr: flash address.
 *
 * @param     length: write data len, unit is byte.
 *
 * @param     buf: source data pointer.
 *
 * @retval    None
 *
 * @note      Program area support main flash, data flash, option1(NVR12).
 *
 */
void FLASH_ProgramPage(uint32_t addr, uint32_t length, uint8_t* buf)
{
    uint32_t i = 0x00;
    uint8_t zone = 0;

    while((FLASH->SR & 0x04));
    if((addr >= MAINFLASH_BASEADDR) && (addr < (MAINFLASH_BASEADDR + MAINFLASH_SIZE)))    //!< main flash
    {
        zone = 0;
        FLASH->KEY = 0x96969696;
        FLASH->KEY = 0x3C3C3C3C;
		    while((FLASH->SR_B.KEYSTA) != FLASH_KEY_MAIN_UNLOCK);
    }
    else if((addr >= DATAFLASH_BASEADDR) && (addr < (DATAFLASH_BASEADDR + DATAFLASH_SIZE)))    //!< data flash
    {
        zone = 0;
        FLASH->KEY = 0xF3E2D1C0;
        FLASH->KEY = 0x01FE2C3D;
		    while((FLASH->SR_B.KEYSTA) != FLASH_KEY_DATA_UNLOCK);
    }
    else if((addr >= OPTION1_BASEADDR) && (addr < (OPTION1_BASEADDR + OPTION1_SIZE)))    //!< option1 ~ NVR12
    {
        zone = 1;
        FLASH->OPTKEY = 0xCCCCDDDD;
        FLASH->OPTKEY = 0x55667788;
		    while((FLASH->SR_B.OPTKEYSTA) != FLASH_OPTKEY_OPT1_UNLOCK);
    }
    else
    {
        return ;
    }
    /** Start program page */
    while (length)
    {
        while((FLASH->SR & 0x04));
        FLASH->CR_B.PREQ = BIT_SET;
        M32(addr) = ((buf[i * 4 + 3] << 24) | (buf[i * 4 + 2] << 16) | (buf[i * 4 + 1] << 8) | buf[i * 4]);
        addr += 4;
        i += 1;
        length -= 4;
    }
    /** Lock Flash */
    if(zone == 0)
    {
        FLASH->KEY = 0x12345678;
    }
		else
    {
        FLASH->OPTKEY = 0x12345678;
    }
    FLASH->SR_B.PRD = BIT_SET;
}

/**@} end of group FLASH_CTRL_Functions*/
/**@} end of group FLASH_CTRL_Driver*/
/**@} end of group Peripherals_Library*/
