/*!
 * @file        main.c
 *
 * @brief       Main program body
 *
 * @version     V1.0.0
 *
 * @date        2026-02-25
 *
 * @attention
 *
 *  Copyright (C) 2026 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (Geehy Semiconductor Software License Agreement).
 *
 *  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 Semiconductor Software License Agreement for the governing permissions
 *  and limitations under the License.
 */

/* Includes */
#include "Board.h"
#include "stdio.h"
#include "inttypes.h"
#include "user_fls_config.h"

/** @addtogroup Examples
  @{
*/

/** @addtogroup FLASH_Examples
  @{
*/

/** @addtogroup FMC_Flash_WriteProtection
  @{
*/

/** @defgroup FMC_Flash_WriteProtection_Variables Variables
  @{
*/

/* Global flag: Trigger WRP restore by key */
static volatile uint8_t g_restore_wrp_flag = 0U;

/**@} end of group FMC_Flash_WriteProtection_Variables */

/** @defgroup FMC_Flash_WriteProtection_Functions Functions
  @{
*/

/* WRP restore function */
void Fmc_RestoreWRP(void);

/*!
 * @brief       Main program
 *
 * @param       None
 *
 * @retval      None
 */
int main(void)
{
    uint32_t addr = 0U;
    uint32_t data[2] = {0U,0U};
    uint32_t pFlashWriteProSts = 0U;
    uint32_t dFlashWriteProSts = 0U;

    /* Initialize USART1 for debug */
    Board_ComInit(COM1);
    /* Initialize KEY1 as external interrupt */
    Board_ButtonInit(BUTTON_KEY1, BUTTON_MODE_EINT);

    /* Unlock FMC */
    Fmc_Unlock();

    /* Get test flash address */
    addr = FLS_TEST_ADDR;

    /* Check test flag */
    if((*(uint32_t*)addr) == (uint32_t)TEST_FLAG_DATA)
    {
        /* Get option byte base address */
        addr = FLS_OPTION_BYTE_ADDR;
        printf("\r\nEnd Of Test,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", addr, *(volatile uint32_t*)addr);
        printf("End Of Test,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", (addr+(uint32_t)4U), 
            *(volatile uint32_t*)(addr+(uint32_t)4U));
        printf("End Of Test,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", (addr+(uint32_t)8U), 
            *(volatile uint32_t*)(addr+(uint32_t)8U));
        printf("End Of Test,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", (addr+(uint32_t)12U), 
            *(volatile uint32_t*)(addr+(uint32_t)12U));
        printf("End Of Test,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", (addr+(uint32_t)16U), 
            *(volatile uint32_t*)(addr+(uint32_t)16U));
        printf("End Of Test,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", (addr+(uint32_t)20U), 
            *(volatile uint32_t*)(addr+(uint32_t)20U));
        printf("Flash write protection test completed!\r\n");

        /* Erase test page */
        addr = FLS_TEST_ADDR;
        Fmc_ErasePage(addr);
    }
    else
    {
        pFlashWriteProSts = (Fmc_ReadPflashWriteProtection() >> 24U) & (uint32_t)0xff;
        dFlashWriteProSts = (Fmc_ReadDflashWriteProtection() >> 24U)  & (uint32_t)0xff;

        /* Check WRP status */
        if((pFlashWriteProSts == (uint32_t)WRP_PFLASH_PROTECT) ||
            (dFlashWriteProSts == (uint32_t)WRP_DFLASH_PROTECT))
        {
            printf("\r\nWRP already enabled!\r\n");

            /* Read option byte data */
            addr = FLS_OPTION_BYTE_ADDR;
            printf("Turn on write protection,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", addr, *(volatile uint32_t*)addr);
            printf("Turn on write protection,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)4U), *(volatile uint32_t*)(addr+(uint32_t)4U));
            printf("Turn on write protection,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)8U), *(volatile uint32_t*)(addr+(uint32_t)8U));
            printf("Turn on write protection,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)12U), *(volatile uint32_t*)(addr+(uint32_t)12U));
            printf("Turn on write protection,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)16U), *(volatile uint32_t*)(addr+(uint32_t)16U));
            printf("Turn on write protection,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)20U), *(volatile uint32_t*)(addr+(uint32_t)20U));

            /* Write test data to flash */
            data[0] = TEST_DATA_0;
            data[1] = TEST_DATA_1;
            addr = FLS_TEST_ADDR;
            Fmc_ProgramWord(addr, (uint32_t)FMC_LEN_64BIT, data);
        }
        else
        {
            /* Get option byte address */
            addr = FLS_OPTION_BYTE_ADDR;

            /* Unlock and erase option byte */
            Fmc_UnlockOptionByte();
            Fmc_EraseOptionByte();

            /* Read data after erase */
            printf("After Erase,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", addr, *(volatile uint32_t*)addr);
            printf("After Erase,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)4U), *(volatile uint32_t*)(addr+(uint32_t)4U));
            printf("After Erase,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)8U), *(volatile uint32_t*)(addr+(uint32_t)8U));
            printf("After Erase,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)12U), *(volatile uint32_t*)(addr+(uint32_t)12U));
            printf("After Erase,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)16U), *(volatile uint32_t*)(addr+(uint32_t)16U));
            printf("After Erase,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)20U), *(volatile uint32_t*)(addr+(uint32_t)20U));

            /* Config option byte */
            Fmc_ConfigOptionByteUser(&g_ob_default_config);

            /* Enable PFLASH write protection */
            Fmc_EnableWriteProtectionPflash(g_wrp_pflash_config);

            /* Enable DFLASH write protection */
            Fmc_EnableWriteProtectionDflash(g_wrp_dflash_config);

            /* Read data after write */
            printf("\r\nAfter Write,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", addr, *(volatile uint32_t*)addr);
            printf("After Write,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)4U), *(volatile uint32_t*)(addr+(uint32_t)4U));
            printf("After Write,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)8U), *(volatile uint32_t*)(addr+(uint32_t)8U));
            printf("After Write,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)12U), *(volatile uint32_t*)(addr+(uint32_t)12U));
            printf("After Write,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)16U), *(volatile uint32_t*)(addr+(uint32_t)16U));
            printf("After Write,       0x%08" PRIx32 " value = 0x%08" PRIx32 "\r\n", 
                (addr+(uint32_t)20U), *(volatile uint32_t*)(addr+(uint32_t)20U));

            /* Lock option byte and load */
            Fmc_LockOptionByte();
            Fmc_LaunchOptionByte();
        }

        /* Main loop */
        for (;;)
        {
            if(1U == g_restore_wrp_flag)
            {
                g_restore_wrp_flag = 0U;
                Fmc_RestoreWRP();
            }
			else
			{
				/* nothing */
			}
            __NOP();
        }
    }

    /* Infinite loop */
    for (;;)
    {
    }
}

/*!
 * @brief    WRP restore interface (back to normal state)
 *
 * @param    None
 *
 * @retval   None
 */
void Fmc_RestoreWRP(void)
{
    /* Unlock FMC and Option Byte */
    Fmc_Unlock();
    Fmc_UnlockOptionByte();

    /* Erase Option Byte (clear original WRP configuration) */
    Fmc_EraseOptionByte();

    /* Configure Option Byte to default value */
    Fmc_ConfigOptionByteUser(&g_ob_default_config);

    /* Disable PFLASH write protection */
    Fmc_EnableWriteProtectionPflash(g_wrp_pflash_restore);

    /* Disable DFLASH write protection */
    Fmc_EnableWriteProtectionDflash(g_wrp_dflash_restore);

    /* Lock Option Byte */
    Fmc_LockOptionByte();

    /* Launch Option Byte loading and prompt reset */
    printf("WRP restored!\r\n");
    Fmc_LaunchOptionByte();
}

/*!
 * @brief    KEY1 interrupt service function: Trigger WRP restore
 *
 * @param    None
 *
 * @retval   None
 */
void Eint_RestoreWrpIsr(void)
{
    if (Eint_ReadStatusFlag((uint32_t)EINT_LINE0) == 1U)
    {
        g_restore_wrp_flag = 1U;
        Eint_ClearStatusFlag((uint32_t)EINT_LINE0);
    }
	else
	{
		/* nothing */
	}

    if (Eint_ReadStatusFlag((uint32_t)EINT_LINE1) == 1U)
    {
        Eint_ClearStatusFlag((uint32_t)EINT_LINE1);
    }
	else
	{
		/* nothing */
	}
}

#if defined (__CC_ARM) || defined (__ICCARM__) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))

/*!
* @brief       Redirect C Library function printf to serial port.
*/
int fputc(int ch, FILE* f)
{
    Usart_TxData(DEBUG_USART, (uint8_t)ch);
    while (Usart_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == 0U)
    {
        /* do nothing */
    }
    return (ch);
}

#elif defined (__GNUC__)

/*!
* @brief       Redirect C Library function printf to serial port.
*/
int __io_putchar(int ch)
{
    Usart_TxData(DEBUG_USART, ch);
    while (Usart_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);
    return ch;
}

/*!
* @brief       Redirect C Library function printf to serial port.
*/
int _write(int file, char* ptr, int len)
{
    int i;
    for (i = 0; i < len; i++)
    {
        __io_putchar(*ptr++);
    }
    return len;
}

#else
#pragma message("Not supported compiler type")
#endif

/**@} end of group FMC_Flash_WriteProtection_Functions */
/**@} end of group FMC_Flash_WriteProtection */
/**@} end of group Examples */
