/*
 * diag_idprom.c
 *
 * Copyright (c) 2010 Allied Telesis Labs
 *
 * This file is licensed under the terms of the GNU General Public License
 * version 2. This program is licensed "as is" without any warranty of any
 * kind, whether express or implied.
 */
#include <common.h>
#include <config.h>
#include <command.h>
#include <malloc.h>
#include <i2c.h>
#include <linux/ctype.h>
#include <bootmenu.h>
#include <idprom.h>
#include <asm/errno.h>
#ifdef CONFIG_ARM
#include <asm/mach-types.h>
#endif

DECLARE_GLOBAL_DATA_PTR;

#define IDPROM_WRITE_DELAY 1000

#define IDPROM_FLAG_CHASSIS	(1 << 0)
#define IDPROM_FLAG_FAN		(1 << 1)

static struct idprom *saved_id;
static u8 idprom_address = CONFIG_IDPROM_LOCAL_ADDR;

#ifdef CONFIG_I2C_BACKPLANE_BUS
int backplane_access(int enable);
#endif

static void idprom_parse(struct idprom *id)
{
        char *p = id->raw;
        char *pr, *pn;
        while (*p) {
                if (strncmp("sn=", p, 3) == 0) {
                        id->sn = strchr(p, '=')+1;
                } else if (strncmp("mn=", p, 3) == 0) {
                        id->mn =  strchr(p, '=')+1;
                } else if (strncmp("smn=", p, 4) == 0) {
                        id->smn =  strchr(p, '=')+1;
                } else if (strncmp("pn=", p, 3) == 0) {
                        id->pn =  strchr(p, '=')+1;
                } else if (strncmp("hv=", p, 3) == 0) {
                        id->hv =  strchr(p, '=')+1;
                } else if (strncmp("hm=", p, 3) == 0) {
                        id->hm =  strchr(p, '=')+1;
                } else if (strncmp("svc=", p, 4) == 0) {
                        id->svc =  strchr(p, '=')+1;
                } else if (strncmp("bd=", p, 3) == 0) {
                        id->bd =  strchr(p, '=')+1;
                } else if (strncmp("mac0=", p, 5) == 0) {
                        id->mac0 =  strchr(p, '=')+1;
                } else if (strncmp("nmac=", p, 5) == 0) {
                        id->nmac =  strchr(p, '=')+1;
                } else if (strncmp("oem=", p, 4) == 0) {
                        id->oem =  strchr(p, '=')+1;
                }

                /* Convert "\r\n" or "\n" terminations to a sequence of '\0' */
                pr = strchr(p, '\r');
                pn = strchr(p, '\n');
                if (pr && (pn == (pr+1))) {
                        *pr = '\0';
                        *pn = '\0';
                        p = pn;
                } else if (pn) {
                        *pn = '\0';
                        p = pn;
                }
                p++;
        }
}

static int idprom_verify(struct idprom *id) {
	char *p = id->raw;
	char *e = strrchr(p, '\n');
	int len;
	uint32_t crc = 0;
	uint32_t stored_crc = 0;

	if (!e)
		return 0;
	else
		e++;

	len = (e - p);
	memcpy(&stored_crc, e+1, 4);
	stored_crc = ntohl(stored_crc);

	crc = crc32(crc, (u8 *)p, len);

	id->crc_ok = crc == stored_crc;
	return id->crc_ok;
}

static void idprom_show(struct idprom *id)
{
	printf("checksum %s\n", id->crc_ok ? "OK" : "NG");
	printf("sn=%s\n", id->sn);
	printf("mn=%s\n", id->mn);
	printf("smn=%s\n", id->smn);
	printf("pn=%s\n", id->pn);
	printf("hv=%s\n", id->hv);
	printf("hm=%s\n", id->hm);
	printf("svc=%s\n", id->svc);
	printf("bd=%s\n", id->bd);
	printf("mac0=%s\n", id->mac0);
	printf("nmac=%s\n", id->nmac);
	printf("oem=%s\n", id->oem);
}

/**
 * Read the raw idprom data from the specified address
 */
static struct idprom *idprom_read(u8 dev_addr)
{
	struct idprom *id = calloc(1, sizeof(struct idprom));

	if (i2c_read(dev_addr, 0, 1, (u8 *)id->raw, IDPROM_SIZE) < 0) {
		free(id);
		return NULL;
	}
	id->raw[IDPROM_SIZE] = '\0';

	return id;
}

/**
 * Read the idprom data from the specified address. This verifies the
 * checksum and parses the idprom into fields accessible through the members
 * of a struct idprom.
 */
static struct idprom *idprom_init(u8 dev_addr)
{
	struct idprom *id;

	if (!(id = idprom_read(dev_addr)))
		return NULL;

	if (!idprom_verify(id)) {
		free(id);
		return NULL;
	}
	idprom_parse(id);

	return id;
}

static int idprom_erase(u8 dev_addr)
{
	uint i;
	u8 byte = 0xff;

	for (i = 0; i < IDPROM_SIZE; i++) {
		if (i2c_write(dev_addr, i, 1, &byte, 1) != 0) {
			printf("Error: i2c write error\n");
			return -1;
		}
		udelay(IDPROM_WRITE_DELAY);
	}

	return 0;
}

static int idprom_write(u8 dev_addr, struct idprom *id)
{
	struct idprom temp;
	uint32_t crc1, crc2;
	uint i;

	for (i = 0; i < IDPROM_SIZE; i++) {
		if (i2c_write(dev_addr, i, 1, (u8*)&id->raw[i], 1) != 0) {
			printf("Error: i2c write error\n");
			return -1;
		}
		udelay(IDPROM_WRITE_DELAY);
	}

	/* re-read and verify the CRC */
	if (i2c_read(dev_addr, 0, 1, (u8 *)temp.raw, IDPROM_SIZE) < 0) {
		return -1;
	}
	temp.raw[IDPROM_SIZE] = '\0';

	crc1 = crc32(0, (u8 *)id->raw, strlen(id->raw));
	crc2 = crc32(0, (u8 *)temp.raw, strlen(temp.raw));

	if (crc1 != crc2) {
		printf("Error: idprom verify failed\n");
		return -1;
	}

	return 0;
}

struct idprom *idprom_get(void)
{
	if (!saved_id)
		saved_id = idprom_init(idprom_address);

	return saved_id;
}

/*
 * parse the non-delimited ascii Ethernet address from an idprom into a proper
 * array of bytes suitable for consumption by the rest of u-boot.
 */
void idprom_parse_enetaddr(struct idprom *id, uchar *enetaddr)
{
	if (id->mac0) {
		int i;
		char bchar[3] = "\0\0\0";
		for(i = 0; i < 6; i++) {
			strncpy(bchar, &id->mac0[i*2], 2);
			enetaddr[i] = simple_strtoul(bchar, NULL, 16);
		}
	}
}

void idprom_setmacaddr(int index)
{
	struct idprom *id = idprom_get();
	uchar mac[6];

	if(id) {
		idprom_parse_enetaddr(id, mac);
		switch (index) {
		case 1:
			eth_setenv_enetaddr("eth1addr", mac);
			break;
		case 2:
			eth_setenv_enetaddr("eth2addr", mac);
			break;
		case 3:
			eth_setenv_enetaddr("eth3addr", mac);
			break;
		case 0:
		default:
			eth_setenv_enetaddr("ethaddr", mac);
			break;
		}
	}
}

#ifdef CONFIG_ARM

struct mn_machid {
	const char *mn;
	int machid;
};

static struct mn_machid modelnumber_to_machid[] = {
	{"AT-SBx31CFC", MACH_TYPE_SBX81CFC},
	{"AT-SBx81CFC", MACH_TYPE_SBX81CFC},
	{"AT-SBx31GP24", MACH_TYPE_SBX81GP24},
	{"AT-SBx81GP24", MACH_TYPE_SBX81GP24},
	{"AT-SBx31GT24", MACH_TYPE_SBX81GT24},
	{"AT-SBx81GT24", MACH_TYPE_SBX81GT24},
	{"AT-SBx31XZ4", MACH_TYPE_SBX81XZ4},
	{"AT-SBx81XZ4", MACH_TYPE_SBX81XZ4},
	{"AT-SBx31GS24", MACH_TYPE_SBX81GS24},
	{"AT-SBx81GS24", MACH_TYPE_SBX81GS24},
	{"AT-SBx81GS24A", MACH_TYPE_SBX81GS24},
	{"AT-SBx31XS6", MACH_TYPE_SBX81XS6},
	{"AT-SBx81XS6", MACH_TYPE_SBX81XS6},
	{"AT-SBx31GT24A", MACH_TYPE_SBX81GT24A},
	{"AT-SBx81GT24A", MACH_TYPE_SBX81GT24A},
	{"AT-SBx31GP24A", MACH_TYPE_SBX81GP24A},
	{"AT-SBx81GP24A", MACH_TYPE_SBX81GP24A},
	{"AT-SBx31GC40", MACH_TYPE_SBX81GT40},
	{"AT-SBx31GT40", MACH_TYPE_SBX81GT40},
	{"AT-SBx81GT40", MACH_TYPE_SBX81GT40},
	{"AT-SBx31XS16", MACH_TYPE_SBX81XS16},
	{"AT-SBx81XS16", MACH_TYPE_SBX81XS16},
	{NULL, 0},
};

/**
 * set the machine id based on the idprom contents
 */
int idprom_setmachid(void)
{
	struct idprom *id = idprom_get();
	struct mn_machid *pt;
	char buf[16];

	if (!id)
		return -ENODEV;

	// if machid is already set then don't modify it
	if (getenv("machid"))
		return 0;

	pt = modelnumber_to_machid;
	while(pt->mn) {
		if (strcmp(pt->mn, id->mn) == 0) {
			sprintf(buf, "%#x", pt->machid);
			setenv("machid", buf);
			return 0;
		}
		pt++;
	}

	return -ENOENT;
}
#endif

int do_idprom_address(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	if (argc > 1) {
		if (strcmp(argv[1], "cpu") == 0) {
			i2c_set_bus_num(0);
			idprom_address = CONFIG_IDPROM_LOCAL_ADDR;
		}
#ifdef CONFIG_I2C_CHASSIS_BUS
		else if (strcmp(argv[1], "shelf") == 0 ||
		    strcmp(argv[1], "chassis") == 0) {
			i2c_set_bus_num(CONFIG_I2C_CHASSIS_BUS);
			idprom_address = CONFIG_IDPROM_BACKPLANE_ADDR;
		}
#endif
#ifdef CONFIG_I2C_FANTRAY_BUS
		else if (strcmp(argv[1], "fan") == 0) {
			i2c_set_bus_num(CONFIG_I2C_FANTRAY_BUS);
			idprom_address = CONFIG_IDPROM_BACKPLANE_ADDR;
		}
#endif
		else {
			i2c_set_bus_num(0);
			idprom_address = simple_strtoul(argv[1], NULL, 16);
		}
	}

	printf("IDPROM address %x\n", idprom_address);

	return 0;
}

int do_idprom_show(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	struct idprom *id = idprom_init(idprom_address);

	if (id) {
		idprom_show(id);
		free(id);
	} else {
		printf("No idprom information found in device at %x\n",
			idprom_address);
	}

	return 0;
}

int do_idprom_erase(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	if (idprom_erase(idprom_address) < 0) {
		printf("Error: Failed to erase idprom\n");
		return -1;
	}

	return 0;
}

/**
 * Write new idprom data. This overwrites any existing idprom data.
 *
 * Arguments are expected to be key=value pairs.
 */
int do_idprom_write(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	int ret = 0;
	struct idprom *id = calloc(1, sizeof(struct idprom));
	uint32_t crc;
	char *pt = id->raw;

	if (argc < 2) {
		printf("Error: Too few parameters\n");
		ret = -1;
		goto out;
	}

	memset(pt, 0xff, IDPROM_SIZE);

	/* Strip off leading command argument */
	argc --;
	argv ++;

	/* Copies arguments and does basic verification that they are
	 * key=value pairs */
	while (argc > 0) {
		if (!strchr(argv[0], '=')) {
			printf("Error: expected key=value got '%s'\n",
				argv[0]);
			ret = -1;
			goto out;
		}
		pt += sprintf(pt, "%s\n", argv[0]);
		argc --;
		argv ++;
	}
	*pt++ = '\0';

	/* Calculate CRC */
	crc = crc32(0, (u8 *)id->raw, strlen(id->raw));
	crc = htonl(crc);
	memcpy(pt, &crc, sizeof(uint32_t));

	if (idprom_write(idprom_address, id) < 0) {
		printf("Error: Failed to write idprom\n");
		ret = -1;
		goto out;
	}

	printf("Successfully wrote IDPROM information to %#02x\n", idprom_address);
out:
	if (id)
		free(id);

	return ret;
}

/**
 * Modify existing idprom data. Other existing data preserved.
 *
 * Argument is expected to be a key=value pair.
 */
int do_idprom_modify(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	int ret = 0;
	struct idprom *cur_id = idprom_read(idprom_address);
	struct idprom *id = calloc(1, sizeof(struct idprom));
	uint32_t crc;
	char *cur_pt = cur_id->raw;
	char *pt = id->raw;
	char *key, *value;
	char *cur_key;

	if (!cur_id) {
		printf("Error: No previous personality to get values from\n");
		ret = -1;
		goto out;
	}

	if (argc != 2) {
		printf("Error: Too %s parameters\n",
			argc > 2 ? "many" : "few");
		ret = -1;
		goto out;
	}

	memset(pt, 0xff, IDPROM_SIZE);

	value = argv[1];
	key = strsep(&value, "=");
	if (!value) {
		printf("Error: expected key=value got '%s'\n",
			argv[0]);
		ret = -1;
		goto out;
	}

	/* Copy up to the current key (or up to the null if its a new key) */
	cur_key = strstr(cur_pt, key);
	while (*cur_pt && cur_pt != cur_key) {
		*pt++ = *cur_pt++;
	}

	/* skip past the old key and value*/
	if (cur_key) {
		cur_pt = strchr(cur_key, '\n');
		if(cur_pt)
			cur_pt ++;
	}

	/* add the new key and value */
	pt += sprintf(pt, "%s=%s\n", key, value);

	/* copy the rest */
	while (cur_pt && *cur_pt) {
		*pt++ = *cur_pt++;
	}

	/* Add null termination */
	*pt++ = '\0';

	/* Calculate CRC */
	crc = crc32(0, (u8 *)id->raw, strlen(id->raw));
	crc = htonl(crc);
	memcpy(pt, &crc, sizeof(uint32_t));

	/* Finally write the modified id */
	if (idprom_write(idprom_address, id) < 0) {
		printf("Error: Failed to write idprom\n");
		ret = -1;
		goto out;
	}

	printf("Successfully wrote IDPROM information to %#02x\n", idprom_address);
out:
	if (cur_id)
		free(cur_id);
	if (id)
		free(id);

	return ret;
}

int do_idprom(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	/* Strip off leading command argument */
	argc--;
	argv++;


	if (!strncmp(argv[0], "sh", 2))
		return do_idprom_show(cmdtp, flag, argc, argv);
	if (!strncmp(argv[0], "er", 2))
		return do_idprom_erase(cmdtp, flag, argc, argv);
	if (!strncmp(argv[0], "wr", 2))
		return do_idprom_write(cmdtp, flag, argc, argv);
	if (!strncmp(argv[0], "mo", 2))
		return do_idprom_modify(cmdtp, flag, argc, argv);
	if (!strncmp(argv[0], "ad", 2))
		return do_idprom_address(cmdtp, flag, argc, argv);

	cmd_usage(cmdtp);
	return 0;
}

U_BOOT_CMD(
	idprom, 13, 1, do_idprom,
	"IDPROM subsystem",
	"show - display IDPROM contents\n"
	"idprom address [address]\n"
	"idprom erase - Erase IDPROM\n"
	"idprom write key=value [key=value...] - Write new data to IDPROM\n"
	"idprom modify key=value - Add or modify a single field\n"
	);

static int diag_idprom_program_menu(char* params, struct idprom *id, int flags)
{
	char *pt = params;
	int n;
	int ret = 0;

	if (id)
		printf("\n  Default values are from the current personality\n\n");
	else
		printf("\n  ***WARNING*** No current personality\n\n");

	pt += sprintf(pt, "sn=");
	n = bootmenu_string_prompt(pt, "  Serial Number .............",
			id ? id->sn : "NONE", 18);
	if (n < 0) {
		ret = -1;
		goto out;
	}
	pt += n;

	pt += sprintf(pt, " mn=");
	n = bootmenu_string_prompt(pt, "  Model Number ..............",
			id ? id->mn : "NONE", 16);
	if (n < 0) {
		ret = -1;
		goto out;
	}
	pt += n;

	if ((flags & (IDPROM_FLAG_CHASSIS|IDPROM_FLAG_FAN)) == 0) {
		pt += sprintf(pt, " smn=");
		n = bootmenu_string_prompt(pt, "  Software Model Number .....",
				id ? id->smn : "NONE", 10);
		if (n < 0) {
			ret = -1;
			goto out;
		}
		pt += n;
	}

	pt += sprintf(pt, " pn=");
	n = bootmenu_string_prompt(pt, "  Part Number ...............",
			id ? id->pn : "844-000000-00", 16);
	if (n < 0) {
		ret = -1;
		goto out;
	}
	pt += n;

	pt += sprintf(pt, " hv=");
	n = bootmenu_string_prompt(pt, "  Hardware version ..........",
			id ? id->hv : "A", 4);
	if (n < 0) {
		ret = -1;
		goto out;
	}
	pt += n;

	if ((flags & (IDPROM_FLAG_FAN)) == 0) {
		pt += sprintf(pt, " hm=");
		n = bootmenu_string_prompt(pt, "  Hardware Matrix ...........",
				id ? id->hm : "H0", 4);
		if (n < 0) {
			ret = -1;
			goto out;
		}
		pt += n;
	}

	pt += sprintf(pt, " svc=");
	n = bootmenu_string_prompt(pt, "  SW Version Compatibility ..",
			id ? id->svc : "00", 4);
	if (n < 0) {
		ret = -1;
		goto out;
	}
	pt += n;

	pt += sprintf(pt, " bd=");
	n = bootmenu_string_prompt(pt, "  Manufacture Date (MMDDYYYY)",
			id ? id->bd : "01011970", 9);
	if (n < 0) {
		ret = -1;
		goto out;
	}
	pt += n;

	if ((flags & (IDPROM_FLAG_FAN)) == 0) {
		pt += sprintf(pt, " mac0=");
		n = bootmenu_string_prompt(pt, "  First MAC address .........",
				id ? id->mac0 : "000000000001", 16);
		if (n < 0) {
			ret = -1;
			goto out;
		}
		pt += n;
	}

	if ((flags & (IDPROM_FLAG_FAN)) == 0) {
		pt += sprintf(pt, " nmac=");
		n = bootmenu_string_prompt(pt, "  Number of MAC addresses....",
				id ? id->nmac : "1", 4);
		if (n < 0) {
			ret = -1;
			goto out;
		}
		pt += n;
	}

	if ((flags & (IDPROM_FLAG_CHASSIS|IDPROM_FLAG_FAN)) == 0) {
		pt += sprintf(pt, " oem=");
		n = bootmenu_string_prompt(pt, "  OEM code (U,C,A,E,D,S,1,2).",
				id ? id->oem : "U", 2);
		if (n < 0) {
			ret = -1;
			goto out;
		}
		pt += n;
	}

	*pt++ = '\0';

out:
	return ret;
}

static void diag_idprom_program_baseboard(void)
{
	char params[256];
	char buf[256 + 32];
	int ret;

	if (diag_idprom_program_menu(params, idprom_get(), 0) < 0)
		return;

	sprintf(buf, "idprom write %s", params);
	printf ("\n  Commands to generate this personality using CLI:\n\n");
	printf ("  %s\n", buf);
	if (atl_bootrom_board_attached()) {
		printf ("  idprom address %x\n", CONFIG_IDPROM_BACKPLANE_ADDR);
		printf ("  %s\n", buf);
	}
	printf ("\n");

	printf ("  Write this personality now? (Y/N) ==> ");
	if (toupper(serial_getc()) != 'Y')
		return;
	printf ("\n");

	ret = run_command(buf, 0);
	if (ret < 0)
		printf("Error: Write to %x failed\n", idprom_address);
	if (atl_bootrom_board_attached()) {
		idprom_address = CONFIG_IDPROM_BACKPLANE_ADDR;
		ret = run_command(buf, 0);
		if (ret < 0)
			printf("Error: Write to %x failed\n", idprom_address);
		idprom_address = CONFIG_IDPROM_LOCAL_ADDR;
	}
}

static void diag_idprom_submenu_baseboard(void)
{
	int quit = 0;
	char key;

	while (!quit) {
		printf("\n\nPersonality Menu (Baseboard):\n\n"
			"  0. Previous menu\n"
			"  ----------------------------------\n"
			"  1. Display personality\n"
			"  2. Program personality\n"
			"  3. Erase personality area\n"
			"\nEnter selection ==> " );
		key = toupper(serial_getc());
		printf ("%c\n\n", isprint(key) ? key : ' ');

		switch ( key ) {
		case '0':
			quit = 1;
			break;
		case '1':
			printf("Local personality\n\n");
			do_idprom_show(NULL, 0, 0, NULL);
			if (atl_bootrom_board_attached()) {
				printf("\nBackplane personality\n\n");
				idprom_address = CONFIG_IDPROM_BACKPLANE_ADDR;
				do_idprom_show(NULL, 0, 0, NULL);
				idprom_address = CONFIG_IDPROM_LOCAL_ADDR;
			}
			break;
		case '2':
			diag_idprom_program_baseboard();
			break;
		case '3':
			if (do_idprom_erase(NULL, 0, 0, NULL) == 0)
				printf("Personality erased.\n");
			if (atl_bootrom_board_attached()) {
				idprom_address = CONFIG_IDPROM_BACKPLANE_ADDR;
				do_idprom_erase(NULL, 0, 0, NULL);
				idprom_address = CONFIG_IDPROM_LOCAL_ADDR;
			}
			break;
		default:
			break;
		}
	}
}

#ifdef CONFIG_I2C_BACKPLANE_BUS
static void diag_idprom_program_backplane_dev(char *name, unsigned int bus, int flags)
{
	char params[256];
	char buf[256 + 32];
	int ret;

	if (backplane_access(1) != 0) {
		printf("Error: Could not access chassis backplane\n");
		return;
	}

	i2c_set_bus_num(bus);
	idprom_address = CONFIG_IDPROM_BACKPLANE_ADDR;

	if (diag_idprom_program_menu(params, idprom_init(idprom_address),
			flags) < 0)
		goto out;

	sprintf(buf, "idprom write %s", params);
	printf ("\n  Commands to generate this personality using CLI:\n\n");
	printf ("  backplane enable\n");
	printf ("  idprom address %s\n", name);
	printf ("  %s\n", buf);
	printf ("\n");

	printf ("  Write this personality now? (Y/N) ==> ");
	if (toupper(serial_getc()) != 'Y')
		goto out;
	printf ("\n");

	ret = run_command(buf, 0);
	if (ret < 0)
		printf("Error: Write to %x failed\n", idprom_address);
out:
	i2c_set_bus_num(0);
	idprom_address = CONFIG_IDPROM_LOCAL_ADDR;
	backplane_access(0);
}
#endif

#ifdef CONFIG_I2C_FANTRAY_BUS
static void diag_idprom_program_fantray(void)
{
	diag_idprom_program_backplane_dev("fan",
		CONFIG_I2C_FANTRAY_BUS, IDPROM_FLAG_FAN);
}

static void diag_idprom_submenu_fantray(void)
{
	int quit = 0;
	char key;

	while (!quit) {
		printf("\n\nPersonality Menu (Fan-tray):\n\n"
			"  0. Previous menu\n"
			"  ----------------------------------\n"
			"  1. Display personality\n"
			"  2. Program personality\n"
			"  3. Erase personality area\n"
			"\nEnter selection ==> " );
		key = toupper(serial_getc());
		printf ("%c\n\n", isprint(key) ? key : ' ');

		switch ( key ) {
		case '0':
			quit = 1;
			break;
		case '1':
			printf("Fan-tray personality\n\n");
			if (backplane_access(1) == 0) {
				i2c_set_bus_num(CONFIG_I2C_FANTRAY_BUS);
				idprom_address = CONFIG_IDPROM_BACKPLANE_ADDR;
				do_idprom_show(NULL, 0, 0, NULL);
			}
			break;
		case '2':
			diag_idprom_program_fantray();
			break;
		case '3':
			if (backplane_access(1) == 0) {
				i2c_set_bus_num(CONFIG_I2C_FANTRAY_BUS);
				idprom_address = CONFIG_IDPROM_BACKPLANE_ADDR;
				if (do_idprom_erase(NULL, 0, 0, NULL) == 0)
					printf("Personality erased.\n");
			}
			break;
		default:
			break;
		}
	}

	idprom_address = CONFIG_IDPROM_LOCAL_ADDR;
	i2c_set_bus_num(0);
	backplane_access(0);
}
#endif

#ifdef CONFIG_I2C_CHASSIS_BUS
static void diag_idprom_program_chassis(void)
{
	diag_idprom_program_backplane_dev("chassis",
		CONFIG_I2C_CHASSIS_BUS, IDPROM_FLAG_CHASSIS);
}

static void diag_idprom_submenu_chassis(void)
{
	int quit = 0;
	char key;

	while (!quit) {
		printf("\n\nPersonality Menu (Chassis):\n\n"
			"  0. Previous menu\n"
			"  ----------------------------------\n"
			"  1. Display personality\n"
			"  2. Program personality\n"
			"  3. Erase personality area\n"
			"\nEnter selection ==> " );
		key = toupper(serial_getc());
		printf ("%c\n\n", isprint(key) ? key : ' ');

		switch ( key ) {
		case '0':
			quit = 1;
			break;
		case '1':
			printf("Chassis personality\n\n");
			if (backplane_access(1) == 0) {
				i2c_set_bus_num(CONFIG_I2C_CHASSIS_BUS);
				idprom_address = CONFIG_IDPROM_BACKPLANE_ADDR;
				do_idprom_show(NULL, 0, 0, NULL);
			}
			break;
		case '2':
			diag_idprom_program_chassis();
			break;
		case '3':
			if (backplane_access(1) == 0) {
				i2c_set_bus_num(CONFIG_I2C_CHASSIS_BUS);
				idprom_address = CONFIG_IDPROM_BACKPLANE_ADDR;
				if (do_idprom_erase(NULL, 0, 0, NULL) == 0)
					printf("Personality erased.\n");
			}
			break;
		default:
			break;
		}
	}

	idprom_address = CONFIG_IDPROM_LOCAL_ADDR;
	i2c_set_bus_num(0);
	backplane_access(0);

}
#endif
void diag_idprom_menu_run(void)
{
	int quit = 0;
	char key;
	u32 silent = gd->flags & GD_FLG_SILENT;

	/* Make the console non-silent so we can use printf */
	gd->flags &= ~GD_FLG_SILENT;

	while (!quit) {
		printf ("\n\nPersonality Menu:\n\n"
			"  0. Previous menu\n"
			"  ----------------------------------\n");
		printf ("  9. Baseboard personality\n");
		// A-D are reserved for the 4 PSUs
#ifdef CONFIG_I2C_FANTRAY_BUS
		printf ("  F. Fan-tray personality\n");
#endif
#ifdef CONFIG_I2C_CHASSIS_BUS
		printf ("  S. Chassis/Shelf personality\n");
#endif

		printf ("\nEnter selection ==> ");
		key = toupper(serial_getc());
		printf ("%c\n", isprint(key) ? key : ' ');

		switch (key) {
		case '0':
			quit = 1;
			break;
		case '9':
			diag_idprom_submenu_baseboard();
			break;
#ifdef CONFIG_I2C_FANTRAY_BUS
		case 'f':
		case 'F':
			diag_idprom_submenu_fantray();
			break;
#endif
#ifdef CONFIG_I2C_CHASSIS_BUS
		case 's':
		case 'S':
			diag_idprom_submenu_chassis();
			break;
#endif
		default:
			break;
		}
	}

	gd->flags |= silent;
}

