

#include <common.h>
#include <command.h>
#include <bootmenu.h>
#include <malloc.h>
#include <linux/ctype.h>
#if defined(CONFIG_X600) || defined (CONFIG_X610) || \
    defined(CONFIG_X200_GE) || defined (CONFIG_RAPIER48x) || \
    defined(CONFIG_X200_X400_FE) || defined(CONFIG_X210_ATRD) || \
    defined(CONFIG_X510)
#include <pci.h>
#include <i2c.h>
#include <epi3.h>
#endif
#ifdef CONFIG_SPI_FLASH
#include <spi_flash.h>
#endif
#ifdef CONFIG_USB_STORAGE
#include <usb.h>
#endif

#ifdef CONFIG_8BIT_ROM
#define EIGHT_BIT_ROM 1
#else
#define EIGHT_BIT_ROM 0
#endif

#if defined(CONFIG_ATRD_X300) || defined(CONFIG_X200_GE) || \
    defined(CONFIG_X200_X400_FE) || defined(CONFIG_X210_ATRD)
#define BCM56218_CMIC_ADDR 0xA8000000
#define BCM56214_VENDOR_ID 0x14e4
#endif /* CONFIG_ATRD_X300 || CONFIG_X200_GE || CONFIG_X200_X400_FE || CONFIG_X210_ATRD */

#ifdef CONFIG_SPI_FLASH
#define CONFIG_BM_SPI_BUS	0
#define CONFIG_BM_SPI_CS		0
#define CONFIG_BM_SPI_MAX_HZ	1000000
#define CONFIG_BM_SPI_MODE	SPI_MODE_3

static struct spi_flash *spi_flash = NULL;
#endif

DECLARE_GLOBAL_DATA_PTR;

int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
extern int mpi_spi_flash_select(int enable, int write);

extern int bootfallback;

void bootmenu_run(void);

#if defined(CONFIG_SYS_STDALONE_TFTP)
#if defined(CONFIG_X600)
static void bootmenu_standalone_tftp(char *file, char *buf)
{
	static struct pci_device_id pci_devs[] = {
		{ 0x14e4, 0xb504 },	// BCM56504
		{ 0x14e4, 0xb502 },	// BCM56502
		{ 0x14e4, 0xb514 },	// BCM56514
		{ 0x14e4, 0xb512 },	// BCM56512
		{ 0, 0 }
	};

	u8 phy_normal = 0xF3;
	char *p;
	pci_dev_t pci_dev;
	int index = 0;
#ifdef CONFIG_DIAG_EPI3
	epi3Personality *epi3 = epi3_getcurrent();
#endif

	// Load the standalone TFTP application into RAM
	sprintf(buf, "cp.b 0xf0080000 0x40000 0x40000");
	run_command(buf, 0);
	flush_cache (0x40000, 0x40000);

	p = buf;
	p += sprintf(p, "go 0x40000");
	while ((pci_dev = pci_find_devices(pci_devs, index++)) >= 0)
	{
		u32 addr;
		u16 vendor_id, device_id;
		u8 revision_id;

		pci_read_config_dword(pci_dev, PCI_BASE_ADDRESS_0, &addr);
		pci_read_config_word(pci_dev, PCI_VENDOR_ID, &vendor_id);
		pci_read_config_word(pci_dev, PCI_DEVICE_ID, &device_id);
		pci_read_config_byte(pci_dev, PCI_REVISION_ID, &revision_id);

		p += sprintf(p, " -d%08X,%X,%X,%X", addr, vendor_id, device_id, revision_id);
	}
	p += sprintf(p, " -f%s", file);
#ifdef CONFIG_DIAG_EPI3
	if (epi3) {
		p += sprintf(p, " -b%d", epi3->epiBoardId);
	}
#endif

	// For X1: release the Phy via I2C */
	i2c_write(0x18, 0x01, 0x01, &phy_normal, 1); /* write u44.0 */
}
#endif

#if defined(CONFIG_ATRD_X300) || defined(CONFIG_X200_GE) || \
    defined(CONFIG_X200_X400_FE) || defined(CONFIG_X210_ATRD)
static void bootmenu_standalone_tftp(char *file, char *buf)
{
	char *p;
	u32 rev;
	u8 rev_id;
	u16 dev_id;
#ifdef CONFIG_DIAG_EPI3
	epi3Personality *epi3 = epi3_getcurrent();
#endif

	// Load the standalone TFTP application into RAM
	sprintf(buf, "cp.b 0x9c080000 0x80040000 0x40000");
	run_command(buf, 0);
	flush_cache (0x80040000, 0x40000);

	p = buf;
	p += sprintf(p, "go 0x80040000");
	/* Get actual dev id/rev from CMIC */
	rev = *(u32 *)(BCM56218_CMIC_ADDR + 0x178);

	dev_id = (rev & 0xFFFF);
	rev_id = ((rev >> 16) & 0xFF);
	p += sprintf(p, " -d%08X,%X,%X,%X", BCM56218_CMIC_ADDR, BCM56214_VENDOR_ID, dev_id, rev_id);
	p += sprintf(p, " -f%s", file);
#ifdef CONFIG_DIAG_EPI3
	if (epi3) {
		p += sprintf(p, " -b%d", epi3->epiBoardId);
	}
#endif
}
#endif

#if defined(CONFIG_X510)
static void bootmenu_standalone_tftp(char *file, char *buf)
{
	static struct pci_device_id pci_devs[] = {
		{ 0x14e4, 0xb321 },	// BCM56321
		{ 0, 0 }
	};

	char *p;
	pci_dev_t pci_dev;
	int index = 0;
#ifdef CONFIG_DIAG_EPI3
	epi3Personality *epi3 = epi3_getcurrent();
#endif

	// Load the standalone TFTP application into RAM
	sprintf(buf, "cp.b 0x9c080000 0x80040000 0x40000");
	run_command(buf, 0);
	flush_cache (0x80040000, 0x40000);

	p = buf;
	p += sprintf(p, "go 0x80040000");

	while ((pci_dev = pci_find_devices(pci_devs, index++)) >= 0)
	{
		u32 addr;
		u16 vendor_id, device_id;
		u8 revision_id;

		pci_read_config_dword(pci_dev, PCI_BASE_ADDRESS_0, &addr);
		pci_read_config_word(pci_dev, PCI_VENDOR_ID, &vendor_id);
		pci_read_config_word(pci_dev, PCI_DEVICE_ID, &device_id);
		pci_read_config_byte(pci_dev, PCI_REVISION_ID, &revision_id);

		p += sprintf(p, " -d%08X,%X,%X,%X", (addr & 0xfffffff0), vendor_id, device_id, revision_id);
	}
	p += sprintf(p, " -f%s", file);
#ifdef CONFIG_DIAG_EPI3
	if (epi3) {
		p += sprintf(p, " -b%d", epi3->epiBoardId);
	}
#endif
}
#endif

#if defined (CONFIG_X610)
static void bootmenu_standalone_tftp(char *file, char *buf)
{
	static struct pci_device_id pci_devs[] = {
		{ 0x14e4, 0xb514 },	// BCM56514
		{ 0x14e4, 0xb512 },	// BCM56512
		{ 0, 0 }
	};

	char *p;
	pci_dev_t pci_dev;
	int index = 0;
#ifdef CONFIG_DIAG_EPI3
	epi3Personality *epi3 = epi3_getcurrent();
#endif

	// Load the standalone TFTP application into RAM
	sprintf(buf, "cp.b 0x%x 0x40000 0x%x", CONFIG_UBOOT_FLASH_BASE, CONFIG_SYS_STDALONE_APP_SIZE);
	run_command(buf, 0);
	flush_cache (0x40000, CONFIG_SYS_STDALONE_APP_SIZE);

	p = buf;
	p += sprintf(p, "go 0x40000");
	while ((pci_dev = pci_find_devices(pci_devs, index++)) >= 0)
	{
		u32 addr;
		u16 vendor_id, device_id;
		u8 revision_id;

		pci_read_config_dword(pci_dev, PCI_BASE_ADDRESS_0, &addr);
		pci_read_config_word(pci_dev, PCI_VENDOR_ID, &vendor_id);
		pci_read_config_word(pci_dev, PCI_DEVICE_ID, &device_id);
		pci_read_config_byte(pci_dev, PCI_REVISION_ID, &revision_id);

		p += sprintf(p, " -d%08X,%X,%X,%X", addr, vendor_id, device_id, revision_id);
	}
	p += sprintf(p, " -f%s", file);
#ifdef CONFIG_DIAG_EPI3
	if (epi3) {
		p += sprintf(p, " -b%d", epi3->epiBoardId);
	}
#endif
}
#endif

#if defined (CONFIG_RAPIER48x)
static void bootmenu_standalone_tftp(char *file, char *buf)
{
	static struct pci_device_id pci_devs[] = {
		{ 0x14e4, 0xb134 },	// BCM56134
		{ 0, 0 }
	};

	char *p;
	pci_dev_t pci_dev;
	int index = 0;
#ifdef CONFIG_DIAGNOSTICS
	epi3Personality *epi3 = epi3_getcurrent();
#endif

	// Load the standalone TFTP application into RAM
	sprintf(buf, "cp.b 0x%x 0x%x 0x%x",
		CONFIG_SYS_STDALONE_FLASH_ADDR, CONFIG_SYS_STDALONE_RAM_ADDR, CONFIG_SYS_STDALONE_APP_SIZE);
	run_command(buf, 0);
	flush_cache (CONFIG_SYS_STDALONE_RAM_ADDR, CONFIG_SYS_STDALONE_APP_SIZE);

	p = buf;
	p += sprintf(p, "go 0x%x", CONFIG_SYS_STDALONE_RAM_ADDR);
	while ((pci_dev = pci_find_devices(pci_devs, index++)) >= 0)
	{
		u32 addr;
		u16 vendor_id, device_id;
		u8 revision_id;

		pci_read_config_dword(pci_dev, PCI_BASE_ADDRESS_0, &addr);
		pci_read_config_word(pci_dev, PCI_VENDOR_ID, &vendor_id);
		pci_read_config_word(pci_dev, PCI_DEVICE_ID, &device_id);
		pci_read_config_byte(pci_dev, PCI_REVISION_ID, &revision_id);

		p += sprintf(p, " -d%08X,%X,%X,%X", addr&0xFFFF0000, vendor_id, device_id, revision_id);
	}
	p += sprintf(p, " -f%s", file);

#ifdef CONFIG_DIAGNOSTICS
	if (epi3) {
		p += sprintf(p, " -b%d", epi3->epiBoardId);
	}
#endif
}
#endif
#endif /* CONFIG_SYS_STDALONE_TFTP */

void bootmenu(void)
{
	if (gd->flags & GD_FLG_BOOTMENU)
	{
		bootmenu_printf("\n");
		bootmenu_run();
	}
}

void bootmenu_run()
{
	char	key;
	u8	quit;

	quit = 0;

	while (!quit)
	{

/*		Display the menu
 */
		serial_puts( "Boot Menu:\n\n" );
		if (getenv("developer"))
		{
			serial_puts("  Warning: Developer mode is enabled! - There are features\n"
				    "  present that would normally be disabled on customer equipment!\n\n" );
		}
			serial_puts("  ------------------------------------------------------\n");
#ifndef CONFIG_SYS_NO_FLASH
			serial_puts("  B. Boot backup software\n"
				   "  ------------------------------------------------------\n");
#endif
			serial_puts("  0. Restart\n"
		                   "  1. Perform one-off boot from alternate source\n"
				   "  2. Change the default boot source (for advanced users)\n"
				   "  3. Update Bootloader\n"
				   "  4. Adjust the console baud rate\n"
				   "  5. Special boot options\n"
				   "  6. System information\n"
				   "  7. Restore Bootloader factory settings\n"
			   );
		if (getenv("developer"))
		       serial_puts("  ------------------------------------------------------\n"
				   "  8. Developer menu\n");
		       serial_puts("  ------------------------------------------------------\n"
		                   "  9. Quit and continue booting\n"	
		                   "\nEnter selection ==> " );

/*		Wait for a key to be pressed
 */
		key = serial_getc();

		if (isdigit(key))
			serial_putc(key);
		
		serial_putc('\n');
		serial_putc('\n');

/*		Action the selection
 */
		switch ( key )
		{
#ifndef CONFIG_SYS_NO_FLASH
			case 'b':
			case 'B':
				bootfallback = 1;
				quit = 1;
				setenv("bootcmd", "awbootf");
				break;
#endif
			case '0':
				do_reset(NULL, 0, 0, NULL);
				break;
			case '1':
				if (bootmenu_one_off_boot(1, 0))
				{
					quit = 1;
				}
				break;
			case '2':
				if (bootmenu_one_off_boot(0, 0))
				{
#if !defined(CONFIG_SYS_NO_FLASH) || defined (CONFIG_SPI_FLASH)
				  bootmenu_printf("  Saving settings... ");
				  saveenv();
				  bootmenu_printf("Complete\n\n");
#endif
				}
				break;
			case '3':
				setenv("filesize", "0"); //make damn sure this is wrong to start with
				
				if (bootmenu_one_off_boot(1, 1))
				{
					setenv("bootcmd", "bootupda");
					quit = 1;
				}
				break;
			case '4':
				bootmenu_baudrate();
				break;
			case '5':
				bootmenu_special();
				break;
			case '6':
				bootmenu_sysinfo();
				break;
			case '7':
				bootmenu_restore_default_settings(0);
				break;
			case '8':
				if (getenv("developer"))
					bootmenu_developer();
				break;
			case '9':		   
				quit = 1;	/* Return from this routine to the boot process */
				break;
			default:	
				break;			/* Ignore any other character */
		}
	}
	
	gd->flags &= ~GD_FLG_BOOTMENU;
}

void bootmenu_tftp_timeout(int timeout)
{
	if (timeout < 0)
		bootmenu_printf("Error: TFTP Retry count exceeded. Trying again...\n");
	else
		bootmenu_printf("(retrying TFTP block transfer)\n");
}

void bootmenu_tftp_error(char *error, u32 code)
{
	bootmenu_printf("Error: TFTP Error code %u (%s)\n", code, error);
}

void bootmenu_set_system_boot(boot_file *file, int upgrading)
{
//	char buf[512];
	
	switch (file->fs_type)
	{
		case TYPE_SDCARD:
			setenv(upgrading ? "updadev" : "bootdev", "sdcard");
			setenv(upgrading ? "updafile" : "bootfile", file->file_name);
			break;
		case TYPE_CFCARD:
			setenv(upgrading ? "updadev" : "bootdev", "cfcard");
			setenv(upgrading ? "updafile" : "bootfile", file->file_name);
			break;
#ifdef CONFIG_USB_STORAGE
		case TYPE_USBCARD:
			setenv(upgrading ? "updadev" : "bootdev", "usbcard");
			setenv(upgrading ? "updafile" : "bootfile", file->file_name);
			break;
#endif
		case TYPE_FLASH:
			setenv(upgrading ? "updadev" : "bootdev", "flash");
			setenv(upgrading ? "updafile" : "bootfile", file->file_name);
			break;
		case TYPE_TFTP:
#ifdef CONFIG_IPV6
			if ( bootmenu_stricmp (getenv("ipversion") ? getenv("ipversion") : "4", "6") == 0 )
			{
				setenv(upgrading ? "updadev" : "bootdev", "tftp6");
			}
			else
#endif	
				setenv(upgrading ? "updadev" : "bootdev", "tftp");
			setenv(upgrading ? "updafile" : "bootfile", file->file_name);
			break;
		case TYPE_YMODEM:
			setenv(upgrading ? "updadev" : "bootdev", "ymodem");
			setenv(upgrading ? "updafile" : "bootfile", "default" CONFIG_RELEASEFILE_EXT);
			break;
		case TYPE_EPROM:
			setenv(upgrading ? "updadev" : "bootdev", "eprom");
			break;
		default:
			bootmenu_printf("Internal error: invalid device type\n");
			break;
	}
}

#ifdef CONFIG_SPI_FLASH
int _load_spi_eprom(uint src, size_t len, void *dest)
{
	unsigned int max_hz;
	int ret = 0;

	mpi_spi_flash_select(1, 0);

	if(getenv("spi_clock"))
		max_hz = simple_strtoul(getenv("spi_clock"), NULL, 16);
	else
		max_hz = CONFIG_BM_SPI_MAX_HZ;

	bootmenu_printf("  Probing MPI board SPI flash ... ");
	spi_flash = spi_flash_probe(CONFIG_BM_SPI_BUS, CONFIG_BM_SPI_CS,
			 max_hz, CONFIG_BM_SPI_MODE);
	if(!spi_flash) {
		bootmenu_printf("  Probe failed\n");
		ret = -1;
		goto out;
	}
	bootmenu_printf("Complete\n");

	bootmenu_printf("  Reading MPI board SPI flash ... ");
	if (spi_flash_read(spi_flash, src, len, dest)) {
		bootmenu_printf("Read failed!\n\n");
		ret = -1;
		goto out;
	}
	bootmenu_printf("Complete\n");

out:
	spi_flash_free(spi_flash);
	spi_flash = NULL;
	mpi_spi_flash_select(0, 0);
	return ret;
}
#endif

void bootmenu_load_file(int update)
{
	char buf[512];

	char file[256];
	char *dev = getenv(update ? "updadev" : "bootdev");

	if (!dev)
	{
		bootmenu_printf("Default boot source not set! Please run the Boot Menu!\n");
		do_reset(NULL, 0, 0, NULL);
		return;
	}
	
	if (getenv_r(update ? "updafile" : "bootfile", file, sizeof(file)) < 0)
	{
		if (strcmp(dev, "ymodem") && strcmp(dev, "eprom")) /* not needed for ymodem */
		{
			bootmenu_printf("Boot file not set! Please run the Boot Menu!\n");
			return;
		}
	}
	
	if (!(gd->flags & GD_FLG_ONEOFFBOOT_FORCED))
	{
		bootmenu_printf("\nWarning: System has been forced to boot from a non-standard location\n");
		bootmenu_printf("To restore the system to default behaviour, enter the Boot Menu,\n"
		                "\tChoose option 2, then option 9\n\n");
	}
	
	if (!strcmp(dev, "sdcard"))
	{
		sprintf(buf, "fatload mmcspi 0 0x%08X %s", CONFIG_DIAGMEM_BASE, file);
		bootmenu_printf("Reading card:%s...\n", file);
		if (run_command(buf, 0) /* perfers to return 0 on success ? */)
		{
			bootmenu_printf("Failed to read card:%s\nRebooting system...\n", file);
			do_reset(NULL, 0, 0, NULL);
		}
	}
	else if (!strcmp(dev, "cfcard"))
	{
		sprintf(buf, "fatload ide 0 0x%08X %s", CONFIG_DIAGMEM_BASE, file);
		bootmenu_printf("Reading card:%s...\n", file);
		if (run_command(buf, 0) /* perfers to return 0 on success ? */)
		{
			bootmenu_printf("Failed to read card:%s\nRebooting system...\n", file);
			do_reset(NULL, 0, 0, NULL);
		}
	}
#ifdef CONFIG_USB_STORAGE
	else if (!strcmp(dev, "usbcard"))
	{
		if (usb_stor_scan(1) < 0)
		{
			bootmenu_printf("No usb storage devices found...\nRebooting system...\n");
			usb_stop();
			do_reset(NULL, 0, 0, NULL);
		}
		sprintf(buf, "fatload usb 0 0x%08X %s", CONFIG_DIAGMEM_BASE, file);
		bootmenu_printf("Reading usb:%s...\n", file);
		if (run_command(buf, 0) /* prefers to return 0 on success ? */)
		{
			bootmenu_printf("Failed to read usb:%s\nRebooting system...\n", file);
			usb_stop();
			do_reset(NULL, 0, 0, NULL);
		}
		usb_stop();
	}
#endif
	else if (!strcmp(dev, "flash"))
	{
		sprintf(buf, "fsload 0x%08X %s", CONFIG_DIAGMEM_BASE, file);
		bootmenu_printf("Reading flash:%s...\n", file);
		if (run_command(buf, 0))
		{
			bootmenu_printf("Failed to read flash:%s\nRebooting system...\n", file);
			do_reset(NULL, 0, 0, NULL);
		}
	}
#ifdef CONFIG_IPV6
	else if (!strcmp(dev, "tftp") || !strcmp(dev, "tftp6"))
#else
	else if (!strcmp(dev, "tftp"))
#endif
	{
#ifdef CONFIG_IPV6
		int ipv6 = !strcmp(dev, "tftp6");
#else
		int ipv6 = 0;
#endif
		char serverip[256];
		char *ipver = ipv6 ? "6" : "";
#ifdef CONFIG_SYS_STDALONE_TFTP
		char *port_str = getenv("download_port");
		int download_port = port_str ? simple_strtol(port_str, NULL, 10) : 0;
		if (download_port) {
			sprintf(buf, "exttftp %s", file);
		}
		else
#endif
		{
			sprintf(buf, "tftpboot%s 0x%08X %s", ipver, CONFIG_DIAGMEM_BASE, file);
		}

		if (getenv_r(ipv6 ? "serverip6" : "serverip", serverip, sizeof(serverip)) < 0) {
			strncpy(serverip, ipv6 ? "::" : "", sizeof(serverip));
		}
		bootmenu_printf("Loading tftp%s://%s/%s...\n", ipver, serverip, file);
		if (run_command(buf, 0) <= 0)
		{
			bootmenu_printf("Failed to load tftp%s://%s/%s...\n", ipver, serverip, file);
			do_reset(NULL, 0, 0, NULL);
		}
	}
	else if (!strcmp(dev, "ymodem"))
	{
		sprintf(buf, "loady 0x%08X", CONFIG_DIAGMEM_BASE);
		bootmenu_printf("Starting YMODEM Transfer...\n");
		
		gd->flags &= ~GD_FLG_SILENT; /* Temporarily re-enable the global console */
		
		if (run_command(buf, 0))
		{
			bootmenu_printf("Error: YMODEM Transfer failed\n");
			do_reset(NULL, 0, 0, NULL);
		}
		
		gd->flags |= GD_FLG_SILENT; /* Disable it again */
	}
	else if (!strcmp(dev, "eprom"))
	{
#ifdef CONFIG_SPI_FLASH
		if (_load_spi_eprom(CONFIG_UBOOT_ROM_BASE, CONFIG_UBOOT_SIZE, (void *)CONFIG_DIAGMEM_BASE))
		{
			bootmenu_printf("Error: Load from MPI board failed\n");
			do_reset(NULL, 0, 0, NULL);
		}
		sprintf(buf, "%X", CONFIG_UBOOT_SIZE);
		setenv("filesize", buf);
#else
		sprintf(buf, "cp.b 0x%08X 0x%08X 0x%08X", CONFIG_UBOOT_ROM_BASE, CONFIG_DIAGMEM_BASE, CONFIG_UBOOT_SIZE);
		run_command(buf, 0);
		sprintf(buf, "%X", CONFIG_UBOOT_SIZE);
		setenv("filesize", buf);
#endif
	}
	else 
	{
		bootmenu_printf("Boot device is invalid! Please run the Boot Menu!\n");
	}
	
	bootmenu_printf("\n");
}

void bootmenu_system_boot(void)
{
	char buf[512];
	unsigned long filesize;
	
	filesize = simple_strtoul(getenv("filesize") ? getenv("filesize") : "0", NULL, 16);

	if (!filesize)
	{
		bootmenu_printf("Error: No file was loaded\n\n");
		goto fail;
	}
	
	sprintf(buf, "awbootm 0x%08X", CONFIG_DIAGMEM_BASE);
	
	run_command(buf, 0);
		
fail:
	bootmenu_printf("Restarting...\n");
	
	do_reset(NULL, 0, 0, NULL);
}

#ifndef CONFIG_SYS_NO_FLASH
static void _do_system_update(void *dest, void *src, unsigned long filesize)
{
	char buf[512];

	sprintf(buf, "prot off 0x%p 0x%p", dest, dest + CONFIG_UBOOT_SIZE - 1);
	bootmenu_printf("  Unprotecting boot block ... ");
	if (run_command(buf, 0) < 0)
	{
		bootmenu_printf("Unprotect failed!\n\n");
		return;
	}
	bootmenu_printf("Complete\n");
	sprintf(buf, "erase 0x%p 0x%p", dest, dest + CONFIG_UBOOT_SIZE - 1);
	bootmenu_printf("  Erasing boot block ........ ");
	if (run_command(buf, 0) < 0)
	{
		bootmenu_printf("Erase failed!\n\n");
		return;
	}
	bootmenu_printf("Complete\n");
	sprintf(buf, "cp.b 0x%p 0x%p 0x%08lX", src, dest, filesize);
	bootmenu_printf("  Writing boot block ........ ");
	if (run_command(buf, 0) < 0)
	{
		bootmenu_printf("Write failed!\n\n");
		return;
	}
	bootmenu_printf("Complete\n");
	sprintf(buf, "prot on 0x%p 0x%p", dest, dest + CONFIG_UBOOT_SIZE - 1);
	bootmenu_printf("  Protecting boot block ..... ");
	if (run_command(buf, 0) < 0)
	{
		bootmenu_printf("Protect failed!\n");
		return;
	}
	bootmenu_printf("Complete\n");
}
#endif

#ifdef CONFIG_SPI_FLASH
static void _do_system_update(void* dest, void *src, unsigned long filesize)
{
	void *data = NULL;
	int src_crc;
	int dest_crc;
	unsigned int max_hz;

	if (getenv("spi_clock"))
		max_hz = simple_strtoul(getenv("spi_clock"), NULL, 16);
	else
		max_hz = CONFIG_BM_SPI_MAX_HZ;

	bootmenu_printf("  Probing SPI flash ...... ");
	spi_flash = spi_flash_probe(CONFIG_BM_SPI_BUS, CONFIG_BM_SPI_CS,
			max_hz, CONFIG_BM_SPI_MODE);
	if (!spi_flash) {
		bootmenu_printf("Probe failed!\n");
		goto out;
	}
	bootmenu_printf("Complete\n");

	bootmenu_printf("  Erasing SPI flash ...... ");
	if (spi_flash_erase(spi_flash, (u32)dest, CONFIG_UBOOT_SIZE)){
		bootmenu_printf("Erase failed!\n");
		goto out_free;
	}
	bootmenu_printf("Complete\n");

	bootmenu_printf("  Writing boot block ..... ");
	if (spi_flash_write(spi_flash, (u32)dest, filesize, src)) {
		bootmenu_printf("Write failed!\n\n");
		goto out_free;
	}
	bootmenu_printf("Complete\n");

	bootmenu_printf("  Verifying boot block ... ");
	data = malloc(filesize);
	if (spi_flash_read(spi_flash, (u32)dest, filesize, data)) {
		bootmenu_printf("Read failed!\n\n");
		goto out_free;
	}

	src_crc = crc32(0, src, filesize);
	dest_crc = crc32(0, data, filesize);
	if (src_crc != dest_crc) {
		bootmenu_printf("Verify failed!\n\n");
		printf("Checksum is %08x but should be %08x\n", dest_crc, src_crc);
		goto out_free;
	}
	bootmenu_printf("Complete\n");

out_free:
	if (data)
		free(data);
	spi_flash_free(spi_flash);
	spi_flash = NULL;
out:
	return;
}
#endif

void bootmenu_system_update(void)
{
	char key;
	unsigned long filesize;

	filesize = simple_strtoul(getenv("filesize") ? getenv("filesize") : "0", NULL, 16);

	if (!filesize)
	{
		bootmenu_printf("Error: No file was loaded\n\n");
		goto fail;
	}

#ifndef CONFIG_UBOOT_SIZE_VARIES
	if (filesize != CONFIG_UBOOT_SIZE)
	{
		bootmenu_printf("Error: The loaded file must be exactly %u bytes long\n\n", CONFIG_UBOOT_SIZE);
		goto fail;
	}
#endif
	bootmenu_print_checksums((void *)CONFIG_DIAGMEM_BASE, filesize);

	if (getenv("developer"))
	{
		bootmenu_printf( "  Developer mode is enabled. Confirmation check skipped." );
	}
	else
	{
		bootmenu_printf("  WARNING: Please ensure that in addition to the checksum(s) being valid,\n"
				"  the file you are about to load is the one intended for this device.\n"
				"  If it is not, the device may be rendered unusable.\n\n");
		bootmenu_printf("  WARNING: Older versions of the bootloader may not work with newer products.\n"
				"  Do not try older versions of bootloader with this product.\n\n"
				"  Are you sure? (type \"yes\") ==> ");

		key = serial_getc();
		if (key != 'y' && key != 'Y')
			goto fail;
		serial_putc('y');

		key = serial_getc();
		if (key != 'e' && key != 'E')
			goto fail;
		serial_putc('e');

		key = serial_getc();
		if (key != 's' && key != 'S')
			goto fail;
		serial_putc('s');

		key = serial_getc();
		if (key != 0xD && key != 0xA)
			goto fail;
	}

	bootmenu_printf("\n\n");

	_do_system_update((void*)CONFIG_UBOOT_FLASH_BASE, (void*)CONFIG_DIAGMEM_BASE, filesize);
	
	bootmenu_printf("  Restarting...");
fail:
	bootmenu_printf("\n");
	do_reset(NULL, 0, 0, NULL);
}

void bootmenu_print_checksums(void *location, u32 size)
{
	u8	*address;
	u16	checksum0 = 0;
	u16	checksum1 = 0;

	for (address = location; address < &((u8 *)location)[size]; address++)
	{
		if (EIGHT_BIT_ROM || ((u32)address & 0x01) == 0)
		{
			checksum0 += *address;
		}
		else
		{
			checksum1 += *address;
		}
	}
	
	bootmenu_printf("  The file you are about to write to the boot block has these checksum(s):\n\n");
	bootmenu_printf("  Checksum0: %04X\n", checksum0);
	if (!EIGHT_BIT_ROM)
		bootmenu_printf("  Checksum1: %04X\n", checksum1);
	bootmenu_printf("\n");
	bootmenu_printf("  Please check these against the file provided, these may be in the name of\n");
	bootmenu_printf("  the file, or at the same location the file was obtained from.\n\n");

} /* DiagMgrRomChecksumTest */

void bootmenu_printf (const char *fmt, ...)
{
	va_list args;
	uint i;
	char printbuffer[CONFIG_SYS_PBSIZE];

	va_start (args, fmt);

	/* For this to work, printbuffer must be larger than
	* anything we ever want to print.
	*/
	i = vsprintf (printbuffer, fmt, args);
	va_end (args);

	/* Print the string */
	serial_puts (printbuffer);
}

int bootmenu_stricmp (const char *s, const char *t) {
	int d = 0;
	do {
		d = toupper(*s) - toupper(*t);
	} while (*s++ && *t++ && !d);
	return (d);
}


int bootmenu_get_int(u32 * ret)
{
	int count, i;
	char buf[16];
	
	count = bootmenu_get_string(buf, 16);
	
	for (i = 0; i < count; i++)
	{
		if (!isdigit(buf[i]))
			return 0;
	}
	
	*ret = simple_strtoul(buf, NULL, 10);
	return 1;
}

int bootmenu_string_prompt(char *str, char *name, char *def, int max)
{
	int ret;
	char *in_default;
	
	if (!def)
		in_default = "";
	else
		in_default = def;
	
	bootmenu_printf("%s [%s]: ", name, in_default);
	
	ret = bootmenu_get_string(str, max);
	
	if (ret == 0)
	{
		strncpy(str, in_default, max);
		str[max - 1] = 0;
		bootmenu_printf("\n");
		return strlen(str);
	}
	if (ret < 0)
	{
		bootmenu_printf("\n");
		return 0;
	}
	
	bootmenu_printf("\n");
	return ret;
}

static char erase_seq[] = "\b \b";

int bootmenu_get_string(char *str, int max)
{
	char c;
	int count;
	count = 0;
	do
	{
		if (count >= max)
		{
			count--;
			break;
		}	
		c = serial_getc();
		switch (c)
		{
		case '\r':				/* Enter		*/
		case '\n':
			str[count] = '\0';
			serial_putc (c);
			return count;

		case '\0':				/* nul			*/
			continue;

		case 0x03:				/* ^C - break		*/
			str[0] = '\0';	/* discard input */
			return (-1);

		case 0x15:				/* ^U - erase line	*/
		case 0x17:				/* ^W - erase word	*/
			while(count > 0) {
				serial_puts(erase_seq);
				count--;
			}
			continue;

		case 0x08:				/* ^H  - backspace	*/
		case 0x7F:				/* DEL - backspace	*/
			if (count > 0) {
				serial_puts(erase_seq);
				count--;
			}
			continue;

		/* ESC - handle xterm escape sequences (by doing nothing).
		 * Known sequences:
		 *   Up = ESC [ A, Down = ESC [ B, Right = ESC [ C, Left = ESC [ D
		 *   Home = ESC [ 1 ~, Ins = ESC [ 3 ~, Del = ESC [ 3 ~,
		 *   End = ESC [ 4 ~ (also ESC O F),
		 *   PgUp = ESC [ 5 ~, PgDn = ESC [ 6 ~
		 *
		 *  based on http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
		 *  plus some experimentation
		 */
		case 0x1b:
			c = serial_getc();
			switch (c) {
			case '[':			// CSI
				c = serial_getc();
				if (c >= '0' && c <= '9')
					c = serial_getc();
				break;
			case 'O':			// SS3
				c = serial_getc();
			}
			continue;

		default:
			if (isprint(c)) {
				serial_putc(c);
				str[count] = c;
				count++;
			}
		}
	} while (1);

	str[count] = 0;	
	return count;
}

int do_bootmenu (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	bootmenu_load_file(0);	
	bootmenu_system_boot();
	return 0;
}

int do_bootmenu_update (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	
	bootmenu_load_file(1);	
	bootmenu_system_update();
	return 0;
}

#if defined(CONFIG_SYS_STDALONE_TFTP)
static int do_exttftp (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	char buf[512];

	if (argc != 2) {
		return 1;
	}

	bootmenu_standalone_tftp(argv[1], buf);
	if (run_command(buf, 0) <= 0) {
		return 1;
	}
	return 0;
}

U_BOOT_CMD(
	exttftp,	2,	1,	do_exttftp,
	"Load via external TFTP",
	"filename"
);
#endif

U_BOOT_CMD(
	bootmenu, 1, 1, do_bootmenu,
	"<INTERNAL COMMAND **DO NOT USE**>",
	""
	);

U_BOOT_CMD(
	bootupda, 1, 1, do_bootmenu_update,
	"<INTERNAL COMMAND **DO NOT USE**>",
	""
	);

#ifdef CONFIG_SPI_FLASH
int do_mpi_update (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	void *addr;
	u32 offset = CONFIG_UBOOT_FLASH_BASE;
	unsigned long size;

	if (argc < 3) {
		cmd_usage(cmdtp);
		return -1;
	}

	addr = (void *)simple_strtoul(argv[1], NULL, 16);
	size = simple_strtoul(argv[2], NULL, 16);
	if (argc == 4)
		offset = simple_strtoul(argv[3], NULL, 16);

	mpi_spi_flash_select(1, 1);
	_do_system_update((void*)offset, addr, size);
	mpi_spi_flash_select(0, 0);
	return 0;
}

U_BOOT_CMD(
	mpiupda, 4, 1, do_mpi_update,
	"update MPI board image",
	"addr size [offset]\n"
	"\taddr   - address of image data\n"
	"\tsize   - size of image data\n"
	"\toffset - offset from the start of flash"
	);
#endif
