/**
 * @file        iap_flash.c
 *
 * @brief       Include IAP flash operation
 *
 * @version     V1.0.0
 *
 * @date        2023-07-31
 *
 * @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.
 */

/* Includes ***************************************************************/
#include "iap_flash.h"

/* Private includes *******************************************************/
#include <stdio.h>

/* Private macro **********************************************************/

/* Private typedef ********************************************************/

/* Private variables ******************************************************/
uint32_t JumpAddress;

/* Private function prototypes ********************************************/

/* External variables *****************************************************/

/* External functions *****************************************************/
pFunction Jump_To_Application;

/**
 * @brief   Unlocks Flash for write access
 *      
 * @param   None
 *      
 * @retval  None
 *
 */
void IAP_FLASH_Init(void)
{
    /* Unlock the flash memory */
    DAL_FLASH_Unlock();

    /* Clear all Flash flags */
    __DAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | \
                           FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | \
                           FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
}

/**
 * @brief   Erase of all user flash area
 *
 * @param   application
 *          @arg IAP_APP1 : IAP_Application1
 *          @arg IAP_APP2 : IAP_Application2
 *
 * @retval  SUCCESS: user flash area successfully erased
 *          ERROR: error occurred
 */
uint32_t IAP_FLASH_Erase(IAP_APP_T application)
{
    FLASH_EraseInitTypeDef Erase_InitStruct = {0};
    
    uint32_t startSector;
    uint32_t endSector;
    uint32_t nbrOfSectors;
    uint32_t sectorError = 0U;
    
    /* Configure the Flash to re-program the Flash */
    IAP_FLASH_Init();
    
    if (application == IAP_APP1)
    {
        startSector = USER_APP1_START_SECTOR;
        endSector = USER_APP1_END_SECTOR;
    }
    else
    {
        startSector = USER_APP2_START_SECTOR;
        endSector = USER_APP2_END_SECTOR;
    }

    nbrOfSectors = endSector - startSector + 1U;
    
    /* Erase sector */
    Erase_InitStruct.Sector         = startSector;
    Erase_InitStruct.NbSectors      = nbrOfSectors;
    Erase_InitStruct.TypeErase      = FLASH_TYPEERASE_SECTORS;
    Erase_InitStruct.VoltageRange   = FLASH_VOLTAGE_RANGE_3;
    if(DAL_FLASHEx_Erase(&Erase_InitStruct, &sectorError) != DAL_OK)
    {
        Error_Handler();
    }

    return (SUCCESS);
}

/**
 * @brief   Write the appoint data buffer in flash
 *
 * @param   address: start address for writing data buffer
 *
 * @param   data: pointer on data buffer
 *
 * @param   length: length of data buffer (unit is 32-bit word)
 *
 * @param   application
 *          @arg IAP_APP1 : IAP_Application1
 *          @arg IAP_APP2 : IAP_Application2
 *
 * @retval  SUCCESS: data successfully written to Flash memory
 *          ERROR: Error occurred while writing data in Flash memory
 *
 * @note    buffer data are 32-bit aligned
 */
uint32_t IAP_FLASH_Write(uint32_t address, uint32_t* data, uint16_t length, IAP_APP_T application)
{
    uint32_t i;
    uint32_t endAddress;

    if (application == IAP_APP1)
    {
        endAddress = USER_APP1_END_ADDRESS - 4U;
    }
    else
    {
        endAddress = USER_APP2_END_ADDRESS - 4U;
    }
    
    for (i = 0U; i < length; i++)
    {
        if (address > endAddress)
        {
            return (ERROR);
        }

        /** The operation will be done by word and Check the written value*/
        if (DAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, *(uint32_t*)(data + i)) != DAL_OK)
        {
            return (ERROR);
        }
        else
        {
            if (*(uint32_t*)address != *(uint32_t*)(data + i))
            {
                return (ERROR);
            }

            address += 4;
        }
    }

    return (SUCCESS);
}

/**
 * @brief   Read flash write protection status
 *  
 * @param   None
 *  
 * @retval  Flash write protection status
 */
uint16_t IAP_OPT_ReadWriteProtect(void)
{
    uint16_t ProtectedSECTOR = 0xFFFU;
    FLASH_OBProgramInitTypeDef OP_Struct = {0};

    DAL_FLASH_Unlock();

    /* Get option byte */
    DAL_FLASHEx_OBGetConfig(&OP_Struct);

    DAL_FLASH_Lock();

    return ProtectedSECTOR;
}

/**
 * @brief   Jump to user application
 *  
 * @param   application : IAP_APP1 or IAP_APP2
 *  
 * @retval  None
 */
void IAP_Jump2App(IAP_APP_T application)
{
    uint32_t address;

    /* Lock the Program memory */
    DAL_FLASH_Lock();
    
    if(application == IAP_APP1)
    {
        address = USER_APP1_ADDRESS;
    }
    else
    {
        address = USER_APP2_ADDRESS;
    }

    /* Jump to user application */
    JumpAddress = *(__IO uint32_t *) (address + 4U);
    Jump_To_Application = (pFunction) JumpAddress;

    /* Initialize user application's Stack Pointer */
    __set_MSP(*(__IO uint32_t *) address);

    /* Jump to application */
    Jump_To_Application();
}
