// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright 2019
 * Broadcom Corp
 */

#include <stdlib.h>
#include <command.h>
#include <common.h>
#include <environment.h>
#include <hexdump.h>
#include <ubi_uboot.h>
#include <cli.h>
#include <console.h>
#include "bca_common.h"
#include "bcm_bootstate.h"
#include "spl_env.h"
#include "bca_sdk.h"
#include "bcm_secure.h"
#include "httpd/bcmbca_net.h"
#include <nand.h>
#include <mmc.h>
#if defined(CONFIG_WDT)
#include <wdt.h>
#endif
#if defined(CONFIG_BCMBCA_PMC)
#include "pmc_drv.h"
#endif

#define SIZE_OVERHEAD		0x100000UL	//1M
#define BOOTFS_SIZE_BYTES	0x00A00000UL	//10M
#define ROOTFS_SIZE_BYTES	0x02800000UL	//40M
#define SQUASHFS_MAGIC		0x73717368
#define UBIFS_MAGIC		0x06101831
#define MIN_IMG_INDEX		1
#define MAX_IMG_INDEX		2

#define atoi(s) simple_strtol(s, NULL, 0)

DECLARE_GLOBAL_DATA_PTR;

/* Flag indicating whether we want to force an update and disregard any compatibility checks */
static int forced_updates = 0;
static char forced_image_media[256] = {0};
static char forced_boot_media[256] = {0};
/* Flag to prevent synching of runtime env to loader bin when flashing a new loader */
static int disable_runtime_env_sync = 0;

typedef int (*flash_fn)( ulong addr, ulong size , int img_index);

typedef struct {
	char name[20]; /* Name of upgrade bundle component */
	flash_fn func; /* Function to flash this upgrade bundle component */
} flashfn_table_entry;

/* Enable CLI commands based on included CMD modules */
#if defined(CONFIG_CMD_GPT) && defined(CONFIG_CMD_PART) && defined(CONFIG_CMD_MMC) && defined(CONFIG_MMC)
#define BCA_SDK_EMMC_CMD	1
#endif

#if defined(CONFIG_CMD_UBI) && defined(CONFIG_CMD_MTD) && defined(CONFIG_NAND)
#define BCA_SDK_NAND_CMD	1
#endif

#if defined(CONFIG_CMD_SF) && defined(CONFIG_SPI_FLASH) && defined(CONFIG_DM_SPI_FLASH) && defined(CONFIG_CMD_MTD)
#define BCA_SDK_SPINOR_CMD	1
#endif

#if defined(BCA_SDK_SPINOR_CMD)
static int spinor_load_bootfs(uint32_t bootfs_load_addr ); 
static int spinor_restoredefault(void);
static int write_spinor_partition( char *const partitionname, ulong addr, ulong size );
static int do_flash_spinor_binary(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
static int do_flash_spinor_bootfs_rootfs(cmd_tbl_t * cmdtp, int flag, int argc,	char *const argv[]);
static int flash_loader_spinor( ulong addr, ulong size);
#endif
#ifdef BCA_SDK_EMMC_CMD
/* EMMC specific routines */
static int flash_rootfs_emmc( ulong addr, ulong size, int img_index );
static int flash_bootfs_emmc( ulong addr, ulong size, int img_index );
static int flash_loader_emmc( ulong addr, ulong size, int img_index );
static int set_emmc_metadata( char* metadata, int size );
static int get_emmc_metadata( char* metadata, int size );
static int emmc_load_bootfs( int img_index, uint32_t bootfs_load_addr ); 
static int emmc_rdwr_userdata_part( char * part_name, ulong addr, ulong size, int write );
static int emmc_rdwr_boot_part( ulong addr, ulong size, int img_index, int write );
static int emmc_restoredefault(void);
static int emmc_do_gpt_fixup(void);
#endif /* BCA_SDK_EMMC_CMD */

#ifdef BCA_SDK_NAND_CMD
/* NAND Specific routines */
static int flash_rootfs_nand( ulong addr, ulong size, int img_index );
static int flash_bootfs_nand( ulong addr, ulong size, int img_index );
static int flash_loader_nand( ulong addr, ulong size, int img_index );
static int set_nand_metadata( char* metadata, int size );
static int get_nand_metadata( char* metadata, int size );
static int nand_load_fit( int img_index, uint32_t fit_load_addr );
static int nand_load_bootfs( int img_index, uint32_t bootfs_load_addr );
static int nand_restoredefault(void);
#endif /* BCA_SDK_NAND_CMD */

/* Generic routines */
static unsigned int bcm_handle_mapper(void* fit, char *flash_device, char *flash_opts);
static int update_flash_parts_from_loader_bin( ulong loader_addr, ulong loader_size );
static int get_binary_from_bundle( ulong bundle_addr, char * conf_name, char * name, 
	char ** bin_name, ulong * addr, ulong * size );
static int sync_update_loader_bin_env(ulong loader_addr);
static int verify_compat_string( const char * compat_str );
static int do_flash_bins(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
static int do_boot(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
static int do_load(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
static int load_linux_img(int flag, int argc, char *const argv[]);
static int do_restoredefault(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
static int do_metadata(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
static int do_force(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
static int flash_rootfs( ulong addr, ulong size , int img_index);
static int flash_bootfs( ulong addr, ulong size , int img_index);
static int flash_loader( ulong addr, ulong size , int img_index);
static int do_flash_loader(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
static int do_flash_bootfs_rootfs( char * bootfs_filename, char * rootfs_filename, int image_index );
static int do_flash_upgrade_img (cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
static int set_metadata_val( int * committed, int * valid, int * seq );
static int get_metadata_val( int * committed, int * valid, int * seq );
static int do_activate(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
char * get_loader_media(void);
static char * get_image_media(void);
static int get_active_img_idx(void);
static int set_active_img_idx( int img_idx);
#ifdef CONFIG_BCMBCA_HTTPD
static int do_httpd_start(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
#endif
#ifdef CONFIG_BCMBCA_XRDP_ETH
static int do_eth_active_port(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[]);
#endif

/* Local Variables */
static flashfn_table_entry fn_table[] =  {
	{ "bootfs", flash_bootfs },
	{ "rootfs", flash_rootfs },
	{ "loader", flash_loader }
};

#ifdef CONFIG_BCMBCA_BOARD_SPECIFIC_DT
#define BCMBCA_BOARDID_MAXSIZE	28
#define BCMBCA_VOICEBOARDID_MAXSIZE	BCMBCA_BOARDID_MAXSIZE
#include "spl_ddrinit.h"
static int on_boardid(const char *name, const char *value, enum env_op op, int flags)
{
	char boardid[BCMBCA_BOARDID_MAXSIZE+4];
	int ret = 0;
	int img_index = 0;
	long conf, node, offset;
	uint32_t env_mcb;

	if (forced_updates || ((flags & H_INTERACTIVE) == 0))
		return 0;

	switch (op) {
	case env_op_create:
	case env_op_overwrite:
		img_index = get_active_img_idx();
		if((img_index < MIN_IMG_INDEX) || (img_index > MAX_IMG_INDEX)) 
			img_index = MIN_IMG_INDEX;
		/* Load bootfs to load address */
		if(  strcasecmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 )  {
#ifdef BCA_SDK_NAND_CMD
			if (nand_load_fit(img_index, load_addr))
			{
				printf("Failed to load bootfs\n");
				return 0;
			}
#endif
		}

		if( ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) {
#ifdef BCA_SDK_EMMC_CMD
			ret = emmc_load_bootfs(img_index, load_addr);
			if (ret)
			{
				printf("Failed to load bootfs\n");
				return 0;
			}
#endif
		}
    
		conf = fdt_path_offset((void*)load_addr, FIT_IMAGES_PATH);
		if (conf < 0) {
			printf("Cannot find /images node: %ld\n", conf);
			return 0;
		}

		if (strlen(value) > BCMBCA_BOARDID_MAXSIZE)
		{
			printf("boardid is too long, max size is %d\n", BCMBCA_BOARDID_MAXSIZE);
			return 1;
		}
		sprintf(boardid,"fdt_%s", value);
		for (node = fdt_first_subnode((const void*)load_addr, conf); node >= 0; node = fdt_next_subnode((const void*)load_addr, node))
		{
			const char* image_name = fit_get_name((const char*)load_addr, node, NULL);
     
			if (strcmp(image_name, boardid) == 0)
			{  
				const uint32_t* memcfg;
				uint32_t fdt_mcb;
				const void* fdt;
				size_t size;
  
				ret = fit_image_get_data_and_size((const void*)load_addr, node, &fdt, &size);
				if (ret)
				{
					printf("Failed to get FDT\n");
					return 0;
				}
             
				offset = fdt_path_offset(fdt, "/memory_controller");
				if (offset < 0) {
					printf("Not found memory_controller node in FDT\n");
					return 0;
				}                

				memcfg = fdt_getprop(fdt, offset, "memcfg", NULL);
				if (memcfg == NULL)
				{
					printf("Can't find memcfg parameter in DTB\n");
					return 0;
				}
				fdt_mcb = be32_to_cpu(*memcfg);
				env_mcb = env_get_hex("MCB", 0);
				if (env_mcb & BP_DDR_CONFIG_OVERRIDE)
				{
					printf("MCB sticky bit is set, MCB is not updated\n Current MCB 0x%x, FDT MCB 0x%x\n", env_mcb, fdt_mcb);
					return 0;
				}
				printf("Updating MCB environment from 0x%x to 0x%x\n", env_mcb, fdt_mcb);
				env_set_hex("MCB", fdt_mcb);
				env_set("boardid", value);
				printf("Memory Configuration Changed -- SAVING ENV AND REBOOT NEEDED\n");				
				return 0;
			}
		}
		printf("Error: boardid %s not supported.\nList of supported boards:\n", value);
		for (node = fdt_first_subnode((const void*)load_addr, conf); node >= 0; node = fdt_next_subnode((const void*)load_addr, node))
		{
			char* name = fit_get_name((const char*)load_addr, node, NULL);
			if ((strncmp("fdt_", name, 4) == 0) && (strcmp("fdt_uboot", name)))
				printf("%s\n", (name+4));
		}
		return 1;
	case env_op_delete:
		if (flags & H_FORCE)
			return 0;

		printf("## boardid may not be deleted\n");
		return 1;
	default:
		return 0;
	}
}
U_BOOT_ENV_CALLBACK(boardid, on_boardid);

static int on_voiceBoardid(const char *name, const char *value, enum env_op op, int flags)
{
	char boardid[BCMBCA_BOARDID_MAXSIZE+4];
	int ret = 0;
	int img_index = 0;
	long conf, node, offset;
	char *pboardid;

	if (forced_updates || ((flags & H_INTERACTIVE) == 0))
		return 0;

	pboardid = env_get("boardid");
	if (!pboardid)
	{
		printf("boardId must be set first.\n");
		return 1;
	}

	switch (op) {
	case env_op_create:
	case env_op_overwrite:
		img_index = get_active_img_idx();
		if((img_index < MIN_IMG_INDEX) || (img_index > MAX_IMG_INDEX)) 
			img_index = MIN_IMG_INDEX;
		/* Load bootfs to load address */
		if(  strcasecmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 )  {
#ifdef BCA_SDK_NAND_CMD
			if (nand_load_fit(img_index, load_addr))
			{
				printf("Failed to load bootfs\n");
				return 0;
			}
#endif
		}

		if( ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) {
#ifdef BCA_SDK_EMMC_CMD
			ret = emmc_load_bootfs(img_index, load_addr);
			if (ret)
			{
				printf("Failed to load bootfs\n");
				return 0;
			}
#endif
		}

		conf = fdt_path_offset((void*)load_addr, FIT_IMAGES_PATH);
		if (conf < 0) {
			printf("Cannot find /images node: %ld\n", conf);
			return 0;
		}

		if (strlen(value) > BCMBCA_VOICEBOARDID_MAXSIZE)
		{
			printf("voiceboardid is too long, max size is %d\n", BCMBCA_VOICEBOARDID_MAXSIZE);
			return 1;
		}
		sprintf(boardid,"fdt_%s", pboardid);
		for (node = fdt_first_subnode((const void*)load_addr, conf); node >= 0; node = fdt_next_subnode((const void*)load_addr, node))
		{
			const char* image_name = fit_get_name((const char*)load_addr, node, NULL);

			if (strcmp(image_name, boardid) == 0)
			{
				size_t size;
				const void* fdt;
				int i, len, idx, slicCount;
				const char *sliclist;

				ret = fit_image_get_data_and_size((const void*)load_addr, node, &fdt, &size);
				if (ret)
				{
					printf("Failed to get FDT\n");
					return 0;
				}

				offset = fdt_path_offset(fdt, "/bcm_voice");
				if (offset < 0) {
					printf("Not found voice node in FDT\n");
					return 0;
				}                
				idx = fdt_stringlist_search(fdt, offset, "sliclist", value);
				/* is the string is found, save it and exit */
				if (0 <= idx)
				{
					env_set("voiceboardid", value);
					printf("-- saving env and reboot needeD\n");				
					return 0;
				}
				printf("%d: %s is not found. idx=%d\n", __LINE__, value, idx);
				slicCount = fdt_stringlist_count(fdt, offset, "sliclist");
				printf("slicCount=%d. List of supported daughter cards:\n", slicCount);
				for (i=0; i < slicCount; i++)
				{
					sliclist = fdt_stringlist_get(fdt, offset, "sliclist", i, &len);
					if (sliclist)
					   printf("%s\n", sliclist);
					else
					   printf("%d: fdt_stringlist_get failed. len=%d\n", __LINE__, len);
				}
				return 1;
			}
		}
		return 1;
	case env_op_delete:
		env_set("voiceboardid", "");
		printf("-- Saving env and reboot needed\n");				
		return 0;
	default:
		return 0;
	}
}
U_BOOT_ENV_CALLBACK( voiceboardid, on_voiceBoardid);
#endif

static int get_active_img_idx( void )
{
	int node,len;
	unsigned int active_img_idx = 0;
	fdt32_t * nodep = NULL;
	node = fdt_path_offset(gd->fdt_blob, "/chosen");
	if (node < 0) {
		printf("Can't find /chosen node in uboot DTB\n");
		return node;
	}
	nodep = (fdt32_t *)fdt_getprop(gd->fdt_blob, node, "active_image", &len);
	active_img_idx = fdt32_to_cpu(*(fdt32_t *)nodep);
	
	return active_img_idx;
}

static int set_active_img_idx( int img_idx )
{
	int node,len;
	fdt32_t * nodep = NULL;
	node = fdt_path_offset(gd->fdt_blob, "/chosen");
	int ret = -1;
	if (node < 0) {
		printf("Can't find /chosen node in uboot DTB, device may not boot properly!\n");
	} else {
		ret = fdt_setprop_u32(gd->fdt_blob, node, "active_image", img_idx);
		if(ret)
		{
			printf("Could not set active image node in the fdt, device may not boot properly!\n");
		}
	}
	return ret;
}

/************************************************************
 *                Flash Specific Functions                  *
 ************************************************************/
#ifdef BCA_SDK_EMMC_CMD
static int emmc_restoredefault(void)
{
	ulong part_size_blk, part_start_blk;
	char cmd[128];
	int ret = -1;

	/* Switch to user data partition */
	if ( run_command("mmc dev 0 0", 0) == 0 ) {
		env_set("part_start_blk", NULL);
		env_set("part_size_blk", NULL);
		run_command("part start mmc 0 data part_start_blk", 0); 
		run_command("part  size mmc 0 data part_size_blk", 0); 
		part_start_blk = env_get_hex("part_start_blk", 0);
		part_size_blk  = env_get_hex("part_size_blk", 0);

		/* If data exists, delete it */
		if( part_size_blk ) {
			sprintf(cmd, "mmc erase 0x%lx 0x%lx", part_start_blk, part_size_blk);
			ret = run_command(cmd, 0);
		} else {
			ret = 0;
		}
	} else {
		printf("Error: Cannot switch to userdata partition!\n");
	}

	return ret;
}

static int emmc_rdwr_boot_part( ulong addr, ulong size, int img_index, int write )
{
	char cmd[128];
	ulong num_blocks = 0;
	ulong block_addr = 0;
	ulong part_size_blk = 0;
	int boot_part_num = 1;
	int ret = -1;
	struct mmc *mmc = NULL;

	mmc = find_mmc_device(0);
	if( !mmc ) {
		printf("Error: Cannot access mmc device!\n");
		goto emmc_rdwr_boot_part_exit;
	}

	/* Calculate number of blocks */
        num_blocks = size/mmc->read_bl_len + (size%mmc->read_bl_len?1:0);

	/* Determine size of raw boot partition */
	part_size_blk = mmc->capacity_boot/mmc->read_bl_len;
	if( num_blocks > part_size_blk ) {
		printf("Error: Insufficient space in boot partition %d for 0x%lx blocks. Partition size is 0x%lx\n", boot_part_num, num_blocks, part_size_blk);
		goto emmc_rdwr_boot_part_exit;
	}

	/* Switch to boot partition */
	sprintf(cmd, "mmc dev 0 %d", boot_part_num);
	if ( run_command(cmd, 0) ) {
		printf("Error: Cannot switch to bootpartition %d!\n", boot_part_num);
		goto emmc_rdwr_boot_part_exit;
	}

	/* Write to raw boot partition */
	sprintf(cmd, "mmc %s 0x%lx 0x%lx 0x%lx", (write?"write":"read"), addr, block_addr, num_blocks);
	ret = run_command(cmd, 0);
	printf("%s: %s (bootp%d)\n", __FUNCTION__, cmd, boot_part_num);

	/* Switch back to userdata partition */
	run_command("mmc dev 0 0", 0); 

emmc_rdwr_boot_part_exit:
	if( ret )
		printf("Error: Failed %s 0x%lx blocks from/to boot partition %d!\n", (write?"writing":"reading"), num_blocks, boot_part_num);

	return ret;
}

static int emmc_rdwr_userdata_part( char * part_name, ulong addr, ulong size, int write )
{
	char cmd[128];
	ulong num_blocks = 0;
	ulong block_addr = 0;
	ulong part_size_blk = 0;
	int ret = -1;
	struct mmc *mmc = NULL;

	mmc = find_mmc_device(0);
	if( !mmc ) {
		printf("Error: Cannot access mmc device!\n");
		goto emmc_rdwr_usrdata_part_exit;
	}

	/* Calculate number of blocks */
        num_blocks = size/mmc->read_bl_len + (size%mmc->read_bl_len?1:0);

	/* Switch to user data partition */
	if ( run_command("mmc dev 0 0", 0) ) {
		printf("Error: Cannot switch to userdata partition!\n");
		goto emmc_rdwr_usrdata_part_exit;
	}

	/* Get GPT partition size in blocks */
	env_set("part_size_blk", NULL);
	sprintf(cmd, "part size mmc 0 %s part_size_blk", part_name);
	if(run_command(cmd, 0) == 0) {
		part_size_blk = env_get_hex("part_size_blk", 0);
		if( num_blocks > part_size_blk ) {
			printf("Error: Insufficient space in partition %s for 0x%lx blocks. Partition size is 0x%lx\n", part_name, num_blocks, part_size_blk);
			goto emmc_rdwr_usrdata_part_exit;
		}
	} else {
		printf("Error: Cannot determine size of GPT partition %s\n", part_name);
		goto emmc_rdwr_usrdata_part_exit;
	}

	/* Get start block address of partition */
	sprintf(cmd, "part start mmc 0 %s part_start_blk", part_name);
	env_set("part_start_blk", NULL);
	if(run_command(cmd, 0) == 0) {
		block_addr = env_get_hex("part_start_blk", 0);
		sprintf(cmd, "mmc %s 0x%lx 0x%lx 0x%lx", (write?"write":"read"), addr, block_addr, num_blocks);
		ret = run_command(cmd, 0);
		printf("%s: %s (%s)\n", __FUNCTION__, cmd, part_name);
	} else {
		printf("Error: Cannot determine start block of GPT partition %s\n", part_name);
		goto emmc_rdwr_usrdata_part_exit;
	}

emmc_rdwr_usrdata_part_exit:
	if( ret )
		printf("Error: Failed %s 0x%lx blocks from/to GPT partition %s!\n", (write?"writing":"reading"), num_blocks, part_name);
	return ret;
}

static int flash_rootfs_emmc( ulong addr, ulong size, int img_index )
{
	char part_name[128];
	int ret = CMD_RET_FAILURE;

	if( !addr || !size || img_index < MIN_IMG_INDEX || img_index > MAX_IMG_INDEX )
		return CMD_RET_USAGE;

	sprintf(part_name, "rootfs%d", img_index);
	ret = emmc_rdwr_userdata_part( part_name, (ulong)addr, size, 1 );

	return ret;
}

static int flash_bootfs_emmc( ulong addr, ulong size, int img_index )
{
	char part_name[128];
	int ret = CMD_RET_FAILURE;

	if( !addr || !size || img_index < MIN_IMG_INDEX || img_index > MAX_IMG_INDEX )
		return CMD_RET_USAGE;

	sprintf(part_name, "bootfs%d", img_index);
	ret = emmc_rdwr_userdata_part( part_name, (ulong)addr, size, 1 );
	return ret;
}

static int flash_loader_emmc( ulong addr, ulong size, int img_index )
{
	int ret = -1;

	if( !addr || !size )
		return CMD_RET_USAGE;

	ret = emmc_rdwr_boot_part( addr, size, img_index, 1);

	return ret;
}

static int set_emmc_metadata( char* metadata, int size )
{
	char part_name[128];
	int ret;
	int i;

	for (i = 1; i < 3; i++) {
		sprintf(part_name, "metadata%d", i);
		ret = emmc_rdwr_userdata_part( part_name, (ulong)metadata, size, 1 );
	}
	return ret;
}

static int get_emmc_metadata( char* metadata, int size )
{
	int ret;
	int committed = 0;
	int valid[2] = {0,0};
	int seq[2] = {-1,-1};
	int i;
	char part_name[128];

	for (i = 1; i < 3; i++) {
		sprintf(part_name, "metadata%d", i);
		ret = emmc_rdwr_userdata_part( part_name, (ulong)metadata, size, 0 );
		if (0 == validate_metadata(metadata, valid, &committed, seq)) {
			break;
		} else {
			printf("metadata parse error\n");
			ret = CMD_RET_FAILURE;
		}
	}
	return ret;
}

static ulong emmc_get_bootfs_size( char * fit )
{
	int images_noffset;
	int noffset;
	int ndepth;
	int count;
	size_t size;
	ulong bootfs_size = 0;
	const void *data;

	/* Find images parent node offset */
	images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
	if (images_noffset < 0) {
		printf("Can't find images parent node '%s' (%s)\n",
		       FIT_IMAGES_PATH, fdt_strerror(images_noffset));
		return 0;
	}

	/* Process all image subnodes */
	for (ndepth = 0, count = 0,
	     noffset = fdt_next_node(fit, images_noffset, &ndepth);
			(noffset >= 0) && (ndepth > 0);
			noffset = fdt_next_node(fit, noffset, &ndepth)) {
		if (ndepth == 1) {
			/*
			 * Direct child node of the images parent node,
			 * i.e. component image node.
			 */
			count++;

			if (fit_image_get_data_and_size(fit, noffset, &data, &size) == 0) {
				bootfs_size = ((ulong)data+(ulong)size) - (ulong)fit;
			}
		}
	}
	printf("Bootfs size is %lu bytes\n", bootfs_size);
	return bootfs_size;
}

static int emmc_add_gpt_part( char * name, ulong size )
{
	char * current_parts = NULL;
	char * new_parts = NULL;
	int ret = -1;

	/* Read partitions into variable */
	env_set("current_parts", NULL);
	run_command("gpt read mmc 0 current_parts", 0);
	current_parts = env_get("current_parts");

	if( strstr(current_parts, name) ) {
		printf("%s: GPT partition:%s of size:%luMiB exists!\n", __FUNCTION__, name, size);
		return 0;	
	} else {
		printf("%s: Adding GPT partition %s of size %luMiB\n", __FUNCTION__, name, size);
	}
	
	/* Update partitions */
	new_parts = malloc(strlen(current_parts) + 1024 );
	if( new_parts )	{
		sprintf(new_parts, "%sname=%s,size=%luMiB;", current_parts, name, size);
		ret = env_set("current_parts", new_parts);
		free(new_parts);
		ret = run_command("gpt write mmc 0 $current_parts", 0);
		ret = run_command("gpt verify mmc 0 $current_parts", 0);
	} else {
		printf("%s: Error allocating memory for partition string!\n", __FUNCTION__);
	}

	//env_set("current_parts", NULL);
	return ret;

}

/* This function will create GPT partitions if it detects partition sizes specfied via
 * uboot environment variables. The env variable name has to be in the format:
 * 	<partition_name>_vol_size=<size in MiB>
 * The function will then create GPT partition with name <partition_name> of size <size>
 *
 * NOTE: For NAND, equivalent ubi volumes are also created dynamically based on the same 
 * uboot env variables. However, for NAND the volumes are created in the mount-fs.sh linux 
 * startup script instead of in uboot proper.
 */
#define VOL_SIZE_STR "_vol_size="
static int emmc_do_gpt_fixup(void)
{
	env_t *ep;
	char *envbuf = NULL;
	int ret = -2;
	int elen;
	char *config;
	char * token = NULL;
        char * token2 = NULL;
        char * name;
        int value;
	ulong token_len = 0;

	config = env_get("env_boot_magic");
	if (NULL == config)
	{
		printf("env_boot_magic missing in the env\n");
		return -1;
	}
	elen = simple_strtoul(config, NULL, 0);
	elen = max(elen, CONFIG_ENV_SIZE);
	envbuf = malloc(elen);
	memset(envbuf, 0, elen);
	
	if (!envbuf)
	{
		printf("memory allocation failed\n");
		return -2;
	}
	ep = (env_t *) (envbuf + 8);
	ret = env_export(ep);

	/* Search environment for variable names matching <part>_vol_size */
	token = (char*)(ep->data);
        while(strlen(token) && (ret == 0) && ((ulong)token < ((ulong)envbuf+elen)))
        {
		token_len = strlen(token);
                if( (token2 = strstr(token, VOL_SIZE_STR)) )
                {
                        *token2 = '\0';
                        name = token;
                        value = atoi( (char*)((ulong)token2 + strlen(VOL_SIZE_STR)) );
                        printf("%s: Detected env defined partition:%s, of size:%dM\n", __FUNCTION__, name, value);
			ret = emmc_add_gpt_part(name, value);
			ret = 0;
                }
                token = (char*)((ulong)token + token_len + 1);
        }
	free(envbuf);
	return(ret);
}

static int do_gpt_fixup(cmd_tbl_t * cmdtp, int flag, int argc,
			char *const argv[])
{
	return emmc_do_gpt_fixup();
}

static int emmc_load_bootfs( int img_index, uint32_t bootfs_load_addr ) 
{
	char cmd[128];
	ulong size;
	int ret = CMD_RET_FAILURE;
	int fit_auth= -1;

	/* Set rootfs volume id */
	int rootfs_partid = (img_index == 1?IMAGE_PART_ID_1:IMAGE_PART_ID_2)+1;

	/* Switch to userdata partition */
	if( run_command("mmc dev 0 0", 0) == 0 ) {
		//FIXME: OR skip loading if FIT is already in memory
		//FIXME: IF FIT is loaded from flash, reverify RSA of header
		sprintf(cmd, "bootfs%d", img_index);

		/* Get FDT header */
		size = sizeof(struct fdt_header);
                ret = emmc_rdwr_userdata_part( cmd, (ulong)bootfs_load_addr, size, 0 );

		/* Retrieve entire FIT header */
                ret = emmc_rdwr_userdata_part( cmd, (ulong)bootfs_load_addr, 
			bcm_sec_get_reqd_load_size((ulong)bootfs_load_addr), 0 );

		if (!ret) {
			fit_auth = bcm_sec_validate_fit((void*)bootfs_load_addr, 0x10000);
		}

		/* Determine size of bootfs from fdt_header and load it */
		size = emmc_get_bootfs_size((void *)bootfs_load_addr);
                ret = emmc_rdwr_userdata_part( cmd, (ulong)bootfs_load_addr, size, 0 );

		/* generate mapper parameters */
		sprintf(cmd,"/dev/mmcblk0p%d", rootfs_partid);

		/* If device mapper not being used, set rootfs_opts manually */
		if ( (bcm_handle_mapper((void*)(ulong)bootfs_load_addr, cmd, "")) != 0)
		{
			/* Set default bootargs */
			sprintf(cmd, "env set -f rootfs_opts root=/dev/mmcblk0p%d", rootfs_partid);
			run_command(cmd, 0);
		}

		if (!fit_auth) {
			bcm_board_boot_fdt_fixup((void*)bootfs_load_addr);
		}
	}
	return ret;
}

#endif /* BCA_SDK_EMMC_CMD */

#ifdef BCA_SDK_NAND_CMD 
#define EXTRA_SPC_SUFFIX_STR "_extra_space"
static uint64_t nand_get_part_extra_bytes(char* partition_prefix)
{
	char var_name[128];
	char * extra_space_str = NULL;
	uint64_t extra_bytes = 0;
	unsigned long iargs[4] = {0};
	char units[4]= {0};
	sprintf(var_name, "%s%s", partition_prefix, EXTRA_SPC_SUFFIX_STR);
	extra_space_str = env_get(var_name);
	if( extra_space_str ) {
		parse_env_nums(extra_space_str, 1, iargs, units);
		extra_bytes = ((long long)iargs[0]) << suffix2shift(units[0]);
	}
	if( extra_bytes < SIZE_OVERHEAD )
		extra_bytes = SIZE_OVERHEAD;

	return extra_bytes;
}

static uint64_t nand_get_avail_space(char * update_vol_name)
{
	int i;
	static struct ubi_device *ubi;
	static struct ubi_volume *vol;
	uint64_t vol_size = 0;
	uint64_t avail_space = 0;
	int ret = -1;

	ret = run_command("ubi part image", 0);
	if( ret == 0 ) {
		ubi = ubi_devices[0];
		for (i = 0; i < (ubi->vtbl_slots + 1); i++) {
			if (!ubi->volumes[i])
				continue;	/* Empty record */
			vol = ubi->volumes[i];
			if( strcmp(vol->name,update_vol_name) == 0 ) {
				vol_size = vol->reserved_pebs * vol->usable_leb_size;
				break;
			}
		}
		avail_space = ubi->avail_pebs * ubi->leb_size + vol_size;
	}
	return avail_space;
}

static int nand_restoredefault(void)
{
	int ret = -1;
	if(run_command("ubi part image", 0) == 0) {

		/* If data exists, delete it */
		if(run_command("ubi check data", 0) == 0 ) {
			ret = run_command("ubi remove data", 0);
		} else {
			ret = 0;
		}
	} else {
		printf("Error: Cannot attach ubi!\n");
	}
	return ret;
}

static int flash_rootfs_nand( ulong addr, ulong size, int img_index )
{
	char cmd[128];
	int ret = CMD_RET_FAILURE;
	int rootfs_volid = (img_index == 1? IMAGE_VOL_ID_1:IMAGE_VOL_ID_2) + 1;
	uint64_t avail_bytes = 0;
	uint64_t extra_space_bytes = nand_get_part_extra_bytes("rootfs");

	if( !addr || !size || img_index < MIN_IMG_INDEX || img_index > MAX_IMG_INDEX )
		return CMD_RET_USAGE;

	if(run_command("ubi part image", 0) == 0) {

		/* check if new rootfs will fit in flash */
		sprintf(cmd, "rootfs%d", img_index); 
		avail_bytes =  nand_get_avail_space(cmd);
		if( avail_bytes < size+extra_space_bytes ) {
			printf("Error: Insufficient space in NAND for 0x%lx Bytes . Available size is 0x%llx\n", size+extra_space_bytes, avail_bytes);
			return -1;
		}

		/* If rootfs1 exists, delete it */
		sprintf(cmd, "ubi check rootfs%d", img_index);
		if(run_command(cmd, 0) == 0) {
			sprintf(cmd, "ubi remove rootfs%d", img_index);
			run_command(cmd, 0);
		}	

		/* Create rootfs volume */
		sprintf(cmd, "ubi create rootfs%d %llx  dynamic %d", img_index, size+extra_space_bytes,
								rootfs_volid);
		ret = run_command(cmd, 0);

		/* Write rootfs data volume */
		if( ret == 0 ) {
			sprintf(cmd, "ubi write %lx rootfs%d %lx\n", addr, img_index, size);
			ret = run_command(cmd, 0);
		}

		run_command("ubi detach", 0);
	}
	return ret;
}

static int flash_bootfs_nand( ulong addr, ulong size, int img_index )
{
	char cmd[128];
	int ret = CMD_RET_FAILURE;
	int bootfs_volid = (img_index == 1? IMAGE_VOL_ID_1:IMAGE_VOL_ID_2);
	int active_img_idx;
	uint64_t avail_bytes = 0;
	uint64_t extra_space_bytes = nand_get_part_extra_bytes("bootfs");

	if( !addr || !size || img_index < MIN_IMG_INDEX || img_index > MAX_IMG_INDEX )
		return CMD_RET_USAGE;

	ret = run_command("ubi part image", 0);
	if( ret ) {
		/* Attach failed, check if we are running bootstrap code */
		active_img_idx = get_active_img_idx();
		if( active_img_idx == ACTIVE_IMGIDX_BOOTSTRAP ) {
			/* We are running bootstrap image, wipe Image partition and retry */
			printf("UBI attach fails for bootstrap image --> erasing IMAGE MTD partition\n");
			run_command("mtd erase image", 0);
			ret = run_command("ubi part image", 0);
		}
	}

	if(ret == 0) {
		/* If fit1 exists, delete it */
		if(run_command("ubi check fit1", 0) == 0) 
			run_command("ubi remove fit1", 0);

		/* check if new bootfs will fit in flash */
		sprintf(cmd, "bootfs%d", img_index); 
		avail_bytes =  nand_get_avail_space(cmd);
		if( avail_bytes < size+extra_space_bytes ) {
			printf("Error: Insufficient space in NAND for 0x%lx Bytes . Available size is 0x%llx\n", size+extra_space_bytes, avail_bytes);
			return -1;
		}

		/* If bootfs exists, delete it */
		sprintf(cmd, "ubi check bootfs%d", img_index); 
		if(run_command(cmd, 0) == 0) {
			sprintf(cmd, "ubi remove bootfs%d", img_index);
			run_command(cmd, 0);
		}

		/* Create bootfs volume */
		sprintf(cmd, "ubi create bootfs%d %llx  static %d", img_index, size+extra_space_bytes,
								bootfs_volid);
		ret = run_command(cmd, 0);

		/* Write img data */
		if( ret == 0 ) {
			sprintf(cmd, "ubi write %lx bootfs%d %lx\n", addr, img_index, size);
			ret = run_command(cmd, 0);
		}

		run_command("ubi detach", 0);
	} else {
		printf("Error! UBI attach failed!\n");
	}

	return ret;
}


static int flash_loader_nand( ulong addr, ulong size, int img_index )
{
	struct mtd_info * mtd;
	struct erase_info erase_op = {};
	loff_t off;
	int ret;
	size_t retlen;
	ulong remaining_bytes = size;
	mtd = get_mtd_device_nm("loader");

	if( !addr || !size )
		return CMD_RET_USAGE;

	if( !mtd ) {
		printf("ERROR: Failed to retrieve MTD device for NAND loader!\n");
		return -1;
	}

	if( size > mtd->size ) {
		printf("ERROR: loader size 0x%08llx is greater than mtd partition size 0x%08llx!\n", size, mtd->size);
		put_mtd_device(mtd);
		return -1;
	}

	(void)img_index;

	/* Block by block write of loader */
	for( off=0; off < mtd->size; off += mtd->erasesize )
	{
		if( remaining_bytes ) {
			/* Erase block */
			erase_op.mtd = mtd;
			erase_op.addr = off;
			erase_op.len = mtd->erasesize;
			erase_op.scrub = 0;
			printf("Erasing next block at 0x%08llx\n", off);
			ret = mtd_erase(mtd, &erase_op);

			/* Abort if its not a bad block error */
			if ((ret == -EIO) && erase_op.fail_addr) {
				printf("Skipping bad block at 0x%08llx\n", erase_op.fail_addr);

				/* Truncate image size if we cant fit due to bad block skip */
				if( remaining_bytes > (mtd->size - off - mtd->erasesize) )
					remaining_bytes -= (remaining_bytes < mtd->erasesize?remaining_bytes:mtd->erasesize);

				continue;
			} else if ( ret ) {
				printf("ERROR: Failed to erase NAND block 0x%08llx\n", off);
				break;
			}

			/* write block */
			printf("Writing next block at 0x%08llx\n", off);
			ret = mtd_write(mtd, off, 
				(remaining_bytes < mtd->erasesize?remaining_bytes:mtd->erasesize), 
				&retlen, (const uchar*)(addr));

			if( ret ) {
				printf("ERROR: Failed to write NAND block 0x%08llx\n", off);
				break;
			}

			addr += (remaining_bytes < mtd->erasesize?remaining_bytes:mtd->erasesize);
			remaining_bytes -= (remaining_bytes < mtd->erasesize?remaining_bytes:mtd->erasesize);
		}
		else
			break;
	}

	if( ret ) {
		/* Loader upgrade failed for some reason */
		printf("ERROR: Failed to flash NAND loader! re=%d\n", ret);
	} else {
		/* Loader upgrade worked */
		printf("NAND loader flashed successfully!\n");
	}

	put_mtd_device(mtd);
	return ret;
}

static int set_nand_metadata( char* metadata, int size )
{
	char cmd[128];
	int ret;
	int i;
	int volmap[] = {-1,METADATA_VOL_ID_1,METADATA_VOL_ID_2};

	ret = ubi_part("image", NULL);
	if (ret != 0) {
		return (ret);
	}
	for (i = 1; i < 3; i++) {
		sprintf(cmd, "ubi remove metadata%d", i);
		run_command(cmd, 0);
		sprintf(cmd,
			"ubi create metadata%d %d static %d", i,
			METADATA_SIZE + 1024, volmap[i]);
		run_command(cmd, 0);
		sprintf(cmd, "metadata%d", i);
		ubi_volume_write(cmd, metadata, size);
	}
	run_command("ubi detach", 0);
	return ret;
}

static int get_nand_metadata( char* metadata, int size )
{
	int ret;
	int committed = 0;
	int valid[2] = {0,0};
	int seq[2] = {-1,-1};
	int i;
	char name[128];

	ret = ubi_part("image", NULL);
	if (ret != 0) {
		return (ret);
	}
	for (i = 1; i < 3; i++) {
		sprintf(name, "metadata%d", i);
		ret = ubi_volume_read(name, metadata, size);
		printf("read from %s returned %d\n", name, ret);
		if (0 == validate_metadata(metadata, valid, &committed, seq)) {
			break;
		} else {
			printf("metadata parse error\n");
			ret = CMD_RET_FAILURE;
		}
	}
	run_command("ubi detach", 0);
	return ret;
}

static int nand_load_fit( int img_index, uint32_t fit_load_addr ) 
{
	char cmd[128];
	int ret = CMD_RET_FAILURE;
	int fit_auth= -1;

	if(run_command("ubi part image", 0) == 0) {
		//FIXME: OR skip loading if FIT is already in memory
		//FIXME: IF FIT is loaded from flash, reverify RSA of header
		sprintf(cmd, "ubi read %lx bootfs%d", fit_load_addr, img_index);
		ret = run_command(cmd, 0); 
		/* may be it is just bootstrap image */
		if (ret) {
			sprintf(cmd, "ubi read %lx fit1", fit_load_addr);
			ret = run_command(cmd, 0); 
		}
	}
	return ret;
}


static unsigned int bcm_handle_mapper(void* fit, char *flash_device, char *flash_opts)
{
	const char *val;
	char cmd[768];
	char work[768];
	char *cp;
	int off, len;
	char dmdev[32];
	unsigned int magic;

 	off = fdt_path_offset(fit, "/brcm_rootfs_encrypt");
	if (off < 0) {
		return(1);
	}
	strcpy(dmdev,"/dev/dm-0");
	if (val = fdt_getprop(fit, off, "dev", &len)) {
		strncpy(dmdev, val, len);
		dmdev[len] = 0;
	}
	val = fdt_getprop(fit, off, "type", &len);
	if (val == NULL) {
		return(1);
	}
	val = fdt_getprop(fit, off, "mapper", &len);
	if (val == NULL) {
		return(1);
	}
	strncpy(work, val, len);
	work[len] = 0;

	while (cp = strstr(work,"%DEVICE%")) {
		strncpy(cmd, work, cp-work);
		sprintf(&cmd[cp-work], "%s %s", \
			flash_device, cp+8);
		strcpy(work,cmd);
	}
	if (cp = strstr(work,"%IMAGE_KEY%")) {
		int i;
		val = NULL;
		len = 0;
		off = fdt_path_offset(gd->fdt_blob, "/trust/key_image_aes");
		if (off < 0) {
			printf("ERROR: Can't find /trust/key_image_aes node in boot DTB!\n");
		}
		val = (char*)(fdt_getprop(gd->fdt_blob, off, "value", &len));
		strncpy(cmd, work, cp-work);
		for (i = 0 ; i < len ; i++) { 
		   sprintf(&cmd[cp-work+2*i], "%02x", val[i]);
                }
		sprintf(&cmd[cp-work+2*i], "%s", cp+11);
		strcpy(work,cmd);
	}
	sprintf(cmd, "root=%s %s dm-mod.create=\"%s\"", dmdev, flash_opts, work);
	env_set("rootfs_opts", cmd);
	return(0);
}
 
static int nand_load_bootfs( int img_index, uint32_t bootfs_load_addr ) 
{
	char cmd[128];
	char device[32];
	unsigned int magic = 0;
	int ret = CMD_RET_FAILURE;
	int fit_auth= -1;

	/* Set rootfs volume id */
	int rootfs_volid = (img_index == 1?IMAGE_VOL_ID_1:IMAGE_VOL_ID_2)+1;

	if(run_command("ubi part image", 0) == 0) {
		/* Set default bootargs */
		/* Determine rootfs type */
		sprintf(cmd, "ubi read %lx bootfs%d", bootfs_load_addr, img_index);
		ret = run_command(cmd, 0); 
		if (!ret) {
			fit_auth = bcm_sec_validate_fit((void*)bootfs_load_addr, 0x10000);
		}

		sprintf(device,"/dev/ubiblock0_%d", rootfs_volid); 
		sprintf(cmd,"ubi.mtd=1 ubi.block=0,%d rootfstype=squashfs", rootfs_volid); 
		if (bcm_handle_mapper((void*)(ulong)bootfs_load_addr, device, cmd) == 0)
		{
			/* mapper has set up the whole thing */
			if (!fit_auth) {
				bcm_board_boot_fdt_fixup((void*)bootfs_load_addr);
			}
			return(0);
		}

		sprintf(cmd, "ubi read %lx rootfs%d %d", &magic, img_index, (int)sizeof(unsigned int));
		if (run_command(cmd, 0) == 0) {
			if( magic == UBIFS_MAGIC ) {
				sprintf(cmd, "env set -f rootfs_opts root=ubi:rootfs%d ubi.mtd=1 rootfstype=ubifs", img_index);
				run_command(cmd, 0);
			}
			else if ( magic == SQUASHFS_MAGIC ) {
				sprintf(cmd, "env set -f rootfs_opts root=/dev/ubiblock0_%d ubi.mtd=1 ubi.block=0,%d rootfstype=squashfs", rootfs_volid, rootfs_volid);
				run_command(cmd, 0);
			} else {
				printf("ERROR: Invalid rootfs detected in volume rootfs1! Boot aborted!\n");
				return ret;
			}
		} else {
			printf("ERROR: Cannot determine rootfs type! Boot aborted!\n");
			return ret;
		}

		if (!fit_auth &&  magic == SQUASHFS_MAGIC ) {
			bcm_board_boot_fdt_fixup((void*)bootfs_load_addr);
		}
	}
	return ret;
}
#endif /* BCA_SDK_NAND_CMD */
#if defined(BCA_SDK_SPINOR_CMD)
static int spinor_load_bootfs(uint32_t bootfs_load_addr) 
{
	int ret= -1;
	struct mtd_info *mtd;
	size_t retlen;	
	int fit_auth;
	
	mtd_probe_devices();
	mtd = get_mtd_device_nm(BOOTFS_PART);
	if (IS_ERR_OR_NULL(mtd)){
		debug("%s:MTD device %s not found, ret %ld\n",__func__, BOOTFS_PART,
		   PTR_ERR(mtd));
		return ret;
	}
	
	ret = mtd_read(mtd,0,mtd->size,&retlen,(u_char*)bootfs_load_addr );

	if (!ret) {
		fit_auth = bcm_sec_validate_fit((void*)bootfs_load_addr, 0x10000);
	}

	/* If device mapper not being used, set the rootfs_opts manually */
	if (bcm_handle_mapper((void*)(ulong)bootfs_load_addr, "/dev/mtdblock3", "rootfstype=squashfs") != 0) {
		env_set("rootfs_opts","root=/dev/mtdblock3 rootfstype=squashfs");
	}

	if (!fit_auth) {
		bcm_board_boot_fdt_fixup((void*)bootfs_load_addr);
	}

	put_mtd_device(mtd);
	return ret;
}
static int spinor_restoredefault(void)
{
	struct mtd_info *mtd;
	struct erase_info ei;
	int ret;

	mtd_probe_devices();
	mtd = get_mtd_device_nm(DATA_PART);
	if (IS_ERR_OR_NULL(mtd)){
		printf("ERROR!!:failed to get data partition!!\n");
		return CMD_RET_FAILURE;
	}
	
	memset(&ei, 0, sizeof(ei));
	ei.mtd = mtd;
	ei.addr = 0;
	ei.len = mtd->size;
	ret = mtd_erase(mtd, &ei);
	if (ret){
		printf("ERROR!!: failed to restore to default!!\n");
		ret = CMD_RET_FAILURE;
	}
	else
		printf("Restore to default done.\n");

	put_mtd_device(mtd);
	return ret;
}

static int flash_loader_spinor( ulong addr, ulong size)
{
	int ret= -1;
	struct mtd_info *mtd;
	struct erase_info ei;
	size_t retlen;	
	char *config;
	u_char *envbuf = NULL;
	char *found = NULL;
	int elen;
	int i;
	size_t wr_len;
	char *c;

	if( !addr || !size )
		return CMD_RET_USAGE;
	mtd_probe_devices();
	mtd = get_mtd_device_nm(LOADER_PART);
	if (IS_ERR_OR_NULL(mtd)){
		printf("%s:MTD device %s not found, ret %ld\n",__func__, LOADER_PART,
		   PTR_ERR(mtd));
		return ret;
	}

	config = env_get("env_boot_magic");
	if (NULL != config){
		elen = simple_strtoul(config, NULL, 0);
		found = malloc(strlen(config) + 1);
		if (found){
			strcpy(found, config);
			envbuf = malloc(elen + 12);
			if(envbuf){
				strtok(found, "@");
				while ((c = strtok(NULL, ","))) {
					i = simple_strtoul(c, NULL, 0);
					debug("read from %x\n", i);
					wr_len = elen+12;
					debug("read len %x\n", wr_len);
					if( mtd_read(mtd,i,wr_len,&retlen, envbuf)){
						debug("%s:MTD device %s read fail\n",__func__, LOADER_PART);
						continue;
					}
					/*assume addr + i  will  not cause any issue*/
					memcpy(addr+i, envbuf, wr_len);
					/*need this?*/
					if( size < (i + wr_len)){
						debug("%s:Change original size from %ld to %d \n",__func__,size, i + wr_len);
						size = i + wr_len;
					}
				}
				free(envbuf);
			}
			free(found);
		}
	}

	printf("\nErasing MTD partition %s...\n",LOADER_PART);
	memset(&ei, 0, sizeof(ei));
	ei.mtd = mtd;
	ei.addr = 0;
	ei.len = mtd->size;
	ret = mtd_erase(mtd, &ei);
	if (ret){
		debug("%s:erase MTD device %s fail, ret %d\n",__func__, LOADER_PART,
	   		ret);
		goto done;
	}
	
	printf("Writing MTD partition %s...\n",LOADER_PART);
	ret = mtd_write(mtd, 0, size, &retlen, addr);
	if (ret){
		debug("%s:write MTD device %s fail, ret %d\n",__func__, LOADER_PART,
	   		ret);
	}
	debug("%s:Erase/write MTD device %s done, ret %d\n",__func__, LOADER_PART,
		ret);
done:	
	put_mtd_device(mtd);
	return ret;

}

static int write_spinor_partition( char *const partitionname, ulong addr, ulong size )
{
	int ret= -1;
	struct mtd_info *mtd;
	struct erase_info ei;
	size_t retlen;	

	if( !addr || !size )
		return CMD_RET_USAGE;

	mtd_probe_devices();
	mtd = get_mtd_device_nm(partitionname);
	if (IS_ERR_OR_NULL(mtd)){
		debug("%s:MTD device %s not found, ret %d\n",__func__, partitionname,
		   PTR_ERR(mtd));
		return ret;
	}
	printf("\nErasing MTD partition %s...\n",partitionname);
	memset(&ei, 0, sizeof(ei));
	ei.mtd = mtd;
	ei.addr = 0;
	ei.len = mtd->size;
	ret = mtd_erase(mtd, &ei);
	if (ret){
		debug("%s:erase MTD device %s fail, ret %d\n",__func__, partitionname,
	   		ret);
		goto done;
	}
	
	printf("Writing MTD partition %s...\n",partitionname);
	ret = mtd_write(mtd, 0, size, &retlen, (u_char*)addr);
	if (ret){
		debug("%s:write MTD device %s fail, ret %d\n",__func__, partitionname,
	   		ret);
	}
done:	
	put_mtd_device(mtd);
	return ret;
}

static int do_flash_spinor_binary(cmd_tbl_t * cmdtp, int flag, int argc, 
			char *const argv[])
{
	char cmd[128];
	ulong size; 
	ulong addr; 

	/* Download binary image */
	sprintf(cmd, "tftp %lx %s", load_addr, argv[1]);
	if( run_command(cmd, 0) == 0 ) {
		addr = env_get_hex("fileaddr", 0);
		size = env_get_hex("filesize", 0);
		write_spinor_partition(SPIFLASH_MTDNAME, addr, size);
	} else 
		printf("ERROR!!: Failed to tftp spinor binary!!\n");

	return 0;
}

static int do_flash_spinor_bootfs_rootfs(cmd_tbl_t * cmdtp, int flag, int argc,
			char *const argv[])
{
	char filename[30];
	int flash_fit=0;
	int ret = CMD_RET_FAILURE;
	char cmd[128];
	ulong size; 
	ulong addr; 
	

	/* Parse arguments */
	if ( strcmp(argv[0], "flash_spinor_fit") == 0) {
		flash_fit=1;
		sprintf(filename, "brcm_full_linux.itb");
	} else {
		sprintf(filename, "rootfs.squashfs");
	}

	sprintf(cmd, "tftp %lx %s\n", load_addr, filename);
	if( run_command(cmd, 0) == 0 ) {
		size = env_get_hex("filesize", 0);
		addr = env_get_hex("fileaddr", 0);
		if(flash_fit){
			ret = write_spinor_partition(BOOTFS_PART, addr, size);
		}
		else{
			ret = write_spinor_partition(ROOTFS_PART, addr, size);
		}
	}
	else
		printf("ERROR!!: Failed to tftp rootfs binary!!\n");

	return ret;
}
#endif /*BCA_SDK_SPINOR_CMD*/

/************************************************************
 *                    Generic Functions                     *
 ************************************************************/
static int sync_update_loader_bin_env(ulong loader_addr)
{
	env_t *ep;
	uint32_t calc_crc;
	char *envbuf = NULL;
	uint32_t *envintp;
	int ret = -2;
	int i;
	loff_t off;
	size_t rdlen;
	uint32_t magichdr[3];
	int num_env_found = 0;
	char * config;

	/* Search loader in memory for env headers */
	for (off = 0; off < (loff_t)env_boot_magic_search_size(); off += 4096) {
		rdlen = 12;
		memcpy((char*)magichdr, (char*)loader_addr + off, rdlen);
		if (magichdr[0] != BOOT_MAGIC_MAGIC) {
			continue;
		} else {
			/* Verify CRC */
			rdlen = magichdr[1];
			ep = (env_t *) ((char*)loader_addr + off + 8);
			calc_crc = the_env_crc32(0, ep->data, rdlen - 4);
			if( ep->crc == calc_crc ) {
				num_env_found++;
			} else {
				continue;
			}
		}

		/* Create synced env blob */
		if(num_env_found == 1)
		{
			/* Export environment to embed in new loader */
			envbuf = malloc(max(rdlen + 12, CONFIG_ENV_SIZE + 12));
			if( !envbuf ) {
				printf("ERROR: Failed to allocate mem for env!\n");
				ret = -1;
				break;
			}	
			ep = (env_t *) (envbuf + 8);
			envintp = (uint32_t *) envbuf;

			/* Delete boot magic string before exporting env */
			config = env_get("env_boot_magic");
			env_set("env_boot_magic", NULL);
			ret = env_export(ep);
			if( ret ) {
				printf("ERROR: Failed to export env!\n");
				env_set("env_boot_magic", config);
				break;
			}

			for (i = CONFIG_ENV_SIZE; i < rdlen; i++) {
				envbuf[12 + i] = 0xff;
			}
			calc_crc = the_env_crc32(0, ep->data, rdlen - 4);
			memcpy(&ep->crc, &calc_crc, sizeof(calc_crc));
			envintp[0] = BOOT_MAGIC_MAGIC;
			envintp[1] = rdlen;
		}

		/* Write synched env back to loader binary */
		printf("Updating env in loader bin at 0x%lx\n", (long)off);
		memcpy((char*)loader_addr + off, envbuf, rdlen+12);
	}

	if(envbuf)
		free(envbuf);

	if(!num_env_found)
		ret = -1;

	return ret;
}

char * get_loader_media(void)
{
	int node,len;
	char *media = NULL;

	if(forced_updates && strlen(forced_boot_media)) {
		media = forced_boot_media;
		printf("WARNING: forced_updates == 1, forcing boot media to %s!\n", media);
	} else {
		node = fdt_path_offset(gd->fdt_blob, "/chosen");
		if (node < 0) {
			printf("ERROR: Can't find /chosen node in cboot DTB! Cannot determine boot media!\n");
			return media;
		}
		media = (char*)(fdt_getprop(gd->fdt_blob, node, "boot_device", &len));
	}
	return media;
}

static char * get_image_media(void)
{
	char *cp = NULL;
	char *media = NULL;

	if(forced_updates && strlen(forced_image_media)) {
		media = forced_image_media;
		printf("WARNING: forced_updates == 1, forcing image media to %s!\n", media);
	} else {
		cp = env_get("IMAGE");
		if (NULL != cp)
		{
			unsigned long iargs[4];
			char units[4];
			parse_env_string_plus_nums(cp, &media, 4, iargs, units);
		}
		else
			printf("ERROR: Can't find IMAGE env parameter! Cannot determine image media!\n");
	}

	return media;
}

static int flash_rootfs( ulong addr, ulong size , int img_index)
{
	int ret = -1;
	if( strcasecmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 ) {
#ifdef BCA_SDK_NAND_CMD
		ret = flash_rootfs_nand(addr, size, img_index);
#endif
	}

	if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) {
#ifdef BCA_SDK_EMMC_CMD
		ret = flash_rootfs_emmc(addr, size, img_index);
#endif
	}

	if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_SPINOR) == 0 ) ) {
#ifdef BCA_SDK_SPINOR_CMD
	ret = write_spinor_partition(ROOTFS_PART, addr, size);
#endif
	}

	if( ret )
		printf("ERROR: Failed to flash bootfs binary!\n");

	return ret;

}
static int flash_bootfs( ulong addr, ulong size , int img_index)
{
	int ret = -1;
	if( strcasecmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 ) {
#ifdef BCA_SDK_NAND_CMD
		ret = flash_bootfs_nand(addr, size, img_index);
#endif
	}

	if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) {
#ifdef BCA_SDK_EMMC_CMD
		ret = flash_bootfs_emmc(addr, size, img_index);
#endif
	}

	if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_SPINOR) == 0 ) ) {
#ifdef BCA_SDK_SPINOR_CMD
		ret = write_spinor_partition(BOOTFS_PART, addr, size);
#endif
	}
	if( ret )
		printf("ERROR: Failed to flash bootfs binary!\n");

	return ret;
}
static int flash_loader( ulong addr, ulong size , int img_index)
{
	int ret = -1;
	char * config;

	/* Synch uboot env to loader binary */
	if( disable_runtime_env_sync ) {
		printf("WARNING: Not synching runtime env to loader board_spl_fit_pre_load!\n");
	} else {
		if( sync_update_loader_bin_env(addr) ) {
			printf("ERROR: Could not sync runtime env to loader bin!\n");
			return ret;
		}
	}

	if( strcasecmp(get_loader_media(), FLASH_DEV_STR_NAND) == 0 ) {
#ifdef BCA_SDK_NAND_CMD
		ret = flash_loader_nand(addr, size, img_index);
#endif 
	}

	if( ret && ( strcasecmp(get_loader_media(), FLASH_DEV_STR_EMMC) == 0 ) ) {
#ifdef BCA_SDK_EMMC_CMD
		ret = flash_loader_emmc(addr, size, img_index);
#endif
	}

	if( ret && ( strcasecmp(get_loader_media(), FLASH_DEV_STR_SPINOR) == 0 ) ) {
#ifdef BCA_SDK_SPINOR_CMD
		ret = flash_loader_spinor(addr, size);
#endif
	}
	if( disable_runtime_env_sync ) {
		printf("WARNING: Not updating runtime env from flashed loader!\n");
		disable_runtime_env_sync = 0;
	} else {
		/* Since sync was successful, we need to scan loader
		 * for boot magic and update runtime env */
		env_relocate();
		env_set("fileaddr", NULL);
		env_save();
	}

	if( ret ) 
		printf("ERROR: Failed to flash loader binary!\n");

	return ret;
}

static int set_metadata_val( int * committed, int * valid, int * seq )
{
	char* b;
	uint32_t *d, crc;
	env_t * ep;
	int i;
	int ret = -1;

	b = malloc(METADATA_SIZE + 2048);
	d = (uint32_t *) b;
	ep = (env_t *) & d[2];
	d[0] = METADATA_SIZE;
	d[1] = METADATA_SIZE;
	i =     sprintf((char*)ep->data,     "COMMITTED=%d",*committed) + 1;
	i = i + sprintf((char*)&ep->data[i], "VALID=%d,%d",valid[0],valid[1]) + 1;
	i = i + sprintf((char*)&ep->data[i], "SEQ=%d,%d",seq[0],seq[1]) + 1;
	ep->data[i] = '\0';
	crc = the_env_crc32(0, ep->data, (d[0] - 4) & 0xffff);
	/* 
	 * We can't use plain crc32 because someone redefines it??
	 * crc = crc32(0, ep->data, (d[0] - 4) & 0xffff);
	 */
	memcpy(&ep->crc, &crc, sizeof(crc));

	/* Flash metadata */
	if( strcasecmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 ) {
#ifdef BCA_SDK_NAND_CMD
		ret = set_nand_metadata(b, METADATA_SIZE + 16);
#endif
	}

	if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) {
#ifdef BCA_SDK_EMMC_CMD
		ret = set_emmc_metadata(b, METADATA_SIZE + 16);
#endif
	}

	printf("Setting committed %d valid %d,%d seq %d,%d\n", *committed, valid[0], valid[1], seq[0], seq[1]);
	free(b);
	return ret;
}

static int get_metadata_val( int * committed, int * valid, int * seq )
{
	char *b = malloc(MAX_METADATA_SIZE);
	int ret = -1;

	/* Read metadata from flash */
	if( strcasecmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 ) {
#ifdef BCA_SDK_NAND_CMD
		ret = get_nand_metadata( b, MAX_METADATA_SIZE);
#endif
	}

	if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) {
#ifdef BCA_SDK_EMMC_CMD
		ret = get_emmc_metadata(b, MAX_METADATA_SIZE);
#endif
	}
	
	/* Validate metadata */
	ret = validate_metadata(b, valid, committed, seq);
	free(b);
	printf("Getting committed %d valid %d,%d seq %d,%d\n", *committed, valid[0], valid[1], seq[0], seq[1]);
	return ret;
}

int commit_image( int img_index )
{
	int committed = 0;
	int valid[2] = {0,0};
	int seq[2] = {-1,-1};

	/*Return directly for SPI Nor*/
	if( strcmp(get_image_media(), FLASH_DEV_STR_SPINOR) == 0)
		return 0;
	
	/* Get committed image */
	get_metadata_val(&committed, valid, seq);

	/* update committed image */
	committed = img_index;
	valid[img_index-1] = img_index;
	seq[img_index-1] = (seq[(img_index == 1) ? 1 : 0] + 1) % 1000; // set newly written image sequence number one greater than other image

	/* Set committed image */
	set_metadata_val(&committed, valid, seq);

	return 0;
}

int get_img_index_for_upgrade(int flag)
{
	int committed = 0;
	int valid[2] = {0,0};
	int seq[2] = {-1,-1};
	(void) flag;
	int img_index = get_active_img_idx();

	/*Return 1 for SPI Nor*/
	if( strcmp(get_image_media(), FLASH_DEV_STR_SPINOR) == 0 )	
		return 1;
	
	/* If we know the active image, always write to the inactive image */
	if( img_index ) {
		return( (img_index==1)?2:1 );
	}

	/* If we cannot determine active image, then get non committed image */
	get_metadata_val(&committed, valid, seq);
	if( !committed ) {
		/* If no commited images then we will flash to the 1st img index */
		return 1;
	} else {
		/* If we have a committed image, then we will upgrade the uncommitted one */
		return (committed == 1? 2: 1);
	}
}

#ifndef CONFIG_LOAD_FIT_OFFSET
#define CONFIG_LOAD_FIT_OFFSET	SZ_16M
#endif

static int load_linux_img( int flag, int argc, char *const argv[])
{
	char cmd[128];
	char * board_id  = NULL;
	int img_index = 0; 
	uint32_t bootfs_load_addr = load_addr + CONFIG_LOAD_FIT_OFFSET;
	int ret = -1;

	if( argc == 1 ) {
		img_index = get_active_img_idx();
	} else if( argc > 1 ) {
		if( strlen(argv[1]) == 1 )
			img_index = *argv[1] - '0';
		else {
			board_id = argv[1];
			img_index = get_active_img_idx();
		}

		if( argc > 2 ) {
			board_id = argv[2];
		}
	}

	if( img_index < MIN_IMG_INDEX || img_index > MAX_IMG_INDEX ) {
		printf("ERROR: Invalid Image Index specified!\n");
		return CMD_RET_FAILURE;
	}	

#if defined(CONFIG_BCM_BOOTSTATE)
	/* bcmbca_set_boot_reason(BCM_BOOT_REASON_WATCHDOG | BCM_BOOT_PHASE_LINUX_START | (bcmbca_get_boot_reason() & 0xffff0000) ); */
#endif
	/* Load bootfs to load address */
	if( strcasecmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 ) {
#ifdef BCA_SDK_NAND_CMD
		ret = nand_load_bootfs(img_index, bootfs_load_addr);
#endif
	}

	if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) {
#ifdef BCA_SDK_EMMC_CMD
		ret = emmc_load_bootfs(img_index, bootfs_load_addr);
		ret = emmc_do_gpt_fixup();
#endif
	}

	if( ret && ( strcasecmp(get_image_media(), FLASH_DEV_STR_SPINOR) == 0 ) ) {
#ifdef BCA_SDK_SPINOR_CMD
		ret = spinor_load_bootfs(bootfs_load_addr);
#endif
	}
	
	if( ret == 0 ) {
#if defined(CONFIG_WDT)
		if (gd->watchdog_dev)
			wdt_stop(gd->watchdog_dev);
#endif
		if(!board_id)
			board_id = env_get("boardid");
		
		/* Load binaries */
		sprintf(cmd, "/configurations/conf_lx_%s", board_id);
		if(board_id && (fdt_path_offset((void *)bootfs_load_addr, cmd) >= 0))
			sprintf(cmd, "bootm start %lx#conf_lx_%s; bootm loados; bootm prep;", bootfs_load_addr, board_id);
		else
			sprintf(cmd, "bootm start %lx#conf_linux; bootm loados; bootm prep;", bootfs_load_addr);

      		run_command(cmd, 0);
	} else {
		printf("ERROR: Failed to load bootfs%d!\n", img_index);
	}

	return ret;
}

static int do_load(cmd_tbl_t * cmdtp, int flag, int argc,
			char *const argv[])
{
	if( load_linux_img( flag, argc, argv ) == 0 ) {
		printf("\nAll Image components have been loaded to DDR:\n");
		printf(" - To edit FDT: 'fdt <fdt cmds> ..'\n");
		printf(" - To launch Linux: 'bootm go'\n");
	} else
		printf("Linux image loading Failed!\n");

	return 0;
}

static int do_boot(cmd_tbl_t * cmdtp, int flag, int argc,
			char *const argv[])
{
	if( load_linux_img( flag, argc, argv ) == 0 ) 
		run_command("bootm go\n", 0);
	else
		printf("Linux image booting Failed!\n");

	return 0;
}

static int do_flash_loader(cmd_tbl_t * cmdtp, int flag, int argc,
			char *const argv[])
{
	char cmd[128];
	const char *chip_num;
	ulong size; 
	ulong addr; 

	/* Determine chip number */
	chip_num = strstr(CONFIG_SYS_SOC, "bcm");	
	chip_num += strlen("bcm");

	if( strcasecmp(get_loader_media(), FLASH_DEV_STR_NAND) == 0 ) {
		sprintf(cmd, "tftp %lx loader_test_nand_%s.bin\n", load_addr, chip_num);
	}

	if( ( strcasecmp(get_loader_media(), FLASH_DEV_STR_EMMC) == 0 ) ) {
		sprintf(cmd, "tftp %lx loader_test_emmc_%s.bin\n", load_addr, chip_num);
	}

	if( ( strcasecmp(get_loader_media(), FLASH_DEV_STR_SPINOR) == 0 ) ) {
		sprintf(cmd, "tftp %lx loader_test_spinor_%s.bin\n", load_addr, chip_num);
	}

	if( run_command(cmd, 0) == 0 ) {
		size = env_get_hex("filesize", 0);
		addr = env_get_hex("fileaddr", 0);
		flash_loader(addr, size, 0);
	} else
		printf("ERROR!!: Failed to tftp loader binary!!\n");

	return 0;
}

static int do_flash_bootfs_rootfs( char * bootfs_filename, char * rootfs_filename, int img_index )
{
	char cmd[128];
	ulong size;
	ulong addr;
	int ret = CMD_RET_FAILURE;

	/* Write bootfs */
	if(bootfs_filename) {
		sprintf(cmd, "tftp %lx %s", load_addr, bootfs_filename);
		if( run_command(cmd, 0) == 0 ) {
			size = env_get_hex("filesize", 0);
			addr = env_get_hex("fileaddr", 0);
			ret = flash_bootfs(addr, size, img_index);
		} else
			printf("ERROR!!: Failed to tftp FIT image!!\n");
	}
	
	/* Write rootfs */
	if(rootfs_filename) {
		sprintf(cmd, "tftp %lx %s\n", load_addr, rootfs_filename);
		
		if( run_command(cmd, 0) == 0 ) {
			size = env_get_hex("filesize", 0);
			addr = env_get_hex("fileaddr", 0);
			ret = flash_rootfs(addr, size, img_index);
		} else
			printf("ERROR!!: Failed to tftp rootfs binary!!\n");
	}

	return ret;
}

static int do_flash_bins(cmd_tbl_t * cmdtp, int flag, int argc,
			char *const argv[])
{
	char * rootfs_fname = NULL;
	char * bootfs_fname = NULL;
	int img_index = 0;
	int ret = CMD_RET_FAILURE;

	/* Parse arguments */
	if ( (strcmp(argv[0], "flash_bootfs_raw") == 0) ) {
		bootfs_fname = argv[1];
	}
	if( (strcmp(argv[0], "flash_rootfs_raw") == 0) ) { 
		rootfs_fname = argv[1];
	}

	if( argc >= 2 ) {
		if( (argc == 3) && (strlen(argv[2]) == 1) ) 
			img_index = *argv[2] - '0';
		else
			img_index = get_img_index_for_upgrade(0);
	} else {
		return CMD_RET_USAGE;
	}

	ret = do_flash_bootfs_rootfs(bootfs_fname, rootfs_fname, img_index);

	return ret;
}

static int verify_compat_string( const char * compat_str )
{
	const char *chip_num;
	char * flash_type;
	char * token = NULL;
#ifdef BCA_SDK_NAND_CMD
	char flash_type_full[20];
 	struct mtd_info *mtd = NULL;
#endif	

	/* Early return if forced updates are enabled */
	if( forced_updates ) {
		printf("WARNING: Skipping all compatibility checks due to forced updates being enabled!\n");
		return 0;
	}

	/* Determine chip number */
	chip_num = strstr(CONFIG_SYS_SOC, "bcm");	
	chip_num += strlen("bcm");

	/* Determine flash type matches*/
	flash_type = get_image_media();

	if( strcmp(flash_type, FLASH_DEV_STR_NAND) == 0 ) {
#ifdef BCA_SDK_NAND_CMD
		mtd = get_nand_dev_by_index(0);
		if (!mtd) {
			printf("ERROR: Cannot determine NAND erase block size!\n");
			return CMD_RET_FAILURE;
		}
		sprintf(flash_type_full, "%s%d", flash_type, mtd->erasesize >> 10 );
#endif
	}

	/* Check compat string */
	token = strtok((char *)compat_str,";");
	while( token ) {
		if( strstr(token, "chip=")) {
			token += strlen("chip=");
			if( strcasecmp(token, chip_num) ) {
				printf("ERROR: Img bundle is for %s, current chip is %s\n", token, chip_num);
				return -1;
			}
		} else if( strstr(token, "flash=")) {
			token += strlen("flash=");
			if( strcasecmp(token, flash_type) ) {
				if( strcmp(flash_type, FLASH_DEV_STR_NAND) == 0 ) {
#ifdef BCA_SDK_NAND_CMD
					if( strcasecmp(token, flash_type_full) ) 
					{	
						printf("ERROR: Img bundle is for %s, current flash is %s\n", token, flash_type_full);
						return -1;
					}
#endif					
				} else {
					printf("ERROR: Img bundle is for %s, current flash is %s\n", token, flash_type);
					return -1;
				}
			}
		}
		token = strtok(NULL,";");
	}
	return 0;
}

static int get_binary_from_bundle( ulong bundle_addr, char * conf_name, char * name, 
	char ** bin_name, ulong * addr, ulong * size )
{
	char path[128];
	int conf_nodeoffset = 0;
	int nodeoffset = 0;
	int len;
	int ret = -1;

	if ( !conf_name || !name || !bin_name ) {
		printf("ERROR: Invalid conf_name %p, bin_name %p, name %p\n",
			conf_name, bin_name, name);
		return ret;
	}

	/* retrieve configuration node */
	sprintf(path, "/configurations");
	sprintf(path, "%s/%s", path, conf_name);
	conf_nodeoffset = fdt_path_offset((void *)bundle_addr, path);
	if( conf_nodeoffset < 0 ) {
		printf("ERROR: %s node not found in bundle!\n", conf_name);
		return ret;
	}

	/* Get actual name of fit node for bundle component */
	*bin_name = fdt_getprop( (void *)bundle_addr, conf_nodeoffset, name, &len);
	if( !*bin_name ) {
		printf("INFO: %s not found in configuration %s ...skipping!\n", name, conf_name);
		return ret;
	}
	
	/* Retrieve node offset for bundle component */
	sprintf(path, "/images/%s", *bin_name);
	nodeoffset = fdt_path_offset((void *)bundle_addr, path);
	if( nodeoffset < 0 ) {
		printf("ERROR: %s node not found in bundle!\n", path);
		return ret;
	}
	
	/* Get location and size of binary's data */
	fit_image_get_data_and_size((void *)bundle_addr, nodeoffset, (const void**)addr, size);
	if( !*size ) {
		printf("ERROR: %s data not found in bundle!\n", *bin_name);
		return ret;
	}
	return 0;
}

static int update_flash_parts_from_loader_bin( ulong loader_addr, ulong loader_size ) {
	env_t *ep;
	uint32_t calc_crc;
	char *envbuf = NULL;
	uint32_t *envintp;
	int ret = -2;
	int i;
	loff_t off;
	size_t rdlen;
	uint32_t magichdr[3];
	char cmd[128];

	/* Search loader in memory for env headers */
	for (off = 0; off < (loff_t)env_boot_magic_search_size(); off += 4096) {
		/* Search loader bin for environment */
		rdlen = 12;
		memcpy((char*)magichdr, (char*)loader_addr + off, rdlen);
		if (magichdr[0] != BOOT_MAGIC_MAGIC) {
			continue;
		} else {
			/* Verify CRC */
			rdlen = magichdr[1];
			ep = (env_t *) ((char*)loader_addr + off + 8);
			calc_crc = the_env_crc32(0, ep->data, rdlen - 4);
			if( ep->crc == calc_crc ) {
				break;
			} else {
				continue;
			}
		}
	}

	/* Set runtime env variables based on loader's env */
	if( off < (loff_t)env_boot_magic_search_size() ) {

		/* read IMAGE variable */
		sprintf(cmd, "env import %lx - IMAGE", (char*)ep);
		run_command(cmd, 0);

		/* Verify */
		if( env_get("IMAGE") == NULL ) {
			printf("ERROR! Failed to import IMAGE parameter from loader!\n");
			return ret;
		}

		/* read default_partitions */
		if( ( strcasecmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) {
			sprintf(cmd, "env import %lx - default_partitions", (char*)ep);
			run_command(cmd, 0);

			/* Verify */
			if( env_get("default_partitions") == NULL ) {
				printf("ERROR! Failed to import default_partitions parameter from loader!\n");
				return ret;
			}
		}

		/* Call partition init code */
		printf("INFO: flash partition config updated. Repartitioning flash!\n");
		ret = board_init_flash_parts(1);
	} else {
		printf("ERROR!: No environment found in loader binary!\n");
	}
	return ret;
}

int flash_upgrade_img_bundle( ulong bundle_addr , int img_index, const char * conf_name)
{
	char path[128];
	ulong addr=0;
	ulong size=0;
	ulong loader_addr=0;
	ulong loader_size=0;
	int i=0;
	int err = 0;
	const char * bin_name;
	const char * loader_bin_name;
	const char * compat_str;
	char *compat_copy;
	int nodeoffset = 0;
	int conf_nodeoffset = 0;
	int max_entries = sizeof(fn_table)/sizeof(flashfn_table_entry);
	int len;

	/* verify hashes of all upgrade bundle contents */
	if( fit_all_image_verify((void *)bundle_addr) ) {

		/* Get offset of config node */
		sprintf(path, "/configurations");
		nodeoffset = fdt_path_offset((void *)bundle_addr, path);
		if( nodeoffset < 0 ) {
			printf("ERROR: %s node not found in bundle!\n", path);
			return CMD_RET_FAILURE;
		}

		/* If configuration is specifed then use it */
		if( !conf_name ) {
			/* Get name of the default configuration */
			conf_name = fdt_getprop((void *)bundle_addr, nodeoffset, "default", &len);
			if( !conf_name ) {
				printf("ERROR: Default configuration not found in bundle!\n");
				return CMD_RET_FAILURE;
			}
		}

		/* Get offset of selected configurations fit node */
		sprintf(path, "%s/%s", path, conf_name);
		conf_nodeoffset = fdt_path_offset((void *)bundle_addr, path);
		if( conf_nodeoffset < 0 ) {
			printf("ERROR: %s node not found in bundle!\n", conf_name);
			return CMD_RET_FAILURE;
		}

		/* Verify compatibility */
		compat_str = fdt_getprop( (void *)bundle_addr, conf_nodeoffset, "compatible", &len);
		if( !compat_str ) {
			printf("Error: 'compatible' node not found in bundle!\n");
			return CMD_RET_FAILURE;
		}
		
		compat_copy = malloc(strlen(compat_str)+1);
		if(!compat_copy)
		{
			printf("Error allocating memory for compatibility string!\n");
			return CMD_RET_FAILURE;
		}
		strcpy(compat_copy, compat_str);
		
		if( verify_compat_string(compat_copy) ) {
			printf("Error: Bundle is not compatible with platform!\n");
			free(compat_copy);
			return CMD_RET_FAILURE;
		}
		free(compat_copy);
		
		/* Decide whether we want to reformat partitions based on loader
		 * binaries partition configuration configuration */
		if( NULL == env_get("IMAGE") ) {
			printf("WARNING: IMAGE env variable missing! Using IMAGE from loader binary!\n");
			if( get_binary_from_bundle(bundle_addr, conf_name, "loader", 
			   &loader_bin_name, &loader_addr, &loader_size) == 0 ) {
				printf("INFO: Reformatting flash media based on new loader binary\n");
				if( update_flash_parts_from_loader_bin( loader_addr, loader_size ) ) {
					printf("ERROR: Failed to reformat flash based loader binary!\n");
					return CMD_RET_FAILURE;
				}

				/* Set flag to prevent sync of run-time env to loader bin */
				disable_runtime_env_sync = 1;
			} else {
				printf("ERROR! No loader binary AND IMAGE parameter not set!\n");
				return CMD_RET_FAILURE;
			}
		} else if (env_get_hex("reformatOnUpgrade", 0)) {
			printf("INFO: Reformatting partitions because reformatOnUpgrade == 1!\n");
			err = board_init_flash_parts(1);
			env_set("reformatOnUpgrade", NULL);
		}

		for( i=0; i<max_entries && !err; i++ ) {
			/* Parse loader only if it has not been parsed before */
			if( (strcmp(fn_table[i].name,"loader") == 0 ) && loader_addr && loader_size ) {
				bin_name = loader_bin_name;
				size = loader_size;
				addr = loader_addr;
			} else {
				if (get_binary_from_bundle( bundle_addr, conf_name, fn_table[i].name, 
				   &bin_name, &addr, &size ) ) {
					continue;
				}
			}
		
			/* Calling flashing function */
			printf("Flashing %s (%s: 0x%lx bytes from 0x%lx) to %s%d.....\n", 
			   fn_table[i].name, bin_name, size, addr, fn_table[i].name, 
			   (strcmp(fn_table[i].name,"loader")?img_index:0));
			err = fn_table[i].func(addr, size, img_index);
		}

		/* Reset flag */
		disable_runtime_env_sync = 0;
	} else {
		err = CMD_RET_FAILURE;
	}

	return err;
}

static int do_flash_upgrade_img (cmd_tbl_t * cmdtp, int flag, int argc, 
			char *const argv[])
{
	char cmd[128];
	ulong bundle_addr=0;
	int err = CMD_RET_FAILURE;
	const char * conf_name = NULL;
	int img_index = 0;
	bool download = true;
	bool commit = true;
	int active_img_idx = get_active_img_idx();

	/* Parse and remove the optional skip argument */
	if ( argc > 1) {
		if ( !strcmp(argv[1], "-s")) {
			download = false;
			argc--;
			argv++;
		} else if ( !strcmp(argv[1], "-i")) {
			commit = false;
			argc--;
			argv++;
		}
	}

	if( argc == 2 ) {
		img_index = get_img_index_for_upgrade(0);
	} else if( argc > 2 ) {
		if( strlen(argv[2]) == 1 )
			img_index = *argv[2] - '0';
		else {
			conf_name = argv[2];
			img_index = get_img_index_for_upgrade(0);
		}

		if( argc > 3 ) {
			conf_name = argv[3];
		}
	}

	if( img_index < MIN_IMG_INDEX || img_index > MAX_IMG_INDEX ) {
		printf("ERROR: Invalid Image Index specified!\n");
		return CMD_RET_USAGE;
	}	

	if (download)
		sprintf(cmd, "tftp %lx %s", load_addr, argv[1]);

	if( !download || run_command(cmd, 0) == 0 ) {
		bundle_addr = env_get_hex("fileaddr", 0);

		if (bundle_addr)
			/* Call main img flashing function */
			err = flash_upgrade_img_bundle(bundle_addr, img_index, conf_name);
	}

	if(err) {
		printf("ERROR: Image upgrade failed!!\n");
	} else {
		printf("INFO: Image upgrade successfull!!\n");		
		if( commit ) {
			printf("INFO: Committing Image!!\n");		
			commit_image( img_index );

			/* Handle bootstrap condition */
			if( active_img_idx == ACTIVE_IMGIDX_BOOTSTRAP ) {
				/* Now that we have a valid image flashed, change active image index */
				set_active_img_idx(img_index);
			}
		} else {
			printf("INFO: Not Committing Image!!\n");		
		}
	}

	return err;
}

static int do_restoredefault(cmd_tbl_t * cmdtp, int flag, int argc,
			   char *const argv[])
{
	int ret = -1;
	if( strcmp(get_image_media(), FLASH_DEV_STR_NAND) == 0 ) {
#ifdef BCA_SDK_NAND_CMD
		ret = nand_restoredefault();
#endif
	}

	if( ret && ( strcmp(get_image_media(), FLASH_DEV_STR_EMMC) == 0 ) ) {
#ifdef BCA_SDK_EMMC_CMD
		ret = emmc_restoredefault();
#endif
	}
	
	if( ret && ( strcmp(get_image_media(), FLASH_DEV_STR_SPINOR) == 0 ) ) {
#ifdef BCA_SDK_SPINOR_CMD
		ret = spinor_restoredefault();
#endif
	}

	return ret;
}

static int do_metadata(cmd_tbl_t * cmdtp, int flag, int argc,
			   char *const argv[])
{
	int ret = -1;
	int committed = 0;
	int valid[2] = {0,0};
	int seq[2] = {-1,-1};
	int status = get_metadata_val(&committed, valid, seq);

	if (argc == 1) {
		if (0 == status) {
			printf("committed-img: %d valid-imgs: ", committed);
			if( valid[0] )
				printf("%d", valid[0]);
			if( valid[1] )
				printf("%d", valid[1]);
			printf(", seq# img 1: %d, img2: %d\n\n", seq[0], seq[1]);
			ret = 0;
		} else {
			printf("metadata parse error\n");
		}
	} else if (argc == 3) {
		committed = atoi(argv[1]);
		valid[0] = atoi(strtok(argv[2],","));
		valid[1] = atoi(strtok(NULL,","));
		
		if (0 != set_metadata_val(&committed, valid, seq)) {
			printf("metadata parse error\n");
		} else {
			ret = 0;
		}
	}
	return ret;
}

#if defined(CONFIG_BCM_BOOTSTATE)
static int do_activate(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[])
{
	bcmbca_set_boot_reason(BCM_BOOT_REASON_ACTIVATE);
	run_command("reset", 0); 
	return 0;
}
#endif

static int do_force(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[])
{
	int ret = 0;
	if( argc > 1 ) {
		if( atoi(argv[1]) == 1 ) {
			if( (argc == 4) 
				&& ( ( strcasecmp(argv[2], FLASH_DEV_STR_NAND) == 0 )
				|| (   strcasecmp(argv[2], FLASH_DEV_STR_SPINOR) == 0 )
				|| (   strcasecmp(argv[2], FLASH_DEV_STR_EMMC) == 0 ) ) 
				&& ( ( strcasecmp(argv[3], FLASH_DEV_STR_NAND) == 0 )
				|| (   strcasecmp(argv[3], FLASH_DEV_STR_SPINOR) == 0 )
				|| (   strcasecmp(argv[3], FLASH_DEV_STR_EMMC) == 0 ) )	) {
				forced_updates = 1;
				strcpy(forced_boot_media,argv[2]);
				strcpy(forced_image_media,argv[3]);
			} else if(argc == 2) {
				forced_updates = 1;
			} else {
				ret = CMD_RET_USAGE;
				printf("Cannot determine boot/image flash type, please specify flash types!\n");
			}
		} else {
			forced_updates = 0;
			forced_boot_media[0] = '\0';
			forced_image_media[0] = '\0';
		}
	}

	printf("Forced Image Updates: %s, Forced flash types: Boot=%s, Image=%s\n", 
		(forced_updates?"Enabled":"Disabled"), 
		(forced_updates?forced_boot_media:"NOT SPECIFIED"),
		(forced_updates?forced_image_media:"NOT SPECIFIED"));

	return ret;
}

#ifdef CONFIG_BCMBCA_HTTPD
static int do_httpd_start(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[])
{
    if(!httpd_check_net_env())
        register_cli_job_cb(0, http_poll);
    return 0;
}
#endif

#ifdef CONFIG_BCMBCA_PMC
static int do_bpcm_cmd(cmd_tbl_t * cmdtp, int flag, int argc,
			char *const argv[])
{
	int err = CMD_RET_USAGE;
	int read = 0;
        u32 addr, off, data;
	
	if ((argc != 5) && (argc != 4))
		return err;  
  
	if (!strcmp(argv[1], "r"))
		read = 1;
	else if (!strcmp(argv[1], "w"))
		read = 0;
	else
		return err;

	addr = atoi(argv[2]);
	off = atoi(argv[3]);
	if (!read) {
		data = atoi(argv[4]);
		err = WriteBPCMRegister(addr, off>>2, data);
		printf("bpcm write data 0x%x to addr 0x%x offset 0x%x ret %d\n",
		       data, addr, off, err);
	} else {
		err = ReadBPCMRegister(addr, off>>2, &data);
		printf("bpcm read data 0x%x from addr 0x%x offset 0x%x ret %d\n",
		       data, addr, off, err);
	}
	

	return err;
}

static int do_cpufreq(cmd_tbl_t * cmdtp, int flag, int argc,
			char *const argv[])
{
	int err = CMD_RET_USAGE, freqMHz;

	if (argc != 2)
		return err;

	err = CMD_RET_SUCCESS;
	freqMHz = atoi(argv[1]);
	freqMHz = set_cpu_freq(freqMHz);
	if (freqMHz > 0)
		printf("cpu freq set to %dMHz\n", freqMHz);
	else
		err = CMD_RET_FAILURE;

	return err;
}
#endif

static int do_dev_spec_key(cmd_tbl_t * cmdtp, int flag, int argc,
			char *const argv[])
{
	int err = CMD_RET_USAGE;
	int rc, len;
	u32 dev_spec_key[8];
	int i;
	bool force = false;                                 
							    
	if ( (argc == 2) && (strcmp(argv[1], "get") == 0)) {
		rc = bcm_sec_get_dev_spec_key((char*)dev_spec_key, sizeof(u32)*8);
	} else if ( (argc >= 10) && (strcmp(argv[1], "set") == 0)) {
		/* Parse and remove the optional FORCE argument */  
		if( argc > 10 ) {
			if ( !strcasecmp(argv[2], "-f")) {              
				force = true;                       
				argc--;                             
				argv++;                             
			}
		}

		for(i=0; i<8; i++) {
			dev_spec_key[i] = simple_strtoul(argv[i+2],NULL, 0);
		}
		if ( !force ) {
			printf("WARNING: You are attempting to set a new device specific key.\n");
			len = cli_readline("This operation cannot be undone, enter 'Y' to continue: ");	
			if( !len || console_buffer[0] != 'Y' ) {
				printf("Device specific key setting aborted.\n");
				return CMD_RET_SUCCESS;
			}
		}
		rc = bcm_sec_set_dev_spec_key((char*)dev_spec_key, sizeof(u32)*8);
	} else {
		return err;
	}

	if (rc == 0) {
		printf("Current Device Specific Key:\n");
		for( i=0; i<8; i++ ) {
			printf(" [%d]0x%08x\n", i, dev_spec_key[i]);
		}
		printf("\n\n");
		err = CMD_RET_SUCCESS;
	} else {
		err = CMD_RET_FAILURE;
	}

	return err;
}

static int do_sec_ser_num(cmd_tbl_t * cmdtp, int flag, int argc,
			char *const argv[])
{
	int err = CMD_RET_USAGE;
	int rc, len;
	u32 sec_ser_num[8] = {0};
	char * sec_ser_num_ptr = (char*)&sec_ser_num[0];
	char * sec_ser_num_char_array;
	char byte_buf[3] = {0};
	int i,j;
	bool force = false;                                 
	bool is_set = false;
	bool is_odd = false;
							    
	if ( (argc == 2) && (strcmp(argv[1], "get") == 0)) {
		rc = bcm_sec_get_sec_ser_num((char*)sec_ser_num, sizeof(u32)*8);
	} else if ( (argc >= 3) && (strcmp(argv[1], "set") == 0)) {
		is_set = true;
		/* Parse and remove the optional FORCE argument */  
		if( argc == 4 ) {
			if ( !strcasecmp(argv[2], "-f")) {              
				force = true;                       
				argc--;                             
				argv++;                             
			}
		}

		/* Check if byte array exceeds 32 bytes OR contains an ODD number of characters */
		sec_ser_num_char_array = argv[2];

		/* Skip leading 0x if present */
		if( sec_ser_num_char_array[1] == 'x' || sec_ser_num_char_array[1] == 'X' )
			sec_ser_num_char_array += 2;

		debug("sernum:%s len:%d\n", sec_ser_num_char_array, (int)strlen(sec_ser_num_char_array));
		if( ((strlen(sec_ser_num_char_array)/2) > sizeof(sec_ser_num)) || (strlen(sec_ser_num_char_array) % 2)){
			return CMD_RET_USAGE;
		}

		/* Check if there is an odd number of digits so that we can insert an extra zero */
		if( strlen(sec_ser_num_char_array) % 2 )
			is_odd = 1;

		for(i=0,j=sizeof(sec_ser_num)-strlen(sec_ser_num_char_array)/2; 
		   i<strlen(sec_ser_num_char_array),j<sizeof(sec_ser_num); j++){
			if( is_odd && (i == 0) ) {
				byte_buf[1] = sec_ser_num_char_array[i];
				is_odd = false;
				i++;
			} else {
				memcpy(byte_buf,&sec_ser_num_char_array[i], 2);
				i += 2;
			}

			sec_ser_num_ptr[j] = simple_strtoul(byte_buf,NULL,16);
			debug("byte_buf: %s 0x%02x\n", byte_buf, sec_ser_num_ptr[j]);
		}

		if ( !force ) {
			printf("WARNING: You are attempting to set a new secure serial number.\n");
			len = cli_readline("This operation cannot be undone, enter 'Y' to continue: ");	
			if( !len || console_buffer[0] != 'Y' ) {
				printf("secure serial number setting aborted.\n");
				return CMD_RET_SUCCESS;
			}
		}
		rc = bcm_sec_set_sec_ser_num((char*)sec_ser_num, sizeof(u32)*8);
	} else {
		return err;
	}

	if (rc == 0) {
		if( is_set ) 
			printf("Set Secure Serial Number as:\n");
		else
			printf("Current Secure Serial Number:\n");

		printf(" [");
		for( i=0; i<sizeof(sec_ser_num); i++ ) {
			printf("%02x", sec_ser_num_ptr[i]);
		}
		printf("]\n");

		printf("In memory represenation:\n");
		for( i=0; i<8; i++ ) {
			printf(" [%d]0x%08x\n", i, sec_ser_num[i]);
		}
		printf("\n\n");
		err = CMD_RET_SUCCESS;
	} else {
		err = CMD_RET_FAILURE;
	}

	return err;
}
static int do_antirollback(cmd_tbl_t * cmdtp, int flag, int argc,
			char *const argv[])
{
	int err = CMD_RET_USAGE;
	int rc, len;
	u32 lvl;
	bool force = false;                                 
							    

	if ( (argc == 2) && (strcmp(argv[1], "get") == 0)) {
		rc = bcm_sec_get_antirollback_lvl(&lvl);
	} else if ( (argc >= 3) && (strcmp(argv[1], "set") == 0)) {
		/* Parse and remove the optional FORCE argument */  
		if( argc > 3 ) {
			if ( !strcasecmp(argv[2], "-f")) {              
				force = true;                       
				argc--;                             
				argv++;                             
			}
		}

		/* get level */
		lvl = atoi(argv[2]);
		if ( !force ) {
			printf("WARNING: You are attempting to set a new anti-rollback level.\n");
			len = cli_readline("This operation cannot be undone, enter 'Y' to continue: ");	
			if( !len || console_buffer[0] != 'Y' ) {
				printf("Anti-rollback level setting aborted.\n");
				return CMD_RET_SUCCESS;
			}
		}
		rc = bcm_sec_set_antirollback_lvl(lvl);
	} else {
		return err;
	}

	if (rc == 0) {
		printf("Current anti-rollback level set to: %d\n", lvl);
		err = CMD_RET_SUCCESS;
	} else {
		err = CMD_RET_FAILURE;
	}

	return err;
}

#ifdef CONFIG_BCMBCA_XRDP_ETH
extern int bcmbca_xrdp_eth_phy_status(void);
extern int bcmbca_xrdp_eth_mac_status(void);
extern int bcmbca_xrdp_eth_active_port_get(void);
extern int bcmbca_xrdp_eth_active_port_set(int port);
extern int bcmbca_xrdp_eth_env_active_port_set(int port);

static int do_eth_active_port(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[])
{
    int port;

	if (argc == 2)
    {
        port = (atoi(argv[1]));
        bcmbca_xrdp_eth_active_port_set(port);
    }

    port = bcmbca_xrdp_eth_active_port_get();
    printf("XRDP Ethernet active port is set to %d\n" ,port);

	return 0;
}

static int on_active_port(const char *name, const char *value, enum env_op op, int flags)
{
    int active_port = -1;

	if ((flags & H_INTERACTIVE) == 0)
		return 0;

	switch (op) {
	case env_op_create:
	case env_op_overwrite:
        active_port = simple_strtoul(value, NULL, 10);
        break;
    case env_op_delete:
        break;
    default:
        break;
    }

    return bcmbca_xrdp_eth_env_active_port_set(active_port);
}
U_BOOT_ENV_CALLBACK(active_port, on_active_port);
#endif

static char usage[] = 
#ifdef BCA_SDK_EMMC_CMD
    "\n## eMMC Specific Test Commands ##\n"
    "gpt_fixup\n"
    " - Create GPT partitions if partition sizes are specfied via special uboot\n"
    " - environment variables. The env variable name has to be in the format:\n"
    " - 	<partition_name>_vol_size=<size in MiB>\n"
#endif /* BCA_SDK_EMMC_CMD */
#ifdef BCA_SDK_SPINOR_CMD
    "\n## SPINOR Specific Test Commands ##\n"
    "flash_spinor_fit\n"
    " - Download and flash atf+uboot+vmlinux+dtb in brcm_full.itb to SPI NOR flash \n"
    "flash_spinor_rootfs\n"
    " - Download and flash rootfs in rootfs.squashfs to SPI NOR flash\n"
    "flash_spinor_upgrade [-s] <filename> [conf]\n"
    "flash_spinor_upgrade <filename> [<img> [<conf>]]\n" 
    " - Download and flash an image upgrade bundle to SPI NOR flash\n"
    "flash_spinor_binary <filename> \n" 
    " - Erase whole flash,download and flash a binary image to SPI NOR flash start from 0\n"
    "boot_spinor [boardid]\n"
    " - Boot linux from image in SPI NOR flash.\n" 
#endif
    "\n## Generic Test Commands ##\n"
    "force \n"
    " - Without arguments, display if forced image updates are enabled\n"
    "force [force value] <[Boot Flash Type] [Image Flash Type]>\n"
    " - Enable(value=1)/Disable(value=0) forced image updates. All compatibility checks are ignored if enabled\n"
    " - If boot and image flash types are not detected, then they need to be specified as 2nd and 3rd args\n"
    " - Valid flash type values = [NAND|SPINAND|EMMC|NOR]\n"
    "restoredefault\n"
    " - Delete contents of /data so that it gets populated with default values\n"
    "flash_loader\n"                        
    " - Download and flash SPL+TPL in loader_test_<flash>_<chip>.bin to boot flash\n"
#if defined(BCA_SDK_NAND_CMD) || defined(BCA_SDK_EMMC_CMD)
    "flash_bootfs_raw <bootfs binary> [img]\n"                      
    "flash_rootfs_raw <rootfs binary> [img]\n"   
    " - Download and flash raw bootfs/rootfs binary to flash \n"
    " - WARNING: These are raw writes with no content, security or compatibility checks. Metadata is NOT updated \n"
    "metadata \n"
    "metadata [committed] [valid],[valid]\n"    
    " - without arguments, parse and display metadata\n"
    " - set metadata values (example \"metadata 1 1,2\" for both images valid and #1 committed)\n" 
    " - NOTE: Must do a sw reset for metadata changes to take affect\n"
#endif
    "flash_img_upgrade [-s] <filename> [conf]\n"
    "flash_img_upgrade [-i] <filename> [<img> [<conf>]]\n" 
    " - Download and flash an image upgrade bundle via TFTP\n"
    " - NOTE: Must do a sw reset to use the new image\n"
    "load_img [boardid]\n"                  
    "load_img [<img> [<boardid>]]\n"                  
    " - Load image from flash into DDR.\n" 
    "boot_img [boardid]\n"                  
    "boot_img [<img> [<boardid>]]\n"                  
    " - Boot linux from image in flash.\n" 
    "[Legend - Optional Parameters]\n"
    " -s:      Skip downloading the image via tftp. Assume it is already at ${fileaddr}\n"
    " -i:      Inactive. Do not change activation state of new image i.e do not commit image\n"
    " img:     Image index for flashing/booting. Valid values: [1|2]. Default index is determined from metadata\n"
    " fstype:  Filesystem type for flashing. Valid values: [squashfs|ubifs|ext4]. Default is squashfs\n"
    " boardid: Boardid to determine boot configuration. If omitted default configuration node is used\n"
    " conf:    Configuration name used for flashing img bundle. If omitted default configuration node is used\n\n"
#ifdef CONFIG_BCMBCA_HTTPD
    "httpd_start \n"
    " - without arguments, start httpd server\n"
#endif
#ifdef CONFIG_BCMBCA_PMC
    "bpcm_cmd <r|w> <addr> <offset> [<data>]\n"                  
    " - read/write bpcm register\n"
    "cpufreq <freq in MHz>\n"                  
    " - set cpu frequency\n" 
#endif	  
    "antirollback get\n"                  
    " - get current anti-rollback level\n" 
    "antirollback set [-f] <new anti-rollback level to set>\n"                  
    " - set new anti-rollback level. [-f] will force the commit and skip warnings\n" 
    "sec_ser_num get \n"                  
    " - get current secure serial number level ( 64 digits )\n" 
    "sec_ser_num set [-f] < Hex secure serial number ( Max 64 digits )>\n"
    " - set new secure serial number . [-f] will force the commit and skip warnings\n" 
    "dev_spec_key get \n"                  
    " - get current device specific key ( 32 bytes )\n" 
    "dev_spec_key set [-f] <32-bit hex word 0>...<32-bit hex word 7> \n"                  
    " - set new device specific key. [-f] will force the commit and skip warnings\n" 
    ;

static char sdk_usage[] = 
    "\nforce\n"
    " - Without arguments, display if forced image updates are enabled\n"
    "force [force value] <[Boot Flash Type] [Image Flash Type]>\n"
    " - Enable(value=1)/Disable(value=0) forced image updates. All compatibility checks are ignored if enabled\n"
    " - If boot and image flash types are not detected, then they need to be specified as 2nd and 3rd args\n"
    " - Valid flash type values = [NAND|SPINAND|EMMC|NOR]\n"
    "restoredefault\n"
    " - Delete contents of /data so that it gets populated with default values\n"
#if defined(BCA_SDK_NAND_CMD) || defined(BCA_SDK_EMMC_CMD)
    "metadata \n"
    "metadata [committed] [valid],[valid]\n"    
    " - without arguments, parse and display metadata\n"
    " - set metadata values (example \"metadata 1 1,2\" for both images valid and #1 committed)\n" 
    " - NOTE: Must do a sw reset for metadata changes to take affect\n"
#endif
    "flash_img_upgrade [-s] <filename> [conf]\n"
    "flash_img_upgrade [-i] <filename> [<img> [<conf>]]\n" 
    " - Download and flash an image upgrade bundle via TFTP\n"
    " - NOTE: Must do a sw reset to use the new image\n"
    "load_img [boardid]\n"                  
    "load_img [<img> [<boardid>]]\n"                  
    " - Load image from flash into DDR.\n" 
    "boot_img [boardid]\n"                  
    "boot_img [<img> [<boardid>]]\n"                  
    " - Boot linux from image in flash.\n" 
    "[Legend - Optional Parameters]\n"
    " -s:      Skip downloading the image via tftp. Assume it is already at ${fileaddr}\n"
    " -i:      Inactive. Mark new image as inactive i.e do not commit image\n"
    " img:     Image index for flashing/booting. Valid values: [1|2]. Default index is determined from metadata\n"
    " boardid: Boardid to determine boot configuration. If omitted default configuration node is used\n"
    " conf:    Configuration name used for flashing img bundle. If omitted default configuration node is used\n\n"
#ifdef CONFIG_BCMBCA_HTTPD
    "httpd_start \n"
    " - without arguments, start httpd server\n"
#endif
#ifdef CONFIG_BCMBCA_XRDP_ETH
    "active_port [port]\n"
    " - Set the active network driver's port to send packets from\n"
    " - without arguments, display the current active port\n"
    "eth_status [type]\n"
    " - Print the XRDP Ethernet network driver status tables\n"
    " - Argument can be phy or mac. If no argument provided, both tables will be printed\n"
#endif
    ;

U_BOOT_CMD_WITH_SUBCMDS(bca_test, "Broadcom test commands", usage,
			U_BOOT_SUBCMD_MKENT(flash_loader, 1, 0, do_flash_loader),
#ifdef BCA_SDK_EMMC_CMD
			U_BOOT_SUBCMD_MKENT(gpt_fixup, 1, 0, do_gpt_fixup),
#endif /* BCA_SDK_EMMC_CMD */
#ifdef BCA_SDK_SPINOR_CMD
			U_BOOT_SUBCMD_MKENT(flash_spinor_fit, 1, 0, do_flash_spinor_bootfs_rootfs),
			U_BOOT_SUBCMD_MKENT(flash_spinor_rootfs, 1, 0, do_flash_spinor_bootfs_rootfs),
			U_BOOT_SUBCMD_MKENT(flash_spinor_upgrade, 5, 0, do_flash_upgrade_img),
			U_BOOT_SUBCMD_MKENT(flash_spinor_binary, 2, 0, do_flash_spinor_binary),
			U_BOOT_SUBCMD_MKENT(boot_spinor, 3, 0, do_boot),
#endif			
#if defined(CONFIG_BCM_BOOTSTATE)
			U_BOOT_SUBCMD_MKENT(activate, 1, 0, do_activate),
#endif /* CONFIG_BCM_BOOTSTATE */
			U_BOOT_SUBCMD_MKENT(force, 4, 0, do_force),
			U_BOOT_SUBCMD_MKENT(restoredefault, 1, 0, do_restoredefault),
#if defined(BCA_SDK_NAND_CMD) || defined(BCA_SDK_EMMC_CMD) 
			U_BOOT_SUBCMD_MKENT(flash_bootfs_raw, 3, 0, do_flash_bins),
			U_BOOT_SUBCMD_MKENT(flash_rootfs_raw, 3, 0, do_flash_bins),
			U_BOOT_SUBCMD_MKENT(metadata, 5, 0, do_metadata),
#endif			
			U_BOOT_SUBCMD_MKENT(flash_img_upgrade, 5, 0, do_flash_upgrade_img),
			U_BOOT_SUBCMD_MKENT(boot_img, 3, 0, do_boot)
#ifdef CONFIG_BCMBCA_HTTPD
			,U_BOOT_SUBCMD_MKENT(httpd_start, 1, 0, do_httpd_start)
#endif
#ifdef CONFIG_BCMBCA_PMC
			,U_BOOT_SUBCMD_MKENT(bpcm_cmd, 5, 0, do_bpcm_cmd)
			,U_BOOT_SUBCMD_MKENT(cpufreq, 2, 0, do_cpufreq)			
#endif			
			,U_BOOT_SUBCMD_MKENT(antirollback, 4, 0, do_antirollback)			
			,U_BOOT_SUBCMD_MKENT(sec_ser_num, 12, 0, do_sec_ser_num)			
			,U_BOOT_SUBCMD_MKENT(dev_spec_key, 12, 0, do_dev_spec_key)			
    );

U_BOOT_CMD_WITH_SUBCMDS(sdk, "Broadcom SDK support commands", sdk_usage,
			U_BOOT_SUBCMD_MKENT(force, 4, 0, do_force),
			U_BOOT_SUBCMD_MKENT(restoredefault, 1, 0, do_restoredefault),
#if defined(BCA_SDK_NAND_CMD) || defined(BCA_SDK_EMMC_CMD) 
			U_BOOT_SUBCMD_MKENT(metadata, 5, 0, do_metadata),
#endif			
			U_BOOT_SUBCMD_MKENT(flash_img_upgrade, 5, 0, do_flash_upgrade_img),
			U_BOOT_SUBCMD_MKENT(boot_img, 3, 0, do_boot),
			U_BOOT_SUBCMD_MKENT(load_img, 3, 0, do_load)
#ifdef CONFIG_BCMBCA_HTTPD
			,U_BOOT_SUBCMD_MKENT(httpd_start, 1, 0, do_httpd_start)
#endif
#ifdef CONFIG_BCMBCA_XRDP_ETH
			,U_BOOT_SUBCMD_MKENT(active_port, 2, 0, do_eth_active_port)
#endif
    );

