/*----------------------------------------------------------------------------*
 * Copyright Statement:                                                       *
 *                                                                            *
 *   This software/firmware and related documentation ("MediaTek Software")   *
 * are protected under international and related jurisdictions'copyright laws *
 * as unpublished works. The information contained herein is confidential and *
 * proprietary to MediaTek Inc. Without the prior written permission of       *
 * MediaTek Inc., any reproduction, modification, use or disclosure of        *
 * MediaTek Software, and information contained herein, in whole or in part,  *
 * shall be strictly prohibited.                                              *
 * MediaTek Inc. Copyright (C) 2010. All rights reserved.                     *
 *                                                                            *
 *   BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND     *
 * AGREES TO THE FOLLOWING:                                                   *
 *                                                                            *
 *   1)Any and all intellectual property rights (including without            *
 * limitation, patent, copyright, and trade secrets) in and to this           *
 * Software/firmware and related documentation ("MediaTek Software") shall    *
 * remain the exclusive property of MediaTek Inc. Any and all intellectual    *
 * property rights (including without limitation, patent, copyright, and      *
 * trade secrets) in and to any modifications and derivatives to MediaTek     *
 * Software, whoever made, shall also remain the exclusive property of        *
 * MediaTek Inc.  Nothing herein shall be construed as any transfer of any    *
 * title to any intellectual property right in MediaTek Software to Receiver. *
 *                                                                            *
 *   2)This MediaTek Software Receiver received from MediaTek Inc. and/or its *
 * representatives is provided to Receiver on an "AS IS" basis only.          *
 * MediaTek Inc. expressly disclaims all warranties, expressed or implied,    *
 * including but not limited to any implied warranties of merchantability,    *
 * non-infringement and fitness for a particular purpose and any warranties   *
 * arising out of course of performance, course of dealing or usage of trade. *
 * MediaTek Inc. does not provide any warranty whatsoever with respect to the *
 * software of any third party which may be used by, incorporated in, or      *
 * supplied with the MediaTek Software, and Receiver agrees to look only to   *
 * such third parties for any warranty claim relating thereto.  Receiver      *
 * expressly acknowledges that it is Receiver's sole responsibility to obtain *
 * from any third party all proper licenses contained in or delivered with    *
 * MediaTek Software.  MediaTek is not responsible for any MediaTek Software  *
 * releases made to Receiver's specifications or to conform to a particular   *
 * standard or open forum.                                                    *
 *                                                                            *
 *   3)Receiver further acknowledge that Receiver may, either presently       *
 * and/or in the future, instruct MediaTek Inc. to assist it in the           *
 * development and the implementation, in accordance with Receiver's designs, *
 * of certain softwares relating to Receiver's product(s) (the "Services").   *
 * Except as may be otherwise agreed to in writing, no warranties of any      *
 * kind, whether express or implied, are given by MediaTek Inc. with respect  *
 * to the Services provided, and the Services are provided on an "AS IS"      *
 * basis. Receiver further acknowledges that the Services may contain errors  *
 * that testing is important and it is solely responsible for fully testing   *
 * the Services and/or derivatives thereof before they are used, sublicensed  *
 * or distributed. Should there be any third party action brought against     *
 * MediaTek Inc. arising out of or relating to the Services, Receiver agree   *
 * to fully indemnify and hold MediaTek Inc. harmless.  If the parties        *
 * mutually agree to enter into or continue a business relationship or other  *
 * arrangement, the terms and conditions set forth herein shall remain        *
 * effective and, unless explicitly stated otherwise, shall prevail in the    *
 * event of a conflict in the terms in any agreements entered into between    *
 * the parties.                                                               *
 *                                                                            *
 *   4)Receiver's sole and exclusive remedy and MediaTek Inc.'s entire and    *
 * cumulative liability with respect to MediaTek Software released hereunder  *
 * will be, at MediaTek Inc.'s sole discretion, to replace or revise the      *
 * MediaTek Software at issue.                                                *
 *                                                                            *
 *   5)The transaction contemplated hereunder shall be construed in           *
 * accordance with the laws of Singapore, excluding its conflict of laws      *
 * principles.  Any disputes, controversies or claims arising thereof and     *
 * related thereto shall be settled via arbitration in Singapore, under the   *
 * then current rules of the International Chamber of Commerce (ICC).  The    *
 * arbitration shall be conducted in English. The awards of the arbitration   *
 * shall be final and binding upon both parties and shall be entered and      *
 * enforceable in any court of competent jurisdiction.                        *
 *---------------------------------------------------------------------------*/

//---------------------------------------------------------------------------
// Include files
//---------------------------------------------------------------------------
//#include "wrap.h"
#include "drvEEPROM.h"
//#include "drvcust_if.h"
//#include "x_ldr_env.h"
#include "MsDebug.h"
#if 0 // unused header files
#include <MsSystem.h>
#include <MsDebug.h>
#include <drvPM.h>
#include <drvWDT.h>
#include <bootlogo/MsPoolDB.h>
#include <exports.h>
#include <MsEnvironment.h>
#endif

#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;
}
