/*
 * (C) Copyright 2003
 * Kyle Harris, kharris@nexus-tech.net
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <command.h>
#include <jffs2/jffs2.h>
#include <mmc.h>
#include <bootimg.h>
#include <android_image.h>

#ifdef CC_EEP2EMMC_DUAL_BANK_SUPPORT
#include "drvcust_if.h"
#endif
#ifdef CC_SUPPORT_AVB
#include "../lib/libavb_ab_uboot/libavb_ab_uboot.h"
#endif
#ifdef CC_SUPPORT_AB_UPDATE
#include <x_bim.h>
#include <x_dram.h>
#include <x_hal_5381.h>
#include <x_typedef.h>
#include <x_ldr_env.h>
#include <gcpu_if.h>
#endif
#ifdef CC_SUPPORT_DTO
#include <libfdt.h>
#endif

#ifndef MAX_IMAGE_SIZE
#define MAX_IMAGE_SIZE (20*1024*1024)
#endif

#if defined(CC_EMMC_BOOT)

#define MSDC_UBOOT_DATA_TEST 1
#ifdef CC_EEP2EMMC_DUAL_BANK_SUPPORT
typedef enum
{
    MSDC_EEPROM_NONE,
    MSDC_EEPROM_PARTA,
    MSDC_EEPROM_PARTB,
} MSDC_EEPROM_PART_T;

#define EEP2MSDC_MASK_ACCESS_PROHIBIT       (0x11)
#define EEP2MSDC_BANKA_ACCESS_PROHIBIT      (1<<0)
#define EEP2MSDC_BANKB_ACCESS_PROHIBIT      (1<<4)

typedef struct _UBOOT_MSDC_EEPROM_T
{
    u32 u4SDMPartId;
    u32 u4EEPPartIdA;
    u32 u4EEPPartIdB;
    u32 u4EEPPartOffA;
    u32 u4EEPPartOffB;
    u32 u4EEPBlkNum;
    u32 u4EEPSize;

    u32 u4WriteCnt;

    u32 u4EEPBlkA;
    u32 u4EEPBlkB;

    u32 u4EEPInitHeaderSize;
    u32 u4EEPHeaderSize;
    u32 u4EEPHeaderOffset;
    u32 u4EEPBankAOffset;
    u32 u4EEPBankBOffset;
    u8  u1fgEEPAccessProtect; //0x00:fully 0x01:A prohibit 0x10:B prohibit 0x11:access prohibit

    u8 *pu1EEPBuf;
} UBOOT_MSDC_EEPROM_T;
static _fgEEPInitialized = 0;
static UBOOT_MSDC_EEPROM_T _rMSDCEEPROM;
#endif


#if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
/* parition handling routines */
int mtdparts_init(void);
int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num);
int find_dev_and_part(const char *id, struct mtd_device **dev, u8 *part_num, struct part_info **part);
int find_partition(const char *part_name, u8 *part_num, u32 *part_size, u32 *part_offset);
#endif

static int emmc_read(u64 offset, void* addr, size_t cnt);
static int emmc_write(u64 offset, void* addr, size_t cnt);
static void print_mmcinfo(struct mmc *mmc)
{
	printf("Device: %s\n", mmc->name);
	printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24);
	printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff);
	printf("Name: %c%c%c%c%c \n", mmc->cid[0] & 0xff,
			(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
			(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);

	printf("Tran Speed: %d\n", mmc->tran_speed);
	printf("Rd Block Len: %d\n", mmc->read_bl_len);

	printf("%s version %d.%d\n", IS_SD(mmc) ? "SD" : "MMC",
			(mmc->version >> 4) & 0xf, mmc->version & 0xf);

	printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No");
	printf("Capacity: %lld\n", mmc->capacity);

	printf("Bus Width: %d-bit\n", mmc->bus_width);
}

int do_mmcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	struct mmc *mmc;
	int dev_num;

	if (argc < 2)
	{
		dev_num = 0;
	}
	else
	{
		dev_num = simple_strtoul(argv[1], NULL, 0);
	}

	mmc = find_mmc_device(dev_num);

	if (mmc)
  {
		mmc_init(mmc);
		print_mmcinfo(mmc);
	}

	return 0;
}
#if MSDC_UBOOT_DATA_TEST

#define MSDC_SRC_WRITE	0x8000000
#define MSDC_SRC_READ  0xD000000
#define MSDC_DATA_TOTAL_SIZE	  (0x500000)


#define MSDC_DATA_TOTAL_SIZE          (0x100000)
#define MSDC_DATA_START_ADDR          (0x20000000)
#define MSDC_DATA_CHUNK_SIZE          (0x20000)
#define SDHC_BLK_SIZE                 (512)

#define MSDC_DATA_TEST_MODE1          (0x1)
#define MSDC_DATA_TEST_MODE2          (0x2)
#define MSDC_DATA_TEST_MODE3          (0x3)

void MSDCTet_DataRandomGen(uint *pRamAddr, uint len, uint seed)
{
    uint i = 0;
    int rand_val = 0;

    //srand(seed); //Not work, why??
	printf("*****random data start!*****\n");
    for(;i<len;i+=sizeof(int))
    {
        rand_val = i * 8;//rand();
        memcpy((void *)(pRamAddr+(i>>2)), (void *)(&rand_val), sizeof(int));
    }

printf("*****random data end!*****\n");

#if 1
    printf("*****random data generated!*****\n");
    for(i = 0;i<0x10;i+=0x10)
    {
        if(i%0x10 == 0x0)
        {
            printf("%08x | %08x %08x %08x %08x\n", ((uint)pRamAddr + i),
                                                                   *(pRamAddr+i),
                                                                   *(pRamAddr+i+1),
                                                                   *(pRamAddr+i+2),
                                                                   *(pRamAddr+i+3));
        }
    }
    printf("********************************\n");
#endif
}

int MSDCTest_DataCompare(uint *pRamAddr1,  uint *pRamAddr2, uint len)
{
    uint i = 0;

    for(;i<len;i+=4)
    {
        if(*(pRamAddr1+(i>>2)) != *(pRamAddr2+(i>>2)))
        {
            printf("compare addr1(%08x) to addr2(%08x) failed:(%08x %08x)!\n",
                                     (uint)(pRamAddr1+(i>>2)), (uint)(pRamAddr2+(i>>2)),
                                     *(pRamAddr1+(i>>2)), *(pRamAddr2+(i>>2)));
            return -1;
        }
    }

    return 0;
}

int MSDCTest_DataTransmission(uint *pSrcWrite,  uint *pDestRead, uint testmode)
{
    uint total_sz = MSDC_DATA_TOTAL_SIZE,
           chuck_sz = MSDC_DATA_CHUNK_SIZE,
           i = 0, step_sz = SDHC_BLK_SIZE;
	int i4Ret = 0;

    if(MSDC_DATA_TEST_MODE1 == testmode)
    {
        step_sz = SDHC_BLK_SIZE;
    }
    else if(MSDC_DATA_TEST_MODE2 == testmode)
    {
        step_sz = total_sz;
    }
    else if(MSDC_DATA_TEST_MODE3 == testmode)
    {
        step_sz = chuck_sz;
    }
    else
    {
        printf("not support data test mode(%d)", testmode);
        i4Ret = -1;
        goto end;
    }

    printf("*****data transmission test*****\n");
    printf("Test Mode: %d\n", testmode);
    printf("Src Addr for Write: %08x\n", (uint)pSrcWrite);
    printf("Dest Addr for Read: %08x\n", (uint)pDestRead);
    printf("Block Size: %08x\n", SDHC_BLK_SIZE);
    printf("Chuck Size: %08x\n", chuck_sz);
    printf("Total Size: %08x\n", total_sz);
    printf("Step Size: %08x\n", step_sz);
    printf("********************************\n");


    printf("################# Start Write #################\n");

    //MsdcDumpRegister();
    for(i=0;i<total_sz;i+=step_sz)
    {


		emmc_write((u64)(MSDC_DATA_START_ADDR+i),(uint *)(pSrcWrite+(i>>2)), step_sz);
        //i4Ret = MsdcWriteCard((uint *)(pSrcWrite+(i>>2)), (u64)(MSDC_DATA_START_ADDR+i), step_sz);
        if(0 != i4Ret)
        {
            printf("write data from dram(0x%08x) to emmc/sd(0x%08x%08x) in test mode(%d) faield!\n",
                                    (uint)(pSrcWrite+(i>>2)), (uint)(((u64)(MSDC_DATA_START_ADDR+i))>>32),
                                    (uint)((u64)(MSDC_DATA_START_ADDR+i)), testmode);
            goto end;
        }
    }
    printf("################# End Write #################\n");

    printf("################# Start Read #################\n");
    //MsdcDumpRegister();

    for(i=0;i<total_sz;i+=step_sz)
    {
    	emmc_read((u64)(MSDC_DATA_START_ADDR+i), (uint *)(pDestRead+(i>>2)), step_sz);
        //i4Ret = MsdcReadCard((u64)(MSDC_DATA_START_ADDR+i), (uint *)(pDestRead+(i>>2)), step_sz);
        if(0 != i4Ret)
        {
            printf("write data from  emmc/sd(0x%08x%08x) to dram(0x%08x) in test mode(%d) faield!\n",
                                           (uint)(((u64)(MSDC_DATA_START_ADDR+i))>>32),
                                           (uint)((u64)(MSDC_DATA_START_ADDR+i)), (uint)(pSrcWrite+(i>>2)),
                                            testmode);
            goto end;
        }
    }
    printf("################# End Read #################\n");

    printf("Start data compare \n");

    i4Ret = MSDCTest_DataCompare((uint *)pSrcWrite, (uint *)pDestRead, total_sz);
    if(0 != i4Ret)
    {
        goto end;
    }

end:
    return i4Ret;
}


void msdc_uboot_data_test(void)
{

	printf("--start msdc_uboot_data_test--\n");
	MSDCTet_DataRandomGen(MSDC_SRC_WRITE, MSDC_DATA_TOTAL_SIZE, 0);
	MSDCTest_DataTransmission(MSDC_SRC_WRITE,	MSDC_SRC_READ, MSDC_DATA_TEST_MODE1);
	MSDCTest_DataTransmission(MSDC_SRC_WRITE,	MSDC_SRC_READ, MSDC_DATA_TEST_MODE2);
	MSDCTest_DataTransmission(MSDC_SRC_WRITE,	MSDC_SRC_READ, MSDC_DATA_TEST_MODE3);
}
#endif
U_BOOT_CMD(
	mmcinfo, 2, 0, do_mmcinfo,
	"display MMC info",
	"<dev num>\n"
	"    - device number of the device to dislay info of\n"
	""
);

int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	int rc = 0;

	switch (argc)
  {

	case 3:
    if (strcmp(argv[1], "rescan") == 0)
    {
			int dev = simple_strtoul(argv[2], NULL, 10);
			struct mmc *mmc = find_mmc_device(dev);

			if (!mmc)
			{
				return 1;
			}

			mmc_init(mmc);
			return 0;
		}
    else if (strncmp(argv[1], "part", 4) == 0)
    {
			int dev = simple_strtoul(argv[2], NULL, 10);
			block_dev_desc_t *mmc_dev;
			struct mmc *mmc = find_mmc_device(dev);

			if (!mmc)
      {
				puts("no mmc devices available\n");
				return 1;
			}

			mmc_init(mmc);
			mmc_dev = mmc_get_dev(dev);
			if (mmc_dev != NULL && mmc_dev->type != DEV_TYPE_UNKNOWN)
			{
				print_part(mmc_dev);
				return 0;
			}

			puts("get mmc type error!\n");
			return 1;
		}
		else if (strcmp(argv[1], "eraseall") == 0)
    {
      int dev = simple_strtoul(argv[2], NULL, 10);
			struct mmc *mmc = find_mmc_device(dev);
			u32 n;

			if (!mmc)
			{
				return 1;
			}

			n = mmc_erase_all(mmc);

      return n;
    }

	case 0:
	case 1:
	case 4:
    return cmd_usage(cmdtp);

	case 2:
        #if MSDC_UBOOT_DATA_TEST
		if (strcmp(argv[1], "dat") == 0)
		{
			msdc_uboot_data_test();
		}
		#endif
		if (!strcmp(argv[1], "list"))
    {
			print_mmc_devices('\n');
			return 0;
		}
		return 1;

	default: /* at least 5 args */
		if ((strcmp(argv[1], "read") == 0) || (strcmp(argv[1], "read.b") == 0))
    {
			int dev = simple_strtoul(argv[2], NULL, 10);
			void *addr = (void *)simple_strtoul(argv[3], NULL, 16);
			u32 cnt = simple_strtoul(argv[5], NULL, 16);
			u32 n;
			u64 blk = simple_strtoull(argv[4], NULL, 16);

			struct mmc *mmc = find_mmc_device(dev);
			if (!mmc)
			{
				return 1;
			}

      if (strcmp(argv[1], "read") == 0)
      {
			    printf("\nMMC block read: dev # %d, block # %lld, count %d ... ", dev, blk, cnt);
      }
      else
      {
			    printf("\nMMC byte read: dev # %d, offset # %lld, count %d ... ", dev, blk, cnt);
      }

	  mmc_init(mmc);

      if (strcmp(argv[1], "read") == 0)
      {
        n = mmc->block_dev.block_read(dev, blk, cnt, addr);

        /* flush cache after read */
        flush_cache((ulong)addr, cnt * 512); /* FIXME */
      }
      else
      {
        n = mmc->block_dev.byte_read(dev, blk, cnt, addr);

        /* flush cache after read */
        flush_cache((ulong)addr, cnt); /* FIXME */
      }

      if (strcmp(argv[1], "read") == 0)
      {
        printf("%d blocks read: %s\n", n, (n==cnt) ? "OK" : "ERROR");
      }
      else
      {
        printf("%d bytes read: %s\n", n, (n==cnt) ? "OK" : "ERROR");
      }
			return (n == cnt) ? 0 : 1;
		}
    else if ((strcmp(argv[1], "write") == 0) || (strcmp(argv[1], "write.b") == 0))
    {
			int dev = simple_strtoul(argv[2], NULL, 10);
			void *addr = (void *)simple_strtoul(argv[3], NULL, 16);
			u32 cnt = simple_strtoul(argv[5], NULL, 16);
			u32 n;
			u64 blk = simple_strtoull(argv[4], NULL, 16);
			struct mmc *mmc = find_mmc_device(dev);
			if (!mmc)
			{
				return 1;
			}

      if (strcmp(argv[1], "read") == 0)
      {
        printf("\nMMC block write: dev # %d, block # %lld, count %d ... ", dev, blk, cnt);
      }
      else
      {
        printf("\nMMC byte write: dev # %d, offset # %lld, count %d ... ", dev, blk, cnt);
      }

			mmc_init(mmc);

      if (strcmp(argv[1], "write") == 0)
      {
        n = mmc->block_dev.block_write(dev, blk, cnt, addr);
      }
      else
      {
        n = mmc->block_dev.byte_write(dev, blk, cnt, addr);
      }

      if (strcmp(argv[1], "read") == 0)
      {
        printf("%d blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR");
      }
      else
      {
        printf("%d bytes written: %s\n", n, (n == cnt) ? "OK" : "ERROR");
      }
			return (n == cnt) ? 0 : 1;
		}
    else if ((strcmp(argv[1], "erase") == 0) ||
    	       (strcmp(argv[1], "trim") == 0) ||
    	       (strcmp(argv[1], "secureerase") == 0) ||
    	       (strcmp(argv[1], "securetrim1") == 0) ||
    	       (strcmp(argv[1], "securetrim2") == 0))
    {
		int dev = simple_strtoul(argv[2], NULL, 10);
		u64 bytestart = simple_strtoull(argv[3], NULL, 16);
		u64 length = simple_strtoull(argv[4], NULL, 16);
		int mode;
		u32 n;

		struct mmc *mmc = find_mmc_device(dev);
		if (!mmc)
		{
			return 1;
		}

		if (strcmp(argv[1], "erase") == 0)
      {
          mode = 0;
      }
      else if (strcmp(argv[1], "trim") == 0)
      {
          mode = 1;
      }
      else if (strcmp(argv[1], "secureerase") == 0)
      {
          mode = 2;
      }
      else if (strcmp(argv[1], "securetrim1") == 0)
      {
          mode = 3;
      }
      else if (strcmp(argv[1], "securetrim2") == 0)
      {
          mode = 4;
      }

      n = mmc_erase(mmc, bytestart, length, mode);

      return n;
    }
    else if (strcmp(argv[1], "wp") == 0)
    {
      int dev = simple_strtoul(argv[2], NULL, 10);
			u32 bytestart = simple_strtoul(argv[3], NULL, 16);
			u32 level = simple_strtoul(argv[4], NULL, 10);
			u32 type = simple_strtoul(argv[5], NULL, 10);
			u32 fgen = simple_strtoul(argv[6], NULL, 10);
			u32 n;

			struct mmc *mmc = find_mmc_device(dev);
			if (!mmc)
			{
				return 1;
			}

			n = mmc_wp(mmc, bytestart, level, type, fgen);

			return n;

    }
    else
    {
			rc = cmd_usage(cmdtp);
    }

		return rc;
	}
}

U_BOOT_CMD(
	mmc, 7, 1, do_mmcops,
	"MMC sub system",
	"read <device num> addr blk# cnt\n"
	"mmc write <device num> addr blk# cnt\n"
	"mmc eraseall\n"
	"mmc erase <device num> byte_addr length mode\n"
	"mmc wp <device num> byte_addr level type en\n"
	"mmc rescan <device num>\n"
	"mmc part <device num> - lists available partition on mmc\n"
	"mmc list - lists available devices");

static int emmc_read(u64 offset, void* addr, size_t cnt)
{
    int r;
    struct mmc *mmc;

    mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
    if (!mmc)
    {
        return 1;
    }

    if (mmc_init(mmc))
    {
        puts("MMC init failed\n");
        return 1;
    }

    r = mmc->block_dev.byte_read(CONFIG_SYS_MMC_ENV_DEV, offset, cnt, addr);
    if (r != cnt)
    {
        puts("** Read error\n");
        return 1;
    }

    return cnt;
}
static int emmc_write(u64 offset, void* addr, size_t cnt)
{
    int r;
    struct mmc *mmc;

    mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
    if (!mmc)
    {
        return 1;
    }

    if (mmc_init(mmc))
    {
        puts("MMC init failed\n");
        return 1;
    }

    r = mmc->block_dev.byte_write(CONFIG_SYS_MMC_ENV_DEV, offset, cnt, addr);
    if (r != cnt)
    {
        puts("** Read error\n");
        return 1;
    }

    return cnt;
}

extern int emmc_read_msdc(u64 offset, void* addr, size_t cnt)
{
    int r;
    struct mmc *mmc;

    mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
    if (!mmc)
    {
        return -1;
    }

    if (mmc_init(mmc))
    {
        puts("MMC init failed\n");
        return -1;
    }

    r = mmc->block_dev.byte_read(CONFIG_SYS_MMC_ENV_DEV, offset, cnt, addr);
    if (r != cnt)
    {
        puts("Read error\n");
        return -1;
    }
    else
    {
        puts("Read ok\n");
    }

    return 0;
}

extern int emmc_write_msdc(u64 offset, void* addr, size_t cnt)
{
    int r;
    struct mmc *mmc;

    mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
    if (!mmc)
    {
        return -1;
    }

    if (mmc_init(mmc))
    {
        puts("MMC init failed\n");
        return -1;
    }

    r = mmc->block_dev.byte_write(CONFIG_SYS_MMC_ENV_DEV, offset, cnt, addr);
    if (r != cnt)
    {
        printf("write error\n");
        return -1;
    }
    else
    {
        printf("write ok\n");
    }

    return 0;
}

extern int access_emmc_rpmb(unsigned char onoff)
{
    int err;
    struct mmc *mmc;

    mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
  
    if (!mmc)
        return -1;

    if (mmc_init(mmc))
        puts("MMC init failed\n");

	if (onoff) {
		err = mmc->block_dev.mmc_access(CONFIG_SYS_MMC_ENV_DEV, 0x3);
		if (err)
			printf("Open RPMB access failed.\n");
		else
			printf("--ENTER RPMB--\n");
	} else {
		err = mmc->block_dev.mmc_access(CONFIG_SYS_MMC_ENV_DEV, 0x0);
		if (err)
			printf("Close RPMB access failed.\n");
		else
			printf("--EXIT RPMB--\n");
	}

	return err;
}

extern int emmc_set_blkcnt(unsigned int count, unsigned int write_flag)
{
    int err;
    struct mmc *mmc;

    mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
  
    if (!mmc)
        return -1;

    if (mmc_init(mmc))
        puts("MMC init failed\n");
  
    err = mmc->block_dev.mmc_setblkcnt(CONFIG_SYS_MMC_ENV_DEV, count, write_flag);
    if (err)
        printf("-- set count failed --\n");
    else
	printf("-- set count success --\n");

    return err;  
}

#ifdef CC_EEP2EMMC_DUAL_BANK_SUPPORT
static int UBOOT_ReadMsdc(u32 u4PartId, u32 u4Offset, void *pvMemPtr, u32 u4MemLen)
{
    // get physical address by partition id.
    u64 u8Offset = u4Offset;
    u8Offset += DRVCUST_InitGet64((QUERY_TYPE_T)(eNANDFlashPartOffset0 + u4PartId));

    if (emmc_read_msdc(u8Offset, (u32)pvMemPtr, u4MemLen) == 0)
    {
        return (int)u4MemLen;
    }
    else
    {
        return -1;
    }
}

static int UBOOT_WriteMsdc(u32 u4PartId, u32 u4Offset, void *pvMemPtr, u32 u4MemLen)
{
    // get physical address by partition id.
    u64 u8Offset = u4Offset;
    u8Offset += DRVCUST_InitGet64((QUERY_TYPE_T)(eNANDFlashPartOffset0 + u4PartId));

    if (emmc_write_msdc(u8Offset, (u32)pvMemPtr, u4MemLen) == 0)
    {
        return (int)u4MemLen;
    }
    else
    {
        return -1;
    }
}

static int _UBOOT_MSDC_EEPROM_Init(void)
{
    u8 u1InitHeader[4]={0};

    if (_fgEEPInitialized)
    {
        return 0;
    }

    _rMSDCEEPROM.u4EEPSize    = DRVCUST_InitGet64(eSystemEepromSize);
    _rMSDCEEPROM.u4SDMPartId  = DRVCUST_InitGet64(eNANDFlashDynamicPartitionID);
    _rMSDCEEPROM.u4EEPPartIdA = DRVCUST_InitGet64(eNANDFlashPartIdEepromA);
    _rMSDCEEPROM.u4EEPPartIdB = DRVCUST_InitGet64(eNANDFlashPartIdEepromB);
    _rMSDCEEPROM.pu1EEPBuf = (u8 *)malloc(_rMSDCEEPROM.u4EEPSize);

    if (_rMSDCEEPROM.pu1EEPBuf == NULL)
    {
        printf("_UBOOT_MSDC_EEPROM_Init Can't allocate memory!\n");
        return -1;
    }

    _rMSDCEEPROM.u4EEPPartOffA = DRVCUST_InitGet64((QUERY_TYPE_T)(eNANDFlashPartOffset0 + _rMSDCEEPROM.u4EEPPartIdA));
    _rMSDCEEPROM.u4EEPPartOffB = DRVCUST_InitGet64((QUERY_TYPE_T)(eNANDFlashPartOffset0 + _rMSDCEEPROM.u4EEPPartIdB));

    _rMSDCEEPROM.u4WriteCnt = 0xFFFFFFFF;
    _rMSDCEEPROM.u4EEPBlkNum = (_rMSDCEEPROM.u4EEPSize < 512) ? 1 : _rMSDCEEPROM.u4EEPSize / 512;
    _rMSDCEEPROM.u4EEPBlkA = 0xFFFFFFFF;
    _rMSDCEEPROM.u4EEPBlkB = 0xFFFFFFFF;

    _rMSDCEEPROM.u4EEPInitHeaderSize = DRVCUST_InitGet64(eSystemEepInitHeaderSize);
    _rMSDCEEPROM.u4EEPHeaderSize = DRVCUST_InitGet64(eSystemEepHeaderSize);
    _rMSDCEEPROM.u4EEPHeaderOffset = _rMSDCEEPROM.u4EEPInitHeaderSize;
    _rMSDCEEPROM.u4EEPBankAOffset = _rMSDCEEPROM.u4EEPHeaderOffset + _rMSDCEEPROM.u4EEPHeaderSize;
    _rMSDCEEPROM.u4EEPBankBOffset = _rMSDCEEPROM.u4EEPBankAOffset + _rMSDCEEPROM.u4EEPHeaderSize;;
    //read init header first
    if (UBOOT_ReadMsdc(_rMSDCEEPROM.u4EEPPartIdA, 0, (void*)(u1InitHeader), 4) != 4)
    {
        printf("_UBOOT_MSDC_EEPROM_Init header(%d) fail!\n",_rMSDCEEPROM.u4EEPPartIdA);
        return -1;
    }
    if((u1InitHeader[0]==0x58) && (u1InitHeader[1]==0x90) &&
		(u1InitHeader[2]==0x58) && (u1InitHeader[3]==0x90))
    {
	    _rMSDCEEPROM.u1fgEEPAccessProtect = 0x00;
    }
    else
    {
        _rMSDCEEPROM.u1fgEEPAccessProtect = 0x11;
    }

    memset((void *)_rMSDCEEPROM.pu1EEPBuf, 0xFF, _rMSDCEEPROM.u4EEPSize);

    _fgEEPInitialized = 1;

   //LOG(3, "_MSDC_EEPROM_Init end!\n");
    return 0;
}
int UBOOT_MSDC_EEPROM_Read_Dual_Bank(u32 u4Offset, u32 u4MemPtr, u32 u4MemLen)
{
    int i4Ret = 0;
    u32 u4HeaderIdx;
    u8 u1HeadderCount=0;

    _UBOOT_MSDC_EEPROM_Init();

    if ((u4Offset >= _rMSDCEEPROM.u4EEPSize) || ((u4Offset + u4MemLen) > _rMSDCEEPROM.u4EEPSize))
    {
        printf("MSDC_EEPROM_Read: out of boundry! u4Offset = 0x%x, u4MemLen = 0x%x\n", u4Offset, u4MemLen);

        return -1;
    }

    if ((_rMSDCEEPROM.u1fgEEPAccessProtect & EEP2MSDC_MASK_ACCESS_PROHIBIT) != 0)
    {
        printf("UBOOT_MSDC_EEPROM_Read access prohibit!\n");
        return -1;
    }

    u4HeaderIdx = u4Offset + _rMSDCEEPROM.u4EEPHeaderOffset;
    if (UBOOT_ReadMsdc(_rMSDCEEPROM.u4EEPPartIdA, u4HeaderIdx,(void*)(&u1HeadderCount), 1) != 1)
    {
        printf("UBOOT_Msdc_Read_EEPROMHeader fail!\n");
        return -1;
    }
    if(u1HeadderCount == 0)
    {
        u4Offset = u4Offset + _rMSDCEEPROM.u4EEPBankAOffset;
    }
    else
    {
        u4Offset = u4Offset + _rMSDCEEPROM.u4EEPBankBOffset;
    }

    if (UBOOT_ReadMsdc(_rMSDCEEPROM.u4EEPPartIdA, u4Offset,(void*)u4MemPtr, u4MemLen) != u4MemLen)
    {
        printf("UBOOT_Msdc_Read_EEPROMData fail!\n");
        return -1;
    }

    return i4Ret;
}

int UBOOT_MSDC_EEPROM_Write_Dual_Bank(u32 u4Offset, u32 u4MemPtr, u32 u4MemLen)
{
    int i4Ret = 0;
    u32 u4HeaderIdx;
    u8 u1HeadderCount=0;
    u32 u4SyncOffset;
    u8 fgDualBankSyncEnalbe=0;

    _UBOOT_MSDC_EEPROM_Init();

    if ((u4Offset >= _rMSDCEEPROM.u4EEPSize) || ((u4Offset + u4MemLen) > _rMSDCEEPROM.u4EEPSize))
    {
        printf("UBOOT_MSDC_EEPROM_Write: out of boundry! u4Offset = 0x%x, u4MemLen = 0x%x\n", u4Offset, u4MemLen);

        return -1;
    }

    //64k area was handled by MTK
    if(u4Offset < 0x10000)
    {
        fgDualBankSyncEnalbe=1;
    }
    if ((_rMSDCEEPROM.u1fgEEPAccessProtect & EEP2MSDC_MASK_ACCESS_PROHIBIT) != 0)
    {
        printf("UBOOT_MSDC_EEPROM_Write access prohibit!\n");
        return -1;
    }
    //read header first
    u4HeaderIdx = u4Offset + _rMSDCEEPROM.u4EEPHeaderOffset;
    if (UBOOT_ReadMsdc(_rMSDCEEPROM.u4EEPPartIdA, u4HeaderIdx,(void*)(&u1HeadderCount), 1) != 1)
    {
        printf("UBOOT_Msdc_Write_Read_EEPROMHeader fail!\n");
        return -1;
    }
    //decide which bank to write to
    if(u1HeadderCount == 0)
    {
        u4SyncOffset = u4Offset + _rMSDCEEPROM.u4EEPBankAOffset;
        u4Offset = u4Offset + _rMSDCEEPROM.u4EEPBankBOffset;
    }
    else
    {
        u4SyncOffset = u4Offset + _rMSDCEEPROM.u4EEPBankBOffset;
        u4Offset = u4Offset + _rMSDCEEPROM.u4EEPBankAOffset;
    }
    //data write
    if (UBOOT_WriteMsdc(_rMSDCEEPROM.u4EEPPartIdA, u4Offset,(void*)u4MemPtr, u4MemLen) != u4MemLen)
    {
        printf("UBOOT_MSDC_EEPROM_Write fail!\n");
        return -1;
    }
    //update header count
    if(u1HeadderCount == 0) {
        u1HeadderCount = 1;
    }
    else {
        u1HeadderCount = 0;
    }
    if (UBOOT_WriteMsdc(_rMSDCEEPROM.u4EEPPartIdA, u4HeaderIdx,(void*)(&u1HeadderCount), 1) != 1)
    {
        printf("UBOOT_Msdc_Write_Read_EEPROMHeader fail!\n");
        return -1;
    }

    //sync data write
    if(fgDualBankSyncEnalbe)
    {
        if (UBOOT_WriteMsdc(_rMSDCEEPROM.u4EEPPartIdA, u4SyncOffset,(void*)u4MemPtr, u4MemLen) != u4MemLen)
        {
            printf("UBOOT_MSDC_EEPROM_Write fail!\n");
            return -1;
        }
    }

    return i4Ret;
}
#endif

#ifdef CC_SECURE_ROM_BOOT
// Auth kernel image at addr
static void emmc_auth_image(struct part_info *part, ulong addr, ulong skip)
{
    image_header_t *hdr = (image_header_t *)addr;
    u32 sig_offset = image_get_image_size(hdr);
#if defined(CC_SECURE_BOOT_V2) || defined(CC_SECURE_BOOT_V3)
    // fix signature offset with pack size - fill 0xff by 16 bytes alignment by sign tool
    sig_offset = ((sig_offset) + 0x10) & (~0xf);
#endif
    printf("kernel: ");
    emmc_read(part->offset + sig_offset + skip, (void *)CFG_LOAD_ADDR, 0x200);
    sig_authetication(addr, sig_offset, (unsigned long *)CFG_LOAD_ADDR, (unsigned long *)(CFG_LOAD_ADDR + 0x100));
}

static void emmc_auth_boot_image(struct part_info *part, ulong addr, ulong skip, ulong size_to_check)
{
    //int i;
    //unsigned char *ptr;
    //image_header_t *hdr = (image_header_t *)addr;
    ulong sig_offset = size_to_check;

#if 0
    printf("header(%llx):\n", addr);
    ptr = (unsigned char *)addr;
    for (i = 0; i < 0x200; i++)
    {
        printf("%02x ", ptr[i]);
        if (i % 16 == 15)
            printf("\n");
    }

 	printf("load signature: ");
#endif
    printf("kernel: ");
    emmc_read(part->offset + sig_offset + skip, (void *)CFG_LOAD_ADDR, 0x100);

#if 0
    printf("signature(%llx):\n", part->offset+sig_offset+skip);
    ptr = (unsigned char *)CFG_LOAD_ADDR;
    for (i = 0; i < 0x100; i++)
    {
        printf("%02x ", ptr[i]);
        if (i % 16 == 15)
            printf("\n");
    }
#endif
    sig_authetication(addr, sig_offset, (unsigned long *)CFG_LOAD_ADDR, NULL);
}

// Auth ramdisk image at addr
static void emmc_auth_image_ex(struct part_info *part, ulong addr, ulong skip, ulong size_to_check)
{
#ifndef CC_SECURE_BOOT_WO_RAMDISK
    printf("ramdisk: ");
#if defined(CC_SECURE_BOOT_V2) || defined(CC_SECURE_BOOT_V3)
    // this only check size_to_check
    //sig_authetication(addr, size_to_check, (unsigned long *)(addr + skip - 0x100), (unsigned long *)(addr + skip));
    // this check full ramdisk size
    sig_authetication(addr, skip - 0x100, (unsigned long *)(addr + skip - 0x100), (unsigned long *)(addr + skip));
#else  // CC_SECURE_BOOT_V2/V3
    sig_authetication(addr, size_to_check, (unsigned long *)(addr + skip - 0x200), (unsigned long *)(addr + skip - 0x100));
#endif // CC_SECURE_BOOT_V2/V3
#endif // CC_SECURE_BOOT_WO_RAMDISK
}
#else
#define emmc_auth_image(part, addr, skip)
#define emmc_auth_image_ex(part, addr, skip, size_to_check)
#define emmc_auth_boot_image(part, addr, skip, size_to_check)
#endif

#ifdef CC_SUPPORT_DTO
static int emmc_load_dtbo(uint8_t *image_addr, size_t image_size)
{
    ulong ram_addr = CFG_DTBO_ADDR;

    if(image_addr != NULL)
    {
        memcpy(ram_addr,image_addr,image_size);

        if(get_dtbo_image_type(ram_addr) == DTBO_IMG_TYPE_UNKNOWN)
        {
            printf("%s:%d:%s: Error, unknown dtbo image type !!\n",__FILE__,__LINE__,__FUNCTION__);
            return 1;
        }
    }
    else
    {
        struct mmc *mmc;
        size_t image_size = 0,read_size = 0;
        uint8_t pnum;
        struct mtd_device *dev;
        struct part_info *part;
        ulong mmc_offset;

    #ifdef CC_SUPPORT_AB_UPDATE
        char dtbo_partition[32] = "dtbo";
        char slot_suffix[8] = {0};
        extern uint8_t env_get_bootargs(const char* key, char* value);

        if(env_get_bootargs("androidboot.slot_suffix", slot_suffix))
        {
            strcat(dtbo_partition, slot_suffix);
        }
        else
        {
            printf("%s:%d:%s: Error, there is no bootable slot!\n",__FILE__,__LINE__,__FUNCTION__);
            return 1;
        }
        if ((mtdparts_init() == 0) &&
            (find_dev_and_part(dtbo_partition, &dev, &pnum, &part) == 0))
    #else
        char dtbo_partition[32] = "dtbo";
        if ((mtdparts_init() == 0) &&
            (find_dev_and_part(dtbo_partition, &dev, &pnum, &part) == 0))
    #endif
        {
            printf("## %s: Load dtbo image from partition: %s\n",__FUNCTION__,dtbo_partition);

            mmc_offset = part->offset;

            image_size = 2048;
            read_size = emmc_read(mmc_offset, ram_addr, image_size);
            if (read_size != image_size)
            {
                printf("%s:%d:%s: MMC Read %s partition error.\n",__FILE__,__LINE__,__FUNCTION__, dtbo_partition);
                return 1;
            }

            if(get_dtbo_image_type(ram_addr) == DTBO_IMG_TYPE_AOSP)
            {
                struct dt_table_header * hdr = (struct dt_table_header *)ram_addr;
                image_size = be32_to_cpu(hdr->total_size);
            }
            else if(get_dtbo_image_type(ram_addr) == DTBO_IMG_TYPE_LEGACY && fdt_magic(image_get_data(ram_addr)) == FDT_MAGIC)
            {
                image_header_t *hdr = (image_header_t *)ram_addr;
                image_size = image_get_image_size(hdr);
            }
            else
            {
                printf("%s:%d:%s: Error, unknown dtbo image type !!\n",__FILE__,__LINE__,__FUNCTION__);
                return 1;
            }

            if(image_size > read_size)
            {
                read_size = emmc_read(mmc_offset, ram_addr, image_size);
                if (read_size != image_size)
                {
                    printf("%s:%d:%s: MMC Read %s partition error.\n",__FILE__,__LINE__,__FUNCTION__, dtbo_partition);
                    return 1;
                }
            }

            if(get_dtbo_image_type(ram_addr) == DTBO_IMG_TYPE_LEGACY)
            {
                printf("Verifying dtbo image Checksum ......");
                if (!image_check_dcrc((image_header_t *)ram_addr))
                {
                    printf(" Bad Data CRC !\n");
                    return 1;
                }
                else
                {
                    printf(" OK !\n");
                }
            }

            printf("## Load dtbo image done, image size: %d Byte !\n",image_size);
        }
        else
        {
            printf("%s:%d:%s: Can not find the dtbo partition: %s\n",__FILE__,__LINE__,__FUNCTION__, dtbo_partition);
            return 1;
        }
    }
    return 0;
}
#endif

#if defined(CC_SUPPORT_AB_UPDATE) && (defined(CC_TRUSTZONE_SUPPORT) || defined(CC_TRUSTZONE_64_SUPPORT))
static int emmc_load_tzbp(uint8_t *tzbp_img_addr, size_t tzbp_img_size)
{
    uint32_t u4TzPlainSize = 0;
    uint32_t u4TzLzhsSize = 0;
    uint8_t u1TzChksum = 0;
    #if defined(CC_TRUSTZONE_IN_CHB)
    uint32_t u4TzLzhsDstDram = TCMGET_CHANNELB_START_ADDR() + TCMGET_CHANNELB_SIZE() * 0x100000 - TRUSTZONE_MEM_SIZE + TRUSTZONE_CODE_BASE;
    #else
    uint32_t u4TzLzhsDstDram = TOTAL_MEM_SIZE - TRUSTZONE_MEM_SIZE + TRUSTZONE_CODE_BASE;
    #endif

    if(tzbp_img_addr == NULL)
    {
        uint8_t pnum;
        struct mtd_device *dev;
        struct part_info *part;
        size_t read_size = 0;

        char tzbp_partition[32] = "tzbp";
        char slot_suffix[8] = {0};
        extern uint8_t env_get_bootargs(const char* key, char* value);
        if(env_get_bootargs("androidboot.slot_suffix",slot_suffix))
        {
            strcat(tzbp_partition, slot_suffix);
        }
        else
        {
            printf("%s:%d:%s: Error, there is no bootable slot!\n",__FILE__,__LINE__,__FUNCTION__);
            return 1;
        }
        if ((mtdparts_init() == 0) &&
            (find_dev_and_part(tzbp_partition, &dev, &pnum, &part) == 0))
        {
            printf("## %s: Load tzbp image from partition: %s\n",__FUNCTION__,tzbp_partition);

            tzbp_img_size = part->size;
            tzbp_img_addr = malloc(tzbp_img_size);
            if(tzbp_img_addr == NULL)
            {
                printf("%s:%d:%s: Error, malloc memory fail, size: %d!\n",__FILE__,__LINE__,__FUNCTION__,tzbp_img_size);
                return 1;
            }

            read_size = emmc_read(part->offset, tzbp_img_addr, tzbp_img_size);
            if (read_size != tzbp_img_size)
            {
                printf("%s:%d:%s: MMC Read %s partition error.\n",__FILE__,__LINE__,__FUNCTION__, tzbp_partition);
                free(tzbp_img_addr);
                tzbp_img_addr = NULL;
                return 1;
            }
        }
        else
        {
            printf("%s:%d:%s: Can not find the tzbp partition: %s\n",__FILE__,__LINE__,__FUNCTION__, tzbp_partition);
            free(tzbp_img_addr);
            tzbp_img_addr = NULL;
            return 1;
        }
    }

#ifdef CC_SECURE_BOOT_SCRAMBLE
    extern BOOL GCPU_Init(UINT32 u4Mode);
    extern BOOL GCPU_SetAesKey(UINT8 *pu1Key, KEY_BIT_LEN eKeyBitLen, UINT8 *pu1Iv);
    extern BOOL GCPU_AesDecrypt(UINT32 u4InBufStart, UINT32 u4OutBufStart, UINT32 u4BufSize);

    LDR_ENV_T *prLdrEnv = (LDR_ENV_T *)CC_LDR_ENV_OFFSET;
    uint8_t *tzbp_lzhs_header = malloc(0x1000);
    if(tzbp_lzhs_header == NULL)
    {
        printf("%s:%d:%s: Error, malloc memory fail, size: %d!\n",__FILE__,__LINE__,__FUNCTION__,(0x1000));
        return 1;
    }

    GCPU_Init(0);
    GCPU_SetAesKey(prLdrEnv->aes_key2, KEY_BIT_LEN_128, prLdrEnv->aes_iv2);
    GCPU_AesDecrypt(tzbp_img_addr,tzbp_lzhs_header,0x1000);

    u4TzPlainSize = *((uint32_t *)tzbp_lzhs_header);
    u4TzLzhsSize = *((uint32_t *)(tzbp_lzhs_header + 4));
    u1TzChksum = *((uint8_t *)(tzbp_lzhs_header + 8));

    free(tzbp_lzhs_header);
    tzbp_lzhs_header = NULL;
#else
    u4TzPlainSize = *((uint32_t *)tzbp_img_addr);
    u4TzLzhsSize = *((uint32_t *)(tzbp_img_addr + 4));
    u1TzChksum = *((uint8_t *)(tzbp_img_addr + 8));
#endif

    // Limit size of tz.
    if(u4TzLzhsSize > tzbp_img_size)
    {
        u4TzLzhsSize = tzbp_img_size;
    }

    printf("tzbp: ");
    sig_authetication(tzbp_img_addr, (u4TzLzhsSize + 0x20) & (~0xf),
                      tzbp_img_addr + ((u4TzLzhsSize + 0x20) & (~0xf)), NULL);

#ifdef CC_SECURE_BOOT_SCRAMBLE
    GCPU_Init(0);
#ifdef CC_SECURE_BOOT_V3
    GCPU_AesDecrypt(tzbp_img_addr, tzbp_img_addr, (u4TzLzhsSize + 0x20) & (~0xf));
#else
    GCPU_AesDecrypt(tzbp_img_addr, tzbp_img_addr, (u4TzLzhsSize + 0x1000) & (~0xf));
#endif
#endif

    if(u4TzPlainSize == u4TzLzhsSize)
    {
        memcpy((void *)u4TzLzhsDstDram, (void *)(tzbp_img_addr + 0x10), u4TzPlainSize);
        printf("## Plaintext tzbp.img, src=0x%x,dst=0x%x,psize=0x%x,lsize=0x%x,chk=0x%x\n",
               tzbp_img_addr + 0x10, u4TzLzhsDstDram, u4TzPlainSize, u4TzLzhsSize, u1TzChksum);
    }
    else
    {
        extern INT32 LZHS_Dec(UINT32 u4SrcAddr, UINT32 u4DestAddr, UINT32 u4Len, UINT8 *pu1CheckSum);

        if (LZHS_Dec(tzbp_img_addr + 0x10, u4TzLzhsDstDram, u4TzPlainSize, &u1TzChksum) != 0)
        {
            printf("## TZ LZHS decode fail! src=0x%x,dst=0x%x,psize=0x%x,lsize=0x%x,chk=0x%x\n",
                   tzbp_img_addr + 0x10, u4TzLzhsDstDram, u4TzPlainSize, u4TzLzhsSize, u1TzChksum);
            free(tzbp_img_addr);
            tzbp_img_addr = NULL;
            return 1;
        }
        else
        {
            printf("## TZ LZHS decode success! src=0x%x,dst=0x%x,psize=0x%x,lsize=0x%x,chk=0x%x\n",
                   tzbp_img_addr + 0x10, u4TzLzhsDstDram, u4TzPlainSize, u4TzLzhsSize, u1TzChksum);
        }
    }
    return 0;
}
#endif

static int emmc_load_image(cmd_tbl_t *cmdtp, struct part_info *part, u64 offset, ulong addr, char *cmd, int check)
{
    u32 n;
    char *ep;
    size_t cnt;
    char *local_args[3];
    char local_args_temp[3][0x30];

    image_header_t *hdr;
    struct mmc *mmc;
#if defined(CONFIG_OF_LIBFDT)
    addr = CFG_RD_SRC_ADDR - 0x40;
#endif

    mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
    if (!mmc)
    {
        return 1;
    }

    if (mmc_init(mmc))
    {
        puts("MMC init failed\n");
        return 1;
    }

    printf("\nLoading from mmc%d, offset 0x%llx\n", CONFIG_SYS_MMC_ENV_DEV, offset);

    n = mmc->block_dev.byte_read(CONFIG_SYS_MMC_ENV_DEV, offset, mmc->read_bl_len, (void *)addr);
    if (n != mmc->read_bl_len)
    {
        puts("** Read error\n");
        show_boot_progress(-56);
        return 1;
    }
    show_boot_progress(56);

    switch (genimg_get_format((void *)addr))
    {
    case IMAGE_FORMAT_LEGACY:
        hdr = (image_header_t *)addr;

        show_boot_progress(57);
        image_print_contents(hdr);

        cnt = image_get_image_size(hdr);
        break;

    default:
        show_boot_progress(-57);
        puts("** Unknown image type\n");
        return 1;
    }

    if (cnt > MAX_IMAGE_SIZE)
    {   //size limit of kernel
        cnt = MAX_IMAGE_SIZE;
    }
    cnt = (cnt + 0x10) & (~0xf);
    n = mmc->block_dev.byte_read(CONFIG_SYS_MMC_ENV_DEV, offset, cnt, (void *)addr);
    if (n != cnt)
    {
        puts("** Read error\n");
        show_boot_progress(-58);
        return 1;
    }
    show_boot_progress(58);

#if !(CONFIG_FAST_BOOT) //skip verify for fast boot
    if (((ep = getenv("verify")) != NULL) && (strcmp(ep, "y") == 0))
    {
        puts("   Verifying Checksum ... ");
        if (!image_check_dcrc(hdr))
        {
            printf("Bad Data CRC\n");
            show_boot_progress(-58);
            return NULL;
        }
        puts("OK\n");
    }
#endif

    if (cmd == NULL)
    {
        return 0;
    }

    // verify kernel
    if (check)
        emmc_auth_image(part, addr, 0);

    /* Loading ok, update default load address */
#if defined(CONFIG_OF_LIBFDT)
    load_addr = CFG_RD_SRC_ADDR;
#else
    load_addr = addr;
#endif

    /* Check if we should attempt an auto-start */
    if (((ep = getenv("autostart")) != NULL) &&
        (strcmp(ep, "yes") == 0) &&
        (hdr->ih_type == IH_TYPE_KERNEL))
    {
        local_args[0] = cmd;

#ifdef TIME_TEST
		BIM_SetTimeLog(8); //loader krnel end
		//_DisplayTimePrint();
#endif
        printf("Automatic boot of image at addr 0x%08lx ...\n", addr);

        if ((ep = getenv("initrd")) != NULL)
        {
            local_args[1] = local_args_temp[1];
            local_args[2] = local_args_temp[2];
            addr = CFG_RD_ADDR;
            sprintf(local_args[1], "0x%x", (unsigned int)load_addr);
            sprintf(local_args[2], "0x%x", (unsigned int)addr);
            do_bootm(cmdtp, 0, 3, local_args);
        }
        else
        {
            local_args[1] = NULL;
            local_args[2] = NULL;
            do_bootm(cmdtp, 0, 1, local_args);
        }
        return cnt;
    }
    return 0;
}

int emmc_read_from_partition(unsigned char* buf, char* pname, u64 offset, unsigned int size)
{
    struct mtd_device *dev;
    struct part_info *part;
    u8 pnum;

    if ((mtdparts_init() == 0) && (find_dev_and_part(pname, &dev, &pnum, &part) == 0))
    {
        emmc_read(part->offset+offset, buf, size);
        return 0;
    }

    return 1;
}

int emmc_write_to_partition(unsigned char* buf, char* pname, u64 offset, unsigned int size)
{
    struct mtd_device *dev;
    struct part_info *part;
    u8 pnum;

    if ((mtdparts_init() == 0) && (find_dev_and_part(pname, &dev, &pnum, &part) == 0))
    {
        emmc_write(part->offset+offset, buf, size);
        return 0;
    }

    return 1;
}

/*now from oryx we known, the dma length is enlarged to 32 bits*/
#define EMMC_ASYNC_SIZE (0xffffffff & ~511) //(63*1024)

struct emmc_fill_data
{
    int cmd_data_left;
    int recv_data_left;
};

struct emmc_normal_fill_data
{
    int recv_data_left;
	uint cur_pos;
	u64 cur_off;
	uint cur_bytes;
};

#if !MSDC_USE_NORMAL_READ_LZO
int emmc_fill(void *data)
{
    int size, recv_size, ret;
    struct emmc_fill_data *ef = (struct emmc_fill_data *)data;

    if (!ef->recv_data_left)
        return -1;

    /* Wait for previous block complete. */
    recv_size = size = (ef->recv_data_left < EMMC_ASYNC_SIZE) ? ef->recv_data_left : EMMC_ASYNC_SIZE;
    ret = emmc_async_dma_wait_finish();

    if (ret)
        printf("emmc_async_dma_wait_finish %d ret %d\n", size, ret);

    ef->recv_data_left -= size;

    /* Now send command for next. */
    if (ef->cmd_data_left)
    {
        size = (ef->cmd_data_left < EMMC_ASYNC_SIZE) ? ef->cmd_data_left : EMMC_ASYNC_SIZE;
        ret = emmc_async_dma_start_trigger(size);
        if (ret)
            printf("emmc_async_dma_start_trigger %d ret %d\n", size, ret);
        ef->cmd_data_left -= size;
    }

    return recv_size;
}
#else
int emmc_normal_fill(void *data)
{
    int ret;
	struct emmc_normal_fill_data *ef = (struct emmc_normal_fill_data *)data;
	int n = 0;

    if (!ef->recv_data_left)
        return -1;

    if (ef->recv_data_left) {
		ef->cur_bytes = (ef->recv_data_left < EMMC_ASYNC_SIZE) ? ef->recv_data_left : EMMC_ASYNC_SIZE;
        n = emmc_read(ef->cur_off,ef->cur_pos,ef->cur_bytes);
		if (n != ef->cur_bytes)
			printf("** emmc_normal_fill error, read cnt 0x%x ,cur off 0x%llx \n",n,ef->cur_off);

		ef->cur_pos += ef->cur_bytes;
		ef->cur_off += ef->cur_bytes;
        ef->recv_data_left -= ef->cur_bytes;
    }

    return ef->cur_bytes;
}
#endif
extern int unlzo_read(u8 *input, int in_len, int (*fill) (void *fill_data), void *fill_data, u8 *output, int *posp);

/* Load & decompress LZO image at the same time.
 *
 * @param cnt      Size of compressed image on EMMC.
 * @param src_buf  Output buffer of source compressed image.
 * @param out_buf  Output buffer of decompressed data
 * @param decomp_size  Decompressed data size.
 * @param skip     Number of bytes to skip at start.
 */


static int emmc_read_unlzo(u64 offset, u8 *src_buf, u8 *out_buf, size_t cnt, int *decomp_size, int skip)
{
    int ret, len, dma_size0, dma_size1, rval = 0;
    u32 n = 0;
	*decomp_size = 0; //set default value
#if MSDC_USE_NORMAL_READ_LZO
    struct emmc_normal_fill_data fdata;

    /* align to 512 bytes */

    len = (cnt + 511) & (~511);
    dma_size0 = (len < EMMC_ASYNC_SIZE) ? len : EMMC_ASYNC_SIZE;

    fdata.recv_data_left = len - dma_size0;
    fdata.cur_bytes = dma_size0;
    fdata.cur_pos = src_buf;
    fdata.cur_off = offset;

    printf("\n -- load lzo from mmc%d, offset 0x%llx\n", CONFIG_SYS_MMC_ENV_DEV, offset);

    /* Read first block */
    n = emmc_read(fdata.cur_off, fdata.cur_pos, fdata.cur_bytes);
    if (n != fdata.cur_bytes)
    {
        printf("** load lzo Read error, read cnt 0x%x \n", n);
        rval = 1;
        goto out;
    }

    fdata.cur_pos += fdata.cur_bytes;
    fdata.cur_off += fdata.cur_bytes;

    /* Check for LZO header, return fail if not found. */
    if (*(int *)(src_buf + skip) != 0x4f5a4c89)
    {
        printf("** load lzo data check failed, cur data 0x%x , skip 0x%x \n", *(int *)(src_buf + skip), skip);
        rval = 1;
        goto out;
    }

#else
    struct emmc_fill_data fdata;

    /* align to 512 bytes */
    len = (cnt + 511) & (~511);

    dma_size0 = (len < EMMC_ASYNC_SIZE) ? len : EMMC_ASYNC_SIZE;
    fdata.recv_data_left = fdata.cmd_data_left = len - dma_size0;

    //printf("[Zj] normal recv_data_left 0x%x, dma_size0  0x%x\n", fdata.recv_data_left, dma_size0);
    //printf("[Zj] normal cnt 0x%x, len 0x%x, buf 0x%p, offset 0x%llx, skip 0x%x\n", cnt, len, src_buf, offset, skip);

    ret = emmc_async_read_setup(0, offset, len, src_buf);
    if (ret)
        printf("emmc_async_read_setup done %d\n", ret);

    /* Read first block, must wait for first block */
    emmc_async_dma_start_trigger(dma_size0);
    emmc_async_dma_wait_finish();

    /* Check for LZO header, return fail if not found. */
    if (*(int *)(src_buf + skip) != 0x4f5a4c89)
    {
        printf("** load lzo data check failed, cur data 0x%x  \n", *(int *)(src_buf + skip));
        rval = 1;
        emmc_async_read_stop(0, 0);
        goto out;
    }

    /* Now, read next, don't wait. */
    if (fdata.cmd_data_left)
    {
        dma_size1 = (fdata.cmd_data_left < EMMC_ASYNC_SIZE) ? fdata.cmd_data_left : EMMC_ASYNC_SIZE;
        emmc_async_dma_start_trigger(dma_size1);
        fdata.cmd_data_left -= dma_size1;
    }

#endif
    printf("---start unlzo--ret %d , decomp size 0x%x -\n", rval, (dma_size0 - skip));

#if MSDC_USE_NORMAL_READ_LZO
    ret = unlzo_read(src_buf + skip, dma_size0 - skip, emmc_normal_fill, &fdata, out_buf, decomp_size);
#else
    ret = unlzo_read(src_buf + skip, dma_size0 - skip, emmc_fill, &fdata, out_buf, decomp_size);
#endif
    if (ret)
    {
        printf("block unlzo failed.. %d\n", ret);
        rval = 1;
        goto out;
    }
    printf("\n---end unlzo---\n");

out:
#if !(MSDC_USE_NORMAL_READ_LZO)
    // Send finish command.
    ret = emmc_async_read_finish(0);
    if (ret)
        printf("emmc_async_read_finish done %d\n", ret);
#endif
    return rval;
}

static int emmc_load_image_lzo(cmd_tbl_t *cmdtp, struct part_info *part, char *cmd)
{
    int r;
    ulong addr;
    u64 offset;
    size_t cnt;
    int decomp_size;
    char *local_args[3];

    // Booting UIMAGE + LZO kernel.
    // !!!!!FIXME!!!!! We don't know the size of image, try part size.
    addr = CFG_KERN_ADDR - 0x40;
    cnt = part->size;
    offset = part->offset;
    printf("emmc_read_unlzo enter size(%d)\n", __LINE__);
    r = emmc_read_unlzo(offset, (void *)CFG_RD_SRC_ADDR, (void *)addr, cnt, &decomp_size, 0x40);

#ifdef TIME_TEST
	BIM_SetTimeLog(8); //loader krnel lzo end
	//_DisplayTimePrint();
#endif
    printf("unzlo done. decomp size 0x%x, limit 0x%x \n", decomp_size, CONFIG_SYS_TEXT_BASE);
    if (decomp_size + addr >= CONFIG_SYS_TEXT_BASE)
    {
        printf("\n The decomp kernel size is too large and will overlap with uboot !\n");
        while (1);
    }

    if (!r)
    {
        // verify the kernel.
        emmc_auth_image(part, CFG_RD_SRC_ADDR, 0);

        // Good. Decompress work, boot it
        load_addr = addr;
        local_args[0] = cmd;
        local_args[1] = NULL;
        local_args[2] = NULL;
        do_bootm(cmdtp, 0, 1, local_args);
    }

    // read fail, go back and try normal flow
    return 0;
}

#define WITH_PADDING_SIZE(size, page)    (((size)+(page)-1) & (~((page)-1)))

#ifdef TIME_TEST
typedef struct
{
    UINT32 u4Timer;
    UINT32 u4Val;
    UINT32 u4Offset;
    UINT32 u4AbsOffset;
    CHAR * szString;
}BOOTTIME;
#define MAX_BOOTTIME_ITEM 1024
static UINT32 u4BoottimeItem=0;
static BOOTTIME _arBootTime[MAX_BOOTTIME_ITEM];
void _CmdStoreTimeItem(UINT32 u4Timer, CHAR *szString)
{
   if(u4BoottimeItem>=MAX_BOOTTIME_ITEM)
	{
	Printf("%s %d: StoreBootTime full\n", __FUNCTION__, __LINE__);
	return;
	}
    _arBootTime[u4BoottimeItem].u4Timer=u4Timer;
    _arBootTime[u4BoottimeItem].szString=szString;
	u4BoottimeItem++;
}


void _CmdArrayTime(void)
{
    UINT32 i,j;
    UINT32 u4Timer;
    CHAR * szString;
    UINT32 u4Reason = PDWNC_ReadWakeupReason();

    for(i=0;i<u4BoottimeItem-1;i++)
    {
        for(j=i+1;j<u4BoottimeItem;j++)
        {
            if(_arBootTime[i].u4Timer<=_arBootTime[j].u4Timer)
            {
                u4Timer= _arBootTime[i].u4Timer;
                _arBootTime[i].u4Timer =_arBootTime[j].u4Timer;
                _arBootTime[j].u4Timer =u4Timer;
                szString= _arBootTime[i].szString;
                _arBootTime[i].szString =_arBootTime[j].szString;
                _arBootTime[j].szString =szString;
            }
        }
    }
    for(i=0;i<u4BoottimeItem;i++)
    {
        _arBootTime[i].u4Val=((~_arBootTime[i].u4Timer)/(GET_XTAL_CLK()/1000000));
        if(i>0)
            _arBootTime[i].u4Offset =_arBootTime[i].u4Val-_arBootTime[i-1].u4Val;
        else
            _arBootTime[i].u4Offset =0;

        #define GAP_ITEM 4
        if (u4Reason == PDWNC_WAKE_UP_REASON_AC_POWER || u4Reason == PDWNC_WAKE_UP_REASON_WATCHDOG)
        {
            _arBootTime[i].u4AbsOffset = _arBootTime[i].u4Val;
        }
        else if (i>=GAP_ITEM)
        {
            _arBootTime[i].u4AbsOffset = (_arBootTime[i].u4Val - _arBootTime[GAP_ITEM].u4Val) + _arBootTime[GAP_ITEM-1].u4Val;
        }
        else
        {
            _arBootTime[i].u4AbsOffset = _arBootTime[i].u4Val;
        }
    }
 }

void _CmdDisplayTime(void)
{
    UINT32 i;
    for(i=0;i<u4BoottimeItem;i++)
    {
        Printf("0x%08x | %6d.%03d ms  %6d.%03d ms - %6d.%03d ms %s\n",(unsigned int)_arBootTime[i].u4Timer, (int)(_arBootTime[i].u4Val / 1000), (int)(_arBootTime[i].u4Val % 1000), (int)(_arBootTime[i].u4Offset/ 1000), (int)(_arBootTime[i].u4Offset% 1000),(int)(_arBootTime[i].u4AbsOffset/ 1000), (int)(_arBootTime[i].u4AbsOffset% 1000), _arBootTime[i].szString);
    }
}

void _DisplayTimePrint(void)
{

	UINT32 *pu4Time;
	UINT32 i, u4Size;
	TIME_STAMP_T *prTimeStamp;
	pu4Time = (UINT32 *)0xf00080e0;
	u4BoottimeItem=0;
	_CmdStoreTimeItem(*pu4Time, "[0]Board_init_r start point");
	pu4Time++;
	_CmdStoreTimeItem(*pu4Time, "[1]MMU,caches,board_init,mem_malloc_init");
	pu4Time++;
	_CmdStoreTimeItem(*pu4Time, "[2]emmc_init");
	pu4Time++;
	_CmdStoreTimeItem(*pu4Time, "[3]ENV initialize");
	pu4Time++;
	_CmdStoreTimeItem(*pu4Time, "[4]init devices,Console_init_r:Serial init");
	pu4Time++;
	_CmdStoreTimeItem(*pu4Time, "[5]bootmode/dual bank init");
	pu4Time++;
	_CmdStoreTimeItem(*pu4Time, "[6]loadaddr,before go to Main_loop()");
	pu4Time++;
	_CmdStoreTimeItem(*pu4Time, "[7]load kernel/lzo start");
	pu4Time++;
	_CmdStoreTimeItem(*pu4Time, "[8]load kernel/lzo end");
	pu4Time++;

	_CmdArrayTime();
	_CmdDisplayTime();
}

#endif

#ifdef CC_SUPPORT_AVB
extern void check_android_boot_state(AvbSlotVerifyData *avb_data, AvbABFlowResult avb_ab_flow_result, unsigned int os_version);

int do_emmcboot(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    int iRet;

    // for Test.
    if (getenv("test_ice") != NULL)
    {
        while(1);
    }

    if(getenv("squashfs_size") == NULL)
    {
        char env_temp[64];
        sprintf(env_temp, "0x%x", sizeof(struct boot_img_hdr));
        setenv("squashfs_size", env_temp);
    }

    #if defined(CONFIG_OF_LIBFDT)
    if(getenv("bootm_size") == NULL)
    {
        char env_temp[64];
        sprintf(env_temp, "0x%x", 100 * 1024 * 1024);
        setenv("bootm_size", env_temp);
    }
    #endif

    if(argc == 2)
    {
        #ifdef CONFIG_USB_FASTBOOT
        if(strcmp(argv[1],"fastboot") == 0)
        {
            unsigned char boot_cmd[32] = "fastboot";

            printf("\n%s:%d:%s: reboot to fastboot mode!!\n\n",__FILE__,__LINE__,__FUNCTION__);

            emmc_write_to_partition(boot_cmd, "misc", 512, sizeof(boot_cmd));
            do_reset(NULL,NULL,NULL,NULL);
            while(1);
        }
        #endif

        if(strcmp(argv[1],"boot") == 0 || strcmp(argv[1],"recovery") == 0)
        {
            // android boot
            struct andr_img_hdr *boot_hdr;
            uint8_t *boot_img_addr = NULL, *kernel_addr = NULL, *ramdisk_addr = NULL;
            int i, boot_img_size = 0;
            char *local_args[3];
            char local_args_temp[3][16];
            char env_temp[64];

            // android verify boot 2.0
            AvbABFlowResult avb_ab_flow_result;
            AvbSlotVerifyData *avb_data = NULL;
            #ifdef CC_SUPPORT_DTO
            uint8_t *dtbo_img_addr = NULL;
            int dtbo_img_size = 0;
            #endif
            #if defined(CC_SUPPORT_AB_UPDATE) && (defined(CC_TRUSTZONE_SUPPORT) || defined(CC_TRUSTZONE_64_SUPPORT))
            uint8_t *tzbp_img_addr = NULL;
            int tzbp_img_size = 0;
            #endif
            const char *avb_ab_partitions[] = { "boot",
            #ifdef CC_SUPPORT_DTO
                                                "dtbo",
            #endif
            #if defined(CC_SUPPORT_AB_UPDATE) && (defined(CC_TRUSTZONE_SUPPORT) || defined(CC_TRUSTZONE_64_SUPPORT))
                                                "tzbp",
            #endif
                                                NULL};

            printf("\n###### Android Verify Boot 2.0 ######\n\n");

            avb_ops_uboot_init();

            // read resetStoredRollbackIndex flag and save
            avb_read_flag_reset_StoredRBI();

            #ifdef  CC_ANDROID_SECURE_BOOT_CLASS_B
            avb_ab_flow_result = avb_ab_flow(   avb_get_ab_ops(),
                                                avb_ab_partitions,
                                                AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,
                                                AVB_HASHTREE_ERROR_MODE_RESTART,
                                                &avb_data);
            #else
            avb_ab_flow_result = avb_ab_flow(   avb_get_ab_ops(),
                                                avb_ab_partitions,
                                                AVB_SLOT_VERIFY_FLAGS_NONE,
                                                AVB_HASHTREE_ERROR_MODE_RESTART,
                                                &avb_data);
            #endif
#ifdef CC_SONY_FILE_BASED_RECIPE_SUPPORT_BY_LOADER
            if(avb_ab_flow_result == AVB_AB_FLOW_RESULT_OK_WITH_UPDATED_INDEX)
            {
                printf("\n%s:%d:%s: reboot to reload slot!!\n\n",__FILE__,__LINE__,__FUNCTION__);
                do_reset(NULL,NULL,NULL,NULL);
                while(1);
            }
            else
#endif
#ifdef CC_UBOOT_AUTO_TEST_IGNORE_AVB
            if(1)
            {
                int original_result = avb_ab_flow_result == AVB_AB_FLOW_RESULT_OK
                || avb_ab_flow_result == AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR;
                printf("Ignore AB verity flow result[%s] for AUTO_TEST!!\n", original_result?"PASS":"FAIL");
#else
            if(avb_ab_flow_result == AVB_AB_FLOW_RESULT_OK
                || avb_ab_flow_result == AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR)
            {
#endif//CC_UBOOT_AUTO_TEST_IGNORE_AVB
                extern void env_add_boot_skip_initramfs(void);
                extern void env_add_boot_slot_suffix(char* slot_suffix);
                extern void env_add_boot_avb_cmdline(char* avb_cmdline);

                printf("\nSystem will boot from slot: %s\n",avb_data->ab_suffix);

                if (strcmp(avb_data->ab_suffix, "_b") == 0)
                {
                    extern unsigned int avb_slot_number;
                    avb_slot_number = 1;
                }

                // append command line.
                if(strcmp(argv[1],"boot") == 0)
                {
                    env_add_boot_skip_initramfs();
                }
                env_add_boot_slot_suffix(avb_data->ab_suffix);
                env_add_boot_avb_cmdline(avb_data->cmdline);

                //get boot image loaded address.
                for(i=0; i < avb_data->num_loaded_partitions; i++)
                {
                    if(strcmp("boot", avb_data->loaded_partitions[i].partition_name) == 0)
                    {
                        boot_img_addr = avb_data->loaded_partitions[i].data;
                        boot_img_size = avb_data->loaded_partitions[i].data_size;
                        break;
                    }
                }

                if(boot_img_addr)
                {
                    boot_hdr = (struct andr_img_hdr *)boot_img_addr;

                    printf("Boot/Recovery image header version: %d\n\n", boot_hdr->header_version);

                    if(boot_hdr->header_version) {
                        kernel_addr = boot_img_addr + WITH_PADDING_SIZE(sizeof(struct andr_img_hdr), boot_hdr->page_size);
                    } else {
                        kernel_addr = boot_img_addr + WITH_PADDING_SIZE(sizeof(struct andr_img_hdr_legacy), boot_hdr->page_size);
                    }
                    ramdisk_addr = kernel_addr + WITH_PADDING_SIZE(boot_hdr->kernel_size, boot_hdr->page_size);

                    // config ramdisk.
                    sprintf(env_temp, "0x%x", ramdisk_addr);
                    setenv("initrd", env_temp);
                    sprintf(env_temp, "0x%x", ramdisk_addr + boot_hdr->ramdisk_size);
                    setenv("initrd_end", env_temp);
                }

                check_android_boot_state(avb_data, avb_ab_flow_result, (boot_hdr == NULL ? 0 : boot_hdr->os_version));

                #ifdef CC_SUPPORT_DTO
                //get dtbo image loaded address.
                for(i=0; i < avb_data->num_loaded_partitions; i++)
                {
                    if(strcmp("dtbo", avb_data->loaded_partitions[i].partition_name) == 0)
                    {
                        dtbo_img_addr = avb_data->loaded_partitions[i].data;
                        dtbo_img_size = avb_data->loaded_partitions[i].data_size;
                        break;
                    }
                }
                // load dtbo.img
                emmc_load_dtbo(dtbo_img_addr, dtbo_img_size);
                #endif

                #if defined(CC_SUPPORT_AB_UPDATE) && (defined(CC_TRUSTZONE_SUPPORT) || defined(CC_TRUSTZONE_64_SUPPORT))
                //get tzbp image loaded address.
                for(i=0; i < avb_data->num_loaded_partitions; i++)
                {
                    if(strcmp("tzbp", avb_data->loaded_partitions[i].partition_name) == 0)
                    {
                        tzbp_img_addr = avb_data->loaded_partitions[i].data;
                        tzbp_img_size = avb_data->loaded_partitions[i].data_size;
                        break;
                    }
                }
                // load tzbp.img
                emmc_load_tzbp(tzbp_img_addr,tzbp_img_size);
                #endif

                // launch kernel.
                local_args[1] = local_args_temp[1];
                local_args[2] = local_args_temp[2];
                sprintf(local_args[1], "0x%x", kernel_addr);
                sprintf(local_args[2], "0x%x", ramdisk_addr);
                iRet = do_bootm(cmdtp, 0, 3, local_args);
            }
            else
            {
                //red state.
                check_android_boot_state(avb_data, avb_ab_flow_result, 0);
                iRet = 1;
            }
        }
        else
        {
            printf("%s:%d:%s: invalid boot command !\n",__FILE__,__LINE__,__FUNCTION__);
            iRet = 1;
        }
    }
    else
    {
        printf("%s:%d:%s: invalid boot param number !\n",__FILE__,__LINE__,__FUNCTION__);
        iRet = 1;
    }

usage:
    printf("%s:%d: Usage: %s\n\n", __FILE__,__LINE__,cmdtp->usage);
    return iRet;
}
#else
int do_emmcboot(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    u8 pnum;
    char *tmp, env[64];
    ulong addr;
    u64 rootfsoffset = 0;

    struct mtd_device *dev;
    struct part_info *part;
#if defined(CONFIG_OF_LIBFDT)

    if (getenv("bootm_size") == NULL)
    {

        char buf[20];
        sprintf(buf, "0x%x", 100 * 1024 * 1024);
        setenv("bootm_size", buf);
    }
#endif

    if (getenv("test_ice") != NULL)
        while(1);

    if (argc >= 2)
    {
#ifdef CONFIG_USB_FASTBOOT
        if(argc == 2 && strcmp(argv[1],"fastboot") == 0)
        {
            unsigned char boot_cmd[32] = "fastboot";

            printf("\n## %s: reboot to fastboot mode !!\n\n",__FUNCTION__);

            emmc_write_to_partition(boot_cmd, "misc", 512, sizeof(boot_cmd));
            do_reset(NULL,NULL,NULL,NULL);
            while(1);
        }
#endif
        if ((mtdparts_init() == 0) &&
            (find_dev_and_part(argv[1], &dev, &pnum, &part) == 0))
        {
            // Fix kernel address at 0x8000 to avoid from reallocation time
            addr = CFG_KERN_ADDR;

            if (dev->id->type != MTD_DEV_TYPE_EMMC)
            {
                puts("Not a EMMC device\n");
                return 1;
            }

            if (argc > 3)
            {
                goto usage;
            }

            if (argc == 3)
            {
                if ((strcmp(argv[2], "rootfs") == 0) ||
                    (strcmp(argv[2], "rootfsA") == 0) ||
                    (strcmp(argv[2], "rootfsB") == 0) ||
                    (strcmp(argv[2], "linux_rootfsA") == 0) ||
                    (strcmp(argv[2], "linux_rootfsB") == 0))
                {
                    u8 part_num;
                    u64 part_size, part_offset;

                    if (find_partition(argv[2], &part_num, &part_size, &part_offset) != 0)
                    {
                        printf("\n** Ramdisk partition %s not found!\n", argv[2]);
                        show_boot_progress(-55);
                        return 1;
                    }

                    rootfsoffset = part_offset;
                }
                else
                {
                    goto usage;
                }
            }
            else if (argc == 2)
            {
                // android boot
                char *local_args[3];
                char local_args_temp[3][16];
                char env[32];
                struct boot_img_hdr hdr;
                int magic_found, ret;
                int cnt, decomp_size;
                u64 offset;

                // 1. load header
                sprintf(env, "0x%x", sizeof(boot_img_hdr));
                setenv("squashfs_size", env);
                offset = part->offset;
                cnt = 2048;
                emmc_read(offset, (void *)CFG_RD_ADDR, cnt);
                memcpy((void *)&hdr, (void *)CFG_RD_ADDR, sizeof(boot_img_hdr));
                magic_found = !strncmp((char *)hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);

                if (magic_found && strstr(argv[0], ".lzo"))
                {
                    // 2. load kernel
                    offset += hdr.page_size;
                    addr = CFG_KERN_ADDR - 0x40;
                    cnt = WITH_PADDING_SIZE(hdr.kernel_size, hdr.page_size);
                    if (cnt > MAX_IMAGE_SIZE)
                    {   //limit size of kernel
                        cnt = MAX_IMAGE_SIZE;
                    }
                    printf("emmc_read_unlzo enter size(%d)\n", __LINE__);
                    ret = emmc_read_unlzo(offset, (void *)CFG_RD_SRC_ADDR, (void *)addr, cnt, &decomp_size, 0x40);

                    if (decomp_size + addr >= 0x1000000)
                    {
                        printf("\n The decomp kernel size is too large and will overlap with uboot !\n");
                        while (1);
                    }
                    if (ret)
                    {
                        // LZO read fail, read as normal uImage.
                        addr = CFG_KERN_ADDR;
                        emmc_read(offset, (void *)addr, WITH_PADDING_SIZE(hdr.kernel_size, hdr.page_size));
                    }

                    // 3. verify the kernel.
                    emmc_auth_image(part, CFG_RD_SRC_ADDR, hdr.page_size);

                    // 4. load ramdisk
                    offset += WITH_PADDING_SIZE(hdr.kernel_size, hdr.page_size);
                    cnt = hdr.ramdisk_size;
                    if (cnt > MAX_IMAGE_SIZE)
                    {   //limit size of ramdisk
                        cnt = MAX_IMAGE_SIZE;
                    }
                    printf("emmc_read_unlzo enter size(%d)\n", __LINE__);
                    emmc_read_unlzo(offset, (void *)CFG_RD_SRC_ADDR, (void *)CFG_RD_ADDR, cnt, &decomp_size, 0);

                    // 5. verify the ramdisk.
                    emmc_auth_image_ex(part, CFG_RD_SRC_ADDR, hdr.ramdisk_size, 256);

                    sprintf(env, "0x%x", CFG_RD_ADDR);
                    setenv("initrd", env);
                    sprintf(env, "0x%x", CFG_RD_ADDR + decomp_size);
                    setenv("initrd_end", env);

                    // 6. launch kernel
                    local_args[1] = local_args_temp[1];
                    local_args[2] = local_args_temp[2];
                    sprintf(local_args[1], "0x%x", addr);
                    sprintf(local_args[2], "0x%x", CFG_RD_ADDR);
                    return do_bootm(cmdtp, 0, 3, local_args);
                }
#ifdef CONFIG_ANDROID_SIGNATURE
                else if (magic_found)
                {
                    printf("bootimg: ");

                    #if defined(CONFIG_OF_LIBFDT)
                    addr = CFG_RD_SRC_ADDR;
                    #endif

                    // 1. load boot.img
                    addr -= hdr.page_size;
                    cnt = WITH_PADDING_SIZE(hdr.kernel_size, hdr.page_size) + hdr.ramdisk_size + 3 * hdr.page_size;
                    if (cnt > MAX_IMAGE_SIZE)
                    {   //limit size of boot.img
                        cnt = MAX_IMAGE_SIZE;
                    }
                    emmc_read(offset, (void *)addr, cnt);

                    #if 1
                    if (getenv("test_verify") != NULL)
                        memcpy(addr + 0x100000, CFG_RD_ADDR, 0x100); // test for verifyboot it's work well
                    #endif
                    // 2. verify boot.img
                    android_image_verify(addr);

                    // 3. copy ramdisk
                    addr += hdr.page_size;
                    memcpy(CFG_RD_ADDR, addr + WITH_PADDING_SIZE(hdr.kernel_size, hdr.page_size), hdr.ramdisk_size);

                    sprintf(env, "0x%x", CFG_RD_ADDR);
                    setenv("initrd", env);
                    sprintf(env, "0x%x", CFG_RD_ADDR + hdr.ramdisk_size);
                    setenv("initrd_end", env);

                    #ifdef CC_SUPPORT_DTO
                    // load dtbo.img
                    emmc_load_dtbo(NULL,0);
                    #endif

                    // 4. launch kernel
                    local_args[1] = local_args_temp[1];
                    local_args[2] = local_args_temp[2];
                    sprintf(local_args[1], "0x%x", addr);
                    sprintf(local_args[2], "0x%x", CFG_RD_ADDR);
                    return do_bootm(cmdtp, 0, 3, local_args);
                }
#else
                else if (magic_found)
                {
                    printf("MINIGZIP:\n");

                    #if defined(CONFIG_OF_LIBFDT)
                    addr = CFG_RD_SRC_ADDR;
                    #endif
                    // 2. load kernel
                    offset += hdr.page_size;
                    cnt = WITH_PADDING_SIZE(hdr.kernel_size, hdr.page_size) + 0x100;
                    if (cnt > MAX_IMAGE_SIZE)
                    {   //limit size of kernel
                        cnt = MAX_IMAGE_SIZE;
                    }
                    emmc_read(offset, (void *)addr, cnt);

                    // 3. verify the kernel.
                    //emmc_auth_image(part, addr, hdr.page_size);
                    emmc_auth_boot_image(part, addr, hdr.page_size, hdr.kernel_size - 0x100);

                    // 4. load ramdisk
                    offset += WITH_PADDING_SIZE(hdr.kernel_size, hdr.page_size);
                    cnt = hdr.ramdisk_size + 0x100;
                    if (cnt > MAX_IMAGE_SIZE)
                    {   //limit size of ramdisk
                        cnt = MAX_IMAGE_SIZE;
                    }
                    emmc_read(offset, (void *)CFG_RD_ADDR, cnt);
                    // 5. verify the ramdisk.
                    emmc_auth_image_ex(part, CFG_RD_ADDR, hdr.ramdisk_size, 256);

                    sprintf(env, "0x%x", CFG_RD_ADDR);
                    setenv("initrd", env);
                    sprintf(env, "0x%x", CFG_RD_ADDR + hdr.ramdisk_size);
                    setenv("initrd_end", env);

                    // 4. launch kernel
                    local_args[1] = local_args_temp[1];
                    local_args[2] = local_args_temp[2];
                    sprintf(local_args[1], "0x%x", addr);
                    sprintf(local_args[2], "0x%x", CFG_RD_ADDR);
                    return do_bootm(cmdtp, 0, 3, local_args);
                }
#endif
                else
                {
                    printf("\n no valid boot image!\n");
                    show_boot_progress(-55);
                    return 1;
                }
            }
#ifdef TIME_TEST
            BIM_SetTimeLog(7);
#endif
            // Load rootfs
            if (((tmp = getenv("ramdisk")) != NULL) && (strcmp(tmp, "yes") == 0))
            {
                emmc_load_image(cmdtp, part, rootfsoffset, CFG_RD_ADDR, argv[0], 0);

                sprintf(env, "0x%x", CFG_RD_ADDR);
                setenv("initrd", env);
            }
            else
            {
                setenv("initrd", NULL);
            }

            // Loading .lzo kernel if specified.
            tmp = strchr(argv[0], '.');
            if (tmp && !strcmp(tmp, ".lzo"))
                emmc_load_image_lzo(cmdtp, part, argv[0]);

            // Load kernel
            return emmc_load_image(cmdtp, part, part->offset, addr, argv[0], 1);
        }
    }
    return 0;

usage:
    printf("Usage:\n%s\n", cmdtp->usage);
    show_boot_progress(-53);
    return 1;
}
#endif



U_BOOT_CMD(eboot, 3, 1, do_emmcboot,
    "eboot   - boot from EMMC device\n",
    "[.lzo] [partition] | [[[loadAddr] dev] offset]\n");

#endif // CC_EMMC_BOOT
