/*
 * 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 "scg0_reg.h"
#include "ftfc_reg.h"
#include "flash_drv.h"
#include "../CVM012x_features.h"

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

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

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

/*******************************************************************************
 * the functions
 ******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

static void            FlashDrv_ClearCommandState(FlashDrvType *obj);
static FlashDrv_Status FlashDrv_CommandSequence(FlashDrvType *obj);
static void            FlashDrv_EeeUpdateSequence(FlashDrvType *obj);

/*******************************************************************************
 * Code
 ******************************************************************************/

void FlashDrv_Init(FlashDrvType *obj, struct _FtfcRegType_ *reg, struct _Scg0RegType_ *scg0Reg)
{
    obj->reg        = reg;
    obj->scg0Reg    = scg0Reg;
    obj->pflashbase = 0x0;
    obj->pflashsize = FEATURE_PFLASH_SIZE;
    obj->dflashbase = 0x10000000;
    obj->dflashsize = FEATURE_DFLASH_SIZE;
    obj->eeevld     = FlashDrv_GetEeeEn(obj);
    obj->callback   = NULL;
    if(obj->eeevld)
    {
        obj->dflashsize = 0x0;
    }
}

void FlashDrv_GetDefaultConfig(FlashDrv_ConfigType *userconfig)
{
    userconfig->pflashbase = 0x0;
    userconfig->pflashsize = FEATURE_PFLASH_SIZE;
    userconfig->dflashbase = 0x10000000;
    userconfig->dflashsize = FEATURE_DFLASH_SIZE;
    userconfig->eeevld     = false;
    userconfig->callback   = NULL;
}

void FlashDrv_Configure(FlashDrvType              *obj,
                        const FlashDrv_ConfigType *userconfig)
{
    obj->pflashbase = 0x0;
    obj->pflashsize = FEATURE_PFLASH_SIZE;
    obj->dflashbase = 0x10000000;
    obj->dflashsize = FEATURE_DFLASH_SIZE;
    /* DFlash size only has one fixed configuration */
    obj->eeevld   = FlashDrv_GetEeeEn(obj);
    obj->callback = userconfig->callback;
    if(obj->eeevld)
    {
        obj->dflashsize = 0x0;
    }
}

void FlashDrv_GetConfig(const FlashDrvType  *obj,
                        FlashDrv_ConfigType *userconfig)
{
    userconfig->pflashbase = obj->pflashbase;
    userconfig->pflashsize = obj->pflashsize;
    userconfig->dflashbase = obj->dflashbase;
    userconfig->dflashsize = obj->dflashsize;
    userconfig->eeevld     = obj->eeevld;
    userconfig->callback   = obj->callback;
}

static void FlashDrv_ClearCommandState(FlashDrvType *obj)
{
    FtfcReg_SetFstatRspErrF(obj->reg, 1);
    FtfcReg_SetFstatProtViolF(obj->reg, 1);
    FtfcReg_SetFstatAccErrF(obj->reg, 1);
    FtfcReg_SetFstatRdColF(obj->reg, 1);
    FtfcReg_SetFstatSecF(obj->reg, 1);
    FtfcReg_SetFstatDedF(obj->reg, 1);
}

static FlashDrv_Status FlashDrv_CommandSequence(FlashDrvType *obj)
{
    FlashDrv_Status ret = FLASHDRV_STATUS_SUCCESS;

    FtfcReg_SetFstatCcif(obj->reg, 1);

    while(0U == FtfcReg_GetFstatCcif(obj->reg))
    {
    }

    if(FtfcReg_GetFstatAccErrF(obj->reg) || FtfcReg_GetFstatProtViolF(obj->reg) || FtfcReg_GetFstatMgStat(obj->reg))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    return ret;
}

static void FlashDrv_EeeUpdateSequence(FlashDrvType *obj)
{
    while(0 == FtfcReg_GetFstatEeeRamRdy(obj->reg))
    {
    }

    if(FtfcReg_GetFstatEeeTlov(obj->reg) == 1)
    {
        FlashDrv_SetFlexRam(obj, false);
        FlashDrv_SetEeeAutoGcEn(obj, true);
        FlashDrv_SetFlexRam(obj, true);
        FlashDrv_SetEeeAutoGcEn(obj, false);
    }
}

FlashDrv_Status FlashDrv_Read1sBlock(FlashDrvType *obj, uint32_t *destaddr)
{
    FlashDrv_Status ret  = FLASHDRV_STATUS_SUCCESS;
    uint32_t        dest = (uint32_t)destaddr;

    dest = ALIGNED(dest, 8);

    if((dest >= (obj->pflashbase + obj->pflashsize) && dest < obj->dflashbase) || (obj->dflashsize > 0 && dest >= (obj->dflashbase + obj->dflashsize)) || (dest >= obj->dflashbase && obj->eeevld))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(ret == FLASHDRV_STATUS_SUCCESS)
    {
        if(0U == FtfcReg_GetFstatCcif(obj->reg))
        {
            ret = FLASHDRV_STATUS_BUSY;
        }
        else
        {
            FlashDrv_ClearCommandState(obj);
            FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_BLKRD1S);
            FtfcReg_SetFccob(obj->reg, 1, FCCOBADDRLOW(dest));
            FtfcReg_SetFccob(obj->reg, 2, FCCOBADDRMDLOW(dest));
            FtfcReg_SetFccob(obj->reg, 3, FCCOBADDRMDHIG(dest));
            FtfcReg_SetFccob(obj->reg, 4, FCCOBADDRHIG(dest));

            ret = FlashDrv_CommandSequence(obj);
        }
    }

    return ret;
}

FlashDrv_Status FlashDrv_Read1sSection(FlashDrvType *obj, uint32_t *destaddr, uint32_t rdsize)
{
    FlashDrv_Status ret  = FLASHDRV_STATUS_SUCCESS;
    uint32_t        dest = (uint32_t)destaddr;

    if(!IS_ALIGNED(dest, 8) || !IS_ALIGNED(rdsize, 8))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }
    if(ALIGNED(dest + rdsize - 1, FLASHDRV_SECTOR_SIZE) > ALIGNED(dest, FLASHDRV_SECTOR_SIZE))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if((dest >= (obj->pflashbase + obj->pflashsize) && dest < obj->dflashbase) || (obj->dflashsize > 0 && dest >= (obj->dflashbase + obj->dflashsize)) || (dest >= obj->dflashbase && obj->eeevld))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(ret == FLASHDRV_STATUS_SUCCESS)
    {
        if(0U == FtfcReg_GetFstatCcif(obj->reg))
        {
            ret = FLASHDRV_STATUS_BUSY;
        }
        else
        {
            FlashDrv_ClearCommandState(obj);
            FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_SCTRD1S);
            FtfcReg_SetFccob(obj->reg, 1, FCCOBADDRLOW(dest));
            FtfcReg_SetFccob(obj->reg, 2, FCCOBADDRMDLOW(dest));
            FtfcReg_SetFccob(obj->reg, 3, FCCOBADDRMDHIG(dest));
            FtfcReg_SetFccob(obj->reg, 4, FCCOBADDRHIG(dest));
            FtfcReg_SetFccob(obj->reg, 5, (rdsize / FLASHDRV_PHRASE_SIZE) - 1);

            ret = FlashDrv_CommandSequence(obj);
        }
    }

    return ret;
}

FlashDrv_Status FlashDrv_CheckPhrase(FlashDrvType *obj, uint32_t *destaddr, uint8_t *expdata)
{
    FlashDrv_Status ret  = FLASHDRV_STATUS_SUCCESS;
    uint32_t        dest = (uint32_t)destaddr;
    uint32_t        i    = 0;

    if(!IS_ALIGNED(dest, 8))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if((dest >= (obj->pflashbase + obj->pflashsize) && dest < obj->dflashbase) || (obj->dflashsize > 0 && dest >= (obj->dflashbase + obj->dflashsize)) || (dest >= obj->dflashbase && obj->eeevld))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(expdata == NULL)
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(ret == FLASHDRV_STATUS_SUCCESS)
    {
        if(0U == FtfcReg_GetFstatCcif(obj->reg))
        {
            ret = FLASHDRV_STATUS_BUSY;
        }
        else
        {
            FlashDrv_ClearCommandState(obj);
            FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_PRSCHK);
            FtfcReg_SetFccob(obj->reg, 1, FCCOBADDRLOW(dest));
            FtfcReg_SetFccob(obj->reg, 2, FCCOBADDRMDLOW(dest));
            FtfcReg_SetFccob(obj->reg, 3, FCCOBADDRMDHIG(dest));
            FtfcReg_SetFccob(obj->reg, 4, FCCOBADDRHIG(dest));

            for(i = 0; i < FLASHDRV_PHRASE_SIZE; i++)
            {
                FtfcReg_SetFccob(obj->reg, 5 + i, expdata[i]);
            }
            ret = FlashDrv_CommandSequence(obj);
            ret |= (FtfcReg_GetFstatDedF(obj->reg) == 1) ? FLASHDRV_STATUS_ERROR : FLASHDRV_STATUS_SUCCESS;
        }
    }
    return ret;
}

FlashDrv_Status FlashDrv_CheckEccState(FlashDrvType *obj, uint32_t *destaddr)
{
    FlashDrv_Status ret  = FLASHDRV_STATUS_SUCCESS;
    uint32_t        dest = (uint32_t)destaddr;
    uint32_t        i    = 0;

    if(!IS_ALIGNED(dest, 8))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if((dest >= (obj->pflashbase + obj->pflashsize) && dest < obj->dflashbase) || (obj->dflashsize > 0 && dest >= (obj->dflashbase + obj->dflashsize)) || (dest >= obj->dflashbase && obj->eeevld))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(ret == FLASHDRV_STATUS_SUCCESS)
    {
        if(0U == FtfcReg_GetFstatCcif(obj->reg))
        {
            ret = FLASHDRV_STATUS_BUSY;
        }
        else
        {
            FlashDrv_ClearCommandState(obj);
            FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_PRSCHK);
            FtfcReg_SetFccob(obj->reg, 1, FCCOBADDRLOW(dest));
            FtfcReg_SetFccob(obj->reg, 2, FCCOBADDRMDLOW(dest));
            FtfcReg_SetFccob(obj->reg, 3, FCCOBADDRMDHIG(dest));
            FtfcReg_SetFccob(obj->reg, 4, FCCOBADDRHIG(dest));

            for(i = 0; i < FLASHDRV_PHRASE_SIZE; i++)
            {
                FtfcReg_SetFccob(obj->reg, 5 + i, 0xFF);
            }
            FlashDrv_CommandSequence(obj);
            ret = ((FtfcReg_GetFstatSecF(obj->reg) | FtfcReg_GetFstatDedF(obj->reg)) == 1) ? FLASHDRV_STATUS_ERROR : FLASHDRV_STATUS_SUCCESS;
        }
    }
    return ret;
}

FlashDrv_Status FlashDrv_ProgramPhrase(FlashDrvType *obj, uint32_t *destaddr, uint8_t *expdata, bool hweccen)
{
    FlashDrv_Status ret  = FLASHDRV_STATUS_SUCCESS;
    uint32_t        dest = (uint32_t)destaddr;
    uint32_t        i    = 0;

    if(!IS_ALIGNED(dest, 8))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }
    if((dest >= (obj->pflashbase + obj->pflashsize) && dest < obj->dflashbase) || (obj->dflashsize > 0 && dest >= (obj->dflashbase + obj->dflashsize)) || (dest >= obj->dflashbase && obj->eeevld))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(expdata == NULL)
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(ret == FLASHDRV_STATUS_SUCCESS)
    {
        if(0U == FtfcReg_GetFstatCcif(obj->reg))
        {
            ret = FLASHDRV_STATUS_BUSY;
        }
        else
        {
            FlashDrv_ClearCommandState(obj);
            FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_PRSPGM);
            FtfcReg_SetFccob(obj->reg, 1, FCCOBADDRLOW(dest));
            FtfcReg_SetFccob(obj->reg, 2, FCCOBADDRMDLOW(dest));
            FtfcReg_SetFccob(obj->reg, 3, FCCOBADDRMDHIG(dest));
            FtfcReg_SetFccob(obj->reg, 4, FCCOBADDRHIG(dest));
            FtfcReg_SetFccob(obj->reg, 5, (hweccen ? 1 : 0));
            for(i = 0; i < FLASHDRV_PHRASE_SIZE; i++)
            {
                FtfcReg_SetFccob(obj->reg, 6 + i, expdata[i]);
            }
            if(!hweccen)
            {
                FtfcReg_SetFccob(obj->reg, 14, expdata[8]);
            }

            ret = FlashDrv_CommandSequence(obj);
        }
    }
    return ret;
}

FlashDrv_Status FlashDrv_EraseBlock(FlashDrvType *obj, uint32_t *destaddr, FlashDrv_SectorErase_t erasetype)
{
    FlashDrv_Status ret  = FLASHDRV_STATUS_SUCCESS;
    uint32_t        dest = (uint32_t)destaddr;

    dest = ALIGNED(dest, 8);

    if((dest >= (obj->pflashbase + obj->pflashsize) && dest < obj->dflashbase) || (obj->dflashsize > 0 && dest >= (obj->dflashbase + obj->dflashsize)) || (dest >= obj->dflashbase && obj->eeevld))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(ret == FLASHDRV_STATUS_SUCCESS)
    {
        if(0U == FtfcReg_GetFstatCcif(obj->reg))
        {
            ret = FLASHDRV_STATUS_BUSY;
        }
        else
        {
            FlashDrv_ClearCommandState(obj);
            FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_BLKERS);
            FtfcReg_SetFccob(obj->reg, 1, FCCOBADDRLOW(dest));
            FtfcReg_SetFccob(obj->reg, 2, FCCOBADDRMDLOW(dest));
            FtfcReg_SetFccob(obj->reg, 3, FCCOBADDRMDHIG(dest));
            FtfcReg_SetFccob(obj->reg, 4, FCCOBADDRHIG(dest));
            FtfcReg_SetFccob(obj->reg, 5, erasetype);

            ret = FlashDrv_CommandSequence(obj);
        }
    }

    return ret;
}

FlashDrv_Status FlashDrv_EraseSector(FlashDrvType *obj, uint32_t *destaddr, FlashDrv_SectorErase_t erasetype)
{
    FlashDrv_Status ret  = FLASHDRV_STATUS_SUCCESS;
    uint32_t        dest = (uint32_t)destaddr;

    dest = ALIGNED(dest, 8);

    if((dest >= (obj->pflashbase + obj->pflashsize) && dest < obj->dflashbase) || (obj->dflashsize > 0 && dest >= (obj->dflashbase + obj->dflashsize)) || (dest >= obj->dflashbase && obj->eeevld))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(ret == FLASHDRV_STATUS_SUCCESS)
    {
        if(0U == FtfcReg_GetFstatCcif(obj->reg))
        {
            ret = FLASHDRV_STATUS_BUSY;
        }
        else
        {
            FlashDrv_ClearCommandState(obj);
            FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_SCTERS);
            FtfcReg_SetFccob(obj->reg, 1, FCCOBADDRLOW(dest));
            FtfcReg_SetFccob(obj->reg, 2, FCCOBADDRMDLOW(dest));
            FtfcReg_SetFccob(obj->reg, 3, FCCOBADDRMDHIG(dest));
            FtfcReg_SetFccob(obj->reg, 4, FCCOBADDRHIG(dest));
            FtfcReg_SetFccob(obj->reg, 5, erasetype);
            ret = FlashDrv_CommandSequence(obj);
        }
    }

    return ret;
}

FlashDrv_Status FlashDrv_ProgramSection(FlashDrvType *obj, uint32_t *destaddr, uint32_t len, uint8_t *databuf)
{
    FlashDrv_Status ret  = FLASHDRV_STATUS_SUCCESS;
    uint32_t        dest = (uint32_t)destaddr;
    uint32_t        i = 0, offset = 0;
    bool            eeeramrdy = false;
    uint8_t        *pfastram, *pdata;

    if(!IS_ALIGNED(dest, 512) || !IS_ALIGNED(len, 512))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }
    if(ALIGNED(dest + len - 1, FLASHDRV_SECTOR_SIZE) > ALIGNED(dest, FLASHDRV_SECTOR_SIZE))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if((dest >= (obj->pflashbase + obj->pflashsize) && dest < obj->dflashbase) || (obj->dflashsize > 0 && dest >= (obj->dflashbase + obj->dflashsize)) || (dest >= obj->dflashbase && obj->eeevld))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    eeeramrdy = (FtfcReg_GetFstatEeeRamRdy(obj->reg) == 1);

    if(eeeramrdy)
    {
        FlashDrv_SetFlexRam(obj, false);
    }

    offset   = dest & (FLASHDRV_SECTOR_SIZE - 1);
    pfastram = (uint8_t *)(FLASHDRV_FLEXRAM_ADDR + offset);
    pdata    = databuf;
    for(i = 0; i < len; i++)
    {
        *pfastram = *pdata;
        pfastram++;
        pdata++;
    }

    if(ret == FLASHDRV_STATUS_SUCCESS)
    {
        if(0U == FtfcReg_GetFstatCcif(obj->reg))
        {
            ret = FLASHDRV_STATUS_BUSY;
        }
        else
        {
            FlashDrv_ClearCommandState(obj);
            FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_SCTPGM);
            FtfcReg_SetFccob(obj->reg, 1, FCCOBADDRLOW(dest));
            FtfcReg_SetFccob(obj->reg, 2, FCCOBADDRMDLOW(dest));
            FtfcReg_SetFccob(obj->reg, 3, FCCOBADDRMDHIG(dest));
            FtfcReg_SetFccob(obj->reg, 4, FCCOBADDRHIG(dest));
            FtfcReg_SetFccob(obj->reg, 5, (len / FLASHDRV_PHRASE_SIZE) - 1);
            ret = FlashDrv_CommandSequence(obj);
        }
    }

    if(eeeramrdy)
    {
        FlashDrv_SetFlexRam(obj, true);
    }
    return ret;
}

FlashDrv_Status FlashDrv_ProgramSectionFast(FlashDrvType *obj, uint32_t *destaddr, uint32_t len)
{
    FlashDrv_Status ret  = FLASHDRV_STATUS_SUCCESS;
    uint32_t        dest = (uint32_t)destaddr;

    if(!IS_ALIGNED(dest, 512) || !IS_ALIGNED(len, 512))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }
    if(ALIGNED(dest + len - 1, FLASHDRV_SECTOR_SIZE) > ALIGNED(dest, FLASHDRV_SECTOR_SIZE))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if((dest >= (obj->pflashbase + obj->pflashsize) && dest < obj->dflashbase) || (obj->dflashsize > 0 && dest >= (obj->dflashbase + obj->dflashsize)) || (dest >= obj->dflashbase && obj->eeevld))
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(ret == FLASHDRV_STATUS_SUCCESS)
    {
        if(0U == FtfcReg_GetFstatCcif(obj->reg))
        {
            ret = FLASHDRV_STATUS_BUSY;
        }
        else
        {
            FlashDrv_ClearCommandState(obj);
            FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_SCTPGM);
            FtfcReg_SetFccob(obj->reg, 1, FCCOBADDRLOW(dest));
            FtfcReg_SetFccob(obj->reg, 2, FCCOBADDRMDLOW(dest));
            FtfcReg_SetFccob(obj->reg, 3, FCCOBADDRMDHIG(dest));
            FtfcReg_SetFccob(obj->reg, 4, FCCOBADDRHIG(dest));
            FtfcReg_SetFccob(obj->reg, 5, (len / FLASHDRV_PHRASE_SIZE) - 1);
            ret = FlashDrv_CommandSequence(obj);
        }
    }

    return ret;
}

FlashDrv_Status FlashDrv_Read1sAllBlock(FlashDrvType *obj)
{
    FlashDrv_Status ret = FLASHDRV_STATUS_SUCCESS;
    if(0U == FtfcReg_GetFstatCcif(obj->reg))
    {
        ret = FLASHDRV_STATUS_BUSY;
    }
    else
    {
        FlashDrv_ClearCommandState(obj);
        FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_ALBLKRD1S);
        ret = FlashDrv_CommandSequence(obj);
    }
    return ret;
}

FlashDrv_Status FlashDrv_Read1sInfoRegion(FlashDrvType         *obj,
                                          FlashDrv_InfoRegion_t regionid,
                                          uint8_t               phraseid,
                                          uint8_t               phrasecnt)
{
    FlashDrv_Status ret = FLASHDRV_STATUS_SUCCESS;

    if(0U == FtfcReg_GetFstatCcif(obj->reg))
    {
        ret = FLASHDRV_STATUS_BUSY;
    }
    else
    {
        FlashDrv_ClearCommandState(obj);
        FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_INRGRD1S);
        FtfcReg_SetFccob(obj->reg, 1, (uint8_t)(regionid + 2));
        FtfcReg_SetFccob(obj->reg, 2, (uint8_t)phraseid);
        FtfcReg_SetFccob(obj->reg, 3, (uint8_t)(phrasecnt - 1));

        ret = FlashDrv_CommandSequence(obj);
    }

    return ret;
}

FlashDrv_Status FlashDrv_ReadInfoRegion(FlashDrvType         *obj,
                                        FlashDrv_InfoRegion_t regionid,
                                        uint8_t               phraseid,
                                        uint8_t              *databuf)
{
    FlashDrv_Status ret = FLASHDRV_STATUS_SUCCESS;
    uint32_t        i   = 0;

    if(phraseid > 127)
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(databuf == NULL)
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(ret == FLASHDRV_STATUS_SUCCESS)
    {
        if(0U == FtfcReg_GetFstatCcif(obj->reg))
        {
            ret = FLASHDRV_STATUS_BUSY;
        }
        else
        {
            FlashDrv_ClearCommandState(obj);
            FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_RDINFORG);
            FtfcReg_SetFccob(obj->reg, 1, phraseid);
            FtfcReg_SetFccob(obj->reg, 2, (uint8_t)regionid);

            ret = FlashDrv_CommandSequence(obj);
        }
    }
    if(ret == FLASHDRV_STATUS_SUCCESS)
    {
        for(i = 0; i < FLASHDRV_PHRASE_SIZE; i++)
        {
            databuf[i] = FtfcReg_GetFccob(obj->reg, 7 + i);
        }
    }
    return ret;
}

FlashDrv_Status FlashDrv_ProgramInfoRegion(FlashDrvType         *obj,
                                           FlashDrv_InfoRegion_t regionid,
                                           uint32_t              phraseid,
                                           uint8_t              *databuf)
{
    FlashDrv_Status ret = FLASHDRV_STATUS_SUCCESS;
    uint32_t        i   = 0;

    if(phraseid > 127)
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(databuf == NULL)
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(ret == FLASHDRV_STATUS_SUCCESS)
    {
        if(0U == FtfcReg_GetFstatCcif(obj->reg))
        {
            ret = FLASHDRV_STATUS_BUSY;
        }
        else
        {
            FlashDrv_ClearCommandState(obj);
            FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_PGMINFORG);
            FtfcReg_SetFccob(obj->reg, 1, (uint8_t)phraseid);
            FtfcReg_SetFccob(obj->reg, 2, (uint8_t)regionid);

            for(i = 0; i < FLASHDRV_PHRASE_SIZE; i++)
            {
                FtfcReg_SetFccob(obj->reg, (uint8_t)(4 + i), databuf[i]);
            }

            ret = FlashDrv_CommandSequence(obj);
        }
    }

    return ret;
}

FlashDrv_Status FlashDrv_EraseAllBlock(FlashDrvType *obj)
{
    FlashDrv_Status ret = FLASHDRV_STATUS_SUCCESS;
    if(0U == FtfcReg_GetFstatCcif(obj->reg))
    {
        ret = FLASHDRV_STATUS_BUSY;
    }
    else
    {
        FlashDrv_ClearCommandState(obj);
        FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_ALBLKERS);
        ret = FlashDrv_CommandSequence(obj);
    }
    return ret;
}

FlashDrv_Status FlashDrv_EraseAllBlockUnsecure(FlashDrvType *obj)
{
    FlashDrv_Status ret = FLASHDRV_STATUS_SUCCESS;
    if(0U == FtfcReg_GetFstatCcif(obj->reg))
    {
        ret = FLASHDRV_STATUS_BUSY;
    }
    else
    {
        FlashDrv_ClearCommandState(obj);
        FtfcReg_SetFccob(obj->reg, 0, (uint8_t)FLASHDRV_OPCMD_ALBLKUERS);
        ret = FlashDrv_CommandSequence(obj);
    }
    return ret;
}

FlashDrv_Status FlashDrv_VerifyUserKey(FlashDrvType *obj, uint8_t *expdata)
{
    FlashDrv_Status ret = FLASHDRV_STATUS_SUCCESS;
    uint32_t        i   = 0;

    if(expdata == NULL)
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(ret == FLASHDRV_STATUS_SUCCESS)
    {
        if(0U == FtfcReg_GetFstatCcif(obj->reg))
        {
            ret = FLASHDRV_STATUS_BUSY;
        }
        else
        {
            FlashDrv_ClearCommandState(obj);
            FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_VRFYKEY);

            for(i = 0; i < FLASHDRV_PHRASE_SIZE; i++)
            {
                FtfcReg_SetFccob(obj->reg, 1 + i, expdata[i]);
            }
            ret = FlashDrv_CommandSequence(obj);
        }
    }
    return ret;
}

void FlashDrv_IrqHandler(FlashDrvType *obj)
{
    FtfcReg_SetFstatCcifClr(obj->reg, 1);
    if(obj->callback != NULL)
    {
        obj->callback();
    }
}

void FlashDrv_SetCmdDoneIrqEn(FlashDrvType *obj, bool en)
{
    FtfcReg_SetFcnfgCcifIE(obj->reg, en ? 1 : 0);
}

FlashDrv_Status FlashDrv_SetFlexRam(FlashDrvType *obj, bool eeeramen)
{
    FlashDrv_Status ret   = FLASHDRV_STATUS_SUCCESS;
    bool            gctrg = false;

    if(FtfcReg_GetFstatEeeRamRdy(obj->reg) == 0 && eeeramen == true && Scg0Reg_GetTrimAutoGcEn(obj->scg0Reg) == 1)
    {
        gctrg = true;
    }

    if(0U == FtfcReg_GetFstatCcif(obj->reg))
    {
        ret = FLASHDRV_STATUS_BUSY;
    }
    else
    {
        FlashDrv_ClearCommandState(obj);
        FtfcReg_SetFccob(obj->reg, 0, FLASHDRV_OPCMD_SETFRAM);
        FtfcReg_SetFccob(obj->reg, 1, eeeramen ? 1 : 0);
        ret = FlashDrv_CommandSequence(obj);

        if(eeeramen && !FtfcReg_GetFstatEeeRamRdy(obj->reg))
        {
            ret |= FLASHDRV_STATUS_ERROR;
        }

        if(!eeeramen && !FtfcReg_GetFstatRamRdy(obj->reg))
        {
            ret |= FLASHDRV_STATUS_ERROR;
        }

        if(ret == FLASHDRV_STATUS_SUCCESS && gctrg == true)
        {
            FtfcReg_SetFstatEeeTlov(obj->reg, 1);
        }
    }

    return ret;
}

FlashDrv_Status FlashDrv_UpdateEeeRam(FlashDrvType *obj, uint32_t offset, uint8_t *srcdata, uint32_t sz)
{
    FlashDrv_Status ret = FLASHDRV_STATUS_SUCCESS;

    uint8_t  *pdsteram8  = 0;
    uint16_t *pdsteram16 = 0;
    uint32_t *pdsteram32 = 0;
    uint8_t  *psrc8      = 0;
    uint16_t *psrc16     = 0;
    uint32_t *psrc32     = 0;
    uint32_t  i = 0, cnt = 0;

    if(!(obj->eeevld) || FtfcReg_GetFstatRamRdy(obj->reg) == 1 || FtfcReg_GetEfProtecion(obj->reg) != 0x0)
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(offset + sz > FLASHDRV_FLEXRAM_SIZE)
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    if(ret != FLASHDRV_STATUS_SUCCESS)
    {
        return ret;
    }

    while(0 == FtfcReg_GetFstatEeeRamRdy(obj->reg))
    {
    }

    pdsteram8 = (uint8_t *)(FLASHDRV_FLEXRAM_ADDR + offset);
    if(IS_ALIGNED(offset, (uint32_t)4) && IS_ALIGNED(sz, (uint32_t)4))
    {
        pdsteram32 = (uint32_t *)pdsteram8;
        psrc32     = (uint32_t *)srcdata;
        cnt        = sz / 4;
        for(i = 0; i < cnt; i++)
        {
            pdsteram32[i] = psrc32[i];
            FlashDrv_EeeUpdateSequence(obj);
        }
    }
    else if(IS_ALIGNED(offset, (uint32_t)2) && IS_ALIGNED(sz, (uint32_t)2))
    {
        pdsteram16 = (uint16_t *)pdsteram8;
        psrc16     = (uint16_t *)srcdata;
        cnt        = sz / 2;
        for(i = 0; i < cnt; i++)
        {
            pdsteram16[i] = psrc16[i];
            FlashDrv_EeeUpdateSequence(obj);
        }
    }
    else
    {
        psrc8 = (uint8_t *)srcdata;
        cnt   = sz;
        for(i = 0; i < cnt; i++)
        {
            pdsteram8[i] = psrc8[i];
            FlashDrv_EeeUpdateSequence(obj);
        }
    }

    if(FtfcReg_GetFstatEeeFlov(obj->reg) == 1)
    {
        ret = FLASHDRV_STATUS_ERROR;
    }

    return ret;
}

bool FlashDrv_GetEeeAutoGcEn(FlashDrvType *obj)
{
    bool eeegcen = false;

    eeegcen = (Scg0Reg_GetTrimAutoGcEn(obj->scg0Reg) == 1);

    return eeegcen;
}

void FlashDrv_SetEeeAutoGcEn(FlashDrvType *obj, bool eeegcen)
{
    Scg0Reg_SetKey(obj->scg0Reg, 0x19962020);
    Scg0Reg_SetTrimAutoGcEn(obj->scg0Reg, eeegcen ? 1 : 0);
    Scg0Reg_SetKey(obj->scg0Reg, 0);
}

bool FlashDrv_GetEeeEn(FlashDrvType *obj)
{
    bool eeeen = false;

    eeeen = (FtfcReg_GetFstatEeevld(obj->reg) == 1);

    return eeeen;
}

bool FlashDrv_GetEeeRamRdy(FlashDrvType *obj)
{
    bool eeeramrdy = false;

    eeeramrdy = (FtfcReg_GetFstatEeeRamRdy(obj->reg) == 1);

    return eeeramrdy;
}

FlashDrv_Status FlashDrv_GetEeeRamProtection(FlashDrvType *obj, uint8_t *protectstatus)
{
    FlashDrv_Status ret = FLASHDRV_STATUS_SUCCESS;

    if(!(obj->eeevld))
    {
        ret = FLASHDRV_STATUS_UNSUPPORTED;
    }
    else
    {
        *protectstatus = FtfcReg_GetEfProtecion(obj->reg);
    }

    return ret;
}

FlashDrv_Status FlashDrv_GetPFlashProtection(FlashDrvType *obj, uint32_t *protectstatus)
{
    FlashDrv_Status ret = FLASHDRV_STATUS_SUCCESS;

    *protectstatus = FtfcReg_GetPfProtecion(obj->reg);

    return ret;
}

FlashDrv_Status FlashDrv_GetDFlashProtection(FlashDrvType *obj, uint8_t *protectstatus)
{
    FlashDrv_Status ret = FLASHDRV_STATUS_SUCCESS;

    if(obj->dflashsize == 0x0)
    {
        ret = FLASHDRV_STATUS_UNSUPPORTED;
    }
    else
    {
        *protectstatus = FtfcReg_GetDfProtecion(obj->reg);
    }

    return ret;
}
