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

#include <ppcboot.h>
#include "L1.h"
#include <asm/processor.h>
#include <405gp_i2c.h>
#include <command.h>
#include <cmd_nvedit.h>
#include <cmd_bootm.h>
#include <rtc.h>
#include <net.h>

#define FACTORY_SETTINGS 0xFFFC0000

#define DHCP_BOOTFILE     67
#define DHCP_ROOTPATH     17
#define DHCP_L1_FIRMWARE 220
#define DHCP_L1_KERNEL   221
#define DHCP_L1_ROOTFS   222

#define XTFTP 0x100000	/* TFTP things to this addr for staging */
#define TFTP_NAMELEN 128

/* Things DHCP server can tellme about.  If there's no flash address, then
 * they dont participate in 'update' to flash, and we force their values
 * back to '0' every boot to be sure to get them fresh from DHCP
 */
typedef struct dhcp_item_s {
	u8   dhcp_option;
	char tftpname[TFTP_NAMELEN];
	char envname[16];
	u32	 new_version;
	u32  flashaddr;
	u32  flashlen;
} dhcp_item_t;
static dhcp_item_t Things[] = {
	{DHCP_BOOTFILE,    "", "bootfile",    0, 0,          0},
	{DHCP_ROOTPATH,    "", "rootpath",    0, 0,          0},
	{DHCP_L1_FIRMWARE, "", "L1-firmware", 0, 0xFFFE0000, 0x020000},
	{DHCP_L1_KERNEL,   "", "L1-kernel",   0, 0xFFF00000, 0x0C0000},
	{DHCP_L1_ROOTFS,   "", "L1-rootfs",   0, 0xFFC00000, 0x300000}
};
#define N_THINGS ((sizeof(Things))/(sizeof(dhcp_item_t)))
static char buf[128];

extern flash_info_t flash_info[];	/* info for FLASH chips */
extern char		BootFile[];			/* TFTP filename in net/net.c */
extern ulong	NetBootFileXferSize;

int board_pre_init (void)
{
   /*-------------------------------------------------------------------------+
   | Interrupt controller setup for the Walnut board.
   | Note: IRQ 0-15  405GP internally generated; active high; level sensitive
   |       IRQ 16    405GP internally generated; active low; level sensitive
   |       IRQ 17-24 RESERVED
   |       IRQ 25 (EXT IRQ 0) FPGA; active high; level sensitive
   |       IRQ 26 (EXT IRQ 1) SMI; active high; level sensitive
   |       IRQ 27 (EXT IRQ 2) Not Used
   |       IRQ 28 (EXT IRQ 3) PCI SLOT 3; active low; level sensitive
   |       IRQ 29 (EXT IRQ 4) PCI SLOT 2; active low; level sensitive
   |       IRQ 30 (EXT IRQ 5) PCI SLOT 1; active low; level sensitive
   |       IRQ 31 (EXT IRQ 6) PCI SLOT 0; active low; level sensitive
   | Note for Walnut board:
   |       An interrupt taken for the FPGA (IRQ 25) indicates that either
   |       the Mouse, Keyboard, IRDA, or External Expansion caused the
   |       interrupt. The FPGA must be read to determine which device
   |       caused the interrupt. The default setting of the FPGA clears
   |
   +-------------------------------------------------------------------------*/

  mtdcr(uicsr, 0xFFFFFFFF);        /* clear all ints */
  mtdcr(uicer, 0x00000000);        /* disable all ints */
  mtdcr(uiccr, 0x00000020);        /* set all but FPGA SMI to be non-critical*/
  mtdcr(uicpr, 0xFFFFFFE0);        /* set int polarities */
  mtdcr(uictr, 0x10000000);        /* set int trigger levels */
  mtdcr(uicvcr, 0x00000001);       /* set vect base=0,INT0 highest priority*/
  mtdcr(uicsr, 0xFFFFFFFF);        /* clear all ints */

  return 0;
}


/* ------------------------------------------------------------------------- */

/*
 * Check Board Identity:
 */


int checkboard (void)
{
#if 0
    unsigned char *s = getenv("serial#");
    unsigned char *e;

    if (!s || strncmp(s, "CRAY_L1", 7))
      {
	printf ("### No HW ID - assuming CRAY_L1\n");
      }
    else
      {
        for (e=s; *e; ++e) {
          if (*e == ' ')
	    break;
        }

        for ( ; s<e; ++s) {
          putc (*s);
        }
      }

    putc ('\n');
#else
#endif
    return (0);
}


void misc_init_r(bd_t *bd)
{
unsigned char *s, *e, needsave=0;
image_header_t *hdr;
time_t timestamp;
struct rtc_time tm;
u8 thing;

	hdr = (image_header_t *)(CFG_MONITOR_BASE - sizeof(image_header_t));
	timestamp = (time_t)hdr->ih_time;
	to_tm (timestamp, &tm);
	printf (
"Welcome to PPCBOOT on Cray L1. Compiled %4d-%02d-%02d  %2d:%02d:%02d (UTC)\n",
		tm.tm_year, tm.tm_mon, tm.tm_mday,
		tm.tm_hour, tm.tm_min, tm.tm_sec);

    if ((s = getenv("ethaddr")) == NULL) 
	{
		printf ("No ENVIRONMENT:");
		e = (unsigned char *)(FACTORY_SETTINGS);
		if (*e != '0')
		{
			printf ("No valid MAC address in flash location 0x3C0000!\n");
		}
		else
		{
			printf ("Factory MAC: %s\n",e);
			setenv ("ethaddr", e);
			// Serial# will later be sucked out of i2c
			// setenv ("serial#", "CRAY_L1");
			setenv ("dhcp_client_id", "crayL1");
			needsave++;
		}
	}
// Also establish the baseline values for firmware,kernel,rootfs
	for ( thing=0; thing < N_THINGS; thing++)
	{
// If the env setting doesn't yet exist, or for things that are NOT associated 
// with save-regions in firmware (i.e. only believe fresh DHCP values) we
// set zero baselines.
    	if (!Things[thing].flashaddr || (getenv(Things[thing].envname) == NULL))
		{
			setenv (Things[thing].envname,"");
			needsave++;
		}
	}

	return;
}

/* 
 * ..in process...
 */
/* ------------------------------------------------------------------------- */


int do_crayL1(cmd_tbl_t *cmdtp, bd_t *bd, int flag, int argc, char *argv[])
{
unsigned long addr, valu,old;
u8 thing,need_save=0,only, *ptr;


	if (strcmp(argv[1], "update") == 0) // TFTP down updates.
	{
		only = 0xff;
		if ( argc > 2 ) 
		{
			for ( thing=0; thing < N_THINGS; thing++)
			{
				if (strcmp(argv[2], Things[thing].envname) == 0)
				{
					only = thing;
					break;
				}
			}
			if ( only == 0xff )
			{
				printf ("update one of: [ ");
				for ( thing=0; thing < N_THINGS; thing++)
					printf ("%s ",Things[thing].envname);
				printf ("]\n");
				return 1;
			}
			else
				printf ("Update %s\n",Things[only].envname);
		}

		for ( thing=0; thing < N_THINGS; thing++)
		{
			if (((only != 0xff) && (thing != only)) || !Things[thing].flashaddr)
				continue;
			printf ("check Update: %s..",Things[thing].envname);
			if ( strlen (Things[thing].tftpname))
			{
				old = simple_strtoul(getenv(Things[thing].envname),NULL,10);
				if ( Things[thing].new_version <= old )
				{
					printf ("..already up to date\n");
					continue;
				}
				printf ("needs updating, will TFTP <%s>\n",
						Things[thing].tftpname);

				load_addr = XTFTP;
				strcpy (BootFile,Things[thing].tftpname);
				if (NetLoop(bd, TFTP) == 0)
					printf ("fail to TFTP %s\n",Things[thing].tftpname);
				else 
				{
					valu = NetBootFileXferSize & 0xffff0000;
					if (NetBootFileXferSize & 0xffff)
						valu += 0x10000;

					printf ("erasing flash regions: %x thru %lx\n",
										Things[thing].flashaddr,
										Things[thing].flashaddr + valu - 1);

					flash_sect_erase(	Things[thing].flashaddr,
										Things[thing].flashaddr + valu - 1);
					printf ("updating flash: %lx bytes from %x to %x\n",
							valu, XTFTP, Things[thing].flashaddr);
					flash_write ((uchar *)XTFTP, Things[thing].flashaddr, valu);

					printf ("Updating environmet setting..\n");
					memset (Things[thing].tftpname,0,TFTP_NAMELEN);
					sprintf( buf,"%d",Things[thing].new_version);
					setenv (Things[thing].envname,buf);
					need_save = 1;
				}
			}
			else
				printf ("No TFTP update file DHCP-provided for %s\n",
							Things[thing].envname);
		}
		if (need_save)
			do_saveenv  (NULL,NULL,0,0,NULL);
    	return 0;
	}

	if (strcmp(argv[1], "boot") == 0) 	// boot ourselves up.
	{
// Some process has happened; probably DHCP, and things have been updated.
// I normally want to boot from image in flash, using rootfs in flash; that
// is the contents of the 'ramboot' environment variable.  If server has
// given me a bootfile and rootpath, then we will assume that we are to boot
// that way, with the 'nfsboot' environment string.  Regardless, we ALWAYS
// set up the ip= boot argument, since kernel lacks DHCP support still.

    	if ((ptr = getenv ("bootfile")) && strlen(ptr))
		{
			load_addr = XTFTP;
			strcpy (BootFile,ptr);
			if (NetLoop(bd, TFTP) == 0)
				printf ("fail to TFTP boot image <%s>; revert to flash.\n",ptr);
		}
		else
			load_addr = 0xfff00000;	/* flash kernel address */

    	if ((ptr = getenv ("rootpath")) && strlen(ptr))
			run_command ("setenv bootargs "
	"root=/dev/nfs nfsroot=$(serverip):$(rootpath) "
	" ip=$(ipaddr):$(serverip):$(gatewayip):$(netmask):$(hostname):eth0:off"
	" ro devfs=mount",bd,0);
		else
			run_command ("setenv bootargs "
	"root=/dev/mtdblock/0 "
	" ip=$(ipaddr):$(serverip):$(gatewayip):$(netmask):$(hostname):eth0:off"
	" ro devfs=mount",bd,0);

		addr = load_addr;
		do_bootm  (cmdtp, bd, 0, 1, NULL);
    	return 0;
	}
	if (strcmp(argv[1], "regs") == 0) 	// Dump DCRs
	{
    	printf("cray regs command..UNIMPLEMENTED.\n");
    	return 1;
	}
	if (strcmp(argv[1], "mfdcr") == 0) 	// read a DCR from argv[2] (HEX!)
	{
		addr = simple_strtoul(argv[2], NULL, 16);
    	printf("cray mfcdr command: read from DCR 0x%lx UNIMPLEMENTED.\n",addr);
    	return 1;
	}
	if (strcmp(argv[1], "mtdcr") == 0) 	// write a DCR from argv[2] (HEX!)
	{
		addr = simple_strtoul(argv[2], NULL, 16);
		valu = simple_strtoul(argv[3], NULL, 16);
    	printf("cray mtcdr command: write 0x%lx to DCR 0x%lx UNIMPLEMENTED\n",
				valu, addr);
    	return 1;
	}
		
    printf("Usage:\n%s\n", cmdtp->usage);
    return 1;
}



/* ------------------------------------------------------------------------- */

/* ------------------------------------------------------------------------- */
/*
  initdram(int board_type) does NOT! read the EEPROM on IIc, but instead
  returns hardwire 32M.
*/
/* ------------------------------------------------------------------------- */
long int initdram (int board_type)
{
	return (32*1024*1024);
}

/* ------------------------------------------------------------------------- */
/* stubs so we can print dates w/o any nvram RTC.*/
void rtc_get (struct rtc_time *tmp) {return;}
void rtc_set (struct rtc_time *tmp) {return;}
void rtc_reset (void) {return;}


/* ------------------------------------------------------------------------- */

u8 *dhcp_vendorex_prep(u8 *e)
{
u8 thing;
char *ptr;

/* my DHCP_CLIENT_IDENTIFIER = 61 */
	if ((ptr = getenv("dhcp_client_id")))
	{
		*e++ = 61;
		*e++ = strlen(ptr);
		while (*ptr)
			*e++ = *ptr++;
	}
    *e++ = 55;		/* Parameter Request List */
	*e++ = N_THINGS;
	for ( thing=0; thing < N_THINGS; thing++)
		*e++ = Things[thing].dhcp_option;
	*e++ = 255;

	return e;
}

/* ------------------------------------------------------------------------- */
u8 *dhcp_vendorex_proc(u8 *popt)
{
u8 oplen,thing,i;

/* have TFTP filenames and some version#.  If no version# then version=0. */
		oplen = *(popt + 1);

		for ( thing=0; thing < N_THINGS; thing++)
		{
			if (*popt == Things[thing].dhcp_option)
			{
				for(i=0;((i<TFTP_NAMELEN) && (i<oplen));i++)
					if ((Things[thing].tftpname[i] = *(popt+2+i)) == ' ')
						break;
				Things[thing].tftpname[i] = '\0';
				while (*(popt+2+i) == ' ') i++;
				Things[thing].new_version = simple_strtoul(popt+2+i,NULL,10);
				printf ("L1-DHCP: image for %s <%s> have#=%s need#=%d\n",
						Things[thing].envname,
						Things[thing].tftpname,
						getenv (Things[thing].envname),
						Things[thing].new_version);
				break;
			}
		}
		return ( thing >= N_THINGS ? NULL : popt);
}
