/*
 * Copyright (c) 2022, Shenzhen CVA Innovation CO.,LTD
 * All rights reserved.
 *
 * Shenzhen CVA Innovation CO.,LTD (CVA chip) is supplying this file for use
 * exclusively with CVA's microcontroller products. This file can be freely
 * distributed within development tools that are supporting such microcontroller
 * products.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * CVA SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 */

/*******************************************************************************
 * the includes
 ******************************************************************************/
#include <stddef.h>
#include "fls.h"
#include "mcu.h"
#include "bootloader_cfg.h"

#if BOOTLOADER_CFG_FLS_CODE_GENERATE_EN == 1u
#if(BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u)
#define PRAGMA_SECTION #pragma location = ".textrw"
#else
#define PRAGMA_SECTION #pragma location = ".ram_flash_driver"
#endif
#else
#define PRAGMA_SECTION #pragma location = ".ram_flash_driver"
#endif

#if(BOOTLOADER_CFG_FLS_CODE_GENERATE_EN != 0u)

/*******************************************************************************
 * the defines
 ******************************************************************************/

#if(BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u)
#pragma location = ".textrw"
#else
#pragma location = ".ram_flash_driver"
#endif
static void
    Fls_ClearCommandState(FtfcRegType *obj);

#if(BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u)
#pragma location = ".textrw"
#else
#pragma location = ".ram_flash_driver"
#endif
static Fls_StdRtnType
    Fls_CommandSequence(FtfcRegType *obj);

#if(BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u)
#pragma location = ".textrw"
#else
#pragma location = ".ram_flash_driver"
#endif
static Fls_StdRtnType
    Fls_CommandSequence(FtfcRegType *obj);

/*******************************************************************************
 * the typedefs
 ******************************************************************************/

/*******************************************************************************
 * the globals
 ******************************************************************************/

#if(BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 0u)
#pragma location = ".ram_flash_driver_map"
__root uint32_t fls_DrvMapTbl[] = {

        0, /* Store flash instance address */
        (uint32_t)(&Fls_Init),
        (uint32_t)(&Fls_Erase),
        (uint32_t)(&Fls_Write),
        (uint32_t)(&Fls_Read)};
#endif

/*******************************************************************************
 * the functions
 ******************************************************************************/
#if(BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u)
#pragma location = ".textrw"
#else
#pragma location = ".ram_flash_driver"
#endif
static void
    Fls_ClearCommandState(FtfcRegType *obj)
{
    FtfcReg_SetFstatRspErrF(obj, 1);
    FtfcReg_SetFstatProtViolF(obj, 1);
    FtfcReg_SetFstatAccErrF(obj, 1);
    FtfcReg_SetFstatRdColF(obj, 1);
    FtfcReg_SetFstatSecF(obj, 1);
    FtfcReg_SetFstatDedF(obj, 1);
}

#if(BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u)
#pragma location = ".textrw"
#else
#pragma location = ".ram_flash_driver"
#endif
static Fls_StdRtnType
    Fls_CommandSequence(FtfcRegType *obj)
{
    Fls_StdRtnType ret = FLS_STD_RETURN_OK;

    FtfcReg_SetFstatCcif(obj, 1);

    while(0U == FtfcReg_GetFstatCcif(obj))
    {
    }

    if(FtfcReg_GetFstatAccErrF(obj) || FtfcReg_GetFstatProtViolF(obj) || FtfcReg_GetFstatMgStat(obj))
    {
        ret = FLS_STD_RETURN_ERASE_NG;
    }

    return ret;
}

#if(BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u)
#pragma location = ".textrw"
#else
#pragma location = ".ram_flash_driver"
#endif
static Fls_StdRtnType
    Fls_SetFlexRam(FtfcRegType *obj, bool eeramEn)
{
    Fls_StdRtnType ret = FLS_STD_RETURN_OK;

    if(0U == FtfcReg_GetFstatCcif(obj))
    {
        ret = FLS_STD_RETURN_BUSY;
    }
    else
    {
        Fls_ClearCommandState(obj);
        FtfcReg_SetFccob(obj, 0, FLASHDRV_OPCMD_SETFRAM);
        FtfcReg_SetFccob(obj, 1, eeramEn ? 1 : 0);
        ret = Fls_CommandSequence(obj);

        if((eeramEn == false) && (FtfcReg_GetFstatRamRdy(obj) == 0))
        {
            ret = FLS_STD_RETURN_PGM_NG;
        }
    }

    return ret;
}

/*! \brief Initializes the Flash driver module
 *
 * This function initializes Flash driver by setting the register
 * instance to it.
 *
 * \param[in] flashDrvInstance : pointer to Flash driver instance
 * \return void
 */
#if(BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u)
#pragma location = ".textrw"
#else
#pragma location = ".ram_flash_driver"
#endif
void Fls_Init(uint32_t flashDrvInstance)
{
    FlashDrvType *flashDrvPtr = (FlashDrvType *)flashDrvInstance;
    /* Update the flash instance */
    *((uint32_t *)BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR) = (uint32_t)flashDrvPtr->reg;
    //fls_DrvMapTbl[0] = (uint32_t)flashDrvPtr->reg;

    flashDrvPtr->callback = NULL;
}

/*! \brief Erased the pflash
 *
 * This function erase the length located in the pflash
 *
 * \param[in] targetAddress : pointer to pFlash address
 * \param[in] sectorNum : how many sector to be erased
 * \return Fls_StdRtnType
 */
#if(BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u)
#pragma location = ".textrw"
#else
#pragma location = ".ram_flash_driver"
#endif
Fls_StdRtnType
    Fls_Erase(uint32_t targetAddress, uint32_t sectorNum)
{
    Fls_StdRtnType ret = FLS_STD_RETURN_OK;
    FtfcRegType   *flashRegPtr;

    flashRegPtr = (FtfcRegType*)((*(uint32_t*)BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR));
    //flashRegPtr = (FtfcRegType *)fls_DrvMapTbl[0];
    /* Check the current requested address is aligned with 1024byte or not */
    if((targetAddress & FLS_ERASE_SECTION_ADDR_ALIGNED_MASK) != 0u)
    {
        ret = FLS_STD_RETURN_ADDR_INVALID;
    }
    else
    {
        uint32_t i = 0;

        /* Check how many sections to be erased */
        for(i = 0; i < sectorNum; i++)
        {
            if(FtfcReg_GetFstatCcif(flashRegPtr) == 0u)
            {
                ret = FLS_STD_RETURN_BUSY;
                break;
            }
            else
            {
                Fls_ClearCommandState(flashRegPtr);

                FtfcReg_SetFccob(flashRegPtr, 0, FLASHDRV_OPCMD_SCTERS);
                FtfcReg_SetFccob(flashRegPtr, 1, FCCOBADDRLOW((targetAddress + (i << 10))));
                FtfcReg_SetFccob(flashRegPtr, 2, FCCOBADDRMDLOW((targetAddress + (i << 10))));
                FtfcReg_SetFccob(flashRegPtr, 3, FCCOBADDRMDHIG((targetAddress + (i << 10))));
                FtfcReg_SetFccob(flashRegPtr, 4, FCCOBADDRHIG((targetAddress + (i << 10))));
                FtfcReg_SetFccob(flashRegPtr, 5, FLASHDRV_SCTERS_RETRY);

                ret = Fls_CommandSequence(flashRegPtr);
            }

            if(ret != FLS_STD_RETURN_OK)
            {
                break;
            }
        }
    }

    return ret;
}

/*! \brief Write datas to pflash
 *
 * This function wrtie the length bytes to pflash
 *
 * \param[in] targetAddress : the target address to begin to write in pflash
 * \param[in] sourceAddressPtr : pointer to write data buffer
 * \param[in] bytesNum : how many bytes to be written to pflash
 * \return Fls_StdRtnType
 */
#if(BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u)
#pragma location = ".textrw"
#else
#pragma location = ".ram_flash_driver"
#endif
Fls_StdRtnType
    Fls_Write(uint32_t targetAddress, const uint8_t *sourceAddressPtr, uint32_t bytesNum)
{
    Fls_StdRtnType ret = FLS_STD_RETURN_OK;
    FtfcRegType   *flashRegPtr;

    flashRegPtr = (FtfcRegType*)((*(uint32_t*)BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR));
    //flashRegPtr = (FtfcRegType *)fls_DrvMapTbl[0];

    /* Check the current requested address is aligned with 8byte or not */
    if((targetAddress & FLS_PGM_SECTION_ADDR_ALIGNED_MASK) != 0u)
    {
        ret = FLS_STD_RETURN_ADDR_INVALID;
    }
    else if((bytesNum & FLS_PGM_SECTION_ADDR_ALIGNED_MASK) != 0u)
    {
        ret = FLS_STD_RETURN_LEN_INVALID;
    }
    else
    {
        bool     eeramrdy = false;
        uint32_t i = 0, offset = 0;
        uint8_t *pfastram;

        if(FtfcReg_GetFstatEeeRamRdy(flashRegPtr) == 1)
        {
            eeramrdy = true;
            Fls_SetFlexRam(flashRegPtr, false);
        }

        offset   = (targetAddress & (FLS_ERASE_SECTION_SIZE - 1u));
        pfastram = (uint8_t *)(FLS_FASTRAM_ADDR + offset);
        for(i = 0; i < bytesNum; i++)
        {
            *pfastram = sourceAddressPtr[i];
            pfastram++;
        }

        if(0U == FtfcReg_GetFstatCcif(flashRegPtr))
        {
            ret = FLS_STD_RETURN_BUSY;
        }
        else
        {
            Fls_ClearCommandState(flashRegPtr);

            FtfcReg_SetFccob(flashRegPtr, 0, FLASHDRV_OPCMD_SCTPGM);
            FtfcReg_SetFccob(flashRegPtr, 1, FCCOBADDRLOW(targetAddress));
            FtfcReg_SetFccob(flashRegPtr, 2, FCCOBADDRMDLOW(targetAddress));
            FtfcReg_SetFccob(flashRegPtr, 3, FCCOBADDRMDHIG(targetAddress));
            FtfcReg_SetFccob(flashRegPtr, 4, FCCOBADDRHIG(targetAddress));
            FtfcReg_SetFccob(flashRegPtr, 5, (uint8_t)((bytesNum / FLS_PGM_PHRASE_BYTE_SIZE) - 1));

            ret = Fls_CommandSequence(flashRegPtr);
        }

        if(eeramrdy)
        {
            Fls_SetFlexRam(flashRegPtr, true);
        }
    }

    return ret;
}

/*! \brief Read datas from pflash
 *
 * This function Read the length bytes from pflash
 *
 * \param[in] targetAddress : the target address to begin to read in pflash
 * \param[in] sourceAddressPtr : pointer to read data buffer
 * \param[in] bytesNum : how many bytes to be read from pflash
 * \return Fls_StdRtnType
 */

/*! \brief Read datas from pflash
 *
 * This function Read the length bytes from pflash
 *
 * \param[in] targetAddress : the target address to begin to read in pflash
 * \param[in] sourceAddressPtr : pointer to read data buffer
 * \param[in] bytesNum : how many bytes to be read from pflash
 * \return Fls_StdRtnType
 */
#if(BOOTLOADER_CFG_FLS_COPY_AUTO_EN == 1u)
#pragma location = ".textrw"
#else
#pragma location = ".ram_flash_driver"
#endif
Fls_StdRtnType
    Fls_Read(uint32_t targetAddress, uint8_t *dataBuff, uint32_t bytesNum)
{
    Fls_StdRtnType ret = FLS_STD_RETURN_OK;
    FtfcRegType   *flashRegPtr;
    uint32_t       i                = 0;
    uint32_t       checekTargetAddr = (targetAddress & ((uint32_t)(~FLS_PGM_SECTION_ADDR_ALIGNED_MASK)));

    flashRegPtr = (FtfcRegType*)((*(uint32_t*)BOOTLOADER_CFG_FLS_DRV_RAM_START_ADDR));
    //flashRegPtr = (FtfcRegType *)fls_DrvMapTbl[0];

    /* Check the current requested address is aligned with 8byte or not */
    if((targetAddress & FLS_PGM_SECTION_ADDR_ALIGNED_MASK) != 0u)
    {
        ret = FLS_STD_RETURN_ADDR_INVALID;
    }
    else if((bytesNum & FLS_PGM_SECTION_ADDR_ALIGNED_MASK) != 0u)
    {
        ret = FLS_STD_RETURN_LEN_INVALID;
    }
    else
    {
        if(0U == FtfcReg_GetFstatCcif(flashRegPtr))
        {
            ret = FLS_STD_RETURN_BUSY;
        }
        else
        {
            Fls_ClearCommandState(flashRegPtr);

            FtfcReg_SetFccob(flashRegPtr, 0, FLASHDRV_OPCMD_PRSCHK);
            FtfcReg_SetFccob(flashRegPtr, 1, FCCOBADDRLOW(targetAddress));
            FtfcReg_SetFccob(flashRegPtr, 2, FCCOBADDRMDLOW(targetAddress));
            FtfcReg_SetFccob(flashRegPtr, 3, FCCOBADDRMDHIG(targetAddress));
            FtfcReg_SetFccob(flashRegPtr, 4, FCCOBADDRHIG(targetAddress));
            FtfcReg_SetFccob(flashRegPtr, 5, (uint8_t)((bytesNum / FLS_PGM_PHRASE_BYTE_SIZE) - 1));

            for(i = 0; i < FLS_PGM_PHRASE_BYTE_SIZE; i++)
            {
                FtfcReg_SetFccob(flashRegPtr, 5 + i, 0xFF);
            }

            Fls_CommandSequence(flashRegPtr);

            ret = ((FtfcReg_GetFstatSecF(flashRegPtr) == 1) || (FtfcReg_GetFstatDedF(flashRegPtr) == 1)) ? FLS_STD_RETURN_CHECK_PHASE_NG : FLS_STD_RETURN_OK;

            if(ret == FLS_STD_RETURN_OK)
            {
                for(i = 0; i < bytesNum; i++)
                {
                    dataBuff[i] = *((uint8_t *)(targetAddress + i));
                }
            }
        }
    }

    return ret;
}

#else

static void
    Fls_ClearCommandState(FtfcRegType* obj)
{
    FtfcReg_SetFstatRspErrF(obj, 1);
    FtfcReg_SetFstatProtViolF(obj, 1);
    FtfcReg_SetFstatAccErrF(obj, 1);
    FtfcReg_SetFstatRdColF(obj, 1);
    FtfcReg_SetFstatSecF(obj, 1);
    FtfcReg_SetFstatDedF(obj, 1);
}

static Fls_StdRtnType
    Fls_CommandSequence(FtfcRegType* obj)
{
    Fls_StdRtnType ret = FLS_STD_RETURN_OK;

    FtfcReg_SetFstatCcif(obj, 1);

    while(0U == FtfcReg_GetFstatCcif(obj))
    {
    }

    if(FtfcReg_GetFstatAccErrF(obj) || FtfcReg_GetFstatProtViolF(obj) || FtfcReg_GetFstatMgStat(obj))
    {
        ret = FLS_STD_RETURN_ERASE_NG;
    }

    return ret;
}

/*! \brief Read datas from pflash
 *
 * This function Read the length bytes from pflash
 *
 * \param[in] flashDrvInstance : the address of flash driver instance
 * \param[in] targetAddress : the target address to begin to read in pflash
 * \param[in] sourceAddressPtr : pointer to read data buffer
 * \param[in] bytesNum : how many bytes to be read from pflash
 * \return Fls_StdRtnType
 */
Fls_StdRtnType
    Fls_Read(uint32_t flashDrvInstance, uint32_t targetAddress, uint8_t* dataBuff, uint32_t bytesNum)
{
    Fls_StdRtnType ret = FLS_STD_RETURN_OK;
    FtfcRegType*   flashRegPtr;
    uint32_t       i                = 0;
    uint32_t       checekTargetAddr = (targetAddress & ((uint32_t)(~FLS_PGM_SECTION_ADDR_ALIGNED_MASK)));

    flashRegPtr = (FtfcRegType*)((*(uint32_t*)flashDrvInstance));

    /* Check the current requested address is aligned with 8byte or not */
    if((targetAddress & FLS_PGM_SECTION_ADDR_ALIGNED_MASK) != 0u)
    {
        ret = FLS_STD_RETURN_ADDR_INVALID;
    }
    else if((bytesNum & FLS_PGM_SECTION_ADDR_ALIGNED_MASK) != 0u)
    {
        ret = FLS_STD_RETURN_LEN_INVALID;
    }
    else
    {
        if(0U == FtfcReg_GetFstatCcif(flashRegPtr))
        {
            ret = FLS_STD_RETURN_BUSY;
        }
        else
        {
            Fls_ClearCommandState(flashRegPtr);

            FtfcReg_SetFccob(flashRegPtr, 0, FLASHDRV_OPCMD_PRSCHK);
            FtfcReg_SetFccob(flashRegPtr, 1, FCCOBADDRLOW(targetAddress));
            FtfcReg_SetFccob(flashRegPtr, 2, FCCOBADDRMDLOW(targetAddress));
            FtfcReg_SetFccob(flashRegPtr, 3, FCCOBADDRMDHIG(targetAddress));
            FtfcReg_SetFccob(flashRegPtr, 4, FCCOBADDRHIG(targetAddress));
            FtfcReg_SetFccob(flashRegPtr, 5, (uint8_t)((bytesNum / FLS_PGM_PHRASE_BYTE_SIZE) - 1));

            for(i = 0; i < FLS_PGM_PHRASE_BYTE_SIZE; i++)
            {
                FtfcReg_SetFccob(flashRegPtr, 5 + i, 0xFF);
            }

            Fls_CommandSequence(flashRegPtr);

            ret = ((FtfcReg_GetFstatSecF(flashRegPtr) == 1) || (FtfcReg_GetFstatDedF(flashRegPtr) == 1)) ? FLS_STD_RETURN_CHECK_PHASE_NG : FLS_STD_RETURN_OK;

            if(ret == FLS_STD_RETURN_OK)
            {
                for(i = 0; i < bytesNum; i++)
                {
                    dataBuff[i] = *((uint8_t*)(targetAddress + i));
                }
            }
        }
    }

    return ret;
}

#endif
