// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/*
 * Copyright (c) 2023 MediaTek Inc.
 */

//---------------------------------------------------------------------------
// Include files
//---------------------------------------------------------------------------
#include "drvEEPROM.h"
#include "debug_impl.h"

#define BOOT_CTRL_QHB     0x1
#define BOOT_CTRL_RESTART 0x2

#define UNUSED(x) (void)(x)

#define OFFSET_LOCATION 2
#define BITS_NUM 8

LDR_ENV_T g_LdrEnv;

int do_set_slot(cmd_tbl_t *cmdtp, int flag, int argc, const char * argv[])
{
    DTVCFG_T rDtvCfg;
    if (argc < 2) {
        printf("Use: %s a/b\n", argv[0]);
        return 0;
    }
    memset(&rDtvCfg, 0, sizeof(rDtvCfg));
    EEPDTV_GetCfg(&rDtvCfg);

    if (!strncmp(argv[1],"a",1)) {
        rDtvCfg.u1Flags3 &= ~(0x2);
    }
    else if (!strncmp(argv[1],"b",1)){
        rDtvCfg.u1Flags3 |= 0x2;
    }
    else {
        printf("Use: %s a/b\n", argv[0]);
        return 0;
    }

    UNUSED(EEPDTV_SetCfg(&rDtvCfg));

    return 0;
}

U_BOOT_CMD(
    set_slot, CONFIG_SYS_MAXARGS, 0, do_set_slot,
    "set which block to start from next time",
    "set which block to start from next time"
);

int do_get_slot(cmd_tbl_t *cmdtp, int flag, int argc, const char * argv[])
{
    DTVCFG_T rDtvCfg;

    if (argc > 1)
        return CMD_RET_USAGE;

    memset(&rDtvCfg, 0, sizeof(rDtvCfg));
    EEPDTV_GetCfg(&rDtvCfg);

    if ((rDtvCfg.u1Flags3 & 0x02) == 0x02) {
        printf("Currently starting from slot B.\n");
    }
    else {
        printf("Currently starting from slot A.\n");
    }

    return 0;
}

U_BOOT_CMD(
    get_slot, CONFIG_SYS_MAXARGS, 0, do_get_slot,
    "get which block to start from next time",
    "get which block to start from next time"
);

//#endif

#define EMMC_EEPROM_PARTITION   "eeprom_a"
#define MMC_SECTOR_SIZE         512
#define CMD_BUF                 128

static int _EepDtvSyncData(DTVCFG_T* prDtvCfg, unsigned int fgSync1, unsigned int fgSync2, unsigned int fgSync3);
static unsigned char _EepDtvCalChecksum(const DTVCFG_T* prDtvCfg);
//-------------------------------------------------------------------------
/** EEPROM_Read
 *  SYSTEM EEPROM read function.
 *  @param  u8Offset     begin address to read.
 *  @param  u4MemPtr     user's buffer address.
 *  @param  u4MemLen     read data length in bytes.
 *  @retval   0        SUCCESS.
 *  @retval   1        FAIL.
 */
//-------------------------------------------------------------------------
int EEPROM_Read(unsigned long long u8Offset, unsigned long u4MemPtr, unsigned int u4MemLen)
{
    char *cmdBuf = NULL;
    int ret;
    char *mmc_buf;
    unsigned int  sector_index = 0;
    unsigned int  sector_offset = 0;
    int snprintf_len;

    UBOOT_TRACE("IN\n");
    UBOOT_DEBUG("u8Offset = 0x%llx\n", u8Offset);
    UBOOT_DEBUG("u4MemPtr = 0x%lx\n", u4MemPtr);
    UBOOT_DEBUG("u4MemLen = 0x%x\n", u4MemLen);

    sector_index = u8Offset/MMC_SECTOR_SIZE;
    sector_offset = u8Offset%MMC_SECTOR_SIZE;

    mmc_buf = malloc(MMC_SECTOR_SIZE);
    memset(mmc_buf, 0, MMC_SECTOR_SIZE);

    cmdBuf = malloc(CMD_BUF);

    if (u4MemLen != 0) {
        memset(cmdBuf,0,CMD_BUF);
        //snprintf(cmdBuf, CMD_BUF,"mmc read.p.continue 0x%X %s 0x%X 0x%X", (unsigned int)mmc_buf, EMMC_EEPROM_PARTITION, sector_index, MMC_SECTOR_SIZE);
        //snprintf(cmdBuf, CMD_BUF,"partition read mmc 0 %s 0x%X 0x%X 0x%X", EMMC_EEPROM_PARTITION, (unsigned int)mmc_buf, MMC_SECTOR_SIZE, sector_index);
        snprintf_len = snprintf(cmdBuf, CMD_BUF,"partition read mmc 0 %s 0x%X 0x%X 0x%X", EMMC_EEPROM_PARTITION, (unsigned int)mmc_buf, u4MemLen, u8Offset);
        if (snprintf_len >= CMD_BUF)
        {
            UBOOT_ERROR("The array size is too small(%d), snprintf fail '%s'\n", CMD_BUF, cmdBuf);
            free(cmdBuf);
            free(mmc_buf);
            return -1;
        }
        UBOOT_DEBUG("cmd=%s\n",cmdBuf);
        ret=run_command(cmdBuf,0);
        if (ret == -1) {
            UBOOT_ERROR("EMMC EEPROM_Read fail\n");
            free(cmdBuf);
            free(mmc_buf);
            return -1;
        }
        if (dbgLevel&EN_DEBUG_LEVEL_DEBUG) {
            //snprintf(cmdBuf, CMD_BUF, "md.b 0x%x 0x%x", (unsigned int)(mmc_buf + sector_offset), u4MemLen);
            snprintf_len = snprintf(cmdBuf, CMD_BUF, "md.b 0x%x 0x%x", (unsigned int)(mmc_buf), u4MemLen);
            if (snprintf_len >= CMD_BUF)
            {
                UBOOT_ERROR("The array size is too small(%d), snprintf fail '%s'\n", CMD_BUF, cmdBuf);
                free(cmdBuf);
                free(mmc_buf);
                return -1;
            }
            run_command(cmdBuf,0);
        }
    }

    memcpy(u4MemPtr, mmc_buf, u4MemLen);
    free(cmdBuf);
    free(mmc_buf);
    UBOOT_TRACE("OK\n");
    return 0;
}

//-------------------------------------------------------------------------
/** EEPROM_Write
 *  SYSTEM EEPROM write function.
 *  @param  u8Offset     begin address to write.
 *  @param  u4MemPtr     user's buffer address.
 *  @param  u4MemLen     writedata length in bytes.
 *  @retval   0        SUCCESS.
 *  @retval   1        FAIL.
 */
//-------------------------------------------------------------------------
int EEPROM_Write(unsigned long long u8Offset, unsigned long u4MemPtr, unsigned int u4MemLen)
{
    char *cmdBuf = NULL;
    int ret;
    char *mmc_buf;
    int snprintf_len;
    unsigned int  sector_index = 0;
    unsigned int  sector_offset = 0;

    UBOOT_TRACE("IN\n");
    UBOOT_DEBUG("u8Offset = 0x%llx\n", u8Offset);
    UBOOT_DEBUG("u4MemPtr = 0x%lx\n", u4MemPtr);
    UBOOT_DEBUG("u4MemLen = 0x%x\n", u4MemLen);
    
    sector_index = u8Offset/MMC_SECTOR_SIZE;
    sector_offset = u8Offset%MMC_SECTOR_SIZE;

    mmc_buf = malloc(MMC_SECTOR_SIZE);
    memset(mmc_buf, 0, MMC_SECTOR_SIZE);

    cmdBuf = malloc(CMD_BUF);

    if (u4MemLen != 0) {
        memset(cmdBuf,0,CMD_BUF);
        //snprintf(cmdBuf, CMD_BUF,"mmc read.p.continue 0x%X %s 0x%X 0x%X", (unsigned int)mmc_buf, EMMC_EEPROM_PARTITION, sector_index, MMC_SECTOR_SIZE);
        //snprintf(cmdBuf, CMD_BUF,"partition read mmc 0 %s 0x%X 0x%X 0x%X", EMMC_EEPROM_PARTITION, (unsigned int)mmc_buf, MMC_SECTOR_SIZE, sector_index);
         snprintf_len = snprintf(cmdBuf, CMD_BUF,"partition read mmc 0 %s 0x%X 0x%X 0x%X", EMMC_EEPROM_PARTITION, (unsigned int)mmc_buf, MMC_SECTOR_SIZE, u8Offset);
        if (snprintf_len >= CMD_BUF)
        {
            UBOOT_ERROR("The array size is too small(%d), snprintf fail '%s'\n", CMD_BUF, cmdBuf);
            free(cmdBuf);
            free(mmc_buf);
            return -1;
        }
        UBOOT_DEBUG("cmd=%s\n",cmdBuf);
        ret=run_command(cmdBuf,0);
        if (ret == -1) {
            UBOOT_ERROR("EMMC EEPROM_Write fail\n");
            free(cmdBuf);
            free(mmc_buf);
            return -1;
        }
        //memcpy(mmc_buf + sector_offset, u4MemPtr, u4MemLen);
        memcpy(mmc_buf, u4MemPtr, u4MemLen);
        //snprintf(cmdBuf, CMD_BUF,"mmc write.p.continue 0x%X %s 0x%X 0x%X 1", (unsigned int)mmc_buf, EMMC_EEPROM_PARTITION, sector_index, MMC_SECTOR_SIZE);
        //snprintf(cmdBuf, CMD_BUF,"partition write mmc 0 %s 0x%X 0x%X 0x%X", EMMC_EEPROM_PARTITION, (unsigned int)mmc_buf, MMC_SECTOR_SIZE, sector_index);
        //snprintf(cmdBuf, CMD_BUF,"partition write mmc 0 %s 0x%X 0x%X 0x%X", EMMC_EEPROM_PARTITION, (unsigned int)mmc_buf, MMC_SECTOR_SIZE, u8Offset);
        snprintf_len = snprintf(cmdBuf, CMD_BUF,"partition write mmc 0 %s 0x%X 0x%X 0x%X", EMMC_EEPROM_PARTITION, (unsigned int)mmc_buf, u4MemLen, u8Offset);
        if (snprintf_len >= CMD_BUF)
        {
            UBOOT_ERROR("The array size is too small(%d), snprintf fail '%s'\n", CMD_BUF, cmdBuf);
            free(cmdBuf);
            free(mmc_buf);
            return -1;
        }
        UBOOT_DEBUG("cmd=%s\n",cmdBuf);
        ret=run_command(cmdBuf,0);
    }

    if (dbgLevel&EN_DEBUG_LEVEL_DEBUG) {
        //snprintf(cmdBuf, CMD_BUF, "md.b 0x%x 0x%x", (unsigned int)(mmc_buf + sector_offset), u4MemLen);
        snprintf_len = snprintf(cmdBuf, CMD_BUF, "md.b 0x%x 0x%x", (unsigned int)(mmc_buf), u4MemLen);
        if (snprintf_len >= CMD_BUF)
        {
            UBOOT_ERROR("The array size is too small(%d), snprintf fail '%s'\n", CMD_BUF, cmdBuf);
            free(cmdBuf);
            free(mmc_buf);
            return -1;
        }
        run_command(cmdBuf,0);
    }

    free(cmdBuf);
    free(mmc_buf);
    UBOOT_TRACE("OK\n");
    return 0;
}

int EEPDTV_Read(unsigned long long u8Offset, unsigned char * u4MemPtr, unsigned int u4MemLen)
{
    int i4Ret;

    if ((u8Offset + u4MemLen) > EEPROM_DTV_DRIVER_SIZE)
    {
        return 1;
    }
    // adjust offset to map to physical offset.
    u8Offset += EEPROM_DTV_DRIVER_OFFSET;
    i4Ret = EEPROM_Read(u8Offset, (unsigned long)u4MemPtr, u4MemLen);

    return i4Ret;
}

int EEPDTV_Write(unsigned long long u8Offset, unsigned char * u4MemPtr, unsigned int u4MemLen)
{
    int i4Ret;

    if ((u8Offset + u4MemLen) > EEPROM_DTV_DRIVER_SIZE)
    {
        return 1;
    }
    // adjust offset to map to physical offset.
    u8Offset += EEPROM_DTV_DRIVER_OFFSET;
    i4Ret = EEPROM_Write(u8Offset, (unsigned long)u4MemPtr, u4MemLen);

    return i4Ret;
}

//-------------------------------------------------------------------------
/** _EepDtvDecideTriple()
 *  Calculate the u1Vals to a decision.
 *  @param u1Val1/2/3.
 *  @return the final data.
 */
//-------------------------------------------------------------------------
static unsigned char _EepDtvDecideTriple(unsigned char u1Val1, unsigned char u1Val2, unsigned char u1Val3)
{
    unsigned char u1Ret = 0;
    int i, i4Count;

    for (i = 0; i < BITS_NUM; i++)
    {
        i4Count = 0;
        if (u1Val1 & (1U << i))
        {
            i4Count++;
        }
        if (u1Val2 & (1U << i))
        {
            i4Count++;
        }
        if (u1Val3 & (1U << i))
        {
            i4Count++;
        }
        if (i4Count > 1)
        {
            u1Ret |= (1U << i);
        }
    }
    return u1Ret;
}

//-------------------------------------------------------------------------
/** _EepDtvIsValid()
 *  Check whether the DTVCFG_T is valid.
 *  @param prDtvCfg the test DtvCfg data structure.
 *  @return 0 is invalid, otherwise is valid.
 *      Valid must magic chars are correct and checksum is correct.
 */
//-------------------------------------------------------------------------
static unsigned int _EepDtvIsValid(const DTVCFG_T* prDtvCfg)
{
    ASSERT(prDtvCfg != NULL);
    if (prDtvCfg->u1Magic1 != DTVCFG_MAGIC_CHAR1)
    {
        return 0;
    }
    if (prDtvCfg->u1Magic2 != DTVCFG_MAGIC_CHAR2)
    {
        return 0;
    }
    if (prDtvCfg->u1Checksum != _EepDtvCalChecksum(prDtvCfg))
    {
        return 0;
    }
    return 1;
}

//-------------------------------------------------------------------------
/** _EepDtvCorrection()
 *  Make DtvCfg triple redudancy sync.
 *  @param prDtvCfg Output - the final decision DtvCfg data structure pointer.
 *  @retval   0        SUCCESS.
 *  @retval   1        FAIL.
 */
//-------------------------------------------------------------------------
static int EEPDTV_Correction(DTVCFG_T* prDtvCfg)
{
    static unsigned int _fgEepDtvInit = 0;
    int i4Ret;
    DTVCFG_T rDtvCfg1, rDtvCfg2, rDtvCfg3;

    UBOOT_TRACE("IN\n");
    memset((void*)&rDtvCfg1, 0, sizeof(DTVCFG_T));
    memset((void*)&rDtvCfg2, 0, sizeof(DTVCFG_T));
    memset((void*)&rDtvCfg3, 0, sizeof(DTVCFG_T));
    ASSERT(prDtvCfg != NULL);
    if (_fgEepDtvInit)
    {
        return 0;
    }
    _fgEepDtvInit = 1;

    UBOOT_DEBUG( "EEPDTV_Correction start\n");
    // read the first DtvCfg location.
    i4Ret = EEPDTV_Read((unsigned long long) 0, (unsigned char *)(void*)&rDtvCfg1, sizeof(DTVCFG_T));
    if (i4Ret)
    {
        return i4Ret;
    }

    // Get the redudancy information.
    i4Ret = EEPDTV_Read((unsigned long long)(sizeof(DTVCFG_T)), (unsigned char *)(void*)&rDtvCfg2, sizeof(DTVCFG_T));
    if (i4Ret)
    {
        return i4Ret;
    }
    i4Ret = EEPDTV_Read((unsigned long long)(sizeof(DTVCFG_T)*OFFSET_LOCATION), (unsigned char *)(void*)&rDtvCfg3, sizeof(DTVCFG_T));
    if (i4Ret)
    {
        return i4Ret;
    }

    // Check non-configured eeprom.
    if (((rDtvCfg1.u1Magic1 != DTVCFG_MAGIC_CHAR1) || (rDtvCfg1.u1Magic2 != DTVCFG_MAGIC_CHAR2)) &&
        (!_EepDtvIsValid(&rDtvCfg2)) && (!_EepDtvIsValid(&rDtvCfg3)))
    {
        // no initial value on 1, and 2/3 are invalid.
        prDtvCfg->u1Flags = 0;
        prDtvCfg->u1Upgrade = 0;
        prDtvCfg->u1Upgrade_Ext = 0;
        prDtvCfg->u1Flags2 = 0;
        prDtvCfg->u1Flags3 = 0;
        prDtvCfg->u1Flags4 = 0;
        prDtvCfg->u1Flags7 = 0;
        return _EepDtvSyncData(prDtvCfg, 1, 1, 1);
    }
    // Check upgrade from old non-checksum version or only 1 is valid.
    else if ((!_EepDtvIsValid(&rDtvCfg1)) && (!_EepDtvIsValid(&rDtvCfg2)) && (!_EepDtvIsValid(&rDtvCfg3)))
    {
        // 0, 0, 0
        prDtvCfg->u1Flags = rDtvCfg1.u1Flags;
        prDtvCfg->u1Upgrade = rDtvCfg1.u1Upgrade;
        prDtvCfg->u1Upgrade_Ext = rDtvCfg1.u1Upgrade_Ext;
        prDtvCfg->u1Flags2 = rDtvCfg1.u1Flags2;
        prDtvCfg->u1Flags3 = rDtvCfg1.u1Flags3;
        prDtvCfg->u1Flags4 = rDtvCfg1.u1Flags4;
        prDtvCfg->u1Flags7 = rDtvCfg1.u1Flags7;
        return _EepDtvSyncData(prDtvCfg, 1, 1, 1);
    }
    // only 3 is valid.
    else if ((!_EepDtvIsValid(&rDtvCfg1)) && (!_EepDtvIsValid(&rDtvCfg2)))
    {
        // 0, 0, 1
        prDtvCfg->u1Flags = rDtvCfg3.u1Flags;
        prDtvCfg->u1Upgrade = rDtvCfg3.u1Upgrade;
        prDtvCfg->u1Upgrade_Ext = rDtvCfg3.u1Upgrade_Ext;
        prDtvCfg->u1Flags2 = rDtvCfg3.u1Flags2;
        prDtvCfg->u1Flags3 = rDtvCfg3.u1Flags3;
        prDtvCfg->u1Flags4 = rDtvCfg3.u1Flags4;
        prDtvCfg->u1Flags7 = rDtvCfg3.u1Flags7;;
        return _EepDtvSyncData(prDtvCfg, 1, 1, 0);
    }
    // only 2 is valid.
    else if ((!_EepDtvIsValid(&rDtvCfg1)) && (_EepDtvIsValid(&rDtvCfg2)))
    {
        // 0, 1, 0/0, 1, 1 both use 2.
        prDtvCfg->u1Flags = rDtvCfg2.u1Flags;
        prDtvCfg->u1Upgrade = rDtvCfg2.u1Upgrade;
        prDtvCfg->u1Upgrade_Ext = rDtvCfg2.u1Upgrade_Ext;
        prDtvCfg->u1Flags2 = rDtvCfg2.u1Flags2;
        prDtvCfg->u1Flags3 = rDtvCfg2.u1Flags3;
        prDtvCfg->u1Flags4 = rDtvCfg2.u1Flags4;
        prDtvCfg->u1Flags7 = rDtvCfg2.u1Flags7;
        return _EepDtvSyncData(prDtvCfg, 1, 0, 1);
    }
    // then if each of 2/3 is invalid.
    else if ((_EepDtvIsValid(&rDtvCfg1)) && ((!_EepDtvIsValid(&rDtvCfg2)) || (!_EepDtvIsValid(&rDtvCfg3))))
    {
        // 1, 0, 0/1, 1, 0/1, 0, 1, all use 1
        prDtvCfg->u1Flags = rDtvCfg1.u1Flags;
        prDtvCfg->u1Upgrade = rDtvCfg1.u1Upgrade;
        prDtvCfg->u1Upgrade_Ext = rDtvCfg1.u1Upgrade_Ext;
        prDtvCfg->u1Flags2 = rDtvCfg1.u1Flags2;
        prDtvCfg->u1Flags3 = rDtvCfg1.u1Flags3;
        prDtvCfg->u1Flags4 = rDtvCfg1.u1Flags4;
        prDtvCfg->u1Flags7 = rDtvCfg1.u1Flags7;
        return _EepDtvSyncData(prDtvCfg, 0, 1, 1);
    }

    // Here is all of them are valid.
    prDtvCfg->u1Flags = _EepDtvDecideTriple(rDtvCfg1.u1Flags, rDtvCfg2.u1Flags, rDtvCfg3.u1Flags);
    prDtvCfg->u1Upgrade = _EepDtvDecideTriple(rDtvCfg1.u1Upgrade, rDtvCfg2.u1Upgrade, rDtvCfg3.u1Upgrade);
    prDtvCfg->u1Upgrade_Ext = rDtvCfg1.u1Upgrade_Ext;
    prDtvCfg->u1Flags2 = rDtvCfg1.u1Flags2;
    prDtvCfg->u1Flags3 = rDtvCfg1.u1Flags3;
    prDtvCfg->u1Flags4 = rDtvCfg1.u1Flags4;
    prDtvCfg->u1Flags7 = rDtvCfg1.u1Flags7;
    i4Ret = _EepDtvSyncData(prDtvCfg, 1, 1, 1);

    UBOOT_DEBUG( "EEPDTV_Correction finsh\n");
    UBOOT_TRACE("OK\n");
    return i4Ret;
}

//-------------------------------------------------------------------------
/** _EepDtvCalChecksum()
 *  Calculate DTVCFG_T checksum value.
 *  @param prDtvCfg the DtvCfg data structure pointer.
 *  @return the unsigned char checksum value.
 */
//-------------------------------------------------------------------------
static unsigned char _EepDtvCalChecksum(const DTVCFG_T* prDtvCfg)
{
    unsigned char u1Ret;

    ASSERT(prDtvCfg != NULL);
    u1Ret = 0;
    u1Ret += (prDtvCfg->u1Magic1 + prDtvCfg->u1Magic2);
    u1Ret += (prDtvCfg->u1Flags + prDtvCfg->u1Upgrade);
    u1Ret += (prDtvCfg->u1Upgrade_Ext + prDtvCfg->u1Flags2 + prDtvCfg->u1Flags3 + prDtvCfg->u1Flags4);
    u1Ret += prDtvCfg->u1Flags7;

    return u1Ret;
}

//-------------------------------------------------------------------------
/** _EepDtvSyncData()
 *  Copy final DTVCFG_T to the location.
 *  @param prDtvCfg the final DTVCFG data.
 *  @param fgSync1 copy DTVCFG to location 1.
 *  @param fgSync2 copy DTVCFG to location 2.
 *  @param fgSync3 copy DTVCFG to location 3.
 */
//-------------------------------------------------------------------------
static int _EepDtvSyncData(DTVCFG_T* prDtvCfg, unsigned int fgSync1, unsigned int fgSync2, unsigned int fgSync3)
{
    DTVCFG_T rDtvCfg;
    LDR_ENV_T* prLdrEnv = (LDR_ENV_T*)CC_LDR_ENV_OFFSET;

    UBOOT_TRACE("IN\n");
    memset((void*)&rDtvCfg, 0, sizeof(DTVCFG_T));
    ASSERT(prDtvCfg != NULL);
    prDtvCfg->u1Magic1 = DTVCFG_MAGIC_CHAR1;
    prDtvCfg->u1Magic2 = DTVCFG_MAGIC_CHAR2;
    prDtvCfg->u1Checksum = _EepDtvCalChecksum(prDtvCfg);

    if (fgSync1)
    {
        if (EEPDTV_Read((unsigned long long)0, (unsigned char *)(void*)&rDtvCfg, sizeof(DTVCFG_T)) != 0)
        {
            return 1;
        }
        if ((rDtvCfg.u1Flags != prDtvCfg->u1Flags)
            || (rDtvCfg.u1Magic1 != DTVCFG_MAGIC_CHAR1) || (rDtvCfg.u1Magic2 != DTVCFG_MAGIC_CHAR2)
            || (rDtvCfg.u1Checksum != prDtvCfg->u1Checksum) || (rDtvCfg.u1Upgrade != prDtvCfg->u1Upgrade)
            || (rDtvCfg.u1Upgrade_Ext != prDtvCfg->u1Upgrade_Ext) || (rDtvCfg.u1Flags2 != prDtvCfg->u1Flags2) ||
            (rDtvCfg.u1Flags3 != prDtvCfg->u1Flags3) || (rDtvCfg.u1Flags4 != prDtvCfg->u1Flags4) ||
            (rDtvCfg.u1Flags7 != prDtvCfg->u1Flags7))
        {
            if (EEPDTV_Write((unsigned long long)EEPDTV_DTVCFG_MAGIC1, (unsigned char *)(void*)prDtvCfg, sizeof(DTVCFG_T)) != 0)
            {
                return 1;
            }
        }
    }

    if (fgSync2)
    {
        if (EEPDTV_Read((unsigned long long)(sizeof(DTVCFG_T)), (unsigned char *)(void*)&rDtvCfg, sizeof(DTVCFG_T)) != 0)
        {
            return 1;
        }
        if ((rDtvCfg.u1Flags != prDtvCfg->u1Flags)
            || (rDtvCfg.u1Magic1 != DTVCFG_MAGIC_CHAR1) || (rDtvCfg.u1Magic2 != DTVCFG_MAGIC_CHAR2)
            || (rDtvCfg.u1Checksum != prDtvCfg->u1Checksum) || (rDtvCfg.u1Upgrade != prDtvCfg->u1Upgrade)
            || (rDtvCfg.u1Upgrade_Ext != prDtvCfg->u1Upgrade_Ext) || (rDtvCfg.u1Flags2 != prDtvCfg->u1Flags2) ||
            (rDtvCfg.u1Flags3 != prDtvCfg->u1Flags3) || (rDtvCfg.u1Flags4 != prDtvCfg->u1Flags4) ||
            (rDtvCfg.u1Flags7 != prDtvCfg->u1Flags7))
        {
            if (EEPDTV_Write((unsigned long long)EEPDTV_DTVCFG_MAGIC1 + (unsigned long long)(sizeof(DTVCFG_T)), (unsigned char *)(void*)prDtvCfg, sizeof(DTVCFG_T)) != 0)
            {
                return 1;
            }
        }
    }

    if (fgSync3)
    {
        if (EEPDTV_Read((unsigned long long)(sizeof(DTVCFG_T)*OFFSET_LOCATION), (unsigned char *)(void *)&rDtvCfg, sizeof(DTVCFG_T))!=0)
        {
            return 1;
        }
        if ((rDtvCfg.u1Flags != prDtvCfg->u1Flags)
            || (rDtvCfg.u1Magic1 != DTVCFG_MAGIC_CHAR1) || (rDtvCfg.u1Magic2 != DTVCFG_MAGIC_CHAR2)
            || (rDtvCfg.u1Checksum != prDtvCfg->u1Checksum) || (rDtvCfg.u1Upgrade != prDtvCfg->u1Upgrade)
            || (rDtvCfg.u1Upgrade_Ext != prDtvCfg->u1Upgrade_Ext) || (rDtvCfg.u1Flags2 != prDtvCfg->u1Flags2) ||
            (rDtvCfg.u1Flags3 != prDtvCfg->u1Flags3) || (rDtvCfg.u1Flags4 != prDtvCfg->u1Flags4) ||
            (rDtvCfg.u1Flags7 != prDtvCfg->u1Flags7))
        {
            if (EEPDTV_Write((unsigned long long)EEPDTV_DTVCFG_MAGIC1 + (unsigned long long)(sizeof(DTVCFG_T)*OFFSET_LOCATION), (unsigned char *)(void*)prDtvCfg, sizeof(DTVCFG_T)) != 0)
            {
                return 1;
            }
        }

    }

    // save dtvcfg in dram cache
    memcpy((void*)prLdrEnv->au1DtvCfg, (void*)prDtvCfg, sizeof(DTVCFG_T));
    UBOOT_TRACE("OK\n");
    return 0;
}

int EEPDTV_GetCfg(DTVCFG_T* prDtvCfg)
{
    int i4Ret;
    static unsigned char fgInit = FALSE;
    LDR_ENV_T* prLdrEnv = (LDR_ENV_T*)CC_LDR_ENV_OFFSET;

    UBOOT_TRACE("IN\n");
    ASSERT(prDtvCfg != NULL);

    if (EEPDTV_Correction(prDtvCfg))
    {
        UBOOT_ERROR("EEPDTV_Correction fail.\n");
        return 1;
    }

    if (!fgInit)
    {
        // read the first DtvCfg location.
        i4Ret = EEPDTV_Read((unsigned long long) 0, (void*)(prLdrEnv->au1DtvCfg), sizeof(DTVCFG_T));
        if (i4Ret)
        {
            return i4Ret;
        }
        fgInit = TRUE;
    }

    // load dtvcfg from dram cache
    memcpy((void*)prDtvCfg, (void*)(prLdrEnv->au1DtvCfg), sizeof(DTVCFG_T));
    UBOOT_TRACE("OK\n");
    return 0;
}

//-------------------------------------------------------------------------
/** EEPDTV_SetCfg()
 *  @param prDtvCfg the input DtvCfg data structure pointer.
 *  @retval   0        SUCCESS.
 *  @retval   1        FAIL.
 */
//-------------------------------------------------------------------------
int EEPDTV_SetCfg(DTVCFG_T* prDtvCfg)
{
    int i4Ret = 1;

    UBOOT_TRACE("IN\n");
    ASSERT(prDtvCfg != NULL);

    prDtvCfg->u1Magic1 = DTVCFG_MAGIC_CHAR1;
    prDtvCfg->u1Magic2 = DTVCFG_MAGIC_CHAR2;
    prDtvCfg->u1Checksum = _EepDtvCalChecksum(prDtvCfg);

    i4Ret = _EepDtvSyncData(prDtvCfg, 1, 1, 1);
    printf("[drvEEPROM.c %s %d] DtvCfg:[%x %x %x %x %x %x %x %x %x %x]\n", __func__, __LINE__,
        prDtvCfg->u1Magic1,
        prDtvCfg->u1Magic2,
        prDtvCfg->u1Flags,
        prDtvCfg->u1Upgrade,
        prDtvCfg->u1Upgrade_Ext,
        prDtvCfg->u1Flags2,
        prDtvCfg->u1Flags3,
        prDtvCfg->u1Flags4,
        prDtvCfg->u1Flags7,
        prDtvCfg->u1Checksum);

    UBOOT_TRACE("OK\n");
    return i4Ret;
}
