/*
 * diag_epi3.c: Matthew Millman
 *
 * Routines for programming EPI3 personalities
 */
 
/* 
 * Header files
 */
 
#include <common.h>
#include <config.h>
#include <command.h>
#include <epi3.h>
#include <diag.h>
#include <malloc.h>
#include <bootmenu.h>
#include <pci.h>
#include <i2c.h>
#include <linux/ctype.h>

/* 
 * Preprocessor directives
 */

#define SWAP_SHORT(x) \
      ((unsigned short)( \
      (((unsigned short)(x) & (unsigned short)0x00ffUL) << 8) | \
      (((unsigned short)(x) & (unsigned short)0xff00UL) >> 8) ))

#define toint(c, _base) (c)+1-(_base)

/*
 * File scope globals
 */
 
DECLARE_GLOBAL_DATA_PTR;

const char ppr3_oems[] = { 'U', 'C', 'A', 'E', 'D', 'S', '1', '2', ' ' };
const char ppr3_mfgs[] = { ' ', 'I', 'R', 'E', 'Z', 'O', 'N', 'K', 'A', 'T', 'D', 'S', 'L', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

const char *ppr3_proto_names[] = {
	"X1",	"X2",	"X3",	"X4",	"X5",	"X6",	"X7",	"X8",
	"X9",	"X10",	"X11",	"X12",	"X13",	"X14",	"X15",	"X16",
	"X17",	"X18",	"X19",	"X20"
};

const int num_proto_names = sizeof(ppr3_proto_names) / sizeof(const char *);

const char *ppr3_rev_names[] = {
	"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L",
	"M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X",
	"Y", "Z", 
	"AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH", "AI", "AJ", 
	"AK", "AL", "AM", "AN", "AO", "AP", "AQ", "AR", "AS", "AT",
	"AU", "AV", "AW", "AX", "AY", "AZ"
};

const int num_rev_names = sizeof(ppr3_rev_names) / sizeof(const char *);

/*
 * Prototypes
 */

int	diag_epi3_submenu_baseboard(void);
int	diag_epi3_pgm_baseboard(epi3Personality *personality);
int	diag_epi3_program_baseboard(void);
int	diag_epi3_squash_baseboard(void);

#ifdef CONFIG_MIPS
/* diag_epi3_reloc_names()
 *	
 * Adjust pointers in arrays of strings to reflect address after relocation
 * Only called on platforms that need it.
 */
void diag_epi3_reloc_names(void)
{
	int i;
	
	for (i = 0; i < num_rev_names; i++) {
		ppr3_rev_names[i] += gd->reloc_off;
	}
	
	for (i = 0; i < num_proto_names; i++) {
		ppr3_proto_names[i] += gd->reloc_off;
	}
}
#endif

/* diag_epi3_menu_run()
 *	
 * Display the personality programming menu
 */
 
void diag_epi3_menu_run(void)
{
	char		key;
	u8		quit;
	int		num_xems;
	int             i;

	diag_epi3_module_init();

#ifdef CONFIG_NUM_XEMS
	num_xems = CONFIG_NUM_XEMS;
#else
	num_xems = 0;
#endif

	quit = 0;

	while (!quit) {
		diag_printf ("\n\nPersonality Menu:\n\n"
				"  0. Previous menu\n"
				"  ----------------------------------\n");
		for (i=1; i<=num_xems; i++) {
			diag_printf ("  %d. XEM Bay %d personality\n", i, i);
		}

#ifndef CONFIG_SYS_NO_FLASH
		diag_printf ("  9. Baseboard personality\n");
#endif /* CONFIG_SYS_NO_FLASH */

#ifdef CONFIG_DIAG_EPI3_PSU
		diag_printf ("  A. PSU1 Personality\n");
#endif

		diag_printf ("\nEnter selection ==> ");

		key = serial_getc();
		key = toupper(key);
		if ( (key >= '0' && key <= '9') ||
		     (key >= 'A' && key <= 'D') ) {
			serial_putc( key );
			serial_putc('\n');
			serial_putc('\n');
		}
		
		switch ( key ) {
			case '0':
				quit = 1;
				break;
#ifdef CONFIG_ATLAT9924TS
                        case '1':
			case '2':
				diag_epi3_module_submenu (EPI3_TYPE_XEM, toint(key,'1'));
				break;
#endif /* CONFIG_ATLAT9924TS */
#ifdef CONFIG_ATLAT10908
			case '1' ... '8':
				diag_epi3_module_submenu (EPI3_TYPE_XEM, toint(key,'1'));
				break;			
#endif /* CONFIG_ATLAT10908 */		
#ifndef CONFIG_SYS_NO_FLASH
			case '9':
				diag_epi3_submenu_baseboard();
				break;
#endif /* CONFIG_SYS_NO_FLASH */
#ifdef CONFIG_DIAG_EPI3_PSU
			case 'A':
				diag_epi3_module_submenu (EPI3_TYPE_PSU, toint(key,'A'));
				break;
#endif
			default:	
				break;
		}
	}
}

int diag_epi3_submenu_baseboard(void)
{
	char		key;
	u8		quit;
	int 		allow_erase;
	quit = 0;
	allow_erase = atl_bootrom_board_attached();

	/* the following boards don't use a bootboard so we need the
	 * factory to be able to erase the personality area */
#if defined(CONFIG_X600) \
	|| defined(CONFIG_ATIAT9424) \
	|| defined(CONFIG_ATRD_X300) \
	|| defined(CONFIG_X200_GE) \
	|| defined (CONFIG_X610) \
	|| defined(CONFIG_X200_X400_FE) \
	|| defined(CONFIG_X210_ATRD) \
	|| defined(CONFIG_X510)
	allow_erase = 1;
#endif

	while (!quit) {
		diag_printf( "\n\nPersonality Menu (Baseboard):\n\n"
				"  0. Previous menu\n"
				"  ----------------------------------\n"
				"  1. Display personalities\n"
				"  2. Program personality\n"	);		
	if (allow_erase)
		diag_printf( 	"  3. Erase personality area\n"
				"  4. Squash personality area\n" );
		diag_printf( 	"\nEnter selection ==> " );

		key = serial_getc();
		if ( key >= '0' && key <= '9' ) {
			serial_putc( key );
			serial_putc('\n');
			serial_putc('\n');
		}
		
		switch ( key ) {
			case '0':
				quit = 1;
				break;
			case '1':
#ifndef CONFIG_SYS_NO_FLASH
				diag_epi3_display_baseboard_history();
#endif
				break;
			case '2':
				diag_epi3_program_baseboard();
				break;
			case '3':
				if (allow_erase)
					diag_epi3_erase_personality_area(0);
				break;
			case '4':
				if (allow_erase)
					diag_epi3_squash_baseboard();
				break;
			default:	
				break;
		}
	}
	
	return 0;
}

int diag_epi3_erase_personality_area(int doit)
{
	char	key;
	char 	buf[512];
	
	key = 0;

	if (!doit) {
		diag_printf(	"\n  WARNING: This option will destroy the contents of the personality block."
				"\n  The personality block should only be erased in the case where it is corrupt."
				"\n  Press Y to proceed, or any other key to return to the previous menu ==> " );

		key = serial_getc();
	
		if ((key >= 0x20) && (key <= 0x7E)) {
			serial_putc( key );
		}

	}
	
	diag_printf("\n");

	if ( key == 'y' || key == 'Y' || doit ) {
		sprintf(buf, "prot off 0x%08X 0x%08X", CONFIG_EPI3_LOCATION, CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE - 1);
		run_command(buf, 0);
		sprintf(buf, "erase 0x%08X 0x%08X", CONFIG_EPI3_LOCATION, CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE - 1);
		run_command(buf, 0);
	}	
	
	return 0;	
}

int diag_epi3_squash_baseboard(void)
{
	epi3Personality * 	last_personality;
	char 			buf[128];
	
	last_personality = (epi3Personality *)malloc(sizeof(epi3Personality));
	
	if (!epi3_getcurrent())
		return 0;
	
	memcpy(last_personality, epi3_getcurrent(), sizeof(epi3Personality));
	
	diag_printf("  Squashing ... ");
	
	sprintf(buf, "prot off %08X %08X", CONFIG_EPI3_LOCATION, CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE - 1);
	run_command(buf, 0);
	
	sprintf(buf, "erase %08X %08X", CONFIG_EPI3_LOCATION, CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE - 1);
	run_command(buf, 0);

	sprintf(buf, "cp.b %08x %08X %08X", (unsigned int)last_personality, CONFIG_EPI3_LOCATION, sizeof(epi3Personality));
	run_command(buf, 0);

	sprintf(buf, "prot on %08X %08X", CONFIG_EPI3_LOCATION, CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE - 1);
	run_command(buf, 0);
	
	free(last_personality);
	
	diag_printf("Complete\n");
	
	return 0;
}

int diag_epi3_program_baseboard(void)
{
	char	params[128];
	char	buf[256];
	int	key;
	
	if (diag_epi3_personality_menu(params, epi3_getcurrent()))
		return 1;
	
	sprintf(buf, "epi3mk %s", params);
	
	diag_printf("\n  Quick command to generate this personality in future (using CLI):\n\n  %s\n\n", buf);
	
	diag_printf("  Write this personality to flash now? (Y/N) ==> " );
	
	key = serial_getc();
	
	if ( key != 'y' && key != 'Y' )
		return 1;
	
	diag_printf("\n\n");
	
	run_command(buf, 0);
	
	return 0;
}


int diag_epi3_pgm_baseboard(epi3Personality *new_personality)
{
	char buf[128];
	
	epi3Personality *epi3Block;
	epi3Block = (epi3Personality *)CONFIG_EPI3_LOCATION;
	
	while (epi3Block->epiMagicNumber != 0xFF) {
		/* Try the next one */
		epi3Block++;
		/* Make sure we haven't reached the end */
		if ((u32)epi3Block >= CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE) {
			/* No more slots */ 
			diag_printf("Error: Out of slots in EPI3 block (might need to erase block and start again?\n");
			return 1;
		}
	}
	

	sprintf(buf, "prot off %08X %08X", CONFIG_EPI3_LOCATION, CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE - 1);
	run_command(buf, 0);
	
	sprintf(buf, "cp.b %08X %08X %08X", (u32)new_personality, (u32)epi3Block, sizeof(epi3Personality));
	run_command(buf, 0);
	
	sprintf(buf, "prot on %08X %08X", CONFIG_EPI3_LOCATION, CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE - 1);
	run_command(buf, 0);
	
	printf("Successfully wrote EPI3 information to flash in position: %u\n", 
	       ((u32)epi3Block - CONFIG_EPI3_LOCATION) / sizeof(epi3Personality));
	
	return 0;
}

int diag_epi3_personality_menu(char *strBuf, epi3Personality * last_personality)
{
	char			epi3_manufacturer[16];
	char			epi3_year[8];
	char			epi3_month[8];
	char			epi3_boardId[8];
	char			epi3_pcbRev[8];
	char			epi3_macAddr[32];
	char			epi3_844bom[8];
	char			epi3_Oem[8];
	char			epi3_hwModLevel[8];
	
	char			epi3_serial_out[64];
	char			epi3_manufacturer_out[8];
	char			epi3_year_out[8];
	char			epi3_month_out[8];
	char			epi3_boardId_out[8];
	char			epi3_pcbRev_out[8];
	char			epi3_macAddr_out[32];
	char			epi3_844bom_out[8];
	char			epi3_Oem_out[8];
	char			epi3_hwModLevel_out[8];
	
	if (last_personality) {
	
		sprintf(epi3_manufacturer,	"%c", ppr3_mfgs[last_personality->epiManufacturer]);
		sprintf(epi3_year,		"%u", last_personality->epiYear);
		sprintf(epi3_month,		"%u", last_personality->epiMonth + 1);
		sprintf(epi3_boardId,		"%u", last_personality->epiBoardId);
			
		if (last_personality->epiPcbRev & 0x8000)
		{
			u16 temp;	
			temp = last_personality->epiPcbRev - 0x8000;
			if (temp < num_proto_names)
				sprintf(epi3_pcbRev, "%s", ppr3_proto_names[temp]);
			else
				sprintf(epi3_pcbRev, "Invalid");
		}
		else
		{	
			u16 temp;	
			temp = last_personality->epiPcbRev;
			if (temp < num_rev_names)
				sprintf(epi3_pcbRev, "%s", ppr3_rev_names[temp]);
			else
				sprintf(epi3_pcbRev, "Invalid");
		}
	
		sprintf(epi3_macAddr, "%02X%02X%02X%02X%02X%02X", 
			(int)last_personality->epiMacAddress[0],
			(int)last_personality->epiMacAddress[1],
			(int)last_personality->epiMacAddress[2],
			(int)last_personality->epiMacAddress[3],
			(int)last_personality->epiMacAddress[4],
			(int)last_personality->epiMacAddress[5]);
			
		sprintf(epi3_844bom, "%s", last_personality->epi844Bom);
			
		sprintf(epi3_Oem, "%c", ppr3_oems[last_personality->epiOEM]);
		sprintf(epi3_hwModLevel, "%u", last_personality->HWModLevel);
	}
	
	if (last_personality)
		diag_printf("\n  Default values are from the current personality\n\n");
	else
		diag_printf("\n  ***WARNING*** No current personality, default values are probably WRONG!\n\n");		

	
	if (last_personality)
	{
		diag_printf("  Serial number ................................. : %s\n", last_personality->epiSerial);
		strcpy(epi3_serial_out, last_personality->epiSerial);
	}
	
	else
	{
		if (!bootmenu_string_prompt(epi3_serial_out, "  Serial number (string, 18 characters max) .....", 
		     "NONE", sizeof(epi3_serial_out)))
			goto fail;
	}
		
	if (!bootmenu_string_prompt(epi3_manufacturer_out, "  Manufacturer code (I,R,E,O,N,K,A,T,D,S,L,1,2) .", 
	     last_personality ? epi3_manufacturer : "A", sizeof(epi3_manufacturer_out)))
		goto fail;
	
	if (!bootmenu_string_prompt(epi3_year_out, "  Manufacture year (YY) .........................",
	     last_personality ? epi3_year : "00", sizeof(epi3_year_out)))
		goto fail;
		
	if (!bootmenu_string_prompt(epi3_month_out, "  Manufacture month (MM) ........................", 
	     last_personality ? epi3_month : "01", sizeof(epi3_month_out)))
		goto fail;
		
	if (!bootmenu_string_prompt(epi3_boardId_out, "  Board ID (integer) ............................", 
	     last_personality ? epi3_boardId : "0", sizeof(epi3_boardId_out)))
		goto fail;
	
	if (!bootmenu_string_prompt(epi3_pcbRev_out, "  PCB revision (X1-X20, A-Z, AA-AZ) .............", 
	     last_personality ? epi3_pcbRev : "A", sizeof(epi3_pcbRev_out)))
		goto fail;
			
	if (!bootmenu_string_prompt(epi3_macAddr_out, "  MAC address (XXXXXXXXXXXX) ....................", 
	     last_personality ? epi3_macAddr : "000000000000", sizeof(epi3_macAddr_out)))
		goto fail;
	
	if (!bootmenu_string_prompt(epi3_844bom_out, "  844 BOM level (string, 3 characters max) ......", 
	     last_personality ? epi3_844bom : "A", sizeof(epi3_844bom_out)))
		goto fail;
		
	if (!bootmenu_string_prompt(epi3_Oem_out, "  OEM code (U,C,A,E,D,S,1,2) ....................", 
	     last_personality ? epi3_Oem : "U", sizeof(epi3_Oem_out)))
		goto fail;
		
	if (!bootmenu_string_prompt(epi3_hwModLevel_out, "  HW mod level (integer) ........................", 
	     last_personality ? epi3_hwModLevel : "0", sizeof(epi3_hwModLevel_out)))
		goto fail;
	
	if (strchr(epi3_serial_out, ' ') || strchr(epi3_manufacturer_out, ' ') || strchr(epi3_year_out, ' ') || strchr(epi3_month_out, ' ') 
				|| strchr(epi3_boardId_out, ' ') || strchr(epi3_pcbRev_out, ' ') || strchr(epi3_macAddr_out, ' ') 
				|| strchr(epi3_844bom_out, ' ') || strchr(epi3_Oem_out, ' ') || strchr(epi3_hwModLevel_out, ' '))
	{
		diag_printf("\nError: Field(s) contain space(s)\n");
		return 1;
	}
	
	sprintf(strBuf, "%s %s %s %s %s %s %s %s %s %s", 
		epi3_serial_out, epi3_manufacturer_out, epi3_year_out, epi3_month_out, epi3_boardId_out,
		epi3_pcbRev_out, epi3_macAddr_out, epi3_844bom_out, epi3_Oem_out, epi3_hwModLevel_out);
	
	return 0;
fail:
	return 1;
}

#ifdef CONFIG_SMT

/**
 * Routine for Calculating the checksum of a field in the proprietary HW Database
 *
 * @param start pointer to the data to checksum
 * @param count length of the data supplied
 *
 */

u32 hwdb_checksum(const void * start, u32 count)
{
	u8 *	bytePt;
	u32 	value;
	u32 	crc32;
	u32 	bit;

	crc32 = 0xFFFFFFFF;
	for (bytePt = (u8 *) start; bytePt < (u8 *) start + count; bytePt++)
	{
		value = *bytePt;
		for (bit = 8; bit > 0; bit--)
		{
			if (((value ^ crc32) & 0x1) != 0)
			{
				crc32 = (crc32 >> 1) ^ 0xEDB88320;
			}	/*Generator polynomial term x**32 is implicit as bit -1*/
			else
			{
				crc32 >>= 1;
			}
			value >>= 1;
		}
	}

	return crc32;
} 

/**
 * Calculate the size of an entry in the proprietary HW Database for malloc()
 *
 * @param data_size size of data in field
 * @param name the name of the field
 *
 */
 
u32 hwdb_entry_getsize(u32 data_size, char *name)
{
	hwdb_entry_header	header;
	u32 			t;
	u32			sz;
	u32			name_size;
	
	sz = 0;
	memset(&header, 0, sizeof(header));
	
	name_size = strlen(name) + 1;
	
	header.nameOffset  = (sizeof(header) + 3) & ~0x3;
	header.valueOffset = ((header.nameOffset + 
			sizeof(u32) + sizeof(u32) + name_size) + 3) & ~0x3;
	
	header.entrySize = ((header.valueOffset + sizeof(u32) + sizeof(u32) +
			data_size) + 3) & ~0x3;
	
	t = sizeof(header);
	sz += t;
	t = (header.nameOffset - sizeof(header)); /* pad */
	sz += t;
	t = sizeof(u32); /* nameSize */
	sz += t;
	t = sizeof(u32); /* nameCheckSum */
	sz += t;
	t = name_size;
	sz += t;
	t = header.valueOffset - sz; /* pad */
	sz += t;
	t = sizeof(u32); /* valueSize */
	sz += t;
	t = sizeof(u32); /* valueChecksum */
	sz += t;
	t = data_size;
	sz += t;
	t = header.entrySize - sz; /* pad */
	sz += t;
	
	return sz;
}

/**
 * Make an entry for the proprietary HW Database
 *
 * @param hwdb_entry pre-allocated buffer to place the entry into
 * @param data the data to place into the entry
 * @param data_size size of the field data
 * @param name of the field
 *
 */
 
void hwdb_make_entry(void *hwdb_entry, void *data, u32 data_size, char *name)
{
	hwdb_entry_header	header;
	u32			t;
	u32			sz;
	u32			pad;
	u32			name_size;
	u32			name_checksum;
	u32			data_checksum;
	
	sz = 0;
	pad = 0;
	
	memset(&header, 0, sizeof(header));
	
	name_size = strlen(name) + 1;
	name_checksum = hwdb_checksum(name, name_size);
	data_checksum = hwdb_checksum(data, data_size);
	
	header.magic = 0x48574403;
	header.state = 0xFFFFFFFF;
	header.nameOffset  = (sizeof(header) + 3) & ~0x3;
	header.valueOffset = ((header.nameOffset + 
			sizeof(u32) + sizeof(u32) + strlen(name) + 1) + 3) & ~0x3;
	
	header.entrySize = ((header.valueOffset + sizeof(u32) + sizeof(u32) +
			data_size) + 3) & ~0x3;
	
	t = sizeof(header);
	memcpy(hwdb_entry + sz, &header, t);
	sz += t;
	
	t = (header.nameOffset - sizeof(header)); /* pad */
	memcpy(hwdb_entry + sz, &pad, t);
	sz += t;
	
	t = sizeof(u32); /* nameSize */	
	memcpy(hwdb_entry + sz, &name_size, t);
	sz += t;
	
	t = sizeof(u32); /* nameCheckSum */
	memcpy(hwdb_entry + sz, &name_checksum, t);
	sz += t;
	
	t = name_size;
	memcpy(hwdb_entry + sz, name, t);
	sz += t;
	
	t = header.valueOffset - sz; /* pad */
	memcpy(hwdb_entry + sz, &pad, t);
	sz += t;
	
	t = sizeof(u32); /* valueSize */
	memcpy(hwdb_entry + sz, &data_size, t);
	sz += t;
	
	t = sizeof(u32); /* valueChecksum */
	memcpy(hwdb_entry + sz, &data_checksum, t);
	sz += t;
	
	t = data_size;
	memcpy(hwdb_entry + sz, data, t);
	sz += t;
	
	t = header.entrySize - sz;
	memcpy(hwdb_entry + sz, &pad, t);
	sz += t;
}

/**
 * Routine for importing the NST HW DB to and EPI3 block
 * Part of the AlliedWare+ release system
 *
 * @param cmdtp command internal data
 * @param flag command flag
 * @param argc number of arguments supplied to the command
 * @param argv arguments list
 * @return 0 on success, 1 otherwise
 */

int do_epi3_epi3import(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	unsigned short *	start;
	epi3Personality * 	block;
	epi3Personality * 	new_personality;
	u32 			ret;
	char *			cmd_args[5];
	int			numblocks;
	unsigned short		epi3header;
	
	start = (unsigned short *)CONFIG_EPI3_LOCATION;
	block = NULL;
	epi3header = 0x0155;
	numblocks = 0;
	
	if (epi3_getcurrent())
	{
		printf("The personality on this device already appears to be in AlliedWare 4.X.X format\n");
		return 0;
	}
	
	while ((u32)start <= (CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE))
	{
		if (*start == epi3header)
		{
			if (!epi3_dataok((epi3Personality *)start))
			{
				printf("EPI3 block found at 0x%08X\n", start);
				numblocks++;
			}
			else
			{
				printf("Found something at 0x%08X that looked like EPI3, but checksum was bad\n", start);
			}
			
		}
		(unsigned char *)start++;
	}
	
	if (!numblocks)
	{
		printf("There does not appear to be any personality information stored in this device\n");
		return 1;
	}
	
	start = (unsigned short *)CONFIG_EPI3_LOCATION;
	
	
	while ((u32)start <= (CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE))
	{
		if (*start == epi3header)
		{
			if (!epi3_dataok((epi3Personality *)start))
			{			
				numblocks--;
				
				if (!numblocks)
				{
					printf("Using block at 0x%08X\n", start);
					block = (epi3Personality *)start;
					break;
				}
			}
			else
			{
				printf("found something at 0x%08X that looked like EPI3, but checksum was bad\n", start);
			}
			
		}
		(unsigned char *)start++;
	}
	
	new_personality = malloc(sizeof(epi3Personality));
	memcpy(new_personality, block, sizeof(epi3Personality));
	
	cmd_args[0] = strdup("prot");
	cmd_args[1] = strdup("off");
	cmd_args[2] = (char *)malloc(9);
	cmd_args[3] = (char *)malloc(9);
	cmd_args[4] = NULL;
	
	sprintf(cmd_args[2], "%08x", CONFIG_EPI3_LOCATION);
	sprintf(cmd_args[3], "%08x", CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE - 1);
	
	ret = do_protect(cmdtp, flag, 4, cmd_args);
	
	free(cmd_args[0]);
	free(cmd_args[1]);
	free(cmd_args[2]);
	free(cmd_args[3]);
	
	if (ret)
		goto fail;
	
	cmd_args[0] = strdup("erase");
	cmd_args[1] = (char *)malloc(9);
	cmd_args[2] = (char *)malloc(9);
	cmd_args[3] = NULL;
	
	sprintf(cmd_args[1], "%08x", CONFIG_EPI3_LOCATION);
	sprintf(cmd_args[2], "%08x", CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE - 1);
	
	ret = do_flerase(cmdtp, flag, 3, cmd_args);
	
	free(cmd_args[0]);
	free(cmd_args[1]);
	free(cmd_args[2]);
	free(cmd_args[3]);
	
	if (ret)
		goto fail;
	
	cmd_args[0] = strdup("cp.b");
	cmd_args[1] = (char *)malloc(9);
	cmd_args[2] = (char *)malloc(9);
	cmd_args[3] = (char *)malloc(9);
	cmd_args[4] = NULL;
	
	sprintf(cmd_args[1], "%08x", (u32)new_personality);
	sprintf(cmd_args[2], "%08x", (u32)CONFIG_EPI3_LOCATION);
	sprintf(cmd_args[3], "%08x", sizeof(epi3Personality));
	
	ret = do_mem_cp(cmdtp, flag, 4, cmd_args);
	
	free(cmd_args[0]);
	free(cmd_args[1]);
	free(cmd_args[2]);
	free(cmd_args[3]);
	
	if (ret)
		goto fail;
		
	cmd_args[0] = strdup("prot");
	cmd_args[1] = strdup("on");
	cmd_args[2] = (char *)malloc(9);
	cmd_args[3] = (char *)malloc(9);
	cmd_args[4] = NULL;
	
	sprintf(cmd_args[2], "%08x", CONFIG_EPI3_LOCATION);
	sprintf(cmd_args[3], "%08x", CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE - 1);
	
	ret = do_protect(cmdtp, flag, 4, cmd_args);
	
	free(cmd_args[0]);
	free(cmd_args[1]);
	free(cmd_args[2]);
	free(cmd_args[3]);
	
	if (ret)
		goto fail;
	
	printf("The personality has successfully been converted to AlliedWare 4.X.X format\n");
	printf("If you did this by mistake, run 'epi3e' to convert it back again.\n");

	free(new_personality);
	
	return 0;
	
fail:
	
		free(new_personality);
	return ret;
}

/**
 * Routine to export EPI3 back to an NST HW DB
 * Part of the AlliedWare+ release system
 *
 * @param cmdtp command internal data
 * @param flag command flag
 * @param argc number of arguments supplied to the command
 * @param argv arguments list
 * @return 0 on success, 1 otherwise
 */
  
int do_epi3_epi3export(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	epi3Personality	* 	block;
	epi3Personality *	old_personality;
	u32 			ret;
	u32			hwdb_entry_size1;
	u32			hwdb_entry_size2;
	u32			hwdb_size;
	char *			cmd_args[5];
	void *			hwdb_entry;
	void *			hwdb;
	
	char 			personality1_name[] = "BoardPersonality";
	char 			personality2_name[] = "BoardPersonalityCopy2";
	
	block = NULL;
	old_personality = NULL;
	hwdb = NULL;
	block = epi3_getcurrent();
	ret = 1;
	
	if (!block)
	{
		printf("There is currently no valid personality on this device (possibly already NST format?)\n");
		goto fail;
	}
	
	old_personality = malloc(sizeof(epi3Personality));
	memcpy(old_personality, block, sizeof(epi3Personality));
	
	hwdb_entry_size1 = hwdb_entry_getsize(sizeof(epi3Personality), personality1_name);
	hwdb_entry_size2 = hwdb_entry_getsize(sizeof(epi3Personality), personality2_name);
	hwdb_size = hwdb_entry_size1 + hwdb_entry_size2;
	
	hwdb = malloc(hwdb_size);
	
	/* entry 1 */
		
	hwdb_entry = malloc(hwdb_entry_size1);
	
	hwdb_make_entry(hwdb_entry, old_personality, sizeof(epi3Personality), personality1_name);
	
	memcpy((void *)hwdb, hwdb_entry, hwdb_entry_size1);
	
	free(hwdb_entry);
	
	/* entry 2 */
	
	hwdb_entry = malloc(hwdb_entry_size2);
	
	hwdb_make_entry(hwdb_entry, old_personality, sizeof(epi3Personality), personality2_name);
	
	memcpy((void *)hwdb + hwdb_entry_size1, hwdb_entry, hwdb_entry_size2);
	
	free(hwdb_entry);
	
	
	cmd_args[0] = strdup("prot");
	cmd_args[1] = strdup("off");
	cmd_args[2] = (char *)malloc(9);
	cmd_args[3] = (char *)malloc(9);
	cmd_args[4] = NULL;
	
	sprintf(cmd_args[2], "%08x", CONFIG_EPI3_LOCATION);
	sprintf(cmd_args[3], "%08x", CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE - 1);
	
	ret = do_protect(cmdtp, flag, 4, cmd_args);
	
	free(cmd_args[0]);
	free(cmd_args[1]);
	free(cmd_args[2]);
	free(cmd_args[3]);
	
	if (ret)
		goto fail;
	
	cmd_args[0] = strdup("erase");
	cmd_args[1] = (char *)malloc(9);
	cmd_args[2] = (char *)malloc(9);
	cmd_args[3] = NULL;
	
	sprintf(cmd_args[1], "%08x", CONFIG_EPI3_LOCATION);
	sprintf(cmd_args[2], "%08x", CONFIG_EPI3_LOCATION + CONFIG_EPI3_SIZE - 1);
	
	ret = do_flerase(cmdtp, flag, 3, cmd_args);
	
	free(cmd_args[0]);
	free(cmd_args[1]);
	free(cmd_args[2]);
	free(cmd_args[3]);
	
	if (ret)
		goto fail;
	
	cmd_args[0] = strdup("cp.b");
	cmd_args[1] = (char *)malloc(9);
	cmd_args[2] = (char *)malloc(9);
	cmd_args[3] = (char *)malloc(9);
	cmd_args[4] = NULL;
	
	sprintf(cmd_args[1], "%08x", (u32)hwdb);
	sprintf(cmd_args[2], "%08x", (u32)CONFIG_EPI3_LOCATION);
	sprintf(cmd_args[3], "%08x", hwdb_size);
	
	ret = do_mem_cp(cmdtp, flag, 4, cmd_args);
	
	free(cmd_args[0]);
	free(cmd_args[1]);
	free(cmd_args[2]);
	free(cmd_args[3]);
	
	if (ret)
		goto fail;
	
	printf("The personality has successfully been converted to AlliedWare 3.X.X Format\n");
	printf("If you did this by mistake, run 'epi3i' to convert it back again.\n");
	
	free(old_personality);
	free(hwdb);
	
	return 0;
	
fail:
		if (old_personality);
		free(old_personality);
		
		if (hwdb)
			free(hwdb);
		
		return ret;
}

U_BOOT_CMD(
		epi3i, 1, 1, do_epi3_epi3import,
	"epi3i\t- Import EPI3 information from NST HW Database\n",
	"\n"
	  );	 
	  
U_BOOT_CMD(
		epi3e, 1, 1, do_epi3_epi3export,
	"epi3e\t- Export EPI3 information to an NST HW Database\n",
	"\n"
	  );	 

#endif /* CONFIG_SMT */
